Skip to content

Commit 447ef15

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 8815253 commit 447ef15

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
@@ -634,6 +634,14 @@ CIRGenCallee CIRGenFunction::emitCallee(const clang::Expr *E) {
634634

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

@@ -653,18 +661,21 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
653661

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

666-
if (vTy != newVecTy) {
667-
llvm_unreachable("NYI");
676+
if (vTy != newVecTy) {
677+
llvm_unreachable("NYI");
678+
}
668679
}
669680
}
670681

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

2977+
// ExtVectorBoolType: In ClangIR, ExtVectorBoolType is always represented
2978+
// as an integer type (!cir.int<u, N>) throughout the IR, including both
2979+
// in registers and in memory. This differs from traditional CodeGen where
2980+
// it may need truncation from storage type to value type. Since we use
2981+
// integer representation consistently, no conversion is needed.
2982+
// See CIRGenTypes.cpp:675-683 for the type conversion logic.
2983+
29562984
return Value;
29572985
}
29582986

@@ -2974,24 +3002,27 @@ mlir::Value CIRGenFunction::emitLoadOfScalar(Address addr, bool isVolatile,
29743002
auto eltTy = addr.getElementType();
29753003

29763004
if (const auto *clangVecTy = ty->getAs<clang::VectorType>()) {
2977-
// Boolean vectors use `iN` as storage type.
3005+
// Boolean vectors use `iN` as storage type. The type conversion in
3006+
// CIRGenTypes::convertType (lines 675-683) returns an integer type for
3007+
// ExtVectorBoolType, so eltTy is already an integer. Skip vector
3008+
// optimizations for bool vectors since they're not actually vectors in CIR.
29783009
if (clangVecTy->isExtVectorBoolType()) {
2979-
llvm_unreachable("NYI");
2980-
}
2981-
2982-
// Handle vectors of size 3 like size 4 for better performance.
2983-
const auto vTy = cast<cir::VectorType>(eltTy);
2984-
auto newVecTy =
2985-
CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts());
2986-
2987-
if (vTy != newVecTy) {
2988-
const Address cast = addr.withElementType(builder, newVecTy);
2989-
mlir::Value v = builder.createLoad(loc, cast, isVolatile);
2990-
const uint64_t oldNumElements = vTy.getSize();
2991-
SmallVector<int64_t, 16> mask(oldNumElements);
2992-
std::iota(mask.begin(), mask.end(), 0);
2993-
v = builder.createVecShuffle(loc, v, mask);
2994-
return emitFromMemory(v, ty);
3010+
// Storage is already an integer type, nothing special needed
3011+
} else {
3012+
// Handle vectors of size 3 like size 4 for better performance.
3013+
const auto vTy = cast<cir::VectorType>(eltTy);
3014+
auto newVecTy =
3015+
CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts());
3016+
3017+
if (vTy != newVecTy) {
3018+
const Address cast = addr.withElementType(builder, newVecTy);
3019+
mlir::Value v = builder.createLoad(loc, cast, isVolatile);
3020+
const uint64_t oldNumElements = vTy.getSize();
3021+
SmallVector<int64_t, 16> mask(oldNumElements);
3022+
std::iota(mask.begin(), mask.end(), 0);
3023+
v = builder.createVecShuffle(loc, v, mask);
3024+
return emitFromMemory(v, ty);
3025+
}
29953026
}
29963027
}
29973028

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

9781009
if (LHSTy->isVectorType()) {
1010+
// Check for ExtVectorBoolType which uses integer storage, not vector
1011+
if (const auto *vecTy = LHSTy->getAs<clang::VectorType>()) {
1012+
if (vecTy->isExtVectorBoolType()) {
1013+
llvm_unreachable(
1014+
"NYI: ExtVectorBoolType comparison operations (requires "
1015+
"element-wise comparison on packed integer representation)");
1016+
}
1017+
}
1018+
9791019
if (!E->getType()->isVectorType()) {
9801020
// If AltiVec, the comparison results in a numeric type, so we use
9811021
// intrinsics comparing vectors and giving 0 or 1 as a result
@@ -2076,6 +2116,15 @@ mlir::Value ScalarExprEmitter::VisitUnaryLNot(const UnaryOperator *E) {
20762116
if (E->getType()->isVectorType() &&
20772117
E->getType()->castAs<VectorType>()->getVectorKind() ==
20782118
VectorKind::Generic) {
2119+
// Check for ExtVectorBoolType which uses integer storage, not vector
2120+
if (const auto *vecTy = E->getType()->getAs<clang::VectorType>()) {
2121+
if (vecTy->isExtVectorBoolType()) {
2122+
llvm_unreachable(
2123+
"NYI: ExtVectorBoolType logical NOT (requires handling padding "
2124+
"bits in integer storage to ensure correct element-wise negation)");
2125+
}
2126+
}
2127+
20792128
mlir::Value oper = Visit(E->getSubExpr());
20802129
mlir::Location loc = CGF.getLoc(E->getExprLoc());
20812130
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)