[Mlir-commits] [mlir] [mlir][gpu] Preserve argument attributes during kernel outlining (PR #221465)

purnima shrivastava llvmlistbot at llvm.org
Tue Sep 15 09:56:46 PDT 2026


https://github.com/purnima-nlp updated https://github.com/llvm/llvm-project/pull/221465

>From 47a0427cf346242fe1f97c67dc845b8f844505f9 Mon Sep 17 00:00:00 2001
From: Purnima Shrivastava <purnimashrivastava05 at .com>
Date: Tue, 15 Sep 2026 21:05:09 +0530
Subject: [PATCH 1/2] [mlir][gpu] Add calling convention arguments to
 gpu.launch

---
 mlir/include/mlir/Dialect/GPU/IR/GPUOps.td |  31 ++++++-
 mlir/lib/Dialect/GPU/IR/GPUDialect.cpp     | 100 ++++++++++++++++++++-
 mlir/python/mlir/dialects/gpu/__init__.py  |   1 +
 mlir/test/Dialect/GPU/invalid.mlir         |  38 ++++++--
 mlir/test/Dialect/GPU/ops.mlir             |  14 +++
 5 files changed, 175 insertions(+), 9 deletions(-)

diff --git a/mlir/include/mlir/Dialect/GPU/IR/GPUOps.td b/mlir/include/mlir/Dialect/GPU/IR/GPUOps.td
index 2aa5219e904040..ee6b3b4b70d188 100644
--- a/mlir/include/mlir/Dialect/GPU/IR/GPUOps.td
+++ b/mlir/include/mlir/Dialect/GPU/IR/GPUOps.td
@@ -823,7 +823,9 @@ def GPU_LaunchOp : GPU_Op<"launch", [
                OptionalAttr<FlatSymbolRefAttr>:$module,
                OptionalAttr<FlatSymbolRefAttr>:$function,
                OptionalAttr<ConfinedAttr<I64Attr, [IntNonNegative]>>:$workgroup_attributions,
-               Optional<AnyType>:$asyncObject)>,
+               Optional<AnyType>:$asyncObject,
+               Variadic<AnyType>:$calling_convention,
+               OptionalAttr<DictArrayAttr>:$arg_attrs)>,
     Results<(outs Optional<GPU_AsyncToken>:$asyncToken)> {
   let summary = "GPU kernel launch operation";
 
@@ -856,6 +858,15 @@ def GPU_LaunchOp : GPU_Op<"launch", [
     The `function` and `module` attributes are optional and specifies
     the kernel name and a module in which the kernel should be outlined.
 
+    `calling_convention` is an explicit, ordered list of values captured from
+    outside the launch region. Each listed value must be used inside the launch
+    region. When the launch is outlined, these values are intended to become
+    the first kernel arguments in the order specified on `gpu.launch`. Values
+    captured by the launch but not listed in `calling_convention` may still be
+    handled separately by outlining. When present, `arg_attrs` contains one
+    dictionary per `calling_convention` operand and carries the per-argument
+    attributes associated with that operand.
+
     Syntax:
 
     ```
@@ -866,9 +877,12 @@ def GPU_LaunchOp : GPU_Op<"launch", [
                              (dynamic_shared_memory_size ssa-use)?
                              (`module(` symbol-ref-id `)`)?
                              (`function(` symbol-ref-id `)`)?
+                             (`calling_convention` `(` calling-convention-args `)`)?
                              memory-attribution
                              region attr-dict?
     ssa-reassignment ::= `(` ssa-id `=` ssa-use (`,` ssa-id `=` ssa-use)* `)`
+    calling-convention-args ::= ssa-use `:` type attr-dict?
+                                (`,` ssa-use `:` type attr-dict?)*
     memory-attribution ::= (`workgroup` `(` ssa-id-and-type-list `)`)?
                            (`private` `(` ssa-id-and-type-list `)`)?
     ```
@@ -902,6 +916,15 @@ def GPU_LaunchOp : GPU_Op<"launch", [
       %3 = "memref.load"(%val1, %bx) : (memref<?xf32, 1>, index) -> f32
     }
 
+    // Launch with explicit kernel arguments and per-argument attributes.
+    gpu.launch blocks(%bx, %by, %bz) in (%sz_bx = %0, %sz_by = %1, %sz_bz = %2)
+               threads(%tx, %ty, %tz) in (%sz_tx = %3, %sz_ty = %4, %sz_tz = %5)
+               calling_convention(%value0 : i32 {test.attr = 42 : i32},
+                                  %value1 : f32) {
+      "some_op"(%value0, %value1) : (i32, f32) -> ()
+      gpu.terminator
+    }
+
     // Launch with memory attributions.
     gpu.launch blocks(%bx, %by, %bz) in (%sz_bx = %0, %sz_by = %1, %sz_bz = %2)
                threads(%tx, %ty, %tz) in (%sz_tx = %3, %sz_ty = %4, %sz_tz = %5)
@@ -962,6 +985,12 @@ def GPU_LaunchOp : GPU_Op<"launch", [
   ];
 
   let extraClassDeclaration = [{
+    /// Add a value to the kernel calling convention.
+    void addCallingConventionArg(Value value, DictionaryAttr attrs = {});
+
+    /// Remove the calling convention argument at `index`.
+    void removeCallingConventionArg(unsigned index);
+
     /// Get the SSA values corresponding to kernel block identifiers.
     KernelDim3 getBlockIds();
     /// Get the SSA values corresponding to kernel thread identifiers.
diff --git a/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp b/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp
index a39b43c9803eb9..6e80f3452b1138 100644
--- a/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp
+++ b/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp
@@ -33,6 +33,7 @@
 #include "mlir/Interfaces/SideEffectInterfaces.h"
 #include "mlir/Interfaces/ValueBoundsOpInterface.h"
 #include "mlir/Transforms/InliningUtils.h"
+#include "mlir/Transforms/RegionUtils.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/TypeSwitch.h"
 #include "llvm/Support/CommandLine.h"
@@ -747,17 +748,54 @@ void LaunchOp::build(OpBuilder &builder, OperationState &result,
   for (Type argTy : privateAttributions)
     body->addArgument(argTy, result.location);
   // Fill OperandSegmentSize Attribute.
-  SmallVector<int32_t, 12> segmentSizes(12, 1);
+  SmallVector<int32_t, 13> segmentSizes(13, 1);
   segmentSizes.front() = asyncDependencies.size();
   segmentSizes[7] = clusterSizeX ? 1 : 0;
   segmentSizes[8] = clusterSizeY ? 1 : 0;
   segmentSizes[9] = clusterSizeZ ? 1 : 0;
   segmentSizes[10] = dynamicSharedMemorySize ? 1 : 0;
   segmentSizes[11] = asyncObject ? 1 : 0;
+  segmentSizes[12] = 0;
   result.addAttribute(getOperandSegmentSizeAttr(),
                       builder.getDenseI32ArrayAttr(segmentSizes));
 }
 
+void LaunchOp::addCallingConventionArg(Value value, DictionaryAttr attrs) {
+  getCallingConventionMutable().append(value);
+  auto argAttrs = getArgAttrs();
+  if (!argAttrs && (!attrs || attrs.empty()))
+    return;
+
+  Builder builder(getContext());
+  DictionaryAttr emptyAttrs = builder.getDictionaryAttr({});
+  SmallVector<Attribute> newArgAttrs;
+  if (argAttrs)
+    llvm::append_range(newArgAttrs, argAttrs->getValue());
+  else
+    newArgAttrs.assign(getCallingConvention().size() - 1, emptyAttrs);
+  newArgAttrs.push_back(attrs && !attrs.empty() ? attrs : emptyAttrs);
+  setArgAttrsAttr(builder.getArrayAttr(newArgAttrs));
+}
+
+void LaunchOp::removeCallingConventionArg(unsigned index) {
+  assert(index < getCallingConvention().size() &&
+         "calling convention argument index out of range");
+  getCallingConventionMutable().erase(index, 1);
+  auto argAttrs = getArgAttrs();
+  if (!argAttrs)
+    return;
+
+  SmallVector<Attribute> newArgAttrs(argAttrs->begin(), argAttrs->end());
+  newArgAttrs.erase(newArgAttrs.begin() + index);
+  if (llvm::all_of(newArgAttrs, [](Attribute attr) {
+        return cast<DictionaryAttr>(attr).empty();
+      })) {
+    removeArgAttrsAttr();
+    return;
+  }
+  setArgAttrsAttr(ArrayAttr::get(getContext(), newArgAttrs));
+}
+
 KernelDim3 LaunchOp::getBlockIds() {
   assert(!getBody().empty() && "LaunchOp body must not be empty.");
   auto args = getBody().getArguments();
@@ -845,6 +883,21 @@ LogicalResult LaunchOp::verifyRegions() {
   if (getBody().empty()) {
     return emitOpError("body region is empty");
   }
+
+  if (auto argAttrs = getArgAttrs()) {
+    if (argAttrs->size() != getCallingConvention().size())
+      return emitOpError(
+          "arg_attrs must have one entry per calling convention operand");
+  }
+
+  SetVector<Value> capturedValues;
+  getUsedValuesDefinedAbove(getBody(), capturedValues);
+  for (Value value : getCallingConvention()) {
+    if (!capturedValues.contains(value))
+      return emitOpError(
+          "calling convention operands must be used in the launch body");
+  }
+
   unsigned actualNumRegionArgs = getBody().getNumArguments();
   unsigned expectedNumRegionArgs =
       getNumConfigRegionAttributes() + getNumWorkgroupAttributions();
@@ -937,6 +990,21 @@ void LaunchOp::print(OpAsmPrinter &p) {
   if (getCooperative())
     p << " cooperative";
 
+  if (!getCallingConvention().empty()) {
+    p << " calling_convention(";
+    auto argAttrs = getArgAttrs();
+    llvm::interleaveComma(
+        llvm::enumerate(getCallingConvention()), p, [&](auto pair) {
+          Value value = pair.value();
+          p << value << " : " << value.getType();
+          if (argAttrs) {
+            auto attrs = cast<DictionaryAttr>((*argAttrs)[pair.index()]);
+            p.printOptionalAttrDict(attrs.getValue());
+          }
+        });
+    p << ')';
+  }
+
   printAttributions(p, getWorkgroupKeyword(), getWorkgroupAttributionBBArgs());
   printAttributions(p, getPrivateKeyword(), getPrivateAttributions());
 
@@ -947,7 +1015,7 @@ void LaunchOp::print(OpAsmPrinter &p) {
       (*this)->getDiscardableAttrDictionary().getValue(), /*elidedAttrs=*/{
           LaunchOp::getOperandSegmentSizeAttr(),
           getWorkgroupAttributionsAttrName(), getCooperativeAttrName(),
-          moduleAttrName, functionAttrName});
+          moduleAttrName, functionAttrName, getArgAttrsAttrName()});
 }
 
 // Parse the size assignment blocks for blocks and threads.  These have the form
@@ -1110,6 +1178,31 @@ ParseResult LaunchOp::parse(OpAsmParser &parser, OperationState &result) {
   if (succeeded(parser.parseOptionalKeyword("cooperative")))
     result.addAttribute("cooperative", parser.getBuilder().getUnitAttr());
 
+  SmallVector<Attribute> argAttrs;
+  bool hasArgAttrs = false;
+  unsigned numCallingConventionOperands = 0;
+  if (succeeded(parser.parseOptionalKeyword("calling_convention"))) {
+    auto parseElement = [&]() -> ParseResult {
+      OpAsmParser::UnresolvedOperand operand;
+      Type type;
+      NamedAttrList attrs;
+      if (parser.parseOperand(operand) || parser.parseColonType(type) ||
+          parser.parseOptionalAttrDict(attrs) ||
+          parser.resolveOperand(operand, type, result.operands))
+        return failure();
+      hasArgAttrs |= !attrs.empty();
+      argAttrs.push_back(attrs.getDictionary(parser.getContext()));
+      ++numCallingConventionOperands;
+      return success();
+    };
+    if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
+                                       parseElement))
+      return failure();
+    if (hasArgAttrs)
+      result.addAttribute(getArgAttrsAttrName(result.name),
+                          parser.getBuilder().getArrayAttr(argAttrs));
+  }
+
   // Create the region arguments: fixed launch-config args (`index`), then
   // workgroup / private attribution args. The workgroup count is stored in the
   // inherent `workgroup_attributions` attribute when non-zero.
@@ -1153,7 +1246,7 @@ ParseResult LaunchOp::parse(OpAsmParser &parser, OperationState &result) {
       parser.parseOptionalAttrDict(result.attributes))
     return failure();
 
-  SmallVector<int32_t, 12> segmentSizes(12, 1);
+  SmallVector<int32_t, 13> segmentSizes(13, 1);
   segmentSizes.front() = asyncDependencies.size();
 
   if (!hasCluster) {
@@ -1163,6 +1256,7 @@ ParseResult LaunchOp::parse(OpAsmParser &parser, OperationState &result) {
   }
   segmentSizes[10] = hasDynamicSharedMemorySize ? 1 : 0;
   segmentSizes[11] = hasAsyncObject ? 1 : 0;
+  segmentSizes[12] = numCallingConventionOperands;
   result.addAttribute(LaunchOp::getOperandSegmentSizeAttr(),
                       parser.getBuilder().getDenseI32ArrayAttr(segmentSizes));
   return success();
diff --git a/mlir/python/mlir/dialects/gpu/__init__.py b/mlir/python/mlir/dialects/gpu/__init__.py
index 2ce4e3a24bf6b2..70470716d80243 100644
--- a/mlir/python/mlir/dialects/gpu/__init__.py
+++ b/mlir/python/mlir/dialects/gpu/__init__.py
@@ -306,6 +306,7 @@ def __init__(
             block_size_y,
             block_size_z,
             dynamicSharedMemorySize=dynamic_shared_memory_size,
+            calling_convention=[],
             loc=loc,
             ip=ip,
         )
diff --git a/mlir/test/Dialect/GPU/invalid.mlir b/mlir/test/Dialect/GPU/invalid.mlir
index 1b6d3a41d2bd09..6713a361bc6cee 100644
--- a/mlir/test/Dialect/GPU/invalid.mlir
+++ b/mlir/test/Dialect/GPU/invalid.mlir
@@ -3,7 +3,7 @@
 func.func @empty_body(%sz : index) {
   // expected-error at +1 {{'gpu.launch' op body region is empty}}
   "gpu.launch"(%sz, %sz, %sz, %sz, %sz, %sz) ({
-  }) {operandSegmentSizes = array<i32: 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0>} : (index, index, index, index, index, index) -> ()
+  }) {operandSegmentSizes = array<i32: 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0>} : (index, index, index, index, index, index) -> ()
   return
 }
 
@@ -13,7 +13,7 @@ func.func @not_enough_sizes(%sz : index) {
   // expected-error at +1 {{expected 6 or more operands, but found 5}}
   "gpu.launch"(%sz, %sz, %sz, %sz, %sz) ({
     gpu.return
-  }) {operandSegmentSizes = array<i32: 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0>} : (index, index, index, index, index) -> ()
+  }) {operandSegmentSizes = array<i32: 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0>} : (index, index, index, index, index) -> ()
   return
 }
 
