// 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
//

#ifndef IREE_COMPILER_PLUGINS_TARGET_LLVMCPU_LINKERTOOL_H_
#define IREE_COMPILER_PLUGINS_TARGET_LLVMCPU_LINKERTOOL_H_

#include <string>

#include "compiler/plugins/target/LLVMCPU/LLVMTargetOptions.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/TargetParser/Triple.h"
#include "mlir/Support/LogicalResult.h"

namespace mlir::iree_compiler::IREE::HAL {

struct Artifact {
  // Wraps an existing file on the file system.
  // The file will not be deleted when the artifact is destroyed.
  static Artifact fromFile(StringRef path);

  // Creates an output file path/container pair.
  // By default the file will be deleted when the link completes; callers must
  // use llvm::ToolOutputFile::keep() to prevent deletion upon success (or if
  // leaving artifacts for debugging).
  static Artifact createTemporary(StringRef prefix, StringRef suffix);

  // Creates an output file derived from the given file's path with a new
  // suffix.
  static Artifact createVariant(StringRef basePath, StringRef suffix);

  Artifact() = default;
  Artifact(std::string path, std::unique_ptr<llvm::ToolOutputFile> outputFile)
      : path(std::move(path)), outputFile(std::move(outputFile)) {}

  std::string path;
  std::unique_ptr<llvm::ToolOutputFile> outputFile;

  // Preserves the file contents on disk after the artifact has been destroyed.
  void keep() const;

  // Reads the artifact file contents as bytes.
  std::optional<std::vector<int8_t>> read() const;

  // Reads the artifact file and writes it into the given |stream|.
  bool readInto(raw_ostream &targetStream) const;

  // Closes the ostream of the file while preserving the temporary entry on
  // disk. Use this if files need to be modified by external tools that may
  // require exclusive access.
  void close();
};

struct Artifacts {
  // File containing the linked library (DLL, ELF, etc).
  Artifact libraryFile;

  // Optional file containing associated debug information (if stored
  // separately, such as PDB files).
  Artifact debugFile;

  // Other files associated with linking.
  SmallVector<Artifact> otherFiles;

  // Keeps all of the artifacts around after linking completes. Useful for
  // debugging.
  void keepAllFiles();
};

// Base type for linker tools that can turn object files into shared objects.
class LinkerTool {
public:
  // Gets an instance of a linker tool for the given target options. This may
  // be a completely different toolchain than that of the host.
  static std::unique_ptr<LinkerTool>
  getForTarget(const llvm::Triple &targetTriple,
               LLVMTargetOptions &targetOptions);

  explicit LinkerTool(llvm::Triple targetTriple,
                      LLVMTargetOptions targetOptions)
      : targetTriple(std::move(targetTriple)),
        targetOptions(std::move(targetOptions)) {}

  virtual ~LinkerTool() = default;

  // Returns the path to the system linker tool binary, or empty string if none
  // was discovered.
  virtual std::string getSystemToolPath() const;

  // Configures a module prior to compilation with any additional
  // functions/exports it may need, such as shared object initializer functions.
  virtual LogicalResult
  configureModule(llvm::Module *llvmModule,
                  ArrayRef<llvm::Function *> exportedFuncs) {
    return success();
  }

  // Links the given object files into a dynamically loadable library.
  // The resulting library (and other associated artifacts) will be returned on
  // success.
  virtual std::optional<Artifacts>
  linkDynamicLibrary(StringRef libraryName, ArrayRef<Artifact> objectFiles) = 0;

protected:
  // Runs the given command line on the shell, logging failures.
  LogicalResult runLinkCommand(std::string commandLine, StringRef env = "");

  llvm::Triple targetTriple;
  LLVMTargetOptions targetOptions;
};

} // namespace mlir::iree_compiler::IREE::HAL

#endif // IREE_COMPILER_PLUGINS_TARGET_LLVMCPU_LINKERTOOL_H_
