roc/crates/compiler/build/src/target.rs
2023-10-23 13:50:31 -07:00

188 lines
7.1 KiB
Rust

use inkwell::{
targets::{CodeModel, InitializationConfig, RelocMode, Target, TargetMachine, TargetTriple},
OptimizationLevel,
};
use roc_error_macros::internal_error;
use roc_mono::ir::OptLevel;
use target_lexicon::{Architecture, Environment, OperatingSystem, Triple};
pub fn target_triple_str(target: &Triple) -> &'static str {
// Best guide I've found on how to determine these magic strings:
//
// https://stackoverflow.com/questions/15036909/clang-how-to-list-supported-target-architectures
match target {
Triple {
architecture: Architecture::X86_64,
operating_system: OperatingSystem::Linux,
..
} => "x86_64-unknown-linux-gnu",
Triple {
architecture: Architecture::X86_32(target_lexicon::X86_32Architecture::I386),
operating_system: OperatingSystem::Linux,
..
} => "i386-unknown-linux-gnu",
Triple {
architecture: Architecture::Wasm32,
..
} => "wasm32-unknown-unknown",
Triple {
architecture: Architecture::Aarch64(_),
operating_system: OperatingSystem::Linux,
..
} => "aarch64-unknown-linux-gnu",
Triple {
architecture: Architecture::Aarch64(_),
operating_system: OperatingSystem::Darwin,
..
} => "aarch64-apple-darwin",
Triple {
architecture: Architecture::X86_64,
operating_system: OperatingSystem::Darwin,
..
} => "x86_64-unknown-darwin10",
Triple {
architecture: Architecture::X86_64,
operating_system: OperatingSystem::Windows,
..
} => "x86_64-pc-windows-gnu",
_ => internal_error!("TODO gracefully handle unsupported target: {:?}", target),
}
}
pub fn target_zig_str(target: &Triple) -> &'static str {
// Zig has its own architecture mappings, defined here:
// https://github.com/ziglang/zig/blob/master/tools/process_headers.zig
//
// and an open proposal to unify them with the more typical "target triples":
// https://github.com/ziglang/zig/issues/4911
match target {
Triple {
architecture: Architecture::X86_64,
operating_system: OperatingSystem::Linux,
environment: Environment::Musl,
..
} => "x86_64-linux-musl",
Triple {
architecture: Architecture::X86_64,
operating_system: OperatingSystem::Linux,
..
} => "x86_64-linux-gnu",
Triple {
architecture: Architecture::X86_32(target_lexicon::X86_32Architecture::I386),
operating_system: OperatingSystem::Linux,
environment: Environment::Musl,
..
} => "i386-linux-musl",
Triple {
architecture: Architecture::X86_32(target_lexicon::X86_32Architecture::I386),
operating_system: OperatingSystem::Linux,
..
} => "i386-linux-gnu",
Triple {
architecture: Architecture::Aarch64(_),
operating_system: OperatingSystem::Linux,
..
} => "aarch64-linux-gnu",
Triple {
architecture: Architecture::X86_64,
operating_system: OperatingSystem::Darwin,
..
} => "x86_64-macos-none",
Triple {
architecture: Architecture::Aarch64(_),
operating_system: OperatingSystem::Darwin,
..
} => "aarch64-macos-none",
_ => internal_error!("TODO gracefully handle unsupported target: {:?}", target),
}
}
pub fn init_arch(target: &Triple) {
match target.architecture {
Architecture::X86_64 | Architecture::X86_32(_)
if cfg!(any(feature = "target-x86", feature = "target-x86_64")) =>
{
Target::initialize_x86(&InitializationConfig::default());
}
Architecture::Aarch64(_) if cfg!(feature = "target-aarch64") => {
Target::initialize_aarch64(&InitializationConfig::default());
}
Architecture::Arm(_) if cfg!(feature = "target-arm") => {
Target::initialize_arm(&InitializationConfig::default());
}
Architecture::Wasm32 if cfg!(feature = "target-wasm32") => {
Target::initialize_webassembly(&InitializationConfig::default());
}
_ => internal_error!(
"TODO gracefully handle unsupported target architecture: {:?}",
target.architecture
),
}
}
/// NOTE: arch_str is *not* the same as the beginning of the magic target triple
/// string! For example, if it's "x86-64" here, the magic target triple string
/// will begin with "x86_64" (with an underscore) instead.
pub fn arch_str(target: &Triple) -> &'static str {
// Best guide I've found on how to determine these magic strings:
//
// https://stackoverflow.com/questions/15036909/clang-how-to-list-supported-target-architectures
match target.architecture {
Architecture::X86_64 if cfg!(feature = "target-x86_64") => "x86-64",
Architecture::X86_32(_) if cfg!(feature = "target-x86") => "x86",
Architecture::Aarch64(_) if cfg!(feature = "target-aarch64") => "aarch64",
Architecture::Arm(_) if cfg!(feature = "target-arm") => "arm",
Architecture::Wasm32 if cfg!(feature = "target-webassembly") => "wasm32",
_ => internal_error!(
"TODO gracefully handle unsupported target architecture: {:?}",
target.architecture
),
}
}
pub fn target_machine(
target: &Triple,
opt: OptimizationLevel,
reloc: RelocMode,
) -> Option<TargetMachine> {
let arch = arch_str(target);
init_arch(target);
let code_model = match target {
Triple {
operating_system: OperatingSystem::Darwin,
architecture: Architecture::Aarch64(_),
..
} => {
// We used to have a problem that LLVM 12 would not compile our programs without a large code model.
// The reason was not totally clear to us, but one guess is a few special-cases in
// llvm/lib/Target/AArch64/AArch64ISelLowering.cpp (instructions)
// llvm/lib/Target/AArch64/AArch64Subtarget.cpp (GoT tables)
// Revisit when upgrading to LLVM 13.
//
// Most recently, we seem to only see this problem on macOS ARM64; removing this
// failed macOS CI here: https://github.com/roc-lang/roc/pull/5644
CodeModel::Large
}
_ => CodeModel::Default,
};
Target::from_name(arch).unwrap().create_target_machine(
&TargetTriple::create(target_triple_str(target)),
"generic",
"",
opt,
reloc,
code_model,
)
}
pub fn convert_opt_level(level: OptLevel) -> OptimizationLevel {
match level {
OptLevel::Development | OptLevel::Normal => OptimizationLevel::None,
// Default is O2/Os. If we want Oz, we have to explicitly turn of loop vectorization as well.
OptLevel::Size => OptimizationLevel::Default,
OptLevel::Optimize => OptimizationLevel::Aggressive,
}
}