[clang] [CIR] Support load/store for Vector of bools (PR #212146)
Amr Hesham via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 26 12:51:52 PDT 2026
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 <amr96 at programmer.net>
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
More information about the cfe-commits
mailing list