Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions llvm/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// SPDX-License-Identifier: MPL-2.0
// AI TRAINING NOTICE: Prohibited without prior written permission. No use for machine learning or generative AI training, fine-tuning, distillation, embedding, or dataset creation.

use crate::codegen::target::{target_spec_for_triple, CodegenTarget};
use std::env;
use std::path::PathBuf;
use std::process::Command;
Expand All @@ -30,11 +31,9 @@ pub struct BackendOptions {
}

fn is_windows_gnu_target(target: Option<&str>) -> bool {
let Some(target) = target else {
return false;
};
let t = target.to_ascii_lowercase();
t.starts_with("x86_64-") && t.contains("windows") && !t.contains("msvc")
target
.and_then(target_spec_for_triple)
.is_some_and(|spec| spec.codegen == CodegenTarget::WindowsX86_64Gnu)
}

fn normalize_llvm_opt_flag(opt_flag: &str) -> &str {
Expand Down Expand Up @@ -173,11 +172,13 @@ fn default_lld_for_target(target: &str) -> String {

fn append_lld_target_args(cmd: &mut Command, target: &str, backend: &BackendOptions) {
if is_darwin_target(target) {
let spec = target_spec_for_triple(target)
.expect("Darwin linker configuration requires a registered target");
cmd.arg("-arch")
.arg(if target.starts_with("x86_64-") {
"x86_64"
} else {
.arg(if spec.arch == "aarch64" {
"arm64"
} else {
spec.arch
})
.arg("-platform_version")
.arg("macos")
Expand Down Expand Up @@ -223,14 +224,14 @@ fn expand_lld_link_args(link_args: &[String]) -> Vec<String> {
}

fn is_darwin_target(target: &str) -> bool {
target.contains("apple-darwin")
target_spec_for_triple(target).is_some_and(|spec| spec.os == "macos")
}

fn elf_lld_emulation(target: &str) -> Option<&'static str> {
match target.split('-').next().unwrap_or(target) {
"x86_64" => Some("elf_x86_64"),
"aarch64" => Some("aarch64elf"),
"riscv64" => Some("elf64lriscv"),
match target_spec_for_triple(target)?.codegen {
CodegenTarget::LinuxX86_64 | CodegenTarget::FreestandingX86_64 => Some("elf_x86_64"),
CodegenTarget::LinuxArm64 | CodegenTarget::FreestandingArm64 => Some("aarch64elf"),
CodegenTarget::FreestandingRISCV64 => Some("elf64lriscv"),
_ => None,
}
}
Expand Down
292 changes: 252 additions & 40 deletions llvm/src/codegen/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,44 +25,206 @@ pub enum CodegenTarget {
FreestandingRISCV64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TargetSpec {
pub triple: &'static str,
pub codegen: CodegenTarget,
pub arch: &'static str,
pub vendor: &'static str,
pub os: &'static str,
pub env: &'static str,
pub object_format: &'static str,
pub hosted: bool,
pub cpus: &'static [&'static str],
pub features: &'static [&'static str],
pub abis: &'static [&'static str],
}

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-x86"))]
const X86_CPUS: &[&str] = &["generic", "x86-64", "x86-64-v2", "x86-64-v3"];
#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-x86"))]
const X86_FEATURES: &[&str] = &["sse2", "sse4.1", "avx", "avx2"];

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-aarch64"))]
const AARCH64_CPUS: &[&str] = &["generic", "cortex-a53", "cortex-a72"];
#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-aarch64"))]
const DARWIN_AARCH64_CPUS: &[&str] = &["generic", "apple-m1"];
#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-aarch64"))]
const AARCH64_FEATURES: &[&str] = &["neon", "fp-armv8", "crypto"];

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-riscv"))]
const RISCV64_CPUS: &[&str] = &["generic", "generic-rv64", "rocket-rv64", "sifive-u74"];
#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-riscv"))]
const RISCV64_FEATURES: &[&str] = &["m", "a", "f", "d", "c"];
#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-riscv"))]
const RISCV64_ABIS: &[&str] = &["lp64", "lp64f", "lp64d"];

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-x86"))]
const LINUX_X86_64: TargetSpec = TargetSpec {
triple: "x86_64-unknown-linux-gnu",
codegen: CodegenTarget::LinuxX86_64,
arch: "x86_64",
vendor: "unknown",
os: "linux",
env: "gnu",
object_format: "elf",
hosted: true,
cpus: X86_CPUS,
features: X86_FEATURES,
abis: &[],
};

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-x86"))]
const DARWIN_X86_64: TargetSpec = TargetSpec {
triple: "x86_64-apple-darwin",
codegen: CodegenTarget::DarwinX86_64,
arch: "x86_64",
vendor: "apple",
os: "macos",
env: "",
object_format: "macho",
hosted: true,
cpus: X86_CPUS,
features: X86_FEATURES,
abis: &[],
};

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-x86"))]
const WINDOWS_W64_X86_64_GNU: TargetSpec = TargetSpec {
triple: "x86_64-w64-windows-gnu",
codegen: CodegenTarget::WindowsX86_64Gnu,
arch: "x86_64",
vendor: "w64",
os: "windows",
env: "gnu",
object_format: "coff",
hosted: true,
cpus: X86_CPUS,
features: X86_FEATURES,
abis: &[],
};

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-x86"))]
const WINDOWS_PC_X86_64_GNU: TargetSpec = TargetSpec {
triple: "x86_64-pc-windows-gnu",
codegen: CodegenTarget::WindowsX86_64Gnu,
arch: "x86_64",
vendor: "pc",
os: "windows",
env: "gnu",
object_format: "coff",
hosted: true,
cpus: X86_CPUS,
features: X86_FEATURES,
abis: &[],
};

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-x86"))]
const FREESTANDING_X86_64: TargetSpec = TargetSpec {
triple: "x86_64-unknown-none-elf",
codegen: CodegenTarget::FreestandingX86_64,
arch: "x86_64",
vendor: "unknown",
os: "none",
env: "none",
object_format: "elf",
hosted: false,
cpus: X86_CPUS,
features: X86_FEATURES,
abis: &[],
};

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-aarch64"))]
const LINUX_AARCH64: TargetSpec = TargetSpec {
triple: "aarch64-unknown-linux-gnu",
codegen: CodegenTarget::LinuxArm64,
arch: "aarch64",
vendor: "unknown",
os: "linux",
env: "gnu",
object_format: "elf",
hosted: true,
cpus: AARCH64_CPUS,
features: AARCH64_FEATURES,
abis: &[],
};

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-aarch64"))]
const DARWIN_AARCH64: TargetSpec = TargetSpec {
triple: "aarch64-apple-darwin",
codegen: CodegenTarget::DarwinArm64,
arch: "aarch64",
vendor: "apple",
os: "macos",
env: "",
object_format: "macho",
hosted: true,
cpus: DARWIN_AARCH64_CPUS,
features: AARCH64_FEATURES,
abis: &[],
};

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-aarch64"))]
const FREESTANDING_AARCH64: TargetSpec = TargetSpec {
triple: "aarch64-unknown-none-elf",
codegen: CodegenTarget::FreestandingArm64,
arch: "aarch64",
vendor: "unknown",
os: "none",
env: "none",
object_format: "elf",
hosted: false,
cpus: AARCH64_CPUS,
features: AARCH64_FEATURES,
abis: &[],
};

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-riscv"))]
const FREESTANDING_RISCV64: TargetSpec = TargetSpec {
triple: "riscv64-unknown-none-elf",
codegen: CodegenTarget::FreestandingRISCV64,
arch: "riscv64",
vendor: "unknown",
os: "none",
env: "none",
object_format: "elf",
hosted: false,
cpus: RISCV64_CPUS,
features: RISCV64_FEATURES,
abis: RISCV64_ABIS,
};

pub fn supported_target_specs() -> Vec<&'static TargetSpec> {
let mut specs: Vec<&'static TargetSpec> = Vec::new();

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-x86"))]
specs.extend([
&LINUX_X86_64,
&DARWIN_X86_64,
&WINDOWS_W64_X86_64_GNU,
&WINDOWS_PC_X86_64_GNU,
&FREESTANDING_X86_64,
]);

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-aarch64"))]
specs.extend([&LINUX_AARCH64, &DARWIN_AARCH64, &FREESTANDING_AARCH64]);

#[cfg(any(feature = "llvm-target-all", feature = "llvm-target-riscv"))]
specs.push(&FREESTANDING_RISCV64);

specs.sort_unstable_by_key(|spec| spec.triple);
specs
}

pub fn target_spec_for_triple(triple: &str) -> Option<&'static TargetSpec> {
supported_target_specs()
.into_iter()
.find(|spec| spec.triple == triple)
}

impl CodegenTarget {
pub fn from_triple_str(triple: &str) -> Option<Self> {
let t = triple.to_ascii_lowercase();

let is_x86_64 = t.starts_with("x86_64");
let is_arm64 = t.starts_with("arm64") || t.starts_with("aarch64");
let is_riscv64 = t.starts_with("riscv64");
let is_linux = t.contains("linux");
let is_darwin = t.contains("darwin");
let is_windows_gnu = t.contains("windows") && !t.contains("msvc");
let is_freestanding = t.contains("-none-") || t.ends_with("-none") || t.contains("elf");

if is_x86_64 && is_linux {
return Some(Self::LinuxX86_64);
}
if is_arm64 && is_linux {
return Some(Self::LinuxArm64);
}
if is_x86_64 && is_darwin {
return Some(Self::DarwinX86_64);
}
if is_arm64 && is_darwin {
return Some(Self::DarwinArm64);
}
if is_x86_64 && is_windows_gnu {
return Some(Self::WindowsX86_64Gnu);
}
if is_x86_64 && is_freestanding {
return Some(Self::FreestandingX86_64);
}
if is_arm64 && is_freestanding {
return Some(Self::FreestandingArm64);
}
if is_riscv64 && is_freestanding {
return Some(Self::FreestandingRISCV64);
}

None
target_spec_for_triple(triple).map(|spec| spec.codegen)
}

pub fn from_target_triple(triple: &TargetTriple) -> Option<Self> {
Expand Down Expand Up @@ -95,9 +257,14 @@ pub fn require_supported_target_from_triple(triple: &TargetTriple) -> CodegenTar
}

let raw = triple.as_str().to_string_lossy();
let supported = supported_target_specs()
.into_iter()
.map(|spec| spec.triple)
.collect::<Vec<_>>()
.join(", ");
panic!(
"unsupported target triple '{}': Wave currently supports linux x86_64/arm64, darwin x86_64/arm64, windows x86_64 gnu, and freestanding x86_64/arm64/riscv64",
raw
"unsupported target triple '{}': Wave currently supports {}",
raw, supported
);
}

Expand All @@ -108,8 +275,53 @@ pub fn require_supported_target_from_module(module: &Module<'_>) -> CodegenTarge

let triple = module.get_triple();
let raw = triple.as_str().to_string_lossy();
let supported = supported_target_specs()
.into_iter()
.map(|spec| spec.triple)
.collect::<Vec<_>>()
.join(", ");
panic!(
"unsupported target triple '{}': Wave currently supports linux x86_64/arm64, darwin x86_64/arm64, windows x86_64 gnu, and freestanding x86_64/arm64/riscv64",
raw
"unsupported target triple '{}': Wave currently supports {}",
raw, supported
);
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn registered_targets_round_trip_through_exact_lookup() {
let specs = supported_target_specs();
assert!(!specs.is_empty());

for (index, spec) in specs.iter().enumerate() {
assert_eq!(target_spec_for_triple(spec.triple), Some(*spec));
assert_eq!(
CodegenTarget::from_triple_str(spec.triple),
Some(spec.codegen)
);
assert!(!spec.arch.is_empty());
assert!(!spec.os.is_empty());
assert!(!spec.object_format.is_empty());

for other in specs.iter().skip(index + 1) {
assert_ne!(spec.triple, other.triple, "duplicate target triple");
}
}
}

#[test]
fn malformed_or_unregistered_triples_do_not_match_by_substring() {
for triple in [
"x86_64-garbage-linux-gnu",
"prefix-x86_64-unknown-linux-gnu-suffix",
"riscv64-unknown-linux-gnu",
"x86_64-unknown-none-elf-waveabi",
"",
] {
assert_eq!(target_spec_for_triple(triple), None, "{triple}");
assert_eq!(CodegenTarget::from_triple_str(triple), None, "{triple}");
}
}
}
Loading
Loading