-
Notifications
You must be signed in to change notification settings - Fork 0
[clang-sycl-linker] Replace llvm-link with API calls #4
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
base: master
Are you sure you want to change the base?
Changes from 6 commits
ce34ccd
ae0210f
ee487ff
a9255b4
e0d1859
c1611f0
596488c
454c386
f4cc3cf
d76a3a1
7249cb8
59c0bcd
a5acaa3
0649b61
987ab66
917987d
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 |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ | |
| #include "llvm/IR/DiagnosticPrinter.h" | ||
| #include "llvm/IRReader/IRReader.h" | ||
| #include "llvm/LTO/LTO.h" | ||
| #include "llvm/Linker/Linker.h" | ||
| #include "llvm/Object/Archive.h" | ||
| #include "llvm/Object/ArchiveWriter.h" | ||
| #include "llvm/Object/Binary.h" | ||
|
|
@@ -180,7 +181,7 @@ Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) { | |
| } | ||
|
|
||
| Expected<SmallVector<std::string>> getInput(const ArgList &Args) { | ||
| // Collect all input bitcode files to be passed to llvm-link. | ||
| // Collect all input bitcode files to be passed to the device linking stage. | ||
| SmallVector<std::string> BitcodeFiles; | ||
| for (const opt::Arg *Arg : Args.filtered(OPT_INPUT)) { | ||
| std::optional<std::string> Filename = std::string(Arg->getValue()); | ||
|
|
@@ -190,49 +191,48 @@ Expected<SmallVector<std::string>> getInput(const ArgList &Args) { | |
| file_magic Magic; | ||
| if (auto EC = identify_magic(*Filename, Magic)) | ||
| return createStringError("Failed to open file " + *Filename); | ||
| // TODO: Current use case involves LLVM IR bitcode files as input. | ||
| // This will be extended to support objects and SPIR-V IR files. | ||
| if (Magic != file_magic::bitcode) | ||
| return createStringError("Unsupported file type"); | ||
| BitcodeFiles.push_back(*Filename); | ||
asudarsa marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| return BitcodeFiles; | ||
| } | ||
|
|
||
| /// Link all SYCL device input files into one before adding device library | ||
| /// files. Device linking is performed using llvm-link tool. | ||
| /// 'InputFiles' is the list of all LLVM IR device input files. | ||
| /// 'Args' encompasses all arguments required for linking device code and will | ||
| /// be parsed to generate options required to be passed into llvm-link. | ||
| Expected<StringRef> linkDeviceInputFiles(ArrayRef<std::string> InputFiles, | ||
| const ArgList &Args) { | ||
| llvm::TimeTraceScope TimeScope("SYCL LinkDeviceInputFiles"); | ||
|
|
||
| assert(InputFiles.size() && "No inputs to llvm-link"); | ||
| // Early check to see if there is only one input. | ||
| if (InputFiles.size() < 2) | ||
| return InputFiles[0]; | ||
|
|
||
| Expected<std::string> LLVMLinkPath = | ||
| findProgram(Args, "llvm-link", {getMainExecutable("llvm-link")}); | ||
| if (!LLVMLinkPath) | ||
| return LLVMLinkPath.takeError(); | ||
|
|
||
| SmallVector<StringRef> CmdArgs; | ||
| CmdArgs.push_back(*LLVMLinkPath); | ||
| for (auto &File : InputFiles) | ||
| CmdArgs.push_back(File); | ||
| // Create a new file to write the linked device file to. | ||
| auto OutFileOrErr = | ||
| createTempFile(Args, sys::path::filename(OutputFile), "bc"); | ||
| if (!OutFileOrErr) | ||
| return OutFileOrErr.takeError(); | ||
| CmdArgs.push_back("-o"); | ||
| CmdArgs.push_back(*OutFileOrErr); | ||
| CmdArgs.push_back("--suppress-warnings"); | ||
| if (Error Err = executeCommands(*LLVMLinkPath, CmdArgs)) | ||
| Expected<std::unique_ptr<Module>> getBitcodeModule(StringRef File, | ||
| LLVMContext &C) { | ||
| SMDiagnostic Err; | ||
|
|
||
| // Handle cases where input file is a LLVM IR bitcode file. | ||
| // When clang-sycl-linker is called via clang-linker-wrapper tool, input files | ||
| // are LLVM IR bitcode files | ||
| // TODO: Support SPIR-V IR files | ||
| auto M = getLazyIRFileModule(File, Err, C); | ||
| if (M) | ||
| return std::move(M); | ||
|
|
||
| // Handle cases where input file is a fat object containing LLVM IR bitcode | ||
| // SYCL device libraries are provided as fat objects containing LLVM IR | ||
| // bitcode | ||
|
||
| ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = | ||
| MemoryBuffer::getFile(File); | ||
| if (std::error_code EC = BufferOrErr.getError()) | ||
| return createFileError(File, EC); | ||
| MemoryBufferRef Buffer = **BufferOrErr; | ||
| SmallVector<OffloadFile> Binaries; | ||
| if (Error Err = extractOffloadBinaries(Buffer, Binaries)) | ||
| return std::move(Err); | ||
| return Args.MakeArgString(*OutFileOrErr); | ||
| // Only one binary of bitcode image type is expected | ||
| assert((Binaries.size() == 1) && "Only one binary per file is expected"); | ||
| auto Bin = std::move(Binaries.front()); | ||
| // TODO: Current use case involves LLVM IR bitcode files as input. | ||
| // This will be extended to support objects and SPIR-V IR files. | ||
| auto TheImage = Bin.getBinary()->getImage(); | ||
| if (identify_magic(TheImage) != file_magic::bitcode) | ||
| return createStringError("Unsupported file type"); | ||
| // TODO: Try to replace this call with getLazyIRModule | ||
| M = parseIR(MemoryBufferRef(TheImage, ""), Err, C); | ||
| if (M) | ||
| return std::move(M); | ||
|
|
||
| return createStringError("Unsupported file type"); | ||
| } | ||
|
|
||
| // This utility function is used to gather all SYCL device library files that | ||
|
|
@@ -264,44 +264,81 @@ Expected<SmallVector<std::string>> getSYCLDeviceLibs(const ArgList &Args) { | |
| return DeviceLibFiles; | ||
| } | ||
|
|
||
| /// Link all device library files and input file into one LLVM IR file. This | ||
| /// linking is performed using llvm-link tool. | ||
| /// 'InputFiles' is the list of all LLVM IR device input files. | ||
| /// 'Args' encompasses all arguments required for linking device code and will | ||
| /// be parsed to generate options required to be passed into llvm-link tool. | ||
| static Expected<StringRef> linkDeviceLibFiles(StringRef InputFile, | ||
| const ArgList &Args) { | ||
| llvm::TimeTraceScope TimeScope("LinkDeviceLibraryFiles"); | ||
| /// Following tasks are performed: | ||
| /// 1. Link all SYCL device bitcode images into one image. Device linking is | ||
| /// performed using the linkInModule API. | ||
| /// 2. Gather all SYCL device library bitcode images. | ||
| /// 3. Link all the images gathered in Step 2 with the output of Step 1 using | ||
| /// linkInModule API. LinkOnlyNeeded flag is used. | ||
| Expected<StringRef> linkDeviceCode(ArrayRef<std::string> InputFiles, | ||
| const ArgList &Args) { | ||
| llvm::TimeTraceScope TimeScope("SYCL link device code"); | ||
| LLVMContext C; | ||
asudarsa marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| assert(InputFiles.size() && "No inputs to link"); | ||
|
|
||
| // Create a new file to write the linked device file to | ||
| auto BitcodeOutput = | ||
| createTempFile(Args, sys::path::filename(OutputFile), "bc"); | ||
| if (!BitcodeOutput) | ||
| return BitcodeOutput.takeError(); | ||
|
|
||
| // Get all SYCL device library files, if any | ||
| auto SYCLDeviceLibFiles = getSYCLDeviceLibs(Args); | ||
| if (!SYCLDeviceLibFiles) | ||
| return SYCLDeviceLibFiles.takeError(); | ||
| if ((*SYCLDeviceLibFiles).empty()) | ||
| return InputFile; | ||
|
|
||
| Expected<std::string> LLVMLinkPath = | ||
| findProgram(Args, "llvm-link", {getMainExecutable("llvm-link")}); | ||
| if (!LLVMLinkPath) | ||
| return LLVMLinkPath.takeError(); | ||
| if (DryRun || Verbose) { | ||
| std::string Inputs = | ||
| std::accumulate(std::next(InputFiles.begin()), InputFiles.end(), | ||
| InputFiles.front(), [](std::string a, std::string b) { | ||
| return std::move(a) + ", " + std::move(b); | ||
| }); | ||
| std::string LibInputs = ""; | ||
| if (!(*SYCLDeviceLibFiles).empty()) | ||
| LibInputs = std::accumulate( | ||
| std::next((*SYCLDeviceLibFiles).begin()), (*SYCLDeviceLibFiles).end(), | ||
| (*SYCLDeviceLibFiles).front(), [](std::string a, std::string b) { | ||
| return std::move(a) + ", " + std::move(b); | ||
| }); | ||
| errs() << formatv( | ||
| "sycl-device-link: inputs: {0} libfiles: {1} output: {2}\n", Inputs, | ||
| LibInputs, *BitcodeOutput); | ||
| if (DryRun) | ||
| return *BitcodeOutput; | ||
| } | ||
asudarsa marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // Create a new file to write the linked device file to. | ||
| auto OutFileOrErr = | ||
| createTempFile(Args, sys::path::filename(OutputFile), "bc"); | ||
| if (!OutFileOrErr) | ||
| return OutFileOrErr.takeError(); | ||
| auto LinkerOutput = std::make_unique<Module>("sycl-device-link", C); | ||
| Linker L(*LinkerOutput); | ||
| // Link SYCL device input files. | ||
| for (auto &File : InputFiles) { | ||
| auto ModOrErr = getBitcodeModule(File, C); | ||
| if (!ModOrErr) | ||
| return ModOrErr.takeError(); | ||
| if (L.linkInModule(std::move(*ModOrErr))) | ||
| return createStringError("Could not link IR"); | ||
| } | ||
|
|
||
| SmallVector<StringRef, 8> CmdArgs; | ||
| CmdArgs.push_back(*LLVMLinkPath); | ||
| CmdArgs.push_back("-only-needed"); | ||
| CmdArgs.push_back(InputFile); | ||
| for (auto &File : *SYCLDeviceLibFiles) | ||
| CmdArgs.push_back(File); | ||
| CmdArgs.push_back("-o"); | ||
| CmdArgs.push_back(*OutFileOrErr); | ||
| CmdArgs.push_back("--suppress-warnings"); | ||
| if (Error Err = executeCommands(*LLVMLinkPath, CmdArgs)) | ||
| return std::move(Err); | ||
| return *OutFileOrErr; | ||
| // Link in SYCL device library files. | ||
| const llvm::Triple Triple(Args.getLastArgValue(OPT_triple)); | ||
| for (auto &File : *SYCLDeviceLibFiles) { | ||
| auto LibMod = getBitcodeModule(File, C); | ||
| if (!LibMod) | ||
| return LibMod.takeError(); | ||
| if ((*LibMod)->getTargetTriple() == Triple) { | ||
| unsigned Flags = Linker::Flags::None; | ||
| if (L.linkInModule(std::move(*LibMod), Flags)) | ||
| return createStringError("Could not link IR"); | ||
| } | ||
| } | ||
|
|
||
| // Write the final output into 'BitcodeOutput' file | ||
| int FD = -1; | ||
| if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeOutput, FD)) | ||
| return errorCodeToError(EC); | ||
| llvm::raw_fd_ostream OS(FD, true); | ||
| WriteBitcodeToFile(*LinkerOutput, OS); | ||
| return *BitcodeOutput; | ||
| } | ||
|
|
||
| /// Add any llvm-spirv option that relies on a specific Triple in addition | ||
|
|
@@ -345,7 +382,7 @@ static void getSPIRVTransOpts(const ArgList &Args, | |
| ",+SPV_INTEL_arbitrary_precision_fixed_point" | ||
| ",+SPV_INTEL_arbitrary_precision_floating_point" | ||
| ",+SPV_INTEL_variable_length_array,+SPV_INTEL_fp_fast_math_mode" | ||
| ",+SPV_INTEL_long_constant_composite" | ||
| ",+SPV_INTEL_long_composites" | ||
| ",+SPV_INTEL_arithmetic_fence" | ||
| ",+SPV_INTEL_global_variable_decorations" | ||
| ",+SPV_INTEL_cache_controls" | ||
|
|
@@ -424,18 +461,14 @@ static Expected<StringRef> runLLVMToSPIRVTranslation(StringRef File, | |
|
|
||
| Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) { | ||
| llvm::TimeTraceScope TimeScope("SYCLDeviceLink"); | ||
| // First llvm-link step | ||
| auto LinkedFile = linkDeviceInputFiles(Files, Args); | ||
|
|
||
| // Link all input bitcode files and SYCL device library files, if any | ||
| auto LinkedFile = linkDeviceCode(Files, Args); | ||
| if (!LinkedFile) | ||
| reportError(LinkedFile.takeError()); | ||
|
|
||
| // second llvm-link step | ||
| auto DeviceLinkedFile = linkDeviceLibFiles(*LinkedFile, Args); | ||
| if (!DeviceLinkedFile) | ||
| reportError(DeviceLinkedFile.takeError()); | ||
|
|
||
| // LLVM to SPIR-V translation step | ||
| auto SPVFile = runLLVMToSPIRVTranslation(*DeviceLinkedFile, Args); | ||
| auto SPVFile = runLLVMToSPIRVTranslation(*LinkedFile, Args); | ||
| if (!SPVFile) | ||
| return SPVFile.takeError(); | ||
| return Error::success(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.