-
Notifications
You must be signed in to change notification settings - Fork 13.9k
[mlir][spirv] Implement gpu::TargetAttrInterface #69949
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1a4319c
54adf40
08b1d2d
aecb8be
f63e385
7f0f119
6a9e268
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -188,4 +188,48 @@ def GpuROCDLAttachTarget: Pass<"rocdl-attach-target", ""> { | |
]; | ||
} | ||
|
||
def GpuSPIRVAttachTarget: Pass<"spirv-attach-target", ""> { | ||
let summary = "Attaches an SPIR-V target attribute to a GPU Module."; | ||
let description = [{ | ||
This pass searches for all GPU Modules in the immediate regions and attaches | ||
an SPIR-V target if the module matches the name specified by the `module` argument. | ||
|
||
Example: | ||
``` | ||
// Given the following file: in1.mlir: | ||
gpu.module @nvvm_module_1 {...} | ||
gpu.module @spirv_module_1 {...} | ||
// With | ||
// mlir-opt --spirv-attach-target="module=spirv.* ver=v1.0 caps=Kernel" in1.mlir | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the following file
With
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
// it will generate, | ||
gpu.module @nvvm_module_1 {...} | ||
gpu.module @spirv_module_1 [#spirv.target<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>] {...} | ||
``` | ||
}]; | ||
let options = [ | ||
Option<"moduleMatcher", "module", "std::string", | ||
/*default=*/ [{""}], | ||
"Regex used to identify the modules to attach the target to.">, | ||
Option<"spirvVersion", "ver", "std::string", | ||
/*default=*/ "\"v1.0\"", | ||
"SPIR-V Version.">, | ||
ListOption<"spirvCapabilities", "caps", "std::string", | ||
"List of supported SPIR-V Capabilities">, | ||
ListOption<"spirvExtensions", "exts", "std::string", | ||
"List of supported SPIR-V Extensions">, | ||
Option<"clientApi", "client_api", "std::string", | ||
/*default=*/ "\"Unknown\"", | ||
"Client API">, | ||
Option<"deviceVendor", "vendor", "std::string", | ||
/*default=*/ "\"Unknown\"", | ||
"Device Vendor">, | ||
Option<"deviceType", "device_type", "std::string", | ||
/*default=*/ "\"Unknown\"", | ||
"Device Type">, | ||
Option<"deviceId", "device_id", "uint32_t", | ||
/*default=*/ "mlir::spirv::TargetEnvAttr::kUnknownDeviceID", | ||
"Device ID">, | ||
]; | ||
} | ||
|
||
#endif // MLIR_DIALECT_GPU_PASSES |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
//===- Target.h - MLIR SPIR-V target registration ---------------*- C++ -*-===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This provides registration calls for attaching the SPIR-V target interface. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef MLIR_TARGET_SPIRV_TARGET_H | ||
#define MLIR_TARGET_SPIRV_TARGET_H | ||
|
||
namespace mlir { | ||
class DialectRegistry; | ||
class MLIRContext; | ||
namespace spirv { | ||
/// Registers the `TargetAttrInterface` for the `#spirv.target_env` attribute in | ||
/// the given registry. | ||
void registerSPIRVTargetInterfaceExternalModels(DialectRegistry ®istry); | ||
|
||
/// Registers the `TargetAttrInterface` for the `#spirv.target_env` attribute in | ||
/// the registry associated with the given context. | ||
void registerSPIRVTargetInterfaceExternalModels(MLIRContext &context); | ||
} // namespace spirv | ||
} // namespace mlir | ||
|
||
#endif // MLIR_TARGET_SPIRV_TARGET_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
//===- SPIRVAttachTarget.cpp - Attach an SPIR-V target --------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This file implements the `GPUSPIRVAttachTarget` pass, attaching | ||
// `#spirv.target_env` attributes to GPU modules. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "mlir/Dialect/GPU/Transforms/Passes.h" | ||
|
||
#include "mlir/Dialect/GPU/IR/GPUDialect.h" | ||
#include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h" | ||
#include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h" | ||
#include "mlir/Dialect/SPIRV/IR/TargetAndABI.h" | ||
#include "mlir/IR/Builders.h" | ||
#include "mlir/Pass/Pass.h" | ||
#include "mlir/Target/SPIRV/Target.h" | ||
#include "llvm/Support/Regex.h" | ||
|
||
namespace mlir { | ||
#define GEN_PASS_DEF_GPUSPIRVATTACHTARGET | ||
#include "mlir/Dialect/GPU/Transforms/Passes.h.inc" | ||
} // namespace mlir | ||
|
||
using namespace mlir; | ||
using namespace mlir::spirv; | ||
|
||
namespace { | ||
struct SPIRVAttachTarget | ||
: public impl::GpuSPIRVAttachTargetBase<SPIRVAttachTarget> { | ||
using Base::Base; | ||
|
||
void runOnOperation() override; | ||
|
||
void getDependentDialects(DialectRegistry ®istry) const override { | ||
registry.insert<spirv::SPIRVDialect>(); | ||
} | ||
}; | ||
} // namespace | ||
|
||
void SPIRVAttachTarget::runOnOperation() { | ||
OpBuilder builder(&getContext()); | ||
auto versionSymbol = symbolizeVersion(spirvVersion); | ||
if (!versionSymbol) | ||
return signalPassFailure(); | ||
auto apiSymbol = symbolizeClientAPI(clientApi); | ||
if (!apiSymbol) | ||
return signalPassFailure(); | ||
auto vendorSymbol = symbolizeVendor(deviceVendor); | ||
if (!vendorSymbol) | ||
return signalPassFailure(); | ||
auto deviceTypeSymbol = symbolizeDeviceType(deviceType); | ||
if (!deviceTypeSymbol) | ||
return signalPassFailure(); | ||
|
||
Version version = versionSymbol.value(); | ||
SmallVector<Capability, 4> capabilities; | ||
SmallVector<Extension, 8> extensions; | ||
for (auto cap : spirvCapabilities) { | ||
auto capSymbol = symbolizeCapability(cap); | ||
if (capSymbol) | ||
capabilities.push_back(capSymbol.value()); | ||
} | ||
ArrayRef<Capability> caps(capabilities); | ||
for (auto ext : spirvExtensions) { | ||
auto extSymbol = symbolizeExtension(ext); | ||
if (extSymbol) | ||
extensions.push_back(extSymbol.value()); | ||
} | ||
ArrayRef<Extension> exts(extensions); | ||
VerCapExtAttr vce = VerCapExtAttr::get(version, caps, exts, &getContext()); | ||
auto target = TargetEnvAttr::get(vce, getDefaultResourceLimits(&getContext()), | ||
apiSymbol.value(), vendorSymbol.value(), | ||
deviceTypeSymbol.value(), deviceId); | ||
llvm::Regex matcher(moduleMatcher); | ||
getOperation()->walk([&](gpu::GPUModuleOp gpuModule) { | ||
// Check if the name of the module matches. | ||
if (!moduleMatcher.empty() && !matcher.match(gpuModule.getName())) | ||
return; | ||
// Create the target array. | ||
SmallVector<Attribute> targets; | ||
if (std::optional<ArrayAttr> attrs = gpuModule.getTargets()) | ||
targets.append(attrs->getValue().begin(), attrs->getValue().end()); | ||
targets.push_back(target); | ||
// Remove any duplicate targets. | ||
targets.erase(std::unique(targets.begin(), targets.end()), targets.end()); | ||
// Update the target attribute array. | ||
gpuModule.setTargetsAttr(builder.getArrayAttr(targets)); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
//===- Target.cpp - MLIR SPIR-V target compilation --------------*- C++ -*-===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This files defines SPIR-V target related functions including registration | ||
// calls for the `#spirv.target_env` compilation attribute. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "mlir/Target/SPIRV/Target.h" | ||
|
||
#include "mlir/Dialect/GPU/IR/GPUDialect.h" | ||
#include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h" | ||
#include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h" | ||
#include "mlir/Dialect/SPIRV/IR/SPIRVOps.h" | ||
#include "mlir/Target/LLVMIR/Dialect/GPU/GPUToLLVMIRTranslation.h" | ||
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h" | ||
#include "mlir/Target/LLVMIR/Export.h" | ||
#include "mlir/Target/SPIRV/Serialization.h" | ||
|
||
#include "llvm/Support/FileSystem.h" | ||
#include "llvm/Support/FileUtilities.h" | ||
#include "llvm/Support/FormatVariadic.h" | ||
#include "llvm/Support/MemoryBuffer.h" | ||
#include "llvm/Support/Path.h" | ||
#include "llvm/Support/Process.h" | ||
#include "llvm/Support/Program.h" | ||
#include "llvm/Support/TargetSelect.h" | ||
|
||
#include <cstdlib> | ||
#include <cstring> | ||
|
||
using namespace mlir; | ||
using namespace mlir::spirv; | ||
|
||
namespace { | ||
// SPIR-V implementation of the gpu:TargetAttrInterface. | ||
class SPIRVTargetAttrImpl | ||
: public gpu::TargetAttrInterface::FallbackModel<SPIRVTargetAttrImpl> { | ||
public: | ||
std::optional<SmallVector<char, 0>> | ||
serializeToObject(Attribute attribute, Operation *module, | ||
const gpu::TargetOptions &options) const; | ||
|
||
Attribute createObject(Attribute attribute, | ||
const SmallVector<char, 0> &object, | ||
const gpu::TargetOptions &options) const; | ||
}; | ||
} // namespace | ||
|
||
// Register the SPIR-V dialect, the SPIR-V translation & the target interface. | ||
void mlir::spirv::registerSPIRVTargetInterfaceExternalModels( | ||
DialectRegistry ®istry) { | ||
registry.addExtension(+[](MLIRContext *ctx, spirv::SPIRVDialect *dialect) { | ||
spirv::TargetEnvAttr::attachInterface<SPIRVTargetAttrImpl>(*ctx); | ||
}); | ||
} | ||
|
||
void mlir::spirv::registerSPIRVTargetInterfaceExternalModels( | ||
MLIRContext &context) { | ||
DialectRegistry registry; | ||
registerSPIRVTargetInterfaceExternalModels(registry); | ||
context.appendDialectRegistry(registry); | ||
} | ||
|
||
// Reuse from existing serializer | ||
std::optional<SmallVector<char, 0>> SPIRVTargetAttrImpl::serializeToObject( | ||
Attribute attribute, Operation *module, | ||
const gpu::TargetOptions &options) const { | ||
if (!module) | ||
return std::nullopt; | ||
auto gpuMod = dyn_cast<gpu::GPUModuleOp>(module); | ||
if (!gpuMod) { | ||
module->emitError("expected to be a gpu.module op"); | ||
return std::nullopt; | ||
} | ||
auto spvMods = gpuMod.getOps<spirv::ModuleOp>(); | ||
if (spvMods.empty()) | ||
return std::nullopt; | ||
|
||
auto spvMod = *spvMods.begin(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A check is necessary before There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added check for empty range. |
||
llvm::SmallVector<uint32_t, 0> spvBinary; | ||
|
||
spvBinary.clear(); | ||
// Serialize the spirv.module op to SPIR-V blob. | ||
if (mlir::failed(spirv::serialize(spvMod, spvBinary))) { | ||
spvMod.emitError() << "failed to serialize SPIR-V module"; | ||
return std::nullopt; | ||
} | ||
|
||
SmallVector<char, 0> spvData(spvBinary.size() * sizeof(uint32_t), 0); | ||
std::memcpy(spvData.data(), spvBinary.data(), spvData.size()); | ||
|
||
spvMod.erase(); | ||
return spvData; | ||
} | ||
|
||
// Prepare Attribute for gpu.binary with serialized kernel object | ||
Attribute | ||
SPIRVTargetAttrImpl::createObject(Attribute attribute, | ||
const SmallVector<char, 0> &object, | ||
const gpu::TargetOptions &options) const { | ||
gpu::CompilationTarget format = options.getCompilationTarget(); | ||
DictionaryAttr objectProps; | ||
Builder builder(attribute.getContext()); | ||
return builder.getAttr<gpu::ObjectAttr>( | ||
attribute, format, | ||
builder.getStringAttr(StringRef(object.data(), object.size())), | ||
objectProps); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels off: you have GPU dialect transforms depending on SPIRV attributes/dialect (esp in header). Why is this pass in GPU dialect rather than SPIRV one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Back when the compilation redesign was happening we decided to add the attach* passes in GPU to avoid polluting lower level dialects with GPU includes. However, I do agree that include shouldn't be there, as far as I could tell, that include is only needed by one pass option
mlir::spirv::TargetEnvAttr::kUnknownDeviceID
, so it should be possible to remove it. I'll fix it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SG. Thanks @jpienaar & @fabianmcg!