Skip to content

Commit a4b68c4

Browse files
committed
[CIR] Add support for ExtVector Bool Type
Implements support for ext_vector_type with bool elements. Bool vectors are represented as integers in CIR (e.g., bool4 uses !cir.int<u, 8>), matching traditional CodeGen's approach. Key changes: - CIRGenTypes: Convert ExtVectorBoolType to integer storage (iN where N = max(num_elements, 8)) - CIRGenExprConst: Pack bool elements into integer bits during constant initialization - CIRGenExprScalar: Handle subscript access by extracting bits from integer - CIRGenExpr: Skip vector optimizations for ExtVectorBoolType in load/store paths Tests added for basic initialization, subscript access, and bitwise operations. ghstack-source-id: 677025c Pull-Request: #1998
1 parent 1af4960 commit a4b68c4

File tree

5 files changed

+364
-36
lines changed

5 files changed

+364
-36
lines changed

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,14 @@ CIRGenCallee CIRGenFunction::emitCallee(const clang::Expr *E) {
635635

636636
mlir::Value CIRGenFunction::emitToMemory(mlir::Value Value, QualType Ty) {
637637
// Bool has a different representation in memory than in registers.
638+
639+
// ExtVectorBoolType: In ClangIR, ExtVectorBoolType is always represented
640+
// as an integer type (!cir.int<u, N>) throughout the IR, including both
641+
// in registers and in memory. This differs from traditional CodeGen where
642+
// it may exist as a vector type that needs conversion to integer for storage.
643+
// Since we use integer representation consistently, no conversion is needed.
644+
// See CIRGenTypes.cpp:675-683 for the type conversion logic.
645+
638646
return Value;
639647
}
640648

@@ -654,18 +662,21 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
654662

655663
auto eltTy = addr.getElementType();
656664
if (const auto *clangVecTy = ty->getAs<clang::VectorType>()) {
657-
// Boolean vectors use `iN` as storage type.
665+
// Boolean vectors use `iN` as storage type. The type conversion in
666+
// CIRGenTypes::convertType (lines 675-683) returns an integer type for
667+
// ExtVectorBoolType, so eltTy is already an integer. Skip vector
668+
// optimizations for bool vectors since they're not actually vectors in CIR.
658669
if (clangVecTy->isExtVectorBoolType()) {
659-
llvm_unreachable("isExtVectorBoolType NYI");
660-
}
661-
662-
// Handle vectors of size 3 like size 4 for better performance.
663-
const auto vTy = cast<cir::VectorType>(eltTy);
664-
auto newVecTy =
665-
CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts());
670+
// Storage is already an integer type, nothing special needed
671+
} else {
672+
// Handle vectors of size 3 like size 4 for better performance.
673+
const auto vTy = cast<cir::VectorType>(eltTy);
674+
auto newVecTy =
675+
CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts());
666676

667-
if (vTy != newVecTy) {
668-
llvm_unreachable("NYI");
677+
if (vTy != newVecTy) {
678+
llvm_unreachable("NYI");
679+
}
669680
}
670681
}
671682

