Skip to content

Commit fdfbd1e

Browse files
committed
[GPU] Implement create and serialize methods of XeVMTargetAttrImpl.
1 parent 8f289df commit fdfbd1e

File tree

2 files changed

+183
-6
lines changed

2 files changed

+183
-6
lines changed

include/gc/Target/LLVM/XeVM/Utils.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===-- Utils.h - MLIR XeVM target utils ------------------------*- C++ -*-===//
2+
//
3+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This files declares XeVM target related utility classes and functions.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef MLIR_TARGET_LLVM_XEVM_UTILS_H
14+
#define MLIR_TARGET_LLVM_XEVM_UTILS_H
15+
16+
#include "gc/Dialect/LLVMIR/XeVMDialect.h"
17+
#include "mlir/Dialect/GPU/IR/CompilationInterfaces.h"
18+
#include "mlir/Target/LLVM/ModuleToObject.h"
19+
20+
namespace mlir {
21+
namespace xevm {
22+
23+
/// Base class for all XeVM serializations from GPU modules into binary strings.
24+
/// By default this class serializes into LLVM bitcode.
25+
class SerializeGPUModuleBase : public LLVM::ModuleToObject {
26+
public:
27+
SerializeGPUModuleBase(Operation &module, XeVMTargetAttr target,
28+
const gpu::TargetOptions &targetOptions = {});
29+
30+
static void init();
31+
XeVMTargetAttr getTarget() const;
32+
33+
protected:
34+
XeVMTargetAttr target;
35+
};
36+
} // namespace xevm
37+
} // namespace mlir
38+
39+
#endif // MLIR_TARGET_LLVM_XEVM_UTILS_H

lib/gc/Target/LLVM/XeVM/Target.cpp

Lines changed: 144 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "gc/Target/LLVM/XeVM/Target.h"
1515

1616
#include "gc/Dialect/LLVMIR/XeVMDialect.h"
17+
#include "gc/Target/LLVM/XeVM/Utils.h"
1718
#include "mlir/Dialect/GPU/IR/CompilationInterfaces.h"
1819
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
1920
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
@@ -36,15 +37,11 @@ class XeVMTargetAttrImpl
3637
public:
3738
std::optional<SmallVector<char, 0>>
3839
serializeToObject(Attribute attribute, Operation *module,
39-
const gpu::TargetOptions &options) const { /*TODO*/
40-
return {};
41-
}
40+
const gpu::TargetOptions &options) const;
4241

4342
Attribute createObject(Attribute attribute, Operation *module,
4443
const SmallVector<char, 0> &object,
45-
const gpu::TargetOptions &options) const { /*TODO*/
46-
return {};
47-
}
44+
const gpu::TargetOptions &options) const;
4845
};
4946
} // namespace
5047

