https://github.com/adams381 created https://github.com/llvm/llvm-project/pull/230280
Bool vectors are now sized one bit per element, as clang does, and are passed and returned the way classic CodeGen passes them, including in records. Assisted-by: Cursor / Claude Opus 5.5 >From 9b19b9a65dee5df9719062838def2a72301b18d8 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Thu, 8 Oct 2026 13:26:22 -0700 Subject: [PATCH] [CIR] Support bool vectors in x86_64 calling-convention lowering Bool vectors are now sized one bit per element, as clang does, and are passed and returned the way classic CodeGen passes them, including in records. Assisted-by: Cursor / Claude Opus 5.5 --- .../include/clang/CIR/Dialect/IR/CIRTypes.td | 11 + clang/lib/CIR/CodeGen/CIRGenCall.cpp | 15 +- .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 24 ++ clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 12 + .../Transforms/CallConvLoweringPass.cpp | 21 +- .../TargetLowering/CIRABIRewriteContext.cpp | 126 +++++---- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 8 +- clang/lib/CIR/Lowering/LoweringHelpers.cpp | 5 +- .../CodeGen/bool-vector-record-storage-nyi.c | 40 +++ ...onv-lowering-x86_64-bool-vector-return.cpp | 38 +++ .../call-conv-lowering-x86_64-bool-vector.c | 246 ++++++++++++++++++ .../builtins-elementwise-bool-nyi.c | 4 +- .../Transforms/abi-lowering/x86_64-vector.cir | 28 ++ 13 files changed, 504 insertions(+), 74 deletions(-) create mode 100644 clang/test/CIR/CodeGen/bool-vector-record-storage-nyi.c create mode 100644 clang/test/CIR/CodeGen/call-conv-lowering-x86_64-bool-vector-return.cpp create mode 100644 clang/test/CIR/CodeGen/call-conv-lowering-x86_64-bool-vector.c diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td index fd092c3ebb7056..2b76d1cb6928db 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td @@ -509,6 +509,11 @@ def CIR_VectorType : CIR_Type<"Vector", "vector", [ !cir.vector<2 x !cir.float> ``` + A fixed-length vector of `!cir.bool` is packed one bit per element, and its + size is rounded up to a power of two of at least one byte, so + `!cir.vector<4 x !cir.bool>` occupies one byte and + `!cir.vector<17 x !cir.bool>` occupies four. + Scalable vectors are indicated by enclosing size in square brackets. Example: @@ -532,6 +537,12 @@ def CIR_VectorType : CIR_Type<"Vector", "vector", [ }]>, ]; + let extraClassDeclaration = [{ + /// The width of the integer a vector of `!cir.bool` is stored as: one bit + /// per element, at least a byte. + uint64_t getBoolStorageWidth() const; + }]; + let extraClassDefinition = [{ bool $cppClass::isSized() const { return ::cir::isSized(getElementType()); diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 4f4e10aa218be9..ea900b00413777 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -606,10 +606,19 @@ static bool determineNoUndef(QualType clangTy, CIRGenTypes &types, // bits from the perspective of LLVM IR. return false; + // Classic CodeGen stores a packed bool vector as an integer with one bit per + // element, at least a byte wide, so the check above drops noundef when that + // integer is not a whole number of bytes. The CIR vector type is sized to a + // power of two of at least a byte, which always passes that check, so test + // the storage integer's width directly. + if (clangTy->isPackedVectorBoolType(types.getASTContext()) && + mlir::cast<cir::VectorType>(ty).getBoolStorageWidth() % 8) + return false; + assert(!cir::MissingFeatures::opCallCallConv()); - // TODO(cir): The calling convention code needs to figure if the - // coerced-to-type is larger than the actual type, and remove the noundef - // attribute. Classic compiler did it here. + // The coerced type is not known until CallConvLowering, which drops noundef + // when it is wider than the value's memory type, the check classic + // DetermineNoUndef makes at this point. if (clangTy->isBitIntType()) return true; if (clangTy->isReferenceType()) diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 4bb483fc57a962..1d324a23fcee1e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -249,9 +249,33 @@ struct CIRRecordLowering final { cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), "getStorageType for bitfields"); } + if (hasBoolVectorStorageAlignMismatch(type)) + cirGenTypes.getCGModule().errorNYI( + fieldDecl->getSourceRange(), + "getStorageType for bool vector whose storage integer is aligned " + "differently from the vector"); return type; } + /// Whether \p type, or the innermost element type of an array \p type, is a + /// bool vector whose storage integer is aligned differently from the + /// vector. A bool vector is stored as an integer with one bit per element, + /// at least a byte wide. The vector is aligned to its own size, so an + /// integer with the same alignment also has the same allocation size. + /// Classic CodeGen lays a mismatched member out as that integer and fills + /// the difference with padding bytes, which this layout does not do yet. + bool hasBoolVectorStorageAlignMismatch(mlir::Type type) { + while (auto arrTy = mlir::dyn_cast<cir::ArrayType>(type)) + type = arrTy.getElementType(); + auto vecTy = mlir::dyn_cast<cir::VectorType>(type); + if (!vecTy || !mlir::isa<cir::BoolType>(vecTy.getElementType())) + return false; + auto storageTy = + mlir::IntegerType::get(type.getContext(), vecTy.getBoolStorageWidth()); + return dataLayout.layout.getTypeABIAlignment(storageTy) != + dataLayout.layout.getTypeABIAlignment(vecTy); + } + uint64_t getFieldBitOffset(const FieldDecl *fieldDecl) { return astRecordLayout.getFieldOffset(fieldDecl->getFieldIndex()); } diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index fe3f41a226e00e..75677d05796e62 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -1494,10 +1494,22 @@ BitFieldType::getABIAlignment(const mlir::DataLayout &dataLayout, llvm::TypeSize cir::VectorType::getTypeSizeInBits( const ::mlir::DataLayout &dataLayout, ::mlir::DataLayoutEntryListRef params) const { + // Clang packs a fixed-length bool vector one bit per element and rounds the + // vector's size up to a power of two of at least a byte. + if (mlir::isa<cir::BoolType>(getElementType()) && !getIsScalable()) { + assert(!cir::MissingFeatures::hlsl()); + return llvm::TypeSize::getFixed(llvm::PowerOf2Ceil(getBoolStorageWidth())); + } return llvm::TypeSize::getFixed( getSize() * dataLayout.getTypeSizeInBits(getElementType())); } +uint64_t cir::VectorType::getBoolStorageWidth() const { + assert(mlir::isa<cir::BoolType>(getElementType()) && + "only a bool vector is stored as an integer"); + return std::max<uint64_t>(getSize(), 8); +} + uint64_t cir::VectorType::getABIAlignment(const ::mlir::DataLayout &dataLayout, ::mlir::DataLayoutEntryListRef params) const { diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp index 402cd09afd6370..c924529945c01e 100644 --- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp @@ -205,11 +205,10 @@ static bool isSupportedType(mlir::Type ty, const DataLayout &dl) { // x86_64 has no calling convention for one. if (vecTy.getIsScalable()) return false; - // The classifier sizes a vector as element count times element width, so - // an element is only usable where that width is the one clang gives it. - // It is not for bool (a bit to clang, a byte here), for a _BitInt narrower - // than a byte (clang rounds to the storage container), or for x87 long - // double (80 bits here against clang's 128). + // mapCIRType maps a bool as a one-bit integer, so a bool vector is sized + // one bit per element, as clang packs it. Vectors of a _BitInt whose + // width is not a whole number of bytes, or of x87 long double, are not + // handled yet. mlir::Type elemTy = vecTy.getElementType(); if (auto elemInt = dyn_cast<cir::IntType>(elemTy)) { if (elemInt.getWidth() % 8) @@ -217,7 +216,7 @@ static bool isSupportedType(mlir::Type ty, const DataLayout &dl) { } else if (auto elemFp = dyn_cast<cir::FPTypeInterface>(elemTy)) { if (&elemFp.getFloatSemantics() == &llvm::APFloat::x87DoubleExtended()) return false; - } else { + } else if (!isa<cir::BoolType>(elemTy)) { return false; } // Clang also rounds the vector's own width up to a power of two, and the @@ -308,7 +307,10 @@ static mlir::Type abiTypeToCIR(const llvm::abi::Type *ty, MLIRContext *ctx) { return llvm::TypeSwitch<const llvm::abi::Type *, mlir::Type>(ty) .Case( [&](const llvm::abi::VoidType *) { return cir::VoidType::get(ctx); }) - .Case([&](const llvm::abi::IntegerType *intTy) { + .Case([&](const llvm::abi::IntegerType *intTy) -> mlir::Type { + // mapCIRType maps a bool as a one-bit integer. + if (intTy->isBool()) + return cir::BoolType::get(ctx); return cir::IntType::get(ctx, intTy->getSizeInBits().getFixedValue(), intTy->isSigned(), intTy->isBitInt()); }) @@ -373,8 +375,9 @@ static const llvm::abi::Type *mapCIRType(mlir::Type type, llvm::Align(dl.getTypeABIAlignment(type))); }) .Case([&](cir::BoolType) { - return tb.getIntegerType(dl.getTypeSizeInBits(type), - llvm::Align(dl.getTypeABIAlignment(type)), + // A bool is a one-bit integer to the classifier, as QualTypeMapper + // maps it, so a bool vector is sized one bit per element. + return tb.getIntegerType(1, llvm::Align(dl.getTypeABIAlignment(type)), /*Signed=*/false); }) .Case([&](cir::VoidType) { return tb.getVoidType(); }) diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp index f79a8535355a85..75eaec4d169866 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp @@ -185,9 +185,36 @@ mlir::Value createIgnoredValue(mlir::OpBuilder &builder, mlir::Location loc, return cir::ConstantOp::create(builder, loc, ty, cir::PoisonAttr::get(ty)); } +/// Whether \p coercedTy is wider than the type a value of type \p origTy has +/// in memory, so the coerced value passes bits the value does not define and +/// cannot be noundef. This is the comparison classic CodeGen makes. A bool +/// vector's memory type is its storage integer, one bit per element and at +/// least a byte wide, not its power-of-two size. +static bool isCoercionWiderThanMemory(mlir::Type origTy, mlir::Type coercedTy, + const mlir::DataLayout &dl) { + uint64_t memoryBits = dl.getTypeSizeInBits(origTy).getFixedValue(); + if (auto vecTy = mlir::dyn_cast<cir::VectorType>(origTy); + vecTy && mlir::isa<cir::BoolType>(vecTy.getElementType())) + memoryBits = vecTy.getBoolStorageWidth(); + return dl.getTypeSizeInBits(coercedTy).getFixedValue() > memoryBits; +} + +/// Whether updateArgAttrs has anything to change: an arg that is Ignore (its +/// slot is dropped), Extend (needs llvm.signext / llvm.zeroext), Indirect +/// (needs the pointer attributes), Expand (changes the argument count), or a +/// coerced Direct, which may be flattened or may lose llvm.noundef. +static bool needsArgAttrUpdate(const FunctionClassification &fc) { + return llvm::any_of(fc.argInfos, [](const ArgClassification &ac) { + return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend || + ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand || + (ac.kind == ArgKind::Direct && ac.coercedType); + }); +} + /// Build an updated arg_attrs ArrayAttr that drops Ignore'd args, adds -/// llvm.signext / llvm.zeroext on Extend args, and adds the pointer -/// attributes for Indirect args. Preserves any existing arg attributes on +/// llvm.signext / llvm.zeroext on Extend args, adds the pointer attributes +/// for Indirect args, and drops llvm.noundef from a coerced Direct arg that +/// passes undefined bits. Preserves any other existing arg attributes on /// retained arg slots. \p origArgTypes provides the pre-rewrite type for /// each arg slot. mlir::ArrayAttr updateArgAttrs(mlir::MLIRContext *ctx, @@ -255,6 +282,12 @@ mlir::ArrayAttr updateArgAttrs(mlir::MLIRContext *ctx, dl.getTypeSize(pointeeTy).getFixedValue())); } newArgAttrs.push_back(attrs.getDictionary(ctx)); + } else if (ac.kind == ArgKind::Direct && ac.coercedType && + isCoercionWiderThanMemory(origArgTypes[oldIdx], ac.coercedType, + dl)) { + mlir::NamedAttrList attrs(existing); + attrs.erase(mlir::LLVM::LLVMDialect::getNoUndefAttrName()); + newArgAttrs.push_back(attrs.getDictionary(ctx)); } else { newArgAttrs.push_back(existing); } @@ -264,22 +297,31 @@ mlir::ArrayAttr updateArgAttrs(mlir::MLIRContext *ctx, /// Build an updated res_attrs ArrayAttr (single entry, since CIR funcs have /// at most one result) that adds llvm.signext / llvm.zeroext on an Extend -/// return. Preserves any existing res attributes. +/// return and drops llvm.noundef from a coerced return that passes undefined +/// bits. Preserves any other existing res attributes. Returns +/// \p existingResAttrs unchanged, which may be null, when there is nothing to +/// add or drop. mlir::ArrayAttr updateResAttrs(mlir::MLIRContext *ctx, mlir::ArrayAttr existingResAttrs, - const ArgClassification &retInfo) { - if (retInfo.kind != ArgKind::Extend) + const ArgClassification &retInfo, + mlir::Type origRetTy, + const mlir::DataLayout &dl) { + bool needsNoUndefDrop = + retInfo.kind == ArgKind::Direct && retInfo.coercedType && + isCoercionWiderThanMemory(origRetTy, retInfo.coercedType, dl); + if (retInfo.kind != ArgKind::Extend && !needsNoUndefDrop) return existingResAttrs; - SmallVector<mlir::NamedAttribute> attrs; + mlir::NamedAttrList attrs; if (existingResAttrs && !existingResAttrs.empty()) - for (mlir::NamedAttribute na : - mlir::cast<mlir::DictionaryAttr>(existingResAttrs[0])) - attrs.push_back(na); - StringRef attrName = retInfo.signExtend ? "llvm.signext" : "llvm.zeroext"; - attrs.push_back(mlir::NamedAttribute(mlir::StringAttr::get(ctx, attrName), - mlir::UnitAttr::get(ctx))); - return mlir::ArrayAttr::get(ctx, {mlir::DictionaryAttr::get(ctx, attrs)}); + attrs.append( + mlir::cast<mlir::DictionaryAttr>(existingResAttrs[0]).getValue()); + if (retInfo.kind == ArgKind::Extend) + attrs.set(retInfo.signExtend ? "llvm.signext" : "llvm.zeroext", + mlir::UnitAttr::get(ctx)); + if (needsNoUndefDrop) + attrs.erase(mlir::LLVM::LLVMDialect::getNoUndefAttrName()); + return mlir::ArrayAttr::get(ctx, {attrs.getDictionary(ctx)}); } /// The number of bytes a coercion memory slot needs to hold a value of type @@ -1259,19 +1301,11 @@ void rewriteIndirectReturnCall(cir::CallOp call, copyCallAttributes(call, newCall); newCall->removeAttr("res_attrs"); - // Shape the per-argument attrs exactly as the non-sret path does - // (signext / zeroext for Extend, drop Ignore slots, byval / align for - // Indirect, flatten for Expand and Direct+canFlatten) before prepending the - // sret slot, so sret composes correctly with Extend / Ignore / Indirect / - // Expand / Direct+canFlatten args. + // Shape the per-argument attrs exactly as the non-sret path does, through + // updateArgAttrs, before prepending the sret slot, so sret composes with + // every argument classification. mlir::ArrayAttr argAttrs = call->getAttrOfType<mlir::ArrayAttr>("arg_attrs"); - bool needsArgAttrUpdate = - llvm::any_of(fc.argInfos, [](const ArgClassification &ac) { - return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend || - ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand || - getFlattenedCoercedType(ac); - }); - if (needsArgAttrUpdate) + if (needsArgAttrUpdate(fc)) argAttrs = updateArgAttrs(ctx, origCallArgTypes, argAttrs, fc, dl); applySretSlotAttrs(newCall, argAttrs, origRetTy, sretAlign, builder); @@ -1609,17 +1643,8 @@ mlir::LogicalResult CIRABIRewriteContext::rewriteFunctionDefinition( funcOp.setFunctionTypeAttr(mlir::TypeAttr::get(newFnTy)); // Rebuild arg_attrs when the function has an sret slot (slot 0 needs the - // sret attribute set) or any arg is Ignore (dropped from the output array), - // Extend (needs llvm.signext / llvm.zeroext), Indirect (gains the pointer - // attributes updateArgAttrs applies), Expand or Direct+canFlatten (both - // change the argument count). - bool needsArgAttrUpdate = - hasSRet || llvm::any_of(fc.argInfos, [](const ArgClassification &ac) { - return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend || - ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand || - getFlattenedCoercedType(ac); - }); - if (needsArgAttrUpdate) { + // sret attribute set) or any arg needs updateArgAttrs. + if (hasSRet || needsArgAttrUpdate(fc)) { auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>("arg_attrs"); mlir::ArrayAttr updated = updateArgAttrs(ctx, oldArgTypes, existing, fc, dl); @@ -1641,10 +1666,12 @@ mlir::LogicalResult CIRABIRewriteContext::rewriteFunctionDefinition( if (mlir::isa<cir::VoidType>(newRetTy)) { funcOp->removeAttr("res_attrs"); - } else if (fc.returnInfo.kind == ArgKind::Extend) { - // Layer llvm.signext / llvm.zeroext onto an Extend return. + } else if (fc.returnInfo.kind == ArgKind::Extend || + fc.returnInfo.kind == ArgKind::Direct) { auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>("res_attrs"); - funcOp->setAttr("res_attrs", updateResAttrs(ctx, existing, fc.returnInfo)); + if (mlir::ArrayAttr updated = + updateResAttrs(ctx, existing, fc.returnInfo, origRetTy, dl)) + funcOp->setAttr("res_attrs", updated); } return mlir::success(); @@ -1872,24 +1899,19 @@ CIRABIRewriteContext::rewriteCallSite(mlir::Operation *callOp, call.getResult().replaceAllUsesWith(coercedBack); } - // Layer llvm.signext / llvm.zeroext onto the new call's arg_attrs and - // res_attrs for Extend args/return. Ignore args require a rebuild because - // their slots are dropped; Indirect args need llvm.byval / llvm.align; - // Expand and Direct+canFlatten args change the argument count. - bool needsArgAttrUpdate = - llvm::any_of(fc.argInfos, [](const ArgClassification &ac) { - return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend || - ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand || - getFlattenedCoercedType(ac); - }); - if (needsArgAttrUpdate) { + if (needsArgAttrUpdate(fc)) { auto existing = call->getAttrOfType<mlir::ArrayAttr>("arg_attrs"); newCall->setAttr("arg_attrs", updateArgAttrs(ctx, origCallArgTypes, existing, fc, dl)); } - if (fc.returnInfo.kind == ArgKind::Extend) { + // Add llvm.signext / llvm.zeroext to an Extend return, and drop llvm.noundef + // from a coerced Direct return that passes undefined bits. + if (fc.returnInfo.kind == ArgKind::Extend || + (hasResult && fc.returnInfo.kind == ArgKind::Direct)) { auto existing = call->getAttrOfType<mlir::ArrayAttr>("res_attrs"); - newCall->setAttr("res_attrs", updateResAttrs(ctx, existing, fc.returnInfo)); + if (mlir::ArrayAttr updated = + updateResAttrs(ctx, existing, fc.returnInfo, origRetTy, dl)) + newCall->setAttr("res_attrs", updated); } else if (hasResult && mlir::isa<cir::VoidType>(callRetTy)) { newCall->removeAttr("res_attrs"); } diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index a254d798362290..4a49b252515496 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -220,10 +220,10 @@ static mlir::Value emitToMemory(mlir::ConversionPatternRewriter &rewriter, resultTy = mlir::VectorType::get( vecTy.getSize(), vecTy.getElementType(), vecTy.getIsScalable()); else { - uint64_t bytePadded = std::max<uint64_t>(vecTy.getSize(), 8); - resultTy = mlir::IntegerType::get(origType.getContext(), bytePadded); - value = emitBoolVecConversion( - rewriter, value, dyn_cast<mlir::IntegerType>(resultTy).getWidth()); + resultTy = mlir::IntegerType::get(origType.getContext(), + vecTy.getBoolStorageWidth()); + value = + emitBoolVecConversion(rewriter, value, vecTy.getBoolStorageWidth()); } return mlir::LLVM::BitcastOp::create(rewriter, value.getLoc(), resultTy, value); diff --git a/clang/lib/CIR/Lowering/LoweringHelpers.cpp b/clang/lib/CIR/Lowering/LoweringHelpers.cpp index e3295c21b6a711..c6baa2fd63e3e9 100644 --- a/clang/lib/CIR/Lowering/LoweringHelpers.cpp +++ b/clang/lib/CIR/Lowering/LoweringHelpers.cpp @@ -96,9 +96,8 @@ mlir::Type convertTypeForMemory(const mlir::TypeConverter &converter, if (auto vecTy = mlir::dyn_cast<cir::VectorType>(type)) { if (mlir::isa<cir::BoolType>(vecTy.getElementType())) { assert(!cir::MissingFeatures::hlsl()); - // Pad to at least one byte. - uint64_t bytePadded = std::max<uint64_t>(vecTy.getSize(), 8); - return mlir::IntegerType::get(type.getContext(), bytePadded); + return mlir::IntegerType::get(type.getContext(), + vecTy.getBoolStorageWidth()); } } diff --git a/clang/test/CIR/CodeGen/bool-vector-record-storage-nyi.c b/clang/test/CIR/CodeGen/bool-vector-record-storage-nyi.c new file mode 100644 index 00000000000000..e14d0ac2e6b540 --- /dev/null +++ b/clang/test/CIR/CodeGen/bool-vector-record-storage-nyi.c @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o /dev/null -verify=b256 -DB256 +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o /dev/null -verify=b300 -DB300 +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o /dev/null -verify=array -DARRAY +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o /dev/null -verify=union -DUNION + +typedef _Bool b256 __attribute__((ext_vector_type(256))); +typedef _Bool b300 __attribute__((ext_vector_type(300))); + +#ifdef B256 +// An i256 is aligned to 16 bytes where the vector is aligned to 32. +struct S256 { + b256 v; // b256-error {{ClangIR code gen Not Yet Implemented: getStorageType for bool vector whose storage integer is aligned differently from the vector}} + int x; +}; +void f256(struct S256 *s) { s->x = 1; } +#endif + +#ifdef B300 +// An i300 is aligned to 16 bytes where the vector is aligned to 64. +struct S300 { + b300 v; // b300-error {{ClangIR code gen Not Yet Implemented: getStorageType for bool vector whose storage integer is aligned differently from the vector}} + int x; +}; +void f300(struct S300 *s) { s->x = 1; } +#endif + +#ifdef ARRAY +struct T { + b256 a[2]; // array-error {{ClangIR code gen Not Yet Implemented: getStorageType for bool vector whose storage integer is aligned differently from the vector}} +}; +void g(struct T *t) { (void)t->a[0]; } +#endif + +#ifdef UNION +union U { + b256 v; // union-error {{ClangIR code gen Not Yet Implemented: getStorageType for bool vector whose storage integer is aligned differently from the vector}} + int x; +}; +void h(union U *u) { u->x = 1; } +#endif diff --git a/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-bool-vector-return.cpp b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-bool-vector-return.cpp new file mode 100644 index 00000000000000..cd38d1a03d9b81 --- /dev/null +++ b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-bool-vector-return.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s + +typedef bool b4 __attribute__((ext_vector_type(4))); +typedef bool b17 __attribute__((ext_vector_type(17))); +typedef bool b24 __attribute__((ext_vector_type(24))); + +b4 rb4(b4 v) { return v; } + +// CIR: cir.func {{.*}} @_Z3rb4Dv4_b(%arg0: !u8i {llvm.noundef} loc({{[^)]+}})) -> (!u8i {llvm.noundef}) +// LLVM: define dso_local noundef i8 @_Z3rb4Dv4_b(i8 noundef %{{[^,)]+}}) + +// The 17-bit storage integer is not a whole number of bytes, so the return is +// not noundef. +b17 rb17(b17 v) { return v; } + +// CIR: cir.func {{.*}} @_Z4rb17Dv17_b(%arg0: !u32i loc({{[^)]+}})) -> !u32i +// LLVM: define dso_local i32 @_Z4rb17Dv17_b(i32 %{{[^,)]+}}) + +// The i32 is wider than the 24 bits of storage, so the return is not noundef. +b24 rb24(b24 v) { return v; } + +// CIR: cir.func {{.*}} @_Z4rb24Dv24_b(%arg0: !u32i loc({{[^)]+}})) -> !u32i +// LLVM: define dso_local i32 @_Z4rb24Dv24_b(i32 %{{[^,)]+}}) + +b4 call_rb4(b4 v) { return rb4(v); } + +// CIR: cir.call @_Z3rb4Dv4_b(%{{[^)]+}}) : (!u8i {llvm.noundef}) -> (!u8i {llvm.noundef}) +// LLVM: call noundef i8 @_Z3rb4Dv4_b(i8 noundef %{{[^,)]+}}) + +b24 call_rb24(b24 v) { return rb24(v); } + +// CIR: cir.call @_Z4rb24Dv24_b(%{{[^)]+}}) : (!u32i) -> !u32i +// LLVM: call i32 @_Z4rb24Dv24_b(i32 %{{[^,)]+}}) diff --git a/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-bool-vector.c b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-bool-vector.c new file mode 100644 index 00000000000000..78f5085c3b4f10 --- /dev/null +++ b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-bool-vector.c @@ -0,0 +1,246 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -target-feature +avx -fclangir -emit-llvm %s -o %t-cir-avx.ll +// RUN: FileCheck --check-prefix=AVX --input-file=%t-cir-avx.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -target-feature +avx -emit-llvm %s -o %t-avx.ll +// RUN: FileCheck --check-prefix=AVX --input-file=%t-avx.ll %s + +typedef _Bool b4 __attribute__((ext_vector_type(4))); +typedef _Bool b8 __attribute__((ext_vector_type(8))); +typedef _Bool b16 __attribute__((ext_vector_type(16))); +typedef _Bool b17 __attribute__((ext_vector_type(17))); +typedef _Bool b24 __attribute__((ext_vector_type(24))); +typedef _Bool b40 __attribute__((ext_vector_type(40))); +typedef _Bool b64 __attribute__((ext_vector_type(64))); +typedef _Bool b100 __attribute__((ext_vector_type(100))); +typedef _Bool b128 __attribute__((ext_vector_type(128))); +typedef _Bool b130 __attribute__((ext_vector_type(130))); +typedef _Bool b256 __attribute__((ext_vector_type(256))); + +// A b4 member takes one byte. +struct SB4 { b4 v; }; +struct SB4x2 { b4 a; b4 b; int i; }; +struct SB4Arr { b4 a[3]; }; +struct SB4F { b4 v; float f; }; +struct SB64x2 { b64 a; b64 b; }; +struct SB64x3 { b64 a, b, c; }; +struct SB128 { b128 v; }; +union UB4 { b4 v; char c; }; + +// CIR-DAG: !rec_SB4 = !cir.struct<"SB4" {data !cir.vector<4 x !cir.bool>}> +// CIR-DAG: !rec_anon_struct = !cir.struct<{data !cir.double, data !cir.double}> +// CIR-DAG: !rec_SB4x2 = !cir.struct<"SB4x2" {data !cir.vector<4 x !cir.bool>, data !cir.vector<4 x !cir.bool>, data !s32i}> +// CIR-DAG: !rec_SB4F = !cir.struct<"SB4F" {data !cir.vector<4 x !cir.bool>, data !cir.float}> +// CIR-DAG: !rec_SB64x3 = !cir.struct<"SB64x3" {data !cir.vector<64 x !cir.bool>, data !cir.vector<64 x !cir.bool>, data !cir.vector<64 x !cir.bool>}> +// CIR-DAG: !rec_SB128 = !cir.struct<"SB128" {data !cir.vector<128 x !cir.bool>}> +// LLVM-DAG: %struct.SB4 = type { i8 } +// LLVM-DAG: %struct.SB4x2 = type { i8, i8, i32 } +// LLVM-DAG: %struct.SB4Arr = type { [3 x i8] } +// LLVM-DAG: %struct.SB4F = type { i8, float } +// LLVM-DAG: %struct.SB64x2 = type { i64, i64 } +// LLVM-DAG: %struct.SB64x3 = type { i64, i64, i64 } +// LLVM-DAG: %struct.SB128 = type { i128 } +// LLVM-DAG: %union.UB4 = type { i8 } + +b4 vb4(b4 v) { return v; } + +// CIR: cir.func {{.*}} @vb4(%arg0: !u8i {llvm.noundef} loc({{[^)]+}})) -> !u8i +// CIR: %[[SLOT:[^ ]+]] = cir.alloca "coerce" align(1) : !cir.ptr<!u8i> +// CIR: cir.store %arg0, %[[SLOT]] : !u8i, !cir.ptr<!u8i> +// CIR: %[[VIEW:[^ ]+]] = cir.cast bitcast %[[SLOT]] : !cir.ptr<!u8i> -> !cir.ptr<!cir.vector<4 x !cir.bool>> +// CIR: cir.load %[[VIEW]] : !cir.ptr<!cir.vector<4 x !cir.bool>>, !cir.vector<4 x !cir.bool> + +// LLVM: define dso_local i8 @vb4(i8 noundef %[[ARG:[^,)]+]]) +// LLVM: store i8 %[[ARG]], ptr %[[SLOT:[^,]+]], align 1 +// LLVM: %[[BITS:[^ ]+]] = load i8, ptr %[[SLOT]], align 1 +// LLVM: bitcast i8 %[[BITS]] to <8 x i1> +// LLVM: ret i8 + +b8 vb8(b8 v) { return v; } + +// CIR: cir.func {{.*}} @vb8(%arg0: !u8i {llvm.noundef} loc({{[^)]+}})) -> !u8i +// LLVM: define dso_local i8 @vb8(i8 noundef %{{[^,)]+}}) + +b16 vb16(b16 v) { return v; } + +// CIR: cir.func {{.*}} @vb16(%arg0: !u16i {llvm.noundef} loc({{[^)]+}})) -> !u16i +// LLVM: define dso_local i16 @vb16(i16 noundef %{{[^,)]+}}) + +// The 17-bit storage integer is not a whole number of bytes, so the argument +// is not noundef. +b17 vb17(b17 v) { return v; } + +// CIR: cir.func {{.*}} @vb17(%arg0: !u32i loc({{[^)]+}})) -> !u32i +// CIR: %[[SLOT17:[^ ]+]] = cir.alloca "coerce" align(4) : !cir.ptr<!u32i> +// CIR: cir.store %arg0, %[[SLOT17]] : !u32i, !cir.ptr<!u32i> +// CIR: cir.cast bitcast %[[SLOT17]] : !cir.ptr<!u32i> -> !cir.ptr<!cir.vector<17 x !cir.bool>> + +// LLVM: define dso_local i32 @vb17(i32 %[[ARG17:[^,)]+]]) +// LLVM: store i32 %[[ARG17]], ptr %[[SLOT17:[^,]+]], align 4 +// LLVM: load i17, ptr %[[SLOT17]], align 4 +// LLVM: ret i32 + +// The coerced types are wider than the 24 and 40 bits of storage, so neither +// is noundef. +b24 vb24(b24 v) { return v; } +b40 vb40(b40 v) { return v; } + +// CIR: cir.func {{.*}} @vb24(%arg0: !u32i loc({{[^)]+}})) -> !u32i +// CIR: cir.func {{.*}} @vb40(%arg0: !cir.double loc({{[^)]+}})) -> !cir.double +// LLVM: define dso_local i32 @vb24(i32 %{{[^,)]+}}) +// LLVM: define dso_local double @vb40(double %{{[^,)]+}}) + +b24 call_vb24(b24 v) { return vb24(v); } + +// CIR: cir.func {{.*}} @call_vb24(%arg0: !u32i loc({{[^)]+}})) -> !u32i +// CIR: cir.call @vb24(%{{[^)]+}}) : (!u32i) -> !u32i +// LLVM: define dso_local i32 @call_vb24(i32 %{{[^,)]+}}) +// LLVM: call i32 @vb24(i32 %{{[^,)]+}}) + +b64 vb64(b64 v) { return v; } + +// CIR: cir.func {{.*}} @vb64(%arg0: !cir.double {llvm.noundef} loc({{[^)]+}})) -> !cir.double +// LLVM: define dso_local double @vb64(double noundef %{{[^,)]+}}) + +// A 100-bit vector is passed as itself, but its storage is not a whole number +// of bytes, so it is not noundef. +b100 vb100(b100 v) { return v; } + +// CIR: cir.func {{.*}} @vb100(%arg0: !cir.vector<100 x !cir.bool> loc({{[^)]+}})) -> !cir.vector<100 x !cir.bool> +// LLVM: define dso_local <100 x i1> @vb100(<100 x i1> %{{[^,)]+}}) + +b128 vb128(b128 v) { return v; } + +// CIR: cir.func {{.*}} @vb128(%arg0: !cir.vector<128 x !cir.bool> {llvm.noundef} loc({{[^)]+}})) -> !cir.vector<128 x !cir.bool> +// CIR-NOT: cir.alloca "coerce" +// CIR: cir.return %{{.+}} : !cir.vector<128 x !cir.bool> + +// LLVM: define dso_local <128 x i1> @vb128(<128 x i1> noundef %[[ARG128:[^,)]+]]) +// LLVM: bitcast <128 x i1> %[[ARG128]] to i128 +// LLVM: ret <128 x i1> + +// Without AVX a 256-bit vector is passed in memory. +void vb256(b256 v) {} + +// CIR: cir.func {{.*}} @vb256(%arg0: !cir.ptr<!cir.vector<256 x !cir.bool>> {llvm.align = 32 : i64, llvm.byval = !cir.vector<256 x !cir.bool>, llvm.noundef} loc({{[^)]+}})) +// LLVM: define dso_local void @vb256(ptr noundef byval(i256) align 32 %{{[^,)]+}}) +// AVX: define dso_local void @vb256(<256 x i1> noundef %{{[^,)]+}}) + +struct SB4 sb4(struct SB4 s) { return s; } + +// CIR: cir.func {{.*}} @sb4(%arg0: !u8i loc({{[^)]+}})) -> !u8i +// LLVM: define dso_local i8 @sb4(i8 %{{[^,)]+}}) + +struct SB4x2 sb4x2(struct SB4x2 s) { return s; } + +// CIR: cir.func {{.*}} @sb4x2(%arg0: !u64i loc({{[^)]+}})) -> !u64i +// LLVM: define dso_local i64 @sb4x2(i64 %{{[^,)]+}}) + +struct SB4Arr sb4arr(struct SB4Arr s) { return s; } + +// CIR: cir.func {{.*}} @sb4arr(%arg0: !cir.int<u, 24> loc({{[^)]+}})) -> !cir.int<u, 24> +// LLVM: define dso_local i24 @sb4arr(i24 %{{[^,)]+}}) + +struct SB64x2 sb64x2(struct SB64x2 s) { return s; } + +// CIR: cir.func {{.*}} @sb64x2(%arg0: !cir.double loc({{[^)]+}}), %arg1: !cir.double loc({{[^)]+}})) -> !rec_anon_struct +// LLVM: define dso_local { double, double } @sb64x2(double %{{[^,)]+}}, double %{{[^,)]+}}) + +union UB4 ub4(union UB4 u) { return u; } + +// CIR: cir.func {{.*}} @ub4(%arg0: !s8i loc({{[^)]+}})) -> !s8i +// LLVM: define dso_local i8 @ub4(i8 %{{[^,)]+}}) + +// Past the integer registers a bool vector still coerces to an integer. +void stack_vb4(long a, long b, long c, long d, long e, long f, b4 v) {} + +// CIR: cir.func {{.*}} @stack_vb4(%arg0: !s64i {llvm.noundef} loc({{[^)]+}}), %arg1: !s64i {llvm.noundef} loc({{[^)]+}}), %arg2: !s64i {llvm.noundef} loc({{[^)]+}}), %arg3: !s64i {llvm.noundef} loc({{[^)]+}}), %arg4: !s64i {llvm.noundef} loc({{[^)]+}}), %arg5: !s64i {llvm.noundef} loc({{[^)]+}}), %arg6: !u8i {llvm.noundef} loc({{[^)]+}})) +// LLVM: define dso_local void @stack_vb4(i64 noundef %{{[^,)]+}}, i64 noundef %{{[^,)]+}}, i64 noundef %{{[^,)]+}}, i64 noundef %{{[^,)]+}}, i64 noundef %{{[^,)]+}}, i64 noundef %{{[^,)]+}}, i8 noundef %{{[^,)]+}}) + +b4 call_vb4(b4 v) { return vb4(v); } + +// CIR: cir.func {{.*}} @call_vb4(%arg0: !u8i {llvm.noundef} loc({{[^)]+}})) -> !u8i +// CIR: cir.call @vb4(%{{[^)]+}}) : (!u8i {llvm.noundef}) -> !u8i + +// LLVM: define dso_local i8 @call_vb4(i8 noundef %{{[^,)]+}}) +// LLVM: call i8 @vb4(i8 noundef %{{[^,)]+}}) + +void var(int n, ...); +void call_var(b4 v) { var(1, v); } + +// CIR: cir.func {{.*}} @call_var(%arg0: !u8i {llvm.noundef} loc({{[^)]+}})) +// CIR: cir.call @var(%{{[^,]+}}, %{{[^)]+}}) : (!s32i {llvm.noundef}, !u8i {llvm.noundef}) -> () +// LLVM: call void (i32, ...) @var(i32 noundef 1, i8 noundef %{{[^,)]+}}) + +void call_var24(b24 v) { var(1, v); } + +// CIR: cir.call @var(%{{[^,]+}}, %{{[^)]+}}) : (!s32i {llvm.noundef}, !u32i) -> () +// LLVM: call void (i32, ...) @var(i32 noundef 1, i32 %{{[^,)]+}}) + +b128 call_vb128(b128 v) { return vb128(v); } + +// CIR: cir.call @vb128(%{{[^)]+}}) : (!cir.vector<128 x !cir.bool> {llvm.noundef}) -> !cir.vector<128 x !cir.bool> +// LLVM: call <128 x i1> @vb128(<128 x i1> noundef %{{[^,)]+}}) + +b40 call_vb40(b40 v) { return vb40(v); } + +// CIR: cir.call @vb40(%{{[^)]+}}) : (!cir.double) -> !cir.double +// LLVM: call double @vb40(double %{{[^,)]+}}) + +void call_vb256(b256 v) { vb256(v); } + +// CIR: cir.call @vb256(%{{[^)]+}}) : (!cir.ptr<!cir.vector<256 x !cir.bool>> {llvm.align = 32 : i64, llvm.byval = !cir.vector<256 x !cir.bool>, llvm.noundef}) -> () +// LLVM: call void @vb256(ptr noundef byval(i256) align 32 %{{[^,)]+}}) +// AVX: call void @vb256(<256 x i1> noundef %{{[^,)]+}}) + +// The byval pointer is noundef even though the 130-bit storage is not a whole +// number of bytes. +void vb130(b130 v) {} + +// CIR: cir.func {{.*}} @vb130(%arg0: !cir.ptr<!cir.vector<130 x !cir.bool>> {llvm.align = 32 : i64, llvm.byval = !cir.vector<130 x !cir.bool>, llvm.noundef} loc({{[^)]+}})) +// LLVM: define dso_local void @vb130(ptr noundef byval(i130) align 32 %{{[^,)]+}}) + +// A record of one 128-bit bool vector is coerced to the vector. +struct SB128 sb128(struct SB128 s) { return s; } + +// CIR: cir.func {{.*}} @sb128(%arg0: !cir.vector<128 x !cir.bool> loc({{[^)]+}})) -> !cir.vector<128 x !cir.bool> +// LLVM: define dso_local <128 x i1> @sb128(<128 x i1> %{{[^,)]+}}) + +struct SB4F sb4f(struct SB4F s) { return s; } + +// CIR: cir.func {{.*}} @sb4f(%arg0: !u64i loc({{[^)]+}})) -> !u64i +// LLVM: define dso_local i64 @sb4f(i64 %{{[^,)]+}}) + +struct SB64x3 sb64x3(struct SB64x3 s) { return s; } + +// CIR: cir.func {{.*}} @sb64x3(%arg0: !cir.ptr<!rec_SB64x3> {llvm.align = 8 : i64, llvm.dead_on_unwind, llvm.noalias, llvm.sret = !rec_SB64x3, llvm.writable} loc({{[^)]+}}), %arg1: !cir.ptr<!rec_SB64x3> {llvm.align = 8 : i64, llvm.byval = !rec_SB64x3, llvm.noundef} loc({{[^)]+}})) +// LLVM: define dso_local void @sb64x3(ptr dead_on_unwind noalias writable sret(%struct.SB64x3) align 8 %{{[^,)]+}}, ptr noundef byval(%struct.SB64x3) align 8 %{{[^,)]+}}) + +// A call returning through an sret slot drops noundef from a coerced +// argument too. +struct Big { long a, b, c; }; +struct Big mk24(b24 v); +struct Big sret_vb24(b24 v) { return mk24(v); } + +// CIR: cir.func {{.*}} @sret_vb24(%arg0: !cir.ptr<!rec_Big> {llvm.align = 8 : i64, llvm.dead_on_unwind, llvm.noalias, llvm.sret = !rec_Big, llvm.writable} loc({{[^)]+}}), %arg1: !u32i loc({{[^)]+}})) +// CIR: cir.call @mk24(%arg0, %{{[^)]+}}) : (!cir.ptr<!rec_Big> {llvm.align = 8 : i64, llvm.dead_on_unwind, llvm.sret = !rec_Big, llvm.writable}, !u32i) -> () +// LLVM: define dso_local void @sret_vb24(ptr dead_on_unwind noalias writable sret(%struct.Big) align 8 %{{[^,)]+}}, i32 %{{[^,)]+}}) +// LLVM: call void @mk24(ptr dead_on_unwind writable sret(%struct.Big) align 8 %{{[^,)]+}}, i32 %{{[^,)]+}}) + +b4 get_va4(int n, ...) { + __builtin_va_list ap; + __builtin_va_start(ap, n); + b4 v = __builtin_va_arg(ap, b4); + __builtin_va_end(ap); + return v; +} + +// CIR: cir.func {{.*}} @get_va4(%arg0: !s32i {llvm.noundef} loc({{[^)]+}}), ...) -> !u8i +// LLVM: define dso_local i8 @get_va4(i32 noundef %{{[^,)]+}}, ...) +// LLVM: icmp ule i32 %{{[^,]+}}, 40 +// LLVM: add i32 %{{[^,]+}}, 8 +// LLVM: load i8, ptr %{{[^,]+}}, align 1 diff --git a/clang/test/CIR/CodeGenBuiltins/builtins-elementwise-bool-nyi.c b/clang/test/CIR/CodeGenBuiltins/builtins-elementwise-bool-nyi.c index 73b31e8f5b5691..933a1298ae5b9b 100644 --- a/clang/test/CIR/CodeGenBuiltins/builtins-elementwise-bool-nyi.c +++ b/clang/test/CIR/CodeGenBuiltins/builtins-elementwise-bool-nyi.c @@ -1,6 +1,4 @@ -// TODO(cir): drop -fno-clangir-call-conv-lowering once CallConvLowering -// supports vector types. -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -fno-clangir-call-conv-lowering %s -verify -emit-cir -o - +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -verify -emit-cir -o - typedef _Bool vbool4 __attribute__((ext_vector_type(4))); diff --git a/clang/test/CIR/Transforms/abi-lowering/x86_64-vector.cir b/clang/test/CIR/Transforms/abi-lowering/x86_64-vector.cir index d5d47a8491b44d..56f472e47f83e1 100644 --- a/clang/test/CIR/Transforms/abi-lowering/x86_64-vector.cir +++ b/clang/test/CIR/Transforms/abi-lowering/x86_64-vector.cir @@ -105,6 +105,31 @@ module attributes { // CHECK: cir.func{{.*}} @take_union_floats(%arg0: !cir.vector<2 x !cir.float>) + // A bool vector is packed one bit per element, so four bools occupy one byte + // and are passed as an i8, which keeps noundef. + cir.func @vbool4(%arg0: !cir.vector<4 x !cir.bool> {llvm.noundef}) -> (!cir.vector<4 x !cir.bool> {llvm.noundef}) { + cir.return %arg0 : !cir.vector<4 x !cir.bool> + } + + // CHECK: cir.func{{.*}} @vbool4(%arg0: !u8i {llvm.noundef}) -> (!u8i {llvm.noundef}) + + // Twenty-four bools are passed as an i32, whose top byte is undefined, so + // noundef is dropped from the argument and the return at both the + // definition and the call. + cir.func @vbool24(%arg0: !cir.vector<24 x !cir.bool> {llvm.noundef}) -> (!cir.vector<24 x !cir.bool> {llvm.noundef}) { + cir.return %arg0 : !cir.vector<24 x !cir.bool> + } + + // CHECK: cir.func{{.*}} @vbool24(%arg0: !u32i) -> !u32i + + cir.func @call_vbool24(%arg0: !cir.vector<24 x !cir.bool>) -> !cir.vector<24 x !cir.bool> { + %0 = cir.call @vbool24(%arg0) : (!cir.vector<24 x !cir.bool> {llvm.noundef}) -> (!cir.vector<24 x !cir.bool> {llvm.noundef}) + cir.return %0 : !cir.vector<24 x !cir.bool> + } + + // CHECK: cir.func{{.*}} @call_vbool24(%arg0: !u32i) -> !u32i + // CHECK: cir.call @vbool24(%{{.+}}) : (!u32i) -> !u32i + cir.func @call_v128(%arg0: !cir.vector<4 x !cir.float>) -> !cir.vector<4 x !cir.float> { %0 = cir.call @v128(%arg0) : (!cir.vector<4 x !cir.float>) -> !cir.vector<4 x !cir.float> cir.return %0 : !cir.vector<4 x !cir.float> @@ -123,3 +148,6 @@ module attributes { // LLVM: define void @take_farr(<2 x float> %{{[^,)]+}}) // LLVM: define void @take_f3(<2 x float> %{{[^,)]+}}, float %{{[^,)]+}}) // LLVM: define void @take_union_floats(<2 x float> %{{[^,)]+}}) +// LLVM: define noundef i8 @vbool4(i8 noundef %{{[^,)]+}}) +// LLVM: define i32 @vbool24(i32 %{{[^,)]+}}) +// LLVM: call i32 @vbool24(i32 %{{[^,)]+}}) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
