https://github.com/adams381 updated 
https://github.com/llvm/llvm-project/pull/209636

>From 63848c2cc05a9597f7fe44130217890b4d3fc05f Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Tue, 14 Jul 2026 14:25:48 -0700
Subject: [PATCH 1/2] [CIR] Add x86_64 scalar calling-convention lowering

CallConvLowering has a test target and a classification-injection mode
but no production ABI classifier.  Add a target=x86_64 mode that bridges
the LLVM ABI Lowering Library's System V x86_64 classifier to the pass:
it maps scalar CIR types to llvm::abi types, runs the classifier, and
converts the result into the mlir::abi FunctionClassification that
CIRABIRewriteContext already consumes.

This first step handles scalar signatures only -- integer up to 64 bits,
pointer, bool, f32, and f64.  Any other type (records, arrays, vectors,
complex, wide or uncommon floats, _BitInt, __int128, member pointers)
is reported through emitOpError as not-yet-implemented so it fails
loudly instead of being misclassified; those types are added in stacked
follow-up PRs.  The pass stays exercised through cir-opt only; wiring it
into the -fclangir pipeline is a separate change.

Assisted-by: Cursor / claude-opus-4.8
---
 clang/include/clang/CIR/Dialect/Passes.h      |   2 +
 clang/include/clang/CIR/Dialect/Passes.td     |   4 +-
 .../lib/CIR/Dialect/Transforms/CMakeLists.txt |   1 +
 .../Transforms/CallConvLoweringPass.cpp       | 188 +++++++++++++++++-
 .../abi-lowering/x86_64-scalars.cir           |  74 +++++++
 5 files changed, 266 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CIR/Transforms/abi-lowering/x86_64-scalars.cir

diff --git a/clang/include/clang/CIR/Dialect/Passes.h 
b/clang/include/clang/CIR/Dialect/Passes.h
index 651a1319cfe5d..c655c2adf402b 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -28,6 +28,8 @@ 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(llvm::StringRef target,
+                                                 unsigned x86AvxAbiLevel);
 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 9cdeb4d42d5a1..1c3c8ead1842f 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -240,11 +240,13 @@ def CallConvLowering : Pass<"cir-call-conv-lowering", 
"mlir::ModuleOp"> {
   let dependentDialects = ["cir::CIRDialect"];
   let options = [
     Option<"target", "target", "std::string", /*default=*/"\"\"",
-           "Target whose ABI rules drive classification (currently: test)">,
+           "Target whose ABI rules drive classification (test, x86_64)">,
     Option<"classificationAttr", "classification-attr", "std::string",
            /*default=*/"\"\"",
            "Function attribute name carrying a pre-built 
FunctionClassification "
            "DictionaryAttr (alternative to target=, used by tests)">,
+    Option<"x86AvxAbiLevel", "x86-avx-abi-level", "unsigned", /*default=*/"0",
+           "AVX ABI level for the x86_64 target (0=None, 1=AVX, 2=AVX512)">,
   ];
 }
 
diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt 
b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
index 5edbfcf467f90..82078bf2e8f73 100644
--- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
@@ -22,6 +22,7 @@ add_clang_library(MLIRCIRTransforms
   clangAST
   clangBasic
 
+  LLVMABI
   MLIRABI
   MLIRAnalysis
   MLIRDLTIDialect
diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp 
b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index c00947593517e..5d153c9699cc6 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -34,6 +34,7 @@
 #include "TargetLowering/CIRABIRewriteContext.h"
 
 #include "mlir/ABI/ABIRewriteContext.h"
+#include "mlir/ABI/ABITypeMapper.h"
 #include "mlir/ABI/Targets/Test/TestTarget.h"
 #include "mlir/Dialect/DLTI/DLTI.h"
 #include "mlir/IR/Builders.h"
@@ -43,6 +44,10 @@
 #include "mlir/Pass/Pass.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 #include "clang/CIR/Dialect/Passes.h"
+#include "llvm/ABI/FunctionInfo.h"
+#include "llvm/ABI/TargetInfo.h"
+#include "llvm/ABI/Types.h"
+#include "llvm/IR/CallingConv.h"
 
 using namespace mlir;
 using namespace mlir::abi;
@@ -55,6 +60,156 @@ namespace mlir {
 
 namespace {
 
+//===----------------------------------------------------------------------===//
+// x86_64 System V classifier bridge (scalar types)
+//
+// Maps scalar CIR types to llvm::abi::Type, runs the LLVM ABI Lowering
+// Library's SysV x86_64 classifier, and converts the result back into the
+// dialect-agnostic mlir::abi::FunctionClassification that CIRABIRewriteContext
+// consumes.  Only integer / pointer / bool / f32 / f64 signatures are handled;
+// aggregates and other leaf types are reported NYI by classifyX86_64 so an
+// unsupported signature fails the pass instead of being misclassified.
+//===----------------------------------------------------------------------===//
+
+/// llvm::Align requires a power of two; DataLayout can report non-power-of-two
+/// alignments for unusual types.
+static llvm::Align safeAlign(uint64_t a) {
+  return llvm::Align(llvm::PowerOf2Ceil(std::max<uint64_t>(a, 1)));
+}
+
+/// The scalar CIR types the x86_64 bridge handles.  A regular integer up to
+/// 64 bits, pointer, bool, void, f32, or f64 is a single-register Direct or
+/// Extend argument.  `_BitInt`, `__int128`, and wider/other types need 
coercion
+/// or indirect passing, which this scalar bridge does not do.
+static bool isSupportedScalarType(mlir::Type ty) {
+  if (isa<cir::VoidType, cir::BoolType, cir::PointerType, cir::SingleType,
+          cir::DoubleType>(ty))
+    return true;
+  if (auto intTy = dyn_cast<cir::IntType>(ty))
+    return !intTy.getIsBitInt() && intTy.getWidth() <= 64;
+  return false;
+}
+
+/// Convert an llvm::abi::Type coercion type back to a scalar CIR type.
+static mlir::Type abiTypeToCIR(const llvm::abi::Type *ty, MLIRContext *ctx) {
+  if (!ty)
+    return nullptr;
+  if (ty->isVoid())
+    return cir::VoidType::get(ctx);
+  if (auto *intTy = llvm::dyn_cast<llvm::abi::IntegerType>(ty))
+    return cir::IntType::get(ctx, intTy->getSizeInBits().getFixedValue(),
+                             intTy->isSigned());
+  if (auto *fltTy = llvm::dyn_cast<llvm::abi::FloatType>(ty)) {
+    const llvm::fltSemantics *sem = fltTy->getSemantics();
+    if (sem == &llvm::APFloat::IEEEsingle())
+      return cir::SingleType::get(ctx);
+    if (sem == &llvm::APFloat::IEEEdouble())
+      return cir::DoubleType::get(ctx);
+  }
+  if (llvm::isa<llvm::abi::PointerType>(ty))
+    return cir::PointerType::get(cir::VoidType::get(ctx));
+  return nullptr;
+}
+
+/// Map a scalar CIR type to an llvm::abi::Type.  classifyX86_64 pre-filters 
the
+/// signature, so only the scalar types handled here can reach this function.
+static const llvm::abi::Type *mapCIRType(mlir::Type type,
+                                         mlir::abi::ABITypeMapper &typeMapper,
+                                         const DataLayout &dl) {
+  llvm::abi::TypeBuilder &tb = typeMapper.getTypeBuilder();
+  if (auto intTy = dyn_cast<cir::IntType>(type))
+    return tb.getIntegerType(intTy.getWidth(),
+                             safeAlign(dl.getTypeABIAlignment(type)),
+                             intTy.isSigned());
+  if (isa<cir::PointerType>(type))
+    return tb.getPointerType(dl.getTypeSizeInBits(type),
+                             safeAlign(dl.getTypeABIAlignment(type)),
+                             /*AddressSpace=*/0);
+  if (isa<cir::BoolType>(type))
+    return tb.getIntegerType(dl.getTypeSizeInBits(type),
+                             safeAlign(dl.getTypeABIAlignment(type)),
+                             /*Signed=*/false);
+  if (isa<cir::VoidType>(type))
+    return tb.getVoidType();
+  if (isa<cir::SingleType>(type))
+    return tb.getFloatType(llvm::APFloat::IEEEsingle(),
+                           safeAlign(dl.getTypeABIAlignment(type)));
+  if (isa<cir::DoubleType>(type))
+    return tb.getFloatType(llvm::APFloat::IEEEdouble(),
+                           safeAlign(dl.getTypeABIAlignment(type)));
+  llvm_unreachable("mapCIRType: type not pre-filtered by classifyX86_64");
+}
+
+/// Convert an llvm::abi::ArgInfo for a scalar type into the ArgClassification
+/// consumed by CIRABIRewriteContext.
+static ArgClassification convertABIArgInfo(const llvm::abi::ArgInfo &info,
+                                           MLIRContext *ctx,
+                                           mlir::Type origTy) {
+  if (info.isDirect())
+    return ArgClassification::getDirect(nullptr);
+  if (info.isExtend()) {
+    if (origTy && isa<cir::BoolType>(origTy))
+      return ArgClassification::getExtend(nullptr, info.isSignExt());
+    if (origTy && !isa<cir::IntType>(origTy))
+      return ArgClassification::getDirect(nullptr);
+    mlir::Type coerced = abiTypeToCIR(info.getCoerceToType(), ctx);
+    return ArgClassification::getExtend(coerced, info.isSignExt());
+  }
+  if (info.isIndirect())
+    return ArgClassification::getIndirect(info.getIndirectAlign(),
+                                          info.getIndirectByVal());
+  return ArgClassification::getIgnore();
+}
+
+/// 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 scalar
+/// bridge does not handle yet.
+static std::optional<FunctionClassification>
+classifyX86_64(cir::FuncOp func, const DataLayout &dl,
+               mlir::abi::ABITypeMapper &typeMapper,
+               const llvm::abi::TargetInfo &targetInfo) {
+  MLIRContext *ctx = func->getContext();
+  cir::FuncType fnTy = func.getFunctionType();
+  mlir::Type retCIR = fnTy.getReturnType();
+  bool voidRet = !retCIR || isa<cir::VoidType>(retCIR);
+
+  auto reject = [&](mlir::Type t) -> bool {
+    if (isSupportedScalarType(t))
+      return false;
+    func.emitOpError()
+        << "x86_64 calling-convention lowering not yet implemented for type "
+        << t;
+    return true;
+  };
+  if (!voidRet && reject(retCIR))
+    return std::nullopt;
+  for (mlir::Type a : fnTy.getInputs())
+    if (reject(a))
+      return std::nullopt;
+
+  const llvm::abi::Type *retAbi =
+      voidRet ? typeMapper.getTypeBuilder().getVoidType()
+              : mapCIRType(retCIR, typeMapper, dl);
+  SmallVector<const llvm::abi::Type *> argAbi;
+  for (mlir::Type a : fnTy.getInputs())
+    argAbi.push_back(mapCIRType(a, typeMapper, dl));
+
+  std::unique_ptr<llvm::abi::FunctionInfo> fi =
+      llvm::abi::FunctionInfo::create(llvm::CallingConv::C, retAbi, argAbi);
+  targetInfo.computeInfo(*fi);
+
+  FunctionClassification fc;
+  mlir::Type origRet = voidRet ? mlir::Type() : retCIR;
+  fc.returnInfo = convertABIArgInfo(fi->getReturnInfo(), ctx, origRet);
+  auto inputs = fnTy.getInputs();
+  for (unsigned i = 0, e = fi->arg_size(); i < e; ++i) {
+    mlir::Type origArg = i < inputs.size() ? inputs[i] : mlir::Type();
+    fc.argInfos.push_back(
+        convertABIArgInfo(fi->getArgInfo(i).Info, ctx, origArg));
+  }
+  return fc;
+}
+
 bool needsRewrite(const FunctionClassification &fc) {
   if ((fc.returnInfo.kind != ArgKind::Direct) || fc.returnInfo.coercedType)
     return true;
@@ -95,7 +250,10 @@ classifyFunction(cir::FuncOp func, const DataLayout &dl, 
StringRef target,
   if (target == "test")
     return mlir::abi::test::classify(argTypes, returnType, dl);
 
-  func.emitOpError() << "unknown target '" << target << "' (supported: test)";
+  // Note: the "x86_64" target is handled directly in runOnOperation (it needs
+  // a shared ABITypeMapper and TargetInfo), so it never reaches here.
+  func.emitOpError() << "unknown target '" << target
+                     << "' (supported: test, x86_64)";
   return std::nullopt;
 }
 
@@ -140,13 +298,30 @@ 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.
+  std::optional<mlir::abi::ABITypeMapper> x86TypeMapper;
+  std::unique_ptr<llvm::abi::TargetInfo> x86Target;
+  if (target == "x86_64") {
+    x86TypeMapper.emplace(dl);
+    auto avx =
+        static_cast<llvm::abi::X86AVXABILevel>(x86AvxAbiLevel.getValue());
+    x86Target = llvm::abi::createX86_64TargetInfo(
+        x86TypeMapper->getTypeBuilder(), avx, /*Has64BitPointers=*/true,
+        llvm::abi::ABICompatInfo());
+  }
+
   // Classify every cir.func up front.  No IR mutation happens here, so
   // later walks can consult any function's classification regardless of
   // visitation order.
   llvm::MapVector<cir::FuncOp, FunctionClassification> classifications;
   bool anyFailed = false;
   moduleOp.walk([&](cir::FuncOp f) {
-    auto fc = classifyFunction(f, dl, target, classificationAttr);
+    std::optional<FunctionClassification> fc;
+    if (x86Target)
+      fc = classifyX86_64(f, dl, *x86TypeMapper, *x86Target);
+    else
+      fc = classifyFunction(f, dl, target, classificationAttr);
     if (!fc) {
       anyFailed = true;
       return;
@@ -226,3 +401,12 @@ void CallConvLoweringPass::runOnOperation() {
 std::unique_ptr<Pass> mlir::createCallConvLoweringPass() {
   return std::make_unique<CallConvLoweringPass>();
 }
+
+std::unique_ptr<Pass>
+mlir::createCallConvLoweringPass(llvm::StringRef target,
+                                 unsigned x86AvxAbiLevel) {
+  CallConvLoweringOptions options;
+  options.target = target.str();
+  options.x86AvxAbiLevel = x86AvxAbiLevel;
+  return std::make_unique<CallConvLoweringPass>(options);
+}
diff --git a/clang/test/CIR/Transforms/abi-lowering/x86_64-scalars.cir 
b/clang/test/CIR/Transforms/abi-lowering/x86_64-scalars.cir
new file mode 100644
index 0000000000000..006f6532f281d
--- /dev/null
+++ b/clang/test/CIR/Transforms/abi-lowering/x86_64-scalars.cir
@@ -0,0 +1,74 @@
+// RUN: cir-opt %s -cir-call-conv-lowering=target=x86_64 | FileCheck %s
+// RUN: cir-opt %s -cir-call-conv-lowering=target=x86_64 -cir-to-llvm -o - 
2>/dev/null \
+// RUN:   | mlir-translate -mlir-to-llvmir --allow-unregistered-dialect \
+// RUN:   | FileCheck %s --check-prefix=LLVM
+
+!s8i = !cir.int<s, 8>
+!u16i = !cir.int<u, 16>
+!s32i = !cir.int<s, 32>
+!s64i = !cir.int<s, 64>
+
+module attributes {
+  dlti.dl_spec = #dlti.dl_spec<
+    #dlti.dl_entry<i1, dense<8>: vector<2xi64>>,
+    #dlti.dl_entry<i8, dense<8>: vector<2xi64>>,
+    #dlti.dl_entry<i16, dense<16>: vector<2xi64>>,
+    #dlti.dl_entry<i32, dense<32>: vector<2xi64>>,
+    #dlti.dl_entry<i64, dense<64>: vector<2xi64>>>
+} {
+
+  // Register-sized integers are Direct: signature and call sites unchanged.
+  cir.func @passthrough(%arg0: !s32i, %arg1: !s64i) -> !s32i {
+    cir.return %arg0 : !s32i
+  }
+
+  // CHECK: cir.func{{.*}} @passthrough(%arg0: !s32i, %arg1: !s64i) -> !s32i
+  // CHECK-NEXT: cir.return %arg0 : !s32i
+
+  // Floating-point scalars are Direct.
+  cir.func @floats(%arg0: !cir.float, %arg1: !cir.double) -> !cir.double {
+    cir.return %arg1 : !cir.double
+  }
+
+  // CHECK: cir.func{{.*}} @floats(%arg0: !cir.float, %arg1: !cir.double) -> 
!cir.double
+
+  // Pointers are Direct.
+  cir.func @take_ptr(%arg0: !cir.ptr<!s32i>) -> !cir.ptr<!s32i> {
+    cir.return %arg0 : !cir.ptr<!s32i>
+  }
+
+  // CHECK: cir.func{{.*}} @take_ptr(%arg0: !cir.ptr<!s32i>) -> !cir.ptr<!s32i>
+
+  // Signed sub-register integer is sign-extended.
+  cir.func @take_s8(%arg0: !s8i) {
+    cir.return
+  }
+
+  // CHECK: cir.func{{.*}} @take_s8(%arg0: !s8i {llvm.signext})
+
+  // Unsigned sub-register integer is zero-extended.
+  cir.func @take_u16(%arg0: !u16i) {
+    cir.return
+  }
+
+  // CHECK: cir.func{{.*}} @take_u16(%arg0: !u16i {llvm.zeroext})
+
+  // bool is zero-extended.
+  cir.func @take_bool(%arg0: !cir.bool) {
+    cir.return
+  }
+
+  // CHECK: cir.func{{.*}} @take_bool(%arg0: !cir.bool {llvm.zeroext})
+
+  // Call site picks up the same extension attribute on the operand.
+  cir.func @call_s8(%arg0: !s8i) {
+    cir.call @take_s8(%arg0) : (!s8i) -> ()
+    cir.return
+  }
+
+  // CHECK: cir.call @take_s8(%arg0) : (!s8i {llvm.signext}) -> ()
+}
+
+// LLVM: define void @take_s8(i8 signext %{{.+}})
+// LLVM: define void @take_u16(i16 zeroext %{{.+}})
+// LLVM: define void @take_bool(i1 zeroext %{{.+}})

>From 4d5b6499120bff25eb71f28df33103e0c173d8a4 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Wed, 15 Jul 2026 15:00:43 -0700
Subject: [PATCH 2/2] [CIR] Address review comments on x86_64 scalars

Convert abiTypeToCIR/mapCIRType to TypeSwitch, and read the real
address space on pointers instead of assuming 0.

Add asserts for the Extend/Indirect/voidRet cases the classifier can
never actually produce for this scalar-only type set, verified
against every ArgInfo::getExtend/getIndirect* call site in
llvm/lib/ABI/Targets/X86.cpp.  Document all four convertABIArgInfo
branches, rename classifyX86_64 to classifyX86_64Function and the
misleading `coerced` local to extendedTy, and replace the `target`
string comparisons with an internal enum so the unknown-target
diagnostic and dispatch share one source of truth instead of a
hand-written name list.

Add return-side sign/zero-extension test coverage; the existing
tests only covered argument extension.

Assisted-by: Cursor / claude-opus-4.8
---
 .../Transforms/CallConvLoweringPass.cpp       | 177 ++++++++++++------
 .../abi-lowering/x86_64-scalars.cir           |  17 ++
 2 files changed, 140 insertions(+), 54 deletions(-)

diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp 
b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index 5d153c9699cc6..987a6e42e43ad 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -47,6 +47,7 @@
 #include "llvm/ABI/FunctionInfo.h"
 #include "llvm/ABI/TargetInfo.h"
 #include "llvm/ABI/Types.h"
+#include "llvm/ADT/TypeSwitch.h"
 #include "llvm/IR/CallingConv.h"
 
 using namespace mlir;
@@ -67,8 +68,8 @@ namespace {
 // Library's SysV x86_64 classifier, and converts the result back into the
 // dialect-agnostic mlir::abi::FunctionClassification that CIRABIRewriteContext
 // consumes.  Only integer / pointer / bool / f32 / f64 signatures are handled;
-// aggregates and other leaf types are reported NYI by classifyX86_64 so an
-// unsupported signature fails the pass instead of being misclassified.
+// aggregates and other leaf types are reported NYI by classifyX86_64Function
+// so an unsupported signature fails the pass instead of being misclassified.
 
//===----------------------------------------------------------------------===//
 
 /// llvm::Align requires a power of two; DataLayout can report non-power-of-two
@@ -94,54 +95,91 @@ static bool isSupportedScalarType(mlir::Type ty) {
 static mlir::Type abiTypeToCIR(const llvm::abi::Type *ty, MLIRContext *ctx) {
   if (!ty)
     return nullptr;
-  if (ty->isVoid())
-    return cir::VoidType::get(ctx);
-  if (auto *intTy = llvm::dyn_cast<llvm::abi::IntegerType>(ty))
-    return cir::IntType::get(ctx, intTy->getSizeInBits().getFixedValue(),
-                             intTy->isSigned());
-  if (auto *fltTy = llvm::dyn_cast<llvm::abi::FloatType>(ty)) {
-    const llvm::fltSemantics *sem = fltTy->getSemantics();
-    if (sem == &llvm::APFloat::IEEEsingle())
-      return cir::SingleType::get(ctx);
-    if (sem == &llvm::APFloat::IEEEdouble())
-      return cir::DoubleType::get(ctx);
-  }
-  if (llvm::isa<llvm::abi::PointerType>(ty))
-    return cir::PointerType::get(cir::VoidType::get(ctx));
-  return nullptr;
+  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) {
+        return cir::IntType::get(ctx, intTy->getSizeInBits().getFixedValue(),
+                                 intTy->isSigned());
+      })
+      .Case([&](const llvm::abi::FloatType *fltTy) -> mlir::Type {
+        const llvm::fltSemantics *sem = fltTy->getSemantics();
+        if (sem == &llvm::APFloat::IEEEsingle())
+          return cir::SingleType::get(ctx);
+        if (sem == &llvm::APFloat::IEEEdouble())
+          return cir::DoubleType::get(ctx);
+        return nullptr;
+      })
+      .Case([&](const llvm::abi::PointerType *) {
+        return cir::PointerType::get(cir::VoidType::get(ctx));
+      })
+      .Default([](const llvm::abi::Type *) -> mlir::Type { return nullptr; });
 }
 
-/// Map a scalar CIR type to an llvm::abi::Type.  classifyX86_64 pre-filters 
the
-/// signature, so only the scalar types handled here can reach this function.
+/// Map a scalar CIR type to an llvm::abi::Type.  classifyX86_64Function
+/// pre-filters the signature, so only the scalar types handled here can
+/// reach this function.
 static const llvm::abi::Type *mapCIRType(mlir::Type type,
                                          mlir::abi::ABITypeMapper &typeMapper,
                                          const DataLayout &dl) {
   llvm::abi::TypeBuilder &tb = typeMapper.getTypeBuilder();
-  if (auto intTy = dyn_cast<cir::IntType>(type))
-    return tb.getIntegerType(intTy.getWidth(),
-                             safeAlign(dl.getTypeABIAlignment(type)),
-                             intTy.isSigned());
-  if (isa<cir::PointerType>(type))
-    return tb.getPointerType(dl.getTypeSizeInBits(type),
-                             safeAlign(dl.getTypeABIAlignment(type)),
-                             /*AddressSpace=*/0);
-  if (isa<cir::BoolType>(type))
-    return tb.getIntegerType(dl.getTypeSizeInBits(type),
-                             safeAlign(dl.getTypeABIAlignment(type)),
-                             /*Signed=*/false);
-  if (isa<cir::VoidType>(type))
-    return tb.getVoidType();
-  if (isa<cir::SingleType>(type))
-    return tb.getFloatType(llvm::APFloat::IEEEsingle(),
-                           safeAlign(dl.getTypeABIAlignment(type)));
-  if (isa<cir::DoubleType>(type))
-    return tb.getFloatType(llvm::APFloat::IEEEdouble(),
-                           safeAlign(dl.getTypeABIAlignment(type)));
-  llvm_unreachable("mapCIRType: type not pre-filtered by classifyX86_64");
+  return llvm::TypeSwitch<mlir::Type, const llvm::abi::Type *>(type)
+      .Case([&](cir::IntType intTy) {
+        return tb.getIntegerType(intTy.getWidth(),
+                                 safeAlign(dl.getTypeABIAlignment(type)),
+                                 intTy.isSigned());
+      })
+      .Case([&](cir::PointerType ptrTy) {
+        unsigned addrSpace = 0;
+        if (auto targetAsAttr =
+                dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
+                    ptrTy.getAddrSpace()))
+          addrSpace = targetAsAttr.getValue();
+        return tb.getPointerType(dl.getTypeSizeInBits(type),
+                                 safeAlign(dl.getTypeABIAlignment(type)),
+                                 addrSpace);
+      })
+      .Case([&](cir::BoolType) {
+        return tb.getIntegerType(dl.getTypeSizeInBits(type),
+                                 safeAlign(dl.getTypeABIAlignment(type)),
+                                 /*Signed=*/false);
+      })
+      .Case([&](cir::VoidType) { return tb.getVoidType(); })
+      .Case([&](cir::SingleType) {
+        return tb.getFloatType(llvm::APFloat::IEEEsingle(),
+                               safeAlign(dl.getTypeABIAlignment(type)));
+      })
+      .Case([&](cir::DoubleType) {
+        return tb.getFloatType(llvm::APFloat::IEEEdouble(),
+                               safeAlign(dl.getTypeABIAlignment(type)));
+      })
+      .Default([](mlir::Type) -> const llvm::abi::Type * {
+        llvm_unreachable(
+            "mapCIRType: type not pre-filtered by classifyX86_64Function");
+      });
 }
 
 /// Convert an llvm::abi::ArgInfo for a scalar type into the ArgClassification
 /// consumed by CIRABIRewriteContext.
+///
+/// Direct: every scalar this bridge maps passes as-is, so no coercion type
+/// is needed (nullptr means "same as the original CIR type").
+///
+/// Extend: bool or a sub-register integer needs a signext/zeroext attribute.
+/// Every ArgInfo::getExtend() call site in the x86_64 classifier
+/// (llvm/lib/ABI/Targets/X86.cpp) is gated on the operand being an integer,
+/// so a non-integer, non-bool origTy here would mean the classifier
+/// disagreed with its own source -- asserted rather than silently handled.
+///
+/// Indirect: needed once records/_BitInt/vectors are supported (sret,
+/// byval, and large _BitInt all classify Indirect), but
+/// X86_64TargetInfo::getIndirectResult()/getIndirectReturnResult() only
+/// return Indirect for aggregates or _BitInt, neither of which the
+/// scalar-only type set this bridge accepts can produce.  Unreachable until
+/// a later PR adds those types and the record/vector/complex/int type gate
+/// that belongs here.
+///
+/// Ignore: a void return has no register or stack slot.
 static ArgClassification convertABIArgInfo(const llvm::abi::ArgInfo &info,
                                            MLIRContext *ctx,
                                            mlir::Type origTy) {
@@ -150,14 +188,14 @@ static ArgClassification convertABIArgInfo(const 
llvm::abi::ArgInfo &info,
   if (info.isExtend()) {
     if (origTy && isa<cir::BoolType>(origTy))
       return ArgClassification::getExtend(nullptr, info.isSignExt());
-    if (origTy && !isa<cir::IntType>(origTy))
-      return ArgClassification::getDirect(nullptr);
-    mlir::Type coerced = abiTypeToCIR(info.getCoerceToType(), ctx);
-    return ArgClassification::getExtend(coerced, info.isSignExt());
+    assert((!origTy || isa<cir::IntType>(origTy)) &&
+           "the x86_64 classifier only returns Extend for integers and bool");
+    mlir::Type extendedTy = abiTypeToCIR(info.getCoerceToType(), ctx);
+    return ArgClassification::getExtend(extendedTy, info.isSignExt());
   }
   if (info.isIndirect())
-    return ArgClassification::getIndirect(info.getIndirectAlign(),
-                                          info.getIndirectByVal());
+    llvm_unreachable("Indirect classification is impossible for the "
+                     "scalar-only type set this bridge accepts");
   return ArgClassification::getIgnore();
 }
 
@@ -165,13 +203,14 @@ static ArgClassification convertABIArgInfo(const 
llvm::abi::ArgInfo &info,
 /// std::nullopt and emits an NYI error if the signature uses a type the scalar
 /// bridge does not handle yet.
 static std::optional<FunctionClassification>
-classifyX86_64(cir::FuncOp func, const DataLayout &dl,
-               mlir::abi::ABITypeMapper &typeMapper,
-               const llvm::abi::TargetInfo &targetInfo) {
+classifyX86_64Function(cir::FuncOp func, const DataLayout &dl,
+                       mlir::abi::ABITypeMapper &typeMapper,
+                       const llvm::abi::TargetInfo &targetInfo) {
   MLIRContext *ctx = func->getContext();
   cir::FuncType fnTy = func.getFunctionType();
   mlir::Type retCIR = fnTy.getReturnType();
-  bool voidRet = !retCIR || isa<cir::VoidType>(retCIR);
+  assert(retCIR && "FuncType::getReturnType() never returns null");
+  bool voidRet = isa<cir::VoidType>(retCIR);
 
   auto reject = [&](mlir::Type t) -> bool {
     if (isSupportedScalarType(t))
@@ -210,6 +249,36 @@ classifyX86_64(cir::FuncOp func, const DataLayout &dl,
   return fc;
 }
 
+/// The classifier targets this pass drives internally via a shared
+/// ABITypeMapper/TargetInfo.  (The classification-attr injection mode is
+/// orthogonal -- it names an arbitrary attribute, not a fixed target -- and
+/// stays string-keyed.)  The name/enum pairs below are the single source of
+/// truth for both parsing the `target` pass option and the unknown-target
+/// diagnostic in classifyFunction.
+enum class CallConvTarget { Test, X86_64 };
+
+const std::pair<llvm::StringRef, CallConvTarget> kCallConvTargets[] = {
+    {"test", CallConvTarget::Test},
+    {"x86_64", CallConvTarget::X86_64},
+};
+
+std::optional<CallConvTarget> parseCallConvTarget(StringRef target) {
+  for (const auto &[name, value] : kCallConvTargets)
+    if (target == name)
+      return value;
+  return std::nullopt;
+}
+
+std::string supportedCallConvTargets() {
+  std::string result;
+  for (const auto &entry : kCallConvTargets) {
+    if (!result.empty())
+      result += ", ";
+    result += entry.first.str();
+  }
+  return result;
+}
+
 bool needsRewrite(const FunctionClassification &fc) {
   if ((fc.returnInfo.kind != ArgKind::Direct) || fc.returnInfo.coercedType)
     return true;
@@ -247,13 +316,13 @@ classifyFunction(cir::FuncOp func, const DataLayout &dl, 
StringRef target,
         attr, [&]() { return func.emitOpError(); });
   }
 
-  if (target == "test")
+  if (parseCallConvTarget(target) == CallConvTarget::Test)
     return mlir::abi::test::classify(argTypes, returnType, dl);
 
   // Note: the "x86_64" target is handled directly in runOnOperation (it needs
   // a shared ABITypeMapper and TargetInfo), so it never reaches here.
   func.emitOpError() << "unknown target '" << target
-                     << "' (supported: test, x86_64)";
+                     << "' (supported: " << supportedCallConvTargets() << ")";
   return std::nullopt;
 }
 
@@ -302,7 +371,7 @@ void CallConvLoweringPass::runOnOperation() {
   // reuse it (and its type mapper) across every function.
   std::optional<mlir::abi::ABITypeMapper> x86TypeMapper;
   std::unique_ptr<llvm::abi::TargetInfo> x86Target;
-  if (target == "x86_64") {
+  if (parseCallConvTarget(target) == CallConvTarget::X86_64) {
     x86TypeMapper.emplace(dl);
     auto avx =
         static_cast<llvm::abi::X86AVXABILevel>(x86AvxAbiLevel.getValue());
@@ -319,7 +388,7 @@ void CallConvLoweringPass::runOnOperation() {
   moduleOp.walk([&](cir::FuncOp f) {
     std::optional<FunctionClassification> fc;
     if (x86Target)
-      fc = classifyX86_64(f, dl, *x86TypeMapper, *x86Target);
+      fc = classifyX86_64Function(f, dl, *x86TypeMapper, *x86Target);
     else
       fc = classifyFunction(f, dl, target, classificationAttr);
     if (!fc) {
diff --git a/clang/test/CIR/Transforms/abi-lowering/x86_64-scalars.cir 
b/clang/test/CIR/Transforms/abi-lowering/x86_64-scalars.cir
index 006f6532f281d..6498702b0f26e 100644
--- a/clang/test/CIR/Transforms/abi-lowering/x86_64-scalars.cir
+++ b/clang/test/CIR/Transforms/abi-lowering/x86_64-scalars.cir
@@ -67,8 +67,25 @@ module attributes {
   }
 
   // CHECK: cir.call @take_s8(%arg0) : (!s8i {llvm.signext}) -> ()
+
+  // A sub-register integer return is also extended, not just arguments.
+  cir.func @ret_s8() -> !s8i {
+    %0 = cir.const #cir.int<0> : !s8i
+    cir.return %0 : !s8i
+  }
+
+  // CHECK: cir.func{{.*}} @ret_s8() -> (!s8i {llvm.signext})
+
+  cir.func @ret_u16() -> !u16i {
+    %0 = cir.const #cir.int<0> : !u16i
+    cir.return %0 : !u16i
+  }
+
+  // CHECK: cir.func{{.*}} @ret_u16() -> (!u16i {llvm.zeroext})
 }
 
 // LLVM: define void @take_s8(i8 signext %{{.+}})
 // LLVM: define void @take_u16(i16 zeroext %{{.+}})
 // LLVM: define void @take_bool(i1 zeroext %{{.+}})
+// LLVM: define signext i8 @ret_s8()
+// LLVM: define zeroext i16 @ret_u16()

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to