use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Arch {
X86,
X86_64,
Aarch64,
Arm,
Riscv32,
Riscv64,
Wasm32,
PowerPc,
PowerPc64,
S390x,
Mips,
Mips64,
Sparc,
Sparc64,
LoongArch64,
}
impl Arch {
pub fn as_str(self) -> &'static str {
match self {
Arch::X86 => "x86",
Arch::X86_64 => "x86_64",
Arch::Aarch64 => "aarch64",
Arch::Arm => "arm",
Arch::Riscv32 => "riscv32",
Arch::Riscv64 => "riscv64",
Arch::Wasm32 => "wasm32",
Arch::PowerPc => "powerpc",
Arch::PowerPc64 => "powerpc64",
Arch::S390x => "s390x",
Arch::Mips => "mips",
Arch::Mips64 => "mips64",
Arch::Sparc => "sparc",
Arch::Sparc64 => "sparc64",
Arch::LoongArch64 => "loongarch64",
}
}
pub fn macros(self) -> &'static [(&'static str, &'static str)] {
match self {
Arch::X86 => &[("__i386__", "1"), ("__i386", "1")],
Arch::X86_64 => &[
("__x86_64__", "1"),
("__x86_64", "1"),
("__amd64__", "1"),
("__amd64", "1"),
("__SSE__", "1"),
("__SSE2__", "1"),
("__SSE_MATH__", "1"),
("__SSE2_MATH__", "1"),
],
Arch::Aarch64 => &[("__aarch64__", "1")],
Arch::Arm => &[("__arm__", "1")],
Arch::Riscv32 => &[("__riscv", "1"), ("__riscv_xlen", "32")],
Arch::Riscv64 => &[("__riscv", "1"), ("__riscv_xlen", "64")],
Arch::Wasm32 => &[
("__wasm", "1"),
("__wasm__", "1"),
("__wasm32", "1"),
("__wasm32__", "1"),
],
Arch::PowerPc => &[("__powerpc__", "1"), ("__PPC__", "1")],
Arch::PowerPc64 => &[
("__powerpc__", "1"),
("__powerpc64__", "1"),
("__PPC__", "1"),
("__PPC64__", "1"),
],
Arch::S390x => &[("__s390__", "1"), ("__s390x__", "1")],
Arch::Mips => &[("__mips__", "1"), ("__mips", "32")],
Arch::Mips64 => &[("__mips__", "1"), ("__mips", "64")],
Arch::Sparc => &[("__sparc__", "1"), ("__sparc", "1")],
Arch::Sparc64 => &[
("__sparc__", "1"),
("__sparc", "1"),
("__sparc64__", "1"),
("__arch64__", "1"),
],
Arch::LoongArch64 => &[("__loongarch__", "1"), ("__loongarch64", "1")],
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Os {
Linux,
Darwin,
Windows,
FreeBsd,
NetBsd,
OpenBsd,
Wasi,
None,
}
impl Os {
pub fn as_str(self) -> &'static str {
match self {
Os::Linux => "linux",
Os::Darwin => "darwin",
Os::Windows => "windows",
Os::FreeBsd => "freebsd",
Os::NetBsd => "netbsd",
Os::OpenBsd => "openbsd",
Os::Wasi => "wasi",
Os::None => "none",
}
}
pub fn macros(self) -> &'static [(&'static str, &'static str)] {
match self {
Os::Linux => &[
("__linux__", "1"),
("__linux", "1"),
("__gnu_linux__", "1"),
("__unix__", "1"),
("__unix", "1"),
],
Os::Darwin => &[
("__APPLE__", "1"),
("__MACH__", "1"),
("__unix__", "1"),
("__unix", "1"),
],
Os::Windows => &[("_WIN32", "1")],
Os::FreeBsd => &[("__FreeBSD__", "1"), ("__unix__", "1"), ("__unix", "1")],
Os::NetBsd => &[("__NetBSD__", "1"), ("__unix__", "1"), ("__unix", "1")],
Os::OpenBsd => &[("__OpenBSD__", "1"), ("__unix__", "1"), ("__unix", "1")],
Os::Wasi => &[("__wasi__", "1")],
Os::None => &[],
}
}
fn is_elf(self) -> bool {
matches!(
self,
Os::Linux | Os::FreeBsd | Os::NetBsd | Os::OpenBsd | Os::None
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub enum Env {
Gnu,
Musl,
Bionic,
Uclibc,
Msvc,
#[default]
None,
}
impl Env {
pub fn as_str(self) -> &'static str {
match self {
Env::Gnu => "gnu",
Env::Musl => "musl",
Env::Bionic => "android",
Env::Uclibc => "uclibc",
Env::Msvc => "msvc",
Env::None => "",
}
}
fn from_component(component: &str, os: Os) -> Self {
if component.starts_with("gnu") {
Env::Gnu
} else if component.starts_with("musl") {
Env::Musl
} else if component.starts_with("android") {
Env::Bionic
} else if component.starts_with("uclibc") {
Env::Uclibc
} else if component == "msvc" {
Env::Msvc
} else if os == Os::Linux {
Env::Gnu
} else {
Env::None
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub enum TargetSource {
#[default]
Host,
Env(String),
Pragma(String),
Explicit,
}
impl TargetSource {
pub fn triple(&self) -> Option<&str> {
match self {
TargetSource::Env(t) | TargetSource::Pragma(t) => Some(t),
TargetSource::Host | TargetSource::Explicit => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
TargetSource::Host => "the host",
TargetSource::Env(_) => "CINRS_TARGET",
TargetSource::Pragma(_) => "#pragma cinrs target",
TargetSource::Explicit => "the options given to the front end",
}
}
pub fn describe(&self) -> String {
match self {
TargetSource::Host => "the host, CINRS_TARGET being unset".to_owned(),
TargetSource::Env(t) => format!("CINRS_TARGET={t}"),
TargetSource::Pragma(t) => format!("#pragma cinrs target \"{t}\""),
TargetSource::Explicit => "the options given to the front end".to_owned(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnknownTarget {
pub triple: String,
detail: String,
}
impl UnknownTarget {
pub fn message(&self, source: &TargetSource) -> String {
let (from, fix) = match source {
TargetSource::Env(_) => (
"CINRS_TARGET: ",
"; set #pragma cinrs target or unset the variable",
),
TargetSource::Pragma(_) => ("#pragma cinrs target: ", ""),
TargetSource::Host | TargetSource::Explicit => ("", ""),
};
format!("{from}{}{fix}; {FAMILIES}", self.detail)
}
}
impl fmt::Display for UnknownTarget {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}; {FAMILIES}", self.detail)
}
}
struct ArchRow {
prefix: &'static str,
arch: Arch,
ptr_bits: u32,
big_endian: bool,
}
impl ArchRow {
const fn new(prefix: &'static str, arch: Arch, ptr_bits: u32) -> Self {
Self {
prefix,
arch,
ptr_bits,
big_endian: false,
}
}
const fn big_endian(mut self) -> Self {
self.big_endian = true;
self
}
}
const ARCHITECTURES: &[ArchRow] = &[
ArchRow::new("x86_64", Arch::X86_64, 64),
ArchRow::new("i386", Arch::X86, 32),
ArchRow::new("i486", Arch::X86, 32),
ArchRow::new("i586", Arch::X86, 32),
ArchRow::new("i686", Arch::X86, 32),
ArchRow::new("aarch64_be", Arch::Aarch64, 64).big_endian(),
ArchRow::new("aarch64", Arch::Aarch64, 64),
ArchRow::new("arm64", Arch::Aarch64, 64),
ArchRow::new("armeb", Arch::Arm, 32).big_endian(),
ArchRow::new("arm", Arch::Arm, 32),
ArchRow::new("thumb", Arch::Arm, 32),
ArchRow::new("riscv32", Arch::Riscv32, 32),
ArchRow::new("riscv64", Arch::Riscv64, 64),
ArchRow::new("wasm32", Arch::Wasm32, 32),
ArchRow::new("powerpc64le", Arch::PowerPc64, 64),
ArchRow::new("powerpc64", Arch::PowerPc64, 64).big_endian(),
ArchRow::new("powerpc", Arch::PowerPc, 32).big_endian(),
ArchRow::new("s390x", Arch::S390x, 64).big_endian(),
ArchRow::new("loongarch64", Arch::LoongArch64, 64),
ArchRow::new("mipsisa64r6el", Arch::Mips64, 64),
ArchRow::new("mipsisa64r6", Arch::Mips64, 64).big_endian(),
ArchRow::new("mipsisa32r6el", Arch::Mips, 32),
ArchRow::new("mipsisa32r6", Arch::Mips, 32).big_endian(),
ArchRow::new("mips64el", Arch::Mips64, 64),
ArchRow::new("mips64", Arch::Mips64, 64).big_endian(),
ArchRow::new("mipsel", Arch::Mips, 32),
ArchRow::new("mips", Arch::Mips, 32).big_endian(),
ArchRow::new("sparc64", Arch::Sparc64, 64).big_endian(),
ArchRow::new("sparcv9", Arch::Sparc64, 64).big_endian(),
ArchRow::new("sparc", Arch::Sparc, 32).big_endian(),
];
const EXOTIC: &[(&str, &str)] = &[
("avr", "'int' is 16 bits and 'double' is 32"),
("msp430", "'int' is 16 bits"),
("xtensa", "cinrs has no model for it"),
("hexagon", "cinrs has no model for it"),
("csky", "cinrs has no model for it"),
("m68k", "cinrs has no model for it"),
("nvptx64", "cinrs has no model for it"),
("bpfel", "cinrs has no model for it"),
("bpfeb", "cinrs has no model for it"),
("wasm64", "cinrs has no model for it"),
];
const OPERATING_SYSTEMS: &[(&str, Os)] = &[
("linux", Os::Linux),
("android", Os::Linux),
("androideabi", Os::Linux),
("darwin", Os::Darwin),
("macos", Os::Darwin),
("macosx", Os::Darwin),
("ios", Os::Darwin),
("tvos", Os::Darwin),
("watchos", Os::Darwin),
("visionos", Os::Darwin),
("windows", Os::Windows),
("freebsd", Os::FreeBsd),
("netbsd", Os::NetBsd),
("openbsd", Os::OpenBsd),
("wasi", Os::Wasi),
("wasip1", Os::Wasi),
("wasip2", Os::Wasi),
("none", Os::None),
("elf", Os::None),
];
const FAMILIES: &str = "the architectures cinrs models are x86, x86_64, aarch64, arm/thumb, \
riscv32, riscv64, wasm32, powerpc, powerpc64, s390x, mips, mips64, \
sparc, sparc64 and loongarch64, on linux (android included), darwin, \
windows, freebsd, netbsd, openbsd, wasi or none";
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TargetModel {
pub arch: Arch,
pub os: Os,
pub env: Env,
pub char_signed: bool,
pub short_bits: u32,
pub int_bits: u32,
pub long_bits: u32,
pub long_long_bits: u32,
pub ptr_bits: u32,
pub max_scalar_align: u64,
pub int128_align: u64,
pub has_int128: bool,
pub big_endian: bool,
pub wchar_bits: u32,
pub wchar_signed: bool,
pub wint_bits: u32,
pub wint_signed: bool,
}
impl TargetModel {
pub const LP64: Self = Self {
arch: Arch::X86_64,
os: Os::Linux,
env: Env::Gnu,
char_signed: true,
short_bits: 16,
int_bits: 32,
long_bits: 64,
long_long_bits: 64,
ptr_bits: 64,
max_scalar_align: 8,
int128_align: 16,
has_int128: true,
big_endian: false,
wchar_bits: 32,
wchar_signed: true,
wint_bits: 32,
wint_signed: false,
};
pub const ILP32: Self = Self {
arch: Arch::X86,
long_bits: 32,
ptr_bits: 32,
max_scalar_align: 4,
has_int128: false,
..Self::LP64
};
pub const LLP64: Self = Self {
os: Os::Windows,
env: Env::Msvc,
long_bits: 32,
wchar_bits: 16,
wchar_signed: false,
wint_bits: 16,
..Self::LP64
};
pub const fn host() -> Self {
let char_signed = !(cfg!(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "csky",
target_arch = "hexagon",
target_arch = "msp430",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "s390x",
target_arch = "xtensa",
)) && !cfg!(any(windows, target_vendor = "apple", target_os = "vita")));
let ptr_bits = if cfg!(target_pointer_width = "64") {
64
} else if cfg!(target_pointer_width = "32") {
32
} else {
16
};
Self {
arch: host_arch(),
os: host_os(),
env: host_env(),
char_signed,
short_bits: 16,
int_bits: if cfg!(any(target_arch = "avr", target_arch = "msp430")) {
16
} else {
32
},
long_bits: if ptr_bits == 64 && !cfg!(windows) {
64
} else {
32
},
long_long_bits: 64,
ptr_bits,
max_scalar_align: if cfg!(all(target_arch = "x86", not(windows))) {
4
} else {
8
},
int128_align: core::mem::align_of::<i128>() as u64,
has_int128: ptr_bits == 64,
big_endian: cfg!(target_endian = "big"),
wchar_bits: if cfg!(windows) { 16 } else { 32 },
wchar_signed: !(cfg!(windows)
|| cfg!(all(
any(target_arch = "aarch64", target_arch = "arm"),
not(target_vendor = "apple")
))),
wint_bits: if cfg!(windows) { 16 } else { 32 },
wint_signed: cfg!(target_vendor = "apple"),
}
}
pub fn from_triple(triple: &str) -> Result<Self, UnknownTarget> {
let unknown = |detail: String| UnknownTarget {
triple: triple.to_owned(),
detail,
};
let mut parts = triple.split('-');
let Some(arch_name) = parts.next().filter(|a| !a.is_empty()) else {
return Err(unknown(format!("unknown target triple '{triple}'")));
};
let rest: Vec<&str> = parts.collect();
if let Some((name, why)) = EXOTIC.iter().find(|(name, _)| arch_name == *name) {
return Err(unknown(format!(
"the target triple '{triple}' is not supported: on '{name}' {why}"
)));
}
let Some(row) = ARCHITECTURES
.iter()
.find(|row| arch_name.starts_with(row.prefix))
else {
return Err(unknown(format!(
"unknown architecture '{arch_name}' in the target triple '{triple}'"
)));
};
let os = match rest
.iter()
.find_map(|c| OPERATING_SYSTEMS.iter().find(|(name, _)| c == name))
{
Some((_, os)) => *os,
None if rest.iter().all(|c| *c == "unknown") => Os::None,
None => {
let named = rest.last().copied().unwrap_or("");
return Err(unknown(format!(
"unsupported operating system '{named}' in the target triple '{triple}'"
)));
}
};
let env = rest.last().copied().unwrap_or("");
let ptr_bits = if env.starts_with("gnux32") || env.starts_with("gnu_ilp32") {
32
} else {
row.ptr_bits
};
let arch = row.arch;
let apple = os == Os::Darwin;
let windows = os == Os::Windows;
let char_signed = !(matches!(
arch,
Arch::Aarch64
| Arch::Arm
| Arch::PowerPc
| Arch::PowerPc64
| Arch::Riscv32
| Arch::Riscv64
| Arch::S390x
) && !apple
&& !windows);
let unsigned_wchar = matches!(arch, Arch::Aarch64 | Arch::Arm) && !apple;
Ok(Self {
arch,
os,
env: Env::from_component(env, os),
char_signed,
short_bits: 16,
int_bits: 32,
long_bits: if (ptr_bits == 64 && !windows) || (arch == Arch::Wasm32 && os == Os::Linux)
{
64
} else {
32
},
long_long_bits: 64,
ptr_bits,
max_scalar_align: if arch == Arch::X86 && !windows { 4 } else { 8 },
int128_align: 16,
has_int128: row.ptr_bits == 64,
big_endian: row.big_endian,
wchar_bits: if windows { 16 } else { 32 },
wchar_signed: !windows && !unsigned_wchar,
wint_bits: if windows { 16 } else { 32 },
wint_signed: apple,
})
}
pub fn is_msvc(&self) -> bool {
self.os == Os::Windows && self.env == Env::Msvc
}
pub fn platform_long_double_is_double(&self) -> bool {
match self.arch {
_ if self.is_msvc() => true,
Arch::Arm | Arch::Mips => true,
Arch::Aarch64 => matches!(self.os, Os::Darwin | Os::Windows),
Arch::X86 => self.env == Env::Bionic,
Arch::PowerPc | Arch::PowerPc64 => self.env == Env::Musl,
_ => false,
}
}
pub fn data_model(&self) -> &'static str {
match (self.int_bits, self.long_bits, self.ptr_bits) {
(32, 64, 64) => "LP64",
(32, 32, 64) => "LLP64",
(32, 32, 32) => "ILP32",
_ => "an unusual data model",
}
}
pub fn describe(&self, source: &TargetSource) -> String {
format!(
"{} ({}-{}, {} 'char', {}-bit 'wchar_t'), chosen from {}",
self.data_model(),
self.arch.as_str(),
self.os.as_str(),
if self.char_signed {
"signed"
} else {
"unsigned"
},
self.wchar_bits,
source.describe()
)
}
pub fn macros(&self) -> Vec<(&'static str, String)> {
let mut out: Vec<(&'static str, String)> = Vec::new();
for (name, value) in self.arch.macros() {
out.push((name, (*value).to_owned()));
}
for (name, value) in self.os.macros() {
out.push((name, (*value).to_owned()));
}
if self.os == Os::Windows && self.ptr_bits == 64 {
out.push(("_WIN64", "1".to_owned()));
}
if self.os.is_elf() && self.arch != Arch::Wasm32 {
out.push(("__ELF__", "1".to_owned()));
}
if self.os == Os::Linux {
match self.env {
Env::Gnu => out.push(("__cinrs_glibc__", "1".to_owned())),
Env::Musl => out.push(("__cinrs_musl__", "1".to_owned())),
_ => {}
}
}
out
}
}
const fn host_env() -> Env {
if cfg!(target_env = "gnu") {
Env::Gnu
} else if cfg!(target_env = "musl") {
Env::Musl
} else if cfg!(target_os = "android") {
Env::Bionic
} else if cfg!(target_env = "uclibc") {
Env::Uclibc
} else if cfg!(target_env = "msvc") {
Env::Msvc
} else if cfg!(target_os = "linux") {
Env::Gnu
} else {
Env::None
}
}
const fn host_arch() -> Arch {
if cfg!(target_arch = "x86_64") {
Arch::X86_64
} else if cfg!(target_arch = "x86") {
Arch::X86
} else if cfg!(target_arch = "aarch64") {
Arch::Aarch64
} else if cfg!(target_arch = "arm") {
Arch::Arm
} else if cfg!(target_arch = "riscv32") {
Arch::Riscv32
} else if cfg!(target_arch = "riscv64") {
Arch::Riscv64
} else if cfg!(target_arch = "wasm32") {
Arch::Wasm32
} else if cfg!(target_arch = "powerpc") {
Arch::PowerPc
} else if cfg!(target_arch = "powerpc64") {
Arch::PowerPc64
} else if cfg!(target_arch = "s390x") {
Arch::S390x
} else if cfg!(target_arch = "mips") {
Arch::Mips
} else if cfg!(target_arch = "mips64") {
Arch::Mips64
} else if cfg!(target_arch = "sparc") {
Arch::Sparc
} else if cfg!(target_arch = "sparc64") {
Arch::Sparc64
} else if cfg!(target_arch = "loongarch64") {
Arch::LoongArch64
} else if cfg!(target_pointer_width = "64") {
Arch::X86_64
} else {
Arch::X86
}
}
const fn host_os() -> Os {
if cfg!(any(target_os = "linux", target_os = "android")) {
Os::Linux
} else if cfg!(target_vendor = "apple") {
Os::Darwin
} else if cfg!(windows) {
Os::Windows
} else if cfg!(target_os = "freebsd") {
Os::FreeBsd
} else if cfg!(target_os = "netbsd") {
Os::NetBsd
} else if cfg!(target_os = "openbsd") {
Os::OpenBsd
} else if cfg!(target_os = "wasi") {
Os::Wasi
} else {
Os::None
}
}
impl Default for TargetModel {
fn default() -> Self {
Self::host()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn model(triple: &str) -> TargetModel {
TargetModel::from_triple(triple).unwrap_or_else(|e| panic!("{triple}: {e}"))
}
fn macros(triple: &str) -> Vec<String> {
model(triple)
.macros()
.into_iter()
.map(|(name, value)| format!("{name}={value}"))
.collect()
}
#[test]
fn the_host_model_is_self_consistent() {
let t = TargetModel::host();
assert!(t.short_bits <= t.int_bits);
assert!(t.int_bits <= t.long_bits);
assert!(t.long_bits <= t.long_long_bits);
assert!(t.long_long_bits >= 64);
}
#[test]
fn the_host_model_agrees_with_core_ffi() {
let t = TargetModel::host();
assert_eq!(t.short_bits, 8 * size_of::<core::ffi::c_short>() as u32);
assert_eq!(t.int_bits, 8 * size_of::<core::ffi::c_int>() as u32);
assert_eq!(t.long_bits, 8 * size_of::<core::ffi::c_long>() as u32);
assert_eq!(
t.long_long_bits,
8 * size_of::<core::ffi::c_longlong>() as u32
);
assert_eq!(t.ptr_bits, 8 * size_of::<*const ()>() as u32);
assert_eq!(t.char_signed, core::ffi::c_char::MIN != 0);
assert_eq!(t.int128_align, align_of::<i128>() as u64);
assert_eq!(t.max_scalar_align, align_of::<u64>() as u64);
assert_eq!(t.max_scalar_align, align_of::<f64>() as u64);
}
#[test]
fn a_64_bit_x86_linux_host_is_lp64() {
if cfg!(all(target_os = "linux", target_arch = "x86_64")) {
assert_eq!(TargetModel::host(), TargetModel::LP64);
}
}
#[test]
fn the_table_agrees_with_the_host() {
if cfg!(all(
target_os = "linux",
target_arch = "x86_64",
target_env = "gnu"
)) {
assert_eq!(
model("x86_64-unknown-linux-gnu"),
TargetModel::host(),
"the table and the host disagree"
);
}
}
#[test]
fn the_lp64_family() {
for triple in [
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"x86_64-unknown-freebsd",
"aarch64-unknown-linux-gnu",
"aarch64-apple-darwin",
"x86_64-apple-darwin",
"riscv64gc-unknown-linux-gnu",
"powerpc64le-unknown-linux-gnu",
"s390x-unknown-linux-gnu",
"loongarch64-unknown-linux-gnu",
"x86_64-unknown-none",
] {
let t = model(triple);
assert_eq!(t.data_model(), "LP64", "{triple}");
assert_eq!(t.long_bits, 64, "{triple}");
assert_eq!(t.ptr_bits, 64, "{triple}");
assert!(t.has_int128, "{triple}");
}
}
#[test]
fn the_llp64_family() {
for triple in [
"x86_64-pc-windows-msvc",
"x86_64-pc-windows-gnu",
"x86_64-pc-windows-gnullvm",
"aarch64-pc-windows-msvc",
"x86_64-uwp-windows-msvc",
] {
let t = model(triple);
assert_eq!(t.data_model(), "LLP64", "{triple}");
assert_eq!(t.long_bits, 32, "{triple}");
assert_eq!(t.ptr_bits, 64, "{triple}");
assert_eq!(t.wchar_bits, 16, "{triple}");
assert!(!t.wchar_signed, "{triple}");
assert_eq!(t.wint_bits, 16, "{triple}");
assert!(t.char_signed, "{triple}");
assert_eq!(t.max_scalar_align, 8, "{triple}");
}
}
#[test]
fn the_msvc_environment() {
for triple in [
"x86_64-pc-windows-msvc",
"i686-pc-windows-msvc",
"aarch64-pc-windows-msvc",
"x86_64-uwp-windows-msvc",
"thumbv7a-pc-windows-msvc",
] {
assert!(model(triple).is_msvc(), "{triple} is MSVC");
}
for triple in [
"x86_64-pc-windows-gnu",
"i686-pc-windows-gnu",
"x86_64-pc-windows-gnullvm",
"aarch64-pc-windows-gnullvm",
"x86_64-unknown-linux-gnu",
"aarch64-apple-darwin",
"x86_64-apple-darwin",
"x86_64-unknown-freebsd",
"wasm32-unknown-unknown",
"thumbv7em-none-eabihf",
] {
assert!(!model(triple).is_msvc(), "{triple} is not MSVC");
}
assert!(TargetModel::LLP64.is_msvc());
assert!(!TargetModel::LP64.is_msvc());
assert!(!TargetModel::ILP32.is_msvc());
assert_eq!(
TargetModel::host().is_msvc(),
cfg!(all(windows, target_env = "msvc"))
);
}
#[test]
fn the_ilp32_family() {
for triple in [
"i686-unknown-linux-gnu",
"i586-unknown-linux-gnu",
"i686-pc-windows-msvc",
"armv7-unknown-linux-gnueabihf",
"thumbv7em-none-eabihf",
"riscv32imac-unknown-none-elf",
"wasm32-unknown-unknown",
"wasm32-wasip1",
"mips-unknown-linux-gnu",
"powerpc-unknown-linux-gnu",
"sparc-unknown-linux-gnu",
"x86_64-unknown-linux-gnux32",
"aarch64-unknown-linux-gnu_ilp32",
] {
let t = model(triple);
assert_eq!(t.data_model(), "ILP32", "{triple}");
assert_eq!(t.long_bits, 32, "{triple}");
assert_eq!(t.ptr_bits, 32, "{triple}");
}
}
#[test]
fn int128_follows_the_architecture() {
assert!(model("x86_64-unknown-linux-gnux32").has_int128);
assert!(model("aarch64-unknown-linux-gnu").has_int128);
assert!(!model("i686-unknown-linux-gnu").has_int128);
assert!(!model("wasm32-unknown-unknown").has_int128);
assert!(!model("armv7-unknown-linux-gnueabihf").has_int128);
}
#[test]
fn plain_char_signedness_follows_core_ffi() {
for triple in [
"x86_64-unknown-linux-gnu",
"i686-unknown-linux-gnu",
"loongarch64-unknown-linux-gnu",
"wasm32-unknown-unknown",
"mips-unknown-linux-gnu",
"sparc64-unknown-netbsd",
"aarch64-pc-windows-msvc",
"aarch64-apple-darwin",
"armv7-apple-ios",
] {
assert!(model(triple).char_signed, "{triple} should be signed");
}
for triple in [
"aarch64-unknown-linux-gnu",
"armv7-unknown-linux-gnueabihf",
"thumbv7em-none-eabihf",
"riscv64gc-unknown-linux-gnu",
"riscv32imac-unknown-none-elf",
"powerpc64le-unknown-linux-gnu",
"powerpc-unknown-linux-gnu",
"s390x-unknown-linux-gnu",
] {
assert!(!model(triple).char_signed, "{triple} should be unsigned");
}
}
#[test]
fn scalar_alignment_splits_i386_from_i386_on_windows() {
assert_eq!(model("i686-unknown-linux-gnu").max_scalar_align, 4);
assert_eq!(model("i586-unknown-netbsd").max_scalar_align, 4);
assert_eq!(model("i686-pc-windows-msvc").max_scalar_align, 8);
assert_eq!(model("i686-pc-windows-gnu").max_scalar_align, 8);
assert_eq!(model("armv7-unknown-linux-gnueabihf").max_scalar_align, 8);
assert_eq!(model("wasm32-unknown-unknown").max_scalar_align, 8);
}
#[test]
fn endianness() {
for triple in [
"s390x-unknown-linux-gnu",
"powerpc64-unknown-linux-gnu",
"powerpc-unknown-linux-gnu",
"sparc64-unknown-linux-gnu",
"mips-unknown-linux-gnu",
"aarch64_be-unknown-linux-gnu",
] {
assert!(model(triple).big_endian, "{triple} is big-endian");
}
for triple in [
"x86_64-unknown-linux-gnu",
"powerpc64le-unknown-linux-gnu",
"mipsel-unknown-linux-gnu",
"mips64el-unknown-linux-gnuabi64",
"aarch64-unknown-linux-gnu",
] {
assert!(!model(triple).big_endian, "{triple} is little-endian");
}
}
#[test]
fn wchar_and_wint() {
let arm = model("aarch64-unknown-linux-gnu");
assert_eq!((arm.wchar_bits, arm.wchar_signed), (32, false));
assert_eq!((arm.wint_bits, arm.wint_signed), (32, false));
let mac = model("aarch64-apple-darwin");
assert_eq!((mac.wchar_bits, mac.wchar_signed), (32, true));
assert_eq!((mac.wint_bits, mac.wint_signed), (32, true));
let win = model("x86_64-pc-windows-msvc");
assert_eq!((win.wchar_bits, win.wchar_signed), (16, false));
assert_eq!((win.wint_bits, win.wint_signed), (16, false));
let linux = model("x86_64-unknown-linux-gnu");
assert_eq!((linux.wchar_bits, linux.wchar_signed), (32, true));
assert_eq!((linux.wint_bits, linux.wint_signed), (32, false));
}
#[test]
fn the_identity_macros() {
let linux = macros("x86_64-unknown-linux-gnu");
for want in [
"__x86_64__=1",
"__amd64__=1",
"__linux__=1",
"__gnu_linux__=1",
"__unix__=1",
"__ELF__=1",
] {
assert!(linux.contains(&want.to_owned()), "{want} in {linux:?}");
}
assert!(!linux.iter().any(|m| m.starts_with("_WIN")));
let win = macros("x86_64-pc-windows-msvc");
assert!(win.contains(&"_WIN32=1".to_owned()));
assert!(win.contains(&"_WIN64=1".to_owned()));
assert!(!win.iter().any(|m| m.starts_with("__ELF__")));
assert!(!win.iter().any(|m| m.starts_with("__unix")));
let win32 = macros("i686-pc-windows-msvc");
assert!(win32.contains(&"_WIN32=1".to_owned()));
assert!(!win32.contains(&"_WIN64=1".to_owned()));
assert!(win32.contains(&"__i386__=1".to_owned()));
let mac = macros("aarch64-apple-darwin");
assert!(mac.contains(&"__APPLE__=1".to_owned()));
assert!(mac.contains(&"__MACH__=1".to_owned()));
assert!(mac.contains(&"__aarch64__=1".to_owned()));
assert!(!mac.iter().any(|m| m.starts_with("__ELF__")));
let wasm = macros("wasm32-unknown-unknown");
assert!(wasm.contains(&"__wasm32__=1".to_owned()));
assert!(!wasm.iter().any(|m| m.starts_with("__ELF__")));
let riscv = macros("riscv64gc-unknown-linux-gnu");
assert!(riscv.contains(&"__riscv=1".to_owned()));
assert!(riscv.contains(&"__riscv_xlen=64".to_owned()));
let bare = macros("thumbv7em-none-eabihf");
assert!(bare.contains(&"__arm__=1".to_owned()));
assert!(bare.contains(&"__ELF__=1".to_owned()));
assert!(!bare.iter().any(|m| m.starts_with("__linux")));
}
#[test]
fn the_c_library_of_a_linux_triple() {
for (triple, want) in [
("x86_64-unknown-linux-gnu", Env::Gnu),
("armv7-unknown-linux-gnueabihf", Env::Gnu),
("x86_64-unknown-linux-gnux32", Env::Gnu),
("x86_64-unknown-linux", Env::Gnu),
("x86_64-unknown-linux-musl", Env::Musl),
("aarch64-unknown-linux-musl", Env::Musl),
("aarch64-linux-android", Env::Bionic),
("armv7-unknown-linux-uclibceabi", Env::Uclibc),
("x86_64-pc-windows-msvc", Env::Msvc),
("x86_64-pc-windows-gnu", Env::Gnu),
("aarch64-apple-darwin", Env::None),
("x86_64-unknown-freebsd", Env::None),
("wasm32-unknown-unknown", Env::None),
] {
assert_eq!(model(triple).env, want, "{triple}");
}
let glibc = "__cinrs_glibc__=1".to_owned();
let musl = "__cinrs_musl__=1".to_owned();
assert!(macros("x86_64-unknown-linux-gnu").contains(&glibc));
assert!(macros("i686-unknown-linux-gnu").contains(&glibc));
assert!(macros("x86_64-unknown-linux-musl").contains(&musl));
for triple in [
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"aarch64-linux-android",
"x86_64-pc-windows-gnu",
"aarch64-apple-darwin",
"x86_64-unknown-freebsd",
"wasm32-unknown-unknown",
] {
let macros = macros(triple);
let named = usize::from(macros.contains(&glibc)) + usize::from(macros.contains(&musl));
assert!(named <= 1, "{triple} claims two C libraries");
}
for triple in [
"aarch64-linux-android",
"x86_64-pc-windows-gnu",
"aarch64-apple-darwin",
] {
let macros = macros(triple);
assert!(!macros.contains(&glibc), "{triple}");
assert!(!macros.contains(&musl), "{triple}");
}
}
#[test]
fn an_unknown_architecture_is_refused() {
let err = TargetModel::from_triple("gizmo-unknown-linux-gnu").unwrap_err();
let message = err.message(&TargetSource::Env("gizmo-unknown-linux-gnu".to_owned()));
assert!(
message.starts_with(
"CINRS_TARGET: unknown architecture 'gizmo' in the target triple \
'gizmo-unknown-linux-gnu'; set #pragma cinrs target or unset the variable; "
),
"{message}"
);
assert!(message.contains("the architectures cinrs models are x86, x86_64"));
}
#[test]
fn an_unknown_operating_system_is_refused() {
let err = TargetModel::from_triple("x86_64-unknown-plan9").unwrap_err();
let message = err.message(&TargetSource::Pragma("x86_64-unknown-plan9".to_owned()));
assert!(
message.starts_with(
"#pragma cinrs target: unsupported operating system 'plan9' in the target \
triple 'x86_64-unknown-plan9'; "
),
"{message}"
);
}
#[test]
fn the_exotic_data_models_are_refused_by_name() {
let err = TargetModel::from_triple("avr-none-unknown").unwrap_err();
let message = err.message(&TargetSource::Env("avr-none-unknown".to_owned()));
assert!(
message.starts_with(
"CINRS_TARGET: the target triple 'avr-none-unknown' is not supported: on \
'avr' 'int' is 16 bits and 'double' is 32; set #pragma cinrs target or \
unset the variable; "
),
"{message}"
);
assert!(TargetModel::from_triple("msp430-none-elf").is_err());
}
#[test]
fn an_empty_triple_is_refused() {
assert!(TargetModel::from_triple("").is_err());
assert!(TargetModel::from_triple("-linux-gnu").is_err());
}
#[test]
fn the_description_names_the_source() {
let t = model("x86_64-pc-windows-msvc");
let said = t.describe(&TargetSource::Env("x86_64-pc-windows-msvc".to_owned()));
assert_eq!(
said,
"LLP64 (x86_64-windows, signed 'char', 16-bit 'wchar_t'), chosen from \
CINRS_TARGET=x86_64-pc-windows-msvc"
);
let host = TargetModel::host().describe(&TargetSource::Host);
assert!(host.contains("CINRS_TARGET being unset"), "{host}");
}
}