https://github.com/xakep8 updated 
https://github.com/llvm/llvm-project/pull/225942

>From eafa7066fef1ab892a37cca6d59eb8daf02422e5 Mon Sep 17 00:00:00 2001
From: Kunal Dubey <[email protected]>
Date: Thu, 24 Sep 2026 02:34:21 +0530
Subject: [PATCH] [CIR] Add target ABI lowering for wide _BitInt

Teach CIR call-convention lowering about i386, AArch64, and PowerPC64 scalar 
_BitInt rules, preserve target storage alignment, and emit split constants 
using target endianness.\n\nDiagnose unsupported target-specific _BitInt va_arg 
lowering.
---
 clang/include/clang/CIR/CIRDataLayoutSpec.h   |   7 +-
 clang/include/clang/CIR/Dialect/Passes.h      |   5 +-
 clang/include/clang/CIR/Dialect/Passes.td     |  16 +-
 clang/lib/CIR/CodeGen/CIRDataLayoutSpec.cpp   |  15 +-
 clang/lib/CIR/CodeGen/CIRGenerator.cpp        |   3 +-
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         |  25 +-
 .../Transforms/CallConvLoweringPass.cpp       | 272 ++++++++++++++----
 .../TargetLowering/CIRABIRewriteContext.cpp   |  59 ++--
 clang/lib/CIR/Lowering/CIRPasses.cpp          |  42 ++-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |   8 +-
 clang/lib/CIR/Lowering/LoweringHelpers.cpp    |  12 +-
 .../test/CIR/CodeGen/bitint-wide-big-endian.c |  12 +
 .../call-conv-lowering-bitint-targets.c       | 102 +++++++
 ...l-conv-lowering-bitint-vaarg-unsupported.c |  16 ++
 clang/tools/cir-translate/cir-translate.cpp   |   3 +-
 15 files changed, 486 insertions(+), 111 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/bitint-wide-big-endian.c
 create mode 100644 clang/test/CIR/CodeGen/call-conv-lowering-bitint-targets.c
 create mode 100644 
clang/test/CIR/CodeGen/call-conv-lowering-bitint-vaarg-unsupported.c

diff --git a/clang/include/clang/CIR/CIRDataLayoutSpec.h 
b/clang/include/clang/CIR/CIRDataLayoutSpec.h
index c270f6283afe47..693130b6de6797 100644
--- a/clang/include/clang/CIR/CIRDataLayoutSpec.h
+++ b/clang/include/clang/CIR/CIRDataLayoutSpec.h
@@ -26,8 +26,11 @@ namespace cir {
 /// Translate \p dl into a DLTI data-layout spec and attach it to \p mod.
 /// On top of the plain mlir::translateDataLayout entries this adds a
 /// #cir.ptr_spec entry keyed on !cir.ptr, which CIR pointer types read for
-/// their size and alignment; without it pointer widths default to 64 bits.
-void setMLIRDataLayout(mlir::ModuleOp mod, const llvm::DataLayout &dl);
+/// their size and alignment, and the target's maximum _BitInt alignment.
+/// Without the pointer entry pointer widths default to 64 bits; without the
+/// _BitInt entry its maximum alignment defaults to 64 bits.
+void setMLIRDataLayout(mlir::ModuleOp mod, const llvm::DataLayout &dl,
+                       unsigned bitIntMaxAlign = 64);
 
 } // namespace cir
 
diff --git a/clang/include/clang/CIR/Dialect/Passes.h 
b/clang/include/clang/CIR/Dialect/Passes.h
index 42dd3383064568..0d11e4bec542a9 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -20,7 +20,7 @@ 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 };
+enum class CallConvTarget { None, Test, X86_64, X86_32, AArch64, PPC64 };
 } // namespace cir
 
 namespace clang {
@@ -40,7 +40,8 @@ std::unique_ptr<Pass>
 createCallConvLoweringPass(cir::CallConvTarget target,
                            llvm::abi::X86AVXABILevel x86AvxAbiLevel,
                            bool allowsX86TargetAttrAvx,
-                           const llvm::abi::X86ABICompatInfo &x86AbiCompat);
+                           const llvm::abi::X86ABICompatInfo &x86AbiCompat,
+                           const llvm::abi::AArch64ABIOptions &aarch64Options);
 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 6994aa8a4b18ad..020366a02df0b1 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -225,10 +225,10 @@ def CallConvLowering : Pass<"cir-call-conv-lowering", 
"mlir::ModuleOp"> {
 
     Two driver modes select how each function's classification is computed:
 
-    - `target=<name>` selects an ABI target.  Currently only `"test"` (the
-      MLIR test target in `mlir/lib/ABI/Targets/Test/`) is supported.  Real
-      targets (x86_64, AArch64, ...) will be added once the LLVM ABI library
-      ships them.
+    - `target=<name>` selects an ABI target.  The MLIR test target and x86_64
+      System V have full classifiers.  AArch64 uses the LLVM ABI classifier
+      for the scalar types it supports; i386 and PowerPC64 provide their
+      integer rules while their full classifiers are developed.
     - `classification-attr=<name>` reads a `DictionaryAttr` named `<name>`
       from each `cir.func` and parses it via the test-target injection
       helper.  Used by tests to inject arbitrary classifications without
@@ -246,7 +246,13 @@ def CallConvLowering : Pass<"cir-call-conv-lowering", 
"mlir::ModuleOp"> {
              clEnumValN(cir::CallConvTarget::Test, "test",
                         "MLIR test ABI target"),
              clEnumValN(cir::CallConvTarget::X86_64, "x86_64",
-                        "x86_64 System V")
+                        "x86_64 System V"),
+             clEnumValN(cir::CallConvTarget::X86_32, "x86_32",
+                        "32-bit x86"),
+             clEnumValN(cir::CallConvTarget::AArch64, "aarch64",
+                        "AArch64"),
+             clEnumValN(cir::CallConvTarget::PPC64, "ppc64",
+                        "64-bit PowerPC ELF")
            )}]>,
     Option<"classificationAttr", "classification-attr", "std::string",
            /*default=*/"\"\"",
diff --git a/clang/lib/CIR/CodeGen/CIRDataLayoutSpec.cpp 
b/clang/lib/CIR/CodeGen/CIRDataLayoutSpec.cpp
index a25fb46da5e8e7..fee867d11ee143 100644
--- a/clang/lib/CIR/CodeGen/CIRDataLayoutSpec.cpp
+++ b/clang/lib/CIR/CodeGen/CIRDataLayoutSpec.cpp
@@ -22,7 +22,8 @@
 #include "clang/CIR/MissingFeatures.h"
 #include "llvm/IR/DataLayout.h"
 
