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

>From b41c998307ddfeff7aef68b1c8b7735b8e1b5919 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Tue, 14 Jul 2026 14:25:48 -0700
Subject: [PATCH 1/5] [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 7d317879ad739..28ec8d936bb05 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 47be65cc78ac5ad347c63e7de90c511223eda142 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Wed, 15 Jul 2026 15:00:43 -0700
Subject: [PATCH 2/5] [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()

>From 781afe367ebe40b3a95c7819c28992ef0d09b78d Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Thu, 16 Jul 2026 13:44:47 -0700
Subject: [PATCH 3/5] [CIR][NFC] Tidy up x86_64 scalar calling-convention
 lowering

This moves the floating-point semantics-to-type mapping out of the
calling-convention pass and into a shared cir::getFloatingPointType helper
in CIRTypes, alongside cir::isSized. It still only handles f32 and f64; any
other semantics now trips an llvm_unreachable instead of returning null,
though the scalar classifier never lets one through.

It also gives the x86_64 AVX pass option its real type,
llvm::abi::X86AVXABILevel, instead of a plain unsigned that we had to cast
back to the enum at the call site.

The target name lookup is now a constexpr array indexed by the
CallConvTarget enum, with a Last sentinel and a static_assert so the names
and the enum can't drift apart.
---
 clang/include/clang/CIR/Dialect/IR/CIRTypes.h | 10 +++++
 clang/include/clang/CIR/Dialect/Passes.h      |  6 ++-
 clang/include/clang/CIR/Dialect/Passes.td     | 10 ++++-
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         | 12 +++++
 .../Transforms/CallConvLoweringPass.cpp       | 45 ++++++++-----------
 clang/lib/CIR/Dialect/Transforms/PassDetail.h |  1 +
 6 files changed, 54 insertions(+), 30 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
index c5f4127040ca0..ccae66e774bbf 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
@@ -24,6 +24,10 @@
 #include "clang/CIR/Dialect/IR/CIROpsEnums.h"
 #include "clang/CIR/Interfaces/CIRTypeInterfaces.h"
 
+namespace llvm {
+struct fltSemantics;
+} // namespace llvm
+
 namespace cir {
 
 namespace detail {
@@ -42,6 +46,12 @@ bool isValidFundamentalIntWidth(unsigned width);
 /// void, or abstract types.
 bool isSized(mlir::Type ty);
 
+/// Returns the CIR floating-point type for a given semantics.  Mirrors
+/// llvm::Type::getFloatingPointTy; currently only covers the semantics
+/// CIR's scalar ABI classifier can reach (f32/f64).
+mlir::Type getFloatingPointType(const llvm::fltSemantics &sem,
+                                mlir::MLIRContext *ctx);
+
 
//===----------------------------------------------------------------------===//
 // AddressSpace helpers
 
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/Passes.h 
b/clang/include/clang/CIR/Dialect/Passes.h
index 28ec8d936bb05..9ac2b4a018b10 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -14,6 +14,7 @@
 #define CLANG_CIR_DIALECT_PASSES_H
 
 #include "mlir/Pass/Pass.h"
+#include "llvm/ABI/TargetInfo.h"
 
 namespace clang {
 class ASTContext;
@@ -28,8 +29,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(llvm::StringRef target,
-                                                 unsigned x86AvxAbiLevel);
+std::unique_ptr<Pass>
+createCallConvLoweringPass(llvm::StringRef target,
+                           llvm::abi::X86AVXABILevel 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 1c3c8ead1842f..6ba5367163636 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -245,8 +245,14 @@ def CallConvLowering : Pass<"cir-call-conv-lowering", 
"mlir::ModuleOp"> {
            /*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)">,
+    Option<"x86AvxAbiLevel", "x86-avx-abi-level", "llvm::abi::X86AVXABILevel",
+           /*default=*/"llvm::abi::X86AVXABILevel::None",
+           "AVX ABI level for the x86_64 target",
+           [{::llvm::cl::values(
+             clEnumValN(llvm::abi::X86AVXABILevel::None, "none", "No AVX"),
+             clEnumValN(llvm::abi::X86AVXABILevel::AVX, "avx", "AVX"),
+             clEnumValN(llvm::abi::X86AVXABILevel::AVX512, "avx512", "AVX512")
+           )}]>,
   ];
 }
 
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index afd3364af4ec2..1ab68de025ca4 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -23,6 +23,7 @@
 #include "clang/CIR/Dialect/IR/CIROpsEnums.h"
 #include "clang/CIR/Dialect/IR/CIRTypesDetails.h"
 #include "clang/CIR/MissingFeatures.h"
+#include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/ADT/TypeSwitch.h"
@@ -38,6 +39,17 @@ bool cir::isSized(mlir::Type ty) {
   return false;
 }
 
+mlir::Type cir::getFloatingPointType(const llvm::fltSemantics &sem,
+                                     mlir::MLIRContext *ctx) {
+  if (&sem == &llvm::APFloat::IEEEsingle())
+    return cir::SingleType::get(ctx);
+  if (&sem == &llvm::APFloat::IEEEdouble())
+    return cir::DoubleType::get(ctx);
+  llvm_unreachable("getFloatingPointType: floating-point semantics not yet "
+                   "handled; add a case here as new float kinds become "
+                   "supported");
+}
+
 
//===----------------------------------------------------------------------===//
 // CIR Custom Parser/Printer Signatures
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp 
b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index 987a6e42e43ad..0396a3444c25e 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -102,13 +102,8 @@ static mlir::Type abiTypeToCIR(const llvm::abi::Type *ty, 
MLIRContext *ctx) {
         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::FloatType *fltTy) {
+        return cir::getFloatingPointType(*fltTy->getSemantics(), ctx);
       })
       .Case([&](const llvm::abi::PointerType *) {
         return cir::PointerType::get(cir::VoidType::get(ctx));
@@ -252,29 +247,29 @@ classifyX86_64Function(cir::FuncOp func, const DataLayout 
&dl,
 /// 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},
-};
+/// stays string-keyed.)  kCallConvTargetNames is the single source of truth
+/// for both parsing the `target` pass option and the unknown-target
+/// diagnostic in classifyFunction; it is indexed by the enum value below.
+enum class CallConvTarget { Test, X86_64, Last = X86_64 };
+
+constexpr llvm::StringRef kCallConvTargetNames[] = {"test", "x86_64"};
+static_assert(std::size(kCallConvTargetNames) ==
+                  static_cast<size_t>(CallConvTarget::Last) + 1,
+              "kCallConvTargetNames must have one entry per CallConvTarget");
 
 std::optional<CallConvTarget> parseCallConvTarget(StringRef target) {
-  for (const auto &[name, value] : kCallConvTargets)
-    if (target == name)
-      return value;
+  for (size_t i = 0; i < std::size(kCallConvTargetNames); ++i)
+    if (target == kCallConvTargetNames[i])
+      return static_cast<CallConvTarget>(i);
   return std::nullopt;
 }
 
 std::string supportedCallConvTargets() {
   std::string result;
-  for (const auto &entry : kCallConvTargets) {
+  for (llvm::StringRef name : kCallConvTargetNames) {
     if (!result.empty())
       result += ", ";
-    result += entry.first.str();
+    result += name.str();
   }
   return result;
 }
@@ -373,11 +368,9 @@ void CallConvLoweringPass::runOnOperation() {
   std::unique_ptr<llvm::abi::TargetInfo> x86Target;
   if (parseCallConvTarget(target) == CallConvTarget::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());
+        x86TypeMapper->getTypeBuilder(), x86AvxAbiLevel.getValue(),
+        /*Has64BitPointers=*/true, llvm::abi::ABICompatInfo());
   }
 
   // Classify every cir.func up front.  No IR mutation happens here, so
@@ -473,7 +466,7 @@ std::unique_ptr<Pass> mlir::createCallConvLoweringPass() {
 
 std::unique_ptr<Pass>
 mlir::createCallConvLoweringPass(llvm::StringRef target,
-                                 unsigned x86AvxAbiLevel) {
+                                 llvm::abi::X86AVXABILevel x86AvxAbiLevel) {
   CallConvLoweringOptions options;
   options.target = target.str();
   options.x86AvxAbiLevel = x86AvxAbiLevel;
diff --git a/clang/lib/CIR/Dialect/Transforms/PassDetail.h 
b/clang/lib/CIR/Dialect/Transforms/PassDetail.h
index ef42a85cc2751..cc585fdec7865 100644
--- a/clang/lib/CIR/Dialect/Transforms/PassDetail.h
+++ b/clang/lib/CIR/Dialect/Transforms/PassDetail.h
@@ -11,6 +11,7 @@
 
 #include "mlir/IR/Dialect.h"
 #include "mlir/Pass/Pass.h"
+#include "llvm/ABI/TargetInfo.h"
 
 namespace cir {
 class CIRDialect;

>From 4c619e3a0fab2123515c4b3c97bf53afc5c2bd5d Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Fri, 17 Jul 2026 08:47:32 -0700
Subject: [PATCH 4/5] [CIR] Refine x86_64 scalar calling-convention lowering

This narrows cir::getFloatingPointType to return cir::FPTypeInterface
rather than a generic mlir::Type, since it only ever returns a CIR
floating-point type.

A pointer whose address space isn't the default or an already-lowered
target address space is now rejected with an NYI diagnostic instead of
silently coercing a LangAddressSpaceAttr to address space 0.  Nothing
lowers those to a target address space before this pass yet, so a visible
failure is better than a wrong result.

The `target` pass option is a CallConvTarget enum with clEnumValN now, so
the option machinery parses the name straight into the enum.  This drops
the parallel name table and the hand-rolled parsing that had to be kept in
sync with the enum.

An unexpected ArgInfo classification is an assert(isIgnore()) rather than a
llvm_unreachable on Indirect, so a future classification kind can't slip
through unnoticed.

This drops safeAlign now that IntType::getABIAlignment returns a
power-of-two alignment (#210187); the DataLayout alignment goes straight to
llvm::Align.
---
 clang/include/clang/CIR/Dialect/IR/CIRTypes.h |  4 +-
 clang/include/clang/CIR/Dialect/Passes.h      |  9 +-
 clang/include/clang/CIR/Dialect/Passes.td     | 11 ++-
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         |  4 +-
 .../Transforms/CallConvLoweringPass.cpp       | 86 ++++++-------------
 clang/lib/CIR/Dialect/Transforms/PassDetail.h |  1 +
 .../x86_64-lang-addrspace-nyi.cir             | 18 ++++
 7 files changed, 66 insertions(+), 67 deletions(-)
 create mode 100644 
clang/test/CIR/Transforms/abi-lowering/x86_64-lang-addrspace-nyi.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
index ccae66e774bbf..bb0e9b080c578 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
@@ -49,8 +49,8 @@ bool isSized(mlir::Type ty);
 /// Returns the CIR floating-point type for a given semantics.  Mirrors
 /// llvm::Type::getFloatingPointTy; currently only covers the semantics
 /// CIR's scalar ABI classifier can reach (f32/f64).
-mlir::Type getFloatingPointType(const llvm::fltSemantics &sem,
-                                mlir::MLIRContext *ctx);
+cir::FPTypeInterface getFloatingPointType(const llvm::fltSemantics &sem,
+                                          mlir::MLIRContext *ctx);
 
 
//===----------------------------------------------------------------------===//
 // AddressSpace helpers
diff --git a/clang/include/clang/CIR/Dialect/Passes.h 
b/clang/include/clang/CIR/Dialect/Passes.h
index 9ac2b4a018b10..0b8142fc394bd 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -16,6 +16,13 @@
 #include "mlir/Pass/Pass.h"
 #include "llvm/ABI/TargetInfo.h"
 
+namespace cir {
+/// The ABI target whose calling-convention rules drive CallConvLowering.
+/// None is the unset state used when the pass runs in classification-attr
+/// mode instead of selecting a target.
+enum class CallConvTarget { None, Test, X86_64 };
+} // namespace cir
+
 namespace clang {
 class ASTContext;
 }
@@ -30,7 +37,7 @@ std::unique_ptr<Pass> createCXXABILoweringPass();
 std::unique_ptr<Pass> createTargetLoweringPass();
 std::unique_ptr<Pass> createCallConvLoweringPass();
 std::unique_ptr<Pass>
-createCallConvLoweringPass(llvm::StringRef target,
+createCallConvLoweringPass(cir::CallConvTarget target,
                            llvm::abi::X86AVXABILevel x86AvxAbiLevel);
 std::unique_ptr<Pass> createHoistAllocasPass();
 std::unique_ptr<Pass> createLoweringPreparePass();
diff --git a/clang/include/clang/CIR/Dialect/Passes.td 
b/clang/include/clang/CIR/Dialect/Passes.td
index 6ba5367163636..04ff1c92cb7f7 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -239,8 +239,15 @@ def CallConvLowering : Pass<"cir-call-conv-lowering", 
"mlir::ModuleOp"> {
   let constructor = "mlir::createCallConvLoweringPass()";
   let dependentDialects = ["cir::CIRDialect"];
   let options = [
-    Option<"target", "target", "std::string", /*default=*/"\"\"",
-           "Target whose ABI rules drive classification (test, x86_64)">,
+    Option<"target", "target", "cir::CallConvTarget",
+           /*default=*/"cir::CallConvTarget::None",
+           "Target whose ABI rules drive classification",
+           [{::llvm::cl::values(
+             clEnumValN(cir::CallConvTarget::Test, "test",
+                        "MLIR test ABI target"),
+             clEnumValN(cir::CallConvTarget::X86_64, "x86_64",
+                        "x86_64 System V")
+           )}]>,
     Option<"classificationAttr", "classification-attr", "std::string",
            /*default=*/"\"\"",
            "Function attribute name carrying a pre-built 
FunctionClassification "
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 1ab68de025ca4..6fffc64e8f7cc 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -39,8 +39,8 @@ bool cir::isSized(mlir::Type ty) {
   return false;
 }
 
-mlir::Type cir::getFloatingPointType(const llvm::fltSemantics &sem,
-                                     mlir::MLIRContext *ctx) {
+cir::FPTypeInterface cir::getFloatingPointType(const llvm::fltSemantics &sem,
+                                               mlir::MLIRContext *ctx) {
   if (&sem == &llvm::APFloat::IEEEsingle())
     return cir::SingleType::get(ctx);
   if (&sem == &llvm::APFloat::IEEEdouble())
diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp 
b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index 0396a3444c25e..bb5b194ee0433 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -72,19 +72,18 @@ namespace {
 // 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))
+  // A pointer is only handled in the default address space (null) or an
+  // already-lowered target address space.  A LangAddressSpaceAttr must be
+  // lowered before this pass, so reject it rather than silently dropping it.
+  if (auto ptrTy = dyn_cast<cir::PointerType>(ty))
+    return !ptrTy.getAddrSpace() ||
+           mlir::isa<cir::TargetAddressSpaceAttr>(ptrTy.getAddrSpace());
+  if (isa<cir::VoidType, cir::BoolType, cir::SingleType, cir::DoubleType>(ty))
     return true;
   if (auto intTy = dyn_cast<cir::IntType>(ty))
     return !intTy.getIsBitInt() && intTy.getWidth() <= 64;
@@ -121,7 +120,7 @@ static const llvm::abi::Type *mapCIRType(mlir::Type type,
   return llvm::TypeSwitch<mlir::Type, const llvm::abi::Type *>(type)
       .Case([&](cir::IntType intTy) {
         return tb.getIntegerType(intTy.getWidth(),
-                                 safeAlign(dl.getTypeABIAlignment(type)),
+                                 llvm::Align(dl.getTypeABIAlignment(type)),
                                  intTy.isSigned());
       })
       .Case([&](cir::PointerType ptrTy) {
@@ -131,22 +130,22 @@ static const llvm::abi::Type *mapCIRType(mlir::Type type,
                     ptrTy.getAddrSpace()))
           addrSpace = targetAsAttr.getValue();
         return tb.getPointerType(dl.getTypeSizeInBits(type),
-                                 safeAlign(dl.getTypeABIAlignment(type)),
+                                 llvm::Align(dl.getTypeABIAlignment(type)),
                                  addrSpace);
       })
       .Case([&](cir::BoolType) {
         return tb.getIntegerType(dl.getTypeSizeInBits(type),
-                                 safeAlign(dl.getTypeABIAlignment(type)),
+                                 llvm::Align(dl.getTypeABIAlignment(type)),
                                  /*Signed=*/false);
       })
       .Case([&](cir::VoidType) { return tb.getVoidType(); })
       .Case([&](cir::SingleType) {
         return tb.getFloatType(llvm::APFloat::IEEEsingle(),
-                               safeAlign(dl.getTypeABIAlignment(type)));
+                               llvm::Align(dl.getTypeABIAlignment(type)));
       })
       .Case([&](cir::DoubleType) {
         return tb.getFloatType(llvm::APFloat::IEEEdouble(),
-                               safeAlign(dl.getTypeABIAlignment(type)));
+                               llvm::Align(dl.getTypeABIAlignment(type)));
       })
       .Default([](mlir::Type) -> const llvm::abi::Type * {
         llvm_unreachable(
@@ -188,9 +187,7 @@ static ArgClassification convertABIArgInfo(const 
llvm::abi::ArgInfo &info,
     mlir::Type extendedTy = abiTypeToCIR(info.getCoerceToType(), ctx);
     return ArgClassification::getExtend(extendedTy, info.isSignExt());
   }
-  if (info.isIndirect())
-    llvm_unreachable("Indirect classification is impossible for the "
-                     "scalar-only type set this bridge accepts");
+  assert(info.isIgnore() && "Unexpected classification");
   return ArgClassification::getIgnore();
 }
 
@@ -244,36 +241,6 @@ classifyX86_64Function(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.)  kCallConvTargetNames is the single source of truth
-/// for both parsing the `target` pass option and the unknown-target
-/// diagnostic in classifyFunction; it is indexed by the enum value below.
-enum class CallConvTarget { Test, X86_64, Last = X86_64 };
-
-constexpr llvm::StringRef kCallConvTargetNames[] = {"test", "x86_64"};
-static_assert(std::size(kCallConvTargetNames) ==
-                  static_cast<size_t>(CallConvTarget::Last) + 1,
-              "kCallConvTargetNames must have one entry per CallConvTarget");
-
-std::optional<CallConvTarget> parseCallConvTarget(StringRef target) {
-  for (size_t i = 0; i < std::size(kCallConvTargetNames); ++i)
-    if (target == kCallConvTargetNames[i])
-      return static_cast<CallConvTarget>(i);
-  return std::nullopt;
-}
-
-std::string supportedCallConvTargets() {
-  std::string result;
-  for (llvm::StringRef name : kCallConvTargetNames) {
-    if (!result.empty())
-      result += ", ";
-    result += name.str();
-  }
-  return result;
-}
-
 bool needsRewrite(const FunctionClassification &fc) {
   if ((fc.returnInfo.kind != ArgKind::Direct) || fc.returnInfo.coercedType)
     return true;
@@ -294,8 +261,8 @@ struct CallConvLoweringPass
 /// (e.g. injection-driver mode but the function is missing the attribute,
 /// or the attribute is malformed).
 std::optional<FunctionClassification>
-classifyFunction(cir::FuncOp func, const DataLayout &dl, StringRef target,
-                 StringRef classificationAttrName) {
+classifyFunction(cir::FuncOp func, const DataLayout &dl,
+                 cir::CallConvTarget target, StringRef classificationAttrName) 
{
   ArrayRef<Type> argTypes = func.getFunctionType().getInputs();
   Type returnType = func.getFunctionType().getReturnType();
 
@@ -311,14 +278,11 @@ classifyFunction(cir::FuncOp func, const DataLayout &dl, 
StringRef target,
         attr, [&]() { return func.emitOpError(); });
   }
 
-  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: " << supportedCallConvTargets() << ")";
-  return std::nullopt;
+  // The x86_64 target is handled directly in runOnOperation (it needs a shared
+  // ABITypeMapper and TargetInfo), so only the test target reaches here.
+  assert(target == cir::CallConvTarget::Test &&
+         "classifyFunction only handles the test target");
+  return mlir::abi::test::classify(argTypes, returnType, dl);
 }
 
 /// Find the cir.func declaration matching a direct cir.call / cir.try_call
@@ -343,7 +307,9 @@ void CallConvLoweringPass::runOnOperation() {
   ModuleOp moduleOp = getOperation();
   MLIRContext *ctx = &getContext();
 
-  if (target.empty() == classificationAttr.empty()) {
+  bool haveTarget = target != cir::CallConvTarget::None;
+  bool haveAttr = !classificationAttr.empty();
+  if (haveTarget == haveAttr) {
     moduleOp.emitOpError() << "CallConvLowering requires exactly one of "
                               "'target' or 'classification-attr' pass options";
     signalPassFailure();
@@ -366,7 +332,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 (parseCallConvTarget(target) == CallConvTarget::X86_64) {
+  if (target == cir::CallConvTarget::X86_64) {
     x86TypeMapper.emplace(dl);
     x86Target = llvm::abi::createX86_64TargetInfo(
         x86TypeMapper->getTypeBuilder(), x86AvxAbiLevel.getValue(),
@@ -465,10 +431,10 @@ std::unique_ptr<Pass> mlir::createCallConvLoweringPass() {
 }
 
 std::unique_ptr<Pass>
-mlir::createCallConvLoweringPass(llvm::StringRef target,
+mlir::createCallConvLoweringPass(cir::CallConvTarget target,
                                  llvm::abi::X86AVXABILevel x86AvxAbiLevel) {
   CallConvLoweringOptions options;
-  options.target = target.str();
+  options.target = target;
   options.x86AvxAbiLevel = x86AvxAbiLevel;
   return std::make_unique<CallConvLoweringPass>(options);
 }
diff --git a/clang/lib/CIR/Dialect/Transforms/PassDetail.h 
b/clang/lib/CIR/Dialect/Transforms/PassDetail.h
index cc585fdec7865..b7703cf4a08b9 100644
--- a/clang/lib/CIR/Dialect/Transforms/PassDetail.h
+++ b/clang/lib/CIR/Dialect/Transforms/PassDetail.h
@@ -11,6 +11,7 @@
 
 #include "mlir/IR/Dialect.h"
 #include "mlir/Pass/Pass.h"
+#include "clang/CIR/Dialect/Passes.h"
 #include "llvm/ABI/TargetInfo.h"
 
 namespace cir {
diff --git 
a/clang/test/CIR/Transforms/abi-lowering/x86_64-lang-addrspace-nyi.cir 
b/clang/test/CIR/Transforms/abi-lowering/x86_64-lang-addrspace-nyi.cir
new file mode 100644
index 0000000000000..a8350b421757d
--- /dev/null
+++ b/clang/test/CIR/Transforms/abi-lowering/x86_64-lang-addrspace-nyi.cir
@@ -0,0 +1,18 @@
+// RUN: not cir-opt %s -cir-call-conv-lowering=target=x86_64 2>&1 | FileCheck 
%s
+
+!s32i = !cir.int<s, 32>
+
+module attributes {
+  dlti.dl_spec = #dlti.dl_spec<
+    #dlti.dl_entry<i32, dense<32>: vector<2xi64>>,
+    #dlti.dl_entry<i64, dense<64>: vector<2xi64>>>
+} {
+
+  cir.func @lang_addrspace_ptr(
+      %arg0: !cir.ptr<!s32i, lang_address_space(offload_global)>) {
+    cir.return
+  }
+
+}
+
+// CHECK: op x86_64 calling-convention lowering not yet implemented for type

>From a91ee5699a5a1a84117c2a450a209dcbdbc728e1 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Fri, 17 Jul 2026 13:16:43 -0700
Subject: [PATCH 5/5] [CIR] Cover all CIR float types in
 cir::getFloatingPointType

This turns cir::getFloatingPointType into a switch over
llvm::APFloat::SemanticsToEnum and maps every floating-point type CIR has
(FP16, BF16, single, double, FP80, FP128), rather than only the f32/f64
that the scalar calling-convention classifier happens to use.

For a semantics CIR has no type for (PPCDoubleDouble, the Float8 formats)
it returns a null type instead of llvm_unreachable.  This is a shared
helper in a general location, so a caller that reaches an unhandled kind
should get a null it can diagnose rather than a crash.

A unit test covers the six mappings and the null result.
---
 clang/include/clang/CIR/Dialect/IR/CIRTypes.h |  6 +--
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         | 21 ++++++--
 clang/unittests/CIR/CMakeLists.txt            |  1 +
 .../CIR/GetFloatingPointTypeTest.cpp          | 53 +++++++++++++++++++
 4 files changed, 73 insertions(+), 8 deletions(-)
 create mode 100644 clang/unittests/CIR/GetFloatingPointTypeTest.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
index bb0e9b080c578..f72d10d236612 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
@@ -46,9 +46,9 @@ bool isValidFundamentalIntWidth(unsigned width);
 /// void, or abstract types.
 bool isSized(mlir::Type ty);
 
-/// Returns the CIR floating-point type for a given semantics.  Mirrors
-/// llvm::Type::getFloatingPointTy; currently only covers the semantics
-/// CIR's scalar ABI classifier can reach (f32/f64).
+/// Returns the CIR floating-point type for the given semantics, or a null
+/// type if CIR has no type for it (e.g. PPCDoubleDouble or a Float8 format).
+/// Mirrors llvm::Type::getFloatingPointTy.
 cir::FPTypeInterface getFloatingPointType(const llvm::fltSemantics &sem,
                                           mlir::MLIRContext *ctx);
 
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 6fffc64e8f7cc..59930749ccff0 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -41,13 +41,24 @@ bool cir::isSized(mlir::Type ty) {
 
 cir::FPTypeInterface cir::getFloatingPointType(const llvm::fltSemantics &sem,
                                                mlir::MLIRContext *ctx) {
-  if (&sem == &llvm::APFloat::IEEEsingle())
+  switch (llvm::APFloat::SemanticsToEnum(sem)) {
+  case llvm::APFloat::S_IEEEhalf:
+    return cir::FP16Type::get(ctx);
+  case llvm::APFloat::S_BFloat:
+    return cir::BF16Type::get(ctx);
+  case llvm::APFloat::S_IEEEsingle:
     return cir::SingleType::get(ctx);
-  if (&sem == &llvm::APFloat::IEEEdouble())
+  case llvm::APFloat::S_IEEEdouble:
     return cir::DoubleType::get(ctx);
-  llvm_unreachable("getFloatingPointType: floating-point semantics not yet "
-                   "handled; add a case here as new float kinds become "
-                   "supported");
+  case llvm::APFloat::S_x87DoubleExtended:
+    return cir::FP80Type::get(ctx);
+  case llvm::APFloat::S_IEEEquad:
+    return cir::FP128Type::get(ctx);
+  default:
+    // CIR has no type for the remaining semantics (PPCDoubleDouble, the
+    // Float8 formats).  Return null and let the caller report it.
+    return {};
+  }
 }
 
 
//===----------------------------------------------------------------------===//
diff --git a/clang/unittests/CIR/CMakeLists.txt 
b/clang/unittests/CIR/CMakeLists.txt
index 446ca36fc39b0..93adf199486a9 100644
--- a/clang/unittests/CIR/CMakeLists.txt
+++ b/clang/unittests/CIR/CMakeLists.txt
@@ -6,6 +6,7 @@ include_directories(${MLIR_TABLEGEN_OUTPUT_DIR})
 add_distinct_clang_unittest(CIRUnitTests
   CallOpTest.cpp
   ControlFlowTest.cpp
+  GetFloatingPointTypeTest.cpp
   IntTypeABIAlignTest.cpp
   PointerLikeTest.cpp
   RecordTypeMetadataTest.cpp
diff --git a/clang/unittests/CIR/GetFloatingPointTypeTest.cpp 
b/clang/unittests/CIR/GetFloatingPointTypeTest.cpp
new file mode 100644
index 0000000000000..de9f34e1c0539
--- /dev/null
+++ b/clang/unittests/CIR/GetFloatingPointTypeTest.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Unit tests for cir::getFloatingPointType.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/IR/MLIRContext.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+#include "llvm/ADT/APFloat.h"
+#include "gtest/gtest.h"
+
+using namespace mlir;
+using namespace cir;
+
+namespace {
+
+class GetFloatingPointTypeTest : public ::testing::Test {
+protected:
+  GetFloatingPointTypeTest() { context.loadDialect<cir::CIRDialect>(); }
+
+  MLIRContext context;
+};
+
+// Every floating-point semantics CIR has a type for maps to that type.
+TEST_F(GetFloatingPointTypeTest, MapsSupportedSemantics) {
+  EXPECT_TRUE(mlir::isa<cir::FP16Type>(
+      getFloatingPointType(llvm::APFloat::IEEEhalf(), &context)));
+  EXPECT_TRUE(mlir::isa<cir::BF16Type>(
+      getFloatingPointType(llvm::APFloat::BFloat(), &context)));
+  EXPECT_TRUE(mlir::isa<cir::SingleType>(
+      getFloatingPointType(llvm::APFloat::IEEEsingle(), &context)));
+  EXPECT_TRUE(mlir::isa<cir::DoubleType>(
+      getFloatingPointType(llvm::APFloat::IEEEdouble(), &context)));
+  EXPECT_TRUE(mlir::isa<cir::FP80Type>(
+      getFloatingPointType(llvm::APFloat::x87DoubleExtended(), &context)));
+  EXPECT_TRUE(mlir::isa<cir::FP128Type>(
+      getFloatingPointType(llvm::APFloat::IEEEquad(), &context)));
+}
+
+// A semantics CIR has no type for returns a null type rather than asserting.
+TEST_F(GetFloatingPointTypeTest, ReturnsNullForUnsupportedSemantics) {
+  EXPECT_FALSE(
+      getFloatingPointType(llvm::APFloat::PPCDoubleDouble(), &context));
+}
+
+} // namespace

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

Reply via email to