@@ -869,6 +880,16 @@ void CIRGenFunction::emitStoreThroughLValue(RValue Src, LValue Dst,
869880
bool isInit) {
870881
if (!Dst.isSimple()) {
871882
if (Dst.isVectorElt()) {
883+
// Check if this is an ExtVectorBoolType element assignment
884+
QualType vectorType = Dst.getType();
885+
if (const auto *vecTy = vectorType->getAs<clang::VectorType>()) {
886+
if (vecTy->isExtVectorBoolType()) {
887+
llvm_unreachable(
888+
"NYI: ExtVectorBoolType element assignment (requires bit "
889+
"manipulation to set/clear individual bits in integer storage)");
890+
}
891+
}
892+
872893
// Read/modify/write the vector, inserting the new element
873894
mlir::Location loc = Dst.getVectorPointer().getLoc();
874895
mlir::Value Vector = builder.createLoad(loc, Dst.getVectorAddress());
@@ -3048,6 +3069,13 @@ mlir::Value CIRGenFunction::emitFromMemory(mlir::Value Value, QualType Ty) {
30483069
llvm_unreachable("NIY");
30493070
}
30503071

3072+
// ExtVectorBoolType: In ClangIR, ExtVectorBoolType is always represented
3073+
// as an integer type (!cir.int<u, N>) throughout the IR, including both
3074+
// in registers and in memory. This differs from traditional CodeGen where
3075+
// it may need truncation from storage type to value type. Since we use
3076+
// integer representation consistently, no conversion is needed.
3077+
// See CIRGenTypes.cpp:675-683 for the type conversion logic.
3078+
30513079
return Value;
30523080
}
30533081

@@ -3069,24 +3097,27 @@ mlir::Value CIRGenFunction::emitLoadOfScalar(Address addr, bool isVolatile,
30693097
auto eltTy = addr.getElementType();
30703098

30713099
if (const auto *clangVecTy = ty->getAs<clang::VectorType>()) {
3072-
// Boolean vectors use `iN` as storage type.
3100+
// Boolean vectors use `iN` as storage type. The type conversion in
3101+
// CIRGenTypes::convertType (lines 675-683) returns an integer type for
3102+
// ExtVectorBoolType, so eltTy is already an integer. Skip vector
3103+
// optimizations for bool vectors since they're not actually vectors in CIR.
30733104
if (clangVecTy->isExtVectorBoolType()) {
3074-
llvm_unreachable("NYI");
3075-
}
3076-
3077-
// Handle vectors of size 3 like size 4 for better performance.
3078-
const auto vTy = cast<cir::VectorType>(eltTy);
3079-
auto newVecTy =
3080-
CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts());
3081-
3082-
if (vTy != newVecTy) {
3083-
const Address cast = addr.withElementType(builder, newVecTy);
3084-
mlir::Value v = builder.createLoad(loc, cast, isVolatile);
3085-
const uint64_t oldNumElements = vTy.getSize();
3086-
SmallVector<int64_t, 16> mask(oldNumElements);
3087-
std::iota(mask.begin(), mask.end(), 0);
3088-
v = builder.createVecShuffle(loc, v, mask);
3089-
return emitFromMemory(v, ty);
3105+
// Storage is already an integer type, nothing special needed
3106+
} else {
3107+
// Handle vectors of size 3 like size 4 for better performance.
3108+
const auto vTy = cast<cir::VectorType>(eltTy);
3109+
auto newVecTy =
3110+
CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts());
3111+
3112+
if (vTy != newVecTy) {
3113+
const Address cast = addr.withElementType(builder, newVecTy);
3114+
mlir::Value v = builder.createLoad(loc, cast, isVolatile);
3115+
const uint64_t oldNumElements = vTy.getSize();
3116+
SmallVector<int64_t, 16> mask(oldNumElements);
3117+
std::iota(mask.begin(), mask.end(), 0);
3118+
v = builder.createVecShuffle(loc, v, mask);
3119+
return emitFromMemory(v, ty);
3120+
}
30903121
}
30913122
}
30923123

clang/lib/CIR/CodeGen/CIRGenExprConst.cpp

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,11 +1134,42 @@ class ConstExprEmitter
11341134
}
11351135

