| // Copyright 2021 The IREE Authors | |
| // | |
| // Licensed under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| #include "compiler/plugins/target/LLVMCPU/LinkerTool.h" | |
| #include "iree/compiler/Utils/ToolUtils.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/IRBuilder.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| #define DEBUG_TYPE "llvm-linker" | |
| namespace mlir::iree_compiler::IREE::HAL { | |
| // Wasm linker using wasm-ld for producing WebAssembly binaries. | |
| // wasm-ld behaves like traditional ELF linkers and uses similar flags. | |
| // | |
| // For details on the linking process and file formats, see: | |
| // * https://lld.llvm.org/WebAssembly.html | |
| // * https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md | |
| // | |
| // For more background on WebAssembly, see: | |
| // * https://webassembly.org/ | |
| // * https://developer.mozilla.org/en-US/docs/WebAssembly | |
| // | |
| // When working with WebAssembly files, these projects are useful: | |
| // * https://github.com/WebAssembly/wabt | |
| // * https://github.com/bytecodealliance/wasmtime | |
| // | |
| // Use with `-iree-llvmcpu-target-triple=wasm32-unknown-unknown` (or | |
| // equivalent). For SIMD support, also set | |
| // `-iree-llvmcpu-target-cpu-features=+simd128`. | |
| class WasmLinkerTool : public LinkerTool { | |
| public: | |
| using LinkerTool::LinkerTool; | |
| std::string getWasmToolPath() const { | |
| // Always use the --iree-llvmcpu-wasm-linker-path flag when specified as | |
| // it's explicitly telling us what to use. | |
| if (!targetOptions.wasmLinkerPath.empty()) { | |
| return targetOptions.wasmLinkerPath; | |
| } | |
| // Allow overriding the automatic search with an environment variable. | |
| char *linkerPath = std::getenv("IREE_LLVM_WASM_LINKER_PATH"); | |
| if (linkerPath) { | |
| return std::string(linkerPath); | |
| } | |
| // No explicit linker specified, search the environment (i.e. our own build | |
| // or install directories) for common tools. | |
| std::string toolPath = findToolFromExecutableDir( | |
| {"wasm-ld", "iree-lld", "lld", "ld.lld", "lld-link"}); | |
| if (!toolPath.empty()) | |
| return toolPath; | |
| llvm::errs() << "No Wasm linker tool specified or discovered\n"; | |
| return ""; | |
| } | |
| LogicalResult | |
| configureModule(llvm::Module *llvmModule, | |
| ArrayRef<llvm::Function *> exportedFuncs) override { | |
| // https://lld.llvm.org/WebAssembly.html#exports | |
| // Note: once we can set --shared this shouldn't be needed, since we set | |
| // default visibility on exported functions. | |
| for (auto func : exportedFuncs) { | |
| func->addFnAttr("wasm-export-name", func->getName()); | |
| } | |
| return success(); | |
| } | |
| std::optional<Artifacts> | |
| linkDynamicLibrary(StringRef libraryName, | |
| ArrayRef<Artifact> objectFiles) override { | |
| Artifacts artifacts; | |
| // Create the wasm binary file name; if we only have a single input object | |
| // we can just reuse that. | |
| if (objectFiles.size() == 1) { | |
| artifacts.libraryFile = | |
| Artifact::createVariant(objectFiles.front().path, "wasm"); | |
| } else { | |
| artifacts.libraryFile = Artifact::createTemporary(libraryName, "wasm"); | |
| } | |
| artifacts.libraryFile.close(); | |
| SmallVector<std::string, 8> flags = { | |
| getWasmToolPath(), | |
| // Forces LLD to act like wasm ld and produce WebAssembly files. | |
| // If not specified then lld tries to figure out what it is by progname | |
| // (ld, ld64, link, etc). | |
| // NOTE: must be first because lld sniffs argv[1]/argv[2]. | |
| "-flavor wasm", | |
| // entry symbol not defined (pass --no-entry to suppress): _start | |
| "--no-entry", | |
| // Treat warnings as errors. | |
| "--fatal-warnings", | |
| // Generated a shared object containing position-independent-code. | |
| "--experimental-pic", | |
| "--shared", | |
| // Import [shared] memory from the environment. | |
| // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory#creating_a_shared_memory | |
| // TODO(scotttodd): Add a flag controlling these - some combination is | |
| // required when using multithreading + SharedArrayBuffer, but they | |
| // must be left off when running single threaded. | |
| // "--import-memory", | |
| // "--shared-memory", | |
| "-o " + artifacts.libraryFile.path, | |
| }; | |
| // Strip debug information when not requested. | |
| if (!targetOptions.target.debugSymbols) { | |
| flags.push_back("--strip-debug"); | |
| } | |
| // Link all input objects. Note that we are not linking whole-archive as | |
| // we want to allow dropping of unused codegen outputs. | |
| for (auto &objectFile : objectFiles) { | |
| flags.push_back(objectFile.path); | |
| } | |
| auto commandLine = llvm::join(flags, " "); | |
| if (failed(runLinkCommand(commandLine))) | |
| return std::nullopt; | |
| return artifacts; | |
| } | |
| }; | |
| std::unique_ptr<LinkerTool> | |
| createWasmLinkerTool(const llvm::Triple &targetTriple, | |
| LLVMTargetOptions &targetOptions) { | |
| return std::make_unique<WasmLinkerTool>(targetTriple, targetOptions); | |
| } | |
| } // namespace mlir::iree_compiler::IREE::HAL |