[clang] [CIR] Make array decay and get_element op perserve address spaces (PR #192361)

Jan Leyonberg via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 15 17:12:17 PDT 2026


https://github.com/jsjodin created https://github.com/llvm/llvm-project/pull/192361

This patch makes sure that the maybeBuildArrayDecay function takes address spaces into account and  makes the get_element op preserve the address space of the base pointer.

Note: In the test case when directly accessing the array the LLVM codegen case preserves addr_space(1), but in OGCG it does not, since it inserts the addrspace cast earlier. This should not be an an issue, I think doing the cast early on made the code generation easier.

Assisted-by: Cursor / claude-4.6-opus-high


>From 649e945bd0393d89299eed29a0c7b9c762f271cc Mon Sep 17 00:00:00 2001
From: Jan Leyonberg <jan_sjodin at yahoo.com>
Date: Wed, 15 Apr 2026 19:48:09 -0400
Subject: [PATCH] [CIR] Make array decay and get_element op perserve address
 spaces

This patch makes sure that the maybeBuildArrayDecay functions takes
address spaces into account, and also makes the get_element op preserve
the address space of the base pointer.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 11 +++-
 clang/lib/CIR/CodeGen/CIRGenBuilder.cpp       |  3 +-
 .../CIR/CodeGen/amdgpu-array-addrspace.cpp    | 50 +++++++++++++++++++
 3 files changed, 61 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/amdgpu-array-addrspace.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 6f8db65acccc9..f20ba262d6480 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3547,8 +3547,9 @@ def CIR_InsertMemberOp : CIR_Op<"insert_member", [
 def CIR_GetElementOp : CIR_Op<"get_element", [
   TypesMatchWith<
       "type of 'result' matches element type of 'base'", "base", "result",
-      "cir::PointerType::get(mlir::cast<cir::ArrayType>(mlir::cast<cir::"
-      "PointerType>($_self).getPointee()).getElementType())">
+      "cir::PointerType::get("
+      "mlir::cast<cir::ArrayType>(mlir::cast<cir::PointerType>($_self).getPointee()).getElementType(), "
+      "mlir::cast<cir::PointerType>($_self).getAddrSpace())">
 ]> {
   let summary = "Get the address of an array element";
 
@@ -3557,6 +3558,7 @@ def CIR_GetElementOp : CIR_Op<"get_element", [
     from the `base` array.
 
     It expects a pointer to the `base` array and the `index` of the element.
+    The result pointer preserves the address space of the base pointer.
 
     Example:
     ```
@@ -3571,6 +3573,11 @@ def CIR_GetElementOp : CIR_Op<"get_element", [
     %i = ...
     %elem_i = cir.get_element %0[%i : !s32i] : !cir.ptr<!array_ty> -> !cir.ptr<!s32i>
 
+    // With address space (e.g., GPU private memory):
+    %elem_gpu = cir.get_element %gpu_arr[%i : !s32i] :
+      !cir.ptr<!cir.array<!s32i x 10>, target_address_space(5)> ->
+      !cir.ptr<!s32i, target_address_space(5)>
+
     ```
   }];
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
index 7953a648715b2..0cf35812babe7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
@@ -21,7 +21,8 @@ mlir::Value CIRGenBuilderTy::maybeBuildArrayDecay(mlir::Location loc,
   const auto arrayTy = mlir::dyn_cast<cir::ArrayType>(arrayPtrTy.getPointee());
 
   if (arrayTy) {
-    const cir::PointerType flatPtrTy = getPointerTo(arrayTy.getElementType());
+    const cir::PointerType flatPtrTy =
+        getPointerTo(arrayTy.getElementType(), arrayPtrTy.getAddrSpace());
     return cir::CastOp::create(*this, loc, flatPtrTy,
                                cir::CastKind::array_to_ptrdecay, arrayPtr);
   }
diff --git a/clang/test/CIR/CodeGen/amdgpu-array-addrspace.cpp b/clang/test/CIR/CodeGen/amdgpu-array-addrspace.cpp
new file mode 100644
index 0000000000000..38b574bd120b5
--- /dev/null
+++ b/clang/test/CIR/CodeGen/amdgpu-array-addrspace.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+// Test that address spaces are preserved through array-to-pointer decay
+// and array element access on AMDGPU, where globals are in AS 1.
+
+int globalArr[10] = {0};
+
+void takes_ptr(int *p);
+
+// The array_to_ptrdecay cast must preserve the address space of the base
+// pointer, followed by an address_space cast.
+
+// CIR-LABEL: cir.func{{.*}} @_Z17pass_global_arrayv()
+// CIR:         %[[ARR:.*]] = cir.get_global @globalArr : !cir.ptr<!cir.array<!s32i x 10>, target_address_space(1)>
+// CIR-NEXT:    %[[DECAY:.*]] = cir.cast array_to_ptrdecay %[[ARR]] : !cir.ptr<!cir.array<!s32i x 10>, target_address_space(1)> -> !cir.ptr<!s32i, target_address_space(1)>
+// CIR-NEXT:    %[[FLAT:.*]] = cir.cast address_space %[[DECAY]] : !cir.ptr<!s32i, target_address_space(1)> -> !cir.ptr<!s32i>
+// CIR-NEXT:    cir.call @_Z9takes_ptrPi(%[[FLAT]])
+
+// LLVM-LABEL: define{{.*}} void @_Z17pass_global_arrayv()
+// LLVM:         call void @_Z9takes_ptrPi(ptr noundef addrspacecast (ptr addrspace(1) @globalArr to ptr))
+
+// OGCG-LABEL: define{{.*}} void @_Z17pass_global_arrayv()
+// OGCG:         call void @_Z9takes_ptrPi(ptr noundef addrspacecast (ptr addrspace(1) @globalArr to ptr))
+void pass_global_array() {
+  takes_ptr(globalArr);
+}
+
+// The get_element op must preserve the address space of the base pointer
+// so that the subsequent load uses the correct address space.
+
+// CIR-LABEL: cir.func{{.*}} @_Z18index_global_arrayi
+// CIR:         %[[ARR:.*]] = cir.get_global @globalArr : !cir.ptr<!cir.array<!s32i x 10>, target_address_space(1)>
+// CIR-NEXT:    %[[ELEM:.*]] = cir.get_element %[[ARR]][%{{.*}} : !s32i] : !cir.ptr<!cir.array<!s32i x 10>, target_address_space(1)> -> !cir.ptr<!s32i, target_address_space(1)>
+// CIR-NEXT:    %{{.*}} = cir.load align(4) %[[ELEM]] : !cir.ptr<!s32i, target_address_space(1)>, !s32i
+
+// LLVM-LABEL: define{{.*}} i32 @_Z18index_global_arrayi
+// LLVM:         %[[GEP:.*]] = getelementptr [10 x i32], ptr addrspace(1) @globalArr, i32 0, i64 %{{.*}}
+// LLVM-NEXT:    %{{.*}} = load i32, ptr addrspace(1) %[[GEP]], align 4
+
+// OGCG-LABEL: define{{.*}} i32 @_Z18index_global_arrayi
+// OGCG:         getelementptr inbounds [10 x i32], ptr addrspacecast (ptr addrspace(1) @globalArr to ptr)
+// OGCG:         load i32, ptr %{{.*}}, align 4
+int index_global_array(int i) {
+  return globalArr[i];
+}



More information about the cfe-commits mailing list