| // 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/StringUtils.h" | 
 | #include "iree/compiler/Utils/ToolUtils.h" | 
 | #include "llvm/Support/MemoryBuffer.h" | 
 | #include "llvm/Support/Process.h" | 
 |  | 
 | #define DEBUG_TYPE "iree-tools" | 
 |  | 
 | namespace mlir::iree_compiler::IREE::HAL { | 
 |  | 
 | // static | 
 | Artifact Artifact::fromFile(StringRef path) { return {path.str(), nullptr}; } | 
 |  | 
 | // static | 
 | Artifact Artifact::createTemporary(StringRef prefix, StringRef suffix) { | 
 |   auto sanitizedPrefix = sanitizeFileName(prefix); | 
 |   auto sanitizedSuffix = sanitizeFileName(suffix); | 
 |  | 
 |   llvm::SmallString<32> filePath; | 
 |   if (std::error_code error = llvm::sys::fs::createTemporaryFile( | 
 |           sanitizedPrefix, sanitizedSuffix, filePath)) { | 
 |     llvm::errs() << "failed to generate temporary file: " << error.message(); | 
 |     return {}; | 
 |   } | 
 |   std::error_code error; | 
 |   auto file = std::make_unique<llvm::ToolOutputFile>(filePath, error, | 
 |                                                      llvm::sys::fs::OF_None); | 
 |   if (error) { | 
 |     llvm::errs() << "failed to open temporary file '" << filePath | 
 |                  << "': " << error.message(); | 
 |     return {}; | 
 |   } | 
 |   return {filePath.str().str(), std::move(file)}; | 
 | } | 
 |  | 
 | // static | 
 | Artifact Artifact::createVariant(StringRef basePath, StringRef suffix) { | 
 |   SmallString<32> filePath(basePath); | 
 |   llvm::sys::path::replace_extension(filePath, suffix); | 
 |   std::error_code error; | 
 |   auto file = std::make_unique<llvm::ToolOutputFile>(filePath, error, | 
 |                                                      llvm::sys::fs::OF_Append); | 
 |   if (error) { | 
 |     llvm::errs() << "failed to open temporary file '" << filePath | 
 |                  << "': " << error.message(); | 
 |     return {}; | 
 |   } | 
 |   return {filePath.str().str(), std::move(file)}; | 
 | } | 
 |  | 
 | void Artifact::keep() const { | 
 |   if (outputFile) | 
 |     outputFile->keep(); | 
 | } | 
 |  | 
 | std::optional<std::vector<int8_t>> Artifact::read() const { | 
 |   auto fileData = llvm::MemoryBuffer::getFile(path); | 
 |   if (!fileData) { | 
 |     llvm::errs() << "failed to load library output file '" << path << "'"; | 
 |     return std::nullopt; | 
 |   } | 
 |   auto sourceBuffer = fileData.get()->getBuffer(); | 
 |   std::vector<int8_t> resultBuffer(sourceBuffer.size()); | 
 |   std::memcpy(resultBuffer.data(), sourceBuffer.data(), sourceBuffer.size()); | 
 |   return resultBuffer; | 
 | } | 
 |  | 
 | bool Artifact::readInto(raw_ostream &targetStream) const { | 
 |   // NOTE: we could make this much more efficient if we read in the file a | 
 |   // chunk at a time and piped it along to targetStream. I couldn't find | 
 |   // anything in LLVM that did this, for some crazy reason, but since we are | 
 |   // dealing with binaries that can be 10+MB here it'd be nice if we could avoid | 
 |   // reading them all into memory. | 
 |   auto fileData = llvm::MemoryBuffer::getFile(path); | 
 |   if (!fileData) { | 
 |     llvm::errs() << "failed to load library output file '" << path << "'"; | 
 |     return false; | 
 |   } | 
 |   auto sourceBuffer = fileData.get()->getBuffer(); | 
 |   targetStream.write(sourceBuffer.data(), sourceBuffer.size()); | 
 |   return true; | 
 | } | 
 |  | 
 | void Artifact::close() { outputFile->os().close(); } | 
 |  | 
 | void Artifacts::keepAllFiles() { | 
 |   libraryFile.keep(); | 
 |   debugFile.keep(); | 
 |   for (auto &file : otherFiles) { | 
 |     file.keep(); | 
 |   } | 
 | } | 
 |  | 
 | std::string LinkerTool::getSystemToolPath() const { | 
 |   // Always use the --iree-llvmcpu-system-linker-path flag when specified as | 
 |   // it's explicitly telling us what to use. | 
 |   if (!targetOptions.systemLinkerPath.empty()) { | 
 |     return targetOptions.systemLinkerPath; | 
 |   } | 
 |  | 
 |   // Allow users to override the automatic search with an environment variable. | 
 |   char *linkerPath = std::getenv("IREE_LLVM_SYSTEM_LINKER_PATH"); | 
 |   if (linkerPath) { | 
 |     return std::string(linkerPath); | 
 |   } | 
 |  | 
 |   // Fallback to other searches as specified by the LinkerTool implementation. | 
 |   return ""; | 
 | } | 
 |  | 
 | LogicalResult LinkerTool::runLinkCommand(std::string commandLine, | 
 |                                          StringRef env) { | 
 |   LLVM_DEBUG(llvm::dbgs() << "Running linker command:\n" | 
 |                           << env << " " << commandLine << "\n"); | 
 |   if (!env.empty()) { | 
 | #if defined(_WIN32) | 
 |     commandLine = ("set " + env + " && " + commandLine).str(); | 
 | #else | 
 |     commandLine = (env + " " + commandLine).str(); | 
 | #endif // _WIN32 | 
 |   } else { | 
 |     commandLine = escapeCommandLineComponent(commandLine); | 
 |   } | 
 |   int exitCode = system(commandLine.c_str()); | 
 |   if (exitCode == 0) | 
 |     return success(); | 
 |   llvm::errs() << "Linking failed; escaped command line returned exit code " | 
 |                << exitCode << ":\n\n" | 
 |                << commandLine << "\n\n"; | 
 |   return failure(); | 
 | } | 
 |  | 
 | } // namespace mlir::iree_compiler::IREE::HAL |