blob: 9e2e4fe6c6fced8b722c488bb2834bdb9e5161d0 [file] [log] [blame]
// 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