Skip to content

Commit bc0f121

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: 677025c94e759ae71cebcba8fa7bccaebe5c4163 Pull-Request: llvm/clangir#1998
1 parent e090c7e commit bc0f121

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

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

@@ -659,18 +667,21 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
659667

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

672-
if (vTy != newVecTy) {
673-
llvm_unreachable("NYI");
682+
if (vTy != newVecTy) {
683+
llvm_unreachable("NYI");
684+
}
674685
}
675686
}
676687

@@ -874,6 +885,16 @@ void CIRGenFunction::emitStoreThroughLValue(RValue Src, LValue Dst,
874885
bool isInit) {
875886
if (!Dst.isSimple()) {
876887
if (Dst.isVectorElt()) {
888+
// Check if this is an ExtVectorBoolType element assignment
889+
QualType vectorType = Dst.getType();
890+
if (const auto *vecTy = vectorType->getAs<clang::VectorType>()) {
891+
if (vecTy->isExtVectorBoolType()) {
892+
llvm_unreachable(
893+
"NYI: ExtVectorBoolType element assignment (requires bit "
894+
"manipulation to set/clear individual bits in integer storage)");
895+
}
896+
}
897+
877898
// Read/modify/write the vector, inserting the new element
878899
mlir::Location loc = Dst.getVectorPointer().getLoc();
879900
mlir::Value Vector = builder.createLoad(loc, Dst.getVectorAddress());
@@ -3053,6 +3074,13 @@ mlir::Value CIRGenFunction::emitFromMemory(mlir::Value Value, QualType Ty) {
30533074
llvm_unreachable("NIY");
30543075
}
30553076

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

@@ -3074,24 +3102,27 @@ mlir::Value CIRGenFunction::emitLoadOfScalar(Address addr, bool isVolatile,
30743102
auto eltTy = addr.getElementType();
30753103

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

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
@@ -2254,6 +2294,15 @@ mlir::Value ScalarExprEmitter::VisitUnaryLNot(const UnaryOperator *E) {
22542294
if (E->getType()->isVectorType() &&
22552295
E->getType()->castAs<VectorType>()->getVectorKind() ==
22562296
VectorKind::Generic) {
2297+
// Check for ExtVectorBoolType which uses integer storage, not vector
2298+
if (const auto *vecTy = E->getType()->getAs<clang::VectorType>()) {
2299+
if (vecTy->isExtVectorBoolType()) {
2300+
llvm_unreachable(
2301+
"NYI: ExtVectorBoolType logical NOT (requires handling padding "
2302+
"bits in integer storage to ensure correct element-wise negation)");
2303+
}
2304+
}
2305+
22572306
mlir::Value oper = Visit(E->getSubExpr());
22582307
mlir::Location loc = CGF.getLoc(E->getExprLoc());
22592308
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)