https://github.com/skc7 created https://github.com/llvm/llvm-project/pull/229792
Summary: - `call-conv-lowering` coercion and flattening temporaries were always created in default AS which is invalid for targets with non zero alloca AS. - This PR creates these allocas in data layout AS. - Added test to check callee, call-site and return coercion, the flattening slot and Expand member pointers. Assisted by : claude opus 5.5 >From 0f069ae79d839b3f5b5869bdda69c210ff544693 Mon Sep 17 00:00:00 2001 From: skc7 <[email protected]> Date: Wed, 7 Oct 2026 19:53:23 +0530 Subject: [PATCH] [CIR] Use alloca address space for call-conv lowering temporaries --- .../TargetLowering/CIRABIRewriteContext.cpp | 31 ++++- .../abi-lowering/coerce-alloca-addrspace.cir | 128 ++++++++++++++++++ 2 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 clang/test/CIR/Transforms/abi-lowering/coerce-alloca-addrspace.cir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp index f79a8535355a8..1f69ae89d4766 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp @@ -336,6 +336,18 @@ emitViewAtOffset(mlir::OpBuilder &builder, mlir::Location loc, return typedView; } +/// A pointer to \p pointee in the target's alloca address space, which every +/// temporary this pass allocates has to live in. +static cir::PointerType getAllocaPtrTy(mlir::Type pointee, + const mlir::DataLayout &dl) { + mlir::ptr::MemorySpaceAttrInterface addrSpace; + if (auto asAttr = mlir::dyn_cast_if_present<mlir::IntegerAttr>( + dl.getAllocaMemorySpace())) + addrSpace = cir::TargetAddressSpaceAttr::get(pointee.getContext(), + asAttr.getUInt()); + return cir::PointerType::get(pointee, addrSpace); +} + /// A coercion slot for \p srcTy and \p dstTy, which must differ. It is typed /// as \p srcTy unless coercionByteSize says \p dstTy is larger, aligned for /// both, and placed at the start of \p slotBlock. \p offset, where the @@ -369,9 +381,9 @@ static cir::AllocaOp createCoercionSlot( mlir::OpBuilder::InsertionGuard guard(builder); builder.setInsertionPointToStart(slotBlock); - auto alloca = cir::AllocaOp::create( - builder, loc, cir::PointerType::get(slotTy), - builder.getStringAttr("coerce"), builder.getI64IntegerAttr(allocaAlign)); + auto alloca = cir::AllocaOp::create(builder, loc, getAllocaPtrTy(slotTy, dl), + builder.getStringAttr("coerce"), + builder.getI64IntegerAttr(allocaAlign)); createdOps.insert(alloca); return alloca; } @@ -848,7 +860,9 @@ void insertArgCoercion( entry.insertArgument(blockArgIdx + f, fieldTy, loc); if (!destAlloca) continue; - mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy); + mlir::Type fieldPtrTy = cir::PointerType::get( + fieldTy, + mlir::cast<cir::PointerType>(destAlloca.getType()).getAddrSpace()); auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy, destAlloca, /*name=*/"", /*index=*/f); @@ -881,14 +895,15 @@ void insertArgCoercion( // setInsertionPointToStart: see comment in the Expand arm above. builder.setInsertionPointToStart(&entry); - auto flatPtrTy = cir::PointerType::get(flatTy); + cir::PointerType flatPtrTy = getAllocaPtrTy(flatTy, dl); uint64_t flatAlign = dl.getTypeABIAlignment(flatTy); auto flatSlot = cir::AllocaOp::create( builder, loc, flatPtrTy, builder.getStringAttr("coerce"), builder.getI64IntegerAttr(flatAlign)); SmallPtrSet<Operation *, 8> flattenOps = {flatSlot}; for (auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) { - Type fieldPtrTy = cir::PointerType::get(fieldTy); + Type fieldPtrTy = + cir::PointerType::get(fieldTy, flatPtrTy.getAddrSpace()); auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy, flatSlot, /*name=*/"", /*index=*/f); @@ -1738,8 +1753,10 @@ CIRABIRewriteContext::rewriteCallSite(mlir::Operation *callOp, mlir::Value coercedPtr = emitCoercionToMemory(builder, call.getLoc(), flatTy, arg, slotBlock, dl, coercionOps, /*offset=*/0); + auto coercedPtrTy = mlir::cast<cir::PointerType>(coercedPtr.getType()); for (auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) { - mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy); + mlir::Type fieldPtrTy = + cir::PointerType::get(fieldTy, coercedPtrTy.getAddrSpace()); auto fieldPtr = cir::GetMemberOp::create(builder, call.getLoc(), fieldPtrTy, coercedPtr, /*name=*/"", /*index=*/f); diff --git a/clang/test/CIR/Transforms/abi-lowering/coerce-alloca-addrspace.cir b/clang/test/CIR/Transforms/abi-lowering/coerce-alloca-addrspace.cir new file mode 100644 index 0000000000000..f4ad7d8bc9e0a --- /dev/null +++ b/clang/test/CIR/Transforms/abi-lowering/coerce-alloca-addrspace.cir @@ -0,0 +1,128 @@ +// RUN: cir-opt %s -cir-call-conv-lowering="classification-attr=test_classify" \ +// RUN: | FileCheck %s + +// Allocas the pass creates use the data layout's alloca address space, +// and pointers derived from them keep that address space. + +!s32i = !cir.int<s, 32> +!s64i = !cir.int<s, 64> +!rec_Pair = !cir.struct<"Pair" {data !s32i, data !s32i}> +!rec_TwoLong = !cir.struct<"TwoLong" {data !s64i, data !s64i}> +!rec_Dim3 = !cir.struct<"Dim3" {data !s32i, data !s32i, data !s32i}> +!rec_coerced_dim3 = !cir.struct<{data !s64i, data !s32i}> + +#coerce_pair_to_i64 = { + return = { kind = "direct" }, + args = [ { kind = "direct", coerced_type = !s64i } ] +} + +#ret_pair_as_i64 = { + return = { kind = "direct", coerced_type = !s64i }, + args = [ ] +} + +#flatten_dim3 = { + return = { kind = "direct" }, + args = [ { kind = "direct", coerced_type = !rec_coerced_dim3, + can_flatten = true } ] +} + +#expand_two = { + return = { kind = "direct" }, + args = [ { kind = "expand" } ] +} + +#passthrough = { + return = { kind = "direct" }, + args = [ ] +} + +module attributes { + dlti.dl_spec = #dlti.dl_spec< + #dlti.dl_entry<i32, dense<32>: vector<2xi64>>, + #dlti.dl_entry<i64, dense<64>: vector<2xi64>>, + #dlti.dl_entry<"dlti.alloca_memory_space", 5 : ui64>> +} { + +cir.func @takes_pair(%arg0: !rec_Pair) + attributes { test_classify = #coerce_pair_to_i64 } { + %0 = cir.alloca "p" align(4) : !cir.ptr<!rec_Pair, target_address_space(5)> + cir.store %arg0, %0 : !rec_Pair, !cir.ptr<!rec_Pair, target_address_space(5)> + cir.return +} + +// CHECK: cir.func{{.*}} @takes_pair(%[[ARG:.*]]: !s64i) +// CHECK: %[[SLOT:.*]] = cir.alloca "coerce" {{.*}} : !cir.ptr<!s64i, target_address_space(5)> +// CHECK: cir.store %[[ARG]], %[[SLOT]] : !s64i, !cir.ptr<!s64i, target_address_space(5)> +// CHECK: cir.cast bitcast %[[SLOT]] : !cir.ptr<!s64i, target_address_space(5)> -> !cir.ptr<!rec_Pair, target_address_space(5)> + +cir.func @call_pair() attributes { test_classify = #passthrough } { + %0 = cir.const #cir.zero : !rec_Pair + cir.call @takes_pair(%0) : (!rec_Pair) -> () + cir.return +} + +// CHECK: cir.func{{.*}} @call_pair() +// CHECK: %[[SLOT:.*]] = cir.alloca "coerce" {{.*}} : !cir.ptr<!rec_Pair, target_address_space(5)> +// CHECK: %[[CAST:.*]] = cir.cast bitcast %[[SLOT]] : !cir.ptr<!rec_Pair, target_address_space(5)> -> !cir.ptr<!s64i, target_address_space(5)> +// CHECK: %[[INT:.*]] = cir.load %[[CAST]] : !cir.ptr<!s64i, target_address_space(5)>, !s64i +// CHECK: cir.call @takes_pair(%[[INT]]) : (!s64i) -> () + +cir.func @ret_pair() -> !rec_Pair + attributes { test_classify = #ret_pair_as_i64 } { + %0 = cir.const #cir.zero : !rec_Pair + cir.return %0 : !rec_Pair +} + +// CHECK: cir.func{{.*}} @ret_pair() -> !s64i +// CHECK: %[[SLOT:.*]] = cir.alloca "coerce" {{.*}} : !cir.ptr<!rec_Pair, target_address_space(5)> +// CHECK: %[[CAST:.*]] = cir.cast bitcast %[[SLOT]] : !cir.ptr<!rec_Pair, target_address_space(5)> -> !cir.ptr<!s64i, target_address_space(5)> +// CHECK: %[[INT:.*]] = cir.load %[[CAST]] : !cir.ptr<!s64i, target_address_space(5)>, !s64i +// CHECK: cir.return %[[INT]] : !s64i + +// The flattened fields go through a slot of the coerced struct, and their +// member pointers keep its address space. +cir.func @takes_dim3(%arg0: !rec_Dim3) + attributes { test_classify = #flatten_dim3 } { + cir.return +} + +// CHECK: cir.func{{.*}} @takes_dim3(%[[D0:.*]]: !s64i, %[[D1:.*]]: !s32i) +// CHECK: cir.alloca "coerce" {{.*}} : !cir.ptr<{{.*}}, target_address_space(5)> +// CHECK: %[[FSLOT:.*]] = cir.alloca "coerce" {{.*}} : !cir.ptr<{{.*}}, target_address_space(5)> +// CHECK: %[[P0:.*]] = cir.get_member %[[FSLOT]][0] {{.*}} -> !cir.ptr<!s64i, target_address_space(5)> +// CHECK: cir.store %[[D0]], %[[P0]] : !s64i, !cir.ptr<!s64i, target_address_space(5)> +// CHECK: %[[P1:.*]] = cir.get_member %[[FSLOT]][1] {{.*}} -> !cir.ptr<!s32i, target_address_space(5)> +// CHECK: cir.store %[[D1]], %[[P1]] : !s32i, !cir.ptr<!s32i, target_address_space(5)> + +cir.func @call_dim3() attributes { test_classify = #passthrough } { + %0 = cir.const #cir.zero : !rec_Dim3 + cir.call @takes_dim3(%0) : (!rec_Dim3) -> () + cir.return +} + +// CHECK: cir.func{{.*}} @call_dim3() +// CHECK: %[[SLOT:.*]] = cir.alloca "coerce" {{.*}} : !cir.ptr<{{.*}}, target_address_space(5)> +// CHECK: %[[P0:.*]] = cir.get_member %[[SLOT]][0] {{.*}} -> !cir.ptr<!s64i, target_address_space(5)> +// CHECK: %[[E0:.*]] = cir.load %[[P0]] : !cir.ptr<!s64i, target_address_space(5)>, !s64i +// CHECK: %[[P1:.*]] = cir.get_member %[[SLOT]][1] {{.*}} -> !cir.ptr<!s32i, target_address_space(5)> +// CHECK: %[[E1:.*]] = cir.load %[[P1]] : !cir.ptr<!s32i, target_address_space(5)>, !s32i +// CHECK: cir.call @takes_dim3(%[[E0]], %[[E1]]) : (!s64i, !s32i) -> () + +// Expanded fields are stored straight into the parameter's own slot, so their +// member pointers take that slot's address space. +cir.func @takes_two_long(%arg0: !rec_TwoLong) + attributes { test_classify = #expand_two } { + %0 = cir.alloca "p" align(8) init : !cir.ptr<!rec_TwoLong, target_address_space(5)> + cir.store %arg0, %0 : !rec_TwoLong, !cir.ptr<!rec_TwoLong, target_address_space(5)> + cir.return +} + +// CHECK: cir.func{{.*}} @takes_two_long(%[[F0:.*]]: !s64i, %[[F1:.*]]: !s64i) +// CHECK: %[[P:.*]] = cir.alloca "p" {{.*}} : !cir.ptr<!rec_TwoLong, target_address_space(5)> +// CHECK: %[[P0:.*]] = cir.get_member %[[P]][0] {{.*}} -> !cir.ptr<!s64i, target_address_space(5)> +// CHECK: cir.store %[[F0]], %[[P0]] : !s64i, !cir.ptr<!s64i, target_address_space(5)> +// CHECK: %[[P1:.*]] = cir.get_member %[[P]][1] {{.*}} -> !cir.ptr<!s64i, target_address_space(5)> +// CHECK: cir.store %[[F1]], %[[P1]] : !s64i, !cir.ptr<!s64i, target_address_space(5)> + +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