11361136
mlir::Attribute EmitVectorInitialization(InitListExpr *ILE, QualType T) {
1137-
cir::VectorType VecTy = mlir::cast<cir::VectorType>(CGM.convertType(T));
1138-
unsigned NumElements = VecTy.getSize();
1137+
auto *VecTy = T->castAs<VectorType>();
1138+
1139+
// ExtVectorBoolType uses integer storage, not vector type
1140+
if (VecTy->isExtVectorBoolType()) {
1141+
// For ExtVectorBoolType, the storage is an integer type
1142+
// Compute the value by packing bools into an integer
1143+
uint64_t numElements = VecTy->getNumElements();
1144+
unsigned numInits = ILE->getNumInits();
1145+
assert(numElements >= numInits && "Too many initializers for a vector");
1146+
1147+
// Create integer value by packing bool elements
1148+
uint64_t value = 0;
1149+
for (unsigned i = 0; i < numInits; ++i) {
1150+
auto Init = ILE->getInit(i);
1151+
Expr::EvalResult result;
1152+
if (!Init->EvaluateAsRValue(result, CGM.getASTContext()))
1153+
return {};
1154+
bool boolVal = result.Val.getInt().getBoolValue();
1155+
if (boolVal)
1156+
value |= (uint64_t(1) << i);
1157+
}
1158+
1159+
// Pad to at least 8 bits
1160+
uint64_t storageBits = std::max<uint64_t>(numElements, 8);
1161+
auto storageTy =
1162+
cir::IntType::get(CGM.getBuilder().getContext(), storageBits,
1163+
/*isSigned=*/false);
1164+
return cir::IntAttr::get(storageTy, value);
1165+
}
1166+
1167+
// Regular vector type
1168+
cir::VectorType CIRVecTy = mlir::cast<cir::VectorType>(CGM.convertType(T));
1169+
unsigned NumElements = CIRVecTy.getSize();
11391170
unsigned NumInits = ILE->getNumInits();
11401171
assert(NumElements >= NumInits && "Too many initializers for a vector");
1141-
QualType EltTy = T->castAs<VectorType>()->getElementType();
1172+
QualType EltTy = VecTy->getElementType();
11421173
SmallVector<mlir::Attribute, 8> Elts;
11431174
// Process the explicit initializers
11441175
for (unsigned i = 0; i < NumInits; ++i) {
@@ -1149,10 +1180,11 @@ class ConstExprEmitter
11491180
}
11501181
// Zero-fill the rest of the vector
11511182
for (unsigned i = NumInits; i < NumElements; ++i) {
1152-
Elts.push_back(CGM.getBuilder().getZeroInitAttr(VecTy.getElementType()));
1183+
Elts.push_back(
1184+
CGM.getBuilder().getZeroInitAttr(CIRVecTy.getElementType()));
11531185
}
11541186
return cir::ConstVectorAttr::get(
1155-
VecTy, mlir::ArrayAttr::get(CGM.getBuilder().getContext(), Elts));
1187+
CIRVecTy, mlir::ArrayAttr::get(CGM.getBuilder().getContext(), Elts));
11561188
}
11571189

