https://github.com/AmrDeveloper created https://github.com/llvm/llvm-project/pull/212146
Implement load and store support for a vector of bools Issue #161233 >From b790e6bfa650d7099c00528d4b7fdf0c31a7f5b8 Mon Sep 17 00:00:00 2001 From: Amr Hesham <[email protected]> Date: Sun, 26 Jul 2026 21:34:13 +0200 Subject: [PATCH] [CIR] Support load/store for Vector of bools --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 74 ++++++++++++++++++------ clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 15 +++++ clang/lib/CIR/CodeGen/CIRGenFunction.h | 4 ++ clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 33 +++++++++++ clang/lib/CIR/CodeGen/CIRGenTypes.h | 14 +++++ clang/test/CIR/CodeGen/vector-bool.cpp | 22 +++++++ 6 files changed, 143 insertions(+), 19 deletions(-) create mode 100644 clang/test/CIR/CodeGen/vector-bool.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 1517de5200734..5ee2ac7b02ea4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -469,20 +469,18 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr, bool isNontemporal) { if (const auto *clangVecTy = ty->getAs<clang::VectorType>()) { - // Boolean vectors use `iN` as storage type. - if (clangVecTy->isExtVectorBoolType()) - cgm.errorNYI(addr.getPointer().getLoc(), - "emitStoreOfScalar ExtVectorBoolType"); - - // Handle vectors of size 3 like size 4 for better performance. - const mlir::Type elementType = addr.getElementType(); - const auto vecTy = cast<cir::VectorType>(elementType); - - // TODO(CIR): Use `ABIInfo::getOptimalVectorMemoryType` once it upstreamed - assert(!cir::MissingFeatures::cirgenABIInfo()); - if (vecTy.getSize() == 3 && !getLangOpts().PreserveVec3Type) - cgm.errorNYI(addr.getPointer().getLoc(), - "emitStoreOfScalar Vec3 & PreserveVec3Type disabled"); + mlir::Type srcTy = value.getType(); + if (auto vecTy = dyn_cast<cir::VectorType>(srcTy)) { + // TODO(CIR): Use `ABIInfo::getOptimalVectorMemoryType` once it upstreamed + assert(!cir::MissingFeatures::cirgenABIInfo()); + if (!clangVecTy->isPackedVectorBoolType(getContext()) && + vecTy.getSize() == 3 && !getLangOpts().PreserveVec3Type) + cgm.errorNYI(addr.getPointer().getLoc(), + "emitStoreOfScalar Vec3 & PreserveVec3Type disabled"); + + if (addr.getElementType() != srcTy) + addr = addr.withElementType(builder, srcTy); + } } value = emitToMemory(value, ty); @@ -709,7 +707,24 @@ mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) { ty = atomicTy->getValueType(); if (ty->isExtVectorBoolType()) { - cgm.errorNYI("emitToMemory: extVectorBoolType"); + mlir::Type storeTy = convertTypeForLoadStore(ty); + if (value.getType() == storeTy) + return value; + + const cir::CIRDataLayout dataLayout = cgm.getDataLayout(); + if (isa<cir::VectorType>(storeTy) && + dataLayout.getTypeSizeInBits(storeTy) > + dataLayout.getTypeSizeInBits(value.getType())) { + cgm.errorNYI("emitToMemory ExtVectorBoolType store.size > value.size"); + return {}; + } + + // Expand to the memory bit width. + unsigned memNumElems = cgm.getDataLayout().getTypeSizeInBits(storeTy); + // <N x i1> --> <P x i1>. + value = emitBoolVecConversion(value, memNumElems); + // <P x i1> --> iP. + value = builder.createBitcast(value, storeTy); } // Unlike in classic codegen CIR, bools are kept as `cir.bool` and BitInts are @@ -723,7 +738,14 @@ mlir::Value CIRGenFunction::emitFromMemory(mlir::Value value, QualType ty) { ty = atomicTy->getValueType(); if (ty->isPackedVectorBoolType(getContext())) { - cgm.errorNYI("emitFromMemory: PackedVectorBoolType"); + mlir::Type rawIntTy = value.getType(); + unsigned widh = cgm.getDataLayout().getTypeSizeInBits(rawIntTy); + auto paddingVecTy = cir::VectorType::get(builder.getBoolTy(), widh); + auto v = builder.createBitcast(value, paddingVecTy); + + mlir::Type valTy = convertType(ty); + unsigned valNumElems = cast<cir::VectorType>(valTy).getSize(); + return emitBoolVecConversion(v, valNumElems); } return value; @@ -750,9 +772,23 @@ mlir::Value CIRGenFunction::emitLoadOfScalar(Address addr, bool isVolatile, mlir::Type eltTy = addr.getElementType(); if (const auto *clangVecTy = ty->getAs<clang::VectorType>()) { - if (clangVecTy->isExtVectorBoolType()) { - cgm.errorNYI(loc, "emitLoadOfScalar: ExtVectorBoolType"); - return nullptr; + // Boolean vectors use `iN` as storage type. + if (clangVecTy->isPackedVectorBoolType(getContext())) { + mlir::Type valTy = convertType(ty); + unsigned valNumElements = cast<cir::VectorType>(valTy).getSize(); + // Load the `iP` storage object (P is the padded vector size). + mlir::Value rawIntV = builder.createLoad(getLoc(loc), addr, isVolatile); + mlir::Type rawIntTy = rawIntV.getType(); + assert(isa<cir::IntTypeInterface>(rawIntTy) && + "compressed 1N storage for bitvectors"); + // Bitcast iP --> <P x i1>. + auto paddingVecTy = cir::VectorType::get( + builder.getBoolTy(), + cast<cir::IntTypeInterface>(rawIntTy).getWidth()); + auto v = builder.createBitcast(getLoc(loc), rawIntV, paddingVecTy); + // Shuffle <P x i1> --> <N x i1> (N is the actual bit size). + v = emitBoolVecConversion(v, valNumElements); + return emitFromMemory(v, ty); } const auto vecTy = cast<cir::VectorType>(eltTy); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 92568dcc3dfd8..1a3afa45ceeb0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -105,6 +105,10 @@ mlir::Type CIRGenFunction::convertType(QualType t) { return cgm.getTypes().convertType(t); } +mlir::Type CIRGenFunction::convertTypeForLoadStore(QualType astTy) { + return cgm.getTypes().convertTypeForLoadStore(astTy); +} + mlir::Location CIRGenFunction::getLoc(SourceLocation srcLoc) { // Some AST nodes might contain invalid source locations (e.g. // CXXDefaultArgExpr), workaround that to still get something out. @@ -804,6 +808,17 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, return fn; } +mlir::Value CIRGenFunction::emitBoolVecConversion(mlir::Value srcVec, + unsigned numElementsDst) { + auto srcTy = cast<cir::VectorType>(srcVec.getType()); + unsigned numElementsSrc = srcTy.getSize(); + if (numElementsSrc == numElementsDst) + return srcVec; + + llvm_unreachable("emitBoolVecConversion"); + return {}; +} + void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { assert(!cir::MissingFeatures::sanitizers()); const auto *ctor = cast<CXXConstructorDecl>(curGD.getDecl()); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index d88f38eefa99a..d36b4ac3bc359 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -271,6 +271,7 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::Type convertTypeForMem(QualType t); mlir::Type convertType(clang::QualType t); + mlir::Type convertTypeForLoadStore(QualType astTy); mlir::Type convertType(const TypeDecl *t) { return convertType(getContext().getTypeDeclType(t)); } @@ -1984,6 +1985,9 @@ class CIRGenFunction : public CIRGenTypeCache { void emitBeginCatch(const CXXCatchStmt *catchStmt, mlir::Value ehToken); + // Truncate or extend a boolean vector to the requested number of elements. + mlir::Value emitBoolVecConversion(mlir::Value srcVec, unsigned numElementsDs); + mlir::LogicalResult emitCXXTryStmt(const clang::CXXTryStmt &s, cxxTryBodyEmitter &bodyCallback); mlir::LogicalResult emitCXXTryStmt(const clang::CXXTryStmt &s); diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index e5af4eec7720f..e4d17eaebb6d9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -689,6 +689,21 @@ mlir::Type CIRGenTypes::convertTypeForMem(clang::QualType qualType, mlir::Type convertedType = convertType(qualType); + // Check for the boolean vector case. + if (qualType->isExtVectorBoolType()) { + auto vecTy = cast<cir::VectorType>(convertedType); + + if (astContext.getLangOpts().HLSL) { + cgm.errorNYI("convertTypeForMem: ExtVectorBoolType HLSL"); + return {}; + } + + // Pad to at least one byte. + uint64_t bytePadded = std::max<uint64_t>(vecTy.getSize(), 8); + return cir::IntType::get(builder.getContext(), bytePadded, + /*is_signed=*/false); + } + assert(!forBitField && "Bit fields NYI"); // If this is a bit-precise integer type in a bitfield representation, map @@ -699,6 +714,24 @@ mlir::Type CIRGenTypes::convertTypeForMem(clang::QualType qualType, return convertedType; } +mlir::Type CIRGenTypes::convertTypeForLoadStore(clang::QualType t) { + if (t->isBitIntType()) { + cgm.errorNYI("convertTypeForLoadStore: BitIntType"); + return {}; + } + + if (t->isConstantMatrixBoolType()) { + cgm.errorNYI("convertTypeForLoadStore: ConstantMatrixBoolType"); + return {}; + } + + if (t->isExtVectorBoolType()) { + return convertTypeForMem(t); + } + + return convertType(t); +} + /// Return record layout info for the given record decl. const CIRGenRecordLayout & CIRGenTypes::getCIRGenRecordLayout(const RecordDecl *rd) { diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h index a7827f76bd5f2..354e67b05bd4a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h @@ -145,6 +145,20 @@ class CIRGenTypes { // TODO: convert this comment to account for MLIR's equivalence mlir::Type convertTypeForMem(clang::QualType, bool forBitField = false); + /// Given that T is a scalar type, return the IR type that should + /// be used for load and store operations. For example, this might + /// be i8 for _Bool or i96 for _BitInt(65). The store size of the + /// load/store type (as reported by LLVM's data layout) is always + /// the same as the alloc size of the memory representation type + /// returned by ConvertTypeForMem. + /// + /// As an optimization, if you already know the scalar value type + /// for T (as would be returned by ConvertType), you can pass + /// it as the second argument so that it does not need to be + /// recomputed in common cases where the value type and + /// load/store type are the same. + mlir::Type convertTypeForLoadStore(QualType t); + /// Get the CIR function type for \arg Info. cir::FuncType getFunctionType(const CIRGenFunctionInfo &info); diff --git a/clang/test/CIR/CodeGen/vector-bool.cpp b/clang/test/CIR/CodeGen/vector-bool.cpp new file mode 100644 index 0000000000000..202dbddd344c0 --- /dev/null +++ b/clang/test/CIR/CodeGen/vector-bool.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +typedef bool v8b __attribute__((ext_vector_type(8))); + +void vec_bool_without_padding_needed() { + v8b a; + v8b b; +} + +// CIR: %[[A_ADDR:.*]] = cir.alloca "a" {{.*}} : !cir.ptr<!u8i> +// CIR: %[[B_ADDR:.*]] = cir.alloca "b" {{.*}} : !cir.ptr<!u8i> + +// LLVM: %[[A_ADDR:.*]] = alloca i8, i64 1, align 1 +// LLVM: %[[B_ADDR:.*]] = alloca i8, i64 1, align 1 + +// OGCG: %[[A_ADDR:.*]] = alloca i8, align 1 +// OGCG: %[[B_ADDR:.*]] = alloca i8, align 1 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
