| // Copyright 2020 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/FileSystem.h" | 
 | #include "llvm/Support/FormatVariadic.h" | 
 |  | 
 | #define DEBUG_TYPE "llvm-linker" | 
 |  | 
 | namespace mlir::iree_compiler::IREE::HAL { | 
 |  | 
 | // Unix linker (ld-like); for ELF files. | 
 | class UnixLinkerTool : public LinkerTool { | 
 | public: | 
 |   using LinkerTool::LinkerTool; | 
 |  | 
 |   std::string getSystemToolPath() const override { | 
 |     // First check for setting the linker explicitly. | 
 |     auto toolPath = LinkerTool::getSystemToolPath(); | 
 |     if (!toolPath.empty()) | 
 |       return toolPath; | 
 |  | 
 |     // No explicit linker specified, search the environment for common tools. | 
 |     // We want LLD: | 
 |     // * On Apple, we want the system linker, which is named `ld` | 
 |     if (targetIsApple()) { | 
 |       // On macOS, the standard system linker is `ld`, and it's | 
 |       // unconditionally what we want to use. | 
 |       toolPath = findToolInEnvironment({"ld"}); | 
 |     } else { | 
 |       // On Linux, the only linker basename that's standard is `ld` but it could | 
 |       // be any of ld.bfd, ld.gold, ld.lld, which are inequivalent in the way | 
 |       // explained in the comment below on the -shared flag. We specifically | 
 |       // want ld.lld here, however we still search for `ld` as a fallback name, | 
 |       // in case the linker would be ld.lld but would be installed only under | 
 |       // the name `ld`. | 
 |       // | 
 |       // Having `ld` as a fallback name also makes sense (at least | 
 |       // theoretically) on "generic Unix": `ld` is the standard name of the | 
 |       // system linker, and `-static -shared` should in theory be supported by | 
 |       // the system linker (as suggested by both the FreeBSD and GNU man pages | 
 |       // for ld). | 
 |       // | 
 |       // On the other hand, on Linux where the possible fallbacks are ld.bfd or | 
 |       // ld.gold, we are specifically not interested in falling back on any | 
 |       // of these, at least given current behavior. | 
 |       toolPath = findToolInEnvironment({"ld.lld", "ld"}); | 
 |     } | 
 |     if (!toolPath.empty()) | 
 |       return toolPath; | 
 |  | 
 |     llvm::errs() << "No Unix linker tool found in environment.\n"; | 
 |     return ""; | 
 |   } | 
 |  | 
 |   std::optional<Artifacts> | 
 |   linkDynamicLibrary(StringRef libraryName, | 
 |                      ArrayRef<Artifact> objectFiles) override { | 
 |     Artifacts artifacts; | 
 |  | 
 |     // Create the shared object 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, "so"); | 
 |     } else { | 
 |       artifacts.libraryFile = Artifact::createTemporary(libraryName, "so"); | 
 |     } | 
 |     artifacts.libraryFile.close(); | 
 |  | 
 |     SmallVector<std::string, 8> flags = { | 
 |         getSystemToolPath(), | 
 |         "-o " + artifacts.libraryFile.path, | 
 |     }; | 
 |  | 
 |     if (targetIsApple()) { | 
 |       // Statically link all dependencies so we don't have any runtime deps. | 
 |       // We cannot have any imports in the module we produce. | 
 |       flags.push_back("-static"); | 
 |  | 
 |       // Produce a Mach-O dylib file. | 
 |       flags.push_back("-dylib"); | 
 |       flags.push_back("-flat_namespace"); | 
 |       flags.push_back( | 
 |           "-L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib " | 
 |           "-lSystem"); | 
 |     } else { | 
 |       // Avoids including any libc/startup files that initialize the CRT as | 
 |       // we don't use any of that. Our shared libraries must be freestanding. | 
 |       flags.push_back("-nostdlib"); // -nodefaultlibs + -nostartfiles | 
 |  | 
 |       // Statically link all dependencies so we don't have any runtime deps. | 
 |       // We cannot have any imports in the module we produce. | 
 |       flags.push_back("-static"); | 
 |  | 
 |       // Generate a dynamic library (ELF type: ET_DYN), otherwise dlopen() | 
 |       // won't succeed on it. This is not incompatible with -static. The GNU | 
 |       // man page for ld, `man ld`, says the following: | 
 |       // | 
 |       //   -static | 
 |       //       Do not link against shared libraries. [...] This option can be | 
 |       //       used with -shared. Doing so means that a shared library is | 
 |       //       being created but that all of the library's external references | 
 |       //       must be resolved by pulling in entries from static libraries. | 
 |       // | 
 |       // While that much is said in the GNU ld man page, the reality is that | 
 |       // out of ld.bfd, ld.gold and ld.lld, only ld.lld actually implements | 
 |       // that. Meanwhile, ld.bfd interprets -static -shared as just -static, | 
 |       // and ld.gold rejects -static -shared outright as "incompatible". | 
 |       flags.push_back("-shared"); | 
 |     } | 
 |  | 
 |     // Strip debug information (only, no relocations) 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; | 
 |   } | 
 |  | 
 | private: | 
 |   bool targetIsApple() const { | 
 |     return targetTriple.isOSDarwin() || targetTriple.isiOS(); | 
 |   } | 
 | }; | 
 |  | 
 | std::unique_ptr<LinkerTool> | 
 | createUnixLinkerTool(const llvm::Triple &targetTriple, | 
 |                      LLVMTargetOptions &targetOptions) { | 
 |   return std::make_unique<UnixLinkerTool>(targetTriple, targetOptions); | 
 | } | 
 |  | 
 | } // namespace mlir::iree_compiler::IREE::HAL |