Skip to content

Commit b68fe86

Browse files
authored
[mlir] Prepare convert-gpu-to-spirv for OpenCL support (#69941)
This includes a couple of changes to pass behavior for OpenCL kernels. Vulkan shaders are not impacted by the changes. 1. SPIR-V module is placed inside GPU module. This change is required for gpu-module-to-binary to work correctly as it expects kernel function to be inside the GPU module. 2. A dummy func.func with same kernel name as gpu.func is created. GPU compilation pipeline defers lowering of gpu launch kernel op. Since spirv.func is not directly tied to gpu launch kernel, a dummy func.func is required to avoid legalization issues. 3. Use correct mapping when mapping MemRef memory space to SPIR-V storage class for OpenCL kernels.
1 parent 9f6010d commit b68fe86

File tree

3 files changed

+55
-4
lines changed

3 files changed

+55
-4
lines changed

mlir/include/mlir/Conversion/Passes.td

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,10 @@ def ConvertGPUToSPIRV : Pass<"convert-gpu-to-spirv", "ModuleOp"> {
556556
to control the set and binding if wanted.
557557
}];
558558
let constructor = "mlir::createConvertGPUToSPIRVPass()";
559-
let dependentDialects = ["spirv::SPIRVDialect"];
559+
let dependentDialects = [
560+
"func::FuncDialect",
561+
"spirv::SPIRVDialect",
562+
];
560563
let options = [
561564
Option<"use64bitIndex", "use-64bit-index",
562565
"bool", /*default=*/"false",

mlir/lib/Conversion/GPUToSPIRV/GPUToSPIRVPass.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "mlir/Conversion/FuncToSPIRV/FuncToSPIRV.h"
1818
#include "mlir/Conversion/GPUToSPIRV/GPUToSPIRV.h"
1919
#include "mlir/Conversion/MemRefToSPIRV/MemRefToSPIRV.h"
20+
#include "mlir/Dialect/Func/IR/FuncOps.h"
2021
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
2122
#include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
2223
#include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
@@ -54,22 +55,47 @@ void GPUToSPIRVPass::runOnOperation() {
5455

5556
SmallVector<Operation *, 1> gpuModules;
5657
OpBuilder builder(context);
58+
59+
auto targetEnvSupportsKernelCapability = [](gpu::GPUModuleOp moduleOp) {
60+
Operation *gpuModule = moduleOp.getOperation();
61+
auto targetAttr = spirv::lookupTargetEnvOrDefault(gpuModule);
62+
spirv::TargetEnv targetEnv(targetAttr);
63+
return targetEnv.allows(spirv::Capability::Kernel);
64+
};
65+
5766
module.walk([&](gpu::GPUModuleOp moduleOp) {
5867
// Clone each GPU kernel module for conversion, given that the GPU
5968
// launch op still needs the original GPU kernel module.
60-
builder.setInsertionPoint(moduleOp.getOperation());
69+
// For Vulkan Shader capabilities, we insert the newly converted SPIR-V
70+
// module right after the original GPU module, as that's the expectation of
71+
// the in-tree Vulkan runner.
72+
// For OpenCL Kernel capabilities, we insert the newly converted SPIR-V
73+
// module inside the original GPU module, as that's the expectaion of the
74+
// normal GPU compilation pipeline.
75+
if (targetEnvSupportsKernelCapability(moduleOp)) {
76+
builder.setInsertionPoint(moduleOp.getBody(),
77+
moduleOp.getBody()->begin());
78+
} else {
79+
builder.setInsertionPoint(moduleOp.getOperation());
80+
}
6181
gpuModules.push_back(builder.clone(*moduleOp.getOperation()));
6282
});
6383

6484
// Run conversion for each module independently as they can have different
6585
// TargetEnv attributes.
6686
for (Operation *gpuModule : gpuModules) {
87+
spirv::TargetEnvAttr targetAttr =
88+
spirv::lookupTargetEnvOrDefault(gpuModule);
89+
6790
// Map MemRef memory space to SPIR-V storage class first if requested.
6891
if (mapMemorySpace) {
6992
std::unique_ptr<ConversionTarget> target =
7093
spirv::getMemorySpaceToStorageClassTarget(*context);
7194
spirv::MemorySpaceToStorageClassMap memorySpaceMap =
72-
spirv::mapMemorySpaceToVulkanStorageClass;
95+
targetEnvSupportsKernelCapability(
96+
dyn_cast<gpu::GPUModuleOp>(gpuModule))
97+
? spirv::mapMemorySpaceToOpenCLStorageClass
98+
: spirv::mapMemorySpaceToVulkanStorageClass;
7399
spirv::MemorySpaceToStorageClassConverter converter(memorySpaceMap);
74100

75101
RewritePatternSet patterns(context);
@@ -79,7 +105,6 @@ void GPUToSPIRVPass::runOnOperation() {
79105
return signalPassFailure();
80106
}
81107

82-
auto targetAttr = spirv::lookupTargetEnvOrDefault(gpuModule);
83108
std::unique_ptr<ConversionTarget> target =
84109
SPIRVConversionTarget::get(targetAttr);
85110

@@ -108,6 +133,25 @@ void GPUToSPIRVPass::runOnOperation() {
108133
if (failed(applyFullConversion(gpuModule, *target, std::move(patterns))))
109134
return signalPassFailure();
110135
}
136+
137+
// For OpenCL, the gpu.func op in the original gpu.module op needs to be
138+
// replaced with an empty func.func op with the same arguments as the gpu.func
139+
// op. The func.func op needs gpu.kernel attribute set.
140+
module.walk([&](gpu::GPUModuleOp moduleOp) {
141+
if (targetEnvSupportsKernelCapability(moduleOp)) {
142+
moduleOp.walk([&](gpu::GPUFuncOp funcOp) {
143+
builder.setInsertionPoint(funcOp);
144+
auto newFuncOp = builder.create<func::FuncOp>(
145+
funcOp.getLoc(), funcOp.getName(), funcOp.getFunctionType());
146+
auto entryBlock = newFuncOp.addEntryBlock();
147+
builder.setInsertionPointToEnd(entryBlock);
148+
builder.create<func::ReturnOp>(funcOp.getLoc());
149+
newFuncOp->setAttr(gpu::GPUDialect::getKernelFuncAttrName(),
150+
builder.getUnitAttr());
151+
funcOp.erase();
152+
});
153+
}
154+
});
111155
}
112156

113157
} // namespace

mlir/test/Conversion/GPUToSPIRV/module-opencl.mlir

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ module attributes {
1212
// CHECK-SAME: {{%.*}}: !spirv.ptr<!spirv.array<12 x f32>, CrossWorkgroup>
1313
// CHECK-NOT: spirv.interface_var_abi
1414
// CHECK-SAME: spirv.entry_point_abi = #spirv.entry_point_abi<workgroup_size = [32, 4, 1]>
15+
// CHECK-LABEL: func.func @basic_module_structure
16+
// CHECK-SAME: attributes {gpu.kernel}
1517
gpu.func @basic_module_structure(%arg0 : f32, %arg1 : memref<12xf32, #spirv.storage_class<CrossWorkgroup>>) kernel
1618
attributes {spirv.entry_point_abi = #spirv.entry_point_abi<workgroup_size = [32, 4, 1]>} {
1719
gpu.return
@@ -45,6 +47,8 @@ module attributes {
4547
// CHECK-SAME: {{%.*}}: !spirv.ptr<!spirv.array<12 x f32>, CrossWorkgroup>
4648
// CHECK-NOT: spirv.interface_var_abi
4749
// CHECK-SAME: spirv.entry_point_abi = #spirv.entry_point_abi<workgroup_size = [32, 4, 1]>
50+
// CHECK-LABEL: func.func @basic_module_structure
51+
// CHECK-SAME: attributes {gpu.kernel}
4852
gpu.func @basic_module_structure(%arg0 : f32, %arg1 : memref<12xf32, #spirv.storage_class<CrossWorkgroup>>) kernel
4953
attributes {spirv.entry_point_abi = #spirv.entry_point_abi<workgroup_size = [32, 4, 1]>} {
5054
gpu.return

0 commit comments

Comments
 (0)