[llvm-branch-commits] [clang] [CIR] Accept fixed-width vectors in x86_64 callconv lowering (PR #215118)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Aug 9 10:24:02 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir
Author: Adam Smith (adams381)
<details>
<summary>Changes</summary>
The CallConvLowering bridge rejects a vector in a parameter or return position, so a function taking one fails the pass. It also never reads the AVX level, which is what decides whether a vector wider than 128 bits reaches a register.
A vector is accepted now where the classifier and clang size it the same way, which means a whole-byte element and a power-of-two width. Scalable vectors and the other widths stay rejected. The module's AVX level comes from the target ABI name, as `CodeGenModule` does. A classifier per level lets a target attribute raise it for one function. An ABI older than the rule pins every function back to the module's level. A direct call takes its callee's level, and an indirect call the level of the function containing it.
CIRGen records target features on a definition but not on a declaration, so a declaration carrying the attribute is classified at the module's level until [#<!-- -->214986](https://github.com/llvm/llvm-project/pull/214986) lands.
Depends on [#<!-- -->215117](https://github.com/llvm/llvm-project/pull/215117)
Assisted-by: Cursor / claude-opus-5
---
Patch is 54.34 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/215118.diff
10 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/Passes.h (+3-4)
- (modified) clang/include/clang/CIR/Dialect/Passes.td (+4)
- (modified) clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp (+131-33)
- (modified) clang/lib/CIR/Lowering/CIRPasses.cpp (+24-4)
- (modified) clang/test/CIR/CodeGen/call-conv-lowering-x86_64-abi-compat.c (+56-6)
- (added) clang/test/CIR/CodeGen/call-conv-lowering-x86_64-avx.c (+165)
- (modified) clang/test/CIR/CodeGen/call-conv-lowering-x86_64.c (+77)
- (modified) clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir (+43)
- (modified) clang/test/CIR/Transforms/abi-lowering/x86_64-variadic-call.cir (+43-28)
- (modified) clang/test/CIR/Transforms/abi-lowering/x86_64-vector.cir (+69-6)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/Passes.h b/clang/include/clang/CIR/Dialect/Passes.h
index 888e7b833b1cf..6c8bec15c3838 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -36,10 +36,9 @@ std::unique_ptr<Pass> createCIREHABILoweringPass();
std::unique_ptr<Pass> createCXXABILoweringPass();
std::unique_ptr<Pass> createTargetLoweringPass();
std::unique_ptr<Pass> createCallConvLoweringPass();
-std::unique_ptr<Pass>
-createCallConvLoweringPass(cir::CallConvTarget target,
- llvm::abi::X86AVXABILevel x86AvxAbiLevel,
- const llvm::abi::ABICompatInfo &x86AbiCompat);
+std::unique_ptr<Pass> createCallConvLoweringPass(
+ cir::CallConvTarget target, llvm::abi::X86AVXABILevel x86AvxAbiLevel,
+ bool x86TargetAttrAvx, const llvm::abi::ABICompatInfo &x86AbiCompat);
std::unique_ptr<Pass> createHoistAllocasPass();
std::unique_ptr<Pass> createLoweringPreparePass();
std::unique_ptr<Pass> createLoweringPreparePass(clang::ASTContext *astCtx);
diff --git a/clang/include/clang/CIR/Dialect/Passes.td b/clang/include/clang/CIR/Dialect/Passes.td
index 04ff1c92cb7f7..bc1b99da01440 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -260,6 +260,10 @@ def CallConvLowering : Pass<"cir-call-conv-lowering", "mlir::ModuleOp"> {
clEnumValN(llvm::abi::X86AVXABILevel::AVX, "avx", "AVX"),
clEnumValN(llvm::abi::X86AVXABILevel::AVX512, "avx512", "AVX512")
)}]>,
+ Option<"x86TargetAttrAvx", "x86-target-attr-avx", "bool",
+ /*default=*/"true",
+ "Let a function's own target features raise its AVX ABI level "
+ "above x86-avx-abi-level">,
];
}
diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index 10ed3d81f8d56..a567ae220c13a 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -47,8 +47,13 @@
#include "llvm/ABI/FunctionInfo.h"
#include "llvm/ABI/TargetInfo.h"
#include "llvm/ABI/Types.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/IR/CallingConv.h"
+#include "llvm/Support/MathExtras.h"
+
+#include <algorithm>
+#include <array>
using namespace mlir;
using namespace mlir::abi;
@@ -68,10 +73,11 @@ namespace {
// SysV x86_64 classifier, and converts the result back into the
// dialect-agnostic mlir::abi::FunctionClassification that CIRABIRewriteContext
// consumes. Integer (including `_BitInt` up to 128 bits) / pointer / bool /
-// floating-point scalars are handled, as are struct / union / array aggregates
-// and `_Complex`. Vectors, packed or padded records, and a union no member of
-// which spans its declared size are reported NYI by classifyX86_64Function so
-// an unsupported signature fails the pass instead of being misclassified.
+// floating-point scalars are handled, as are struct / union / array aggregates,
+// `_Complex`, and a fixed-width vector whose width is a power of two. Other
+// vectors, packed or padded records, and a union no member of which spans its
+// declared size are reported NYI by classifyX86_64Function so an unsupported
+// signature fails the pass instead of being misclassified.
//===----------------------------------------------------------------------===//
/// Whether a struct's declared argument-passing kind (from the module's
@@ -103,8 +109,9 @@ static llvm::Align recordDeclaredAlign(ModuleOp modOp, cir::RecordType recTy,
/// bits (including `_BitInt` and `__int128`), pointer, bool, void, or any
/// floating-point type. Aggregates: a complete struct or union whose members
/// are all themselves supported, or an array of a supported element type.
-/// Also a `_Complex` of a supported element type. Everything else is reported
-/// NYI at the reject() choke point in classifyX86_64Function.
+/// Also a `_Complex`, or a fixed-width vector, of a supported element type.
+/// Everything else is reported NYI at the reject() choke point in
+/// classifyX86_64Function.
static bool isSupportedType(mlir::Type ty, const DataLayout &dl) {
// A pointer is only handled in the default address space (null) or an
// already-lowered target address space. A LangAddressSpaceAttr must be
@@ -135,6 +142,34 @@ static bool isSupportedType(mlir::Type ty, const DataLayout &dl) {
}
if (auto complexTy = dyn_cast<cir::ComplexType>(ty))
return isSupportedType(complexTy.getElementType(), dl);
+ if (auto vecTy = dyn_cast<cir::VectorType>(ty)) {
+ // A scalable vector has no size the SysV eightbyte rules can read, and
+ // 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). A pointer is excluded for a
+ // different reason, its pointee being what abiTypeToCIR drops.
+ mlir::Type elemTy = vecTy.getElementType();
+ if (auto elemInt = dyn_cast<cir::IntType>(elemTy)) {
+ if (elemInt.getWidth() % 8)
+ return false;
+ } else if (auto elemFp = dyn_cast<cir::FPTypeInterface>(elemTy)) {
+ if (&elemFp.getFloatSemantics() == &llvm::APFloat::x87DoubleExtended())
+ return false;
+ } else {
+ return false;
+ }
+ // Clang also rounds the vector's own width up to a power of two, and the
+ // classifier branches on the exact width, so a three-char vector would be
+ // classified at 24 bits where clang uses 32.
+ if (!llvm::isPowerOf2_64(dl.getTypeSizeInBits(ty).getFixedValue()))
+ return false;
+ return isSupportedType(elemTy, dl);
+ }
if (auto arrTy = dyn_cast<cir::ArrayType>(ty))
return isSupportedType(arrTy.getElementType(), dl);
if (auto recTy = dyn_cast<cir::RecordType>(ty)) {
@@ -254,6 +289,14 @@ static const llvm::abi::Type *mapCIRType(mlir::Type type,
mapCIRType(complexTy.getElementType(), typeMapper, dl, modOp),
llvm::Align(dl.getTypeABIAlignment(type)));
})
+ .Case([&](cir::VectorType vecTy) {
+ // isSupportedType rejects a scalable vector, so the element count is
+ // always fixed here.
+ return tb.getVectorType(
+ mapCIRType(vecTy.getElementType(), typeMapper, dl, modOp),
+ llvm::ElementCount::getFixed(vecTy.getSize()),
+ llvm::Align(dl.getTypeABIAlignment(type)));
+ })
.Case([&](cir::ArrayType arrTy) {
const llvm::abi::Type *elemAbi =
mapCIRType(arrTy.getElementType(), typeMapper, dl, modOp);
@@ -332,10 +375,10 @@ convertABIArgInfo(const llvm::abi::ArgInfo &info, MLIRContext *ctx,
// type, so a non-null coerce does not by itself mean a rewrite is needed.
const llvm::abi::Type *coerceAbi = info.getCoerceToType();
bool isAggregate = isa_and_present<cir::RecordType, cir::ArrayType>(origTy);
- // For a _Complex the classifier's coerce is only sometimes the natural
- // type, so it has to be read rather than assumed.
+ // For a _Complex or a vector the classifier's coerce is only sometimes the
+ // natural type, so it has to be read rather than assumed.
bool comparesAgainstCoerce =
- coerceAbi && isa_and_present<cir::ComplexType>(origTy);
+ coerceAbi && isa_and_present<cir::ComplexType, cir::VectorType>(origTy);
bool coerceIsRegisterTuple =
isa_and_present<llvm::abi::RecordType>(coerceAbi);
// Compare widths rather than identity: a coerce no wider than the natural
@@ -379,8 +422,7 @@ convertABIArgInfo(const llvm::abi::ArgInfo &info, MLIRContext *ctx,
/// Where \p fnTy's declared parameters end and its ellipsis arguments begin.
///
/// The only x86_64 rule that reads this boundary sends an unnamed vector wider
-/// than 128 bits to memory, and isSupportedType rejects every vector, so no
-/// input this bridge accepts can observe the difference.
+/// than 128 bits to memory.
static llvm::abi::RequiredArgs requiredArgs(cir::FuncType fnTy) {
if (!fnTy.isVarArg())
return llvm::abi::RequiredArgs::All;
@@ -463,6 +505,34 @@ static std::optional<FunctionClassification> classifyX86_64Signature(
return fc;
}
+/// The AVX level in force at \p func, following getEffectiveX86AVXABILevel in
+/// clang/lib/CodeGen/Targets/X86.cpp. Classic reads the target attribute
+/// itself, where this reads the feature list CIRGen recorded from it. Two
+/// consequences: a declaration gets \p base, CIRGen recording features only on
+/// definitions, and target_clones and cpu_specific will need excluding once
+/// multiversioning is implemented, classic leaving those at the module level.
+static llvm::abi::X86AVXABILevel funcAvxLevel(cir::FuncOp func,
+ llvm::abi::X86AVXABILevel base) {
+ auto features = func->getAttrOfType<mlir::StringAttr>("cir.target-features");
+ if (!features)
+ return base;
+ // A '-' entry disables the feature, so match a whole '+' entry rather than
+ // searching for the name.
+ auto enabled = [&](llvm::StringRef name) {
+ for (llvm::StringRef feature : llvm::split(features.getValue(), ','))
+ if (feature.consume_front("+") && feature == name)
+ return true;
+ return false;
+ };
+ // avx512f implies avx, so both entries are present. Test the wider name
+ // first or the AVX512 branch is unreachable.
+ if (enabled("avx512f"))
+ return std::max(base, llvm::abi::X86AVXABILevel::AVX512);
+ if (enabled("avx"))
+ return std::max(base, llvm::abi::X86AVXABILevel::AVX);
+ return base;
+}
+
/// Classify a cir.func for x86_64 SysV using the LLVM ABI library. Returns
/// std::nullopt and emits an NYI error if the signature uses a type the bridge
/// does not handle yet.
@@ -632,16 +702,33 @@ void CallConvLoweringPass::runOnOperation() {
CIRABIRewriteContext rewriteCtx(moduleOp, dl);
SymbolTable symbolTable(moduleOp);
- // For the x86_64 target, build the LLVM ABI library classifier once and
- // reuse it (and its type mapper) across every function.
+ // A per-function target attribute can raise the AVX level, so one classifier
+ // per module would misclassify a wide vector in such a function.
+ static constexpr unsigned numAvxLevels =
+ static_cast<unsigned>(llvm::abi::X86AVXABILevel::AVX512) + 1;
+ bool isX86 = target == cir::CallConvTarget::X86_64;
std::optional<mlir::abi::ABITypeMapper> x86TypeMapper;
- std::unique_ptr<llvm::abi::TargetInfo> x86Target;
- if (target == cir::CallConvTarget::X86_64) {
+ std::array<std::unique_ptr<llvm::abi::TargetInfo>, numAvxLevels> x86Targets;
+ if (isX86)
x86TypeMapper.emplace(dl);
- x86Target = llvm::abi::createX86_64TargetInfo(
- x86TypeMapper->getTypeBuilder(), x86AvxAbiLevel.getValue(),
- /*Has64BitPointers=*/true, x86AbiCompat);
- }
+ auto x86TargetFor =
+ [&](llvm::abi::X86AVXABILevel level) -> const llvm::abi::TargetInfo & {
+ assert(static_cast<unsigned>(level) < numAvxLevels &&
+ "a new X86AVXABILevel needs a slot in x86Targets");
+ std::unique_ptr<llvm::abi::TargetInfo> &slot =
+ x86Targets[static_cast<unsigned>(level)];
+ if (!slot)
+ slot = llvm::abi::createX86_64TargetInfo(
+ x86TypeMapper->getTypeBuilder(), level,
+ /*Has64BitPointers=*/true, x86AbiCompat);
+ return *slot;
+ };
+ llvm::abi::X86AVXABILevel baseAvxLevel = x86AvxAbiLevel.getValue();
+ auto avxLevelFor = [&](cir::FuncOp func) -> llvm::abi::X86AVXABILevel {
+ if (!x86TargetAttrAvx || !func)
+ return baseAvxLevel;
+ return funcAvxLevel(func, baseAvxLevel);
+ };
// Classify every cir.func up front. No IR mutation happens here, so
// later walks can consult any function's classification regardless of
@@ -650,8 +737,9 @@ void CallConvLoweringPass::runOnOperation() {
bool anyFailed = false;
moduleOp.walk([&](cir::FuncOp f) {
std::optional<FunctionClassification> fc;
- if (x86Target)
- fc = classifyX86_64Function(f, dl, *x86TypeMapper, *x86Target, moduleOp);
+ if (isX86)
+ fc = classifyX86_64Function(f, dl, *x86TypeMapper,
+ x86TargetFor(avxLevelFor(f)), moduleOp);
else
fc = classifyFunction(f, dl, target, classificationAttr);
if (!fc) {
@@ -692,7 +780,7 @@ void CallConvLoweringPass::runOnOperation() {
// drivers the classification comes from a fixed per-function source, so
// such a call stays short a classification and rewriteCallSite reports it.
cir::FuncType calleeTy = callee.getFunctionType();
- if (!x86Target || call.getNumArgOperands() <= calleeTy.getNumInputs())
+ if (!isX86 || call.getNumArgOperands() <= calleeTy.getNumInputs())
return;
// A callee declared without a prototype also takes more operands than it
// declares, and the verifier allows it. Those extra arguments are named
@@ -704,8 +792,14 @@ void CallConvLoweringPass::runOnOperation() {
anyFailed = true;
return;
}
- std::optional<FunctionClassification> fc = classifyX86_64VariadicCall(
- call, calleeTy, dl, *x86TypeMapper, *x86Target, moduleOp);
+ // The callee's level, not the caller's: this pass rewrites the definition
+ // and its call sites from one classification, so they have to agree.
+ // Classic instead arranges every call site from the caller and reports a
+ // caller whose level disagrees with its callee in checkFunctionCallABI,
+ // which has no equivalent here yet.
+ std::optional<FunctionClassification> fc =
+ classifyX86_64VariadicCall(call, calleeTy, dl, *x86TypeMapper,
+ x86TargetFor(avxLevelFor(callee)), moduleOp);
if (!fc) {
anyFailed = true;
return;
@@ -805,11 +899,15 @@ void CallConvLoweringPass::runOnOperation() {
cir::FuncType funcTy = indirectCalleeType(c);
auto classifySignature =
[&](mlir::TypeRange argTypes) -> std::optional<FunctionClassification> {
- if (x86Target)
- return classifyX86_64Signature(funcTy.getReturnType(), argTypes,
- requiredArgs(funcTy), ctx, dl,
- *x86TypeMapper, *x86Target, moduleOp,
- [&]() { return c->emitOpError(); });
+ // A callee resolved at run time carries no features of its own, so the
+ // level comes from the function containing the call, which is the
+ // declaration classic arranges every call site from.
+ if (isX86)
+ return classifyX86_64Signature(
+ funcTy.getReturnType(), argTypes, requiredArgs(funcTy), ctx, dl,
+ *x86TypeMapper,
+ x86TargetFor(avxLevelFor(c->getParentOfType<cir::FuncOp>())),
+ moduleOp, [&]() { return c->emitOpError(); });
return withReturnVoidness(
mlir::abi::test::classify(argTypes, funcTy.getReturnType(), dl),
funcTy.getReturnType());
@@ -855,12 +953,12 @@ std::unique_ptr<Pass> mlir::createCallConvLoweringPass() {
return std::make_unique<CallConvLoweringPass>();
}
-std::unique_ptr<Pass>
-mlir::createCallConvLoweringPass(cir::CallConvTarget target,
- llvm::abi::X86AVXABILevel x86AvxAbiLevel,
- const llvm::abi::ABICompatInfo &x86AbiCompat) {
+std::unique_ptr<Pass> mlir::createCallConvLoweringPass(
+ cir::CallConvTarget target, llvm::abi::X86AVXABILevel x86AvxAbiLevel,
+ bool x86TargetAttrAvx, const llvm::abi::ABICompatInfo &x86AbiCompat) {
CallConvLoweringOptions options;
options.target = target;
options.x86AvxAbiLevel = x86AvxAbiLevel;
+ options.x86TargetAttrAvx = x86TargetAttrAvx;
return std::make_unique<CallConvLoweringPass>(options, x86AbiCompat);
}
diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp b/clang/lib/CIR/Lowering/CIRPasses.cpp
index dad17f2ef8659..972dc6d1a4a8b 100644
--- a/clang/lib/CIR/Lowering/CIRPasses.cpp
+++ b/clang/lib/CIR/Lowering/CIRPasses.cpp
@@ -29,6 +29,26 @@ static CallConvTarget getCallConvTarget(const llvm::Triple &triple) {
return CallConvTarget::None;
}
+/// The AVX level the classifier uses to size a native vector. Read from the
+/// target ABI name, as CodeGenModule does.
+static llvm::abi::X86AVXABILevel getX86AVXABILevel(llvm::StringRef abi) {
+ if (abi == "avx512")
+ return llvm::abi::X86AVXABILevel::AVX512;
+ if (abi == "avx")
+ return llvm::abi::X86AVXABILevel::AVX;
+ return llvm::abi::X86AVXABILevel::None;
+}
+
+/// Whether `__attribute__((target(...)))` on a function may raise its AVX ABI
+/// level above the command line's. Mirrors getEffectiveX86AVXABILevel in
+/// clang/lib/CodeGen/Targets/X86.cpp, which keeps a target that opts out, and
+/// any ABI older than the rule, at the module level.
+static bool allowsX86TargetAttrAvx(const clang::ASTContext &astContext) {
+ return !astContext.getTargetInfo().getTriple().isPS() &&
+ astContext.getLangOpts().getClangABICompat() >
+ clang::LangOptions::ClangABI::Ver23;
+}
+
/// The x86_64 ABI-compatibility flags, derived from the target and the
/// requested compatibility version. Every flag defaults to true in the ABI
/// library, which is not what any target computes: Clang11Compat is false for a
@@ -95,12 +115,12 @@ runCIRToCIRPasses(mlir::ModuleOp theModule, mlir::MLIRContext &mlirContext,
// so it must run after CXXABILowering has lowered C++ ABI types to plain
// records the classifier can handle. Only the x86_64 System V classifier
// is implemented; other targets are left unchanged.
- CallConvTarget target =
- getCallConvTarget(astContext.getTargetInfo().getTriple());
+ const clang::TargetInfo &targetInfo = astContext.getTargetInfo();
+ CallConvTarget target = getCallConvTarget(targetInfo.getTriple());
if (target != CallConvTarget::None)
pm.addPass(mlir::createCallConvLoweringPass(
- target, llvm::abi::X86AVXABILevel::None,
- getX86ABICompatInfo(astContext)));
+ target, getX86AVXABILevel(targetInfo.getABI()),
+ allowsX86TargetAttrAvx(astContext), getX86ABICompatInfo(astContext)));
}
pm.addPass(mlir::createLoweringPreparePass(&astContext));
diff --git a/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-abi-compat.c b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-abi-compat.c
index 6a1d9a3c4d3d2..62bc030ff1579 100644
--- a/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-abi-compat.c
+++ b/clang/test/CIR/CodeGen/call-conv-lowering-x86_64-abi-compat.c
@@ -1,13 +1,32 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -clangir-enable-call-conv-lowering -emit-llvm %s -o %t-cir.ll
-// RUN: FileCheck --check-prefix=LINUX-CIR --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=LINUX-OGCG --input-file=%t.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -target-feature +avx -fclangir -clangir-enable-call-conv-lowering -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefixes=LINUX,LINUX-CIR --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -target-feature +avx -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefixes=LINUX,LINUX-OGCG --input-file=%t.ll %s
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -fclangir -clangir-enable-call-conv-lowering -emit-llvm %s -o %t-darwin-cir.ll
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -target-feature +avx -fclang-abi-compat=3.8 -fclangir -clangir-enable-call-conv-lowering -emit-llvm %s -o %t-38-cir.ll
+// RUN: FileCheck --check-prefix=LINUX38 --input-file=%t-38-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -target-feature +avx -fclang-abi-compat=3.8 -emit-llvm %s -o %t-38.ll
+// RUN: FileCheck --check-prefix=LINUX38 --input-file=%t-38.ll %s
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -target-feature +avx -fclang-abi-compat=9 -fclangir -clangir-enable-call-conv-lowering -emit-llvm %s -o %t-9-cir.ll
+// RUN: FileCheck...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/215118
More information about the llvm-branch-commits
mailing list