Skip to content

Commit b43068e

Browse files
committed
[mlir][gpu] Update GPU translation to accept binaries.
== Commit message == Modifies GPU translation to accept GPU binaries embedding them using the object manager interface method `embedBinary`, as well as accepting kernel launch operations translating them using the interface method `launchKernel`. Depends on D154152 = Explanation = **Summary:** These patches aim to be a replacement to the current GPU compilation infrastructure, with extensibility and trying to minimizing future disruption as the primary goal. The biggest updates performed by these patches are: - The introduction of Target attributes, these attributes handle compilation of GPU modules into binary strings. These attributes can be implemented by any dialect, leaving the option for downstream users to implement their own serializations. - The introduction of the GPU binary operation, this operation stores GPU objects for different targets and can be invoked by `gpu.launch_func`. - Making `gpu.binary` & `gpu.launch_func` translatable to LLVM IR, with the translation being controlled by Object Manager attributes. - The introduction of the `gpu-module-to-binary` pass. This pass serializes GPU modules into GPU binaries, using the GPU targets available in the module. - The introduction of the `#gpu.select_object` object manager as the default object manager, it selects a single object for embedding in the IR, by default it selects the first object. These patches leave the current infrastructure in place, allowing for a migration period for downstream users. **Examples:** - GPU modules using target attributes: ``` gpu.module @my_module [#gpu.nvptx<chip = "sm_90">, #gpu.amdgpu, #gpu.amdgpu<chip = "gfx90a">] { ... } ``` - Applying the `gpu-module-to-binary` pass: ``` gpu.module @my_module [#gpu.nvptx<chip = "sm_90">, #gpu.amdgpu] { ... } ; mlir-opt --gpu-module-to-binary gpu.binary @my_module [#gpu.object<#gpu.nvptx<chip = "sm_90">, "BINARY DATA">, #gpu.object<#gpu.amdgpu, "BINARY DATA">] ``` - Choosing the `#gpu.amdgpu` object for embedding: ``` gpu.binary @my_module <#gpu.select_object<#gpu.amdgpu>> [#gpu.object<#gpu.nvptx<chip = "sm_90">, "BINARY DATA">, #gpu.object<#gpu.amdgpu, "BINARY DATA">] ; It's also valid to pass the index of the object. gpu.binary @my_module <#gpu.select_object<1>> [#gpu.object<#gpu.nvptx<chip = "sm_90">, "BINARY DATA">, #gpu.object<#gpu.amdgpu, "BINARY DATA">] ``` **Testing:** This infrastructure was tested in 2 systems, one with a NVIDIA V100 and the other one with a AMD MI250X, in both cases the test completion was successful. Input files: - **test.cpp** {F28084155} - **test_nvvm.mlir** {F28084157} - **test_rocdl.mlir** {F28084162} 1. Steps for assembling the test for the NVIDIA system: ``` mlir-opt --gpu-to-llvm --gpu-module-to-binary test_nvvm.mlir | mlir-translate --mlir-to-llvmir -o test_nvptx.ll clang++ test_nvptx.ll test.cpp -l ``` **Output file:** test_nvptx.ll {F28084210} 2. Steps for assembling the test for the AMD system: ``` mlir-opt --gpu-to-llvm --gpu-module-to-binary test_rocdl.mlir | mlir-translate --mlir-to-llvmir -o test_amdgpu.ll clang++ test_amdgpu.ll test.cpp -l ``` **Output file:** test_amdgpu.ll {F28084217} == Diff list == The following patches implement the proposal described in: https://discourse.llvm.org/t/rfc-extending-mlir-gpu-device-codegen-pipeline/70199/54 : - D154098: Add a `GlobalSymbol` trait. - D154097: Add a parameter for passing default values to `StringRefParameter` - D154100: Adds an utility class for serializing operations to binary strings. - D154104: Add GPU target attribute interface. - D154113: Add target attribute to GPU modules. - D154117: Adds the NVPTX target attribute. - D154129: Adds the AMDGPU target attribute. - D154108: Add the GPU object manager attribute interface. - D154132: Add `gpu.binary` op and `#gpu.object` attribute. - D154137: Modifies `gpu.launch_func` to allow lowering it after gpu-to-llvm. - D154147: Add the Select Object compilation attribute. - D154149: Add the `gpu-module-to-binary` pass. - D154152: Add GPU target support to `gpu-to-llvm`. Reviewed By: mehdi_amini Differential Revision: https://reviews.llvm.org/D154153
1 parent fcfeb1e commit b43068e

File tree

4 files changed

+117
-2
lines changed

4 files changed

+117
-2
lines changed

mlir/lib/Target/LLVMIR/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ add_mlir_translation_library(MLIRToLLVMIRTranslationRegistration
5757
MLIROpenACCToLLVMIRTranslation
5858
MLIROpenMPToLLVMIRTranslation
5959
MLIRROCDLToLLVMIRTranslation
60+
MLIRNVVMTarget
61+
MLIRROCDLTarget
6062
)
6163