@@ -25,7 +25,7 @@ func.func @no_region_attrs(%sz : index) {
   ^bb1(%bx: index, %by: index, %bz: index,
        %tx: index, %ty: index, %tz: index):
     gpu.terminator
-  }) {operandSegmentSizes = array<i32: 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0>} : (index, index, index, index, index, index) -> ()
+  }) {operandSegmentSizes = array<i32: 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0>} : (index, index, index, index, index, index) -> ()
   return
 }
 
@@ -39,7 +39,7 @@ func.func @not_enough_cluster_region_attrs(%sz : index) {
        %sbx: index, %sby: index, %sbz: index,
        %stx: index, %sty: index, %stz: index):
     gpu.terminator
-  }) {operandSegmentSizes = array<i32: 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0>} : (index, index, index, index, index, index, index, index, index) -> ()
+  }) {operandSegmentSizes = array<i32: 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0>} : (index, index, index, index, index, index, index, index, index) -> ()
   return
 }
 
@@ -111,7 +111,7 @@ func.func @launch_async_deps_no_token(%dep : !gpu.async.token, %sz : index) {
        %nbx: index, %nby: index, %nbz: index,
        %ntx: index, %nty: index, %ntz: index):
     gpu.terminator
-  }) {operandSegmentSizes = array<i32: 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0>}
+  }) {operandSegmentSizes = array<i32: 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0>}
      : (!gpu.async.token, index, index, index, index, index, index) -> ()
   return
 }
