use std::collections::HashMap;
use std::collections::HashSet;
use crate::data_layout::DataLayout;
use crate::target_info::TargetFeature;
use crate::target_machine::{
CodeGenOptLevel, CodeModel, FloatABI, RelocModel, TargetMachine, TargetOptions,
};
use crate::triple::{Arch, Triple};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DebugInfoKind {
NoDebug,
LineTablesOnly,
LimitedDebug,
FullDebug,
FullWithMacros,
}
impl DebugInfoKind {
pub fn from_flag(g_level: u8) -> Self {
match g_level {
0 => Self::NoDebug,
1 => Self::LineTablesOnly,
2 => Self::LimitedDebug,
3 => Self::FullWithMacros,
_ => Self::FullDebug,
}
}
pub fn has_debug(&self) -> bool {
!matches!(self, Self::NoDebug)
}
pub fn has_types(&self) -> bool {
matches!(self, Self::FullDebug | Self::FullWithMacros)
}
pub fn as_str(&self) -> &'static str {
match self {
Self::NoDebug => "none",
Self::LineTablesOnly => "line-tables-only",
Self::LimitedDebug => "limited",
Self::FullDebug => "full",
Self::FullWithMacros => "full-with-macros",
}
}
}
impl Default for DebugInfoKind {
fn default() -> Self {
Self::NoDebug
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum StackProtectorLevel {
None,
Basic,
Strong,
All,
}
impl StackProtectorLevel {
pub fn from_flag(flag: &str) -> Self {
match flag {
"none" | "off" => Self::None,
"all" => Self::All,
"strong" => Self::Strong,
_ => Self::Basic,
}
}
pub fn is_enabled(&self) -> bool {
*self > Self::None
}
pub fn as_llvm_str(&self) -> &'static str {
match self {
Self::None => "none",
Self::Basic => "basic",
Self::Strong => "strong",
Self::All => "all",
}
}
}
impl Default for StackProtectorLevel {
fn default() -> Self {
Self::None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClangOptLevel {
None,
Less,
Default,
Aggressive,
DefaultSize,
AggressiveSize,
FastMath,
}
impl ClangOptLevel {
pub fn from_flag(s: &str) -> Self {
match s {
"0" => Self::None,
"1" => Self::Less,
"2" | "" => Self::Default,
"3" => Self::Aggressive,
"s" => Self::DefaultSize,
"z" => Self::AggressiveSize,
"fast" => Self::FastMath,
_ => Self::Default,
}
}
pub fn to_llvm_opt(&self) -> CodeGenOptLevel {
match self {
Self::None => CodeGenOptLevel::None,
Self::Less => CodeGenOptLevel::Less,
Self::Default | Self::DefaultSize => CodeGenOptLevel::Default,
Self::Aggressive | Self::AggressiveSize | Self::FastMath => CodeGenOptLevel::Aggressive,
}
}
pub fn is_size_oriented(&self) -> bool {
matches!(self, Self::DefaultSize | Self::AggressiveSize)
}
pub fn implies_fast_math(&self) -> bool {
matches!(self, Self::FastMath)
}
pub fn as_str(&self) -> &'static str {
match self {
Self::None => "O0",
Self::Less => "O1",
Self::Default => "O2",
Self::Aggressive => "O3",
Self::DefaultSize => "Os",
Self::AggressiveSize => "Oz",
Self::FastMath => "Ofast",
}
}
}
impl Default for ClangOptLevel {
fn default() -> Self {
Self::None
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClangABI {
pub base_abi: String,
pub float_abi: String,
pub triple: String,
}
impl ClangABI {
pub fn new(triple: &str) -> Self {
let t = Triple::parse(triple);
let (base_abi, float_abi) = Self::default_abi_for_arch(t.arch);
Self {
base_abi,
float_abi,
triple: triple.to_string(),
}
}
fn default_abi_for_arch(arch: Arch) -> (String, String) {
match arch {
Arch::X86_64 => (String::from("lp64"), String::from("hard")),
Arch::X86 => (String::from("ilp32"), String::from("hard")),
Arch::AArch64 => (String::from("lp64"), String::from("hard")),
Arch::ARM => (String::from("ilp32"), String::from("soft")),
Arch::Thumb => (String::from("ilp32"), String::from("soft")),
Arch::AArch64_32 => (String::from("ilp32"), String::from("hard")),
Arch::RISCV64 => (String::from("lp64d"), String::from("hard")),
Arch::RISCV32 => (String::from("ilp32"), String::from("hard")),
Arch::Mips | Arch::Mipsel => (String::from("o32"), String::from("hard")),
Arch::Mips64 | Arch::Mips64el => (String::from("n64"), String::from("hard")),
Arch::PowerPC => (String::from("ilp32"), String::from("hard")),
Arch::PowerPC64 | Arch::PowerPC64le => (String::from("lp64"), String::from("hard")),
Arch::Sparc => (String::from("ilp32"), String::from("hard")),
Arch::Sparcv9 => (String::from("lp64"), String::from("hard")),
Arch::SystemZ => (String::from("lp64"), String::from("hard")),
Arch::WebAssembly32 => (String::from("ilp32"), String::from("soft")),
Arch::WebAssembly64 => (String::from("lp64"), String::from("soft")),
Arch::AVR => (String::from("ilp16"), String::from("soft")),
Arch::MSP430 => (String::from("ilp16"), String::from("soft")),
Arch::BPF | Arch::BPFEB | Arch::BPF64 => (String::from("lp64"), String::from("soft")),
Arch::Hexagon => (String::from("ilp32"), String::from("soft")),
Arch::Lanai => (String::from("ilp32"), String::from("soft")),
Arch::NVPTX | Arch::NVPTX64 => (String::from("lp64"), String::from("hard")),
Arch::AMDGPU => (String::from("lp64"), String::from("hard")),
Arch::ARC => (String::from("ilp32"), String::from("soft")),
Arch::CSKY => (String::from("ilp32"), String::from("soft")),
Arch::Xtensa => (String::from("ilp32"), String::from("soft")),
_ => (String::from("ilp32"), String::from("soft")),
}
}
pub fn with_base_abi(mut self, abi: &str) -> Self {
self.base_abi = abi.to_string();
self
}
pub fn with_float_abi(mut self, fa: &str) -> Self {
self.float_abi = fa.to_string();
self
}
pub fn is_soft_float(&self) -> bool {
self.float_abi == "soft"
}
pub fn is_64bit(&self) -> bool {
matches!(
self.base_abi.as_str(),
"lp64" | "lp64d" | "lp64f" | "n64" | "aarch64" | "ilp64"
)
}
pub fn to_llvm_float_abi(&self) -> FloatABI {
match self.float_abi.as_str() {
"soft" => FloatABI::Soft,
"hard" | "softfp" => FloatABI::Hard,
_ => FloatABI::Default,
}
}
}
impl Default for ClangABI {
fn default() -> Self {
Self::new("x86_64-unknown-linux-gnu")
}
}
#[derive(Debug, Clone)]
pub struct TargetCodeGenInfo {
pub arch: Arch,
pub uses_sret_demotion: bool,
pub indirect_struct_return: bool,
pub max_reg_return_size: u32,
pub split_struct_return: bool,
pub varargs_save_area: bool,
pub setjmp_returns_twice: bool,
pub longjmp_noreturn: bool,
pub global_address_space: u32,
pub constant_address_space: u32,
pub tls_address_space: u32,
pub stack_grows_down: bool,
pub stack_alignment: u32,
pub null_pointer_is_valid: bool,
pub use_regparm: bool,
pub regparm_count: u32,
}
impl TargetCodeGenInfo {
pub fn for_arch(arch: Arch) -> Self {
match arch {
Arch::X86_64 => Self {
arch,
uses_sret_demotion: true,
indirect_struct_return: true,
max_reg_return_size: 128,
split_struct_return: true,
varargs_save_area: true,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 16,
null_pointer_is_valid: false,
use_regparm: false,
regparm_count: 0,
},
Arch::X86 => Self {
arch,
uses_sret_demotion: false,
indirect_struct_return: false,
max_reg_return_size: 64,
split_struct_return: false,
varargs_save_area: false,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 16,
null_pointer_is_valid: false,
use_regparm: true,
regparm_count: 3,
},
Arch::AArch64 => Self {
arch,
uses_sret_demotion: true,
indirect_struct_return: true,
max_reg_return_size: 128,
split_struct_return: true,
varargs_save_area: true,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 16,
null_pointer_is_valid: false,
use_regparm: false,
regparm_count: 0,
},
Arch::ARM | Arch::ARMeb => Self {
arch,
uses_sret_demotion: false,
indirect_struct_return: true,
max_reg_return_size: 64,
split_struct_return: true,
varargs_save_area: true,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 8,
null_pointer_is_valid: false,
use_regparm: false,
regparm_count: 0,
},
Arch::Thumb | Arch::Thumbeb => Self {
arch,
uses_sret_demotion: false,
indirect_struct_return: true,
max_reg_return_size: 64,
split_struct_return: false,
varargs_save_area: true,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 8,
null_pointer_is_valid: false,
use_regparm: false,
regparm_count: 0,
},
Arch::RISCV64 => Self {
arch,
uses_sret_demotion: true,
indirect_struct_return: true,
max_reg_return_size: 128,
split_struct_return: true,
varargs_save_area: true,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 16,
null_pointer_is_valid: false,
use_regparm: false,
regparm_count: 0,
},
Arch::RISCV32 => Self {
arch,
uses_sret_demotion: false,
indirect_struct_return: true,
max_reg_return_size: 64,
split_struct_return: true,
varargs_save_area: true,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 16,
null_pointer_is_valid: false,
use_regparm: false,
regparm_count: 0,
},
Arch::Mips | Arch::Mipsel | Arch::Mips64 | Arch::Mips64el => Self {
arch,
uses_sret_demotion: false,
indirect_struct_return: true,
max_reg_return_size: 64,
split_struct_return: false,
varargs_save_area: true,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 8,
null_pointer_is_valid: false,
use_regparm: false,
regparm_count: 0,
},
Arch::PowerPC | Arch::PowerPC64 | Arch::PowerPC64le => Self {
arch,
uses_sret_demotion: true,
indirect_struct_return: true,
max_reg_return_size: 64,
split_struct_return: true,
varargs_save_area: true,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 16,
null_pointer_is_valid: false,
use_regparm: false,
regparm_count: 0,
},
Arch::SystemZ => Self {
arch,
uses_sret_demotion: true,
indirect_struct_return: true,
max_reg_return_size: 128,
split_struct_return: false,
varargs_save_area: true,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 8,
null_pointer_is_valid: false,
use_regparm: false,
regparm_count: 0,
},
Arch::WebAssembly32 | Arch::WebAssembly64 => Self {
arch,
uses_sret_demotion: false,
indirect_struct_return: false,
max_reg_return_size: 64,
split_struct_return: false,
varargs_save_area: false,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 16,
null_pointer_is_valid: false,
use_regparm: false,
regparm_count: 0,
},
_ => Self {
arch,
uses_sret_demotion: false,
indirect_struct_return: true,
max_reg_return_size: 64,
split_struct_return: false,
varargs_save_area: true,
setjmp_returns_twice: true,
longjmp_noreturn: true,
global_address_space: 0,
constant_address_space: 0,
tls_address_space: 0,
stack_grows_down: true,
stack_alignment: 16,
null_pointer_is_valid: false,
use_regparm: false,
regparm_count: 0,
},
}
}
pub fn should_pass_indirectly(&self, size: u32) -> bool {
if !self.indirect_struct_return {
return false;
}
size > self.max_reg_return_size / 8
}
pub fn uses_sret(&self) -> bool {
self.uses_sret_demotion
}
}
impl Default for TargetCodeGenInfo {
fn default() -> Self {
Self::for_arch(Arch::X86_64)
}
}
#[derive(Debug, Clone)]
pub struct HostCpuDetector {
cached_name: Option<String>,
cached_features: Option<HashSet<String>>,
}
impl HostCpuDetector {
pub fn new() -> Self {
Self {
cached_name: None,
cached_features: None,
}
}
pub fn host_cpu_name(&mut self) -> &str {
if self.cached_name.is_none() {
self.cached_name = Some(Self::detect_host_cpu());
}
self.cached_name.as_deref().unwrap()
}
pub fn host_cpu_features(&mut self) -> &HashSet<String> {
if self.cached_features.is_none() {
self.cached_features = Some(Self::detect_host_features());
}
self.cached_features.as_ref().unwrap()
}
#[cfg(target_arch = "x86_64")]
fn detect_host_cpu() -> String {
if Self::has_x86_feature("avx512f") {
String::from("skylake-avx512")
} else if Self::has_x86_feature("avx2") {
String::from("haswell")
} else if Self::has_x86_feature("avx") {
String::from("sandybridge")
} else if Self::has_x86_feature("sse4_2") {
String::from("nehalem")
} else if Self::has_x86_feature("sse4_1") {
String::from("penryn")
} else if Self::has_x86_feature("ssse3") {
String::from("core2")
} else if Self::has_x86_feature("sse3") {
String::from("prescott")
} else {
String::from("pentium4")
}
}
#[cfg(target_arch = "aarch64")]
fn detect_host_cpu() -> String {
if Self::has_aarch64_feature("sve2") {
String::from("neoverse-v2")
} else if Self::has_aarch64_feature("sve") {
String::from("neoverse-n2")
} else if Self::has_aarch64_feature("neon") {
String::from("cortex-a76")
} else {
String::from("cortex-a53")
}
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
fn detect_host_cpu() -> String {
String::from("generic")
}
#[cfg(target_arch = "x86_64")]
fn detect_host_features() -> HashSet<String> {
let mut feats = HashSet::new();
let all = [
("mmx", "mmx"),
("sse", "sse"),
("sse2", "sse2"),
("sse3", "sse3"),
("ssse3", "ssse3"),
("sse4.1", "sse4.1"),
("sse4.2", "sse4.2"),
("popcnt", "popcnt"),
("avx", "avx"),
("avx2", "avx2"),
("fma", "fma"),
("bmi", "bmi"),
("bmi2", "bmi2"),
("lzcnt", "lzcnt"),
("avx512f", "avx512f"),
("avx512bw", "avx512bw"),
("avx512dq", "avx512dq"),
("avx512vl", "avx512vl"),
];
for (feat, _name) in &all {
if Self::has_x86_feature(feat) {
feats.insert(feat.to_string());
}
}
feats
}
#[cfg(target_arch = "aarch64")]
fn detect_host_features() -> HashSet<String> {
let mut feats = HashSet::new();
let all = [
("fp", "fp"),
("asimd", "neon"),
("crc", "crc"),
("crypto", "crypto"),
("fp16", "fp16"),
("lse", "lse"),
("rdm", "rdm"),
("rcpc", "rcpc"),
("dotprod", "dotprod"),
("sve", "sve"),
("sve2", "sve2"),
("i8mm", "i8mm"),
];
for (feat, _name) in &all {
if Self::has_aarch64_feature(feat) {
feats.insert(feat.to_string());
}
}
feats
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
fn detect_host_features() -> HashSet<String> {
HashSet::new()
}
#[cfg(target_arch = "x86_64")]
fn has_x86_feature(_feat: &str) -> bool {
matches!(
_feat,
"mmx"
| "sse"
| "sse2"
| "sse3"
| "ssse3"
| "sse4.1"
| "sse4.2"
| "popcnt"
| "avx"
| "avx2"
)
}
#[cfg(target_arch = "aarch64")]
fn has_aarch64_feature(_feat: &str) -> bool {
matches!(_feat, "fp" | "asimd" | "crc" | "crypto" | "lse")
}
}
impl Default for HostCpuDetector {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct CpuFeatureDatabase {
cpus: HashMap<String, HashSet<String>>,
features: HashMap<String, TargetFeature>,
host_detector: HostCpuDetector,
}
impl CpuFeatureDatabase {
pub fn new() -> Self {
let mut db = Self {
cpus: HashMap::new(),
features: HashMap::new(),
host_detector: HostCpuDetector::new(),
};
db.register_x86_cpus();
db.register_aarch64_cpus();
db.register_arm_cpus();
db.register_riscv_cpus();
db.register_x86_features();
db.register_aarch64_features();
db.register_arm_features();
db.register_riscv_features();
db
}
pub fn cpu_features(&self, cpu_name: &str) -> Option<&HashSet<String>> {
if cpu_name == "native" {
None
} else {
self.cpus.get(cpu_name)
}
}
pub fn resolve_cpu(&mut self, cpu_name: &str) -> HashSet<String> {
if cpu_name == "native" {
return self.host_detector.host_cpu_features().clone();
}
if cpu_name == "generic" {
return HashSet::new();
}
self.cpus.get(cpu_name).cloned().unwrap_or_default()
}
pub fn feature_string(&mut self, cpu: &str, extra_features: &[(String, bool)]) -> String {
let mut feats: HashSet<String> = if cpu.is_empty() || cpu == "generic" {
HashSet::new()
} else {
self.resolve_cpu(cpu)
};
for (name, enable) in extra_features {
if *enable {
feats.insert(name.clone());
if let Some(tf) = self.features.get(name.as_str()) {
for imp in &tf.implies {
feats.insert(imp.clone());
}
}
} else {
feats.remove(name.as_str());
}
}
let mut sorted: Vec<&String> = feats.iter().collect();
sorted.sort();
sorted
.into_iter()
.map(|s| format!("+{}", s))
.collect::<Vec<_>>()
.join(",")
}
pub fn has_feature(&self, name: &str) -> bool {
self.features.contains_key(name)
}
fn register_x86_cpus(&mut self) {
let x86_cpus: &[(&str, &[&str])] = &[
("pentium", &["mmx"]),
("pentium4", &["mmx", "sse", "sse2"]),
("prescott", &["mmx", "sse", "sse2", "sse3"]),
("nocona", &["mmx", "sse", "sse2", "sse3", "cx16"]),
("core2", &["mmx", "sse", "sse2", "sse3", "ssse3", "cx16"]),
(
"penryn",
&["mmx", "sse", "sse2", "sse3", "ssse3", "sse4.1", "cx16"],
),
(
"nehalem",
&[
"mmx", "sse", "sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "popcnt", "cx16",
],
),
(
"sandybridge",
&[
"mmx", "sse", "sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "popcnt", "avx",
"cx16",
],
),
(
"ivybridge",
&[
"mmx", "sse", "sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "popcnt", "avx",
"cx16", "f16c",
],
),
(
"haswell",
&[
"mmx", "sse", "sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "popcnt", "avx",
"avx2", "fma", "bmi", "bmi2", "lzcnt", "cx16", "f16c", "movbe",
],
),
(
"broadwell",
&[
"mmx", "sse", "sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "popcnt", "avx",
"avx2", "fma", "bmi", "bmi2", "lzcnt", "cx16", "f16c", "movbe", "adx",
"rdseed",
],
),
(
"skylake",
&[
"mmx",
"sse",
"sse2",
"sse3",
"ssse3",
"sse4.1",
"sse4.2",
"popcnt",
"avx",
"avx2",
"fma",
"bmi",
"bmi2",
"lzcnt",
"cx16",
"f16c",
"movbe",
"adx",
"rdseed",
"xsavec",
"xsaves",
"clflushopt",
],
),
(
"skylake-avx512",
&[
"mmx",
"sse",
"sse2",
"sse3",
"ssse3",
"sse4.1",
"sse4.2",
"popcnt",
"avx",
"avx2",
"fma",
"bmi",
"bmi2",
"lzcnt",
"cx16",
"f16c",
"movbe",
"adx",
"rdseed",
"xsavec",
"xsaves",
"clflushopt",
"avx512f",
"avx512bw",
"avx512dq",
"avx512vl",
],
),
(
"cannonlake",
&[
"mmx",
"sse",
"sse2",
"sse3",
"ssse3",
"sse4.1",
"sse4.2",
"popcnt",
"avx",
"avx2",
"fma",
"bmi",
"bmi2",
"lzcnt",
"cx16",
"f16c",
"movbe",
"adx",
"rdseed",
"xsavec",
"xsaves",
"clflushopt",
"avx512f",
"avx512bw",
"avx512dq",
"avx512vl",
"avx512vbmi",
"sha",
],
),
(
"icelake-client",
&[
"mmx",
"sse",
"sse2",
"sse3",
"ssse3",
"sse4.1",
"sse4.2",
"popcnt",
"avx",
"avx2",
"fma",
"bmi",
"bmi2",
"lzcnt",
"cx16",
"f16c",
"movbe",
"adx",
"rdseed",
"xsavec",
"xsaves",
"clflushopt",
"avx512f",
"avx512bw",
"avx512dq",
"avx512vl",
"avx512vbmi",
"sha",
"avx512vbmi2",
"avx512bitalg",
"vaes",
"vpclmulqdq",
"gfni",
],
),
(
"znver1",
&[
"mmx",
"sse",
"sse2",
"sse3",
"ssse3",
"sse4.1",
"sse4.2",
"popcnt",
"avx",
"avx2",
"fma",
"bmi",
"bmi2",
"lzcnt",
"cx16",
"f16c",
"movbe",
"adx",
"rdseed",
"xsavec",
"xsaves",
"clzero",
"clflushopt",
"mwaitx",
"sha",
],
),
(
"znver2",
&[
"mmx",
"sse",
"sse2",
"sse3",
"ssse3",
"sse4.1",
"sse4.2",
"popcnt",
"avx",
"avx2",
"fma",
"bmi",
"bmi2",
"lzcnt",
"cx16",
"f16c",
"movbe",
"adx",
"rdseed",
"xsavec",
"xsaves",
"clzero",
"clflushopt",
"mwaitx",
"sha",
"wbnoinvd",
],
),
(
"znver3",
&[
"mmx",
"sse",
"sse2",
"sse3",
"ssse3",
"sse4.1",
"sse4.2",
"popcnt",
"avx",
"avx2",
"fma",
"bmi",
"bmi2",
"lzcnt",
"cx16",
"f16c",
"movbe",
"adx",
"rdseed",
"xsavec",
"xsaves",
"clzero",
"clflushopt",
"mwaitx",
"sha",
"wbnoinvd",
"vaes",
"vpclmulqdq",
"avx512f",
],
),
];
for (name, feats) in x86_cpus {
self.cpus.insert(
name.to_string(),
feats.iter().map(|s| s.to_string()).collect(),
);
}
}
fn register_aarch64_cpus(&mut self) {
let aarch64_cpus: &[(&str, &[&str])] = &[
("cortex-a53", &["fp", "neon", "crc", "crypto"]),
(
"cortex-a55",
&["fp", "neon", "crc", "crypto", "dotprod", "rcpc"],
),
("cortex-a57", &["fp", "neon", "crc", "crypto"]),
("cortex-a72", &["fp", "neon", "crc", "crypto"]),
("cortex-a73", &["fp", "neon", "crc", "crypto"]),
(
"cortex-a75",
&["fp", "neon", "crc", "crypto", "dotprod", "rcpc"],
),
(
"cortex-a76",
&["fp", "neon", "crc", "crypto", "dotprod", "rcpc", "fp16"],
),
(
"cortex-a77",
&["fp", "neon", "crc", "crypto", "dotprod", "rcpc", "fp16"],
),
(
"cortex-a78",
&[
"fp", "neon", "crc", "crypto", "dotprod", "rcpc", "fp16", "i8mm",
],
),
(
"cortex-x1",
&[
"fp", "neon", "crc", "crypto", "dotprod", "rcpc", "fp16", "i8mm",
],
),
(
"neoverse-n1",
&["fp", "neon", "crc", "crypto", "dotprod", "rcpc", "fp16"],
),
(
"neoverse-n2",
&[
"fp", "neon", "crc", "crypto", "dotprod", "rcpc", "fp16", "sve", "i8mm",
],
),
(
"neoverse-v1",
&[
"fp", "neon", "crc", "crypto", "dotprod", "rcpc", "fp16", "sve", "i8mm",
],
),
(
"neoverse-v2",
&[
"fp", "neon", "crc", "crypto", "dotprod", "rcpc", "fp16", "sve", "sve2", "i8mm",
],
),
("apple-a7", &["fp", "neon"]),
("apple-a8", &["fp", "neon"]),
("apple-a9", &["fp", "neon"]),
("apple-a10", &["fp", "neon", "crc"]),
("apple-a11", &["fp", "neon", "crc", "crypto"]),
("apple-a12", &["fp", "neon", "crc", "crypto", "fp16"]),
(
"apple-a13",
&["fp", "neon", "crc", "crypto", "fp16", "dotprod"],
),
(
"apple-a14",
&["fp", "neon", "crc", "crypto", "fp16", "dotprod"],
),
(
"apple-m1",
&["fp", "neon", "crc", "crypto", "fp16", "dotprod", "lse"],
),
];
for (name, feats) in aarch64_cpus {
self.cpus.insert(
name.to_string(),
feats.iter().map(|s| s.to_string()).collect(),
);
}
}
fn register_arm_cpus(&mut self) {
let arm_cpus: &[(&str, &[&str])] = &[
("arm7tdmi", &[]),
("arm9", &[]),
("arm926ej-s", &[]),
("arm1136jf-s", &["vfp"]),
("cortex-a5", &["neon", "vfp4"]),
("cortex-a7", &["neon", "vfp4"]),
("cortex-a8", &["neon", "vfp3"]),
("cortex-a9", &["neon", "vfp3"]),
("cortex-a12", &["neon", "vfp4"]),
("cortex-a15", &["neon", "vfp4"]),
("cortex-a17", &["neon", "vfp4"]),
("cortex-m0", &[]),
("cortex-m3", &[]),
("cortex-m4", &["vfp4"]),
("cortex-m7", &["vfp5"]),
("cortex-r4", &[]),
("cortex-r5", &["vfp3"]),
("cortex-r7", &["vfp3"]),
];
for (name, feats) in arm_cpus {
self.cpus.insert(
name.to_string(),
feats.iter().map(|s| s.to_string()).collect(),
);
}
}
fn register_riscv_cpus(&mut self) {
let riscv_cpus: &[(&str, &[&str])] = &[
("sifive-e20", &["m"]),
("sifive-e21", &["m"]),
("sifive-e24", &["m"]),
("sifive-e31", &["m"]),
("sifive-e34", &["m"]),
("sifive-e76", &["m", "a", "c"]),
("sifive-s21", &["m", "a"]),
("sifive-s51", &["m", "a"]),
("sifive-s54", &["m", "a", "c"]),
("sifive-s76", &["m", "a", "c"]),
("sifive-u54", &["m", "a", "c", "f", "d"]),
("sifive-u74", &["m", "a", "c", "f", "d"]),
("rocket-rv32", &["m", "a", "c"]),
("rocket-rv64", &["m", "a", "c", "f", "d"]),
("boom-v2", &["m", "a", "c", "f", "d"]),
("xiangshan-nanhu", &["m", "a", "c", "f", "d"]),
];
for (name, feats) in riscv_cpus {
self.cpus.insert(
name.to_string(),
feats.iter().map(|s| s.to_string()).collect(),
);
}
}
fn register_x86_features(&mut self) {
let x86_feats: &[(&str, &str, bool, &[&str])] = &[
("mmx", "MMX SIMD instructions", true, &[]),
("sse", "SSE (Streaming SIMD Extensions)", true, &["mmx"]),
("sse2", "SSE2 instructions", true, &["sse"]),
("sse3", "SSE3 instructions", true, &["sse2"]),
("ssse3", "Supplemental SSE3", true, &["sse3"]),
("sse4.1", "SSE4.1 instructions", false, &["ssse3"]),
("sse4.2", "SSE4.2 instructions", false, &["sse4.1"]),
("popcnt", "POPCNT instruction", false, &["sse4.2"]),
("avx", "Advanced Vector Extensions", false, &["sse4.2"]),
("avx2", "AVX2 instructions", false, &["avx"]),
("fma", "FMA3 instructions", false, &["avx"]),
("bmi", "BMI instructions", false, &[]),
("bmi2", "BMI2 instructions", false, &["bmi"]),
("lzcnt", "LZCNT instruction", false, &[]),
("f16c", "F16C instructions", false, &["avx"]),
("movbe", "MOVBE instruction", false, &[]),
("adx", "ADCX/ADOX instructions", false, &[]),
("rdseed", "RDSEED instruction", false, &[]),
("cx16", "CMPXCHG16B instruction", true, &[]),
("xsavec", "XSAVEC instruction", false, &[]),
("xsaves", "XSAVES instruction", false, &["xsavec"]),
("clflushopt", "CLFLUSHOPT instruction", false, &[]),
("avx512f", "AVX-512 Foundation", false, &["avx2"]),
("avx512bw", "AVX-512 Byte/Word", false, &["avx512f"]),
("avx512dq", "AVX-512 DQ", false, &["avx512f"]),
("avx512vl", "AVX-512 Vector Length", false, &["avx512f"]),
("avx512vbmi", "AVX-512 VBMI", false, &["avx512bw"]),
("avx512vbmi2", "AVX-512 VBMI2", false, &["avx512vbmi"]),
("avx512bitalg", "AVX-512 BITALG", false, &["avx512bw"]),
("vaes", "VAES instructions", false, &["avx"]),
("vpclmulqdq", "VPCLMULQDQ", false, &["avx"]),
("gfni", "GFNI instructions", false, &["avx"]),
("sha", "SHA instructions", false, &["sse2"]),
("clzero", "CLZERO instruction", false, &[]),
("mwaitx", "MONITORX/MWAITX", false, &[]),
("wbnoinvd", "WBNOINVD instruction", false, &[]),
];
for (name, desc, default, implies) in x86_feats {
let mut f = TargetFeature::new(name, desc, *default);
f.implies = implies.iter().map(|s| s.to_string()).collect();
self.features.insert(name.to_string(), f);
}
}
fn register_aarch64_features(&mut self) {
let aarch64_feats: &[(&str, &str, bool, &[&str])] = &[
("fp", "Floating-point (base)", true, &[]),
("neon", "Advanced SIMD (NEON)", true, &["fp"]),
("crc", "CRC instructions", true, &[]),
("crypto", "Cryptographic extensions", false, &["neon"]),
("fp16", "Half-precision FP", false, &["fp"]),
("lse", "Large System Extensions", false, &[]),
(
"rcpc",
"Release Consistent Processor Consistent",
false,
&[],
),
("dotprod", "Dot product instructions", false, &["neon"]),
("rdm", "Rounding Double Multiply", false, &["neon"]),
("sve", "Scalable Vector Extension", false, &["neon"]),
("sve2", "Scalable Vector Extension 2", false, &["sve"]),
("i8mm", "Integer 8-bit Matrix Multiply", false, &["neon"]),
("pauth", "Pointer Authentication", false, &[]),
("mte", "Memory Tagging Extension", false, &[]),
("bf16", "BFloat16 extension", false, &["neon"]),
];
for (name, desc, default, implies) in aarch64_feats {
let mut f = TargetFeature::new(name, desc, *default);
f.implies = implies.iter().map(|s| s.to_string()).collect();
self.features.insert(name.to_string(), f);
}
}
fn register_arm_features(&mut self) {
let arm_feats: &[(&str, &str, bool, &[&str])] = &[
("vfp2", "VFPv2 floating-point", false, &[]),
("vfp3", "VFPv3 floating-point", false, &["vfp2"]),
("vfp4", "VFPv4 floating-point", false, &["vfp3"]),
("vfp5", "VFPv5 floating-point", false, &["vfp4"]),
("neon", "NEON SIMD", false, &["vfp3"]),
("dsp", "DSP instructions", false, &[]),
("thumb2", "Thumb-2 instructions", true, &[]),
("aclass", "ARM Application-class", false, &[]),
];
for (name, desc, default, implies) in arm_feats {
let mut f = TargetFeature::new(name, desc, *default);
f.implies = implies.iter().map(|s| s.to_string()).collect();
self.features.insert(name.to_string(), f);
}
}
fn register_riscv_features(&mut self) {
let riscv_feats: &[(&str, &str, bool, &[&str])] = &[
("m", "Integer Multiplication/Division", false, &[]),
("a", "Atomic instructions", false, &[]),
("f", "Single-precision FP", false, &[]),
("d", "Double-precision FP", false, &["f"]),
("c", "Compressed instructions", false, &[]),
("v", "Vector extension", false, &["f"]),
("zicsr", "Control and Status Register", false, &[]),
("zifencei", "Instruction-Fetch Fence", false, &[]),
("zba", "Bit-manipulation Address generation", false, &[]),
("zbb", "Bit-manipulation Base", false, &[]),
("zbc", "Bit-manipulation Carry-less multiply", false, &[]),
("zbs", "Bit-manipulation Single-bit", false, &[]),
];
for (name, desc, default, implies) in riscv_feats {
let mut f = TargetFeature::new(name, desc, *default);
f.implies = implies.iter().map(|s| s.to_string()).collect();
self.features.insert(name.to_string(), f);
}
}
}
impl Default for CpuFeatureDatabase {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ClangTargetInfo {
pub triple: String,
pub parsed_triple: Triple,
pub codegen_info: TargetCodeGenInfo,
pub abi: ClangABI,
pub cpu: String,
pub features: String,
pub code_model: CodeModel,
pub reloc_model: RelocModel,
pub opt_level: ClangOptLevel,
pub debug_info: DebugInfoKind,
pub stack_protector: StackProtectorLevel,
pub no_frame_pointer_elim: bool,
pub unsafe_fp_math: bool,
pub no_signed_zeros_fp_math: bool,
pub no_strict_aliasing: bool,
pub position_independent: bool,
pub local_exec_tls: bool,
pub use_soft_float: bool,
pub emit_unwind_tables: bool,
pub stack_alignment: u32,
pub thread_model: String,
pub function_sections: bool,
pub data_sections: bool,
pub integrated_as: bool,
pub cpu_db: CpuFeatureDatabase,
}
impl ClangTargetInfo {
pub fn new(triple: &str) -> Self {
let t = Triple::parse(triple);
let codegen_info = TargetCodeGenInfo::for_arch(t.arch);
let abi = ClangABI::new(triple);
Self {
triple: triple.to_string(),
parsed_triple: t,
codegen_info,
abi,
cpu: String::from("generic"),
features: String::new(),
code_model: CodeModel::Small,
reloc_model: RelocModel::Static,
opt_level: ClangOptLevel::default(),
debug_info: DebugInfoKind::default(),
stack_protector: StackProtectorLevel::default(),
no_frame_pointer_elim: false,
unsafe_fp_math: false,
no_signed_zeros_fp_math: false,
no_strict_aliasing: false,
position_independent: false,
local_exec_tls: false,
use_soft_float: false,
emit_unwind_tables: true,
stack_alignment: 0,
thread_model: String::from("posix"),
function_sections: false,
data_sections: false,
integrated_as: true,
cpu_db: CpuFeatureDatabase::new(),
}
}
pub fn with_triple(mut self, triple: &str) -> Self {
self.triple = triple.to_string();
self.parsed_triple = Triple::parse(triple);
self.codegen_info = TargetCodeGenInfo::for_arch(self.parsed_triple.arch);
self.abi = ClangABI::new(triple);
self
}
pub fn with_cpu(mut self, cpu: &str) -> Self {
self.cpu = cpu.to_string();
self
}
pub fn with_features(mut self, features: &str) -> Self {
self.features = features.to_string();
self
}
pub fn add_feature(&mut self, feature: &str, enable: bool) {
if !self.features.is_empty() {
self.features.push(',');
}
if enable {
self.features.push('+');
} else {
self.features.push('-');
}
self.features.push_str(feature);
}
pub fn with_abi(mut self, abi: &str) -> Self {
self.abi = self.abi.with_base_abi(abi);
self
}
pub fn with_float_abi(mut self, fa: &str) -> Self {
self.abi = self.abi.with_float_abi(fa);
self.use_soft_float = self.abi.is_soft_float();
self
}
pub fn with_code_model(mut self, model: CodeModel) -> Self {
self.code_model = model;
self
}
pub fn with_reloc_model(mut self, model: RelocModel) -> Self {
self.reloc_model = model;
self.position_independent = model.is_pic();
self
}
pub fn with_opt_level(mut self, level: ClangOptLevel) -> Self {
self.opt_level = level;
if level.implies_fast_math() {
self.unsafe_fp_math = true;
self.no_signed_zeros_fp_math = true;
}
self
}
pub fn with_debug_info(mut self, kind: DebugInfoKind) -> Self {
self.debug_info = kind;
self
}
pub fn with_stack_protector(mut self, level: StackProtectorLevel) -> Self {
self.stack_protector = level;
self
}
pub fn with_frame_pointer_elim(mut self, elim: bool) -> Self {
self.no_frame_pointer_elim = !elim;
self
}
pub fn with_unsafe_fp_math(mut self, v: bool) -> Self {
self.unsafe_fp_math = v;
self
}
pub fn with_no_signed_zeros(mut self, v: bool) -> Self {
self.no_signed_zeros_fp_math = v;
self
}
pub fn with_strict_aliasing(mut self, sa: bool) -> Self {
self.no_strict_aliasing = !sa;
self
}
pub fn with_arch(mut self, arch: &str) -> Self {
if arch == "native" {
let mut detector = HostCpuDetector::new();
let host_cpu = detector.host_cpu_name().to_string();
self.cpu = host_cpu;
} else {
let cpu = match arch {
"x86-64" => "x86-64", "armv6" => "arm1176jzf-s",
"armv7-a" => "cortex-a8",
"armv7ve" => "cortex-a7",
"armv8-a" => "cortex-a53",
"armv8.1-a" => "cortex-a55",
"armv8.2-a" => "cortex-a75",
"armv8.3-a" => "cortex-a76",
"armv8.4-a" => "neoverse-n1",
"armv8.5-a" => "neoverse-n2",
"armv8.6-a" => "neoverse-v1",
"armv9-a" => "neoverse-v2",
"rv32i" => "rocket-rv32",
"rv32imac" => "sifive-e76",
"rv64gc" => "sifive-u74",
"rv64imafdc" => "rocket-rv64",
_ => "generic",
};
self.cpu = cpu.to_string();
}
self
}
pub fn with_tune_cpu(mut self, tune: &str) -> Self {
if self.cpu == "generic" && tune != "generic" {
self.cpu = tune.to_string();
}
self
}
pub fn with_mattr(mut self, attr: &str) -> Self {
for part in attr.split(',') {
let trimmed = part.trim();
if trimmed.is_empty() {
continue;
}
let enable = !trimmed.starts_with('-');
let feat = if trimmed.starts_with('+') || trimmed.starts_with('-') {
&trimmed[1..]
} else {
trimmed
};
self.add_feature(feat, enable);
}
self
}
pub fn create_target_machine(&mut self) -> TargetMachine {
let features = self.cpu_db.feature_string(&self.cpu, &[]);
let mut combined = features;
if !self.features.is_empty() {
if !combined.is_empty() {
combined.push(',');
}
combined.push_str(&self.features);
}
let opts = TargetOptions {
triple: self.triple.clone(),
cpu: self.cpu.clone(),
features: combined,
abi: self.abi.base_abi.clone(),
opt_level: self.opt_level.to_llvm_opt(),
reloc_model: self.reloc_model,
code_model: self.code_model,
float_abi: self.abi.to_llvm_float_abi(),
position_independent: self.position_independent,
use_local_exec_tls: self.local_exec_tls,
use_soft_float: self.use_soft_float,
disable_fp_elim: self.no_frame_pointer_elim,
emit_unwind_tables: self.emit_unwind_tables,
stack_alignment: self.stack_alignment,
output_file: None,
no_signed_zeros_fp_math: self.no_signed_zeros_fp_math,
unsafe_fp_math: self.unsafe_fp_math,
trap_unaligned: false,
function_sections: self.function_sections,
data_sections: self.data_sections,
integrated_as: self.integrated_as,
preserve_asm_comments: false,
emit_compact_unwind: false,
thread_model: self.thread_model.clone(),
};
TargetMachine::with_options(&self.triple, opts)
}
pub fn data_layout(&self) -> DataLayout {
data_layout_for_triple(&self.parsed_triple)
}
pub fn is_64bit(&self) -> bool {
self.parsed_triple.is_64bit()
}
pub fn is_little_endian(&self) -> bool {
data_layout_for_triple(&self.parsed_triple).is_little_endian()
}
pub fn pointer_width(&self) -> u32 {
if self.is_64bit() {
64
} else {
32
}
}
pub fn is_windows_abi(&self) -> bool {
self.parsed_triple.is_windows()
}
}
impl Default for ClangTargetInfo {
fn default() -> Self {
Self::new("x86_64-unknown-linux-gnu")
}
}
pub fn data_layout_for_triple(triple: &Triple) -> DataLayout {
match triple.arch {
Arch::X86_64 => DataLayout::parse(
"e-m:e-p:64:64-i64:64-f80:128-n8:16:32:64-S128"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::X86 => DataLayout::parse(
"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::AArch64 => DataLayout::aarch64_linux(),
Arch::AArch64_32 => DataLayout::parse(
"e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
).unwrap_or_else(|_| DataLayout::aarch64_linux()),
Arch::ARM | Arch::ARMeb | Arch::Thumb | Arch::Thumbeb => DataLayout::parse(
"e-m:e-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:64-a:0:32-n32-S64"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::RISCV64 => DataLayout::parse(
"e-m:e-p:64:64-i64:64-i128:128-n64-S128"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::RISCV32 => DataLayout::parse(
"e-m:e-p:32:32-i64:64-n32-S128"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::Mips | Arch::Mipsel => DataLayout::parse(
"E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32:64-S128"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::Mips64 | Arch::Mips64el => DataLayout::parse(
"E-m:m-i8:8:32-i16:16:32-i64:64-n32:64-S128"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::PowerPC => DataLayout::parse(
"E-m:e-p:32:32-i64:64-n32"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::PowerPC64 => DataLayout::parse(
"E-m:e-i64:64-n32:64"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::PowerPC64le => DataLayout::parse(
"e-m:e-i64:64-n32:64"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::Sparc | Arch::Sparcv9 => DataLayout::parse(
"E-m:e-p:64:64-i64:64-f128:64-n32:64-S128"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::SystemZ => DataLayout::parse(
"E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-a:8:16-n32:64"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::BPF | Arch::BPFEB | Arch::BPF64 => DataLayout::parse(
"e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::WebAssembly32 | Arch::WebAssembly64 => DataLayout::parse(
"e-m:e-p:32:32-i64:64-n32:64-S128"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::AVR => DataLayout::parse(
"e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::MSP430 => DataLayout::parse(
"e-m:e-p:16:16-i32:16:32-a:16-n8:16"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::Hexagon => DataLayout::parse(
"e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32:32-i16:16:16-i1:8:8-f64:64:64-f32:32:32-v64:64:64-v32:32:32"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::Lanai => DataLayout::parse(
"E-m:e-p:32:32-i64:64-a:0:32-n32"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::NVPTX | Arch::NVPTX64 => DataLayout::parse(
"e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::AMDGPU => DataLayout::parse(
"e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::ARC => DataLayout::parse(
"e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-f32:32:32-i64:32:32-f64:32:32-v64:32:32-v128:32:32-a:0:32-n32"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::CSKY => DataLayout::parse(
"e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-f32:32:32-i64:32:32-f64:32:32-n32"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::Xtensa => DataLayout::parse(
"e-m:e-p:32:32-i8:8:32-i16:16:32-i32:32:32-f32:32:32-n32"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
Arch::LoongArch64 => DataLayout::parse(
"e-m:e-p:64:64-i64:64-i128:128-n64-S128"
).unwrap_or_else(|_| DataLayout::x86_64_linux()),
_ => DataLayout::x86_64_linux(),
}
}
#[derive(Debug, Clone, Default)]
pub struct TargetFlags {
pub target_triple: Option<String>,
pub march: Option<String>,
pub mcpu: Option<String>,
pub mtune: Option<String>,
pub mattr: Option<String>,
pub mabi: Option<String>,
pub mfloat_abi: Option<String>,
pub mcmodel: Option<String>,
pub fpic: bool,
pub fpie: bool,
pub fno_pic: bool,
pub fstack_protector: Option<String>,
pub fomit_frame_pointer: bool,
pub fno_omit_frame_pointer: bool,
pub ffast_math: bool,
pub funsafe_math_optimizations: bool,
pub fno_strict_aliasing: bool,
pub g_level: u8,
pub opt_level: Option<String>,
pub m32: bool,
pub m64: bool,
}
impl TargetFlags {
pub fn to_target_info(&self) -> ClangTargetInfo {
let triple = self.resolve_triple();
let mut info = ClangTargetInfo::new(&triple);
if let Some(ref march) = self.march {
info = info.with_arch(march);
}
if let Some(ref mcpu) = self.mcpu {
info = info.with_cpu(mcpu);
} else if let Some(ref mtune) = self.mtune {
info = info.with_tune_cpu(mtune);
}
if let Some(ref mattr) = self.mattr {
info = info.with_mattr(mattr);
}
if let Some(ref mabi) = self.mabi {
info = info.with_abi(mabi);
}
if let Some(ref mfloat_abi) = self.mfloat_abi {
info = info.with_float_abi(mfloat_abi);
}
if let Some(ref mcmodel) = self.mcmodel {
let model = match mcmodel.as_str() {
"tiny" => CodeModel::Tiny,
"small" => CodeModel::Small,
"kernel" => CodeModel::Kernel,
"medium" => CodeModel::Medium,
"large" => CodeModel::Large,
_ => CodeModel::default(),
};
info.code_model = model;
}
if self.fno_pic {
info = info.with_reloc_model(RelocModel::Static);
} else if self.fpie {
info = info.with_reloc_model(RelocModel::PIC);
} else if self.fpic {
info = info.with_reloc_model(RelocModel::PIC);
}
if let Some(ref sp) = self.fstack_protector {
info = info.with_stack_protector(StackProtectorLevel::from_flag(sp));
}
if self.fno_omit_frame_pointer {
info.no_frame_pointer_elim = true;
} else if self.fomit_frame_pointer {
info.no_frame_pointer_elim = false;
}
if self.ffast_math || self.funsafe_math_optimizations {
info.unsafe_fp_math = true;
}
if self.fno_strict_aliasing {
info.no_strict_aliasing = true;
}
info.debug_info = DebugInfoKind::from_flag(self.g_level);
if let Some(ref opt) = self.opt_level {
info.opt_level = ClangOptLevel::from_flag(opt);
}
info
}
fn resolve_triple(&self) -> String {
if let Some(ref t) = self.target_triple {
return t.clone();
}
if self.m32 {
return String::from("i686-unknown-linux-gnu");
}
if self.m64 {
return String::from("x86_64-unknown-linux-gnu");
}
String::from("x86_64-unknown-linux-gnu")
}
}
#[derive(Debug, Clone, Default)]
pub struct FunctionAttrMapper {
pub no_frame_pointer_elim: bool,
pub unsafe_fp_math: bool,
pub no_signed_zeros_fp_math: bool,
pub no_trapping_math: bool,
pub approx_func_fp_math: bool,
pub no_nans_fp_math: bool,
pub no_infs_fp_math: bool,
pub denormal_fp_math_mode: String,
pub denormal_fp_math_f32_mode: String,
pub stack_alignment: u32,
pub trap_func_name: Option<String>,
pub target_cpu: Option<String>,
pub target_features: Option<String>,
}
impl FunctionAttrMapper {
pub fn from_target_info(ti: &ClangTargetInfo) -> Self {
let mut mapper = Self::default();
mapper.no_frame_pointer_elim = ti.no_frame_pointer_elim;
mapper.unsafe_fp_math = ti.unsafe_fp_math;
mapper.no_signed_zeros_fp_math = ti.no_signed_zeros_fp_math;
mapper.stack_alignment = ti.stack_alignment;
mapper.target_cpu = Some(ti.cpu.clone());
mapper.target_features = Some(ti.features.clone());
if ti.unsafe_fp_math {
mapper.no_trapping_math = true;
mapper.approx_func_fp_math = true;
mapper.no_nans_fp_math = true;
mapper.no_infs_fp_math = true;
}
mapper
}
pub fn emit_attributes(&self) -> Vec<String> {
let mut attrs: Vec<String> = Vec::new();
if self.unsafe_fp_math {
attrs.push(r#""unsafe-fp-math"="true""#.to_string());
}
if self.no_signed_zeros_fp_math {
attrs.push(r#""no-signed-zeros-fp-math"="true""#.to_string());
}
if self.no_trapping_math {
attrs.push(r#""no-trapping-math"="true""#.to_string());
}
if self.approx_func_fp_math {
attrs.push(r#""approx-func-fp-math"="true""#.to_string());
}
if self.no_nans_fp_math {
attrs.push(r#""no-nans-fp-math"="true""#.to_string());
}
if self.no_infs_fp_math {
attrs.push(r#""no-infs-fp-math"="true""#.to_string());
}
if self.no_frame_pointer_elim {
attrs.push(r#""no-frame-pointer-elim"="true""#.to_string());
}
if !self.denormal_fp_math_mode.is_empty() {
attrs.push(format!(
r#""denormal-fp-math"="{}""#,
self.denormal_fp_math_mode
));
}
if !self.denormal_fp_math_f32_mode.is_empty() {
attrs.push(format!(
r#""denormal-fp-math-f32"="{}""#,
self.denormal_fp_math_f32_mode
));
}
if let Some(ref trap_fn) = self.trap_func_name {
attrs.push(format!(r#""trap-func-name"="{}""#, trap_fn));
}
if let Some(ref cpu) = self.target_cpu {
attrs.push(format!(r#""target-cpu"="{}""#, cpu));
}
if let Some(ref feats) = self.target_features {
if !feats.is_empty() {
attrs.push(format!(r#""target-features"="{}""#, feats));
}
}
if self.stack_alignment > 0 {
attrs.push(format!(r#""stack-alignment"="{}""#, self.stack_alignment));
}
attrs
}
pub fn with_no_strict_aliasing(self, v: bool) -> Self {
let _ = v;
self
}
}
pub fn create_target_machine(triple: &str, cpu: &str, features: &str) -> TargetMachine {
let info = ClangTargetInfo::new(triple)
.with_cpu(cpu)
.with_features(features);
let mut info = info;
info.create_target_machine()
}
pub fn create_x86_64_machine(cpu: &str, features: &str) -> TargetMachine {
create_target_machine("x86_64-unknown-linux-gnu", cpu, features)
}
pub fn create_aarch64_machine(cpu: &str, features: &str) -> TargetMachine {
create_target_machine("aarch64-unknown-linux-gnu", cpu, features)
}
pub fn create_arm_machine(cpu: &str, features: &str) -> TargetMachine {
create_target_machine("arm-unknown-linux-gnueabihf", cpu, features)
}
pub fn create_riscv64_machine(cpu: &str, features: &str) -> TargetMachine {
create_target_machine("riscv64-unknown-linux-gnu", cpu, features)
}
pub fn create_riscv32_machine(cpu: &str, features: &str) -> TargetMachine {
create_target_machine("riscv32-unknown-linux-gnu", cpu, features)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_debug_info_from_flag() {
assert_eq!(DebugInfoKind::from_flag(0), DebugInfoKind::NoDebug);
assert_eq!(DebugInfoKind::from_flag(1), DebugInfoKind::LineTablesOnly);
assert_eq!(DebugInfoKind::from_flag(2), DebugInfoKind::LimitedDebug);
assert_eq!(DebugInfoKind::from_flag(3), DebugInfoKind::FullWithMacros);
}
#[test]
fn test_stack_protector_from_flag() {
assert_eq!(
StackProtectorLevel::from_flag("none"),
StackProtectorLevel::None
);
assert_eq!(
StackProtectorLevel::from_flag("all"),
StackProtectorLevel::All
);
assert_eq!(
StackProtectorLevel::from_flag("strong"),
StackProtectorLevel::Strong
);
assert_eq!(
StackProtectorLevel::from_flag("default"),
StackProtectorLevel::Basic
);
}
#[test]
fn test_clang_opt_level_from_flag() {
assert_eq!(ClangOptLevel::from_flag("0"), ClangOptLevel::None);
assert_eq!(ClangOptLevel::from_flag("1"), ClangOptLevel::Less);
assert_eq!(ClangOptLevel::from_flag("2"), ClangOptLevel::Default);
assert_eq!(ClangOptLevel::from_flag("3"), ClangOptLevel::Aggressive);
assert_eq!(ClangOptLevel::from_flag("s"), ClangOptLevel::DefaultSize);
assert_eq!(ClangOptLevel::from_flag("z"), ClangOptLevel::AggressiveSize);
assert_eq!(ClangOptLevel::from_flag("fast"), ClangOptLevel::FastMath);
}
#[test]
fn test_clang_abi_defaults() {
let abi = ClangABI::new("x86_64-unknown-linux-gnu");
assert_eq!(abi.base_abi, "lp64");
assert!(!abi.is_soft_float());
let abi2 = ClangABI::new("arm-unknown-linux-gnueabi");
assert_eq!(abi2.base_abi, "ilp32");
}
#[test]
fn test_clang_abi_override() {
let abi = ClangABI::new("riscv64-unknown-linux-gnu")
.with_base_abi("lp64")
.with_float_abi("soft");
assert_eq!(abi.base_abi, "lp64");
assert!(abi.is_soft_float());
}
#[test]
fn test_target_codegen_info_x86_64() {
let info = TargetCodeGenInfo::for_arch(Arch::X86_64);
assert!(info.uses_sret_demotion);
assert!(info.indirect_struct_return);
assert!(info.varargs_save_area);
assert!(info.setjmp_returns_twice);
assert_eq!(info.stack_alignment, 16);
}
#[test]
fn test_target_codegen_info_arm_thumb() {
let info = TargetCodeGenInfo::for_arch(Arch::Thumb);
assert!(!info.uses_sret_demotion);
assert_eq!(info.stack_alignment, 8);
}
#[test]
fn test_target_codegen_info_wasm() {
let info = TargetCodeGenInfo::for_arch(Arch::WebAssembly32);
assert!(!info.varargs_save_area);
assert!(!info.indirect_struct_return);
}
#[test]
fn test_host_cpu_detector() {
let mut detector = HostCpuDetector::new();
let name = detector.host_cpu_name().to_string();
assert!(!name.is_empty());
let feats = detector.host_cpu_features();
assert!(feats.len() >= 0);
}
#[test]
fn test_cpu_feature_database_x86() {
let mut db = CpuFeatureDatabase::new();
let feats = db.resolve_cpu("haswell");
assert!(feats.contains("avx2"));
assert!(feats.contains("fma"));
assert!(feats.contains("avx"));
let feats2 = db.resolve_cpu("generic");
assert!(feats2.is_empty());
}
#[test]
fn test_cpu_feature_database_aarch64() {
let mut db = CpuFeatureDatabase::new();
let feats = db.resolve_cpu("cortex-a53");
assert!(feats.contains("neon"));
assert!(feats.contains("crc"));
}
#[test]
fn test_cpu_feature_database_riscv() {
let mut db = CpuFeatureDatabase::new();
let feats = db.resolve_cpu("sifive-u74");
assert!(feats.contains("m"));
assert!(feats.contains("a"));
assert!(feats.contains("c"));
assert!(feats.contains("f"));
assert!(feats.contains("d"));
}
#[test]
fn test_feature_string_generation() {
let mut db = CpuFeatureDatabase::new();
let s = db.feature_string(
"cortex-a53",
&[(String::from("sve"), false), (String::from("fp16"), true)],
);
assert!(s.contains("+crc"));
assert!(s.contains("+crypto"));
assert!(s.contains("+fp16"));
assert!(!s.contains("sve")); }
#[test]
fn test_clang_target_info_basic() {
let info = ClangTargetInfo::new("x86_64-unknown-linux-gnu");
assert_eq!(info.triple, "x86_64-unknown-linux-gnu");
assert!(!info.position_independent);
assert!(info.emit_unwind_tables);
}
#[test]
fn test_clang_target_info_with_flags() {
let info = ClangTargetInfo::new("aarch64-unknown-linux-gnu")
.with_cpu("cortex-a76")
.with_opt_level(ClangOptLevel::Aggressive)
.with_reloc_model(RelocModel::PIC)
.with_debug_info(DebugInfoKind::FullDebug)
.with_stack_protector(StackProtectorLevel::Strong)
.with_frame_pointer_elim(false)
.with_unsafe_fp_math(true);
assert_eq!(info.cpu, "cortex-a76");
assert_eq!(info.opt_level, ClangOptLevel::Aggressive);
assert!(info.position_independent);
assert_eq!(info.debug_info, DebugInfoKind::FullDebug);
assert_eq!(info.stack_protector, StackProtectorLevel::Strong);
assert!(info.no_frame_pointer_elim);
assert!(info.unsafe_fp_math);
}
#[test]
fn test_target_flags_to_target_info() {
let flags = TargetFlags {
target_triple: Some(String::from("arm-unknown-linux-gnueabihf")),
mcpu: Some(String::from("cortex-a9")),
mfloat_abi: Some(String::from("hard")),
mcmodel: Some(String::from("small")),
fpic: true,
g_level: 2,
opt_level: Some(String::from("3")),
..Default::default()
};
let info = flags.to_target_info();
assert_eq!(info.triple, "arm-unknown-linux-gnueabihf");
assert_eq!(info.cpu, "cortex-a9");
assert!(!info.abi.is_soft_float());
assert_eq!(info.code_model, CodeModel::Small);
assert!(info.position_independent);
assert_eq!(info.debug_info, DebugInfoKind::LimitedDebug);
assert_eq!(info.opt_level, ClangOptLevel::Aggressive);
}
#[test]
fn test_target_flags_defaults() {
let flags = TargetFlags::default();
assert!(flags.target_triple.is_none());
assert!(flags.mcpu.is_none());
}
#[test]
fn test_function_attr_mapper_basic() {
let ti = ClangTargetInfo::new("x86_64-unknown-linux-gnu");
let mapper = FunctionAttrMapper::from_target_info(&ti);
let attrs = mapper.emit_attributes();
assert!(!attrs.is_empty());
let has_cpu = attrs.iter().any(|a| a.contains("target-cpu"));
assert!(has_cpu);
}
#[test]
fn test_function_attr_mapper_fast_math() {
let ti = ClangTargetInfo::new("x86_64-unknown-linux-gnu").with_unsafe_fp_math(true);
let mapper = FunctionAttrMapper::from_target_info(&ti);
let attrs = mapper.emit_attributes();
let has_ufm = attrs.iter().any(|a| a.contains("unsafe-fp-math"));
let has_ninf = attrs.iter().any(|a| a.contains("no-infs-fp-math"));
assert!(has_ufm);
assert!(has_ninf);
}
#[test]
fn test_create_target_machine() {
let tm = create_target_machine("x86_64-unknown-linux-gnu", "generic", "");
assert_eq!(tm.triple(), "x86_64-unknown-linux-gnu");
}
#[test]
fn test_create_x86_64_machine() {
let tm = create_x86_64_machine("haswell", "+avx2");
assert!(tm.triple().contains("x86_64"));
}
#[test]
fn test_create_aarch64_machine() {
let tm = create_aarch64_machine("cortex-a53", "+neon");
assert!(tm.triple().contains("aarch64"));
}
#[test]
fn test_create_arm_machine() {
let tm = create_arm_machine("cortex-a9", "");
assert!(tm.triple().contains("arm"));
}
#[test]
fn test_create_riscv64_machine() {
let tm = create_riscv64_machine("sifive-u74", "+m,+a,+c,+f,+d");
assert!(tm.triple().contains("riscv64"));
}
#[test]
fn test_clang_target_info_modify() {
let mut info = ClangTargetInfo::new("x86_64-unknown-linux-gnu");
info.add_feature("avx2", true);
info.add_feature("sse3", false);
let feats = info.features.clone();
assert!(feats.contains("+avx2"));
assert!(feats.contains("-sse3"));
}
#[test]
fn test_clang_target_info_arch_mapping() {
let info = ClangTargetInfo::new("aarch64-unknown-linux-gnu").with_arch("armv8.2-a");
assert_eq!(info.cpu, "cortex-a75");
let info2 = ClangTargetInfo::new("riscv64-unknown-linux-gnu").with_arch("rv64gc");
assert_eq!(info2.cpu, "sifive-u74");
}
#[test]
fn test_float_abi_mapping() {
let info = ClangTargetInfo::new("arm-unknown-linux-gnueabi");
assert!(info.abi.is_soft_float());
let info = ClangTargetInfo::new("arm-unknown-linux-gnueabihf");
assert!(!info.abi.is_soft_float());
}
#[test]
fn test_code_model_mapping() {
let info =
ClangTargetInfo::new("x86_64-unknown-linux-gnu").with_code_model(CodeModel::Kernel);
assert_eq!(info.code_model, CodeModel::Kernel);
}
#[test]
fn test_target_flags_fast_math() {
let flags = TargetFlags {
ffast_math: true,
..Default::default()
};
let info = flags.to_target_info();
assert!(info.unsafe_fp_math);
}
#[test]
fn test_target_flags_no_strict_aliasing() {
let flags = TargetFlags {
fno_strict_aliasing: true,
..Default::default()
};
let info = flags.to_target_info();
assert!(info.no_strict_aliasing);
}
#[test]
fn test_target_flags_fno_omit_frame_pointer() {
let flags = TargetFlags {
fno_omit_frame_pointer: true,
..Default::default()
};
let info = flags.to_target_info();
assert!(info.no_frame_pointer_elim);
}
#[test]
fn test_clang_opt_level_is_size() {
assert!(ClangOptLevel::DefaultSize.is_size_oriented());
assert!(ClangOptLevel::AggressiveSize.is_size_oriented());
assert!(!ClangOptLevel::None.is_size_oriented());
assert!(!ClangOptLevel::Default.is_size_oriented());
}
#[test]
fn test_clang_opt_level_to_llvm() {
assert_eq!(ClangOptLevel::None.to_llvm_opt(), CodeGenOptLevel::None);
assert_eq!(ClangOptLevel::Less.to_llvm_opt(), CodeGenOptLevel::Less);
assert_eq!(
ClangOptLevel::Default.to_llvm_opt(),
CodeGenOptLevel::Default
);
assert_eq!(
ClangOptLevel::Aggressive.to_llvm_opt(),
CodeGenOptLevel::Aggressive
);
}
#[test]
fn test_target_codegen_indirect_pass() {
let x86 = TargetCodeGenInfo::for_arch(Arch::X86_64);
assert!(x86.should_pass_indirectly(64));
let wasm = TargetCodeGenInfo::for_arch(Arch::WebAssembly32);
assert!(!wasm.should_pass_indirectly(64));
}
#[test]
fn test_detect_host_cpu_stub() {
#[cfg(target_arch = "x86_64")]
{
let cpu = HostCpuDetector::detect_host_cpu();
assert!(!cpu.is_empty());
}
}
}