6264
add_mlir_translation_library(MLIRTargetLLVMIRImport

mlir/lib/Target/LLVMIR/ConvertToLLVMIR.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "mlir/Dialect/DLTI/DLTI.h"
1414
#include "mlir/Dialect/Func/IR/FuncOps.h"
1515
#include "mlir/IR/BuiltinOps.h"
16+
#include "mlir/Target/LLVM/NVVM/Target.h"
17+
#include "mlir/Target/LLVM/ROCDL/Target.h"
1618
#include "mlir/Target/LLVMIR/Dialect/All.h"
1719
#include "mlir/Target/LLVMIR/Export.h"
1820
#include "mlir/Tools/mlir-translate/Translation.h"
@@ -36,6 +38,8 @@ void registerToLLVMIRTranslation() {
3638
},
3739
[](DialectRegistry &registry) {
3840
registry.insert<DLTIDialect, func::FuncDialect>();
41+
registerNVVMTarget(registry);
42+
registerROCDLTarget(registry);
3943
registerAllToLLVMIRTranslations(registry);
4044
});
4145
}

mlir/lib/Target/LLVMIR/Dialect/GPU/GPUToLLVMIRTranslation.cpp

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,52 @@
1212
#include "mlir/Target/LLVMIR/Dialect/GPU/GPUToLLVMIRTranslation.h"
1313
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
1414
#include "mlir/Target/LLVMIR/LLVMTranslationInterface.h"
15+
#include "llvm/ADT/TypeSwitch.h"
1516

1617
using namespace mlir;
1718

