From 0f32ec59bdbce01c93196e3426d35857d4a57788 Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Fri, 21 Nov 2025 19:50:18 -0800 Subject: [PATCH 1/4] Update [ghstack-poisoned] --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 58 ++++++++++++---------- clang/lib/CIR/CodeGen/CIRGenExprConst.cpp | 42 ++++++++++++++-- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 35 ++++++++++++- clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 14 +++++- clang/test/CIR/CodeGen/extvector-bool.cpp | 30 +++++++++++ 5 files changed, 143 insertions(+), 36 deletions(-) create mode 100644 clang/test/CIR/CodeGen/extvector-bool.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index adfec6387ca4..f1dbc21bc6a2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -653,18 +653,20 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr, auto eltTy = addr.getElementType(); if (const auto *clangVecTy = ty->getAs()) { - // Boolean vectors use `iN` as storage type. + // Boolean vectors use `iN` as storage type. This is handled by + // convertTypeForMem, which returns an integer type for ExtVectorBoolType. + // Skip vector optimizations for bool vectors. if (clangVecTy->isExtVectorBoolType()) { - llvm_unreachable("isExtVectorBoolType NYI"); - } - - // Handle vectors of size 3 like size 4 for better performance. - const auto vTy = cast(eltTy); - auto newVecTy = - CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts()); + // Storage is already an integer type, nothing special needed + } else { + // Handle vectors of size 3 like size 4 for better performance. + const auto vTy = cast(eltTy); + auto newVecTy = + CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts()); - if (vTy != newVecTy) { - llvm_unreachable("NYI"); + if (vTy != newVecTy) { + llvm_unreachable("NYI"); + } } } @@ -2977,24 +2979,26 @@ mlir::Value CIRGenFunction::emitLoadOfScalar(Address addr, bool isVolatile, auto eltTy = addr.getElementType(); if (const auto *clangVecTy = ty->getAs()) { - // Boolean vectors use `iN` as storage type. + // Boolean vectors use `iN` as storage type. This is handled by + // convertTypeForMem, which returns an integer type for ExtVectorBoolType. + // Skip vector optimizations for bool vectors. if (clangVecTy->isExtVectorBoolType()) { - llvm_unreachable("NYI"); - } - - // Handle vectors of size 3 like size 4 for better performance. - const auto vTy = cast(eltTy); - auto newVecTy = - CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts()); - - if (vTy != newVecTy) { - const Address cast = addr.withElementType(builder, newVecTy); - mlir::Value v = builder.createLoad(loc, cast, isVolatile); - const uint64_t oldNumElements = vTy.getSize(); - SmallVector mask(oldNumElements); - std::iota(mask.begin(), mask.end(), 0); - v = builder.createVecShuffle(loc, v, mask); - return emitFromMemory(v, ty); + // Storage is already an integer type, nothing special needed + } else { + // Handle vectors of size 3 like size 4 for better performance. + const auto vTy = cast(eltTy); + auto newVecTy = + CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts()); + + if (vTy != newVecTy) { + const Address cast = addr.withElementType(builder, newVecTy); + mlir::Value v = builder.createLoad(loc, cast, isVolatile); + const uint64_t oldNumElements = vTy.getSize(); + SmallVector mask(oldNumElements); + std::iota(mask.begin(), mask.end(), 0); + v = builder.createVecShuffle(loc, v, mask); + return emitFromMemory(v, ty); + } } } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp index 0e5a403968f2..d725aa3c5c7a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp @@ -1134,11 +1134,42 @@ class ConstExprEmitter } mlir::Attribute EmitVectorInitialization(InitListExpr *ILE, QualType T) { - cir::VectorType VecTy = mlir::cast(CGM.convertType(T)); - unsigned NumElements = VecTy.getSize(); + auto *VecTy = T->castAs(); + + // ExtVectorBoolType uses integer storage, not vector type + if (VecTy->isExtVectorBoolType()) { + // For ExtVectorBoolType, the storage is an integer type + // Compute the value by packing bools into an integer + uint64_t numElements = VecTy->getNumElements(); + unsigned numInits = ILE->getNumInits(); + assert(numElements >= numInits && "Too many initializers for a vector"); + + // Create integer value by packing bool elements + uint64_t value = 0; + for (unsigned i = 0; i < numInits; ++i) { + auto Init = ILE->getInit(i); + Expr::EvalResult result; + if (!Init->EvaluateAsRValue(result, CGM.getASTContext())) + return {}; + bool boolVal = result.Val.getInt().getBoolValue(); + if (boolVal) + value |= (uint64_t(1) << i); + } + + // Pad to at least 8 bits + uint64_t storageBits = std::max(numElements, 8); + auto storageTy = + cir::IntType::get(CGM.getBuilder().getContext(), storageBits, + /*isSigned=*/false); + return cir::IntAttr::get(storageTy, value); + } + + // Regular vector type + cir::VectorType CIRVecTy = mlir::cast(CGM.convertType(T)); + unsigned NumElements = CIRVecTy.getSize(); unsigned NumInits = ILE->getNumInits(); assert(NumElements >= NumInits && "Too many initializers for a vector"); - QualType EltTy = T->castAs()->getElementType(); + QualType EltTy = VecTy->getElementType(); SmallVector Elts; // Process the explicit initializers for (unsigned i = 0; i < NumInits; ++i) { @@ -1149,10 +1180,11 @@ class ConstExprEmitter } // Zero-fill the rest of the vector for (unsigned i = NumInits; i < NumElements; ++i) { - Elts.push_back(CGM.getBuilder().getZeroInitAttr(VecTy.getElementType())); + Elts.push_back( + CGM.getBuilder().getZeroInitAttr(CIRVecTy.getElementType())); } return cir::ConstVectorAttr::get( - VecTy, mlir::ArrayAttr::get(CGM.getBuilder().getContext(), Elts)); + CIRVecTy, mlir::ArrayAttr::get(CGM.getBuilder().getContext(), Elts)); } mlir::Attribute VisitImplicitValueInitExpr(ImplicitValueInitExpr *E, diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index a1adea6cd172..3785b9787423 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -309,8 +309,39 @@ class ScalarExprEmitter : public StmtVisitor { if (E->getBase()->getType()->isVectorType()) { assert(!cir::MissingFeatures::scalableVectors() && "NYI: index into scalable vector"); - // Subscript of vector type. This is handled differently, with a custom - // operation. + + // ExtVectorBoolType uses integer storage, handle it specially + const auto *VecTy = E->getBase() + ->getType() + .getCanonicalType() + ->getAs(); + if (VecTy && VecTy->isExtVectorBoolType()) { + // For ExtVectorBoolType, extract a bit from the integer + mlir::Value IntValue = Visit(E->getBase()); + mlir::Value IndexValue = Visit(E->getIdx()); + + // Extract the bit: (IntValue >> IndexValue) & 1 + auto Loc = CGF.getLoc(E->getSourceRange()); + auto BoolTy = CGF.builder.getBoolTy(); + auto IntTy = IntValue.getType(); + + // Shift right by index: IntValue >> IndexValue + mlir::Value Shifted = + cir::ShiftOp::create(CGF.builder, Loc, IntTy, IntValue, IndexValue, + /*isShiftLeft=*/false); + + // Mask with 1: Shifted & 1 + mlir::Value One = CGF.builder.getConstInt(Loc, IntTy, 1); + mlir::Value Masked = cir::BinOp::create( + CGF.builder, Loc, IntTy, cir::BinOpKind::And, Shifted, One); + + // Convert to bool: Masked != 0 + mlir::Value Zero = CGF.builder.getConstInt(Loc, IntTy, 0); + return cir::CmpOp::create(CGF.builder, Loc, BoolTy, cir::CmpOpKind::ne, + Masked, Zero); + } + + // Regular vector subscript mlir::Value VecValue = Visit(E->getBase()); mlir::Value IndexValue = Visit(E->getIdx()); return cir::VecExtractOp::create(CGF.getBuilder(), diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index 4de89fc7081a..72063b913ea6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -670,8 +670,18 @@ mlir::Type CIRGenTypes::convertType(QualType T) { case Type::ExtVector: case Type::Vector: { const VectorType *V = cast(Ty); - auto ElementType = convertTypeForMem(V->getElementType()); - ResultType = cir::VectorType::get(ElementType, V->getNumElements()); + // Boolean vectors use an integer as storage type, matching traditional + // CodeGen. For N bool elements, storage is iM where M = max(N, 8). + if (V->isExtVectorBoolType()) { + uint64_t numElements = V->getNumElements(); + // Pad to at least one byte (8 bits) + uint64_t storageBits = std::max(numElements, 8); + ResultType = cir::IntType::get(Builder.getContext(), storageBits, + /*isSigned=*/false); + } else { + auto ElementType = convertTypeForMem(V->getElementType()); + ResultType = cir::VectorType::get(ElementType, V->getNumElements()); + } break; } case Type::ConstantMatrix: { diff --git a/clang/test/CIR/CodeGen/extvector-bool.cpp b/clang/test/CIR/CodeGen/extvector-bool.cpp new file mode 100644 index 000000000000..22f6e42bc31d --- /dev/null +++ b/clang/test/CIR/CodeGen/extvector-bool.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +// Test basic ext_vector_type with bool elements +typedef bool bool4 __attribute__((ext_vector_type(4))); + +// CHECK-LABEL: cir.func {{.*}}@_Z10test_basicv +void test_basic() { + // CHECK: %[[ALLOCA:.*]] = cir.alloca !u8i, !cir.ptr, ["v" + bool4 v = {true, false, true, false}; + // CHECK: %[[CONST:.*]] = cir.const #cir.int<5> : !u8i + // CHECK: cir.store {{.*}} %[[CONST]], %[[ALLOCA]] +} + +// CHECK-LABEL: cir.func {{.*}}@_Z14test_subscriptv +void test_subscript() { + bool4 v = {true, false, true, false}; + // CHECK: %[[LOAD:.*]] = cir.load{{.*}}!u8i + // CHECK: %[[IDX:.*]] = cir.const #cir.int<2> + // CHECK: %[[SHIFT:.*]] = cir.shift(right, %[[LOAD]]{{.*}}, %[[IDX]] + // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + // CHECK: cir.cmp(ne,{{.*}}){{.*}}!cir.bool + bool b = v[2]; +} + +// CHECK-LABEL: cir.func {{.*}}@_Z8test_ops +void test_ops(bool4 a, bool4 b) { + // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + bool4 c = a & b; +} From c362cbc50e59f9963287a87fe15744e1d49785d8 Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Fri, 21 Nov 2025 20:11:08 -0800 Subject: [PATCH 2/4] Update [ghstack-poisoned] --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 29 +++-- clang/test/CIR/CodeGen/extvector-bool.cpp | 125 ++++++++++++++++++++++ 2 files changed, 148 insertions(+), 6 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index f1dbc21bc6a2..28a988cc08a5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -634,6 +634,14 @@ CIRGenCallee CIRGenFunction::emitCallee(const clang::Expr *E) { mlir::Value CIRGenFunction::emitToMemory(mlir::Value Value, QualType Ty) { // Bool has a different representation in memory than in registers. + + // ExtVectorBoolType: In ClangIR, ExtVectorBoolType is always represented + // as an integer type (!cir.int) throughout the IR, including both + // in registers and in memory. This differs from traditional CodeGen where + // it may exist as a vector type that needs conversion to integer for storage. + // Since we use integer representation consistently, no conversion is needed. + // See CIRGenTypes.cpp:675-683 for the type conversion logic. + return Value; } @@ -653,9 +661,10 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr, auto eltTy = addr.getElementType(); if (const auto *clangVecTy = ty->getAs()) { - // Boolean vectors use `iN` as storage type. This is handled by - // convertTypeForMem, which returns an integer type for ExtVectorBoolType. - // Skip vector optimizations for bool vectors. + // Boolean vectors use `iN` as storage type. The type conversion in + // CIRGenTypes::convertType (lines 675-683) returns an integer type for + // ExtVectorBoolType, so eltTy is already an integer. Skip vector + // optimizations for bool vectors since they're not actually vectors in CIR. if (clangVecTy->isExtVectorBoolType()) { // Storage is already an integer type, nothing special needed } else { @@ -2958,6 +2967,13 @@ mlir::Value CIRGenFunction::emitFromMemory(mlir::Value Value, QualType Ty) { llvm_unreachable("NIY"); } + // ExtVectorBoolType: In ClangIR, ExtVectorBoolType is always represented + // as an integer type (!cir.int) throughout the IR, including both + // in registers and in memory. This differs from traditional CodeGen where + // it may need truncation from storage type to value type. Since we use + // integer representation consistently, no conversion is needed. + // See CIRGenTypes.cpp:675-683 for the type conversion logic. + return Value; } @@ -2979,9 +2995,10 @@ mlir::Value CIRGenFunction::emitLoadOfScalar(Address addr, bool isVolatile, auto eltTy = addr.getElementType(); if (const auto *clangVecTy = ty->getAs()) { - // Boolean vectors use `iN` as storage type. This is handled by - // convertTypeForMem, which returns an integer type for ExtVectorBoolType. - // Skip vector optimizations for bool vectors. + // Boolean vectors use `iN` as storage type. The type conversion in + // CIRGenTypes::convertType (lines 675-683) returns an integer type for + // ExtVectorBoolType, so eltTy is already an integer. Skip vector + // optimizations for bool vectors since they're not actually vectors in CIR. if (clangVecTy->isExtVectorBoolType()) { // Storage is already an integer type, nothing special needed } else { diff --git a/clang/test/CIR/CodeGen/extvector-bool.cpp b/clang/test/CIR/CodeGen/extvector-bool.cpp index 22f6e42bc31d..a5653f93aa03 100644 --- a/clang/test/CIR/CodeGen/extvector-bool.cpp +++ b/clang/test/CIR/CodeGen/extvector-bool.cpp @@ -3,6 +3,8 @@ // Test basic ext_vector_type with bool elements typedef bool bool4 __attribute__((ext_vector_type(4))); +typedef bool bool2 __attribute__((ext_vector_type(2))); +typedef bool bool16 __attribute__((ext_vector_type(16))); // CHECK-LABEL: cir.func {{.*}}@_Z10test_basicv void test_basic() { @@ -28,3 +30,126 @@ void test_ops(bool4 a, bool4 b) { // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i bool4 c = a & b; } + +// NOTE: The following operations are not yet fully implemented for +// ExtVectorBoolType and require special handling: +// - Element assignment (v[2] = true): Requires bit manipulation to set/clear individual bits +// - Unary logical NOT (!v): May require special handling beyond bitwise NOT + +// Test bitwise operations +// CHECK-LABEL: cir.func {{.*}}@_Z16test_bitwise_opsv +void test_bitwise_ops() { + bool4 a = {true, false, true, false}; + bool4 b = {false, true, true, false}; + + // Bitwise AND + // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + bool4 c = a & b; + + // Bitwise OR + // CHECK: cir.binop(or,{{.*}}){{.*}}!u8i + bool4 d = a | b; + + // Bitwise XOR + // CHECK: cir.binop(xor,{{.*}}){{.*}}!u8i + bool4 e = a ^ b; +} + +// Test different vector sizes +// CHECK-LABEL: cir.func {{.*}}@_Z17test_vector_sizesv +void test_vector_sizes() { + // bool2 uses u8i (padded to 8 bits minimum) + // CHECK: cir.alloca !u8i, !cir.ptr, ["v2" + bool2 v2 = {true, false}; + // CHECK-DAG: cir.const #cir.int<1> : !u8i + // CHECK-DAG: cir.store{{.*}}!u8i, !cir.ptr + + // bool16 uses u16i + // CHECK-DAG: cir.alloca !u16i, !cir.ptr, ["v16" + bool16 v16 = {true, false, true, false, true, false, true, false, + false, true, false, true, false, true, false, true}; + // CHECK-DAG: cir.const #cir.int<43605> : !u16i + // CHECK-DAG: cir.store{{.*}}!u16i, !cir.ptr +} + +// Test function parameters and returns +// CHECK-LABEL: cir.func {{.*}}@_Z12invert_bool4 +// CHECK-SAME: %arg0: !u8i +// CHECK-SAME: -> !u8i +bool4 invert_bool4(bool4 v) { + // Bitwise NOT + // CHECK: %[[LOAD:.*]] = cir.load{{.*}}!u8i + // CHECK: cir.unary(not, %[[LOAD]]){{.*}}!u8i + return ~v; +} + +// Test all bits set and all bits clear +// CHECK-LABEL: cir.func {{.*}}@_Z15test_edge_casesv +void test_edge_cases() { + // All false (0) + // CHECK-DAG: cir.alloca !u8i, !cir.ptr, ["all_false" + bool4 all_false = {false, false, false, false}; + // CHECK-DAG: cir.const #cir.int<0> : !u8i + // CHECK-DAG: cir.store{{.*}}!u8i, !cir.ptr + + // All true (15 = 0b1111 for 4 bits) + // CHECK-DAG: cir.alloca !u8i, !cir.ptr, ["all_true" + bool4 all_true = {true, true, true, true}; + // CHECK-DAG: cir.const #cir.int<15> : !u8i + // CHECK-DAG: cir.store{{.*}}!u8i, !cir.ptr +} + +// Test subscript with variable index +// CHECK-LABEL: cir.func {{.*}}@_Z23test_variable_subscript +void test_variable_subscript(int idx) { + bool4 v = {true, false, true, false}; + // CHECK: cir.load{{.*}}!u8i + // CHECK: cir.load{{.*}}!s32i + // CHECK: cir.shift(right,{{.*}}){{.*}}!u8i + // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + bool b = v[idx]; +} + +// Test initialization with all same value +// CHECK-LABEL: cir.func {{.*}}@_Z18test_same_init_valv +void test_same_init_val() { + // Initialize all elements to true using splat + // CHECK: cir.alloca !u8i, !cir.ptr, ["v" + bool4 v = {true, true, true, true}; + // CHECK: cir.const #cir.int<15> : !u8i + // CHECK: cir.store{{.*}}!u8i, !cir.ptr +} + +// Test multiple operations in sequence +// CHECK-LABEL: cir.func {{.*}}@_Z17test_multiple_opsv +void test_multiple_ops() { + bool4 a = {true, false, true, false}; + bool4 b = {false, true, true, false}; + + // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + bool4 c = a & b; + // CHECK: cir.binop(or,{{.*}}){{.*}}!u8i + bool4 d = c | b; + // CHECK: cir.unary(not,{{.*}}){{.*}}!u8i + bool4 e = ~d; +} + +// Test reading specific elements +// CHECK-LABEL: cir.func {{.*}}@_Z18test_read_elementsv +void test_read_elements() { + bool4 v = {true, false, true, false}; + + // Read element 0 + // CHECK: cir.load{{.*}}!u8i + // CHECK: cir.const #cir.int<0> + // CHECK: cir.shift(right,{{.*}}){{.*}}!u8i + // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + bool e0 = v[0]; + + // Read element 3 + // CHECK: cir.load{{.*}}!u8i + // CHECK: cir.const #cir.int<3> + // CHECK: cir.shift(right,{{.*}}){{.*}}!u8i + // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + bool e3 = v[3]; +} From 76a3f13687b55a6a2a42482910b5b99969651b8a Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Fri, 21 Nov 2025 21:17:55 -0800 Subject: [PATCH 3/4] Update [ghstack-poisoned] --- clang/test/CIR/CodeGen/extvector-bool.cpp | 172 ++++++++++++++-------- 1 file changed, 114 insertions(+), 58 deletions(-) diff --git a/clang/test/CIR/CodeGen/extvector-bool.cpp b/clang/test/CIR/CodeGen/extvector-bool.cpp index a5653f93aa03..78e6c29d87d4 100644 --- a/clang/test/CIR/CodeGen/extvector-bool.cpp +++ b/clang/test/CIR/CodeGen/extvector-bool.cpp @@ -1,34 +1,62 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir -// RUN: FileCheck --input-file=%t.cir %s +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.og.ll +// RUN: FileCheck --input-file=%t.og.ll %s --check-prefix=OGCG // Test basic ext_vector_type with bool elements typedef bool bool4 __attribute__((ext_vector_type(4))); typedef bool bool2 __attribute__((ext_vector_type(2))); typedef bool bool16 __attribute__((ext_vector_type(16))); -// CHECK-LABEL: cir.func {{.*}}@_Z10test_basicv +// CIR-LABEL: cir.func {{.*}}@_Z10test_basicv void test_basic() { - // CHECK: %[[ALLOCA:.*]] = cir.alloca !u8i, !cir.ptr, ["v" + // CIR: %[[ALLOCA:.*]] = cir.alloca !u8i, !cir.ptr, ["v" bool4 v = {true, false, true, false}; - // CHECK: %[[CONST:.*]] = cir.const #cir.int<5> : !u8i - // CHECK: cir.store {{.*}} %[[CONST]], %[[ALLOCA]] + // CIR: %[[CONST:.*]] = cir.const #cir.int<5> : !u8i + // CIR: cir.store {{.*}} %[[CONST]], %[[ALLOCA]] + + // LLVM-LABEL: define {{.*}}@_Z10test_basicv + // LLVM: alloca i8 + // LLVM: store i8 5 + + // OGCG-LABEL: define {{.*}}@_Z10test_basicv + // OGCG: alloca i8 + // OGCG: store i8 } -// CHECK-LABEL: cir.func {{.*}}@_Z14test_subscriptv +// CIR-LABEL: cir.func {{.*}}@_Z14test_subscriptv void test_subscript() { bool4 v = {true, false, true, false}; - // CHECK: %[[LOAD:.*]] = cir.load{{.*}}!u8i - // CHECK: %[[IDX:.*]] = cir.const #cir.int<2> - // CHECK: %[[SHIFT:.*]] = cir.shift(right, %[[LOAD]]{{.*}}, %[[IDX]] - // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i - // CHECK: cir.cmp(ne,{{.*}}){{.*}}!cir.bool + // CIR: %[[LOAD:.*]] = cir.load{{.*}}!u8i + // CIR: %[[IDX:.*]] = cir.const #cir.int<2> + // CIR: %[[SHIFT:.*]] = cir.shift(right, %[[LOAD]]{{.*}}, %[[IDX]] + // CIR: cir.binop(and,{{.*}}){{.*}}!u8i + // CIR: cir.cmp(ne,{{.*}}){{.*}}!cir.bool bool b = v[2]; + + // LLVM-LABEL: define {{.*}}@_Z14test_subscriptv + // LLVM: lshr i8 + // LLVM: and i8 + // LLVM: icmp ne i8 + + // OGCG-LABEL: define {{.*}}@_Z14test_subscriptv + // OGCG: extractelement + // OGCG: zext i1 } -// CHECK-LABEL: cir.func {{.*}}@_Z8test_ops +// CIR-LABEL: cir.func {{.*}}@_Z8test_ops void test_ops(bool4 a, bool4 b) { - // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + // CIR: cir.binop(and,{{.*}}){{.*}}!u8i bool4 c = a & b; + + // LLVM-LABEL: define {{.*}}@_Z8test_opsDv4_bS_ + // LLVM: and i8 + + // OGCG-LABEL: define {{.*}}@_Z8test_opsDv4_bS_ + // OGCG: shufflevector + // OGCG: and <4 x i1> } // NOTE: The following operations are not yet fully implemented for @@ -37,119 +65,147 @@ void test_ops(bool4 a, bool4 b) { // - Unary logical NOT (!v): May require special handling beyond bitwise NOT // Test bitwise operations -// CHECK-LABEL: cir.func {{.*}}@_Z16test_bitwise_opsv +// CIR-LABEL: cir.func {{.*}}@_Z16test_bitwise_opsv void test_bitwise_ops() { bool4 a = {true, false, true, false}; bool4 b = {false, true, true, false}; // Bitwise AND - // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + // CIR: cir.binop(and,{{.*}}){{.*}}!u8i bool4 c = a & b; // Bitwise OR - // CHECK: cir.binop(or,{{.*}}){{.*}}!u8i + // CIR: cir.binop(or,{{.*}}){{.*}}!u8i bool4 d = a | b; // Bitwise XOR - // CHECK: cir.binop(xor,{{.*}}){{.*}}!u8i + // CIR: cir.binop(xor,{{.*}}){{.*}}!u8i bool4 e = a ^ b; + + // LLVM-LABEL: define {{.*}}@_Z16test_bitwise_opsv + // LLVM: and i8 + // LLVM: or i8 + // LLVM: xor i8 + + // OGCG-LABEL: define {{.*}}@_Z16test_bitwise_opsv + // OGCG: and <4 x i1> + // OGCG: or <4 x i1> + // OGCG: xor <4 x i1> } // Test different vector sizes -// CHECK-LABEL: cir.func {{.*}}@_Z17test_vector_sizesv +// CIR-LABEL: cir.func {{.*}}@_Z17test_vector_sizesv void test_vector_sizes() { // bool2 uses u8i (padded to 8 bits minimum) - // CHECK: cir.alloca !u8i, !cir.ptr, ["v2" + // CIR: cir.alloca !u8i, !cir.ptr, ["v2" bool2 v2 = {true, false}; - // CHECK-DAG: cir.const #cir.int<1> : !u8i - // CHECK-DAG: cir.store{{.*}}!u8i, !cir.ptr + // CIR-DAG: cir.const #cir.int<1> : !u8i + // CIR-DAG: cir.store{{.*}}!u8i, !cir.ptr // bool16 uses u16i - // CHECK-DAG: cir.alloca !u16i, !cir.ptr, ["v16" + // CIR-DAG: cir.alloca !u16i, !cir.ptr, ["v16" bool16 v16 = {true, false, true, false, true, false, true, false, false, true, false, true, false, true, false, true}; - // CHECK-DAG: cir.const #cir.int<43605> : !u16i - // CHECK-DAG: cir.store{{.*}}!u16i, !cir.ptr + // CIR-DAG: cir.const #cir.int<43605> : !u16i + // CIR-DAG: cir.store{{.*}}!u16i, !cir.ptr + + // LLVM-LABEL: define {{.*}}@_Z17test_vector_sizesv + // LLVM-DAG: alloca i8 + // LLVM-DAG: store i8 1 + // LLVM-DAG: alloca i16 + // LLVM-DAG: store i16 + + // OGCG-LABEL: define {{.*}}@_Z17test_vector_sizesv + // OGCG-DAG: alloca i8 + // OGCG-DAG: store i8{{.*}}, ptr % + // OGCG-DAG: alloca i16 + // OGCG-DAG: store i16 } // Test function parameters and returns -// CHECK-LABEL: cir.func {{.*}}@_Z12invert_bool4 -// CHECK-SAME: %arg0: !u8i -// CHECK-SAME: -> !u8i +// CIR-LABEL: cir.func {{.*}}@_Z12invert_bool4 +// CIR-SAME: %arg0: !u8i +// CIR-SAME: -> !u8i bool4 invert_bool4(bool4 v) { // Bitwise NOT - // CHECK: %[[LOAD:.*]] = cir.load{{.*}}!u8i - // CHECK: cir.unary(not, %[[LOAD]]){{.*}}!u8i + // CIR: %[[LOAD:.*]] = cir.load{{.*}}!u8i + // CIR: cir.unary(not, %[[LOAD]]){{.*}}!u8i return ~v; + + // LLVM-LABEL: define {{.*}}@_Z12invert_bool4Dv4_b + // LLVM: xor i8 + + // OGCG-LABEL: define {{.*}}@_Z12invert_bool4Dv4_b + // OGCG: xor <4 x i1> } // Test all bits set and all bits clear -// CHECK-LABEL: cir.func {{.*}}@_Z15test_edge_casesv +// CIR-LABEL: cir.func {{.*}}@_Z15test_edge_casesv void test_edge_cases() { // All false (0) - // CHECK-DAG: cir.alloca !u8i, !cir.ptr, ["all_false" + // CIR-DAG: cir.alloca !u8i, !cir.ptr, ["all_false" bool4 all_false = {false, false, false, false}; - // CHECK-DAG: cir.const #cir.int<0> : !u8i - // CHECK-DAG: cir.store{{.*}}!u8i, !cir.ptr + // CIR-DAG: cir.const #cir.int<0> : !u8i + // CIR-DAG: cir.store{{.*}}!u8i, !cir.ptr // All true (15 = 0b1111 for 4 bits) - // CHECK-DAG: cir.alloca !u8i, !cir.ptr, ["all_true" + // CIR-DAG: cir.alloca !u8i, !cir.ptr, ["all_true" bool4 all_true = {true, true, true, true}; - // CHECK-DAG: cir.const #cir.int<15> : !u8i - // CHECK-DAG: cir.store{{.*}}!u8i, !cir.ptr + // CIR-DAG: cir.const #cir.int<15> : !u8i + // CIR-DAG: cir.store{{.*}}!u8i, !cir.ptr } // Test subscript with variable index -// CHECK-LABEL: cir.func {{.*}}@_Z23test_variable_subscript +// CIR-LABEL: cir.func {{.*}}@_Z23test_variable_subscript void test_variable_subscript(int idx) { bool4 v = {true, false, true, false}; - // CHECK: cir.load{{.*}}!u8i - // CHECK: cir.load{{.*}}!s32i - // CHECK: cir.shift(right,{{.*}}){{.*}}!u8i - // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + // CIR: cir.load{{.*}}!u8i + // CIR: cir.load{{.*}}!s32i + // CIR: cir.shift(right,{{.*}}){{.*}}!u8i + // CIR: cir.binop(and,{{.*}}){{.*}}!u8i bool b = v[idx]; } // Test initialization with all same value -// CHECK-LABEL: cir.func {{.*}}@_Z18test_same_init_valv +// CIR-LABEL: cir.func {{.*}}@_Z18test_same_init_valv void test_same_init_val() { // Initialize all elements to true using splat - // CHECK: cir.alloca !u8i, !cir.ptr, ["v" + // CIR: cir.alloca !u8i, !cir.ptr, ["v" bool4 v = {true, true, true, true}; - // CHECK: cir.const #cir.int<15> : !u8i - // CHECK: cir.store{{.*}}!u8i, !cir.ptr + // CIR: cir.const #cir.int<15> : !u8i + // CIR: cir.store{{.*}}!u8i, !cir.ptr } // Test multiple operations in sequence -// CHECK-LABEL: cir.func {{.*}}@_Z17test_multiple_opsv +// CIR-LABEL: cir.func {{.*}}@_Z17test_multiple_opsv void test_multiple_ops() { bool4 a = {true, false, true, false}; bool4 b = {false, true, true, false}; - // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + // CIR: cir.binop(and,{{.*}}){{.*}}!u8i bool4 c = a & b; - // CHECK: cir.binop(or,{{.*}}){{.*}}!u8i + // CIR: cir.binop(or,{{.*}}){{.*}}!u8i bool4 d = c | b; - // CHECK: cir.unary(not,{{.*}}){{.*}}!u8i + // CIR: cir.unary(not,{{.*}}){{.*}}!u8i bool4 e = ~d; } // Test reading specific elements -// CHECK-LABEL: cir.func {{.*}}@_Z18test_read_elementsv +// CIR-LABEL: cir.func {{.*}}@_Z18test_read_elementsv void test_read_elements() { bool4 v = {true, false, true, false}; // Read element 0 - // CHECK: cir.load{{.*}}!u8i - // CHECK: cir.const #cir.int<0> - // CHECK: cir.shift(right,{{.*}}){{.*}}!u8i - // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + // CIR: cir.load{{.*}}!u8i + // CIR: cir.const #cir.int<0> + // CIR: cir.shift(right,{{.*}}){{.*}}!u8i + // CIR: cir.binop(and,{{.*}}){{.*}}!u8i bool e0 = v[0]; // Read element 3 - // CHECK: cir.load{{.*}}!u8i - // CHECK: cir.const #cir.int<3> - // CHECK: cir.shift(right,{{.*}}){{.*}}!u8i - // CHECK: cir.binop(and,{{.*}}){{.*}}!u8i + // CIR: cir.load{{.*}}!u8i + // CIR: cir.const #cir.int<3> + // CIR: cir.shift(right,{{.*}}){{.*}}!u8i + // CIR: cir.binop(and,{{.*}}){{.*}}!u8i bool e3 = v[3]; } From 7a87de6cec85fb77a21f9e7b3d8e53550baa8282 Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Fri, 21 Nov 2025 21:44:49 -0800 Subject: [PATCH 4/4] Update [ghstack-poisoned] --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 31 ++++++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 18 +++++++++++++ clang/test/CIR/CodeGen/extvector-bool.cpp | 5 ---- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 28a988cc08a5..4d8553c89ad4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -879,6 +879,16 @@ void CIRGenFunction::emitStoreThroughLValue(RValue Src, LValue Dst, bool isInit) { if (!Dst.isSimple()) { if (Dst.isVectorElt()) { + // Check if this is an ExtVectorBoolType element assignment + QualType vectorType = Dst.getType(); + if (const auto *vecTy = vectorType->getAs()) { + if (vecTy->isExtVectorBoolType()) { + llvm_unreachable( + "NYI: ExtVectorBoolType element assignment (requires bit " + "manipulation to set/clear individual bits in integer storage)"); + } + } + // Read/modify/write the vector, inserting the new element mlir::Location loc = Dst.getVectorPointer().getLoc(); mlir::Value Vector = builder.createLoad(loc, Dst.getVectorAddress()); @@ -2746,6 +2756,8 @@ LValue CIRGenFunction::emitLValue(const Expr *E) { return emitStmtExprLValue(cast(E)); case Expr::ChooseExprClass: return emitLValue(cast(E)->getChosenSubExpr()); + case Expr::CXXTypeidExprClass: + return emitCXXTypeidLValue(cast(E)); } llvm_unreachable("NYI"); @@ -3466,3 +3478,22 @@ RValue CIRGenFunction::emitPseudoObjectRValue(const PseudoObjectExpr *expr, LValue CIRGenFunction::emitPseudoObjectLValue(const PseudoObjectExpr *expr) { return emitPseudoObjectExpr(*this, expr, true, AggValueSlot::ignored()).lv; } + +LValue CIRGenFunction::emitCXXTypeidLValue(const CXXTypeidExpr *E) { + // Emit the typeid expression, which returns a pointer to the RTTI descriptor. + mlir::Value typeInfoPtr = emitCXXTypeidExpr(E); + + // Cast the pointer to the actual type_info type for proper type safety. + auto typeInfoTy = convertTypeForMem(E->getType()); + auto typeInfoPtrTy = builder.getPointerTo(typeInfoTy); + typeInfoPtr = builder.createBitcast(getLoc(E->getSourceRange()), typeInfoPtr, + typeInfoPtrTy); + + // Create an LValue from the pointer with natural alignment. + // We use getTypeAlignInChars() which returns the natural alignment for the + // type_info type, matching traditional CodeGen's getNaturalTypeAlignment(). + Address addr(typeInfoPtr, typeInfoTy, + getContext().getTypeAlignInChars(E->getType())); + + return makeAddrLValue(addr, E->getType(), AlignmentSource::Decl); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 3785b9787423..53deeb2c40ab 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -1007,6 +1007,15 @@ class ScalarExprEmitter : public StmtVisitor { mlir::Value RHS = BOInfo.RHS; if (LHSTy->isVectorType()) { + // Check for ExtVectorBoolType which uses integer storage, not vector + if (const auto *vecTy = LHSTy->getAs()) { + if (vecTy->isExtVectorBoolType()) { + llvm_unreachable( + "NYI: ExtVectorBoolType comparison operations (requires " + "element-wise comparison on packed integer representation)"); + } + } + if (!E->getType()->isVectorType()) { // If AltiVec, the comparison results in a numeric type, so we use // intrinsics comparing vectors and giving 0 or 1 as a result @@ -2107,6 +2116,15 @@ mlir::Value ScalarExprEmitter::VisitUnaryLNot(const UnaryOperator *E) { if (E->getType()->isVectorType() && E->getType()->castAs()->getVectorKind() == VectorKind::Generic) { + // Check for ExtVectorBoolType which uses integer storage, not vector + if (const auto *vecTy = E->getType()->getAs()) { + if (vecTy->isExtVectorBoolType()) { + llvm_unreachable( + "NYI: ExtVectorBoolType logical NOT (requires handling padding " + "bits in integer storage to ensure correct element-wise negation)"); + } + } + mlir::Value oper = Visit(E->getSubExpr()); mlir::Location loc = CGF.getLoc(E->getExprLoc()); auto operVecTy = mlir::cast(oper.getType()); diff --git a/clang/test/CIR/CodeGen/extvector-bool.cpp b/clang/test/CIR/CodeGen/extvector-bool.cpp index 78e6c29d87d4..04a2a8c20495 100644 --- a/clang/test/CIR/CodeGen/extvector-bool.cpp +++ b/clang/test/CIR/CodeGen/extvector-bool.cpp @@ -59,11 +59,6 @@ void test_ops(bool4 a, bool4 b) { // OGCG: and <4 x i1> } -// NOTE: The following operations are not yet fully implemented for -// ExtVectorBoolType and require special handling: -// - Element assignment (v[2] = true): Requires bit manipulation to set/clear individual bits -// - Unary logical NOT (!v): May require special handling beyond bitwise NOT - // Test bitwise operations // CIR-LABEL: cir.func {{.*}}@_Z16test_bitwise_opsv void test_bitwise_ops() {