[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