@@ -129,6 +129,34 @@ func.func @launch_async_object_and_token(%sz : index, %stream : !llvm.ptr) {
 
 // -----
 
+func.func @launch_unused_calling_convention_operand(%sz : index, %value : i32) {
+  // expected-error at +1 {{'gpu.launch' op calling convention operands must be used in the launch body}}
+  gpu.launch blocks(%bx, %by, %bz) in (%sbx = %sz, %sby = %sz, %sbz = %sz)
+             threads(%tx, %ty, %tz) in (%stx = %sz, %sty = %sz, %stz = %sz)
+             calling_convention(%value : i32) {
+    gpu.terminator
+  }
+  return
+}
+
+// -----
+
+func.func @launch_mismatched_arg_attrs(%sz : index, %value : i32) {
+  // expected-error at +1 {{'gpu.launch' op arg_attrs must have one entry per calling convention operand}}
+  "gpu.launch"(%sz, %sz, %sz, %sz, %sz, %sz, %value) ({
+  ^bb0(%bx: index, %by: index, %bz: index,
+       %tx: index, %ty: index, %tz: index,
+       %nbx: index, %nby: index, %nbz: index,
+       %ntx: index, %nty: index, %ntz: index):
+    %sum = arith.addi %value, %value : i32
+    gpu.terminator
+  }) {arg_attrs = [], operandSegmentSizes = array<i32: 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1>}
+     : (index, index, index, index, index, index, i32) -> ()
+  return
+}
+
+// -----
+
 func.func @launch_func_too_few_operands(%sz : index) {
   // expected-error at +1 {{expected 6 or more operands}}
   "gpu.launch_func"(%sz, %sz, %sz, %sz, %sz)
diff --git a/mlir/test/Dialect/GPU/ops.mlir b/mlir/test/Dialect/GPU/ops.mlir
index 1e0c02d5a9b507..e7d697d5d887f5 100644
--- a/mlir/test/Dialect/GPU/ops.mlir
+++ b/mlir/test/Dialect/GPU/ops.mlir
@@ -17,6 +17,20 @@ module attributes {gpu.container_module} {
     return
   }
 
+  // CHECK-LABEL: func @launch_calling_convention(
+  // CHECK-SAME: %{{.*}}: index, %[[VALUE0:[^, ]+]]: i32, %[[VALUE1:[^, ]+]]: f32)
+  func.func @launch_calling_convention(%sz : index, %value0 : i32, %value1 : f32) {
+    // CHECK: gpu.launch blocks({{.*}}) threads({{.*}}) calling_convention(%[[VALUE0]] : i32 {test.attr = 42 : i32}, %[[VALUE1]] : f32)
+    gpu.launch blocks(%bx, %by, %bz) in (%grid_x = %sz, %grid_y = %sz, %grid_z = %sz)
+               threads(%tx, %ty, %tz) in (%block_x = %sz, %block_y = %sz, %block_z = %sz)
+               calling_convention(%value0 : i32 {test.attr = 42 : i32}, %value1 : f32) {
+      "use"(%value0) : (i32) -> ()
+      "use"(%value1) : (f32) -> ()
+      gpu.terminator
+    }
+    return
+  }
+
   // CHECK-LABEL:func @launch_cooperative(%{{.*}}: index)
   func.func @launch_cooperative(%sz : index) {
     // CHECK: gpu.launch blocks(%{{.*}}, %{{.*}}, %{{.*}}) in (%{{.*}} = %{{.*}}, %{{.*}} = %{{.*}}, %{{.*}} = %{{.*}}) threads(%{{.*}}, %{{.*}}, %{{.*}}) in (%{{.*}} = %{{.*}}, %{{.*}} = %{{.*}}, %{{.*}} = %{{.*}}) cooperative

>From e95a52779a18ed9b01613e824261a2a20dc15c4c Mon Sep 17 00:00:00 2001
From: Purnima Shrivastava <purnimashrivastava05 at .com>
Date: Tue, 15 Sep 2026 21:41:42 +0530
Subject: [PATCH 2/2] [mlir][gpu] Preserve argument attributes during kernel
 outlining

---
 .../GPU/Transforms/KernelOutlining.cpp        | 18 ++++++++++---
 mlir/test/Dialect/GPU/outlining.mlir          | 27 +++++++++++++++++++
 2 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/mlir/lib/Dialect/GPU/Transforms/KernelOutlining.cpp b/mlir/lib/Dialect/GPU/Transforms/KernelOutlining.cpp
index 0ee3d425f66bef..9b15ea2b75a69b 100644
--- a/mlir/lib/Dialect/GPU/Transforms/KernelOutlining.cpp
+++ b/mlir/lib/Dialect/GPU/Transforms/KernelOutlining.cpp
@@ -183,9 +183,14 @@ static gpu::GPUFuncOp outlineKernelFuncImpl(gpu::LaunchOp launchOp,
   OpBuilder builder(launchOp.getContext());
   Region &launchOpBody = launchOp.getBody();
 
-  // Identify uses from values defined outside of the scope of the launch
-  // operation.
-  getUsedValuesDefinedAbove(launchOpBody, operands);
+  // Keep calling convention operands before other operands and captures.
+  SetVector<Value> orderedOperands;
+  for (Value operand : launchOp.getCallingConvention())
+    orderedOperands.insert(operand);
+  for (Value operand : operands)
+    orderedOperands.insert(operand);
+  getUsedValuesDefinedAbove(launchOpBody, orderedOperands);
+  operands = std::move(orderedOperands);
 
   // Create the gpu.func operation.
   SmallVector<Type, 4> kernelOperandTypes;
@@ -200,6 +205,13 @@ static gpu::GPUFuncOp outlineKernelFuncImpl(gpu::LaunchOp launchOp,
       TypeRange(ValueRange(launchOp.getWorkgroupAttributionBBArgs())),
       TypeRange(ValueRange(launchOp.getPrivateAttributions())));
   outlinedFunc.setKernel(true);
+  if (auto argAttrs = launchOp.getArgAttrs()) {
+    DictionaryAttr emptyAttrs = builder.getDictionaryAttr({});
+    SmallVector<Attribute> kernelArgAttrs(operands.size(), emptyAttrs);
+    for (const auto &[index, attr] : llvm::enumerate(*argAttrs))
+      kernelArgAttrs[index] = attr;
+    outlinedFunc.setArgAttrsAttr(builder.getArrayAttr(kernelArgAttrs));
+  }
 
   // If we can infer bounds on the grid and/or block sizes from the arguments
   // to the launch op, propagate them to the generated kernel. This is safe
diff --git a/mlir/test/Dialect/GPU/outlining.mlir b/mlir/test/Dialect/GPU/outlining.mlir
index f708561bc2f019..4f51b21849af85 100644
--- a/mlir/test/Dialect/GPU/outlining.mlir
+++ b/mlir/test/Dialect/GPU/outlining.mlir
@@ -60,6 +60,33 @@ func.func @launch() {
 
 // -----
 
+// CHECK-LABEL: func @launch_calling_convention(
+// CHECK-SAME: %[[EXPLICIT0:.*]]: i32, %[[EXPLICIT1:.*]]: f32, %[[IMPLICIT:.*]]: memref<?xf32>)
+func.func @launch_calling_convention(%explicit0 : i32, %explicit1 : f32,
+                                     %implicit : memref<?xf32>) {
+  %dim = arith.constant 1 : index
+  // CHECK: gpu.launch_func @launch_calling_convention_kernel::@launch_calling_convention_kernel
+  // CHECK-SAME: args(%[[EXPLICIT0]] : i32, %[[EXPLICIT1]] : f32, %[[IMPLICIT]] : memref<?xf32>)
+  gpu.launch blocks(%bx, %by, %bz) in (%grid_x = %dim, %grid_y = %dim,
+                                       %grid_z = %dim)
+             threads(%tx, %ty, %tz) in (%block_x = %dim, %block_y = %dim,
+                                        %block_z = %dim)
+             calling_convention(%explicit0 : i32 {test.attr0},
+                                %explicit1 : f32 {test.attr1 = 42 : i32}) {
+    "use"(%explicit0, %explicit1) : (i32, f32) -> ()
+    %value = memref.load %implicit[%tx] : memref<?xf32>
+    "use"(%value) : (f32) -> ()
+    gpu.terminator
+  }
+  return
+}
+
+// CHECK-LABEL: gpu.module @launch_calling_convention_kernel
+// CHECK-NEXT: gpu.func @launch_calling_convention_kernel
+// CHECK-SAME: (%[[KERNEL_EXPLICIT0:.*]]: i32 {test.attr0}, %[[KERNEL_EXPLICIT1:.*]]: f32 {test.attr1 = 42 : i32}, %[[KERNEL_IMPLICIT:.*]]: memref<?xf32>)
+
+// -----
+
 // Verify that we can outline a CFG
 // CHECK-LABEL:  gpu.func @launchCFG_kernel(
 // CHECK: cf.br



More information about the Mlir-commits mailing list