1819
namespace {
20+
LogicalResult launchKernel(gpu::LaunchFuncOp launchOp,
21+
llvm::IRBuilderBase &builder,
22+
LLVM::ModuleTranslation &moduleTranslation) {
23+
auto kernelBinary = SymbolTable::lookupNearestSymbolFrom<gpu::BinaryOp>(
24+
launchOp, launchOp.getKernelModuleName());
25+
if (!kernelBinary) {
26+
launchOp.emitError("Couldn't find the binary holding the kernel: ")
27+
<< launchOp.getKernelModuleName();
28+
return failure();
29+
}
30+
auto offloadingHandler =
31+
dyn_cast<gpu::OffloadingLLVMTranslationAttrInterface>(
32+
kernelBinary.getOffloadingHandlerAttr());
33+
assert(offloadingHandler && "Invalid offloading handler.");
34+
return offloadingHandler.launchKernel(launchOp, kernelBinary, builder,
35+
moduleTranslation);
36+
}
1937

2038
class GPUDialectLLVMIRTranslationInterface
2139
: public LLVMTranslationDialectInterface {
2240
public:
2341
using LLVMTranslationDialectInterface::LLVMTranslationDialectInterface;
2442

2543
LogicalResult
26-
convertOperation(Operation *op, llvm::IRBuilderBase &builder,
44+
convertOperation(Operation *operation, llvm::IRBuilderBase &builder,
2745
LLVM::ModuleTranslation &moduleTranslation) const override {
28-
return isa<gpu::GPUModuleOp>(op) ? success() : failure();
46+
return llvm::TypeSwitch<Operation *, LogicalResult>(operation)
47+
.Case([&](gpu::GPUModuleOp) { return success(); })
48+
.Case([&](gpu::BinaryOp op) {
49+
auto offloadingHandler =
50+
dyn_cast<gpu::OffloadingLLVMTranslationAttrInterface>(
51+
op.getOffloadingHandlerAttr());
52+
assert(offloadingHandler && "Invalid offloading handler.");
53+
return offloadingHandler.embedBinary(op, builder, moduleTranslation);
54+
})
55+
.Case([&](gpu::LaunchFuncOp op) {
56+
return launchKernel(op, builder, moduleTranslation);
57+
})
58+
.Default([&](Operation *op) {
59+
return op->emitError("unsupported GPU operation: ") << op->getName();
60+
});
2961
}
3062
};
3163

mlir/test/Target/LLVMIR/gpu.mlir

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// RUN: mlir-translate -mlir-to-llvmir -split-input-file %s | FileCheck %s
2+
3+
// Checking the translation of the `gpu.binary` & `gpu.launch_fun` ops.
4+
module attributes {gpu.container_module} {
5+
// CHECK: [[ARGS_TY:%.*]] = type { i32, i32 }
6+
// CHECK: @kernel_module_bin_cst = internal constant [4 x i8] c"BLOB", align 8
7+
// CHECK: @kernel_module_kernel_kernel_name = private unnamed_addr constant [7 x i8] c"kernel\00", align 1
8+
gpu.binary @kernel_module [#gpu.object<#nvvm.target, "BLOB">]
9+
llvm.func @foo() {
10+
// CHECK: [[ARGS:%.*]] = alloca %{{.*}}, align 8
11+
// CHECK: [[ARGS_ARRAY:%.*]] = alloca ptr, i64 2, align 8
12+
// CHECK: [[ARG0:%.*]] = getelementptr inbounds [[ARGS_TY]], ptr [[ARGS]], i32 0, i32 0
13+
// CHECK: store i32 32, ptr [[ARG0]], align 4
14+
// CHECK: %{{.*}} = getelementptr ptr, ptr [[ARGS_ARRAY]], i32 0
15+
// CHECK: store ptr [[ARG0]], ptr %{{.*}}, align 8
16+
// CHECK: [[ARG1:%.*]] = getelementptr inbounds [[ARGS_TY]], ptr [[ARGS]], i32 0, i32 1
17+
// CHECK: store i32 32, ptr [[ARG1]], align 4
18+
// CHECK: %{{.*}} = getelementptr ptr, ptr [[ARGS_ARRAY]], i32 1
19+
// CHECK: store ptr [[ARG1]], ptr %{{.*}}, align 8
20+
// CHECK: [[MODULE:%.*]] = call ptr @mgpuModuleLoad(ptr @kernel_module_bin_cst)
21+
// CHECK: [[FUNC:%.*]] = call ptr @mgpuModuleGetFunction(ptr [[MODULE]], ptr @kernel_module_kernel_kernel_name)
22+
// CHECK: [[STREAM:%.*]] = call ptr @mgpuStreamCreate()
23+
// CHECK: call void @mgpuLaunchKernel(ptr [[FUNC]], i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i32 256, ptr [[STREAM]], ptr [[ARGS_ARRAY]], ptr null)
24+
// CHECK: call void @mgpuStreamSynchronize(ptr [[STREAM]])
25+
// CHECK: call void @mgpuStreamDestroy(ptr [[STREAM]])
26+
// CHECK: call void @mgpuModuleUnload(ptr [[MODULE]])
27+
%0 = llvm.mlir.constant(8 : index) : i64
28+
%1 = llvm.mlir.constant(32 : i32) : i32
29+
%2 = llvm.mlir.constant(256 : i32) : i32
30+
gpu.launch_func @kernel_module::@kernel blocks in (%0, %0, %0) threads in (%0, %0, %0) : i64 dynamic_shared_memory_size %2 args(%1 : i32, %1 : i32)
31+
llvm.return
32+
}
33+
}
34+
35+
// -----
36+
37+
// Checking the correct selection of the second object using an index as a selector.
38+
module {
39+
// CHECK: @kernel_module_bin_cst = internal constant [1 x i8] c"1", align 8
40+
gpu.binary @kernel_module <#gpu.select_object<1>> [#gpu.object<#nvvm.target, "0">, #gpu.object<#nvvm.target, "1">]
41+
}
42+
43+
// -----
44+
45+
// Checking the correct selection of the second object using a target as a selector.
46+
module {
47+
// CHECK: @kernel_module_bin_cst = internal constant [6 x i8] c"AMDGPU", align 8
48+
gpu.binary @kernel_module <#gpu.select_object<#rocdl.target>> [#gpu.object<#nvvm.target, "NVPTX">, #gpu.object<#rocdl.target, "AMDGPU">]
49+
}
50+
51+
// -----
52+
53+
// Checking the translation of `gpu.launch_fun` with an async dependency.
54+
module attributes {gpu.container_module} {
55+
// CHECK: @kernel_module_bin_cst = internal constant [4 x i8] c"BLOB", align 8
56+
gpu.binary @kernel_module [#gpu.object<#rocdl.target, "BLOB">]
57+
llvm.func @foo() {
58+
%0 = llvm.mlir.constant(8 : index) : i64
59+
// CHECK: = call ptr @mgpuStreamCreate()
60+
// CHECK-NEXT: = alloca {{.*}}, align 8
61+
// CHECK-NEXT: [[ARGS:%.*]] = alloca ptr, i64 0, align 8
62+
// CHECK-NEXT: [[MODULE:%.*]] = call ptr @mgpuModuleLoad(ptr @kernel_module_bin_cst)
63+
// CHECK-NEXT: [[FUNC:%.*]] = call ptr @mgpuModuleGetFunction(ptr [[MODULE]], ptr @kernel_module_kernel_kernel_name)
64+
// CHECK-NEXT: call void @mgpuLaunchKernel(ptr [[FUNC]], i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i32 0, ptr {{.*}}, ptr [[ARGS]], ptr null)
65+
// CHECK-NEXT: call void @mgpuModuleUnload(ptr [[MODULE]])
66+
// CHECK-NEXT: call void @mgpuStreamSynchronize(ptr %{{.*}})
67+
// CHECK-NEXT: call void @mgpuStreamDestroy(ptr %{{.*}})
68+
%1 = llvm.call @mgpuStreamCreate() : () -> !llvm.ptr
69+
gpu.launch_func <%1 : !llvm.ptr> @kernel_module::@kernel blocks in (%0, %0, %0) threads in (%0, %0, %0) : i64
70+
llvm.call @mgpuStreamSynchronize(%1) : (!llvm.ptr) -> ()
71+
llvm.call @mgpuStreamDestroy(%1) : (!llvm.ptr) -> ()
72+
llvm.return
73+
}
74+
llvm.func @mgpuStreamCreate() -> !llvm.ptr
75+
llvm.func @mgpuStreamSynchronize(!llvm.ptr)
76+
llvm.func @mgpuStreamDestroy(!llvm.ptr)
77+
}

0 commit comments

Comments
 (0)