[clang] [CIR][ARM] Generic 32-bit ARM codegen and lowering support (PR #204360)
Akshay K via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 30 09:47:59 PDT 2026
https://github.com/kumarak updated https://github.com/llvm/llvm-project/pull/204360
>From 23fe8191f022af0a10d5208571fc0c9de9af9dea Mon Sep 17 00:00:00 2001
From: AkshayK <iit.akshay at gmail.com>
Date: Wed, 17 Jun 2026 10:16:06 -0400
Subject: [PATCH 1/2] [CIR] Pointer and vptr width from a CIR-native
data-layout entry
Port of llvm/llvm-project#204185. Attach a cir.ptr data-layout entry at module
setup storing {size, abi-align} read from the target DataLayout; PointerType and
VPtrType read their width from it (falling back to 64/8 when absent), and the
CIR->LLVM lowering strips the entry. Fixes record-layout crashes on targets with
32-bit pointers (e.g. nvptx, spirv32).
---
clang/lib/CIR/CodeGen/CIRGenerator.cpp | 25 ++++++++-
clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 56 +++++++++++++++++--
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 17 ++++++
.../test/CIR/CodeGen/pointer-width-32bit.cpp | 44 +++++++++++++++
4 files changed, 135 insertions(+), 7 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/pointer-width-32bit.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
index d4fcbb6e42f3e..61efaebad9b82 100644
--- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
@@ -21,6 +21,7 @@
#include "clang/AST/DeclGroup.h"
#include "clang/CIR/CIRGenerator.h"
#include "clang/CIR/InitAllDialects.h"
+#include "clang/CIR/MissingFeatures.h"
#include "llvm/IR/DataLayout.h"
using namespace cir;
@@ -42,7 +43,29 @@ static void setMLIRDataLayout(mlir::ModuleOp &mod, const llvm::DataLayout &dl) {
mlir::MLIRContext *mlirContext = mod.getContext();
mlir::DataLayoutSpecInterface dlSpec =
mlir::translateDataLayout(dl, mlirContext);
- mod->setAttr(mlir::DLTIDialect::kDataLayoutAttrName, dlSpec);
+
+ // Add a CIR-native pointer data-layout entry so cir.ptr / cir.vptr size and
+ // alignment are driven by the data layout rather than hardcoded.
+ // The value stores {size-in-bits, abi-align-in-bits} keyed on cir.ptr.
+ //
+ // TODO(cir): Only the default address space is recorded and
+ // address-space-dependent pointer sizes are not modeled yet. Emit
+ // per-address-space entries.
+ assert(!cir::MissingFeatures::dataLayoutPtrHandlingBasedOnLangAS());
+ constexpr unsigned kBitsInByte = 8;
+ unsigned ptrSizeBits = dl.getPointerSizeInBits(/*AS=*/0);
+ unsigned ptrAlignBits =
+ dl.getPointerABIAlignment(/*AS=*/0).value() * kBitsInByte;
+ auto ptrKey = cir::PointerType::get(cir::VoidType::get(mlirContext));
+ auto ptrVal = mlir::DenseI32ArrayAttr::get(
+ mlirContext,
+ {static_cast<int32_t>(ptrSizeBits), static_cast<int32_t>(ptrAlignBits)});
+ llvm::SmallVector<mlir::DataLayoutEntryInterface> entries(
+ dlSpec.getEntries().begin(), dlSpec.getEntries().end());
+ entries.push_back(mlir::DataLayoutEntryAttr::get(ptrKey, ptrVal));
+
+ mod->setAttr(mlir::DLTIDialect::kDataLayoutAttrName,
+ mlir::DataLayoutSpecAttr::get(mlirContext, entries));
}
void CIRGenerator::Initialize(ASTContext &astContext) {
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 9c2a40e3681aa..04da6c85bbbb1 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -580,20 +580,62 @@ void RecordType::removeABIConversionNamePrefix() {
// Data Layout information for types
//===----------------------------------------------------------------------===//
+// A CIR-native pointer data-layout entry stores {size-in-bits,
+// abi-align-in-bits} as a dense i32 array keyed on a cir.ptr type (see
+// setMLIRDataLayout in CIRGenerator).
+namespace {
+constexpr static uint64_t kBitsInByte = 8;
+
+// Defaults used only when the module carries no cir.ptr data-layout entry
+// (e.g. CIR parsed from text without a data layout). These mirror the MLIR LLVM
+// dialect's pointer defaults.
+constexpr static uint64_t kDefaultPointerSizeBits = 64;
+constexpr static uint64_t kDefaultPointerAlignment = 8;
+
+enum class CIRPtrDLPos { Size = 0, AbiAlign = 1 };
+
+// Returns the requested field of the cir.ptr data-layout entry.
+std::optional<uint64_t> getPointerSpecValue(mlir::DataLayoutEntryListRef params,
+ CIRPtrDLPos pos) {
+ for (mlir::DataLayoutEntryInterface entry : params) {
+ if (!entry.isTypeEntry())
+ continue;
+ auto spec = mlir::dyn_cast<mlir::DenseI32ArrayAttr>(entry.getValue());
+ assert(spec && spec.size() == 2 &&
+ "malformed cir.ptr data layout entry: expected a pair of i32 "
+ "{size-in-bits, abi-align-in-bits}");
+ return static_cast<uint64_t>(spec[static_cast<int>(pos)]);
+ }
+ return std::nullopt;
+}
+} // namespace
+
llvm::TypeSize
PointerType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout,
::mlir::DataLayoutEntryListRef params) const {
+ // The pointer width comes from the CIR-native data-layout entry keyed on
+ // cir.ptr, which records the width for the default address space; fall back
+ // to 64 bits if the module carries no such entry.
// FIXME: improve this in face of address spaces
assert(!cir::MissingFeatures::dataLayoutPtrHandlingBasedOnLangAS());
- return llvm::TypeSize::getFixed(64);
+ if (std::optional<uint64_t> size =
+ getPointerSpecValue(params, CIRPtrDLPos::Size))
+ return llvm::TypeSize::getFixed(*size);
+ return llvm::TypeSize::getFixed(kDefaultPointerSizeBits);
}
uint64_t
PointerType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
::mlir::DataLayoutEntryListRef params) const {
+ // As with the size, the alignment is taken from the default-address-space
+ // cir.ptr data-layout entry. Address-space-dependent alignments are not yet
+ // modeled.
// FIXME: improve this in face of address spaces
assert(!cir::MissingFeatures::dataLayoutPtrHandlingBasedOnLangAS());
- return 8;
+ if (std::optional<uint64_t> align =
+ getPointerSpecValue(params, CIRPtrDLPos::AbiAlign))
+ return *align / kBitsInByte;
+ return kDefaultPointerAlignment;
}
llvm::TypeSize
@@ -1112,14 +1154,16 @@ DataMemberType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
llvm::TypeSize
VPtrType::getTypeSizeInBits(const mlir::DataLayout &dataLayout,
mlir::DataLayoutEntryListRef params) const {
- // FIXME: consider size differences under different ABIs
- return llvm::TypeSize::getFixed(64);
+ // The vtable pointer is an ordinary data pointer; route the query through a
+ // cir.ptr so it picks up the same data-layout-driven width.
+ return dataLayout.getTypeSizeInBits(
+ cir::PointerType::get(cir::VoidType::get(getContext())));
}
uint64_t VPtrType::getABIAlignment(const mlir::DataLayout &dataLayout,
mlir::DataLayoutEntryListRef params) const {
- // FIXME: consider alignment differences under different ABIs
- return 8;
+ return dataLayout.getTypeABIAlignment(
+ cir::PointerType::get(cir::VoidType::get(getContext())));
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index e0fc9e58ed4b7..00c94b37e1a6c 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3794,6 +3794,23 @@ void ConvertCIRToLLVMPass::runOnOperation() {
if (failed(applyPartialConversion(ops, target, std::move(patterns))))
signalPassFailure();
+ // The CIR-native pointer data-layout entry (keyed on cir.ptr) drives pointer
+ // widths during CIR codegen and lowering, but cir.ptr has no meaning once the
+ // module is translated to LLVM IR. Drop it so the resulting data layout only
+ // references LLVM 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())))
+ continue;
+ kept.push_back(entry);
+ }
+ module->setAttr(mlir::DLTIDialect::kDataLayoutAttrName,
+ mlir::DataLayoutSpecAttr::get(module.getContext(), kept));
+ }
+
// Emit the llvm.global_ctors array.
buildCtorDtorList(module, cir::CIRDialect::getGlobalCtorsAttrName(),
"llvm.global_ctors", [](mlir::Attribute attr) {
diff --git a/clang/test/CIR/CodeGen/pointer-width-32bit.cpp b/clang/test/CIR/CodeGen/pointer-width-32bit.cpp
new file mode 100644
index 0000000000000..15f3ec92d7e16
--- /dev/null
+++ b/clang/test/CIR/CodeGen/pointer-width-32bit.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -std=c++20 -triple nvptx-nvidia-cuda -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple nvptx-nvidia-cuda -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple nvptx-nvidia-cuda -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// On a target with 32-bit pointers (e.g. nvptx) both a data pointer (!cir.ptr)
+// and the vtable pointer (!cir.vptr) are 4 bytes wide. The pointer width is
+// carried by a CIR-native data-layout entry keyed on cir.ptr, so the field
+// following a pointer lands at the AST-mandated offset. Sizing pointers as a
+// hardcoded 64 bits previously tripped the record layout builder (insertPadding:
+// assertion `offset >= size`) on every record containing a pointer.
+
+struct S {
+ int *p;
+ int x;
+};
+
+S s;
+
+class A {
+public:
+ virtual void f();
+ int x;
+};
+
+void A::f() {}
+
+// The module carries a CIR-native pointer data-layout entry ({size, abi-align}
+// in bits) that drives both cir.ptr and cir.vptr widths. The 4-byte pointer is
+// immediately followed by 'x' at offset 4 with no padding, and each record is
+// 4-byte aligned.
+// CIR-DAG: !rec_S = !cir.struct<"S" {!cir.ptr<!s32i>, !s32i}>
+// CIR-DAG: !rec_A = !cir.struct<class "A" {!cir.vptr, !s32i}>
+// CIR-DAG: !cir.ptr<!cir.void> = array<i32: 32, 32>
+// CIR: cir.global external @s = #cir.zero : !rec_S {alignment = 4 : i64}
+// CIR: cir.global{{.*}}@_ZTV1A = #cir.vtable<{{.*}}{alignment = 4 : i64}
+
+// LLVM: @s = global %struct.S zeroinitializer, align 4
+// LLVM: @_ZTV1A = global { [3 x ptr] } {{.*}}, align 4
+
+// OGCG: @s = global %struct.S zeroinitializer, align 4
+// OGCG: @_ZTV1A = {{.*}}constant { [3 x ptr] } {{.*}}, align 4
>From bfec19785b8b6dd358c96d08c4e8c4c62a15f85b Mon Sep 17 00:00:00 2001
From: AkshayK <iit.akshay at gmail.com>
Date: Tue, 30 Jun 2026 12:39:21 -0400
Subject: [PATCH 2/2] [CIR][ARM] Add base 32-bit ARM (GenericARM) codegen and
lowering
Add the GenericARM transform-pass CXXABI dispatch (ARM method-pointer ABI), drive the size_t width of exception allocation and cir.copy memcpy from the data layout, and implement the NEON vget_lane/vgetq_lane intrinsics in CIRGenBuiltinAArch64.cpp.
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 4 +-
.../lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp | 38 ++++++++++++++++++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 4 ++
.../TargetLowering/LowerItaniumCXXABI.cpp | 8 ++++
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 26 +++++++++----
.../CodeGen/ARM/arm-aggregate-copy-size.cpp | 23 +++++++++++
.../CIR/CodeGen/ARM/arm-record-layout.cpp | 39 +++++++++++++++++++
.../CIR/CodeGen/ARM/arm-throw-alloc-size.cpp | 23 +++++++++++
.../CodeGenBuiltins/ARM/arm-neon-vget-lane.c | 27 +++++++++++++
9 files changed, 182 insertions(+), 10 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/ARM/arm-aggregate-copy-size.cpp
create mode 100644 clang/test/CIR/CodeGen/ARM/arm-record-layout.cpp
create mode 100644 clang/test/CIR/CodeGen/ARM/arm-throw-alloc-size.cpp
create mode 100644 clang/test/CIR/CodeGenBuiltins/ARM/arm-neon-vget-lane.c
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 3b8149411e7c4..5f6bbb9fe9170 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -2744,9 +2744,7 @@ emitTargetArchBuiltinExpr(CIRGenFunction *cgf, unsigned builtinID,
case llvm::Triple::armeb:
case llvm::Triple::thumb:
case llvm::Triple::thumbeb:
- // These are actually NYI, but that will be reported by emitBuiltinExpr.
- // At this point, we don't even know that the builtin is target-specific.
- return std::nullopt;
+ return cgf->emitARMBuiltinExpr(builtinID, e, returnValue, arch);
case llvm::Triple::aarch64:
case llvm::Triple::aarch64_32:
case llvm::Triple::aarch64_be:
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
index e3418d561df34..2432b534ce8a0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
@@ -1844,6 +1844,44 @@ static const std::pair<unsigned, unsigned> neonEquivalentIntrinsicMap[] = {
NEON::BI__builtin_neon_vstl1q_lane_s64},
};
+std::optional<mlir::Value>
+CIRGenFunction::emitARMBuiltinExpr(unsigned builtinID, const CallExpr *expr,
+ ReturnValueSlot returnValue,
+ llvm::Triple::ArchType arch) {
+ // Only the NEON lane-read intrinsics are implemented for 32-bit ARM so far;
+ // they lower to a vector element extraction, matching classic CodeGen
+ // (clang/lib/CodeGen/TargetBuiltins/ARM.cpp) and the AArch64 path. Every other
+ // ARM builtin is reported as not-yet-implemented rather than silently ignored.
+ switch (builtinID) {
+ case NEON::BI__builtin_neon_vget_lane_i8:
+ case NEON::BI__builtin_neon_vget_lane_i16:
+ case NEON::BI__builtin_neon_vget_lane_i32:
+ case NEON::BI__builtin_neon_vget_lane_i64:
+ case NEON::BI__builtin_neon_vget_lane_bf16:
+ case NEON::BI__builtin_neon_vget_lane_f32:
+ case NEON::BI__builtin_neon_vgetq_lane_i8:
+ case NEON::BI__builtin_neon_vgetq_lane_i16:
+ case NEON::BI__builtin_neon_vgetq_lane_i32:
+ case NEON::BI__builtin_neon_vgetq_lane_i64:
+ case NEON::BI__builtin_neon_vgetq_lane_bf16:
+ case NEON::BI__builtin_neon_vgetq_lane_f32:
+ case NEON::BI__builtin_neon_vduph_lane_bf16:
+ case NEON::BI__builtin_neon_vduph_laneq_bf16: {
+ mlir::Location loc = getLoc(expr->getExprLoc());
+ mlir::Value vec = emitScalarExpr(expr->getArg(0));
+ mlir::Value index = emitScalarExpr(expr->getArg(1));
+ return cir::VecExtractOp::create(builder, loc, vec, index);
+ }
+ default:
+ break;
+ }
+
+ cgm.errorNYI(expr->getSourceRange(),
+ std::string("unimplemented ARM builtin call: ") +
+ getContext().BuiltinInfo.getName(builtinID));
+ return mlir::Value{};
+}
+
std::optional<mlir::Value>
CIRGenFunction::emitAArch64BuiltinExpr(unsigned builtinID, const CallExpr *expr,
ReturnValueSlot returnValue,
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 322355fde3957..39455339fba29 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1542,6 +1542,10 @@ class CIRGenFunction : public CIRGenTypeCache {
emitAArch64BuiltinExpr(unsigned builtinID, const CallExpr *expr,
ReturnValueSlot returnValue,
llvm::Triple::ArchType arch);
+ std::optional<mlir::Value> emitARMBuiltinExpr(unsigned builtinID,
+ const CallExpr *expr,
+ ReturnValueSlot returnValue,
+ llvm::Triple::ArchType arch);
std::optional<mlir::Value> emitAArch64SMEBuiltinExpr(unsigned builtinID,
const CallExpr *expr);
std::optional<mlir::Value> emitAArch64SVEBuiltinExpr(unsigned builtinID,
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
index 5218d4979f353..f5a0f51f7ab86 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
@@ -144,6 +144,14 @@ std::unique_ptr<CIRCXXABI> createItaniumCXXABI(LowerModule &lm) {
/*useARMMethodPtrABI=*/true,
/*use32BitVTableOffsetABI=*/true);
+ case clang::TargetCXXABI::GenericARM:
+ // 32-bit ARM uses the ARM method-pointer encoding but, unlike AppleARM64,
+ // does not use 32-bit vtable offsets.
+ return std::make_unique<LowerItaniumCXXABI>(
+ lm,
+ /*useARMMethodPtrABI=*/true,
+ /*use32BitVTableOffsetABI=*/false);
+
case clang::TargetCXXABI::GenericItanium:
return std::make_unique<LowerItaniumCXXABI>(lm);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 00c94b37e1a6c..656240a26223f 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -212,9 +212,14 @@ mlir::LogicalResult CIRToLLVMCopyOpLowering::matchAndRewrite(
cir::CopyOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
+ // The llvm.memcpy length is size_t-wide; its width is target-dependent (e.g.
+ // 32 bits on 32-bit ARM), so drive it from the data layout rather than
+ // hardcoding i64.
+ auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+ mlir::Type lenTy =
+ rewriter.getIntegerType(layout.getTypeSizeInBits(llvmPtrTy));
const mlir::Value length = mlir::LLVM::ConstantOp::create(
- rewriter, op.getLoc(), rewriter.getI64Type(),
- op.getCopySizeInBytes(layout));
+ rewriter, op.getLoc(), lenTy, op.getCopySizeInBytes(layout));
assert(!cir::MissingFeatures::aggValueSlotVolatile());
rewriter.replaceOpWithNewOp<mlir::LLVM::MemcpyOp>(
op, adaptor.getDst(), adaptor.getSrc(), length, op.getIsVolatile());
@@ -3971,15 +3976,22 @@ mlir::LogicalResult CIRToLLVMThrowOpLowering::matchAndRewrite(
mlir::LogicalResult CIRToLLVMAllocExceptionOpLowering::matchAndRewrite(
cir::AllocExceptionOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
- // Get or create `declare ptr @__cxa_allocate_exception(i64)`
+ // Get or create `declare ptr @__cxa_allocate_exception(size_t)`. The
+ // thrown_size parameter is size_t, whose width is target-dependent (e.g. 32
+ // bits on 32-bit ARM), so drive it from the data layout rather than
+ // hardcoding i64; otherwise the call mismatches the runtime on 32-bit
+ // targets.
StringRef fnName = "__cxa_allocate_exception";
auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
- auto int64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
- auto fnTy = mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {int64Ty});
+ mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
+ auto sizeTTy = mlir::IntegerType::get(rewriter.getContext(),
+ layout.getTypeSizeInBits(llvmPtrTy));
+ auto fnTy = mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {sizeTTy});
createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
- auto exceptionSize = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
- adaptor.getSizeAttr());
+ auto exceptionSize = mlir::LLVM::ConstantOp::create(
+ rewriter, op.getLoc(), sizeTTy,
+ rewriter.getIntegerAttr(sizeTTy, op.getSize()));
auto allocaExceptionCall = mlir::LLVM::CallOp::create(
rewriter, op.getLoc(), mlir::TypeRange{llvmPtrTy}, fnName,
diff --git a/clang/test/CIR/CodeGen/ARM/arm-aggregate-copy-size.cpp b/clang/test/CIR/CodeGen/ARM/arm-aggregate-copy-size.cpp
new file mode 100644
index 0000000000000..0d687d85d06b3
--- /dev/null
+++ b/clang/test/CIR/CodeGen/ARM/arm-aggregate-copy-size.cpp
@@ -0,0 +1,23 @@
+// The length operand of the llvm.memcpy emitted for a cir.copy (e.g. passing an
+// aggregate by value) is size_t-wide, whose width is target-dependent. On
+// 32-bit ARM it must be i32, not the hardcoded i64 used by 64-bit targets.
+//
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=ARM --input-file=%t.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-x86.ll
+// RUN: FileCheck --check-prefix=X86 --input-file=%t-x86.ll %s
+
+struct P { int x; int y; };
+int sum(P p);
+int use() { P p; p.x = 1; p.y = 2; return sum(p); }
+
+// The aggregate pass-by-value lowers to a cir.copy; its memcpy length width is
+// resolved later, during lowering to LLVM, from the data layout.
+// CIR-LABEL: cir.func{{.*}} @_Z3usev()
+// CIR: cir.copy {{.*}} : !cir.ptr<!rec_P>
+
+// ARM: call void @llvm.memcpy.p0.p0.i32(ptr {{.*}}, ptr {{.*}}, i32 8, i1 false)
+
+// X86: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}, ptr {{.*}}, i64 8, i1 false)
diff --git a/clang/test/CIR/CodeGen/ARM/arm-record-layout.cpp b/clang/test/CIR/CodeGen/ARM/arm-record-layout.cpp
new file mode 100644
index 0000000000000..53a8d6024f0f5
--- /dev/null
+++ b/clang/test/CIR/CodeGen/ARM/arm-record-layout.cpp
@@ -0,0 +1,39 @@
+// 32-bit ARM lowers end-to-end through CIR, including the GenericARM CXXABI
+// path (virtual classes / vtables). A record containing a pointer lays out with
+// a 4-byte pointer followed by the next field with no padding, and both records
+// are 4-byte aligned.
+//
+//
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -emit-llvm %s -o %t-ogcg.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t-ogcg.ll %s
+
+struct S {
+ int *p;
+ int x;
+};
+
+S s;
+
+class A {
+public:
+ virtual void f();
+ int x;
+};
+
+void A::f() {}
+
+// CIR-DAG: !rec_S = !cir.struct<"S" {!cir.ptr<!s32i>, !s32i}>
+// CIR-DAG: !rec_A = !cir.struct<class "A" {!cir.vptr, !s32i}>
+// CIR-DAG: !cir.ptr<!cir.void> = array<i32: 32, 32>
+// CIR: cir.global external @s = #cir.zero : !rec_S {alignment = 4 : i64}
+// CIR: cir.global {{.*}}@_ZTV1A = #cir.vtable<{{.*}}{alignment = 4 : i64}
+
+// LLVM: @s = global %struct.S zeroinitializer, align 4
+// LLVM: @_ZTV1A = global { [3 x ptr] } {{.*}}, align 4
+
+// OGCG: @s = global %struct.S zeroinitializer, align 4
+// OGCG: @_ZTV1A = {{.*}}constant { [3 x ptr] } {{.*}}, align 4
diff --git a/clang/test/CIR/CodeGen/ARM/arm-throw-alloc-size.cpp b/clang/test/CIR/CodeGen/ARM/arm-throw-alloc-size.cpp
new file mode 100644
index 0000000000000..0624905590a1f
--- /dev/null
+++ b/clang/test/CIR/CodeGen/ARM/arm-throw-alloc-size.cpp
@@ -0,0 +1,23 @@
+// The thrown_size argument to __cxa_allocate_exception is size_t, whose width
+// is target-dependent. On 32-bit ARM it must be i32, not the hardcoded i64
+// used by 64-bit targets.
+//
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fcxx-exceptions -fexceptions -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fcxx-exceptions -fexceptions -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=ARM --input-file=%t.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -fclangir -emit-llvm %s -o %t-x86.ll
+// RUN: FileCheck --check-prefix=X86 --input-file=%t-x86.ll %s
+
+void f() { throw 42; }
+
+// The thrown size is carried as an attribute in CIR; its size_t width for the
+// __cxa_allocate_exception call is resolved later, during lowering to LLVM.
+// CIR-LABEL: cir.func{{.*}} @_Z1fv()
+// CIR: cir.alloc.exception 4
+
+// ARM: declare ptr @__cxa_allocate_exception(i32)
+// ARM: call ptr @__cxa_allocate_exception(i32 4)
+
+// X86: declare ptr @__cxa_allocate_exception(i64)
+// X86: call ptr @__cxa_allocate_exception(i64 4)
diff --git a/clang/test/CIR/CodeGenBuiltins/ARM/arm-neon-vget-lane.c b/clang/test/CIR/CodeGenBuiltins/ARM/arm-neon-vget-lane.c
new file mode 100644
index 0000000000000..f045ed753ecd5
--- /dev/null
+++ b/clang/test/CIR/CodeGenBuiltins/ARM/arm-neon-vget-lane.c
@@ -0,0 +1,27 @@
+// The NEON lane-read intrinsics (vget_lane/vgetq_lane) lower to __builtin_neon_*
+// builtins on 32-bit ARM (unlike AArch64, where arm_neon.h uses generic vector
+// subscripting). Make sure CIR codegen lowers them to a vector element
+// extraction instead of reporting them as unimplemented.
+
+// RUN: %clang_cc1 -triple armv7-unknown-linux-gnueabihf -target-feature +neon -ffreestanding -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple armv7-unknown-linux-gnueabihf -target-feature +neon -ffreestanding -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+
+#include <arm_neon.h>
+
+// CIR-LABEL: cir.func{{.*}} @get_s32(
+// CIR: cir.vec.extract {{.*}} : !cir.vector<4 x !s32i>
+// LLVM-LABEL: define dso_local i32 @get_s32(
+// LLVM: extractelement <4 x i32> %{{.*}}, i32 2
+int get_s32(int32x4_t v) { return vgetq_lane_s32(v, 2); }
+
+// CIR-LABEL: cir.func{{.*}} @get_f32(
+// CIR: cir.vec.extract {{.*}} : !cir.vector<4 x !cir.float>
+// LLVM-LABEL: define dso_local float @get_f32(
+// LLVM: extractelement <4 x float> %{{.*}}, i32 1
+float get_f32(float32x4_t v) { return vgetq_lane_f32(v, 1); }
+
+// CIR-LABEL: cir.func{{.*}} @get_s16(
+// CIR: cir.vec.extract {{.*}} : !cir.vector<4 x !s16i>
+// LLVM-LABEL: define dso_local i16 @get_s16(
+// LLVM: extractelement <4 x i16> %{{.*}}, i32 3
+short get_s16(int16x4_t v) { return vget_lane_s16(v, 3); }
More information about the cfe-commits
mailing list