1a825fb2cSChristian Sigg //===- LowerGPUToHSACO.cpp - Convert GPU kernel to HSACO blob -------------===//
2a825fb2cSChristian Sigg //
3a825fb2cSChristian Sigg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a825fb2cSChristian Sigg // See https://llvm.org/LICENSE.txt for license information.
5a825fb2cSChristian Sigg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a825fb2cSChristian Sigg //
7a825fb2cSChristian Sigg //===----------------------------------------------------------------------===//
8a825fb2cSChristian Sigg //
9a825fb2cSChristian Sigg // This file implements a pass that serializes a gpu module into HSAco blob and
10a825fb2cSChristian Sigg // adds that blob as a string attribute of the module.
11a825fb2cSChristian Sigg //
12a825fb2cSChristian Sigg //===----------------------------------------------------------------------===//
13d7ef488bSMogball 
14d7ef488bSMogball #include "mlir/Dialect/GPU/Transforms/Passes.h"
15f849640aSKrzysztof Drewniak #include "mlir/IR/Location.h"
16f849640aSKrzysztof Drewniak #include "mlir/IR/MLIRContext.h"
17a825fb2cSChristian Sigg 
18a825fb2cSChristian Sigg #if MLIR_GPU_TO_HSACO_PASS_ENABLE
19bd22554aSKrzysztof Drewniak #include "mlir/ExecutionEngine/OptUtils.h"
20a825fb2cSChristian Sigg #include "mlir/Pass/Pass.h"
21a825fb2cSChristian Sigg #include "mlir/Support/FileUtilities.h"
22a825fb2cSChristian Sigg #include "mlir/Target/LLVMIR/Dialect/ROCDL/ROCDLToLLVMIRTranslation.h"
23a825fb2cSChristian Sigg #include "mlir/Target/LLVMIR/Export.h"
24a825fb2cSChristian Sigg 
25a6f53afbSKrzysztof Drewniak #include "llvm/IR/Constants.h"
26a6f53afbSKrzysztof Drewniak #include "llvm/IR/GlobalVariable.h"
27a6f53afbSKrzysztof Drewniak #include "llvm/IR/Module.h"
28a6f53afbSKrzysztof Drewniak #include "llvm/IRReader/IRReader.h"
29a6f53afbSKrzysztof Drewniak #include "llvm/Linker/Linker.h"
30a6f53afbSKrzysztof Drewniak 
31a825fb2cSChristian Sigg #include "llvm/MC/MCAsmBackend.h"
32a825fb2cSChristian Sigg #include "llvm/MC/MCAsmInfo.h"
33a825fb2cSChristian Sigg #include "llvm/MC/MCCodeEmitter.h"
34a825fb2cSChristian Sigg #include "llvm/MC/MCContext.h"
351aa71944SKrzysztof Drewniak #include "llvm/MC/MCInstrInfo.h"
36a825fb2cSChristian Sigg #include "llvm/MC/MCObjectFileInfo.h"
37a825fb2cSChristian Sigg #include "llvm/MC/MCObjectWriter.h"
38a825fb2cSChristian Sigg #include "llvm/MC/MCParser/MCTargetAsmParser.h"
39c37b3e41SKrzysztof Drewniak #include "llvm/MC/MCRegisterInfo.h"
40a825fb2cSChristian Sigg #include "llvm/MC/MCStreamer.h"
41a825fb2cSChristian Sigg #include "llvm/MC/MCSubtargetInfo.h"
4289b57061SReid Kleckner #include "llvm/MC/TargetRegistry.h"
4320f79f8cSKrzysztof Drewniak 
4420f79f8cSKrzysztof Drewniak #include "llvm/Support/CommandLine.h"
45a825fb2cSChristian Sigg #include "llvm/Support/FileUtilities.h"
46e7d0dae7SKrzysztof Drewniak #include "llvm/Support/Path.h"
47a825fb2cSChristian Sigg #include "llvm/Support/Program.h"
48f849640aSKrzysztof Drewniak #include "llvm/Support/SourceMgr.h"
49a825fb2cSChristian Sigg #include "llvm/Support/TargetSelect.h"
50a825fb2cSChristian Sigg #include "llvm/Support/WithColor.h"
51f849640aSKrzysztof Drewniak 
52f849640aSKrzysztof Drewniak #include "llvm/Target/TargetMachine.h"
53a825fb2cSChristian Sigg #include "llvm/Target/TargetOptions.h"
54a825fb2cSChristian Sigg 
55a6f53afbSKrzysztof Drewniak #include "llvm/Transforms/IPO/Internalize.h"
56a6f53afbSKrzysztof Drewniak 
57a825fb2cSChristian Sigg #include <mutex>
58a825fb2cSChristian Sigg 
59a825fb2cSChristian Sigg using namespace mlir;
60a825fb2cSChristian Sigg 
61a825fb2cSChristian Sigg namespace {
62a825fb2cSChristian Sigg class SerializeToHsacoPass
63a825fb2cSChristian Sigg     : public PassWrapper<SerializeToHsacoPass, gpu::SerializeToBlobPass> {
64a825fb2cSChristian Sigg public:
655e50dd04SRiver Riddle   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(SerializeToHsacoPass)
665e50dd04SRiver Riddle 
67bd22554aSKrzysztof Drewniak   SerializeToHsacoPass(StringRef triple, StringRef arch, StringRef features,
68bd22554aSKrzysztof Drewniak                        int optLevel);
69bd22554aSKrzysztof Drewniak   SerializeToHsacoPass(const SerializeToHsacoPass &other);
getArgument() const70b5e22e6dSMehdi Amini   StringRef getArgument() const override { return "gpu-to-hsaco"; }
getDescription() const71b5e22e6dSMehdi Amini   StringRef getDescription() const override {
72b5e22e6dSMehdi Amini     return "Lower GPU kernel function to HSACO binary annotations";
73b5e22e6dSMehdi Amini   }
74b5e22e6dSMehdi Amini 
75bd22554aSKrzysztof Drewniak protected:
76bd22554aSKrzysztof Drewniak   Option<int> optLevel{
77bd22554aSKrzysztof Drewniak       *this, "opt-level",
78bd22554aSKrzysztof Drewniak       llvm::cl::desc("Optimization level for HSACO compilation"),
79bd22554aSKrzysztof Drewniak       llvm::cl::init(2)};
80bd22554aSKrzysztof Drewniak 
8120f79f8cSKrzysztof Drewniak   Option<std::string> rocmPath{*this, "rocm-path",
8220f79f8cSKrzysztof Drewniak                                llvm::cl::desc("Path to ROCm install")};
8320f79f8cSKrzysztof Drewniak 
84a6f53afbSKrzysztof Drewniak   // Overload to allow linking in device libs
85a6f53afbSKrzysztof Drewniak   std::unique_ptr<llvm::Module>
86a6f53afbSKrzysztof Drewniak   translateToLLVMIR(llvm::LLVMContext &llvmContext) override;
87a6f53afbSKrzysztof Drewniak 
88bd22554aSKrzysztof Drewniak   /// Adds LLVM optimization passes
89bd22554aSKrzysztof Drewniak   LogicalResult optimizeLlvm(llvm::Module &llvmModule,
90bd22554aSKrzysztof Drewniak                              llvm::TargetMachine &targetMachine) override;
91bd22554aSKrzysztof Drewniak 
92a825fb2cSChristian Sigg private:
93a825fb2cSChristian Sigg   void getDependentDialects(DialectRegistry &registry) const override;
94a825fb2cSChristian Sigg 
95a6f53afbSKrzysztof Drewniak   // Loads LLVM bitcode libraries
96a6f53afbSKrzysztof Drewniak   Optional<SmallVector<std::unique_ptr<llvm::Module>, 3>>
97a6f53afbSKrzysztof Drewniak   loadLibraries(SmallVectorImpl<char> &path,
98a6f53afbSKrzysztof Drewniak                 SmallVectorImpl<StringRef> &libraries,
99a6f53afbSKrzysztof Drewniak                 llvm::LLVMContext &context);
100a6f53afbSKrzysztof Drewniak 
101a825fb2cSChristian Sigg   // Serializes ROCDL to HSACO.
102a825fb2cSChristian Sigg   std::unique_ptr<std::vector<char>>
103a825fb2cSChristian Sigg   serializeISA(const std::string &isa) override;
104a825fb2cSChristian Sigg 
105a825fb2cSChristian Sigg   std::unique_ptr<SmallVectorImpl<char>> assembleIsa(const std::string &isa);
106a825fb2cSChristian Sigg   std::unique_ptr<std::vector<char>>
107a825fb2cSChristian Sigg   createHsaco(const SmallVectorImpl<char> &isaBinary);
10820f79f8cSKrzysztof Drewniak 
10920f79f8cSKrzysztof Drewniak   std::string getRocmPath();
110a825fb2cSChristian Sigg };
111be0a7e9fSMehdi Amini } // namespace
112a825fb2cSChristian Sigg 
SerializeToHsacoPass(const SerializeToHsacoPass & other)113bd22554aSKrzysztof Drewniak SerializeToHsacoPass::SerializeToHsacoPass(const SerializeToHsacoPass &other)
114bd22554aSKrzysztof Drewniak     : PassWrapper<SerializeToHsacoPass, gpu::SerializeToBlobPass>(other) {}
115a825fb2cSChristian Sigg 
11620f79f8cSKrzysztof Drewniak /// Get a user-specified path to ROCm
11720f79f8cSKrzysztof Drewniak // Tries, in order, the --rocm-path option, the ROCM_PATH environment variable
11820f79f8cSKrzysztof Drewniak // and a compile-time default
getRocmPath()11920f79f8cSKrzysztof Drewniak std::string SerializeToHsacoPass::getRocmPath() {
12020f79f8cSKrzysztof Drewniak   if (rocmPath.getNumOccurrences() > 0)
12120f79f8cSKrzysztof Drewniak     return rocmPath.getValue();
122a825fb2cSChristian Sigg 
12320f79f8cSKrzysztof Drewniak   return __DEFAULT_ROCM_PATH__;
124a825fb2cSChristian Sigg }
125a825fb2cSChristian Sigg 
126a825fb2cSChristian Sigg // Sets the 'option' to 'value' unless it already has a value.
maybeSetOption(Pass::Option<std::string> & option,function_ref<std::string ()> getValue)127a825fb2cSChristian Sigg static void maybeSetOption(Pass::Option<std::string> &option,
128a825fb2cSChristian Sigg                            function_ref<std::string()> getValue) {
129a825fb2cSChristian Sigg   if (!option.hasValue())
130a825fb2cSChristian Sigg     option = getValue();
131a825fb2cSChristian Sigg }
132a825fb2cSChristian Sigg 
SerializeToHsacoPass(StringRef triple,StringRef arch,StringRef features,int optLevel)133fb1a06aaSKrzysztof Drewniak SerializeToHsacoPass::SerializeToHsacoPass(StringRef triple, StringRef arch,
134bd22554aSKrzysztof Drewniak                                            StringRef features, int optLevel) {
135fb1a06aaSKrzysztof Drewniak   maybeSetOption(this->triple, [&triple] { return triple.str(); });
136fb1a06aaSKrzysztof Drewniak   maybeSetOption(this->chip, [&arch] { return arch.str(); });
137fb1a06aaSKrzysztof Drewniak   maybeSetOption(this->features, [&features] { return features.str(); });
138bd22554aSKrzysztof Drewniak   if (this->optLevel.getNumOccurrences() == 0)
139bd22554aSKrzysztof Drewniak     this->optLevel.setValue(optLevel);
140a825fb2cSChristian Sigg }
141a825fb2cSChristian Sigg 
getDependentDialects(DialectRegistry & registry) const142a825fb2cSChristian Sigg void SerializeToHsacoPass::getDependentDialects(
143a825fb2cSChristian Sigg     DialectRegistry &registry) const {
144a825fb2cSChristian Sigg   registerROCDLDialectTranslation(registry);
145a825fb2cSChristian Sigg   gpu::SerializeToBlobPass::getDependentDialects(registry);
146a825fb2cSChristian Sigg }
147a825fb2cSChristian Sigg 
148a6f53afbSKrzysztof Drewniak Optional<SmallVector<std::unique_ptr<llvm::Module>, 3>>
loadLibraries(SmallVectorImpl<char> & path,SmallVectorImpl<StringRef> & libraries,llvm::LLVMContext & context)149a6f53afbSKrzysztof Drewniak SerializeToHsacoPass::loadLibraries(SmallVectorImpl<char> &path,
150a6f53afbSKrzysztof Drewniak                                     SmallVectorImpl<StringRef> &libraries,
151a6f53afbSKrzysztof Drewniak                                     llvm::LLVMContext &context) {
152a6f53afbSKrzysztof Drewniak   SmallVector<std::unique_ptr<llvm::Module>, 3> ret;
153a6f53afbSKrzysztof Drewniak   size_t dirLength = path.size();
154a6f53afbSKrzysztof Drewniak 
155a6f53afbSKrzysztof Drewniak   if (!llvm::sys::fs::is_directory(path)) {
156a6f53afbSKrzysztof Drewniak     getOperation().emitRemark() << "Bitcode path: " << path
157a6f53afbSKrzysztof Drewniak                                 << " does not exist or is not a directory\n";
158a6f53afbSKrzysztof Drewniak     return llvm::None;
159a6f53afbSKrzysztof Drewniak   }
160a6f53afbSKrzysztof Drewniak 
161a6f53afbSKrzysztof Drewniak   for (const StringRef file : libraries) {
162a6f53afbSKrzysztof Drewniak     llvm::SMDiagnostic error;
163a6f53afbSKrzysztof Drewniak     llvm::sys::path::append(path, file);
164a6f53afbSKrzysztof Drewniak     llvm::StringRef pathRef(path.data(), path.size());
165a6f53afbSKrzysztof Drewniak     std::unique_ptr<llvm::Module> library =
166a6f53afbSKrzysztof Drewniak         llvm::getLazyIRFileModule(pathRef, error, context);
167b77d4d54SDuncan P. N. Exon Smith     path.truncate(dirLength);
168a6f53afbSKrzysztof Drewniak     if (!library) {
169a6f53afbSKrzysztof Drewniak       getOperation().emitError() << "Failed to load library " << file
170a6f53afbSKrzysztof Drewniak                                  << " from " << path << error.getMessage();
171a6f53afbSKrzysztof Drewniak       return llvm::None;
172a6f53afbSKrzysztof Drewniak     }
173a6f53afbSKrzysztof Drewniak     // Some ROCM builds don't strip this like they should
174a6f53afbSKrzysztof Drewniak     if (auto *openclVersion = library->getNamedMetadata("opencl.ocl.version"))
175a6f53afbSKrzysztof Drewniak       library->eraseNamedMetadata(openclVersion);
176a6f53afbSKrzysztof Drewniak     // Stop spamming us with clang version numbers
177a6f53afbSKrzysztof Drewniak     if (auto *ident = library->getNamedMetadata("llvm.ident"))
178a6f53afbSKrzysztof Drewniak       library->eraseNamedMetadata(ident);
179a6f53afbSKrzysztof Drewniak     ret.push_back(std::move(library));
180a6f53afbSKrzysztof Drewniak   }
181a6f53afbSKrzysztof Drewniak 
182a6f53afbSKrzysztof Drewniak   return ret;
183a6f53afbSKrzysztof Drewniak }
184a6f53afbSKrzysztof Drewniak 
185a6f53afbSKrzysztof Drewniak std::unique_ptr<llvm::Module>
translateToLLVMIR(llvm::LLVMContext & llvmContext)186a6f53afbSKrzysztof Drewniak SerializeToHsacoPass::translateToLLVMIR(llvm::LLVMContext &llvmContext) {
187a6f53afbSKrzysztof Drewniak   // MLIR -> LLVM translation
188a6f53afbSKrzysztof Drewniak   std::unique_ptr<llvm::Module> ret =
189a6f53afbSKrzysztof Drewniak       gpu::SerializeToBlobPass::translateToLLVMIR(llvmContext);
190a6f53afbSKrzysztof Drewniak 
191a6f53afbSKrzysztof Drewniak   if (!ret) {
192a6f53afbSKrzysztof Drewniak     getOperation().emitOpError("Module lowering failed");
193a6f53afbSKrzysztof Drewniak     return ret;
194a6f53afbSKrzysztof Drewniak   }
195a6f53afbSKrzysztof Drewniak   // Walk the LLVM module in order to determine if we need to link in device
196a6f53afbSKrzysztof Drewniak   // libs
197a6f53afbSKrzysztof Drewniak   bool needOpenCl = false;
198a6f53afbSKrzysztof Drewniak   bool needOckl = false;
199a6f53afbSKrzysztof Drewniak   bool needOcml = false;
200a6f53afbSKrzysztof Drewniak   for (llvm::Function &f : ret->functions()) {
201a6f53afbSKrzysztof Drewniak     if (f.hasExternalLinkage() && f.hasName() && !f.hasExactDefinition()) {
202a6f53afbSKrzysztof Drewniak       StringRef funcName = f.getName();
203a6f53afbSKrzysztof Drewniak       if ("printf" == funcName)
204a6f53afbSKrzysztof Drewniak         needOpenCl = true;
205a6f53afbSKrzysztof Drewniak       if (funcName.startswith("__ockl_"))
206a6f53afbSKrzysztof Drewniak         needOckl = true;
207a6f53afbSKrzysztof Drewniak       if (funcName.startswith("__ocml_"))
208a6f53afbSKrzysztof Drewniak         needOcml = true;
209a6f53afbSKrzysztof Drewniak     }
210a6f53afbSKrzysztof Drewniak   }
211a6f53afbSKrzysztof Drewniak 
212a6f53afbSKrzysztof Drewniak   if (needOpenCl)
213a6f53afbSKrzysztof Drewniak     needOcml = needOckl = true;
214a6f53afbSKrzysztof Drewniak 
215a6f53afbSKrzysztof Drewniak   // No libraries needed (the typical case)
216a6f53afbSKrzysztof Drewniak   if (!(needOpenCl || needOcml || needOckl))
217a6f53afbSKrzysztof Drewniak     return ret;
218a6f53afbSKrzysztof Drewniak 
219a6f53afbSKrzysztof Drewniak   // Define one of the control constants the ROCm device libraries expect to be
220a6f53afbSKrzysztof Drewniak   // present These constants can either be defined in the module or can be
221a6f53afbSKrzysztof Drewniak   // imported by linking in bitcode that defines the constant. To simplify our
222a6f53afbSKrzysztof Drewniak   // logic, we define the constants into the module we are compiling
223a6f53afbSKrzysztof Drewniak   auto addControlConstant = [&module = *ret](StringRef name, uint32_t value,
224a6f53afbSKrzysztof Drewniak                                              uint32_t bitwidth) {
225a6f53afbSKrzysztof Drewniak     using llvm::GlobalVariable;
226a6f53afbSKrzysztof Drewniak     if (module.getNamedGlobal(name)) {
227a6f53afbSKrzysztof Drewniak       return;
228a6f53afbSKrzysztof Drewniak     }
229a6f53afbSKrzysztof Drewniak     llvm::IntegerType *type =
230a6f53afbSKrzysztof Drewniak         llvm::IntegerType::getIntNTy(module.getContext(), bitwidth);
231a6f53afbSKrzysztof Drewniak     auto *initializer = llvm::ConstantInt::get(type, value, /*isSigned=*/false);
232a6f53afbSKrzysztof Drewniak     auto *constant = new GlobalVariable(
233a6f53afbSKrzysztof Drewniak         module, type,
234a6f53afbSKrzysztof Drewniak         /*isConstant=*/true, GlobalVariable::LinkageTypes::LinkOnceODRLinkage,
235a6f53afbSKrzysztof Drewniak         initializer, name,
236a6f53afbSKrzysztof Drewniak         /*before=*/nullptr,
237a6f53afbSKrzysztof Drewniak         /*threadLocalMode=*/GlobalVariable::ThreadLocalMode::NotThreadLocal,
238a6f53afbSKrzysztof Drewniak         /*addressSpace=*/4);
239a6f53afbSKrzysztof Drewniak     constant->setUnnamedAddr(GlobalVariable::UnnamedAddr::Local);
240a6f53afbSKrzysztof Drewniak     constant->setVisibility(
241a6f53afbSKrzysztof Drewniak         GlobalVariable::VisibilityTypes::ProtectedVisibility);
242a6f53afbSKrzysztof Drewniak     constant->setAlignment(llvm::MaybeAlign(bitwidth / 8));
243a6f53afbSKrzysztof Drewniak   };
244a6f53afbSKrzysztof Drewniak 
2454e817b3fSKrzysztof Drewniak   // Set up control variables in the module instead of linking in tiny bitcode
246a6f53afbSKrzysztof Drewniak   if (needOcml) {
247a6f53afbSKrzysztof Drewniak     // TODO(kdrewnia): Enable math optimizations once we have support for
248a6f53afbSKrzysztof Drewniak     // `-ffast-math`-like options
249a6f53afbSKrzysztof Drewniak     addControlConstant("__oclc_finite_only_opt", 0, 8);
250a6f53afbSKrzysztof Drewniak     addControlConstant("__oclc_daz_opt", 0, 8);
251a6f53afbSKrzysztof Drewniak     addControlConstant("__oclc_correctly_rounded_sqrt32", 1, 8);
252a6f53afbSKrzysztof Drewniak     addControlConstant("__oclc_unsafe_math_opt", 0, 8);
253a6f53afbSKrzysztof Drewniak   }
254a6f53afbSKrzysztof Drewniak   if (needOcml || needOckl) {
255a6f53afbSKrzysztof Drewniak     addControlConstant("__oclc_wavefrontsize64", 1, 8);
256a6f53afbSKrzysztof Drewniak     StringRef chipSet = this->chip.getValue();
257a6f53afbSKrzysztof Drewniak     if (chipSet.startswith("gfx"))
258a6f53afbSKrzysztof Drewniak       chipSet = chipSet.substr(3);
259a6f53afbSKrzysztof Drewniak     uint32_t minor =
260a6f53afbSKrzysztof Drewniak         llvm::APInt(32, chipSet.substr(chipSet.size() - 2), 16).getZExtValue();
261a6f53afbSKrzysztof Drewniak     uint32_t major = llvm::APInt(32, chipSet.substr(0, chipSet.size() - 2), 10)
262a6f53afbSKrzysztof Drewniak                          .getZExtValue();
263a6f53afbSKrzysztof Drewniak     uint32_t isaNumber = minor + 1000 * major;
264a6f53afbSKrzysztof Drewniak     addControlConstant("__oclc_ISA_version", isaNumber, 32);
265a2cdb979SKrzysztof Drewniak 
266a2cdb979SKrzysztof Drewniak     // This constant must always match the default code object ABI version
267a2cdb979SKrzysztof Drewniak     // of the AMDGPU backend.
268a2cdb979SKrzysztof Drewniak     addControlConstant("__oclc_ABI_version", 400, 32);
269a6f53afbSKrzysztof Drewniak   }
270a6f53afbSKrzysztof Drewniak 
271a6f53afbSKrzysztof Drewniak   // Determine libraries we need to link - order matters due to dependencies
272a6f53afbSKrzysztof Drewniak   llvm::SmallVector<StringRef, 4> libraries;
273a6f53afbSKrzysztof Drewniak   if (needOpenCl)
274a6f53afbSKrzysztof Drewniak     libraries.push_back("opencl.bc");
275a6f53afbSKrzysztof Drewniak   if (needOcml)
276a6f53afbSKrzysztof Drewniak     libraries.push_back("ocml.bc");
277a6f53afbSKrzysztof Drewniak   if (needOckl)
278a6f53afbSKrzysztof Drewniak     libraries.push_back("ockl.bc");
279a6f53afbSKrzysztof Drewniak 
280a6f53afbSKrzysztof Drewniak   Optional<SmallVector<std::unique_ptr<llvm::Module>, 3>> mbModules;
281a6f53afbSKrzysztof Drewniak   std::string theRocmPath = getRocmPath();
28259c3be74SMehdi Amini   llvm::SmallString<32> bitcodePath(theRocmPath);
283a6f53afbSKrzysztof Drewniak   llvm::sys::path::append(bitcodePath, "amdgcn", "bitcode");
284a6f53afbSKrzysztof Drewniak   mbModules = loadLibraries(bitcodePath, libraries, llvmContext);
285a6f53afbSKrzysztof Drewniak 
286a6f53afbSKrzysztof Drewniak   if (!mbModules) {
287a6f53afbSKrzysztof Drewniak     getOperation()
2884e817b3fSKrzysztof Drewniak             .emitWarning("Could not load required device libraries")
289a6f53afbSKrzysztof Drewniak             .attachNote()
290a6f53afbSKrzysztof Drewniak         << "This will probably cause link-time or run-time failures";
291a6f53afbSKrzysztof Drewniak     return ret; // We can still abort here
292a6f53afbSKrzysztof Drewniak   }
293a6f53afbSKrzysztof Drewniak 
294a6f53afbSKrzysztof Drewniak   llvm::Linker linker(*ret);
2956d5fc1e3SKazu Hirata   for (std::unique_ptr<llvm::Module> &libModule : *mbModules) {
296a6f53afbSKrzysztof Drewniak     // This bitcode linking code is substantially similar to what is used in
297a6f53afbSKrzysztof Drewniak     // hip-clang It imports the library functions into the module, allowing LLVM
298a6f53afbSKrzysztof Drewniak     // optimization passes (which must run after linking) to optimize across the
299a6f53afbSKrzysztof Drewniak     // libraries and the module's code. We also only import symbols if they are
300a6f53afbSKrzysztof Drewniak     // referenced by the module or a previous library since there will be no
301a6f53afbSKrzysztof Drewniak     // other source of references to those symbols in this compilation and since
302a6f53afbSKrzysztof Drewniak     // we don't want to bloat the resulting code object.
303a6f53afbSKrzysztof Drewniak     bool err = linker.linkInModule(
304a6f53afbSKrzysztof Drewniak         std::move(libModule), llvm::Linker::Flags::LinkOnlyNeeded,
305a6f53afbSKrzysztof Drewniak         [](llvm::Module &m, const StringSet<> &gvs) {
306a6f53afbSKrzysztof Drewniak           llvm::internalizeModule(m, [&gvs](const llvm::GlobalValue &gv) {
307a6f53afbSKrzysztof Drewniak             return !gv.hasName() || (gvs.count(gv.getName()) == 0);
308a6f53afbSKrzysztof Drewniak           });
309a6f53afbSKrzysztof Drewniak         });
310a6f53afbSKrzysztof Drewniak     // True is linker failure
311a6f53afbSKrzysztof Drewniak     if (err) {
312a6f53afbSKrzysztof Drewniak       getOperation().emitError(
313a6f53afbSKrzysztof Drewniak           "Unrecoverable failure during device library linking.");
314a6f53afbSKrzysztof Drewniak       // We have no guaranties about the state of `ret`, so bail
315a6f53afbSKrzysztof Drewniak       return nullptr;
316a6f53afbSKrzysztof Drewniak     }
317a6f53afbSKrzysztof Drewniak   }
318e1da6291SKrzysztof Drewniak 
319a6f53afbSKrzysztof Drewniak   return ret;
320a6f53afbSKrzysztof Drewniak }
321a6f53afbSKrzysztof Drewniak 
322bd22554aSKrzysztof Drewniak LogicalResult
optimizeLlvm(llvm::Module & llvmModule,llvm::TargetMachine & targetMachine)323bd22554aSKrzysztof Drewniak SerializeToHsacoPass::optimizeLlvm(llvm::Module &llvmModule,
324bd22554aSKrzysztof Drewniak                                    llvm::TargetMachine &targetMachine) {
325bd22554aSKrzysztof Drewniak   int optLevel = this->optLevel.getValue();
326bd22554aSKrzysztof Drewniak   if (optLevel < 0 || optLevel > 3)
327bd22554aSKrzysztof Drewniak     return getOperation().emitError()
328bd22554aSKrzysztof Drewniak            << "Invalid HSA optimization level" << optLevel << "\n";
329bd22554aSKrzysztof Drewniak 
330bd22554aSKrzysztof Drewniak   targetMachine.setOptLevel(static_cast<llvm::CodeGenOpt::Level>(optLevel));
331bd22554aSKrzysztof Drewniak 
332bd22554aSKrzysztof Drewniak   auto transformer =
333bd22554aSKrzysztof Drewniak       makeOptimizingTransformer(optLevel, /*sizeLevel=*/0, &targetMachine);
334bd22554aSKrzysztof Drewniak   auto error = transformer(&llvmModule);
335bd22554aSKrzysztof Drewniak   if (error) {
336bd22554aSKrzysztof Drewniak     InFlightDiagnostic mlirError = getOperation()->emitError();
337bd22554aSKrzysztof Drewniak     llvm::handleAllErrors(
338bd22554aSKrzysztof Drewniak         std::move(error), [&mlirError](const llvm::ErrorInfoBase &ei) {
339bd22554aSKrzysztof Drewniak           mlirError << "Could not optimize LLVM IR: " << ei.message() << "\n";
340bd22554aSKrzysztof Drewniak         });
341bd22554aSKrzysztof Drewniak     return mlirError;
342bd22554aSKrzysztof Drewniak   }
343bd22554aSKrzysztof Drewniak   return success();
344bd22554aSKrzysztof Drewniak }
345bd22554aSKrzysztof Drewniak 
346a825fb2cSChristian Sigg std::unique_ptr<SmallVectorImpl<char>>
assembleIsa(const std::string & isa)347a825fb2cSChristian Sigg SerializeToHsacoPass::assembleIsa(const std::string &isa) {
348a825fb2cSChristian Sigg   auto loc = getOperation().getLoc();
349a825fb2cSChristian Sigg 
350a825fb2cSChristian Sigg   SmallVector<char, 0> result;
351a825fb2cSChristian Sigg   llvm::raw_svector_ostream os(result);
352a825fb2cSChristian Sigg 
353a825fb2cSChristian Sigg   llvm::Triple triple(llvm::Triple::normalize(this->triple));
354a825fb2cSChristian Sigg   std::string error;
355a825fb2cSChristian Sigg   const llvm::Target *target =
356a825fb2cSChristian Sigg       llvm::TargetRegistry::lookupTarget(triple.normalize(), error);
357a825fb2cSChristian Sigg   if (!target) {
358a825fb2cSChristian Sigg     emitError(loc, Twine("failed to lookup target: ") + error);
359a825fb2cSChristian Sigg     return {};
360a825fb2cSChristian Sigg   }
361a825fb2cSChristian Sigg 
362a825fb2cSChristian Sigg   llvm::SourceMgr srcMgr;
363*b7f93c28SJeff Niu   srcMgr.AddNewSourceBuffer(llvm::MemoryBuffer::getMemBuffer(isa), SMLoc());
364a825fb2cSChristian Sigg 
365a825fb2cSChristian Sigg   const llvm::MCTargetOptions mcOptions;
366a825fb2cSChristian Sigg   std::unique_ptr<llvm::MCRegisterInfo> mri(
367a825fb2cSChristian Sigg       target->createMCRegInfo(this->triple));
368a825fb2cSChristian Sigg   std::unique_ptr<llvm::MCAsmInfo> mai(
369a825fb2cSChristian Sigg       target->createMCAsmInfo(*mri, this->triple, mcOptions));
370a825fb2cSChristian Sigg   mai->setRelaxELFRelocations(true);
371f849640aSKrzysztof Drewniak   std::unique_ptr<llvm::MCSubtargetInfo> sti(
372f849640aSKrzysztof Drewniak       target->createMCSubtargetInfo(this->triple, this->chip, this->features));
373a825fb2cSChristian Sigg 
374f849640aSKrzysztof Drewniak   llvm::MCContext ctx(triple, mai.get(), mri.get(), sti.get(), &srcMgr,
375f849640aSKrzysztof Drewniak                       &mcOptions);
376c2f819afSPhilipp Krones   std::unique_ptr<llvm::MCObjectFileInfo> mofi(target->createMCObjectFileInfo(
377c2f819afSPhilipp Krones       ctx, /*PIC=*/false, /*LargeCodeModel=*/false));
378c2f819afSPhilipp Krones   ctx.setObjectFileInfo(mofi.get());
379a825fb2cSChristian Sigg 
380a825fb2cSChristian Sigg   SmallString<128> cwd;
381a825fb2cSChristian Sigg   if (!llvm::sys::fs::current_path(cwd))
382a825fb2cSChristian Sigg     ctx.setCompilationDir(cwd);
383a825fb2cSChristian Sigg 
384a825fb2cSChristian Sigg   std::unique_ptr<llvm::MCStreamer> mcStreamer;
385a825fb2cSChristian Sigg   std::unique_ptr<llvm::MCInstrInfo> mcii(target->createMCInstrInfo());
386a825fb2cSChristian Sigg 
3872aed07e9SShao-Ce SUN   llvm::MCCodeEmitter *ce = target->createMCCodeEmitter(*mcii, ctx);
388a825fb2cSChristian Sigg   llvm::MCAsmBackend *mab = target->createMCAsmBackend(*sti, *mri, mcOptions);
389a825fb2cSChristian Sigg   mcStreamer.reset(target->createMCObjectStreamer(
390a825fb2cSChristian Sigg       triple, ctx, std::unique_ptr<llvm::MCAsmBackend>(mab),
391a825fb2cSChristian Sigg       mab->createObjectWriter(os), std::unique_ptr<llvm::MCCodeEmitter>(ce),
392a825fb2cSChristian Sigg       *sti, mcOptions.MCRelaxAll, mcOptions.MCIncrementalLinkerCompatible,
393a825fb2cSChristian Sigg       /*DWARFMustBeAtTheEnd*/ false));
394a825fb2cSChristian Sigg   mcStreamer->setUseAssemblerInfoForParsing(true);
395a825fb2cSChristian Sigg 
396a825fb2cSChristian Sigg   std::unique_ptr<llvm::MCAsmParser> parser(
397a825fb2cSChristian Sigg       createMCAsmParser(srcMgr, ctx, *mcStreamer, *mai));
398a825fb2cSChristian Sigg   std::unique_ptr<llvm::MCTargetAsmParser> tap(
399a825fb2cSChristian Sigg       target->createMCAsmParser(*sti, *parser, *mcii, mcOptions));
400a825fb2cSChristian Sigg 
401a825fb2cSChristian Sigg   if (!tap) {
402a825fb2cSChristian Sigg     emitError(loc, "assembler initialization error");
403a825fb2cSChristian Sigg     return {};
404a825fb2cSChristian Sigg   }
405a825fb2cSChristian Sigg 
406a825fb2cSChristian Sigg   parser->setTargetParser(*tap);
407a825fb2cSChristian Sigg   parser->Run(false);
408a825fb2cSChristian Sigg 
409a825fb2cSChristian Sigg   return std::make_unique<SmallVector<char, 0>>(std::move(result));
410a825fb2cSChristian Sigg }
411a825fb2cSChristian Sigg 
412a825fb2cSChristian Sigg std::unique_ptr<std::vector<char>>
createHsaco(const SmallVectorImpl<char> & isaBinary)413a825fb2cSChristian Sigg SerializeToHsacoPass::createHsaco(const SmallVectorImpl<char> &isaBinary) {
414a825fb2cSChristian Sigg   auto loc = getOperation().getLoc();
415a825fb2cSChristian Sigg 
416a825fb2cSChristian Sigg   // Save the ISA binary to a temp file.
417a825fb2cSChristian Sigg   int tempIsaBinaryFd = -1;
418a825fb2cSChristian Sigg   SmallString<128> tempIsaBinaryFilename;
419a825fb2cSChristian Sigg   if (llvm::sys::fs::createTemporaryFile("kernel", "o", tempIsaBinaryFd,
420a825fb2cSChristian Sigg                                          tempIsaBinaryFilename)) {
421a825fb2cSChristian Sigg     emitError(loc, "temporary file for ISA binary creation error");
422a825fb2cSChristian Sigg     return {};
423a825fb2cSChristian Sigg   }
424a825fb2cSChristian Sigg   llvm::FileRemover cleanupIsaBinary(tempIsaBinaryFilename);
425a825fb2cSChristian Sigg   llvm::raw_fd_ostream tempIsaBinaryOs(tempIsaBinaryFd, true);
426a825fb2cSChristian Sigg   tempIsaBinaryOs << StringRef(isaBinary.data(), isaBinary.size());
427a825fb2cSChristian Sigg   tempIsaBinaryOs.close();
428a825fb2cSChristian Sigg 
429a825fb2cSChristian Sigg   // Create a temp file for HSA code object.
430a825fb2cSChristian Sigg   int tempHsacoFD = -1;
431a825fb2cSChristian Sigg   SmallString<128> tempHsacoFilename;
432a825fb2cSChristian Sigg   if (llvm::sys::fs::createTemporaryFile("kernel", "hsaco", tempHsacoFD,
433a825fb2cSChristian Sigg                                          tempHsacoFilename)) {
434a825fb2cSChristian Sigg     emitError(loc, "temporary file for HSA code object creation error");
435a825fb2cSChristian Sigg     return {};
436a825fb2cSChristian Sigg   }
437a825fb2cSChristian Sigg   llvm::FileRemover cleanupHsaco(tempHsacoFilename);
438a825fb2cSChristian Sigg 
4391ce314ceSKrzysztof Drewniak   std::string theRocmPath = getRocmPath();
44059c3be74SMehdi Amini   llvm::SmallString<32> lldPath(theRocmPath);
4411ce314ceSKrzysztof Drewniak   llvm::sys::path::append(lldPath, "llvm", "bin", "ld.lld");
4421ce314ceSKrzysztof Drewniak   int lldResult = llvm::sys::ExecuteAndWait(
4431ce314ceSKrzysztof Drewniak       lldPath,
4441ce314ceSKrzysztof Drewniak       {"ld.lld", "-shared", tempIsaBinaryFilename, "-o", tempHsacoFilename});
4451ce314ceSKrzysztof Drewniak   if (lldResult != 0) {
446a825fb2cSChristian Sigg     emitError(loc, "lld invocation error");
447a825fb2cSChristian Sigg     return {};
448a825fb2cSChristian Sigg   }
449a825fb2cSChristian Sigg 
450a825fb2cSChristian Sigg   // Load the HSA code object.
451a825fb2cSChristian Sigg   auto hsacoFile = openInputFile(tempHsacoFilename);
452a825fb2cSChristian Sigg   if (!hsacoFile) {
453a825fb2cSChristian Sigg     emitError(loc, "read HSA code object from temp file error");
454a825fb2cSChristian Sigg     return {};
455a825fb2cSChristian Sigg   }
456a825fb2cSChristian Sigg 
457a825fb2cSChristian Sigg   StringRef buffer = hsacoFile->getBuffer();
458a825fb2cSChristian Sigg   return std::make_unique<std::vector<char>>(buffer.begin(), buffer.end());
459a825fb2cSChristian Sigg }
460a825fb2cSChristian Sigg 
461a825fb2cSChristian Sigg std::unique_ptr<std::vector<char>>
serializeISA(const std::string & isa)462a825fb2cSChristian Sigg SerializeToHsacoPass::serializeISA(const std::string &isa) {
463a825fb2cSChristian Sigg   auto isaBinary = assembleIsa(isa);
464a825fb2cSChristian Sigg   if (!isaBinary)
465a825fb2cSChristian Sigg     return {};
466a825fb2cSChristian Sigg   return createHsaco(*isaBinary);
467a825fb2cSChristian Sigg }
468a825fb2cSChristian Sigg 
469a825fb2cSChristian Sigg // Register pass to serialize GPU kernel functions to a HSACO binary annotation.
registerGpuSerializeToHsacoPass()470a825fb2cSChristian Sigg void mlir::registerGpuSerializeToHsacoPass() {
471*b7f93c28SJeff Niu   PassRegistration<SerializeToHsacoPass> registerSerializeToHSACO([] {
472a825fb2cSChristian Sigg     // Initialize LLVM AMDGPU backend.
473a825fb2cSChristian Sigg     LLVMInitializeAMDGPUAsmParser();
474a825fb2cSChristian Sigg     LLVMInitializeAMDGPUAsmPrinter();
475a825fb2cSChristian Sigg     LLVMInitializeAMDGPUTarget();
476a825fb2cSChristian Sigg     LLVMInitializeAMDGPUTargetInfo();
477a825fb2cSChristian Sigg     LLVMInitializeAMDGPUTargetMC();
478a825fb2cSChristian Sigg 
479*b7f93c28SJeff Niu     return std::make_unique<SerializeToHsacoPass>("amdgcn-amd-amdhsa", "", "",
480*b7f93c28SJeff Niu                                                   2);
481a825fb2cSChristian Sigg   });
482a825fb2cSChristian Sigg }
48340aef79dSKrzysztof Drewniak 
48440aef79dSKrzysztof Drewniak /// Create an instance of the GPU kernel function to HSAco binary serialization
48540aef79dSKrzysztof Drewniak /// pass.
createGpuSerializeToHsacoPass(StringRef triple,StringRef arch,StringRef features,int optLevel)48640aef79dSKrzysztof Drewniak std::unique_ptr<Pass> mlir::createGpuSerializeToHsacoPass(StringRef triple,
48740aef79dSKrzysztof Drewniak                                                           StringRef arch,
48840aef79dSKrzysztof Drewniak                                                           StringRef features,
48940aef79dSKrzysztof Drewniak                                                           int optLevel) {
49040aef79dSKrzysztof Drewniak   return std::make_unique<SerializeToHsacoPass>(triple, arch, features,
49140aef79dSKrzysztof Drewniak                                                 optLevel);
49240aef79dSKrzysztof Drewniak }
49340aef79dSKrzysztof Drewniak 
494a825fb2cSChristian Sigg #else  // MLIR_GPU_TO_HSACO_PASS_ENABLE
registerGpuSerializeToHsacoPass()495a825fb2cSChristian Sigg void mlir::registerGpuSerializeToHsacoPass() {}
496a825fb2cSChristian Sigg #endif // MLIR_GPU_TO_HSACO_PASS_ENABLE
497