about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/bootstrap/configure.py2
-rw-r--r--src/bootstrap/defaults/config.compiler.toml2
-rw-r--r--src/bootstrap/defaults/config.dist.toml2
-rw-r--r--src/bootstrap/defaults/config.library.toml2
-rw-r--r--src/bootstrap/defaults/config.tools.toml2
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs10
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs1
-rw-r--r--src/bootstrap/src/core/builder.rs1
-rw-r--r--src/bootstrap/src/core/config/config.rs4
-rw-r--r--src/bootstrap/src/lib.rs1
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
-rw-r--r--src/ci/docker/host-x86_64/dist-various-2/Dockerfile2
-rw-r--r--src/tools/llvm-bitcode-linker/Cargo.toml14
-rw-r--r--src/tools/llvm-bitcode-linker/README.md5
-rw-r--r--src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs62
-rw-r--r--src/tools/llvm-bitcode-linker/src/lib.rs7
-rw-r--r--src/tools/llvm-bitcode-linker/src/linker.rs163
-rw-r--r--src/tools/llvm-bitcode-linker/src/opt.rs53
-rw-r--r--src/tools/llvm-bitcode-linker/src/target.rs20
19 files changed, 357 insertions, 1 deletions
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index 4257c0f7991..9c43160d455 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -54,6 +54,7 @@ o("cargo-native-static", "build.cargo-native-static", "static native libraries i
 o("profiler", "build.profiler", "build the profiler runtime")
 o("full-tools", None, "enable all tools")
 o("lld", "rust.lld", "build lld")
+o("llvm-bitcode-linker", "rust.llvm-bitcode-linker", "build llvm bitcode linker")
 o("clang", "llvm.clang", "build clang")
 o("use-libcxx", "llvm.use-libcxx", "build LLVM with libc++")
 o("control-flow-guard", "rust.control-flow-guard", "Enable Control Flow Guard")
@@ -366,6 +367,7 @@ def apply_args(known_args, option_checking, config):
             set('rust.codegen-backends', ['llvm'], config)
             set('rust.lld', True, config)
             set('rust.llvm-tools', True, config)
+            set('rust.llvm-bitcode-linker', True, config)
             set('build.extended', True, config)
         elif option.name in ['option-checking', 'verbose-configure']:
             # this was handled above
diff --git a/src/bootstrap/defaults/config.compiler.toml b/src/bootstrap/defaults/config.compiler.toml
index e276f126211..d93c5fd25a1 100644
--- a/src/bootstrap/defaults/config.compiler.toml
+++ b/src/bootstrap/defaults/config.compiler.toml
@@ -17,6 +17,8 @@ lto = "off"
 # Forces frame pointers to be used with `-Cforce-frame-pointers`.
 # This can be helpful for profiling at a small performance cost.
 frame-pointers = true
+# Build the llvm-bitcode-linker as it is required for running nvptx tests
+llvm-bitcode-linker = true
 
 [llvm]
 # Having this set to true disrupts compiler development workflows for people who use `llvm.download-ci-llvm = true`
diff --git a/src/bootstrap/defaults/config.dist.toml b/src/bootstrap/defaults/config.dist.toml
index 44efdf50b96..f3c6ffc9bd5 100644
--- a/src/bootstrap/defaults/config.dist.toml
+++ b/src/bootstrap/defaults/config.dist.toml
@@ -16,6 +16,8 @@ download-ci-llvm = false
 # Make sure they don't get set when installing from source.
 channel = "nightly"
 download-rustc = false
+# Build the llvm-bitcode-linker as it is required for running nvptx tests
+llvm-bitcode-linker = true
 
 [dist]
 # Use better compression when preparing tarballs.
diff --git a/src/bootstrap/defaults/config.library.toml b/src/bootstrap/defaults/config.library.toml
index 087544397f5..4a1a49b7275 100644
--- a/src/bootstrap/defaults/config.library.toml
+++ b/src/bootstrap/defaults/config.library.toml
@@ -10,6 +10,8 @@ bench-stage = 0
 incremental = true
 # Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown.
 lto = "off"
+# Build the llvm-bitcode-linker as it is required for running nvptx tests
+llvm-bitcode-linker = true
 
 [llvm]
 # Will download LLVM from CI if available on your platform.
diff --git a/src/bootstrap/defaults/config.tools.toml b/src/bootstrap/defaults/config.tools.toml
index 6e6c3660027..94c8b724cbf 100644
--- a/src/bootstrap/defaults/config.tools.toml
+++ b/src/bootstrap/defaults/config.tools.toml
@@ -12,6 +12,8 @@ incremental = true
 # Using these defaults will download the stage2 compiler (see `download-rustc`
 # setting) and the stage2 toolchain should therefore be used for these defaults.
 download-rustc = "if-unchanged"
+# Build the llvm-bitcode-linker as it is required for running nvptx tests
+llvm-bitcode-linker = true
 
 [build]
 # Document with the in-tree rustdoc by default, since `download-rustc` makes it quick to compile.
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 6a2bff5970f..242fe3c12b9 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -1843,6 +1843,16 @@ impl Step for Assemble {
             }
         }
 
