[flang-commits] [flang] [flang][cuda] Flatten memref descriptors in GPU kernel argument packing (PR #193651)

via flang-commits flang-commits at lists.llvm.org
Wed Apr 22 20:00:30 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: Zhen Wang (wangzpgi)

<details>
<summary>Changes</summary>

createKernelArgArray in CUFGPUToLLVMConversion packed one pointer per operand, treating a memref argument as a single value. But gpu-to-nvvm expands each memref into 3 + 2*rank scalar parameters, so the host-side kernelParams did not match the device kernel signature and cudaLaunchKernel failed with cudaErrorInvalidValue.

Host-side packing now unpacks each memref descriptor into its scalar fields (allocatedPtr, alignedPtr, offset, sizes..., strides...), matching the NVVM ABI. Non-memref operands pass through unchanged.

---
Full diff: https://github.com/llvm/llvm-project/pull/193651.diff


2 Files Affected:

- (modified) flang/lib/Optimizer/Transforms/CUDA/CUFGPUToLLVMConversion.cpp (+52-7) 
- (modified) flang/test/Fir/CUDA/cuda-gpu-launch-func.mlir (+143) 


``````````diff
diff --git a/flang/lib/Optimizer/Transforms/CUDA/CUFGPUToLLVMConversion.cpp b/flang/lib/Optimizer/Transforms/CUDA/CUFGPUToLLVMConversion.cpp
index 3d29454d1403a..4c0717cb16e39 100644
--- a/flang/lib/Optimizer/Transforms/CUDA/CUFGPUToLLVMConversion.cpp
+++ b/flang/lib/Optimizer/Transforms/CUDA/CUFGPUToLLVMConversion.cpp
@@ -33,16 +33,61 @@ using namespace Fortran::runtime;
 
 namespace {
 
+// Flatten a memref descriptor value into its individual scalar fields using
+// the same ordering as MLIR's standard memref-to-LLVM lowering:
+//   allocatedPtr, alignedPtr, offset, sizes[0..rank-1], strides[0..rank-1].
+// This must match the device-side ABI produced by the gpu-to-nvvm lowering,
+// which expands each memref argument into (3 + 2*rank) scalar parameters.
+static void flattenMemRefDescriptor(mlir::Location loc, mlir::Value desc,
+                                    int64_t rank,
+                                    mlir::PatternRewriter &rewriter,
+                                    llvm::SmallVectorImpl<mlir::Value> &out) {
+  assert(mlir::isa<mlir::LLVM::LLVMStructType>(desc.getType()) &&
+         "expected adapted memref operand to be an LLVM descriptor struct");
+  out.push_back(mlir::LLVM::ExtractValueOp::create(rewriter, loc, desc, 0));
+  out.push_back(mlir::LLVM::ExtractValueOp::create(rewriter, loc, desc, 1));
+  out.push_back(mlir::LLVM::ExtractValueOp::create(rewriter, loc, desc, 2));
+  for (int64_t d = 0; d < rank; ++d)
+    out.push_back(mlir::LLVM::ExtractValueOp::create(
+        rewriter, loc, desc, mlir::ArrayRef<int64_t>{3, d}));
+  for (int64_t d = 0; d < rank; ++d)
+    out.push_back(mlir::LLVM::ExtractValueOp::create(
+        rewriter, loc, desc, mlir::ArrayRef<int64_t>{4, d}));
+}
+
+// Build the kernel argument array used for the CUDA kernel launch.
+//
+// For each operand:
+//   - memref operands are flattened into their descriptor scalar fields so the
+//     host-side parameter list matches the NVVM-lowered device kernel signature
+//     (gpu-to-nvvm expands each memref into 3 + 2*rank scalar parameters);
+//   - all other operands are passed through unchanged.
+//
+// The flattened values are materialized on the stack in a single struct
+// (preserving argument order), and a companion pointer array is populated with
+// the address of each field. That pointer array is what the CUDA launch
+// interface expects as kernelParams.
 static mlir::Value createKernelArgArray(mlir::Location loc,
-                                        mlir::ValueRange operands,
+                                        mlir::ValueRange origOperands,
+                                        mlir::ValueRange adaptedOperands,
                                         mlir::PatternRewriter &rewriter) {
 
   auto *ctx = rewriter.getContext();
-  llvm::SmallVector<mlir::Type> structTypes(operands.size(), nullptr);
 
-  for (auto [i, arg] : llvm::enumerate(operands))
-    structTypes[i] = arg.getType();
+  llvm::SmallVector<mlir::Value, 8> flatValues;
+  flatValues.reserve(adaptedOperands.size());
+  for (auto [origArg, adaptedArg] :
+       llvm::zip_equal(origOperands, adaptedOperands)) {
+    if (auto memrefTy = mlir::dyn_cast<mlir::MemRefType>(origArg.getType())) {
+      flattenMemRefDescriptor(loc, adaptedArg, memrefTy.getRank(), rewriter,
+                              flatValues);
+      continue;
+    }
+    flatValues.push_back(adaptedArg);
+  }
 
+  auto structTypes = llvm::map_to_vector(
+      flatValues, [](mlir::Value v) { return v.getType(); });
   auto structTy = mlir::LLVM::LLVMStructType::getLiteral(ctx, structTypes);
   auto ptrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
   mlir::Type i32Ty = rewriter.getI32Type();
@@ -57,7 +102,7 @@ static mlir::Value createKernelArgArray(mlir::Location loc,
   mlir::Value argArray =
       mlir::LLVM::AllocaOp::create(rewriter, loc, ptrTy, ptrTy, size);
 
-  for (auto [i, arg] : llvm::enumerate(operands)) {
+  for (auto [i, arg] : llvm::enumerate(flatValues)) {
     auto indice = mlir::LLVM::ConstantOp::create(
         rewriter, loc, i32Ty, rewriter.getIntegerAttr(i32Ty, i));
     mlir::Value structMember =
@@ -98,8 +143,8 @@ struct GPULaunchKernelConversion
       dynamicMemorySize = mlir::LLVM::ConstantOp::create(
           rewriter, loc, i32Ty, rewriter.getIntegerAttr(i32Ty, 0));
 
-    mlir::Value kernelArgs =
-        createKernelArgArray(loc, adaptor.getKernelOperands(), rewriter);
+    mlir::Value kernelArgs = createKernelArgArray(
+        loc, op.getKernelOperands(), adaptor.getKernelOperands(), rewriter);
 
     auto ptrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
     auto kernel = mod.lookupSymbol<mlir::LLVM::LLVMFuncOp>(op.getKernelName());
diff --git a/flang/test/Fir/CUDA/cuda-gpu-launch-func.mlir b/flang/test/Fir/CUDA/cuda-gpu-launch-func.mlir
index 5d0c4438cbda4..0a643b399d8be 100644
--- a/flang/test/Fir/CUDA/cuda-gpu-launch-func.mlir
+++ b/flang/test/Fir/CUDA/cuda-gpu-launch-func.mlir
@@ -253,3 +253,146 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<!llvm.ptr<272>, d
 // CHECK-LABEL: llvm.func @_QQmain()
 // CHECK: %[[STREAM:.*]] = llvm.alloca %{{.*}} x i64 : (i64) -> !llvm.ptr
 // CHECK: llvm.call @_FortranACUFLaunchKernel(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[STREAM]], %{{.*}}, %{{.*}}, %{{.*}})
+
+// -----
+
+// Verify that a memref kernel operand is unpacked into its descriptor fields
+// (allocatedPtr, alignedPtr, offset, sizes[...], strides[...]) so that the
+// host-side kernelParams match the NVVM-lowered device kernel signature.
+module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<i1, dense<8> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr, dense<64> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<270>, dense<32> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<271>, dense<32> : vector<4xi64>>, #dlti.dl_entry<i8, dense<8> : vector<2xi64>>, #dlti.dl_entry<i16, dense<16> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr<272>, dense<64> : vector<4xi64>>, #dlti.dl_entry<i64, dense<64> : vector<2xi64>>, #dlti.dl_entry<i32, dense<32> : vector<2xi64>>, #dlti.dl_entry<f128, dense<128> : vector<2xi64>>, #dlti.dl_entry<i128, dense<128> : vector<2xi64>>, #dlti.dl_entry<f64, dense<64> : vector<2xi64>>, #dlti.dl_entry<f80, dense<128> : vector<2xi64>>, #dlti.dl_entry<f16, dense<16> : vector<2xi64>>, #dlti.dl_entry<"dlti.endianness", "little">, #dlti.dl_entry<"dlti.stack_alignment", 128 : i64>>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", gpu.container_module, llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", llvm.target_triple = "x86_64-unknown-linux-gnu"} {
+  llvm.func @_QMmod1Phost_sub() {
+    %c1_i64 = llvm.mlir.constant(1 : i64) : i64
+    %c0_i64 = llvm.mlir.constant(0 : i64) : i64
+    %c10000_i64 = llvm.mlir.constant(10000 : i64) : i64
+    %c128_i64 = llvm.mlir.constant(128 : i64) : i64
+    %c0_i32 = llvm.mlir.constant(0 : i32) : i32
+    %buf = llvm.alloca %c10000_i64 x f32 : (i64) -> !llvm.ptr
+    %u0 = llvm.mlir.undef : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %u1 = llvm.insertvalue %buf, %u0[0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %u2 = llvm.insertvalue %buf, %u1[1] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %u3 = llvm.insertvalue %c0_i64, %u2[2] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %u4 = llvm.insertvalue %c10000_i64, %u3[3, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %desc = llvm.insertvalue %c1_i64, %u4[4, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %memref = builtin.unrealized_conversion_cast %desc : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)> to memref<10000xf32>
+    gpu.launch_func @cuda_device_mod::@_QMmod1Psub1 blocks in (%c1_i64, %c1_i64, %c1_i64) threads in (%c128_i64, %c1_i64, %c1_i64) : i64 dynamic_shared_memory_size %c0_i32 args(%memref : memref<10000xf32>) {cuf.proc_attr = #cuf.cuda_proc<global>}
+    llvm.return
+  }
+  llvm.func @_QMmod1Psub1(!llvm.ptr, !llvm.ptr, i64, i64, i64) -> ()
+  gpu.binary @cuda_device_mod  [#gpu.object<#nvvm.target, "">]
+}
+
+// CHECK-LABEL: llvm.func @_QMmod1Phost_sub()
+// Extract each descriptor field from the memref descriptor value in order:
+// allocatedPtr (0), alignedPtr (1), offset (2), sizes[0] (3,0), strides[0] (4,0).
+// CHECK: %[[ALLOC:.*]] = llvm.extractvalue %{{.*}}[0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+// CHECK: %[[ALIGN:.*]] = llvm.extractvalue %{{.*}}[1] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+// CHECK: %[[OFF:.*]] = llvm.extractvalue %{{.*}}[2] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+// CHECK: %[[SZ0:.*]] = llvm.extractvalue %{{.*}}[3, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+// CHECK: %[[ST0:.*]] = llvm.extractvalue %{{.*}}[4, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+// The per-arg storage struct must contain the 5 unpacked memref descriptor
+// fields (ptr, ptr, i64, i64, i64), not a single struct wrapper.
+// CHECK: %[[ARGSTRUCT:.*]] = llvm.alloca %{{.*}} x !llvm.struct<(ptr, ptr, i64, i64, i64)> : (i32) -> !llvm.ptr
+// The param array must hold exactly 5 pointers, matching the 5 flattened fields.
+// CHECK: %[[FIVE:.*]] = llvm.mlir.constant(5 : i32) : i32
+// CHECK: %[[PARAMS:.*]] = llvm.alloca %[[FIVE]] x !llvm.ptr : (i32) -> !llvm.ptr
+// Each extracted field is stored into the corresponding struct member, and the
+// member's address is stored into the param array in the same order.
+// CHECK: %[[S0:.*]] = llvm.getelementptr %[[ARGSTRUCT]][%{{.*}}, 0]
+// CHECK: llvm.store %[[ALLOC]], %[[S0]]
+// CHECK: %[[S1:.*]] = llvm.getelementptr %[[ARGSTRUCT]][%{{.*}}, 1]
+// CHECK: llvm.store %[[ALIGN]], %[[S1]]
+// CHECK: %[[S2:.*]] = llvm.getelementptr %[[ARGSTRUCT]][%{{.*}}, 2]
+// CHECK: llvm.store %[[OFF]], %[[S2]]
+// CHECK: %[[S3:.*]] = llvm.getelementptr %[[ARGSTRUCT]][%{{.*}}, 3]
+// CHECK: llvm.store %[[SZ0]], %[[S3]]
+// CHECK: %[[S4:.*]] = llvm.getelementptr %[[ARGSTRUCT]][%{{.*}}, 4]
+// CHECK: llvm.store %[[ST0]], %[[S4]]
+// CHECK: llvm.call @_FortranACUFLaunchKernel(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[PARAMS]], %{{.*}})
+
+// -----
+
+// Rank-2 memref: verifies per-dimension loop flattens
+// sizes[0], sizes[1], strides[0], strides[1] in order.
+module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<i1, dense<8> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr, dense<64> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<270>, dense<32> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<271>, dense<32> : vector<4xi64>>, #dlti.dl_entry<i8, dense<8> : vector<2xi64>>, #dlti.dl_entry<i16, dense<16> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr<272>, dense<64> : vector<4xi64>>, #dlti.dl_entry<i64, dense<64> : vector<2xi64>>, #dlti.dl_entry<i32, dense<32> : vector<2xi64>>, #dlti.dl_entry<f128, dense<128> : vector<2xi64>>, #dlti.dl_entry<i128, dense<128> : vector<2xi64>>, #dlti.dl_entry<f64, dense<64> : vector<2xi64>>, #dlti.dl_entry<f80, dense<128> : vector<2xi64>>, #dlti.dl_entry<f16, dense<16> : vector<2xi64>>, #dlti.dl_entry<"dlti.endianness", "little">, #dlti.dl_entry<"dlti.stack_alignment", 128 : i64>>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", gpu.container_module, llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", llvm.target_triple = "x86_64-unknown-linux-gnu"} {
+  llvm.func @_QMmod1Phost_sub2d() {
+    %c1_i64 = llvm.mlir.constant(1 : i64) : i64
+    %c0_i64 = llvm.mlir.constant(0 : i64) : i64
+    %c4_i64 = llvm.mlir.constant(4 : i64) : i64
+    %c8_i64 = llvm.mlir.constant(8 : i64) : i64
+    %c32_i64 = llvm.mlir.constant(32 : i64) : i64
+    %c128_i64 = llvm.mlir.constant(128 : i64) : i64
+    %c0_i32 = llvm.mlir.constant(0 : i32) : i32
+    %buf = llvm.alloca %c32_i64 x f32 : (i64) -> !llvm.ptr
+    %u0 = llvm.mlir.undef : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+    %u1 = llvm.insertvalue %buf, %u0[0] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+    %u2 = llvm.insertvalue %buf, %u1[1] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+    %u3 = llvm.insertvalue %c0_i64, %u2[2] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+    %u4 = llvm.insertvalue %c4_i64, %u3[3, 0] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+    %u5 = llvm.insertvalue %c8_i64, %u4[3, 1] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+    %u6 = llvm.insertvalue %c1_i64, %u5[4, 0] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+    %desc = llvm.insertvalue %c4_i64, %u6[4, 1] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+    %memref = builtin.unrealized_conversion_cast %desc : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)> to memref<4x8xf32>
+    gpu.launch_func @cuda_device_mod::@_QMmod1Psub2d blocks in (%c1_i64, %c1_i64, %c1_i64) threads in (%c128_i64, %c1_i64, %c1_i64) : i64 dynamic_shared_memory_size %c0_i32 args(%memref : memref<4x8xf32>) {cuf.proc_attr = #cuf.cuda_proc<global>}
+    llvm.return
+  }
+  llvm.func @_QMmod1Psub2d(!llvm.ptr, !llvm.ptr, i64, i64, i64, i64, i64) -> ()
+  gpu.binary @cuda_device_mod  [#gpu.object<#nvvm.target, "">]
+}
+
+// CHECK-LABEL: llvm.func @_QMmod1Phost_sub2d()
+// Rank-2 descriptor: 7 extractvalues in order (alloc, align, offset,
+// sizes[0], sizes[1], strides[0], strides[1]).
+// CHECK: llvm.extractvalue %{{.*}}[0] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+// CHECK: llvm.extractvalue %{{.*}}[1] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+// CHECK: llvm.extractvalue %{{.*}}[2] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+// CHECK: llvm.extractvalue %{{.*}}[3, 0] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+// CHECK: llvm.extractvalue %{{.*}}[3, 1] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+// CHECK: llvm.extractvalue %{{.*}}[4, 0] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+// CHECK: llvm.extractvalue %{{.*}}[4, 1] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
+// Per-arg struct has 2*ptr + offset + 2*size + 2*stride = 7 fields.
+// CHECK: llvm.alloca %{{.*}} x !llvm.struct<(ptr, ptr, i64, i64, i64, i64, i64)> : (i32) -> !llvm.ptr
+// Param array has 7 pointer slots.
+// CHECK: %[[SEVEN:.*]] = llvm.mlir.constant(7 : i32) : i32
+// CHECK: llvm.alloca %[[SEVEN]] x !llvm.ptr : (i32) -> !llvm.ptr
+// CHECK: llvm.call @_FortranACUFLaunchKernel
+
+// -----
+
+// Scalar + memref mixed arguments: verifies non-memref operands pass through
+// unchanged while memref operands are flattened, preserving argument ordering.
+module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<i1, dense<8> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr, dense<64> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<270>, dense<32> : vector<4xi64>>, #dlti.dl_entry<!llvm.ptr<271>, dense<32> : vector<4xi64>>, #dlti.dl_entry<i8, dense<8> : vector<2xi64>>, #dlti.dl_entry<i16, dense<16> : vector<2xi64>>, #dlti.dl_entry<!llvm.ptr<272>, dense<64> : vector<4xi64>>, #dlti.dl_entry<i64, dense<64> : vector<2xi64>>, #dlti.dl_entry<i32, dense<32> : vector<2xi64>>, #dlti.dl_entry<f128, dense<128> : vector<2xi64>>, #dlti.dl_entry<i128, dense<128> : vector<2xi64>>, #dlti.dl_entry<f64, dense<64> : vector<2xi64>>, #dlti.dl_entry<f80, dense<128> : vector<2xi64>>, #dlti.dl_entry<f16, dense<16> : vector<2xi64>>, #dlti.dl_entry<"dlti.endianness", "little">, #dlti.dl_entry<"dlti.stack_alignment", 128 : i64>>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", gpu.container_module, llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", llvm.target_triple = "x86_64-unknown-linux-gnu"} {
+  llvm.func @_QMmod1Phost_mixed() {
+    %c1_i64 = llvm.mlir.constant(1 : i64) : i64
+    %c0_i64 = llvm.mlir.constant(0 : i64) : i64
+    %c10_i64 = llvm.mlir.constant(10 : i64) : i64
+    %c42_i32 = llvm.mlir.constant(42 : i32) : i32
+    %c128_i64 = llvm.mlir.constant(128 : i64) : i64
+    %c0_i32 = llvm.mlir.constant(0 : i32) : i32
+    %buf = llvm.alloca %c10_i64 x f32 : (i64) -> !llvm.ptr
+    %u0 = llvm.mlir.undef : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %u1 = llvm.insertvalue %buf, %u0[0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %u2 = llvm.insertvalue %buf, %u1[1] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %u3 = llvm.insertvalue %c0_i64, %u2[2] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %u4 = llvm.insertvalue %c10_i64, %u3[3, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %desc = llvm.insertvalue %c1_i64, %u4[4, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+    %memref = builtin.unrealized_conversion_cast %desc : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)> to memref<10xf32>
+    gpu.launch_func @cuda_device_mod::@_QMmod1Psub_mix blocks in (%c1_i64, %c1_i64, %c1_i64) threads in (%c128_i64, %c1_i64, %c1_i64) : i64 dynamic_shared_memory_size %c0_i32 args(%c42_i32 : i32, %memref : memref<10xf32>) {cuf.proc_attr = #cuf.cuda_proc<global>}
+    llvm.return
+  }
+  llvm.func @_QMmod1Psub_mix(i32, !llvm.ptr, !llvm.ptr, i64, i64, i64) -> ()
+  gpu.binary @cuda_device_mod  [#gpu.object<#nvvm.target, "">]
+}
+
+// CHECK-LABEL: llvm.func @_QMmod1Phost_mixed()
+// Only the memref operand is extracted; the scalar is not.
+// CHECK: llvm.extractvalue %{{.*}}[0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+// CHECK: llvm.extractvalue %{{.*}}[1] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+// CHECK: llvm.extractvalue %{{.*}}[2] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+// CHECK: llvm.extractvalue %{{.*}}[3, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+// CHECK: llvm.extractvalue %{{.*}}[4, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
+// Per-arg struct preserves argument order: i32 (scalar) then 5 memref fields.
+// CHECK: llvm.alloca %{{.*}} x !llvm.struct<(i32, ptr, ptr, i64, i64, i64)> : (i32) -> !llvm.ptr
+// Param array has 6 pointer slots (1 scalar + 5 memref fields).
+// CHECK: %[[SIX:.*]] = llvm.mlir.constant(6 : i32) : i32
+// CHECK: llvm.alloca %[[SIX]] x !llvm.ptr : (i32) -> !llvm.ptr
+// CHECK: llvm.call @_FortranACUFLaunchKernel

``````````

</details>


https://github.com/llvm/llvm-project/pull/193651


More information about the flang-commits mailing list