[clang] [CIR] Support load/store for Vector of bools (PR #212146)
Amr Hesham via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 6 09:36:39 PDT 2026
https://github.com/AmrDeveloper updated https://github.com/llvm/llvm-project/pull/212146
>From 49aa8c6c0ca098570cd3df463af75fe30460bf87 Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Thu, 6 Aug 2026 11:02:11 +0200
Subject: [PATCH 1/2] [CIR] Support load/store for Vector of bools
---
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 26 +---------
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 32 +++++++++++--
clang/test/CIR/CodeGen/vector-bool.cpp | 47 +++++++++++++++++++
3 files changed, 78 insertions(+), 27 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 eef1cda1c90f9..34c7efc208ca2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -469,18 +469,10 @@ 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)
+ if (clangVecTy->getNumElements() == 3 && !getLangOpts().PreserveVec3Type)
cgm.errorNYI(addr.getPointer().getLoc(),
"emitStoreOfScalar Vec3 & PreserveVec3Type disabled");
}
@@ -707,14 +699,8 @@ LValue CIRGenFunction::emitLValueForFieldInitialization(
mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) {
if (auto *atomicTy = ty->getAs<AtomicType>())
ty = atomicTy->getValueType();
-
- if (ty->isExtVectorBoolType()) {
- cgm.errorNYI("emitToMemory: extVectorBoolType");
- }
-
// Unlike in classic codegen CIR, bools are kept as `cir.bool` and BitInts are
// kept as `cir.int<N>` until further lowering
-
return value;
}
@@ -748,18 +734,10 @@ mlir::Value CIRGenFunction::emitLoadOfScalar(Address addr, bool isVolatile,
// Traditional LLVM codegen handles thread local separately, CIR handles
// as part of getAddrOfGlobalVar (GetGlobalOp).
mlir::Type eltTy = addr.getElementType();
-
if (const auto *clangVecTy = ty->getAs<clang::VectorType>()) {
- if (clangVecTy->isExtVectorBoolType()) {
- cgm.errorNYI(loc, "emitLoadOfScalar: ExtVectorBoolType");
- return nullptr;
- }
-
- const auto vecTy = cast<cir::VectorType>(eltTy);
-
// Handle vectors of size 3 like size 4 for better performance.
assert(!cir::MissingFeatures::cirgenABIInfo());
- if (vecTy.getSize() == 3 && !getLangOpts().PreserveVec3Type)
+ if (clangVecTy->getNumElements() == 3 && !getLangOpts().PreserveVec3Type)
cgm.errorNYI(addr.getPointer().getLoc(),
"emitLoadOfScalar Vec3 & PreserveVec3Type disabled");
}
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 08c2405547dae..8e0ada62a5cb0 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -115,6 +115,13 @@ static mlir::Type convertTypeForMemory(const mlir::TypeConverter &converter,
dataLayout.getTypeSizeInBits(type));
}
+ if (auto vecTy = mlir::dyn_cast<cir::VectorType>(type)) {
+ if (mlir::isa<cir::BoolType>(vecTy.getElementType())) {
+ uint64_t bytePadded = std::max<uint64_t>(vecTy.getSize(), 8);
+ return mlir::IntegerType::get(type.getContext(), bytePadded);
+ }
+ }
+
// _BitInt(N) keeps its literal width as a value but is stored in a padded
// integer iM in memory, the same way bool is i1 as a value and i8 in memory.
// The byte-array storage form for wide split widths is not implemented; a
@@ -194,9 +201,9 @@ lowerCIRVisibilityToLLVMVisibility(cir::VisibilityKind visibilityKind) {
/// the memory represetnation of a CIR type is not equal to its scalar
/// representation.
static mlir::Value emitFromMemory(mlir::ConversionPatternRewriter &rewriter,
+ const mlir::TypeConverter &converter,
mlir::DataLayout const &dataLayout,
cir::LoadOp op, mlir::Value value) {
-
// TODO(cir): Handle other types similarly to clang's codegen EmitFromMemory
if (auto boolTy = mlir::dyn_cast<cir::BoolType>(op.getType())) {
// Create a cast value from specified size in datalayout to i1
@@ -204,6 +211,15 @@ static mlir::Value emitFromMemory(mlir::ConversionPatternRewriter &rewriter,
return createIntCast(rewriter, value, rewriter.getI1Type());
}
+ // Convert the `iN` back to boolean vectors
+ if (auto vecTy = mlir::dyn_cast<cir::VectorType>(op.getType())) {
+ if (mlir::isa<cir::BoolType>(vecTy.getElementType())) {
+ mlir::Type mlirVecTy = converter.convertType(vecTy);
+ return mlir::LLVM::BitcastOp::create(rewriter, value.getLoc(), mlirVecTy,
+ value);
+ }
+ }
+
// Truncate the padded storage integer back to the _BitInt's literal width.
if (auto intTy = mlir::dyn_cast<cir::IntType>(op.getType());
intTy && intTy.isBitInt())
@@ -228,6 +244,16 @@ static mlir::Value emitToMemory(mlir::ConversionPatternRewriter &rewriter,
return createIntCast(rewriter, value, memType);
}
+ // Boolean vectors use `iN` as storage type
+ if (auto vecTy = mlir::dyn_cast<cir::VectorType>(origType)) {
+ if (mlir::isa<cir::BoolType>(vecTy.getElementType())) {
+ uint64_t bytePadded = std::max<uint64_t>(vecTy.getSize(), 8);
+ auto resultTy = mlir::IntegerType::get(origType.getContext(), bytePadded);
+ return mlir::LLVM::BitcastOp::create(rewriter, value.getLoc(), resultTy,
+ value);
+ }
+ }
+
// Sign/zero-extend the _BitInt value to its padded storage integer.
if (auto intTy = mlir::dyn_cast<cir::IntType>(origType);
intTy && intTy.isBitInt())
@@ -2244,8 +2270,8 @@ mlir::LogicalResult CIRToLLVMLoadOpLowering::matchAndRewrite(
newLoad->setAttr("cir.riscv_nontemporal_domain", domain);
// Convert adapted result to its original type if needed.
- mlir::Value result =
- emitFromMemory(rewriter, dataLayout, op, newLoad.getResult());
+ mlir::Value result = emitFromMemory(rewriter, *getTypeConverter(), dataLayout,
+ op, newLoad.getResult());
rewriter.replaceOp(op, result);
assert(!cir::MissingFeatures::opLoadStoreTbaa());
return mlir::LogicalResult::success();
diff --git a/clang/test/CIR/CodeGen/vector-bool.cpp b/clang/test/CIR/CodeGen/vector-bool.cpp
new file mode 100644
index 0000000000000..b5e8b9b3a2c88
--- /dev/null
+++ b/clang/test/CIR/CodeGen/vector-bool.cpp
@@ -0,0 +1,47 @@
+// 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<!cir.vector<8 x !cir.bool>>
+// CIR: %[[B_ADDR:.*]] = cir.alloca "b" {{.*}} : !cir.ptr<!cir.vector<8 x !cir.bool>>
+
+// 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
+
+void vec_bool_load_store_without_padding_needed() {
+ v8b a;
+ v8b b;
+ a = b;
+}
+
+// CIR: %[[A_ADDR:.*]] = cir.alloca "a" {{.*}} : !cir.ptr<!cir.vector<8 x !cir.bool>>
+// CIR: %[[B_ADDR:.*]] = cir.alloca "b" {{.*}} : !cir.ptr<!cir.vector<8 x !cir.bool>>
+// CIR: %[[TMP_B:.*]] = cir.load {{.*}} %[[B_ADDR]] : !cir.ptr<!cir.vector<8 x !cir.bool>>, !cir.vector<8 x !cir.bool>
+// CIR: cir.store {{.*}} %[[TMP_B]], %[[A_ADDR]] : !cir.vector<8 x !cir.bool>, !cir.ptr<!cir.vector<8 x !cir.bool>>
+
+// LLVM: %[[A_ADDR:.*]] = alloca i8, i64 1, align 1
+// LLVM: %[[B_ADDR:.*]] = alloca i8, i64 1, align 1
+// LLVM: %[[TMP_B:.*]] = load i8, ptr %[[B_ADDR]], align 1
+// LLVM: %[[TMP_B_VEC:.*]] = bitcast i8 %[[TMP_B]] to <8 x i1>
+// LLVM: %[[TMP_B_I8:.*]] = bitcast <8 x i1> %[[TMP_B_VEC]] to i8
+// LLVM: store i8 %[[TMP_B_I8]], ptr %[[A_ADDR]], align 1
+
+// OGCG: %[[A_ADDR:.*]] = alloca i8, align 1
+// OGCG: %[[B_ADDR:.*]] = alloca i8, align 1
+// OGCG: %[[TMP_B:.*]] = load i8, ptr %b, align 1
+// OGCG: %[[TMP_B_VEC:.*]] = bitcast i8 %[[TMP_B]] to <8 x i1>
+// OGCG: %[[TMP_B_I8:.*]] = bitcast <8 x i1> %[[TMP_B_VEC]] to i8
+// OGCG: store i8 %[[TMP_B_I8]], ptr %[[A_ADDR]], align 1
>From fbf3ae56ca1856b8016bb2389d5ca231cbdf13eb Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Thu, 6 Aug 2026 18:36:20 +0200
Subject: [PATCH 2/2] Add NYI for constant matrix type
---
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 34c7efc208ca2..562ee0b94da95 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -699,6 +699,12 @@ LValue CIRGenFunction::emitLValueForFieldInitialization(
mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) {
if (auto *atomicTy = ty->getAs<AtomicType>())
ty = atomicTy->getValueType();
+
+ if (ty->isConstantMatrixBoolType()) {
+ cgm.errorNYI("emitToMemory: ConstantMatrixBoolType");
+ return {};
+ }
+
// Unlike in classic codegen CIR, bools are kept as `cir.bool` and BitInts are
// kept as `cir.int<N>` until further lowering
return value;
More information about the cfe-commits
mailing list