11090a830SDenis Khalikov //===- ConvertGPULaunchFuncToVulkanLaunchFunc.cpp - MLIR conversion pass --===// 21090a830SDenis Khalikov // 31090a830SDenis Khalikov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 41090a830SDenis Khalikov // See https://llvm.org/LICENSE.txt for license information. 51090a830SDenis Khalikov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 61090a830SDenis Khalikov // 71090a830SDenis Khalikov //===----------------------------------------------------------------------===// 81090a830SDenis Khalikov // 91090a830SDenis Khalikov // This file implements a pass to convert gpu launch function into a vulkan 101090a830SDenis Khalikov // launch function. Creates a SPIR-V binary shader from the `spirv::ModuleOp` 111090a830SDenis Khalikov // using `spirv::serialize` function, attaches binary data and entry point name 121090a830SDenis Khalikov // as an attributes to vulkan launch call op. 131090a830SDenis Khalikov // 141090a830SDenis Khalikov //===----------------------------------------------------------------------===// 151090a830SDenis Khalikov 161090a830SDenis Khalikov #include "mlir/Conversion/GPUToVulkan/ConvertGPUToVulkanPass.h" 171090a830SDenis Khalikov #include "mlir/Dialect/GPU/GPUDialect.h" 181090a830SDenis Khalikov #include "mlir/Dialect/SPIRV/SPIRVOps.h" 191090a830SDenis Khalikov #include "mlir/Dialect/SPIRV/Serialization.h" 201090a830SDenis Khalikov #include "mlir/Dialect/StandardOps/IR/Ops.h" 211090a830SDenis Khalikov #include "mlir/IR/Attributes.h" 221090a830SDenis Khalikov #include "mlir/IR/Builders.h" 231090a830SDenis Khalikov #include "mlir/IR/Function.h" 241090a830SDenis Khalikov #include "mlir/IR/Module.h" 251090a830SDenis Khalikov #include "mlir/IR/StandardTypes.h" 261090a830SDenis Khalikov #include "mlir/Pass/Pass.h" 271090a830SDenis Khalikov 281090a830SDenis Khalikov using namespace mlir; 291090a830SDenis Khalikov 301090a830SDenis Khalikov static constexpr const char *kSPIRVBlobAttrName = "spirv_blob"; 311090a830SDenis Khalikov static constexpr const char *kSPIRVEntryPointAttrName = "spirv_entry_point"; 321090a830SDenis Khalikov static constexpr const char *kVulkanLaunch = "vulkanLaunch"; 331090a830SDenis Khalikov 341090a830SDenis Khalikov namespace { 351090a830SDenis Khalikov 36bfb2ce02SDenis Khalikov /// A pass to convert gpu launch op to vulkan launch call op, by creating a 37bfb2ce02SDenis Khalikov /// SPIR-V binary shader from `spirv::ModuleOp` using `spirv::serialize` 38bfb2ce02SDenis Khalikov /// function and attaching binary data and entry point name as an attributes to 39bfb2ce02SDenis Khalikov /// created vulkan launch call op. 401090a830SDenis Khalikov class ConvertGpuLaunchFuncToVulkanLaunchFunc 41*80aca1eaSRiver Riddle : public PassWrapper<ConvertGpuLaunchFuncToVulkanLaunchFunc, 42*80aca1eaSRiver Riddle OperationPass<ModuleOp>> { 431090a830SDenis Khalikov public: 449a277af2SRiver Riddle /// Include the generated pass utilities. 459a277af2SRiver Riddle #define GEN_PASS_ConvertGpuLaunchFuncToVulkanLaunchFunc 469a277af2SRiver Riddle #include "mlir/Conversion/Passes.h.inc" 479a277af2SRiver Riddle 48722f909fSRiver Riddle void runOnOperation() override; 491090a830SDenis Khalikov 501090a830SDenis Khalikov private: 511090a830SDenis Khalikov /// Creates a SPIR-V binary shader from the given `module` using 521090a830SDenis Khalikov /// `spirv::serialize` function. 531090a830SDenis Khalikov LogicalResult createBinaryShader(ModuleOp module, 541090a830SDenis Khalikov std::vector<char> &binaryShader); 551090a830SDenis Khalikov 56e5a85126SKazuaki Ishizaki /// Converts the given `launchOp` to vulkan launch call. 571090a830SDenis Khalikov void convertGpuLaunchFunc(gpu::LaunchFuncOp launchOp); 581090a830SDenis Khalikov 591090a830SDenis Khalikov /// Checks where the given type is supported by Vulkan runtime. 601090a830SDenis Khalikov bool isSupportedType(Type type) { 611090a830SDenis Khalikov // TODO(denis0x0D): Handle other types. 621090a830SDenis Khalikov if (auto memRefType = type.dyn_cast_or_null<MemRefType>()) 638f4ab8c7SDenis Khalikov return memRefType.hasRank() && 640718e3aeSDenis Khalikov (memRefType.getRank() >= 1 && memRefType.getRank() <= 3); 651090a830SDenis Khalikov return false; 661090a830SDenis Khalikov } 671090a830SDenis Khalikov 681090a830SDenis Khalikov /// Declares the vulkan launch function. Returns an error if the any type of 691090a830SDenis Khalikov /// operand is unsupported by Vulkan runtime. 701090a830SDenis Khalikov LogicalResult declareVulkanLaunchFunc(Location loc, 711090a830SDenis Khalikov gpu::LaunchFuncOp launchOp); 721090a830SDenis Khalikov }; 731090a830SDenis Khalikov 741090a830SDenis Khalikov } // anonymous namespace 751090a830SDenis Khalikov 76722f909fSRiver Riddle void ConvertGpuLaunchFuncToVulkanLaunchFunc::runOnOperation() { 771090a830SDenis Khalikov bool done = false; 78722f909fSRiver Riddle getOperation().walk([this, &done](gpu::LaunchFuncOp op) { 791090a830SDenis Khalikov if (done) { 801090a830SDenis Khalikov op.emitError("should only contain one 'gpu::LaunchFuncOp' op"); 811090a830SDenis Khalikov return signalPassFailure(); 821090a830SDenis Khalikov } 831090a830SDenis Khalikov done = true; 841090a830SDenis Khalikov convertGpuLaunchFunc(op); 851090a830SDenis Khalikov }); 861090a830SDenis Khalikov 871090a830SDenis Khalikov // Erase `gpu::GPUModuleOp` and `spirv::Module` operations. 881090a830SDenis Khalikov for (auto gpuModule : 89722f909fSRiver Riddle llvm::make_early_inc_range(getOperation().getOps<gpu::GPUModuleOp>())) 901090a830SDenis Khalikov gpuModule.erase(); 911090a830SDenis Khalikov 921090a830SDenis Khalikov for (auto spirvModule : 93722f909fSRiver Riddle llvm::make_early_inc_range(getOperation().getOps<spirv::ModuleOp>())) 941090a830SDenis Khalikov spirvModule.erase(); 951090a830SDenis Khalikov } 961090a830SDenis Khalikov 971090a830SDenis Khalikov LogicalResult ConvertGpuLaunchFuncToVulkanLaunchFunc::declareVulkanLaunchFunc( 981090a830SDenis Khalikov Location loc, gpu::LaunchFuncOp launchOp) { 99722f909fSRiver Riddle OpBuilder builder(getOperation().getBody()->getTerminator()); 1001090a830SDenis Khalikov // TODO: Workgroup size is written into the kernel. So to properly modelling 1011090a830SDenis Khalikov // vulkan launch, we cannot have the local workgroup size configuration here. 1021090a830SDenis Khalikov SmallVector<Type, 8> vulkanLaunchTypes{launchOp.getOperandTypes()}; 1031090a830SDenis Khalikov 1041090a830SDenis Khalikov // Check that all operands have supported types except those for the launch 1051090a830SDenis Khalikov // configuration. 1068f4ab8c7SDenis Khalikov for (auto type : 1078f4ab8c7SDenis Khalikov llvm::drop_begin(vulkanLaunchTypes, gpu::LaunchOp::kNumConfigOperands)) { 1081090a830SDenis Khalikov if (!isSupportedType(type)) 1091090a830SDenis Khalikov return launchOp.emitError() << type << " is unsupported to run on Vulkan"; 1101090a830SDenis Khalikov } 1111090a830SDenis Khalikov 1121090a830SDenis Khalikov // Declare vulkan launch function. 1131090a830SDenis Khalikov builder.create<FuncOp>( 1141090a830SDenis Khalikov loc, kVulkanLaunch, 1151090a830SDenis Khalikov FunctionType::get(vulkanLaunchTypes, ArrayRef<Type>{}, loc->getContext()), 1161090a830SDenis Khalikov ArrayRef<NamedAttribute>{}); 1171090a830SDenis Khalikov 1181090a830SDenis Khalikov return success(); 1191090a830SDenis Khalikov } 1201090a830SDenis Khalikov 1211090a830SDenis Khalikov LogicalResult ConvertGpuLaunchFuncToVulkanLaunchFunc::createBinaryShader( 1221090a830SDenis Khalikov ModuleOp module, std::vector<char> &binaryShader) { 1231090a830SDenis Khalikov bool done = false; 1241090a830SDenis Khalikov SmallVector<uint32_t, 0> binary; 1251090a830SDenis Khalikov for (auto spirvModule : module.getOps<spirv::ModuleOp>()) { 1261090a830SDenis Khalikov if (done) 1271090a830SDenis Khalikov return spirvModule.emitError("should only contain one 'spv.module' op"); 1281090a830SDenis Khalikov done = true; 1291090a830SDenis Khalikov 1301090a830SDenis Khalikov if (failed(spirv::serialize(spirvModule, binary))) 1311090a830SDenis Khalikov return failure(); 1321090a830SDenis Khalikov } 1331090a830SDenis Khalikov binaryShader.resize(binary.size() * sizeof(uint32_t)); 1341090a830SDenis Khalikov std::memcpy(binaryShader.data(), reinterpret_cast<char *>(binary.data()), 1351090a830SDenis Khalikov binaryShader.size()); 1361090a830SDenis Khalikov return success(); 1371090a830SDenis Khalikov } 1381090a830SDenis Khalikov 1391090a830SDenis Khalikov void ConvertGpuLaunchFuncToVulkanLaunchFunc::convertGpuLaunchFunc( 1401090a830SDenis Khalikov gpu::LaunchFuncOp launchOp) { 141722f909fSRiver Riddle ModuleOp module = getOperation(); 1421090a830SDenis Khalikov OpBuilder builder(launchOp); 1431090a830SDenis Khalikov Location loc = launchOp.getLoc(); 1441090a830SDenis Khalikov 1451090a830SDenis Khalikov // Serialize `spirv::Module` into binary form. 1461090a830SDenis Khalikov std::vector<char> binary; 1471090a830SDenis Khalikov if (failed(createBinaryShader(module, binary))) 1481090a830SDenis Khalikov return signalPassFailure(); 1491090a830SDenis Khalikov 1501090a830SDenis Khalikov // Declare vulkan launch function. 1511090a830SDenis Khalikov if (failed(declareVulkanLaunchFunc(loc, launchOp))) 1521090a830SDenis Khalikov return signalPassFailure(); 1531090a830SDenis Khalikov 1541090a830SDenis Khalikov // Create vulkan launch call op. 1551090a830SDenis Khalikov auto vulkanLaunchCallOp = builder.create<CallOp>( 1561090a830SDenis Khalikov loc, ArrayRef<Type>{}, builder.getSymbolRefAttr(kVulkanLaunch), 1571090a830SDenis Khalikov launchOp.getOperands()); 1581090a830SDenis Khalikov 1591090a830SDenis Khalikov // Set SPIR-V binary shader data as an attribute. 1601090a830SDenis Khalikov vulkanLaunchCallOp.setAttr( 1611090a830SDenis Khalikov kSPIRVBlobAttrName, 1621090a830SDenis Khalikov StringAttr::get({binary.data(), binary.size()}, loc->getContext())); 1631090a830SDenis Khalikov 1641090a830SDenis Khalikov // Set entry point name as an attribute. 1651090a830SDenis Khalikov vulkanLaunchCallOp.setAttr( 1661090a830SDenis Khalikov kSPIRVEntryPointAttrName, 1671090a830SDenis Khalikov StringAttr::get(launchOp.kernel(), loc->getContext())); 1681090a830SDenis Khalikov 1691090a830SDenis Khalikov launchOp.erase(); 1701090a830SDenis Khalikov } 1711090a830SDenis Khalikov 172*80aca1eaSRiver Riddle std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>> 1731090a830SDenis Khalikov mlir::createConvertGpuLaunchFuncToVulkanLaunchFuncPass() { 1741090a830SDenis Khalikov return std::make_unique<ConvertGpuLaunchFuncToVulkanLaunchFunc>(); 1751090a830SDenis Khalikov } 176