-void cir::setMLIRDataLayout(mlir::ModuleOp mod, const llvm::DataLayout &dl) {
+void cir::setMLIRDataLayout(mlir::ModuleOp mod, const llvm::DataLayout &dl,
+                            unsigned bitIntMaxAlign) {
   mlir::MLIRContext *mlirContext = mod.getContext();
   mlir::DataLayoutSpecInterface dlSpec =
       mlir::translateDataLayout(dl, mlirContext);
@@ -44,6 +45,18 @@ void cir::setMLIRDataLayout(mlir::ModuleOp mod, const 
llvm::DataLayout &dl) {
       dlSpec.getEntries().begin(), dlSpec.getEntries().end());
   entries.push_back(mlir::DataLayoutEntryAttr::get(ptrKey, ptrSpec));
 
+  // LLVM's data-layout string has no spelling for Clang's target-specific
+  // maximum _BitInt alignment (for example, 32 bits on i386, 64 on x86_64,
+  // and 128 on AArch64).  Record it on a sentinel CIR _BitInt entry so
+  // arbitrary-width IntTypes can recover the AST layout decision.
+  auto bitIntKey = cir::IntType::get(mlirContext, /*width=*/1,
+                                     /*is_signed=*/false,
+                                     /*is_bit_int=*/true);
+  auto bitIntMaxAlignAttr = mlir::IntegerAttr::get(
+      mlir::IntegerType::get(mlirContext, 32), bitIntMaxAlign);
+  entries.push_back(
+      mlir::DataLayoutEntryAttr::get(bitIntKey, bitIntMaxAlignAttr));
+
   mod->setAttr(mlir::DLTIDialect::kDataLayoutAttrName,
                mlir::DataLayoutSpecAttr::get(mlirContext, entries));
 }
diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp 
b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
index 8cbee4ea39a4b8..eb69f13f60db41 100644
--- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
@@ -58,7 +58,8 @@ void CIRGenerator::Initialize(ASTContext &astContext) {
   mlir::ModuleOp mod = cgm->getModule();
   llvm::DataLayout layout =
       llvm::DataLayout(astContext.getTargetInfo().getDataLayoutString());
-  cir::setMLIRDataLayout(mod, layout);
+  cir::setMLIRDataLayout(mod, layout,
+                         astContext.getTargetInfo().getBitIntMaxAlign());
 }
 
 bool CIRGenerator::verifyModule() const { return cgm->verifyModule(); }
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index b9263609e4fe48..d5dd387fc3f253 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -729,6 +729,23 @@ constexpr static uint64_t kBitsInByte = 8;
 constexpr static uint64_t kDefaultPointerSizeBits = 64;
 constexpr static uint64_t kDefaultPointerAlignment = 8;
 
+/// Read Clang's target-specific maximum _BitInt alignment, in bits, from the
+/// sentinel CIR integer data-layout entry.  Hand-written CIR without the entry
+/// keeps the historical/default 64-bit cap.
+static uint64_t getBitIntMaxAlign(mlir::DataLayoutEntryListRef params) {
+  for (mlir::DataLayoutEntryInterface entry : params) {
+    if (!entry.isTypeEntry())
+      continue;
+    auto key =
+        mlir::dyn_cast<cir::IntType>(mlir::cast<mlir::Type>(entry.getKey()));
+    if (!key || !key.isBitInt() || key.getWidth() != 1)
+      continue;
+    if (auto value = mlir::dyn_cast<mlir::IntegerAttr>(entry.getValue()))
+      return value.getValue().getZExtValue();
+  }
+  return 64;
+}
+
 /// Returns the default-address-space #cir.ptr_spec entry, or a synthesized
 /// 64-bit default when there is none. Per-AS entries are not modeled yet.
 cir::PtrSpecAttr getPointerSpec(mlir::DataLayoutEntryListRef params,
@@ -1046,7 +1063,7 @@ unsigned
 IntType::getStorageTypeWidth(const mlir::DataLayout &dataLayout) const {
   if (!isBitInt())
     return getWidth();
-  uint64_t alignBits = getABIAlignment(dataLayout, {}) * 8;
+  uint64_t alignBits = dataLayout.getTypeABIAlignment(*this) * 8;
   return static_cast<unsigned>(llvm::alignTo(getWidth(), alignBits));
 }
 
@@ -1063,10 +1080,10 @@ uint64_t IntType::getABIAlignment(const 
mlir::DataLayout &dataLayout,
                                   mlir::DataLayoutEntryListRef params) const {
   unsigned width = getWidth();
   if (isBitInt()) {
-    // _BitInt alignment: min(PowerOf2Ceil(width), 64 bits) in bytes.
-    // Matches Clang's TargetInfo::getBitIntAlign with default max = 64.
+    // _BitInt alignment: min(PowerOf2Ceil(width), target maximum) in bytes.
+    // Matches Clang's TargetInfo::getBitIntAlign.
     uint64_t alignBits =
-        std::min(llvm::PowerOf2Ceil(width), static_cast<uint64_t>(64));
+        std::min(llvm::PowerOf2Ceil(width), getBitIntMaxAlign(params));
     return std::max(alignBits / 8, static_cast<uint64_t>(1));
   }
   // Round up to a power-of-two byte alignment.  DataLayout consumers such as
diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp 
b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index ae7960654e1e1b..1ac8cd333d9347 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -16,8 +16,8 @@
 //   target=test
 //     Use the MLIR test ABI target (mlir/lib/ABI/Targets/Test/) to classify
 //     each function.  Predictable rules that approximate x86_64 SysV.  Real
-//     targets (x86_64, AArch64) will be added once the LLVM ABI library
-//     ships them.
+//     x86_64 and AArch64 use LLVM ABI library classifiers; i386 and
+//     PowerPC64 currently provide their integer rules.
 //
 //   classification-attr=<name>
 //     Read a DictionaryAttr named <name> from each cir.func and parse it via
@@ -69,10 +69,10 @@ namespace mlir {
 namespace {
 
 
//===----------------------------------------------------------------------===//
-// x86_64 System V classifier bridge
+// LLVM ABI classifier bridge
 //
-// Maps 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
+// Maps CIR types to llvm::abi::Type, runs an LLVM ABI Lowering Library target
+// classifier, and converts the result back into the dialect-agnostic
 // mlir::abi::FunctionClassification that CIRABIRewriteContext consumes.
 // isSupportedType says which CIR types the bridge handles, and a signature
 // naming any other fails the pass instead of being misclassified.
@@ -645,20 +645,91 @@ static llvm::abi::RequiredArgs requiredArgs(cir::FuncType 
fnTy) {
   return llvm::abi::RequiredArgs(fnTy.getNumInputs());
 }
 
-/// Classify an x86_64 SysV signature (return type + argument types) using the
-/// LLVM ABI library.  Shared by the cir.func path, the variadic-call path and
-/// the indirect-call path (the latter classifies from the callee function
+/// The current AArch64 LLVM ABI classifier implements scalar arguments and
+/// returns, but deliberately reports aggregate and vector classification NYI.
+/// Keep those types out of the bridge: its NYI fallback is Ignore, which would
+/// otherwise silently remove a value from the wire signature.
+static bool isSupportedTypeForTarget(mlir::Type ty, const DataLayout &dl,
+                                     cir::CallConvTarget target) {
+  if (!isSupportedType(ty, dl))
+    return false;
+  if (target != cir::CallConvTarget::AArch64)
+    return true;
+  return !isa<cir::ArrayType, cir::RecordType, cir::ComplexType,
+              cir::VectorType, cir::BitFieldType>(ty);
+}
+
+/// Classify the integer rules needed on targets that do not yet have a full
+/// LLVM ABI library target.  All other types stay Direct, preserving the
+/// pre-existing CIR behavior while fixing the function-boundary mismatch that
+/// wide _BitInt exposed.
+static FunctionClassification
+classifyTargetIntRules(mlir::Type retTy, mlir::TypeRange inputs,
+                       const DataLayout &dl, cir::CallConvTarget target) {
+  assert((target == cir::CallConvTarget::X86_32 ||
+          target == cir::CallConvTarget::AArch64 ||
+          target == cir::CallConvTarget::PPC64) &&
+         "only integer fallback targets reach this helper");
+
+  auto classify = [&](mlir::Type ty, bool isReturn) {
+    if (isa<cir::BoolType>(ty) && target != cir::CallConvTarget::AArch64)
+      return ArgClassification::getExtend(/*coercedType=*/nullptr,
+                                          /*signExtend=*/false);
+    auto intTy = dyn_cast<cir::IntType>(ty);
+    if (!intTy)
+      return isa<cir::VoidType>(ty) ? ArgClassification::getIgnore()
+                                    : ArgClassification::getDirect();
+
+    unsigned indirectAbove = target == cir::CallConvTarget::X86_32 ? 64 : 128;
+    if (intTy.isBitInt() && intTy.getWidth() > indirectAbove)
+      return ArgClassification::getIndirect(
+          llvm::Align(dl.getTypeABIAlignment(intTy)),
+          /*byVal=*/!isReturn && target == cir::CallConvTarget::PPC64);
+
+    unsigned extendBelow = target == cir::CallConvTarget::X86_32  ? 32
+                           : target == cir::CallConvTarget::PPC64 ? 64
+                                                                  : 0;
+    if (intTy.getWidth() < extendBelow)
+      return ArgClassification::getExtend(/*coercedType=*/nullptr,
+                                          intTy.isSigned());
+    return ArgClassification::getDirect();
+  };
+
+  FunctionClassification fc;
+  fc.returnsVoid = isa<cir::VoidType>(retTy);
+  fc.returnInfo = classify(retTy, /*isReturn=*/true);
+  for (mlir::Type input : inputs)
+    fc.argInfos.push_back(classify(input, /*isReturn=*/false));
+  return fc;
+}
+
+static bool signatureSupportedByABITarget(mlir::Type retTy,
+                                          mlir::TypeRange inputs,
+                                          const DataLayout &dl,
+                                          cir::CallConvTarget target) {
+  if (!isa<cir::VoidType>(retTy) &&
+      !isSupportedTypeForTarget(retTy, dl, target))
+    return false;
+  return llvm::all_of(inputs, [&](mlir::Type input) {
+    return isSupportedTypeForTarget(input, dl, target);
+  });
+}
+
+/// Classify a signature (return type + argument types) using an LLVM ABI
+/// library target.  Shared by the cir.func path, the variadic-call path and 
the
+/// indirect-call path (the latter classifies from the callee function
 /// pointer's pointee FuncType).  \p required marks where the declared
-/// parameters in \p inputs end.  The classifier treats every argument past 
that
-/// point as passed through an ellipsis.  Returns std::nullopt and emits an NYI
-/// error via \p emitError if the signature uses a type the bridge does not
+/// parameters in \p inputs end.  The classifier treats every argument past
+/// that point as passed through an ellipsis.  Returns std::nullopt and emits 
an
+/// NYI error via \p emitError if the signature uses a type the bridge does not
 /// handle yet.
-static std::optional<FunctionClassification> classifyX86_64Signature(
-    mlir::Type retCIR, mlir::TypeRange inputs, llvm::abi::RequiredArgs 
required,
-    MLIRContext *ctx, const DataLayout &dl,
-    mlir::abi::ABITypeMapper &typeMapper,
-    const llvm::abi::TargetInfo &targetInfo, ModuleOp modOp,
-    llvm::function_ref<mlir::InFlightDiagnostic()> emitError) {
+static std::optional<FunctionClassification>
+classifyABISignature(mlir::Type retCIR, mlir::TypeRange inputs,
+                     llvm::abi::RequiredArgs required, MLIRContext *ctx,
+                     const DataLayout &dl, mlir::abi::ABITypeMapper 
&typeMapper,
+                     const llvm::abi::TargetInfo &targetInfo, ModuleOp modOp,
+                     cir::CallConvTarget target, llvm::StringRef targetName,
+                     llvm::function_ref<mlir::InFlightDiagnostic()> emitError) 
{
   assert(retCIR && "signature return type must be non-null");
   assert((!required.allowsOptionalArgs() ||
           required.getNumRequiredArgs() <= inputs.size()) &&
@@ -666,11 +737,11 @@ static std::optional<FunctionClassification> 
classifyX86_64Signature(
   bool voidRet = isa<cir::VoidType>(retCIR);
 
   auto reject = [&](mlir::Type t) -> bool {
-    if (isSupportedType(t, dl))
+    if (isSupportedTypeForTarget(t, dl, target))
       return false;
-    emitError()
-        << "x86_64 calling-convention lowering not yet implemented for type "
-        << t;
+    emitError() << targetName
+                << " calling-convention lowering not yet implemented for type "
+                << t;
     return true;
   };
   if (!voidRet && reject(retCIR))
@@ -693,7 +764,8 @@ static std::optional<FunctionClassification> 
classifyX86_64Signature(
   // convertABIArgInfo returns nullopt when the classifier picks a coercion 
this
   // bridge cannot represent.
   auto nyiCoercion = [&](mlir::Type t) {
-    emitError() << "x86_64 calling-convention lowering not yet "
+    emitError() << targetName
+                << " calling-convention lowering not yet "
                    "implemented for the ABI coercion of type "
                 << t;
   };
@@ -753,15 +825,15 @@ static llvm::abi::X86AVXABILevel funcAvxLevel(cir::FuncOp 
func,
 /// std::nullopt and emits an NYI error if the signature uses a type the bridge
 /// does not handle yet.
 static std::optional<FunctionClassification>
-classifyX86_64Function(cir::FuncOp func, const DataLayout &dl,
-                       mlir::abi::ABITypeMapper &typeMapper,
-                       const llvm::abi::TargetInfo &targetInfo,
-                       ModuleOp modOp) {
+classifyABIFunction(cir::FuncOp func, const DataLayout &dl,
+                    mlir::abi::ABITypeMapper &typeMapper,
+                    const llvm::abi::TargetInfo &targetInfo, ModuleOp modOp,
+                    cir::CallConvTarget target, llvm::StringRef targetName) {
   cir::FuncType fnTy = func.getFunctionType();
-  return classifyX86_64Signature(fnTy.getReturnType(), fnTy.getInputs(),
-                                 requiredArgs(fnTy), func->getContext(), dl,
-                                 typeMapper, targetInfo, modOp,
-                                 [&]() { return func.emitOpError(); });
+  return classifyABISignature(fnTy.getReturnType(), fnTy.getInputs(),
+                              requiredArgs(fnTy), func->getContext(), dl,
+                              typeMapper, targetInfo, modOp, target, 
targetName,
+                              [&]() { return func.emitOpError(); });
 }
 
 /// Classify the single type fetched by a `cir.va_arg` as an unnamed argument
@@ -772,9 +844,10 @@ static std::optional<ArgClassification> 
classifyX86_64VarArgType(
     mlir::abi::ABITypeMapper &typeMapper,
     const llvm::abi::TargetInfo &targetInfo, ModuleOp modOp,
     llvm::function_ref<mlir::InFlightDiagnostic()> emitError) {
-  std::optional<FunctionClassification> fc = classifyX86_64Signature(
+  std::optional<FunctionClassification> fc = classifyABISignature(
       cir::VoidType::get(ctx), mlir::TypeRange(ty), llvm::abi::RequiredArgs(0),
-      ctx, dl, typeMapper, targetInfo, modOp, emitError);
+      ctx, dl, typeMapper, targetInfo, modOp, cir::CallConvTarget::X86_64,
+      "x86_64", emitError);
   if (!fc)
     return std::nullopt;
   return fc->argInfos[0];
@@ -787,17 +860,18 @@ static std::optional<ArgClassification> 
classifyX86_64VarArgType(
 /// small struct is passed in registers early in the list and in memory once
 /// the integer registers are gone.  Classifying from the call's operands
 /// rather than the callee's signature is what makes that accounting right.
-static std::optional<FunctionClassification> classifyX86_64VariadicCall(
+static std::optional<FunctionClassification> classifyABIVariadicCall(
     cir::CIRCallOpInterface call, cir::FuncType calleeTy, const DataLayout &dl,
     mlir::abi::ABITypeMapper &typeMapper,
-    const llvm::abi::TargetInfo &targetInfo, ModuleOp modOp) {
+    const llvm::abi::TargetInfo &targetInfo, ModuleOp modOp,
+    cir::CallConvTarget target, llvm::StringRef targetName) {
   assert(calleeTy.isVarArg() &&
          "only a variadic callee can take more operands than it declares");
   Operation *op = call.getOperation();
-  return classifyX86_64Signature(
+  return classifyABISignature(
       calleeTy.getReturnType(), call.getArgOperands().getTypes(),
       requiredArgs(calleeTy), op->getContext(), dl, typeMapper, targetInfo,
-      modOp, [&]() { return op->emitOpError(); });
+      modOp, target, targetName, [&]() { return op->emitOpError(); });
 }
 
 /// Whether \p fc gives the callee access to memory through a pointer the ABI
@@ -845,8 +919,10 @@ struct CallConvLoweringPass
   using CallConvLoweringBase::CallConvLoweringBase;
 
   CallConvLoweringPass(const CallConvLoweringOptions &options,
-                       const llvm::abi::X86ABICompatInfo &x86AbiCompat)
-      : CallConvLoweringBase(options), x86AbiCompat(x86AbiCompat) {}
+                       const llvm::abi::X86ABICompatInfo &x86AbiCompat,
+                       const llvm::abi::AArch64ABIOptions &aarch64Options)
+      : CallConvLoweringBase(options), x86AbiCompat(x86AbiCompat),
+        aarch64Options(aarch64Options) {}
 
   void runOnOperation() override;
 
@@ -855,6 +931,7 @@ struct CallConvLoweringPass
   /// struct has no command-line parser, so a cir-opt run gets the library
   /// defaults rather than a target's values.
   llvm::abi::X86ABICompatInfo x86AbiCompat;
+  llvm::abi::AArch64ABIOptions aarch64Options;
 };
 
 /// Record on \p fc whether \p returnType is CIR's void.  The x86_64 classifier
@@ -968,10 +1045,18 @@ void CallConvLoweringPass::runOnOperation() {
   static constexpr unsigned numAvxLevels =
       static_cast<unsigned>(llvm::abi::X86AVXABILevel::Last) + 1;
   bool isX86 = target == cir::CallConvTarget::X86_64;
-  std::optional<mlir::abi::ABITypeMapper> x86TypeMapper;
+  bool isAArch64 = target == cir::CallConvTarget::AArch64;
+  bool hasBitIntTarget = target == cir::CallConvTarget::X86_32 ||
+                         target == cir::CallConvTarget::PPC64;
+  bool hasABITarget = isX86 || isAArch64;
+  std::optional<mlir::abi::ABITypeMapper> abiTypeMapper;
   std::array<std::unique_ptr<llvm::abi::TargetInfo>, numAvxLevels> x86Targets;
-  if (isX86)
-    x86TypeMapper.emplace(dl);
+  std::unique_ptr<llvm::abi::TargetInfo> aarch64Target;
+  if (hasABITarget)
+    abiTypeMapper.emplace(dl);
+  if (isAArch64)
+    aarch64Target = llvm::abi::createAArch64TargetInfo(
+        abiTypeMapper->getTypeBuilder(), aarch64Options);
   auto x86TargetFor =
       [&](llvm::abi::X86AVXABILevel level) -> const llvm::abi::TargetInfo & {
     assert(static_cast<unsigned>(level) < numAvxLevels &&
@@ -980,7 +1065,7 @@ void CallConvLoweringPass::runOnOperation() {
         x86Targets[static_cast<unsigned>(level)];
     if (!slot)
       slot = llvm::abi::createX86_64TargetInfo(
-          x86TypeMapper->getTypeBuilder(), level,
+          abiTypeMapper->getTypeBuilder(), level,
           /*Has64BitPointers=*/true, x86AbiCompat);
     return *slot;
   };
@@ -990,6 +1075,52 @@ void CallConvLoweringPass::runOnOperation() {
       return baseAvxLevel;
     return funcAvxLevel(func, baseAvxLevel);
   };
+  auto targetFor = [&](cir::FuncOp func) -> const llvm::abi::TargetInfo & {
+    if (isX86)
+      return x86TargetFor(avxLevelFor(func));
+    assert(isAArch64 && "only LLVM ABI library targets reach this helper");
+    return *aarch64Target;
+  };
+  llvm::StringRef targetName;
+  switch (target.getValue()) {
+  case cir::CallConvTarget::X86_64:
+    targetName = "x86_64";
+    break;
+  case cir::CallConvTarget::X86_32:
+    targetName = "i386";
+    break;
+  case cir::CallConvTarget::AArch64:
+    targetName = "AArch64";
+    break;
+  case cir::CallConvTarget::PPC64:
+    targetName = "PowerPC64";
+    break;
+  default:
+    break;
+  }
+
+  // CIRABIRewriteContext::rewriteVAArg currently implements only the x86_64
+  // SysV va_list layout.  Reject _BitInt fetches on the other new targets
+  // before mutating any signatures.  Even directly-passed widths need the
+  // target's register-save-area and stack cursor rules; leaving cir.va_arg to
+  // generic LLVM lowering can miscompile and, on AArch64 ELF, assert in the
+  // backend.
+  if (isAArch64 || hasBitIntTarget) {
+    bool hasVAArg = false;
+    moduleOp.walk([&](cir::VAArgOp v) {
+      auto intTy = dyn_cast<cir::IntType>(v.getType());
+      if (!intTy || !intTy.isBitInt())
+        return;
+      v->emitOpError() << targetName
+                       << " va_arg lowering for _BitInt not yet "
+                          "implemented in CallConvLowering";
+      hasVAArg = true;
+    });
+    if (hasVAArg) {
+      signalPassFailure();
+      return;
+    }
+  }
 
   // Classify every cir.func up front.  No IR mutation happens here, so
   // later walks can consult any function's classification regardless of
@@ -1007,9 +1138,14 @@ void CallConvLoweringPass::runOnOperation() {
          llvm::any_of(fnTy.getInputs(), hasIncompleteRecordByValue)))
       return;
     std::optional<FunctionClassification> fc;
-    if (isX86)
-      fc = classifyX86_64Function(f, dl, *x86TypeMapper,
-                                  x86TargetFor(avxLevelFor(f)), moduleOp);
+    if (hasABITarget &&
+        (isX86 || signatureSupportedByABITarget(fnTy.getReturnType(),
+                                                fnTy.getInputs(), dl, target)))
+      fc = classifyABIFunction(f, dl, *abiTypeMapper, targetFor(f), moduleOp,
+                               target, targetName);
+    else if (hasBitIntTarget || isAArch64)
+      fc = classifyTargetIntRules(fnTy.getReturnType(), fnTy.getInputs(), dl,
+                                  target);
     else
       fc = classifyFunction(f, dl, target, classificationAttr);
     if (!fc) {
@@ -1046,11 +1182,13 @@ void CallConvLoweringPass::runOnOperation() {
       return;
     callers[callee].push_back(op);
 
-    // Only the x86_64 driver classifies per call site.  Under the other
-    // drivers the classification comes from a fixed per-function source, so
-    // such a call stays short a classification and rewriteCallSite reports it.
+    // LLVM ABI targets classify a variadic call from its full operand list.
+    // Under the other drivers the classification comes from a fixed
+    // per-function source, so such a call stays short a classification and
+    // rewriteCallSite reports it.
     cir::FuncType calleeTy = callee.getFunctionType();
-    if (!isX86 || call.getNumArgOperands() <= calleeTy.getNumInputs())
+    if ((!hasABITarget && !hasBitIntTarget) ||
+        call.getNumArgOperands() <= calleeTy.getNumInputs())
       return;
     // A callee declared without a prototype also takes more operands than it
     // declares, and the verifier allows it.  Those extra arguments are named
@@ -1067,9 +1205,17 @@ void CallConvLoweringPass::runOnOperation() {
     // Classic instead arranges every call site from the caller and reports a
     // caller whose level disagrees with its callee in checkFunctionCallABI,
     // which has no equivalent here yet.
-    std::optional<FunctionClassification> fc =
-        classifyX86_64VariadicCall(call, calleeTy, dl, *x86TypeMapper,
-                                   x86TargetFor(avxLevelFor(callee)), 
moduleOp);
+    std::optional<FunctionClassification> fc;
+    mlir::TypeRange callArgTypes = call.getArgOperands().getTypes();
+    if (hasABITarget &&
+        (isX86 || signatureSupportedByABITarget(calleeTy.getReturnType(),
+                                                callArgTypes, dl, target)))
+      fc = classifyABIVariadicCall(call, calleeTy, dl, *abiTypeMapper,
+                                   targetFor(callee), moduleOp, target,
+                                   targetName);
+    else
+      fc = classifyTargetIntRules(calleeTy.getReturnType(), callArgTypes, dl,
+                                  target);
     if (!fc) {
       anyFailed = true;
       return;
@@ -1187,12 +1333,16 @@ void CallConvLoweringPass::runOnOperation() {
       // A callee resolved at run time carries no features of its own, so the
       // level comes from the function containing the call, which is the
       // declaration classic arranges every call site from.
-      if (isX86)
-        return classifyX86_64Signature(
+      if (hasABITarget &&
+          (isX86 || signatureSupportedByABITarget(funcTy.getReturnType(),
+                                                  argTypes, dl, target)))
+        return classifyABISignature(
             funcTy.getReturnType(), argTypes, requiredArgs(funcTy), ctx, dl,
-            *x86TypeMapper,
-            x86TargetFor(avxLevelFor(c->getParentOfType<cir::FuncOp>())),
-            moduleOp, [&]() { return c->emitOpError(); });
+            *abiTypeMapper, targetFor(c->getParentOfType<cir::FuncOp>()),
+            moduleOp, target, targetName, [&]() { return c->emitOpError(); });
+      if (hasBitIntTarget || isAArch64)
+        return classifyTargetIntRules(funcTy.getReturnType(), argTypes, dl,
+                                      target);
       return withReturnVoidness(
           mlir::abi::test::classify(argTypes, funcTy.getReturnType(), dl),
           funcTy.getReturnType());
@@ -1241,7 +1391,7 @@ void CallConvLoweringPass::runOnOperation() {
     for (cir::VAArgOp v : vaArgs) {
       cir::FuncOp enclosing = v->getParentOfType<cir::FuncOp>();
       std::optional<ArgClassification> ac = classifyX86_64VarArgType(
-          v.getType(), ctx, dl, *x86TypeMapper,
+          v.getType(), ctx, dl, *abiTypeMapper,
           x86TargetFor(avxLevelFor(enclosing)), moduleOp,
           [&]() { return v->emitOpError(); });
       if (!ac) {
@@ -1265,10 +1415,12 @@ std::unique_ptr<Pass> 
mlir::createCallConvLoweringPass() {
 std::unique_ptr<Pass> mlir::createCallConvLoweringPass(
     cir::CallConvTarget target, llvm::abi::X86AVXABILevel x86AvxAbiLevel,
     bool allowsX86TargetAttrAvx,
-    const llvm::abi::X86ABICompatInfo &x86AbiCompat) {
+    const llvm::abi::X86ABICompatInfo &x86AbiCompat,
+    const llvm::abi::AArch64ABIOptions &aarch64Options) {
   CallConvLoweringOptions options;
   options.target = target;
   options.x86AvxAbiLevel = x86AvxAbiLevel;
   options.allowsX86TargetAttrAvx = allowsX86TargetAttrAvx;
-  return std::make_unique<CallConvLoweringPass>(options, x86AbiCompat);
+  return std::make_unique<CallConvLoweringPass>(options, x86AbiCompat,
+                                                aarch64Options);
 }
diff --git 
a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp 
b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp
index a9f73e667f1f3f..43f49971e749a3 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp
@@ -31,16 +31,17 @@ using namespace mlir::abi;
 //
 // An Indirect argument is byval or not, following its classification's
 // byVal flag.  byval is a by-value parameter the ABI passes in memory rather
-// than registers, usually for its size.  Non-byval is a by-value parameter
-// whose type cannot be copied freely, because it has a non-trivial copy
-// constructor, move constructor, or destructor, so the callee works on the
-// caller's own object rather than a copy.
+// than registers, usually for its size.  Non-byval has two forms.  A record
+// whose type cannot be copied freely (for example because it has a non-trivial
+// copy constructor) is forwarded in the caller's storage.  Some ABIs also
+// classify a trivially-copyable scalar such as a wide _BitInt as non-byval
+// indirect; that still needs a source-language value copy, just without an
+// LLVM byval attribute.
 //
-// At the call site byval copies into a fresh alloca while a non-byval
-// argument forwards the caller's storage.  At the callee, byval loads the
-// incoming pointer (a local copy), while non-byval rewires the CIRGen
-// param-slot alloca to the incoming pointer so the body mutates the caller's
-// storage in place.
+// At the call site byval and non-record indirect arguments copy into a fresh
+// alloca, while a non-byval record forwards the caller's storage.  At the
+// callee, copied arguments load the incoming pointer; a forwarded record
+// rewires the CIRGen param-slot alloca to the incoming pointer.
 //
 // For Expand, the single struct argument is replaced by N scalar arguments
 // (one per field).  At the callee, the N field block arguments are stored
@@ -241,15 +242,21 @@ mlir::ArrayAttr updateArgAttrs(mlir::MLIRContext *ctx,
         attrs.set(mlir::LLVM::LLVMDialect::getByValAttrName(),
                   mlir::TypeAttr::get(pointeeTy));
       } else {
-        // Classic adds llvm.dead_on_return when the object's lifetime ends in
-        // the callee, which needs the destructor's triviality from
-        // cir.record_layout's has_trivial_dtor.
-        assert(!cir::MissingFeatures::deadOnReturnAttr());
         attrs.set(mlir::LLVM::LLVMDialect::getNoFreeObjAttrName(),
                   builder.getUnitAttr());
         attrs.set(mlir::LLVM::LLVMDialect::getDereferenceableAttrName(),
                   builder.getI64IntegerAttr(
                       dl.getTypeSize(pointeeTy).getFixedValue()));
+        // A copied scalar's temporary dies when the callee returns, exactly
+        // the lifetime represented by bare LLVM `dead_on_return` (all 
reachable
+        // memory, encoded as the all-ones integer form).  Records still need
+        // the destructor-triviality information called out by the existing
+        // MissingFeatures hook before this can be set generally.
+        if (!isa<cir::RecordType>(pointeeTy))
+          attrs.set(mlir::LLVM::LLVMDialect::getDeadOnReturnAttrName(),
+                    builder.getI64IntegerAttr(-1));
+        else
+          assert(!cir::MissingFeatures::deadOnReturnAttr());
       }
       newArgAttrs.push_back(attrs.getDictionary(ctx));
     } else {
@@ -771,13 +778,14 @@ void insertArgCoercion(
       // to adapted (now of the original type != the alloca's pointee type).
       blockArg.replaceAllUsesExcept(adapted, coercionOps);
     } else if (ac.kind == ArgKind::Indirect) {
-      // byval and non-byval both lower to !cir.ptr<T>, and which it is shows
-      // up only in the attrs updateArgAttrs applies.  Body lowering differs:
-      // byval copies into the callee (load at entry), while non-byval must
-      // operate on the caller's storage in place.
+      // Every indirect argument lowers to !cir.ptr<T>.  An LLVM byval
+      // argument and a non-record argument both represent a source-language
+      // value copy, while a non-byval record must operate on the caller's
+      // storage in place.
+      bool copyIndirect = ac.byVal || 
!isa<cir::RecordType>(blockArg.getType());
       auto ptrTy = cir::PointerType::get(blockArg.getType());
 
-      if (!ac.byVal) {
+      if (!copyIndirect) {
         // Without byval, drop the spill store and let the slot's uses read the
         // incoming pointer, so the body operates on the caller's storage in
         // place.  A byte-copy would be wrong for non-trivially-copyable
@@ -800,8 +808,8 @@ void insertArgCoercion(
         if (destAlloca)
           pendingParamSlots.emplace_back(destAlloca, blockArg);
       } else {
-        // byval: load the incoming pointer so the body sees a T value (and
-        // any CIRGen param-slot store becomes a local copy of that value).
+        // Load the incoming pointer so the body sees a T value and any CIRGen
+        // parameter-slot store becomes a local copy of that value.
         blockArg.setType(ptrTy);
 
         builder.setInsertionPointToStart(&entry);
@@ -1524,12 +1532,11 @@ CIRABIRewriteContext::rewriteCallSite(mlir::Operation 
*callOp,
                          dl, ac.directOffset);
       newArgs.push_back(arg);
     } else if (ac.kind == ArgKind::Indirect) {
-      // byval hands the callee its own copy.  Without byval the argument must
-      // name the caller's storage instead, so that the object the callee
-      // operates on is the one the caller destroys.  That means forwarding
-      // the address the operand was loaded from rather than the loaded value,
-      // so a store to that storage after the load is visible to the callee.
-      if (!ac.byVal) {
+      // byval and non-record indirect arguments hand the callee a value copy.
+      // A non-byval record must name the caller's storage instead, so that the
+      // object the callee operates on is the one the caller destroys.
+      bool copyIndirect = ac.byVal || !isa<cir::RecordType>(arg.getType());
+      if (!copyIndirect) {
         // The rewritten parameter is a pointer to the argument type in the
         // default address space, so an operand read through an address-space
         // cast cannot be handed on as it stands.  cir.load already pins the
diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp 
b/clang/lib/CIR/Lowering/CIRPasses.cpp
index b612b64b714760..28f18e4c128b1e 100644
--- a/clang/lib/CIR/Lowering/CIRPasses.cpp
+++ b/clang/lib/CIR/Lowering/CIRPasses.cpp
@@ -28,6 +28,16 @@ static CallConvTarget getCallConvTarget(const llvm::Triple 
&triple) {
   // Windows is not supported.  UEFI shares its convention.
   if (triple.getArch() == llvm::Triple::x86_64 && !triple.isOSWindowsOrUEFI())
     return CallConvTarget::X86_64;
+  if (triple.getArch() == llvm::Triple::x86 && triple.isOSBinFormatELF())
+    return CallConvTarget::X86_32;
+  if (triple.getArch() == llvm::Triple::aarch64 ||
+      triple.getArch() == llvm::Triple::aarch64_32 ||
+      triple.getArch() == llvm::Triple::aarch64_be)
+    return CallConvTarget::AArch64;
+  if ((triple.getArch() == llvm::Triple::ppc64 ||
+       triple.getArch() == llvm::Triple::ppc64le) &&
+      triple.isOSBinFormatELF())
+    return CallConvTarget::PPC64;
   return CallConvTarget::None;
 }
 
@@ -74,6 +84,31 @@ getX86ABICompatInfo(const clang::ASTContext &astContext,
   return abiCompat;
 }
 
+/// Resolve Clang's target and language options into the flags consumed by the
+/// LLVM ABI library's AArch64 classifier.  Keep this in sync with classic
+/// CodeGen's CodeGenModule::getLLVMABITargetInfo.
+static llvm::abi::AArch64ABIOptions
+getAArch64ABIOptions(const clang::ASTContext &astContext,
+                     clang::LangOptions::ClangABI compat) {
+  const clang::TargetInfo &targetInfo = astContext.getTargetInfo();
+  const llvm::Triple &triple = targetInfo.getTriple();
+  llvm::abi::AArch64ABIOptions options;
+  if (targetInfo.getABI() == "darwinpcs")
+    options.Kind = llvm::abi::AArch64ABIKind::DarwinPCS;
+  else if (triple.isOSWindows())
+    options.Kind = llvm::abi::AArch64ABIKind::Win64;
+  else if (targetInfo.getABI() == "aapcs-soft")
+    options.Kind = llvm::abi::AArch64ABIKind::AAPCSSoft;
+  else
+    options.Kind = llvm::abi::AArch64ABIKind::AAPCS;
+
+  options.IsILP32 = triple.getArch() == llvm::Triple::aarch64_32;
+  options.IsCXX = astContext.getLangOpts().CPlusPlus;
+  options.IsMicrosoftCXXABI = targetInfo.getCXXABI().isMicrosoft();
+  options.CompatInfo.IsMatrixHA = compat > clang::LangOptions::ClangABI::Ver23;
+  return options;
+}
+
 mlir::LogicalResult
 runCIRToCIRPasses(mlir::ModuleOp theModule, mlir::MLIRContext &mlirContext,
                   clang::ASTContext &astContext, bool enableVerifier,
@@ -116,8 +151,8 @@ runCIRToCIRPasses(mlir::ModuleOp theModule, 
mlir::MLIRContext &mlirContext,
   if (enableCallConvLowering) {
     // CallConvLowering rewrites signatures and call sites using the 
classifier,
     // so it must run after CXXABILowering has lowered C++ ABI types to plain
-    // records the classifier can handle.  Only the x86_64 System V classifier
-    // is implemented; other targets are left unchanged.
+    // records the classifier can handle.  Targets without a full LLVM ABI
+    // library classifier use only the rules implemented by this pass.
     const clang::TargetInfo &targetInfo = astContext.getTargetInfo();
     CallConvTarget target = getCallConvTarget(targetInfo.getTriple());
     if (target != CallConvTarget::None) {
@@ -135,7 +170,8 @@ runCIRToCIRPasses(mlir::ModuleOp theModule, 
mlir::MLIRContext &mlirContext,
       pm.addPass(mlir::createCallConvLoweringPass(
           target, getX86AVXABILevel(targetInfo.getABI()),
           allowsX86TargetAttrAvx(astContext, compat),
-          getX86ABICompatInfo(astContext, compat)));
+          getX86ABICompatInfo(astContext, compat),
+          getAArch64ABIOptions(astContext, compat)));
     }
   }
 
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 53bffe02590025..13863d45323555 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -85,7 +85,7 @@ static uint64_t getMemoryFallbackAlignment(mlir::Type cirType,
                                            const mlir::DataLayout &dataLayout) 
{
   if (auto intTy = mlir::dyn_cast<cir::IntType>(cirType);
       intTy && intTy.isBitInt())
-    return intTy.getABIAlignment(dataLayout, {});
+    return dataLayout.getTypeABIAlignment(intTy);
   return dataLayout.getTypeABIAlignment(llvmMemType);
 }
 
@@ -4501,14 +4501,14 @@ void ConvertCIRToLLVMPass::runOnOperation() {
   if (failed(applyPartialConversion(ops, target, std::move(patterns))))
     signalPassFailure();
 
-  // Drop the cir.ptr-keyed data-layout entries: they drove pointer-width
+  // Drop the CIR-type-keyed data-layout entries: they drove CIR layout
   // queries up to this point, but the LLVM IR exporter rejects CIR types.
   if (auto dlSpec = mlir::dyn_cast_or_null<mlir::DataLayoutSpecAttr>(
           module->getAttr(mlir::DLTIDialect::kDataLayoutAttrName))) {
     llvm::SmallVector<mlir::DataLayoutEntryInterface> kept;
     for (mlir::DataLayoutEntryInterface entry : dlSpec.getEntries()) {
-      if (entry.isTypeEntry() &&
-          mlir::isa<cir::PointerType>(mlir::cast<mlir::Type>(entry.getKey())))
+      if (entry.isTypeEntry() && mlir::isa<cir::PointerType, cir::IntType>(
+                                     mlir::cast<mlir::Type>(entry.getKey())))
         continue;
       kept.push_back(entry);
     }
diff --git a/clang/lib/CIR/Lowering/LoweringHelpers.cpp 
b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
index 83d6ef6d935ab5..ed59ce28038b65 100644
--- a/clang/lib/CIR/Lowering/LoweringHelpers.cpp
+++ b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
@@ -11,6 +11,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "clang/CIR/LoweringHelpers.h"
+#include "mlir/Dialect/DLTI/DLTI.h"
 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
 #include "mlir/Dialect/LLVMIR/LLVMTypes.h"
 #include "mlir/IR/BuiltinTypes.h"
@@ -61,10 +62,17 @@ mlir::Attribute 
getBitIntStorageAttr(mlir::ConversionPatternRewriter &rewriter,
   // If we have to do split storage, we are an array of bytes.  Split this up
   // into the array that matches convertTypeForMemory.
   unsigned numBytes = storageBits / 8;
+  bool isBigEndian = false;
+  if (auto endianness = mlir::dyn_cast_if_present<mlir::StringAttr>(
+          dataLayout.getEndianness()))
+    isBigEndian =
+        endianness.getValue() == mlir::DLTIDialect::kDataLayoutEndiannessBig;
   llvm::SmallVector<mlir::APInt> bytes;
   bytes.reserve(numBytes);
-  for (unsigned i = 0; i != numBytes; ++i)
-    bytes.emplace_back(8, val.extractBitsAsZExtValue(8, i * 8));
+  for (unsigned i = 0; i != numBytes; ++i) {
+    unsigned valueByte = isBigEndian ? numBytes - i - 1 : i;
+    bytes.emplace_back(8, val.extractBitsAsZExtValue(8, valueByte * 8));
+  }
 
   auto i8Ty = mlir::IntegerType::get(intTy.getContext(), 8);
   return mlir::DenseElementsAttr::get(
diff --git a/clang/test/CIR/CodeGen/bitint-wide-big-endian.c 
b/clang/test/CIR/CodeGen/bitint-wide-big-endian.c
new file mode 100644
index 00000000000000..d54169b1498a34
--- /dev/null
+++ b/clang/test/CIR/CodeGen/bitint-wide-big-endian.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -triple powerpc64-unknown-linux-gnu \
+// RUN:   -fexperimental-max-bitint-width=8388608 -fclangir -emit-llvm %s -o - 
\
+// RUN:   | FileCheck %s
+// RUN: %clang_cc1 -triple powerpc64-unknown-linux-gnu \
+// RUN:   -fexperimental-max-bitint-width=8388608 -emit-llvm %s -o - \
+// RUN:   | FileCheck %s
+
+signed _BitInt(129) one = 1;
+signed _BitInt(129) minus_two = -2;
+
+// CHECK-DAG: @one = global [24 x i8] 
c"\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\01", 
align 8
+// CHECK-DAG: @minus_two = global [24 x i8] 
c"\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FE", 
align 8
diff --git a/clang/test/CIR/CodeGen/call-conv-lowering-bitint-targets.c 
b/clang/test/CIR/CodeGen/call-conv-lowering-bitint-targets.c
new file mode 100644
index 00000000000000..3053ad64b9eadf
--- /dev/null
+++ b/clang/test/CIR/CodeGen/call-conv-lowering-bitint-targets.c
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -triple i386-unknown-linux-gnu \
+// RUN:   -fexperimental-max-bitint-width=8388608 -fclangir -emit-llvm %s -o - 
\
+// RUN:   | FileCheck %s --check-prefix=I386
+// RUN: %clang_cc1 -triple powerpc64-unknown-linux-gnu \
+// RUN:   -fexperimental-max-bitint-width=8388608 -fclangir -emit-llvm %s -o - 
\
+// RUN:   | FileCheck %s --check-prefix=PPC64
+// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu \
+// RUN:   -fexperimental-max-bitint-width=8388608 -fclangir -emit-llvm %s -o - 
\
+// RUN:   | FileCheck %s --check-prefix=AARCH64
+// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu \
+// RUN:   -fexperimental-max-bitint-width=8388608 -fclangir -emit-cir %s -o 
%t.cir
+// RUN: cir-translate -cir-to-llvmir --disable-cc-lowering %t.cir -o - \
+// RUN:   | FileCheck %s --check-prefix=AARCH64
+
+typedef signed _BitInt(129) i129;
+typedef i129 (*fn_t)(i129);
+
+extern i129 external(i129);
+
+i129 direct(i129 value) { return external(value); }
+
+// I386-LABEL: define dso_local void @direct(
+// I386-SAME: ptr dead_on_unwind noalias writable sret([20 x i8]) align 4 
%{{[^,]+}},
+// I386-SAME: ptr nofreeobj noundef align 4 dead_on_return dereferenceable(20) 
%{{[^)]+}})
+// I386: load i160, ptr %{{[^,]+}}, align 4
+// I386: call void @external(
+// I386-SAME: ptr dead_on_unwind writable sret([20 x i8]) align 4 %{{[^,]+}},
+// I386-SAME: ptr nofreeobj noundef align 4 dead_on_return dereferenceable(20) 
%{{[^)]+}})
+// I386: declare void @external(
+// I386-SAME: ptr dead_on_unwind writable sret([20 x i8]) align 4,
+// I386-SAME: ptr nofreeobj noundef align 4 dead_on_return dereferenceable(20))
+
+// PPC64-LABEL: define dso_local void @direct(
+// PPC64-SAME: ptr dead_on_unwind noalias writable sret([24 x i8]) align 8 
%{{[^,]+}},
+// PPC64-SAME: ptr noundef byval([24 x i8]) align 8 %{{[^)]+}})
+// PPC64: load i192, ptr %{{[^,]+}}, align 8
+// PPC64: call void @external(
+// PPC64-SAME: ptr dead_on_unwind writable sret([24 x i8]) align 8 %{{[^,]+}},
+// PPC64-SAME: ptr noundef byval([24 x i8]) align 8 %{{[^)]+}})
+// PPC64: declare void @external(
+// PPC64-SAME: ptr dead_on_unwind writable sret([24 x i8]) align 8,
+// PPC64-SAME: ptr noundef byval([24 x i8]) align 8)
+
+// AARCH64-LABEL: define dso_local void @direct(
+// AARCH64-SAME: ptr dead_on_unwind noalias writable sret(i256) align 16 
%{{[^,]+}},
+// AARCH64-SAME: ptr nofreeobj noundef align 16 dead_on_return 
dereferenceable(32) %{{[^)]+}})
+// AARCH64: load i256, ptr %{{[^,]+}}, align 16
+// AARCH64: call void @external(
+// AARCH64-SAME: ptr dead_on_unwind writable sret(i256) align 16 %{{[^,]+}},
+// AARCH64-SAME: ptr nofreeobj noundef align 16 dead_on_return 
dereferenceable(32) %{{[^)]+}})
+// AARCH64: declare void @external(
+// AARCH64-SAME: ptr dead_on_unwind writable sret(i256) align 16,
+// AARCH64-SAME: ptr nofreeobj noundef align 16 dead_on_return 
dereferenceable(32))
+
+i129 indirect(fn_t fn, i129 value) { return fn(value); }
+
+// I386-LABEL: define dso_local void @indirect(
+// I386: call void %{{[^ (]+}}(
+// I386-SAME: ptr dead_on_unwind writable sret([20 x i8]) align 4 %{{[^,]+}},
+// I386-SAME: ptr nofreeobj noundef align 4 dead_on_return dereferenceable(20) 
%{{[^)]+}})
+
+// PPC64-LABEL: define dso_local void @indirect(
+// PPC64: call void %{{[^ (]+}}(
+// PPC64-SAME: ptr dead_on_unwind writable sret([24 x i8]) align 8 %{{[^,]+}},
+// PPC64-SAME: ptr noundef byval([24 x i8]) align 8 %{{[^)]+}})
+
+// AARCH64-LABEL: define dso_local void @indirect(
+// AARCH64: call void %{{[^ (]+}}(
+// AARCH64-SAME: ptr dead_on_unwind writable sret(i256) align 16 %{{[^,]+}},
+// AARCH64-SAME: ptr nofreeobj noundef align 16 dead_on_return 
dereferenceable(32) %{{[^)]+}})
+
+signed _BitInt(31) extend31(signed _BitInt(31) value) { return value; }
+signed _BitInt(33) extend33(signed _BitInt(33) value) { return value; }
+_Bool extend_bool(_Bool value) { return value; }
+
+// I386-LABEL: define dso_local signext i31 @extend31(i31 noundef signext
+// I386-LABEL: define dso_local i33 @extend33(i33 noundef
+// I386-LABEL: define dso_local zeroext i1 @extend_bool(i1 noundef zeroext
+// PPC64-LABEL: define dso_local signext i31 @extend31(i31 noundef signext
+// PPC64-LABEL: define dso_local signext i33 @extend33(i33 noundef signext
+// PPC64-LABEL: define dso_local zeroext i1 @extend_bool(i1 noundef zeroext
+// AARCH64-LABEL: define dso_local i31 @extend31(i31 noundef
+// AARCH64-LABEL: define dso_local i33 @extend33(i33 noundef
+// AARCH64-LABEL: define dso_local i1 @extend_bool(i1 noundef
+
+signed _BitInt(64) boundary64(signed _BitInt(64) value) { return value; }
+signed _BitInt(65) boundary65(signed _BitInt(65) value) { return value; }
+signed _BitInt(128) boundary128(signed _BitInt(128) value) { return value; }
+
+// I386-LABEL: define dso_local i64 @boundary64(i64 noundef
+// I386-LABEL: define dso_local void @boundary65(
+// I386-SAME: ptr dead_on_unwind noalias writable sret([12 x i8]) align 4 
%{{[^,]+}},
+// I386-SAME: ptr nofreeobj noundef align 4 dead_on_return dereferenceable(12) 
%{{[^)]+}})
+// I386-LABEL: define dso_local void @boundary128(
+// I386-SAME: ptr dead_on_unwind noalias writable sret(i128) align 4 
%{{[^,]+}},
+// I386-SAME: ptr nofreeobj noundef align 4 dead_on_return dereferenceable(16) 
%{{[^)]+}})
+// PPC64-LABEL: define dso_local i64 @boundary64(i64 noundef
+// PPC64-LABEL: define dso_local i65 @boundary65(i65 noundef
+// PPC64-LABEL: define dso_local i128 @boundary128(i128 noundef
+// AARCH64-LABEL: define dso_local i64 @boundary64(i64 noundef
+// AARCH64-LABEL: define dso_local i65 @boundary65(i65 noundef
+// AARCH64-LABEL: define dso_local i128 @boundary128(i128 noundef
diff --git 
a/clang/test/CIR/CodeGen/call-conv-lowering-bitint-vaarg-unsupported.c 
b/clang/test/CIR/CodeGen/call-conv-lowering-bitint-vaarg-unsupported.c
new file mode 100644
index 00000000000000..6df759c49df6d5
--- /dev/null
+++ b/clang/test/CIR/CodeGen/call-conv-lowering-bitint-vaarg-unsupported.c
@@ -0,0 +1,16 @@
+// RUN: not %clang_cc1 -triple i386-unknown-linux-gnu -fclangir -emit-llvm \
+// RUN:   %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=I386
+// RUN: not %clang_cc1 -triple powerpc64-unknown-linux-gnu -fclangir \
+// RUN:   -emit-llvm %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=PPC64
+// RUN: not %clang_cc1 -triple aarch64-unknown-linux-gnu -fclangir -emit-llvm \
+// RUN:   %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=AARCH64
+
+typedef signed _BitInt(31) value_t;
+
+value_t fetch(__builtin_va_list ap) {
+  return __builtin_va_arg(ap, value_t);
+}
+
+// I386: error: 'cir.va_arg' op i386 va_arg lowering for _BitInt not yet 
implemented in CallConvLowering
+// PPC64: error: 'cir.va_arg' op PowerPC64 va_arg lowering for _BitInt not yet 
implemented in CallConvLowering
+// AARCH64: error: 'cir.va_arg' op AArch64 va_arg lowering for _BitInt not yet 
implemented in CallConvLowering
diff --git a/clang/tools/cir-translate/cir-translate.cpp 
b/clang/tools/cir-translate/cir-translate.cpp
index cefa7d2996f300..3e882217df7ca1 100644
--- a/clang/tools/cir-translate/cir-translate.cpp
+++ b/clang/tools/cir-translate/cir-translate.cpp
@@ -114,7 +114,8 @@ llvm::LogicalResult 
prepareCIRModuleDataLayout(mlir::ModuleOp mod,
   context->loadDialect<mlir::DLTIDialect, mlir::LLVM::LLVMDialect,
                        mlir::omp::OpenMPDialect>();
 
-  cir::setMLIRDataLayout(mod, llvm::DataLayout(layoutString));
+  cir::setMLIRDataLayout(mod, llvm::DataLayout(layoutString),
+                         targetInfo->getBitIntMaxAlign());
 
   return llvm::success();
 }

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

Reply via email to