+        if builder.config.llvm_bitcode_linker_enabled {
+            let src_path = builder.ensure(crate::core::build_steps::tool::LlvmBitcodeLinker {
+                compiler: build_compiler,
+                target: target_compiler.host,
+                extra_features: vec![],
+            });
+            let tool_exe = exe("llvm-bitcode-linker", target_compiler.host);
+            builder.copy(&src_path, &libdir_bin.join(&tool_exe));
+        }
+
         // Ensure that `libLLVM.so` ends up in the newly build compiler directory,
         // so that it can be found when the newly built `rustc` is run.
         dist::maybe_install_llvm_runtime(builder, target_compiler.host, &sysroot);
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 889876f461d..53dc1cff0ae 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -795,6 +795,7 @@ tool_extended!((self, builder),
     Rls, "src/tools/rls", "rls", stable=true, tool_std=true;
     RustDemangler, "src/tools/rust-demangler", "rust-demangler", stable=false, tool_std=true;
     Rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, add_bins_to_sysroot = ["rustfmt", "cargo-fmt"];
+    LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker", stable=false, add_bins_to_sysroot = ["llvm-bitcode-linker"];
 );
 
 impl<'a> Builder<'a> {
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 4927c837608..5e5d6d024ee 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -763,6 +763,7 @@ impl<'a> Builder<'a> {
                 tool::RustdocGUITest,
                 tool::OptimizedDist,
                 tool::CoverageDump,
+                tool::LlvmBitcodeLinker
             ),
             Kind::Check | Kind::Clippy | Kind::Fix => describe!(
                 check::Std,
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 683e0a4302f..ae5169e9383 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -236,6 +236,7 @@ pub struct Config {
     pub lld_mode: LldMode,
     pub lld_enabled: bool,
     pub llvm_tools_enabled: bool,
+    pub llvm_bitcode_linker_enabled: bool,
 
     pub llvm_cflags: Option<String>,
     pub llvm_cxxflags: Option<String>,
@@ -1099,6 +1100,7 @@ define_config! {
         dist_src: Option<bool> = "dist-src",
         save_toolstates: Option<String> = "save-toolstates",
         codegen_backends: Option<Vec<String>> = "codegen-backends",
+        llvm_bitcode_linker: Option<bool> = "llvm-bitcode-linker",
         lld: Option<bool> = "lld",
         lld_mode: Option<LldMode> = "use-lld",
         llvm_tools: Option<bool> = "llvm-tools",
@@ -1571,6 +1573,7 @@ impl Config {
                 codegen_backends,
                 lld,
                 llvm_tools,
+                llvm_bitcode_linker,
                 deny_warnings,
                 backtrace_on_ice,
                 verify_llvm_ir,
@@ -1650,6 +1653,7 @@ impl Config {
             }
             set(&mut config.lld_mode, lld_mode);
             set(&mut config.lld_enabled, lld);
+            set(&mut config.llvm_bitcode_linker_enabled, llvm_bitcode_linker);
 
             if matches!(config.lld_mode, LldMode::SelfContained)
                 && !config.lld_enabled
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 938b95cc60e..6520b7ed089 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -64,6 +64,7 @@ const LLVM_TOOLS: &[&str] = &[
     "llvm-ar",       // used for creating and modifying archive files
     "llvm-as",       // used to convert LLVM assembly to LLVM bitcode
     "llvm-dis",      // used to disassemble LLVM bitcode
+    "llvm-link",     // Used to link LLVM bitcode
     "llc",           // used to compile LLVM bytecode
     "opt",           // used to optimize LLVM bytecode
 ];
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index a348fa35841..85dfe45111f 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -146,4 +146,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Info,
         summary: "a new `target.*.runner` option is available to specify a wrapper executable required to run tests for a target",
     },
+    ChangeInfo {
+        change_id: 117458,
+        severity: ChangeSeverity::Info,
+        summary: "New option `rust.llvm-bitcode-linker` that will build the llvm-bitcode-linker.",
+    },
 ];
diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
index e90141bb9a9..abd109a6ea3 100644
--- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
@@ -135,7 +135,7 @@ ENV TARGETS=$TARGETS,x86_64-unknown-uefi
 # Luckily one of the folders is /usr/local/include so symlink /usr/include/x86_64-linux-gnu/asm there
 RUN ln -s /usr/include/x86_64-linux-gnu/asm /usr/local/include/asm
 
-ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --disable-docs \
+ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-llvm-bitcode-linker --disable-docs \
   --set target.wasm32-wasi.wasi-root=/wasm32-wasip1 \
   --set target.wasm32-wasip1.wasi-root=/wasm32-wasip1 \
   --set target.wasm32-wasi-preview1-threads.wasi-root=/wasm32-wasi-preview1-threads \
diff --git a/src/tools/llvm-bitcode-linker/Cargo.toml b/src/tools/llvm-bitcode-linker/Cargo.toml
new file mode 100644
index 00000000000..a9210b562f3
--- /dev/null
+++ b/src/tools/llvm-bitcode-linker/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "llvm-bitcode-linker"
+version = "0.0.1"
+description = "A self-contained linker for llvm bitcode"
+license = "MIT OR Apache-2.0"
+edition = "2021"
+publish = false
+
+[dependencies]
+anyhow = "1.0"
+tracing = "0.1"
+tracing-subscriber = {version = "0.3.0", features = ["std"] }
+clap = { version = "4.3", features = ["derive"] }
+thiserror = "1.0.24"
diff --git a/src/tools/llvm-bitcode-linker/README.md b/src/tools/llvm-bitcode-linker/README.md
new file mode 100644
index 00000000000..9b8719e3a77
--- /dev/null
+++ b/src/tools/llvm-bitcode-linker/README.md
@@ -0,0 +1,5 @@
+# LLVM Bitcode Linker
+The LLVM bitcode linker can be used to link targets without any dependency on system libraries.
+The code will be linked in llvm-bc before compiling to native code. For some of these targets
+(e.g. ptx) there does not exist a sensible way to link the native format at all. A bitcode linker
+is required to link code compiled for such targets.
diff --git a/src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs b/src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs
new file mode 100644
index 00000000000..a92af71a36e
--- /dev/null
+++ b/src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs
@@ -0,0 +1,62 @@
+use std::path::PathBuf;
+
+use clap::Parser;
+
+use llvm_bitcode_linker::{Optimization, Session, Target};
+
+#[derive(Debug, Parser)]
+/// Linker for embedded code without any system dependencies
+pub struct Args {
+    /// Input files - objects, archives and static libraries.
+    ///
+    /// An archive can be, but not required to be, a Rust rlib.
+    files: Vec<PathBuf>,
+
+    /// A symbol that should be exported
+    #[arg(long)]
+    export_symbol: Vec<String>,
+
+    /// Input files directory
+    #[arg(short = 'L')]
+    input_dir: Vec<PathBuf>,
+
+    /// Target triple for which the code is compiled
+    #[arg(long)]
+    target: Target,
+
+    /// The target cpu
+    #[arg(long)]
+    target_cpu: Option<String>,
+
+    /// Write output to the filename
+    #[arg(short, long)]
+    output: PathBuf,
+
+    // Enable link time optimization
+    #[arg(long)]
+    lto: bool,
+
+    /// Emit debug information
+    #[arg(long)]
+    debug: bool,
+
+    /// The optimization level
+    #[arg(short = 'O', value_enum, default_value = "0")]
+    optimization: Optimization,
+}
+
+fn main() -> anyhow::Result<()> {
+    tracing_subscriber::FmtSubscriber::builder().with_max_level(tracing::Level::DEBUG).init();
+
+    let args = Args::parse();
+
+    let mut linker = Session::new(args.target, args.target_cpu, args.output);
+
+    linker.add_exported_symbols(args.export_symbol);
+
+    for rlib in args.files {
+        linker.add_file(rlib);
+    }
+
+    linker.lto(args.optimization, args.debug)
+}
diff --git a/src/tools/llvm-bitcode-linker/src/lib.rs b/src/tools/llvm-bitcode-linker/src/lib.rs
new file mode 100644
index 00000000000..48f918f631c
--- /dev/null
+++ b/src/tools/llvm-bitcode-linker/src/lib.rs
@@ -0,0 +1,7 @@
+mod linker;
+mod opt;
+mod target;
+
+pub use linker::Session;
+pub use opt::Optimization;
+pub use target::Target;
diff --git a/src/tools/llvm-bitcode-linker/src/linker.rs b/src/tools/llvm-bitcode-linker/src/linker.rs
new file mode 100644
index 00000000000..aa6b6443e4d
--- /dev/null
+++ b/src/tools/llvm-bitcode-linker/src/linker.rs
@@ -0,0 +1,163 @@
+use std::path::PathBuf;
+
+use anyhow::Context;
+
+use crate::Optimization;
+use crate::Target;
+
+#[derive(Debug)]
+pub struct Session {
+    target: Target,
+    cpu: Option<String>,
+    symbols: Vec<String>,
+
+    /// A file that `llvm-link` supports, like a bitcode file or an archive.
+    files: Vec<PathBuf>,
+
+    // Output files
+    link_path: PathBuf,
+    opt_path: PathBuf,
+    sym_path: PathBuf,
+    out_path: PathBuf,
+}
+
+impl Session {
+    pub fn new(target: crate::Target, cpu: Option<String>, out_path: PathBuf) -> Self {
+        let link_path = out_path.with_extension("o");
+        let opt_path = out_path.with_extension("optimized.o");
+        let sym_path = out_path.with_extension("symbols.txt");
+
+        Session {
+            target,
+            cpu,
+            symbols: Vec::new(),
+            files: Vec::new(),
+            link_path,
+            opt_path,
+            sym_path,
+            out_path,
+        }
+    }
+
+    /// Add a file, like an rlib or bitcode file that should be linked
+    pub fn add_file(&mut self, path: PathBuf) {
+        self.files.push(path);
+    }
+
+    /// Add a Vec of symbols to the list of exported symbols
+    pub fn add_exported_symbols(&mut self, symbols: Vec<String>) {
+        self.symbols.extend(symbols);
+    }
+
+    /// Reads every file that was added to the session and link them without optimization.
+    ///
+    /// The resulting artifact will be written to a file that can later be read to perform
+    /// optimizations and/or compilation from bitcode to the final artifact.
+    fn link(&mut self) -> anyhow::Result<()> {
+        tracing::info!("Linking {} files using llvm-link", self.files.len());
+
+        let llvm_link_output = std::process::Command::new("llvm-link")
+            .arg("--ignore-non-bitcode")
+            .args(&self.files)
+            .arg("-o")
+            .arg(&self.link_path)
+            .output()
+            .unwrap();
+
+        if !llvm_link_output.status.success() {
+            tracing::error!(
+                "llvm-link returned with Exit status: {}\n stdout: {}\n stderr: {}",
+                llvm_link_output.status,
+                String::from_utf8(llvm_link_output.stdout).unwrap(),
+                String::from_utf8(llvm_link_output.stderr).unwrap(),
+            );
+            anyhow::bail!("llvm-link failed to link files {:?}", self.files);
+        }
+
+        Ok(())
+    }
+
+    /// Optimize and compile to native format using `opt` and `llc`
+    ///
+    /// Before this can be called `link` needs to be called
+    fn optimize(&mut self, optimization: Optimization, mut debug: bool) -> anyhow::Result<()> {
+        let mut passes = format!("default<{}>", optimization);
+
+        // FIXME(@kjetilkjeka) Debug symbol generation is broken for nvptx64 so we must remove them even in debug mode
+        if debug && self.target == crate::Target::Nvptx64NvidiaCuda {
+            tracing::warn!("nvptx64 target detected - stripping debug symbols");
+            debug = false;
+        }
+
+        // We add an internalize pass as the rust compiler as we require exported symbols to be explicitly marked
+        passes.push_str(",internalize,globaldce");
+        let symbol_file_content = self.symbols.iter().fold(String::new(), |s, x| s + &x + "\n");
+        std::fs::write(&self.sym_path, symbol_file_content)
+            .context(format!("Failed to write symbol file: {}", self.sym_path.display()))?;
+
+        tracing::info!("optimizing bitcode with passes: {}", passes);
+        let mut opt_cmd = std::process::Command::new("opt");
+        opt_cmd
+            .arg(&self.link_path)
+            .arg("-o")
+            .arg(&self.opt_path)
+            .arg(format!("--internalize-public-api-file={}", self.sym_path.display()))
+            .arg(format!("--passes={}", passes));
+
+        if !debug {
+            opt_cmd.arg("--strip-debug");
+        }
+
+        let opt_output = opt_cmd.output().unwrap();
+
+        if !opt_output.status.success() {
+            tracing::error!(
+                "opt returned with Exit status: {}\n stdout: {}\n stderr: {}",
+                opt_output.status,
+                String::from_utf8(opt_output.stdout).unwrap(),
+                String::from_utf8(opt_output.stderr).unwrap(),
+            );
+            anyhow::bail!("opt failed optimize bitcode: {}", self.link_path.display());
+        };
+
+        Ok(())
+    }
+
+    /// Compile the optimized bitcode file to native format using `llc`
+    ///
+    /// Before this can be called `optimize` needs to be called
+    fn compile(&mut self) -> anyhow::Result<()> {
+        let mut lcc_command = std::process::Command::new("llc");
+
+        if let Some(mcpu) = &self.cpu {
+            lcc_command.arg("--mcpu").arg(mcpu);
+        }
+
+        let lcc_output =
+            lcc_command.arg(&self.opt_path).arg("-o").arg(&self.out_path).output().unwrap();
+
+        if !lcc_output.status.success() {
+            tracing::error!(
+                "llc returned with Exit status: {}\n stdout: {}\n stderr: {}",
+                lcc_output.status,
+                String::from_utf8(lcc_output.stdout).unwrap(),
+                String::from_utf8(lcc_output.stderr).unwrap(),
+            );
+
+            anyhow::bail!(
+                "llc failed to compile {} into {}",
+                self.opt_path.display(),
+                self.out_path.display()
+            );
+        }
+
+        Ok(())
+    }
+
+    /// Links, optimizes and compiles to the native format
+    pub fn lto(&mut self, optimization: crate::Optimization, debug: bool) -> anyhow::Result<()> {
+        self.link()?;
+        self.optimize(optimization, debug)?;
+        self.compile()
+    }
+}
diff --git a/src/tools/llvm-bitcode-linker/src/opt.rs b/src/tools/llvm-bitcode-linker/src/opt.rs
new file mode 100644
index 00000000000..93f0ec7e2d6
--- /dev/null
+++ b/src/tools/llvm-bitcode-linker/src/opt.rs
@@ -0,0 +1,53 @@
+use std::fmt::Display;
+use std::fmt::Formatter;
+
+#[derive(Debug, Clone, Copy, Default, Hash, Eq, PartialEq, clap::ValueEnum)]
+pub enum Optimization {
+    #[default]
+    #[value(name = "0")]
+    O0,
+    #[value(name = "1")]
+    O1,
+    #[value(name = "2")]
+    O2,
+    #[value(name = "3")]
+    O3,
+    #[value(name = "s")]
+    Os,
+    #[value(name = "z")]
+    Oz,
+}
+
+#[derive(Debug, Clone, Copy, thiserror::Error)]
+/// An invalid optimization level
+#[error("invalid optimization level")]
+pub struct InvalidOptimizationLevel;
+
+impl std::str::FromStr for Optimization {
+    type Err = InvalidOptimizationLevel;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "0" | "O0" => Ok(Optimization::O0),
+            "1" | "O1" => Ok(Optimization::O1),
+            "2" | "O2" => Ok(Optimization::O2),
+            "3" | "O3" => Ok(Optimization::O3),
+            "s" | "Os" => Ok(Optimization::Os),
+            "z" | "Oz" => Ok(Optimization::Oz),
+            _ => Err(InvalidOptimizationLevel),
+        }
+    }
+}
+
+impl Display for Optimization {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        match *self {
+            Optimization::O0 => write!(f, "O0"),
+            Optimization::O1 => write!(f, "O1"),
+            Optimization::O2 => write!(f, "O2"),
+            Optimization::O3 => write!(f, "O3"),
+            Optimization::Os => write!(f, "Os"),
+            Optimization::Oz => write!(f, "Oz"),
+        }
+    }
+}
diff --git a/src/tools/llvm-bitcode-linker/src/target.rs b/src/tools/llvm-bitcode-linker/src/target.rs
new file mode 100644
index 00000000000..d9f8ff3852b
--- /dev/null
+++ b/src/tools/llvm-bitcode-linker/src/target.rs
@@ -0,0 +1,20 @@
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, clap::ValueEnum)]
+pub enum Target {
+    Nvptx64NvidiaCuda,
+}
+
+#[derive(Debug, Clone, Copy, thiserror::Error)]
+/// The target is not supported by this linker
+#[error("unsupported target")]
+pub struct UnsupportedTarget;
+
+impl std::str::FromStr for Target {
+    type Err = UnsupportedTarget;
+
+    fn from_str(s: &str) -> Result<Target, UnsupportedTarget> {
+        match s {
+            "nvptx64-nvidia-cuda" => Ok(Target::Nvptx64NvidiaCuda),
+            _ => Err(UnsupportedTarget),
+        }
+    }
+}