| #include "../SuppressLLVMWarnings.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/Bitcode/BitcodeWriter.h" | |
| #include "llvm/IR/IRBuilder.h" | |
| #include "llvm/Object/OffloadBinary.h" | |
| #include "llvm/Support/CBindingWrapping.h" | |
| #include "llvm/Support/FileOutputBuffer.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Target/TargetMachine.h" | |
| #include "llvm/Transforms/Utils/Cloning.h" | |
| #include "llvm/Transforms/Utils/ModuleUtils.h" | |
| #include "llvm/Transforms/Utils/ValueMapper.h" | |
| using namespace llvm; | |
| using namespace llvm::object; | |
| static Error writeFile(StringRef Filename, StringRef Data) { | |
| Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr = | |
| FileOutputBuffer::create(Filename, Data.size()); | |
| if (!OutputOrErr) | |
| return OutputOrErr.takeError(); | |
| std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr); | |
| llvm::copy(Data, Output->getBufferStart()); | |
| if (Error E = Output->commit()) | |
| return E; | |
| return Error::success(); | |
| } | |
| // This is the first of many steps in creating a binary using llvm offload, | |
| // to run code on the gpu. Concrete, it replaces the following binary use: | |
| // clang-offload-packager -o device.bin | |
| // --image=file=device.bc,triple=amdgcn-amd-amdhsa,arch=gfx90a,kind=openmp | |
| // The input module is the rust code compiled for a gpu target like amdgpu. | |
| // Based on clang/tools/clang-offload-packager/ClangOffloadPackager.cpp | |
| extern "C" bool LLVMRustBundleImages(LLVMModuleRef M, TargetMachine &TM, | |
| const char *HostOutPath) { | |
| std::string Storage; | |
| llvm::raw_string_ostream OS1(Storage); | |
| llvm::WriteBitcodeToFile(*unwrap(M), OS1); | |
| OS1.flush(); | |
| auto MB = llvm::MemoryBuffer::getMemBufferCopy(Storage, "device.bc"); | |
| SmallVector<char, 1024> BinaryData; | |
| raw_svector_ostream OS2(BinaryData); | |
| OffloadBinary::OffloadingImage ImageBinary{}; | |
| ImageBinary.TheImageKind = object::IMG_Bitcode; | |
| ImageBinary.Image = std::move(MB); | |
| ImageBinary.TheOffloadKind = object::OFK_OpenMP; | |
| std::string TripleStr = TM.getTargetTriple().str(); | |
| llvm::StringRef CPURef = TM.getTargetCPU(); | |
| ImageBinary.StringData["triple"] = TripleStr; | |
| ImageBinary.StringData["arch"] = CPURef; | |
| llvm::SmallString<0> Buffer = OffloadBinary::write(ImageBinary); | |
| if (Buffer.size() % OffloadBinary::getAlignment() != 0) | |
| // Offload binary has invalid size alignment | |
| return false; | |
| OS2 << Buffer; | |
| if (Error E = writeFile(HostOutPath, | |
| StringRef(BinaryData.begin(), BinaryData.size()))) | |
| return false; | |
| return true; | |
| } | |
| extern "C" bool LLVMRustOffloadEmbedBufferInModule(LLVMModuleRef HostM, | |
| const char *HostOutPath) { | |
| auto MBOrErr = MemoryBuffer::getFile(HostOutPath); | |
| if (!MBOrErr) { | |
| auto E = MBOrErr.getError(); | |
| auto _B = errorCodeToError(E); | |
| return false; | |
| } | |
| MemoryBufferRef Buf = (*MBOrErr)->getMemBufferRef(); | |
| Module *M = unwrap(HostM); | |
| StringRef SectionName = ".llvm.offloading"; | |
| Align Alignment = Align(8); | |
| llvm::embedBufferInModule(*M, Buf, SectionName, Alignment); | |
| return true; | |
| } | |
| // Clone OldFn into NewFn, remapping its arguments to RebuiltArgs. | |
| // Each arg of OldFn is replaced with the corresponding value in RebuiltArgs. | |
| // For scalars, RebuiltArgs contains the value cast and/or truncated to the | |
| // original type. | |
| extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn, | |
| const LLVMValueRef *RebuiltArgs) { | |
| llvm::Function *oldFn = llvm::unwrap<llvm::Function>(OldFn); | |
| llvm::Function *newFn = llvm::unwrap<llvm::Function>(NewFn); | |
| // Map old arguments to new arguments. We skip the first dyn_ptr argument, | |
| // since it can't be used directly by user code. | |
| llvm::ValueToValueMapTy vmap; | |
| auto newArgIt = newFn->arg_begin(); | |
| newArgIt->setName("dyn_ptr"); | |
| unsigned i = 0; | |
| for (auto &oldArg : oldFn->args()) { | |
| vmap[&oldArg] = unwrap<Value>(RebuiltArgs[i++]); | |
| } | |
| llvm::SmallVector<llvm::ReturnInst *, 8> returns; | |
| llvm::CloneFunctionInto(newFn, oldFn, vmap, | |
| llvm::CloneFunctionChangeType::LocalChangesOnly, | |
| returns); | |
| BasicBlock &entry = newFn->getEntryBlock(); | |
| BasicBlock &clonedEntry = *std::next(newFn->begin()); | |
| if (entry.getTerminator()) | |
| entry.getTerminator()->eraseFromParent(); | |
| IRBuilder<> B(&entry); | |
| B.CreateBr(&clonedEntry); | |
| } |