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

Reply via email to