Skip to content

Commit 9db21c1

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: 29c37cf Pull-Request: #1998
1 parent d0cac23 commit 9db21c1

File tree

5 files changed

+143
-36
lines changed

5 files changed

+143
-36
lines changed

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -653,18 +653,20 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
653653

654654
auto eltTy = addr.getElementType();
655655
if (const auto *clangVecTy = ty->getAs<clang::VectorType>()) {
656-
// Boolean vectors use `iN` as storage type.
656+
// Boolean vectors use `iN` as storage type. This is handled by
657+
// convertTypeForMem, which returns an integer type for ExtVectorBoolType.
658+
// Skip vector optimizations for bool vectors.
657659
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());
660+
// Storage is already an integer type, nothing special needed
661+
} else {
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());
665666

666-
if (vTy != newVecTy) {
667-
llvm_unreachable("NYI");
667+
if (vTy != newVecTy) {
668+
llvm_unreachable("NYI");
669+
}
668670
}
669671
}
670672

@@ -2977,24 +2979,26 @@ mlir::Value CIRGenFunction::emitLoadOfScalar(Address addr, bool isVolatile,
29772979
auto eltTy = addr.getElementType();
29782980

29792981
if (const auto *clangVecTy = ty->getAs<clang::VectorType>()) {
2980-
// Boolean vectors use `iN` as storage type.
2982+
// Boolean vectors use `iN` as storage type. This is handled by
2983+
// convertTypeForMem, which returns an integer type for ExtVectorBoolType.
2984+
// Skip vector optimizations for bool vectors.
29812985
if (clangVecTy->isExtVectorBoolType()) {
2982-
llvm_unreachable("NYI");
2983-
}
2984-
2985-
// Handle vectors of size 3 like size 4 for better performance.
2986-
const auto vTy = cast<cir::VectorType>(eltTy);
2987-
auto newVecTy =
2988-
CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts());
2989-
2990-
if (vTy != newVecTy) {
2991-
const Address cast = addr.withElementType(builder, newVecTy);
2992-
mlir::Value v = builder.createLoad(loc, cast, isVolatile);
2993-
const uint64_t oldNumElements = vTy.getSize();
2994-
SmallVector<int64_t, 16> mask(oldNumElements);
2995-
std::iota(mask.begin(), mask.end(), 0);
2996-
v = builder.createVecShuffle(loc, v, mask);
2997-
return emitFromMemory(v, ty);
2986+
// Storage is already an integer type, nothing special needed
2987+
} else {
2988+
// Handle vectors of size 3 like size 4 for better performance.
2989+
const auto vTy = cast<cir::VectorType>(eltTy);
2990+
auto newVecTy =
2991+
CGM.getABIInfo().getOptimalVectorMemoryType(vTy, getLangOpts());
2992+
2993+
if (vTy != newVecTy) {
2994+
const Address cast = addr.withElementType(builder, newVecTy);
2995+
mlir::Value v = builder.createLoad(loc, cast, isVolatile);
2996+
const uint64_t oldNumElements = vTy.getSize();
2997+
SmallVector<int64_t, 16> mask(oldNumElements);
2998+
std::iota(mask.begin(), mask.end(), 0);
2999+
v = builder.createVecShuffle(loc, v, mask);
3000+
return emitFromMemory(v, ty);
3001+
}
29983002
}
29993003
}
30003004

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: 33 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(),

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: {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
// Test basic ext_vector_type with bool elements
5+
typedef bool bool4 __attribute__((ext_vector_type(4)));
6+
7+
// CHECK-LABEL: cir.func {{.*}}@_Z10test_basicv
8+
void test_basic() {
9+
// CHECK: %[[ALLOCA:.*]] = cir.alloca !u8i, !cir.ptr<!u8i>, ["v"
10+
bool4 v = {true, false, true, false};
11+
// CHECK: %[[CONST:.*]] = cir.const #cir.int<5> : !u8i
12+
// CHECK: cir.store {{.*}} %[[CONST]], %[[ALLOCA]]
13+
}
14+
15+
// CHECK-LABEL: cir.func {{.*}}@_Z14test_subscriptv
16+
void test_subscript() {
17+
bool4 v = {true, false, true, false};
18+
// CHECK: %[[LOAD:.*]] = cir.load{{.*}}!u8i
19+
// CHECK: %[[IDX:.*]] = cir.const #cir.int<2>
20+
// CHECK: %[[SHIFT:.*]] = cir.shift(right, %[[LOAD]]{{.*}}, %[[IDX]]
21+
// CHECK: cir.binop(and,{{.*}}){{.*}}!u8i
22+
// CHECK: cir.cmp(ne,{{.*}}){{.*}}!cir.bool
23+
bool b = v[2];
24+
}
25+
26+
// CHECK-LABEL: cir.func {{.*}}@_Z8test_ops
27+
void test_ops(bool4 a, bool4 b) {
28+
// CHECK: cir.binop(and,{{.*}}){{.*}}!u8i
29+
bool4 c = a & b;
30+
}

0 commit comments

Comments
 (0)