1#ifndef PROTEUS_CORE_LLVM_HIP_H
2#define PROTEUS_CORE_LLVM_HIP_H
12#include <llvm/Bitcode/BitcodeWriter.h>
13#include <llvm/CodeGen/MachineModuleInfo.h>
14#include <llvm/IR/DiagnosticPrinter.h>
15#include <llvm/IR/Function.h>
16#include <llvm/IR/LegacyPassManager.h>
17#include <llvm/IR/Module.h>
18#include <llvm/IR/Verifier.h>
19#include <llvm/LTO/LTO.h>
20#include <llvm/MC/MCSubtargetInfo.h>
21#include <llvm/Support/CodeGen.h>
22#include <llvm/Support/FileSystem.h>
23#include <llvm/Support/MemoryBuffer.h>
24#include <llvm/Support/Path.h>
25#include <llvm/Support/Signals.h>
26#include <llvm/Support/TargetSelect.h>
27#include <llvm/Support/WithColor.h>
28#include <llvm/Target/TargetMachine.h>
32#if LLVM_VERSION_MAJOR >= 18
33#include <lld/Common/Driver.h>
44 static SmallVector<StringRef> Names = {
45 "_ZNK17__HIP_CoordinatesI13__HIP_GridDimE3__XcvjEv",
46 "llvm.amdgcn.num.workgroups.x",
"_ZL20__hip_get_grid_dim_xv"};
51 static SmallVector<StringRef> Names = {
52 "_ZNK17__HIP_CoordinatesI13__HIP_GridDimE3__YcvjEv",
53 "llvm.amdgcn.num.workgroups.y",
"_ZL20__hip_get_grid_dim_yv"};
58 static SmallVector<StringRef> Names = {
59 "_ZNK17__HIP_CoordinatesI13__HIP_GridDimE3__ZcvjEv",
60 "llvm.amdgcn.num.workgroups.z",
"_ZL20__hip_get_grid_dim_zv"};
65 static SmallVector<StringRef> Names = {
66 "_ZNK17__HIP_CoordinatesI14__HIP_BlockDimE3__XcvjEv",
67 "llvm.amdgcn.workgroup.size.x",
"_ZL21__hip_get_block_dim_xv"};
72 static SmallVector<StringRef> Names = {
73 "_ZNK17__HIP_CoordinatesI14__HIP_BlockDimE3__YcvjEv",
74 "llvm.amdgcn.workgroup.size.y",
"_ZL21__hip_get_block_dim_yv"};
79 static SmallVector<StringRef> Names = {
80 "_ZNK17__HIP_CoordinatesI14__HIP_BlockDimE3__ZcvjEv",
81 "llvm.amdgcn.workgroup.size.z",
"_ZL21__hip_get_block_dim_zv"};
86 static SmallVector<StringRef> Names = {
87 "_ZNK17__HIP_CoordinatesI14__HIP_BlockIdxE3__XcvjEv",
88 "llvm.amdgcn.workgroup.id.x"};
93 static SmallVector<StringRef> Names = {
94 "_ZNK17__HIP_CoordinatesI14__HIP_BlockIdxE3__YcvjEv",
95 "llvm.amdgcn.workgroup.id.y"};
100 static SmallVector<StringRef> Names = {
101 "_ZNK17__HIP_CoordinatesI14__HIP_BlockIdxE3__ZcvjEv",
102 "llvm.amdgcn.workgroup.id.z"};
107 static SmallVector<StringRef> Names = {
108 "_ZNK17__HIP_CoordinatesI15__HIP_ThreadIdxE3__XcvjEv",
109 "llvm.amdgcn.workitem.id.x"};
114 static SmallVector<StringRef> Names = {
115 "_ZNK17__HIP_CoordinatesI15__HIP_ThreadIdxE3__YcvjEv",
116 "llvm.amdgcn.workitem.id.y"};
121 static SmallVector<StringRef> Names = {
122 "_ZNK17__HIP_CoordinatesI15__HIP_ThreadIdxE3__ZcvjEv",
123 "llvm.amdgcn.workitem.id.z"};
129 SmallString<128> TmpDir;
130 sys::path::system_temp_directory(
true, TmpDir);
132 SmallString<64> FileName;
133 FileName.append(Prefix);
134 FileName.append(Suffix.empty() ?
"-%%%%%%%" :
"-%%%%%%%.");
135 FileName.append(Suffix);
136 sys::path::append(TmpDir, FileName);
137 return sys::fs::TempFile::create(TmpDir);
140#if LLVM_VERSION_MAJOR >= 18
141inline SmallVector<std::unique_ptr<sys::fs::TempFile>>
142codegenSerial(Module &M, StringRef DeviceArch,
143 [[maybe_unused]]
char OptLevel =
'3',
int CodegenOptLevel = 3) {
145 SmallVector<std::unique_ptr<sys::fs::TempFile>> ObjectFiles;
152 std::unique_ptr<TargetMachine> TM = std::move(*ExpectedTM);
153 TargetLibraryInfoImpl TLII(Triple(M.getTargetTriple()));
154 M.setDataLayout(TM->createDataLayout());
156 legacy::PassManager PM;
157 PM.add(
new TargetLibraryInfoWrapperPass(TLII));
158 MachineModuleInfoWrapperPass *MMIWP =
159#if LLVM_VERSION_MAJOR >= 20
160 new MachineModuleInfoWrapperPass(TM.get());
162 new MachineModuleInfoWrapperPass(
163 reinterpret_cast<LLVMTargetMachine *
>(TM.get()));
166 SmallVector<char, 4096> ObjectCode;
167 raw_svector_ostream OS(ObjectCode);
169 if (
auto E = ExpectedF.takeError())
172 auto ObjectFile = std::move(*ExpectedF);
173 auto FileStream = std::make_unique<CachedFileStream>(
174 std::make_unique<llvm::raw_fd_ostream>(ObjectFile.FD,
false));
175 TM->addPassesToEmitFile(PM, *FileStream->OS,
nullptr,
176 CodeGenFileType::ObjectFile,
179 std::unique_ptr<sys::fs::TempFile> ObjectFilePtr =
180 std::make_unique<sys::fs::TempFile>(std::move(ObjectFile));
181 ObjectFiles.emplace_back(std::move(ObjectFilePtr));
184 if (Error E = FileStream->commit())
191inline SmallVector<std::unique_ptr<sys::fs::TempFile>>
192codegenParallel(Module &M, StringRef DeviceArch,
197 std::atomic<bool> LTOError =
false;
199 auto DiagnosticHandler = [&](
const DiagnosticInfo &DI) {
200 std::string ErrStorage;
201 raw_string_ostream OS(ErrStorage);
202 DiagnosticPrinterRawOStream DP(OS);
205 switch (DI.getSeverity()) {
207 WithColor::error(errs(),
"[proteus codegen]") << ErrStorage <<
"\n";
211 WithColor::warning(errs(),
"[proteus codegen]") << ErrStorage <<
"\n";
214 WithColor::note(errs(),
"[proteus codegen]") << ErrStorage <<
"\n";
217 WithColor::remark(errs()) << ErrStorage <<
"\n";
224 M, DeviceArch, OptConfig.CodegenOptLevel);
227 std::unique_ptr<TargetMachine> TM = std::move(*ExpectedTM);
230 Conf.CPU = DeviceArch;
233 std::string FeatureStr = TM->getMCSubtargetInfo()->getFeatureString().str();
234 if (!FeatureStr.empty()) {
235 SmallVector<StringRef> Features;
236 StringRef(FeatureStr).split(Features,
',');
237 for (
auto &F : Features)
238 Conf.MAttrs.push_back(F.str());
244 Conf.Options = TM->Options;
246 Conf.DisableVerify =
true;
247 Conf.TimeTraceEnabled =
false;
248 Conf.DebugPassManager =
false;
249 Conf.VerifyEach =
false;
250 Conf.DiagHandler = DiagnosticHandler;
251 Conf.OptLevel = OptConfig.OptLevel;
255 if (OptConfig.PassPipeline ||
258 OptConfig.PassPipeline, OptConfig.OptLevel, Plugins);
259 for (
const auto &PluginPath :
261 Conf.PassPlugins.push_back(PluginPath);
262 Conf.CGOptLevel =
static_cast<CodeGenOptLevel
>(OptConfig.CodegenOptLevel);
264 unsigned ParallelCodeGenParallelismLevel =
265 std::max(1u, std::thread::hardware_concurrency());
266 lto::LTO L(std::move(Conf), {}, ParallelCodeGenParallelismLevel);
269 M.setDataLayout(TM->createDataLayout());
271 SmallString<0> BitcodeBuf;
272 raw_svector_ostream BitcodeOS(BitcodeBuf);
273 WriteBitcodeToFile(M, BitcodeOS);
278 auto IF = cantFail(lto::InputFile::create(
279 MemoryBufferRef{BitcodeBuf, M.getModuleIdentifier()}));
281 std::set<std::string> PrevailingSymbols;
282 auto BuildResolutions = [&]() {
284 const auto Symbols =
IF->symbols();
285 SmallVector<lto::SymbolResolution, 16> Resolutions(Symbols.size());
286 size_t SymbolIdx = 0;
287 for (
auto &Sym : Symbols) {
288 lto::SymbolResolution &Res = Resolutions[SymbolIdx];
292 Res.Prevailing = !Sym.isUndefined() &&
293 PrevailingSymbols.insert(Sym.getName().str()).second;
295 Res.VisibleToRegularObj =
297 Sym.getVisibility() != GlobalValue::HiddenVisibility &&
298 !Sym.canBeOmittedFromSymbolTable();
301 Sym.getVisibility() != GlobalValue::HiddenVisibility &&
302 (!Sym.canBeOmittedFromSymbolTable());
304 Res.FinalDefinitionInLinkageUnit =
305 Sym.getVisibility() != GlobalValue::DefaultVisibility &&
306 (!Sym.isUndefined() && !Sym.isCommon());
310 Res.LinkerRedefined =
false;
313 auto PrintSymbol = [](
const lto::InputFile::Symbol &Sym,
314 lto::SymbolResolution &Res) {
316 OutStream <<
"Vis: ";
317 switch (Sym.getVisibility()) {
318 case GlobalValue::HiddenVisibility:
321 case GlobalValue::ProtectedVisibility:
324 case GlobalValue::DefaultVisibility:
329 OutStream <<
" Sym: ";
330 auto PrintBool = [&](
char C,
bool B) { OutStream << (B ? C :
'-'); };
331 PrintBool(
'U', Sym.isUndefined());
332 PrintBool(
'C', Sym.isCommon());
333 PrintBool(
'W', Sym.isWeak());
334 PrintBool(
'I', Sym.isIndirect());
335 PrintBool(
'O', Sym.canBeOmittedFromSymbolTable());
336 PrintBool(
'T', Sym.isTLS());
337 PrintBool(
'X', Sym.isExecutable());
338 OutStream <<
' ' << Sym.getName();
339 OutStream <<
"| P " << Res.Prevailing;
340 OutStream <<
" V " << Res.VisibleToRegularObj;
341 OutStream <<
" E " << Res.ExportDynamic;
342 OutStream <<
" F " << Res.FinalDefinitionInLinkageUnit;
346 PrintSymbol(Sym, Res);
351 cantFail(L.add(std::move(
IF), Resolutions));
357 size_t MaxTasks = L.getMaxTasks();
358 SmallVector<std::unique_ptr<sys::fs::TempFile>> ObjectFiles{MaxTasks};
362 const Twine & ) -> std::unique_ptr<CachedFileStream> {
363 std::string TaskStr = Task ?
"." + std::to_string(Task) :
"";
365 if (
auto E = ExpectedF.takeError())
368 std::make_unique<sys::fs::TempFile>(std::move(*ExpectedF));
369 auto Ret = std::make_unique<CachedFileStream>(
370 std::make_unique<llvm::raw_fd_ostream>(ObjectFiles[Task]->FD,
false));
376 if (Error E = L.run(AddStream))
381 createStringError(inconvertibleErrorCode(),
382 "Errors encountered inside the LTO pipeline.")));
389 StringRef DeviceArch) {
394 SmallString<4096> ModuleBuf;
395 raw_svector_ostream ModuleBufOS(ModuleBuf);
396 WriteBitcodeToFile(M, ModuleBufOS);
398 hiprtcLinkState HipLinkStatePtr;
405 std::string MArchOpt = (
"-march=" + DeviceArch).str();
410 const char *OptArgs[] = {MArchOpt.c_str()};
411 std::vector<hiprtcJIT_option> JITOptions = {
412 HIPRTC_JIT_IR_TO_ISA_OPT_EXT, HIPRTC_JIT_IR_TO_ISA_OPT_COUNT_EXT};
413 size_t OptArgsSize = 1;
414 const void *JITOptionsValues[] = {(
void *)OptArgs, (
void *)(OptArgsSize)};
416 JITOptions.size(), JITOptions.data(), (
void **)JITOptionsValues,
423 HipLinkStatePtr, HIPRTC_JIT_INPUT_LLVM_BITCODE, (
void *)ModuleBuf.data(),
424 ModuleBuf.size(),
"", 0,
nullptr,
nullptr));
426 HipLinkStatePtr, (
void **)&BinOut, &BinSize));
428 return MemoryBuffer::getMemBuffer(StringRef{BinOut, BinSize});
434 int MinBlocksPerSM = 0) {
438 F.addFnAttr(
"amdgpu-flat-work-group-size",
439 "1," + std::to_string(std::min(1024, MaxNumWorkGroups)));
441 if (MinBlocksPerSM != 0) {
447 F.addFnAttr(
"amdgpu-waves-per-eu", std::to_string(MinBlocksPerSM) +
"," +
448 std::to_string(MinBlocksPerSM));
452 <<
" => Set Workgroup size " << MaxNumWorkGroups
453 <<
" WavesPerEU (unused) " << MinBlocksPerSM <<
"\n");
456inline std::unique_ptr<MemoryBuffer>
458 [[maybe_unused]] SmallPtrSetImpl<void *> &GlobalLinkedBinaries,
463 assert(GlobalLinkedBinaries.empty() &&
464 "Expected empty linked binaries for HIP");
466 SmallVector<std::unique_ptr<sys::fs::TempFile>> ObjectFiles;
471 <<
"Codegen RTC " << T.
elapsed() <<
" ms\n");
474#if LLVM_VERSION_MAJOR >= 18
476 ObjectFiles = detail::codegenSerial(M, DeviceArch);
479 ObjectFiles = detail::codegenParallel(M, DeviceArch, OptConfig);
486 if (ObjectFiles.empty())
489#if LLVM_VERSION_MAJOR >= 18
491 if (
auto E = ExpectedF.takeError())
495 auto SharedObject = std::move(*ExpectedF);
497 std::vector<const char *>
Args{
"ld.lld",
"--no-undefined",
"-shared",
"-o",
498 SharedObject.TmpName.c_str()};
499 for (
auto &File : ObjectFiles) {
502 Args.push_back(File->TmpName.c_str());
506 for (
auto &Arg :
Args) {
513 <<
"Codegen object " <<
toString(CGOption) <<
"["
514 << ObjectFiles.size() <<
"] " << T.
elapsed() <<
" ms\n");
518 static std::mutex Mutex;
520 std::lock_guard LockGuard{Mutex};
521 lld::Result S = lld::lldMain(
Args, llvm::outs(), llvm::errs(),
522 {{lld::Gnu, &lld::elf::link}});
527 ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
528 MemoryBuffer::getFileAsStream(SharedObject.TmpName);
533 for (
auto &File : ObjectFiles) {
536 if (
auto E = File->discard())
540 if (
auto E = SharedObject.discard())
545 <<
"Codegen linking " << T.
elapsed() <<
" ms\n");
547 return std::move(*Buffer);
char int void ** Args
Definition CompilerInterfaceHost.cpp:23
#define PROTEUS_TIMER_OUTPUT(x)
Definition Config.h:483
#define PROTEUS_DBG(x)
Definition Debug.h:9
#define TIMESCOPE(...)
Definition TimeTracing.h:66
#define proteusHiprtcErrCheck(CALL)
Definition UtilsHIP.h:30
static Config & get()
Definition Config.h:371
bool ProteusDebugOutput
Definition Config.h:387
static llvm::raw_ostream & outs(const std::string &Name)
Definition Logger.h:25
static llvm::raw_ostream & logs(const std::string &Name)
Definition Logger.h:19
Definition TimeTracing.h:33
void reset()
Definition TimeTracing.cpp:68
uint64_t elapsed()
Definition TimeTracing.cpp:66
Definition CompiledLibrary.h:8
const SmallVector< StringRef > & threadIdxXFnName()
Definition CoreLLVMCUDA.h:70
const SmallVector< StringRef > & gridDimYFnName()
Definition CoreLLVMCUDA.h:30
std::vector< std::string > getUniqueJITPassPluginPaths(const std::vector< JITPassPluginConfig > &Plugins)
Definition CoreLLVM.h:162
const SmallVector< StringRef > & threadIdxZFnName()
Definition CoreLLVMCUDA.h:80
std::string composeOptimizationPassPipeline(std::optional< std::string > PassPipeline, char OptLevel, const std::vector< JITPassPluginConfig > &Plugins)
Definition CoreLLVM.h:124
const SmallVector< StringRef > & blockIdxZFnName()
Definition CoreLLVMCUDA.h:65
const SmallVector< StringRef > & gridDimZFnName()
Definition CoreLLVMCUDA.h:35
std::unique_ptr< MemoryBuffer > codegenRTC(Module &M, StringRef DeviceArch)
Definition CoreLLVMHIP.h:388
const SmallVector< StringRef > & gridDimXFnName()
Definition CoreLLVMCUDA.h:25
const SmallVector< StringRef > & blockIdxXFnName()
Definition CoreLLVMCUDA.h:55
Expected< std::unique_ptr< TargetMachine > > createTargetMachine(Module &M, StringRef Arch, unsigned OptLevel=3)
Definition CoreLLVM.h:70
const SmallVector< StringRef > & threadIdxYFnName()
Definition CoreLLVMCUDA.h:75
Expected< sys::fs::TempFile > createTempFile(StringRef Prefix, StringRef Suffix)
Definition CoreLLVMHIP.h:127
const SmallVector< StringRef > & blockIdxYFnName()
Definition CoreLLVMCUDA.h:60
bool hasJITPassPluginInsertion(const std::vector< JITPassPluginConfig > &Plugins)
Definition CoreLLVM.h:154
const SmallVector< StringRef > & blockDimYFnName()
Definition CoreLLVMCUDA.h:45
const SmallVector< StringRef > & blockDimZFnName()
Definition CoreLLVMCUDA.h:50
const SmallVector< StringRef > & blockDimXFnName()
Definition CoreLLVMCUDA.h:40
hiprtcResult rtcLinkAddData(hiprtcLinkState LinkState, hiprtcJITInputType InputType, void *Image, size_t ImageSize, const char *Name, unsigned int NumOptions, hiprtcJIT_option *Options, void **OptionValues)
Definition HIPRuntimeAPI.cpp:198
hiprtcResult rtcLinkCreate(unsigned int NumOptions, hiprtcJIT_option *Options, void **OptionValues, hiprtcLinkState *LinkStateOut)
Definition HIPRuntimeAPI.cpp:191
hiprtcResult rtcLinkComplete(hiprtcLinkState LinkState, void **BinOut, size_t *SizeOut)
Definition HIPRuntimeAPI.cpp:209
Definition MemoryCache.h:27
std::vector< JITPassPluginConfig > getJITPassPluginConfigs()
Definition JITPassPluginRegistry.cpp:105
void setLaunchBoundsForKernel(Function &F, int MaxThreadsPerSM, int MinBlocksPerSM=0)
Definition CoreLLVMCUDA.h:87
void reportFatalError(const llvm::Twine &Reason, const char *FILE, unsigned Line)
Definition Error.cpp:14
CodegenOption
Definition Config.h:19
std::unique_ptr< MemoryBuffer > codegenObject(Module &M, StringRef DeviceArch, SmallPtrSetImpl< void * > &GlobalLinkedBinaries, CodegenOption CGOption=CodegenOption::RTC)
Definition CoreLLVMCUDA.h:176
std::string toString(CodegenOption Option)
Definition Config.h:31
Definition CoreLLVM.h:291