@@ -61,3 +58,144 @@ void mlir::xevm::registerXeVMTargetInterfaceExternalModels(
6158
registerXeVMTargetInterfaceExternalModels(registry);
6259
context.appendDialectRegistry(registry);
6360
}
61+
62+
SerializeGPUModuleBase::SerializeGPUModuleBase(
63+
Operation &module, XeVMTargetAttr target,
64+
const gpu::TargetOptions &targetOptions)
65+
: ModuleToObject(module, target.getTriple(), target.getChip(), {},
66+
target.getO()),
67+
target(target) {}
68+
69+
void SerializeGPUModuleBase::init() {
70+
static llvm::once_flag initializeBackendOnce;
71+
llvm::call_once(initializeBackendOnce, []() {
72+
#if LLVM_HAS_SPIRV_TARGET
73+
LLVMInitializeSPIRVTarget();
74+
LLVMInitializeSPIRVTargetInfo();
75+
LLVMInitializeSPIRVTargetMC();
76+
LLVMInitializeSPIRVAsmPrinter();
77+
#endif
78+
});
79+
}
80+
81+
XeVMTargetAttr SerializeGPUModuleBase::getTarget() const { return target; }
82+
83+
namespace {
84+
class SpirSerializer : public SerializeGPUModuleBase {
85+
public:
86+
SpirSerializer(Operation &module, XeVMTargetAttr target,
87+
const gpu::TargetOptions &targetOptions)
88+
: SerializeGPUModuleBase(module, target, targetOptions) {}
89+
90+
gpu::GPUModuleOp getOperation();
91+
92+
std::optional<SmallVector<char, 0>>
93+
moduleToObject(llvm::Module &llvmModule) override;
94+
95+
private:
96+
std::optional<std::string>
97+
translateToSPIRVBinary(llvm::Module &llvmModule,
98+
llvm::TargetMachine &targetMachine);
99+
gpu::TargetOptions targetOptions;
100+
};
101+
} // namespace
102+
103+
gpu::GPUModuleOp SpirSerializer::getOperation() {
104+
return dyn_cast<gpu::GPUModuleOp>(&SerializeGPUModuleBase::getOperation());
105+
}
106+
107+
std::optional<SmallVector<char, 0>>
108+
SpirSerializer::moduleToObject(llvm::Module &llvmModule) {
109+
// Return LLVM IR if the compilation target is `offload`.
110+
if (targetOptions.getCompilationTarget() == gpu::CompilationTarget::Offload)
111+
return SerializeGPUModuleBase::moduleToObject(llvmModule);
112+
113+
#if !LLVM_HAS_SPIRV_TARGET
114+
getOperation()->emitError(
115+
"The `SPIRV` target was not built. Please enable it when building LLVM.");
116+
return std::nullopt;
117+
#endif // LLVM_HAS_SPIRV_TARGET
118+
119+
std::optional<llvm::TargetMachine *> targetMachine =
120+
getOrCreateTargetMachine();
121+
if (!targetMachine) {
122+
getOperation().emitError() << "Target Machine unavailable for triple "
123+
<< triple << ", can't compile with LLVM\n";
124+
return std::nullopt;
125+
}
126+
127+
// Return SPIRV if the compilation target is `assembly`.
128+
if (targetOptions.getCompilationTarget() ==
129+
gpu::CompilationTarget::Assembly) {
130+
std::optional<std::string> serializedISA =
131+
translateToISA(llvmModule, **targetMachine);
132+
if (!serializedISA) {
133+
getOperation().emitError() << "Failed translating the module to ISA.";
134+
return std::nullopt;
135+
}
136+
// Make sure to include the null terminator.
137+
StringRef bin(serializedISA->c_str(), serializedISA->size() + 1);
138+
return SmallVector<char, 0>(bin.begin(), bin.end());
139+
}
140+
141+
std::optional<std::string> serializedSPIRVBinary =
142+
translateToSPIRVBinary(llvmModule, **targetMachine);
143+
if (!serializedSPIRVBinary) {
144+
getOperation().emitError() << "Failed translating the module to Binary.";
145+
return std::nullopt;
146+
}
147+
148+
StringRef bin(serializedSPIRVBinary->c_str(),
149+
serializedSPIRVBinary->size() + 1);
150+
return SmallVector<char, 0>(bin.begin(), bin.end());
151+
}
152+
153+
std::optional<std::string>
154+
SpirSerializer::translateToSPIRVBinary(llvm::Module &llvmModule,
155+
llvm::TargetMachine &targetMachine) {
156+
std::string targetISA;
157+
llvm::raw_string_ostream stream(targetISA);
158+
159+
{ // Drop pstream after this to prevent the ISA from being stuck buffering
160+
llvm::buffer_ostream pstream(stream);
161+
llvm::legacy::PassManager codegenPasses;
162+
163+
if (targetMachine.addPassesToEmitFile(codegenPasses, pstream, nullptr,
164+
llvm::CodeGenFileType::ObjectFile))
165+
return std::nullopt;
166+
167+
codegenPasses.run(llvmModule);
168+
}
169+
return stream.str();
170+
}
171+
172+
std::optional<SmallVector<char, 0>>
173+
XeVMTargetAttrImpl::serializeToObject(Attribute attribute, Operation *module,
174+
const gpu::TargetOptions &options) const {
175+
if (!module)
176+
return std::nullopt;
177+
auto gpuMod = dyn_cast<gpu::GPUModuleOp>(module);
178+
if (!gpuMod) {
179+
module->emitError("expected to be a gpu.module op");
180+
return std::nullopt;
181+
}
182+
183+
// TODO: reroute to another serializer for a different target?
184+
SpirSerializer serializer(*module, cast<XeVMTargetAttr>(attribute), options);
185+
serializer.init();
186+
187+
return serializer.run();
188+
}
189+
190+
Attribute
191+
XeVMTargetAttrImpl::createObject(Attribute attribute, Operation *module,
192+
const SmallVector<char, 0> &object,
193+
const gpu::TargetOptions &options) const {
194+
gpu::CompilationTarget format = options.getCompilationTarget();
195+
DictionaryAttr objectProps;
196+
Builder builder(attribute.getContext());
197+
return builder.getAttr<gpu::ObjectAttr>(
198+
attribute, format,
199+
builder.getStringAttr(StringRef(object.data(), object.size())),
200+
objectProps, /*kernels=*/nullptr);
201+
}

0 commit comments

Comments
 (0)