11581190
mlir::Attribute VisitImplicitValueInitExpr(ImplicitValueInitExpr *E,

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,39 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
311311
if (E->getBase()->getType()->isVectorType()) {
312312
assert(!cir::MissingFeatures::scalableVectors() &&
313313
"NYI: index into scalable vector");
314-
// Subscript of vector type. This is handled differently, with a custom
315-
// operation.
314+
315+
// ExtVectorBoolType uses integer storage, handle it specially
316+
const auto *VecTy = E->getBase()
317+
->getType()
318+
.getCanonicalType()
319+
->getAs<clang::VectorType>();
320+
if (VecTy && VecTy->isExtVectorBoolType()) {
321+
// For ExtVectorBoolType, extract a bit from the integer
322+
mlir::Value IntValue = Visit(E->getBase());
323+
mlir::Value IndexValue = Visit(E->getIdx());
324+
325+
// Extract the bit: (IntValue >> IndexValue) & 1
326+
auto Loc = CGF.getLoc(E->getSourceRange());
327+
auto BoolTy = CGF.builder.getBoolTy();
328+
auto IntTy = IntValue.getType();
329+
330+
// Shift right by index: IntValue >> IndexValue
331+
mlir::Value Shifted =
332+
cir::ShiftOp::create(CGF.builder, Loc, IntTy, IntValue, IndexValue,
333+
/*isShiftLeft=*/false);
334+
335+
// Mask with 1: Shifted & 1
336+
mlir::Value One = CGF.builder.getConstInt(Loc, IntTy, 1);
337+
mlir::Value Masked = cir::BinOp::create(
338+
CGF.builder, Loc, IntTy, cir::BinOpKind::And, Shifted, One);
339+
340+
// Convert to bool: Masked != 0
341+
mlir::Value Zero = CGF.builder.getConstInt(Loc, IntTy, 0);
342+
return cir::CmpOp::create(CGF.builder, Loc, BoolTy, cir::CmpOpKind::ne,
343+
Masked, Zero);
344+
}
345+
346+
// Regular vector subscript
316347
mlir::Value VecValue = Visit(E->getBase());
317348
mlir::Value IndexValue = Visit(E->getIdx());
318349
return cir::VecExtractOp::create(CGF.getBuilder(),
@@ -1052,6 +1083,15 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
10521083
mlir::Value RHS = BOInfo.RHS;
10531084

10541085
if (LHSTy->isVectorType()) {
1086+
// Check for ExtVectorBoolType which uses integer storage, not vector
1087+
if (const auto *vecTy = LHSTy->getAs<clang::VectorType>()) {
1088+
if (vecTy->isExtVectorBoolType()) {
1089+
llvm_unreachable(
1090+
"NYI: ExtVectorBoolType comparison operations (requires "
1091+
"element-wise comparison on packed integer representation)");
1092+
}
1093+
}
1094+
10551095
if (!E->getType()->isVectorType()) {
10561096
// If AltiVec, the comparison results in a numeric type, so we use
10571097
// intrinsics comparing vectors and giving 0 or 1 as a result
@@ -2259,6 +2299,15 @@ mlir::Value ScalarExprEmitter::VisitUnaryLNot(const UnaryOperator *E) {
22592299
if (E->getType()->isVectorType() &&
22602300
E->getType()->castAs<VectorType>()->getVectorKind() ==
22612301
VectorKind::Generic) {
2302+
// Check for ExtVectorBoolType which uses integer storage, not vector
2303+
if (const auto *vecTy = E->getType()->getAs<clang::VectorType>()) {
2304+
if (vecTy->isExtVectorBoolType()) {
2305+
llvm_unreachable(
2306+
"NYI: ExtVectorBoolType logical NOT (requires handling padding "
2307+
"bits in integer storage to ensure correct element-wise negation)");
2308+
}
2309+
}
2310+
22622311
mlir::Value oper = Visit(E->getSubExpr());
22632312
mlir::Location loc = CGF.getLoc(E->getExprLoc());
22642313
auto operVecTy = mlir::cast<cir::VectorType>(oper.getType());

clang/lib/CIR/CodeGen/CIRGenTypes.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,8 +670,18 @@ mlir::Type CIRGenTypes::convertType(QualType T) {
670670
case Type::ExtVector:
671671
case Type::Vector: {
672672
const VectorType *V = cast<VectorType>(Ty);
673-
auto ElementType = convertTypeForMem(V->getElementType());
674-
ResultType = cir::VectorType::get(ElementType, V->getNumElements());
673+
// Boolean vectors use an integer as storage type, matching traditional
674+
// CodeGen. For N bool elements, storage is iM where M = max(N, 8).
675+
if (V->isExtVectorBoolType()) {
676+
uint64_t numElements = V->getNumElements();
677+
// Pad to at least one byte (8 bits)
678+
uint64_t storageBits = std::max<uint64_t>(numElements, 8);
679+
ResultType = cir::IntType::get(Builder.getContext(), storageBits,
680+
/*isSigned=*/false);
681+
} else {
682+
auto ElementType = convertTypeForMem(V->getElementType());
683+
ResultType = cir::VectorType::get(ElementType, V->getNumElements());
684+
}
675685
break;
676686
}
677687
case Type::ConstantMatrix: {

0 commit comments

Comments
 (0)