1use std::fmt;
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
62pub enum Arch {
63 X86,
65 X86_64,
67 Aarch64,
69 Arm,
71 Riscv32,
73 Riscv64,
75 Wasm32,
77 PowerPc,
79 PowerPc64,
81 S390x,
83 Mips,
85 Mips64,
87 Sparc,
89 Sparc64,
91 LoongArch64,
93}
94
95impl Arch {
96 pub fn as_str(self) -> &'static str {
98 match self {
99 Arch::X86 => "x86",
100 Arch::X86_64 => "x86_64",
101 Arch::Aarch64 => "aarch64",
102 Arch::Arm => "arm",
103 Arch::Riscv32 => "riscv32",
104 Arch::Riscv64 => "riscv64",
105 Arch::Wasm32 => "wasm32",
106 Arch::PowerPc => "powerpc",
107 Arch::PowerPc64 => "powerpc64",
108 Arch::S390x => "s390x",
109 Arch::Mips => "mips",
110 Arch::Mips64 => "mips64",
111 Arch::Sparc => "sparc",
112 Arch::Sparc64 => "sparc64",
113 Arch::LoongArch64 => "loongarch64",
114 }
115 }
116
117 pub fn macros(self) -> &'static [(&'static str, &'static str)] {
125 match self {
126 Arch::X86 => &[("__i386__", "1"), ("__i386", "1")],
127 Arch::X86_64 => &[
128 ("__x86_64__", "1"),
129 ("__x86_64", "1"),
130 ("__amd64__", "1"),
131 ("__amd64", "1"),
132 ],
133 Arch::Aarch64 => &[("__aarch64__", "1")],
134 Arch::Arm => &[("__arm__", "1")],
135 Arch::Riscv32 => &[("__riscv", "1"), ("__riscv_xlen", "32")],
136 Arch::Riscv64 => &[("__riscv", "1"), ("__riscv_xlen", "64")],
137 Arch::Wasm32 => &[
138 ("__wasm", "1"),
139 ("__wasm__", "1"),
140 ("__wasm32", "1"),
141 ("__wasm32__", "1"),
142 ],
143 Arch::PowerPc => &[("__powerpc__", "1"), ("__PPC__", "1")],
144 Arch::PowerPc64 => &[
145 ("__powerpc__", "1"),
146 ("__powerpc64__", "1"),
147 ("__PPC__", "1"),
148 ("__PPC64__", "1"),
149 ],
150 Arch::S390x => &[("__s390__", "1"), ("__s390x__", "1")],
151 Arch::Mips => &[("__mips__", "1"), ("__mips", "32")],
152 Arch::Mips64 => &[("__mips__", "1"), ("__mips", "64")],
153 Arch::Sparc => &[("__sparc__", "1"), ("__sparc", "1")],
154 Arch::Sparc64 => &[
155 ("__sparc__", "1"),
156 ("__sparc", "1"),
157 ("__sparc64__", "1"),
158 ("__arch64__", "1"),
159 ],
160 Arch::LoongArch64 => &[("__loongarch__", "1"), ("__loongarch64", "1")],
161 }
162 }
163}
164
165#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
172pub enum Os {
173 Linux,
175 Darwin,
177 Windows,
179 FreeBsd,
181 NetBsd,
183 OpenBsd,
185 Wasi,
187 None,
190}
191
192impl Os {
193 pub fn as_str(self) -> &'static str {
195 match self {
196 Os::Linux => "linux",
197 Os::Darwin => "darwin",
198 Os::Windows => "windows",
199 Os::FreeBsd => "freebsd",
200 Os::NetBsd => "netbsd",
201 Os::OpenBsd => "openbsd",
202 Os::Wasi => "wasi",
203 Os::None => "none",
204 }
205 }
206
207 pub fn macros(self) -> &'static [(&'static str, &'static str)] {
212 match self {
213 Os::Linux => &[
214 ("__linux__", "1"),
215 ("__linux", "1"),
216 ("__gnu_linux__", "1"),
217 ("__unix__", "1"),
218 ("__unix", "1"),
219 ],
220 Os::Darwin => &[
221 ("__APPLE__", "1"),
222 ("__MACH__", "1"),
223 ("__unix__", "1"),
224 ("__unix", "1"),
225 ],
226 Os::Windows => &[("_WIN32", "1")],
227 Os::FreeBsd => &[("__FreeBSD__", "1"), ("__unix__", "1"), ("__unix", "1")],
228 Os::NetBsd => &[("__NetBSD__", "1"), ("__unix__", "1"), ("__unix", "1")],
229 Os::OpenBsd => &[("__OpenBSD__", "1"), ("__unix__", "1"), ("__unix", "1")],
230 Os::Wasi => &[("__wasi__", "1")],
231 Os::None => &[],
232 }
233 }
234
235 fn is_elf(self) -> bool {
237 matches!(
238 self,
239 Os::Linux | Os::FreeBsd | Os::NetBsd | Os::OpenBsd | Os::None
240 )
241 }
242}
243
244#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
254pub enum Env {
255 Gnu,
257 Musl,
259 Bionic,
261 Uclibc,
263 Msvc,
265 #[default]
268 None,
269}
270
271impl Env {
272 pub fn as_str(self) -> &'static str {
274 match self {
275 Env::Gnu => "gnu",
276 Env::Musl => "musl",
277 Env::Bionic => "android",
278 Env::Uclibc => "uclibc",
279 Env::Msvc => "msvc",
280 Env::None => "",
281 }
282 }
283
284 fn from_component(component: &str, os: Os) -> Self {
289 if component.starts_with("gnu") {
290 Env::Gnu
291 } else if component.starts_with("musl") {
292 Env::Musl
293 } else if component.starts_with("android") {
294 Env::Bionic
295 } else if component.starts_with("uclibc") {
296 Env::Uclibc
297 } else if component == "msvc" {
298 Env::Msvc
299 } else if os == Os::Linux {
300 Env::Gnu
301 } else {
302 Env::None
303 }
304 }
305}
306
307#[derive(Clone, Debug, PartialEq, Eq, Default)]
309pub enum TargetSource {
310 #[default]
313 Host,
314 Env(String),
316 Pragma(String),
318 Explicit,
321}
322
323impl TargetSource {
324 pub fn triple(&self) -> Option<&str> {
326 match self {
327 TargetSource::Env(t) | TargetSource::Pragma(t) => Some(t),
328 TargetSource::Host | TargetSource::Explicit => None,
329 }
330 }
331
332 pub fn as_str(&self) -> &'static str {
334 match self {
335 TargetSource::Host => "the host",
336 TargetSource::Env(_) => "CINRS_TARGET",
337 TargetSource::Pragma(_) => "#pragma cinrs target",
338 TargetSource::Explicit => "the options given to the front end",
339 }
340 }
341
342 pub fn describe(&self) -> String {
346 match self {
347 TargetSource::Host => "the host, CINRS_TARGET being unset".to_owned(),
348 TargetSource::Env(t) => format!("CINRS_TARGET={t}"),
349 TargetSource::Pragma(t) => format!("#pragma cinrs target \"{t}\""),
350 TargetSource::Explicit => "the options given to the front end".to_owned(),
351 }
352 }
353}
354
355#[derive(Clone, Debug, PartialEq, Eq)]
357pub struct UnknownTarget {
358 pub triple: String,
360 detail: String,
362}
363
364impl UnknownTarget {
365 pub fn message(&self, source: &TargetSource) -> String {
371 let (from, fix) = match source {
372 TargetSource::Env(_) => (
373 "CINRS_TARGET: ",
374 "; set #pragma cinrs target or unset the variable",
375 ),
376 TargetSource::Pragma(_) => ("#pragma cinrs target: ", ""),
377 TargetSource::Host | TargetSource::Explicit => ("", ""),
378 };
379 format!("{from}{}{fix}; {FAMILIES}", self.detail)
380 }
381}
382
383impl fmt::Display for UnknownTarget {
384 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385 write!(f, "{}; {FAMILIES}", self.detail)
386 }
387}
388
389struct ArchRow {
391 prefix: &'static str,
393 arch: Arch,
394 ptr_bits: u32,
397 big_endian: bool,
398}
399
400impl ArchRow {
401 const fn new(prefix: &'static str, arch: Arch, ptr_bits: u32) -> Self {
402 Self {
403 prefix,
404 arch,
405 ptr_bits,
406 big_endian: false,
407 }
408 }
409
410 const fn big_endian(mut self) -> Self {
411 self.big_endian = true;
412 self
413 }
414}
415
416const ARCHITECTURES: &[ArchRow] = &[
422 ArchRow::new("x86_64", Arch::X86_64, 64),
423 ArchRow::new("i386", Arch::X86, 32),
424 ArchRow::new("i486", Arch::X86, 32),
425 ArchRow::new("i586", Arch::X86, 32),
426 ArchRow::new("i686", Arch::X86, 32),
427 ArchRow::new("aarch64_be", Arch::Aarch64, 64).big_endian(),
428 ArchRow::new("aarch64", Arch::Aarch64, 64),
429 ArchRow::new("arm64", Arch::Aarch64, 64),
430 ArchRow::new("armeb", Arch::Arm, 32).big_endian(),
431 ArchRow::new("arm", Arch::Arm, 32),
432 ArchRow::new("thumb", Arch::Arm, 32),
433 ArchRow::new("riscv32", Arch::Riscv32, 32),
434 ArchRow::new("riscv64", Arch::Riscv64, 64),
435 ArchRow::new("wasm32", Arch::Wasm32, 32),
436 ArchRow::new("powerpc64le", Arch::PowerPc64, 64),
437 ArchRow::new("powerpc64", Arch::PowerPc64, 64).big_endian(),
438 ArchRow::new("powerpc", Arch::PowerPc, 32).big_endian(),
439 ArchRow::new("s390x", Arch::S390x, 64).big_endian(),
440 ArchRow::new("loongarch64", Arch::LoongArch64, 64),
441 ArchRow::new("mipsisa64r6el", Arch::Mips64, 64),
442 ArchRow::new("mipsisa64r6", Arch::Mips64, 64).big_endian(),
443 ArchRow::new("mipsisa32r6el", Arch::Mips, 32),
444 ArchRow::new("mipsisa32r6", Arch::Mips, 32).big_endian(),
445 ArchRow::new("mips64el", Arch::Mips64, 64),
446 ArchRow::new("mips64", Arch::Mips64, 64).big_endian(),
447 ArchRow::new("mipsel", Arch::Mips, 32),
448 ArchRow::new("mips", Arch::Mips, 32).big_endian(),
449 ArchRow::new("sparc64", Arch::Sparc64, 64).big_endian(),
450 ArchRow::new("sparcv9", Arch::Sparc64, 64).big_endian(),
451 ArchRow::new("sparc", Arch::Sparc, 32).big_endian(),
452];
453
454const EXOTIC: &[(&str, &str)] = &[
457 ("avr", "'int' is 16 bits and 'double' is 32"),
458 ("msp430", "'int' is 16 bits"),
459 ("xtensa", "cinrs has no model for it"),
460 ("hexagon", "cinrs has no model for it"),
461 ("csky", "cinrs has no model for it"),
462 ("m68k", "cinrs has no model for it"),
463 ("nvptx64", "cinrs has no model for it"),
464 ("bpfel", "cinrs has no model for it"),
465 ("bpfeb", "cinrs has no model for it"),
466 ("wasm64", "cinrs has no model for it"),
467];
468
469const OPERATING_SYSTEMS: &[(&str, Os)] = &[
472 ("linux", Os::Linux),
473 ("android", Os::Linux),
474 ("androideabi", Os::Linux),
475 ("darwin", Os::Darwin),
476 ("macos", Os::Darwin),
477 ("macosx", Os::Darwin),
478 ("ios", Os::Darwin),
479 ("tvos", Os::Darwin),
480 ("watchos", Os::Darwin),
481 ("visionos", Os::Darwin),
482 ("windows", Os::Windows),
483 ("freebsd", Os::FreeBsd),
484 ("netbsd", Os::NetBsd),
485 ("openbsd", Os::OpenBsd),
486 ("wasi", Os::Wasi),
487 ("wasip1", Os::Wasi),
488 ("wasip2", Os::Wasi),
489 ("none", Os::None),
490 ("elf", Os::None),
491];
492
493const FAMILIES: &str = "the architectures cinrs models are x86, x86_64, aarch64, arm/thumb, \
495 riscv32, riscv64, wasm32, powerpc, powerpc64, s390x, mips, mips64, \
496 sparc, sparc64 and loongarch64, on linux (android included), darwin, \
497 windows, freebsd, netbsd, openbsd, wasi or none";
498
499#[derive(Clone, Copy, Debug, PartialEq, Eq)]
507pub struct TargetModel {
508 pub arch: Arch,
510 pub os: Os,
513 pub env: Env,
516 pub char_signed: bool,
524 pub short_bits: u32,
526 pub int_bits: u32,
528 pub long_bits: u32,
531 pub long_long_bits: u32,
533 pub ptr_bits: u32,
535 pub max_scalar_align: u64,
546 pub int128_align: u64,
553 pub has_int128: bool,
558 pub big_endian: bool,
564 pub wchar_bits: u32,
566 pub wchar_signed: bool,
569 pub wint_bits: u32,
571 pub wint_signed: bool,
574}
575
576impl TargetModel {
577 pub const LP64: Self = Self {
580 arch: Arch::X86_64,
581 os: Os::Linux,
582 env: Env::Gnu,
583 char_signed: true,
584 short_bits: 16,
585 int_bits: 32,
586 long_bits: 64,
587 long_long_bits: 64,
588 ptr_bits: 64,
589 max_scalar_align: 8,
590 int128_align: 16,
591 has_int128: true,
592 big_endian: false,
593 wchar_bits: 32,
594 wchar_signed: true,
595 wint_bits: 32,
596 wint_signed: false,
597 };
598
599 pub const ILP32: Self = Self {
603 arch: Arch::X86,
604 long_bits: 32,
605 ptr_bits: 32,
606 max_scalar_align: 4,
607 has_int128: false,
608 ..Self::LP64
609 };
610
611 pub const LLP64: Self = Self {
614 os: Os::Windows,
615 env: Env::Msvc,
616 long_bits: 32,
617 wchar_bits: 16,
618 wchar_signed: false,
619 wint_bits: 16,
620 ..Self::LP64
621 };
622
623 pub const fn host() -> Self {
628 let char_signed = !(cfg!(any(
634 target_arch = "aarch64",
635 target_arch = "arm",
636 target_arch = "csky",
637 target_arch = "hexagon",
638 target_arch = "msp430",
639 target_arch = "powerpc",
640 target_arch = "powerpc64",
641 target_arch = "riscv32",
642 target_arch = "riscv64",
643 target_arch = "s390x",
644 target_arch = "xtensa",
645 )) && !cfg!(any(windows, target_vendor = "apple", target_os = "vita")));
646 let ptr_bits = if cfg!(target_pointer_width = "64") {
647 64
648 } else if cfg!(target_pointer_width = "32") {
649 32
650 } else {
651 16
652 };
653 Self {
654 arch: host_arch(),
655 os: host_os(),
656 env: host_env(),
657 char_signed,
658 short_bits: 16,
659 int_bits: if cfg!(any(target_arch = "avr", target_arch = "msp430")) {
660 16
661 } else {
662 32
663 },
664 long_bits: if ptr_bits == 64 && !cfg!(windows) {
667 64
668 } else {
669 32
670 },
671 long_long_bits: 64,
672 ptr_bits,
673 max_scalar_align: if cfg!(all(target_arch = "x86", not(windows))) {
674 4
675 } else {
676 8
677 },
678 int128_align: core::mem::align_of::<i128>() as u64,
682 has_int128: ptr_bits == 64,
683 big_endian: cfg!(target_endian = "big"),
684 wchar_bits: if cfg!(windows) { 16 } else { 32 },
685 wchar_signed: !(cfg!(windows)
686 || cfg!(all(
687 any(target_arch = "aarch64", target_arch = "arm"),
688 not(target_vendor = "apple")
689 ))),
690 wint_bits: if cfg!(windows) { 16 } else { 32 },
691 wint_signed: cfg!(target_vendor = "apple"),
692 }
693 }
694
695 pub fn from_triple(triple: &str) -> Result<Self, UnknownTarget> {
710 let unknown = |detail: String| UnknownTarget {
711 triple: triple.to_owned(),
712 detail,
713 };
714 let mut parts = triple.split('-');
715 let Some(arch_name) = parts.next().filter(|a| !a.is_empty()) else {
716 return Err(unknown(format!("unknown target triple '{triple}'")));
717 };
718 let rest: Vec<&str> = parts.collect();
719 if let Some((name, why)) = EXOTIC.iter().find(|(name, _)| arch_name == *name) {
720 return Err(unknown(format!(
721 "the target triple '{triple}' is not supported: on '{name}' {why}"
722 )));
723 }
724 let Some(row) = ARCHITECTURES
725 .iter()
726 .find(|row| arch_name.starts_with(row.prefix))
727 else {
728 return Err(unknown(format!(
729 "unknown architecture '{arch_name}' in the target triple '{triple}'"
730 )));
731 };
732 let os = match rest
737 .iter()
738 .find_map(|c| OPERATING_SYSTEMS.iter().find(|(name, _)| c == name))
739 {
740 Some((_, os)) => *os,
741 None if rest.iter().all(|c| *c == "unknown") => Os::None,
742 None => {
743 let named = rest.last().copied().unwrap_or("");
744 return Err(unknown(format!(
745 "unsupported operating system '{named}' in the target triple '{triple}'"
746 )));
747 }
748 };
749 let env = rest.last().copied().unwrap_or("");
750 let ptr_bits = if env.starts_with("gnux32") || env.starts_with("gnu_ilp32") {
752 32
753 } else {
754 row.ptr_bits
755 };
756 let arch = row.arch;
757 let apple = os == Os::Darwin;
758 let windows = os == Os::Windows;
759 let char_signed = !(matches!(
761 arch,
762 Arch::Aarch64
763 | Arch::Arm
764 | Arch::PowerPc
765 | Arch::PowerPc64
766 | Arch::Riscv32
767 | Arch::Riscv64
768 | Arch::S390x
769 ) && !apple
770 && !windows);
771 let unsigned_wchar = matches!(arch, Arch::Aarch64 | Arch::Arm) && !apple;
772 Ok(Self {
773 arch,
774 os,
775 env: Env::from_component(env, os),
776 char_signed,
777 short_bits: 16,
778 int_bits: 32,
779 long_bits: if (ptr_bits == 64 && !windows) || (arch == Arch::Wasm32 && os == Os::Linux)
783 {
784 64
785 } else {
786 32
787 },
788 long_long_bits: 64,
789 ptr_bits,
790 max_scalar_align: if arch == Arch::X86 && !windows { 4 } else { 8 },
791 int128_align: 16,
792 has_int128: row.ptr_bits == 64,
795 big_endian: row.big_endian,
796 wchar_bits: if windows { 16 } else { 32 },
797 wchar_signed: !windows && !unsigned_wchar,
798 wint_bits: if windows { 16 } else { 32 },
799 wint_signed: apple,
800 })
801 }
802
803 pub fn is_msvc(&self) -> bool {
819 self.os == Os::Windows && self.env == Env::Msvc
820 }
821
822 pub fn data_model(&self) -> &'static str {
824 match (self.int_bits, self.long_bits, self.ptr_bits) {
825 (32, 64, 64) => "LP64",
826 (32, 32, 64) => "LLP64",
827 (32, 32, 32) => "ILP32",
828 _ => "an unusual data model",
829 }
830 }
831
832 pub fn describe(&self, source: &TargetSource) -> String {
835 format!(
836 "{} ({}-{}, {} 'char', {}-bit 'wchar_t'), chosen from {}",
837 self.data_model(),
838 self.arch.as_str(),
839 self.os.as_str(),
840 if self.char_signed {
841 "signed"
842 } else {
843 "unsigned"
844 },
845 self.wchar_bits,
846 source.describe()
847 )
848 }
849
850 pub fn macros(&self) -> Vec<(&'static str, String)> {
858 let mut out: Vec<(&'static str, String)> = Vec::new();
859 for (name, value) in self.arch.macros() {
860 out.push((name, (*value).to_owned()));
861 }
862 for (name, value) in self.os.macros() {
863 out.push((name, (*value).to_owned()));
864 }
865 if self.os == Os::Windows && self.ptr_bits == 64 {
866 out.push(("_WIN64", "1".to_owned()));
867 }
868 if self.os.is_elf() && self.arch != Arch::Wasm32 {
870 out.push(("__ELF__", "1".to_owned()));
871 }
872 if self.os == Os::Linux {
881 match self.env {
882 Env::Gnu => out.push(("__cinrs_glibc__", "1".to_owned())),
883 Env::Musl => out.push(("__cinrs_musl__", "1".to_owned())),
884 _ => {}
885 }
886 }
887 out
888 }
889}
890
891const fn host_env() -> Env {
893 if cfg!(target_env = "gnu") {
894 Env::Gnu
895 } else if cfg!(target_env = "musl") {
896 Env::Musl
897 } else if cfg!(target_os = "android") {
898 Env::Bionic
899 } else if cfg!(target_env = "uclibc") {
900 Env::Uclibc
901 } else if cfg!(target_env = "msvc") {
902 Env::Msvc
903 } else if cfg!(target_os = "linux") {
904 Env::Gnu
905 } else {
906 Env::None
907 }
908}
909
910const fn host_arch() -> Arch {
917 if cfg!(target_arch = "x86_64") {
918 Arch::X86_64
919 } else if cfg!(target_arch = "x86") {
920 Arch::X86
921 } else if cfg!(target_arch = "aarch64") {
922 Arch::Aarch64
923 } else if cfg!(target_arch = "arm") {
924 Arch::Arm
925 } else if cfg!(target_arch = "riscv32") {
926 Arch::Riscv32
927 } else if cfg!(target_arch = "riscv64") {
928 Arch::Riscv64
929 } else if cfg!(target_arch = "wasm32") {
930 Arch::Wasm32
931 } else if cfg!(target_arch = "powerpc") {
932 Arch::PowerPc
933 } else if cfg!(target_arch = "powerpc64") {
934 Arch::PowerPc64
935 } else if cfg!(target_arch = "s390x") {
936 Arch::S390x
937 } else if cfg!(target_arch = "mips") {
938 Arch::Mips
939 } else if cfg!(target_arch = "mips64") {
940 Arch::Mips64
941 } else if cfg!(target_arch = "sparc") {
942 Arch::Sparc
943 } else if cfg!(target_arch = "sparc64") {
944 Arch::Sparc64
945 } else if cfg!(target_arch = "loongarch64") {
946 Arch::LoongArch64
947 } else if cfg!(target_pointer_width = "64") {
948 Arch::X86_64
949 } else {
950 Arch::X86
951 }
952}
953
954const fn host_os() -> Os {
956 if cfg!(any(target_os = "linux", target_os = "android")) {
957 Os::Linux
958 } else if cfg!(target_vendor = "apple") {
959 Os::Darwin
960 } else if cfg!(windows) {
961 Os::Windows
962 } else if cfg!(target_os = "freebsd") {
963 Os::FreeBsd
964 } else if cfg!(target_os = "netbsd") {
965 Os::NetBsd
966 } else if cfg!(target_os = "openbsd") {
967 Os::OpenBsd
968 } else if cfg!(target_os = "wasi") {
969 Os::Wasi
970 } else {
971 Os::None
972 }
973}
974
975impl Default for TargetModel {
976 fn default() -> Self {
977 Self::host()
978 }
979}
980
981#[cfg(test)]
982mod tests {
983 use super::*;
984
985 fn model(triple: &str) -> TargetModel {
986 TargetModel::from_triple(triple).unwrap_or_else(|e| panic!("{triple}: {e}"))
987 }
988
989 fn macros(triple: &str) -> Vec<String> {
990 model(triple)
991 .macros()
992 .into_iter()
993 .map(|(name, value)| format!("{name}={value}"))
994 .collect()
995 }
996
997 #[test]
998 fn the_host_model_is_self_consistent() {
999 let t = TargetModel::host();
1000 assert!(t.short_bits <= t.int_bits);
1001 assert!(t.int_bits <= t.long_bits);
1002 assert!(t.long_bits <= t.long_long_bits);
1003 assert!(t.long_long_bits >= 64);
1004 }
1005
1006 #[test]
1010 fn the_host_model_agrees_with_core_ffi() {
1011 let t = TargetModel::host();
1012 assert_eq!(t.short_bits, 8 * size_of::<core::ffi::c_short>() as u32);
1013 assert_eq!(t.int_bits, 8 * size_of::<core::ffi::c_int>() as u32);
1014 assert_eq!(t.long_bits, 8 * size_of::<core::ffi::c_long>() as u32);
1015 assert_eq!(
1016 t.long_long_bits,
1017 8 * size_of::<core::ffi::c_longlong>() as u32
1018 );
1019 assert_eq!(t.ptr_bits, 8 * size_of::<*const ()>() as u32);
1020 assert_eq!(t.char_signed, core::ffi::c_char::MIN != 0);
1021 assert_eq!(t.int128_align, align_of::<i128>() as u64);
1022 assert_eq!(t.max_scalar_align, align_of::<u64>() as u64);
1023 assert_eq!(t.max_scalar_align, align_of::<f64>() as u64);
1024 }
1025
1026 #[test]
1027 fn a_64_bit_x86_linux_host_is_lp64() {
1028 if cfg!(all(target_os = "linux", target_arch = "x86_64")) {
1029 assert_eq!(TargetModel::host(), TargetModel::LP64);
1030 }
1031 }
1032
1033 #[test]
1036 fn the_table_agrees_with_the_host() {
1037 if cfg!(all(
1038 target_os = "linux",
1039 target_arch = "x86_64",
1040 target_env = "gnu"
1041 )) {
1042 assert_eq!(
1043 model("x86_64-unknown-linux-gnu"),
1044 TargetModel::host(),
1045 "the table and the host disagree"
1046 );
1047 }
1048 }
1049
1050 #[test]
1051 fn the_lp64_family() {
1052 for triple in [
1053 "x86_64-unknown-linux-gnu",
1054 "x86_64-unknown-linux-musl",
1055 "x86_64-unknown-freebsd",
1056 "aarch64-unknown-linux-gnu",
1057 "aarch64-apple-darwin",
1058 "x86_64-apple-darwin",
1059 "riscv64gc-unknown-linux-gnu",
1060 "powerpc64le-unknown-linux-gnu",
1061 "s390x-unknown-linux-gnu",
1062 "loongarch64-unknown-linux-gnu",
1063 "x86_64-unknown-none",
1064 ] {
1065 let t = model(triple);
1066 assert_eq!(t.data_model(), "LP64", "{triple}");
1067 assert_eq!(t.long_bits, 64, "{triple}");
1068 assert_eq!(t.ptr_bits, 64, "{triple}");
1069 assert!(t.has_int128, "{triple}");
1070 }
1071 }
1072
1073 #[test]
1074 fn the_llp64_family() {
1075 for triple in [
1076 "x86_64-pc-windows-msvc",
1077 "x86_64-pc-windows-gnu",
1078 "x86_64-pc-windows-gnullvm",
1079 "aarch64-pc-windows-msvc",
1080 "x86_64-uwp-windows-msvc",
1081 ] {
1082 let t = model(triple);
1083 assert_eq!(t.data_model(), "LLP64", "{triple}");
1084 assert_eq!(t.long_bits, 32, "{triple}");
1085 assert_eq!(t.ptr_bits, 64, "{triple}");
1086 assert_eq!(t.wchar_bits, 16, "{triple}");
1087 assert!(!t.wchar_signed, "{triple}");
1088 assert_eq!(t.wint_bits, 16, "{triple}");
1089 assert!(t.char_signed, "{triple}");
1092 assert_eq!(t.max_scalar_align, 8, "{triple}");
1093 }
1094 }
1095
1096 #[test]
1099 fn the_msvc_environment() {
1100 for triple in [
1101 "x86_64-pc-windows-msvc",
1102 "i686-pc-windows-msvc",
1103 "aarch64-pc-windows-msvc",
1104 "x86_64-uwp-windows-msvc",
1105 "thumbv7a-pc-windows-msvc",
1106 ] {
1107 assert!(model(triple).is_msvc(), "{triple} is MSVC");
1108 }
1109 for triple in [
1112 "x86_64-pc-windows-gnu",
1113 "i686-pc-windows-gnu",
1114 "x86_64-pc-windows-gnullvm",
1115 "aarch64-pc-windows-gnullvm",
1116 "x86_64-unknown-linux-gnu",
1117 "aarch64-apple-darwin",
1118 "x86_64-apple-darwin",
1119 "x86_64-unknown-freebsd",
1120 "wasm32-unknown-unknown",
1121 "thumbv7em-none-eabihf",
1122 ] {
1123 assert!(!model(triple).is_msvc(), "{triple} is not MSVC");
1124 }
1125 assert!(TargetModel::LLP64.is_msvc());
1126 assert!(!TargetModel::LP64.is_msvc());
1127 assert!(!TargetModel::ILP32.is_msvc());
1128 assert_eq!(
1131 TargetModel::host().is_msvc(),
1132 cfg!(all(windows, target_env = "msvc"))
1133 );
1134 }
1135
1136 #[test]
1137 fn the_ilp32_family() {
1138 for triple in [
1139 "i686-unknown-linux-gnu",
1140 "i586-unknown-linux-gnu",
1141 "i686-pc-windows-msvc",
1142 "armv7-unknown-linux-gnueabihf",
1143 "thumbv7em-none-eabihf",
1144 "riscv32imac-unknown-none-elf",
1145 "wasm32-unknown-unknown",
1146 "wasm32-wasip1",
1147 "mips-unknown-linux-gnu",
1148 "powerpc-unknown-linux-gnu",
1149 "sparc-unknown-linux-gnu",
1150 "x86_64-unknown-linux-gnux32",
1151 "aarch64-unknown-linux-gnu_ilp32",
1152 ] {
1153 let t = model(triple);
1154 assert_eq!(t.data_model(), "ILP32", "{triple}");
1155 assert_eq!(t.long_bits, 32, "{triple}");
1156 assert_eq!(t.ptr_bits, 32, "{triple}");
1157 }
1158 }
1159
1160 #[test]
1163 fn int128_follows_the_architecture() {
1164 assert!(model("x86_64-unknown-linux-gnux32").has_int128);
1165 assert!(model("aarch64-unknown-linux-gnu").has_int128);
1166 assert!(!model("i686-unknown-linux-gnu").has_int128);
1167 assert!(!model("wasm32-unknown-unknown").has_int128);
1168 assert!(!model("armv7-unknown-linux-gnueabihf").has_int128);
1169 }
1170
1171 #[test]
1174 fn plain_char_signedness_follows_core_ffi() {
1175 for triple in [
1176 "x86_64-unknown-linux-gnu",
1177 "i686-unknown-linux-gnu",
1178 "loongarch64-unknown-linux-gnu",
1179 "wasm32-unknown-unknown",
1180 "mips-unknown-linux-gnu",
1181 "sparc64-unknown-netbsd",
1182 "aarch64-pc-windows-msvc",
1184 "aarch64-apple-darwin",
1185 "armv7-apple-ios",
1186 ] {
1187 assert!(model(triple).char_signed, "{triple} should be signed");
1188 }
1189 for triple in [
1190 "aarch64-unknown-linux-gnu",
1191 "armv7-unknown-linux-gnueabihf",
1192 "thumbv7em-none-eabihf",
1193 "riscv64gc-unknown-linux-gnu",
1194 "riscv32imac-unknown-none-elf",
1195 "powerpc64le-unknown-linux-gnu",
1196 "powerpc-unknown-linux-gnu",
1197 "s390x-unknown-linux-gnu",
1198 ] {
1199 assert!(!model(triple).char_signed, "{triple} should be unsigned");
1200 }
1201 }
1202
1203 #[test]
1207 fn scalar_alignment_splits_i386_from_i386_on_windows() {
1208 assert_eq!(model("i686-unknown-linux-gnu").max_scalar_align, 4);
1209 assert_eq!(model("i586-unknown-netbsd").max_scalar_align, 4);
1210 assert_eq!(model("i686-pc-windows-msvc").max_scalar_align, 8);
1211 assert_eq!(model("i686-pc-windows-gnu").max_scalar_align, 8);
1212 assert_eq!(model("armv7-unknown-linux-gnueabihf").max_scalar_align, 8);
1213 assert_eq!(model("wasm32-unknown-unknown").max_scalar_align, 8);
1214 }
1215
1216 #[test]
1217 fn endianness() {
1218 for triple in [
1219 "s390x-unknown-linux-gnu",
1220 "powerpc64-unknown-linux-gnu",
1221 "powerpc-unknown-linux-gnu",
1222 "sparc64-unknown-linux-gnu",
1223 "mips-unknown-linux-gnu",
1224 "aarch64_be-unknown-linux-gnu",
1225 ] {
1226 assert!(model(triple).big_endian, "{triple} is big-endian");
1227 }
1228 for triple in [
1229 "x86_64-unknown-linux-gnu",
1230 "powerpc64le-unknown-linux-gnu",
1231 "mipsel-unknown-linux-gnu",
1232 "mips64el-unknown-linux-gnuabi64",
1233 "aarch64-unknown-linux-gnu",
1234 ] {
1235 assert!(!model(triple).big_endian, "{triple} is little-endian");
1236 }
1237 }
1238
1239 #[test]
1243 fn wchar_and_wint() {
1244 let arm = model("aarch64-unknown-linux-gnu");
1245 assert_eq!((arm.wchar_bits, arm.wchar_signed), (32, false));
1246 assert_eq!((arm.wint_bits, arm.wint_signed), (32, false));
1247
1248 let mac = model("aarch64-apple-darwin");
1249 assert_eq!((mac.wchar_bits, mac.wchar_signed), (32, true));
1250 assert_eq!((mac.wint_bits, mac.wint_signed), (32, true));
1251
1252 let win = model("x86_64-pc-windows-msvc");
1253 assert_eq!((win.wchar_bits, win.wchar_signed), (16, false));
1254 assert_eq!((win.wint_bits, win.wint_signed), (16, false));
1255
1256 let linux = model("x86_64-unknown-linux-gnu");
1257 assert_eq!((linux.wchar_bits, linux.wchar_signed), (32, true));
1258 assert_eq!((linux.wint_bits, linux.wint_signed), (32, false));
1259 }
1260
1261 #[test]
1262 fn the_identity_macros() {
1263 let linux = macros("x86_64-unknown-linux-gnu");
1264 for want in [
1265 "__x86_64__=1",
1266 "__amd64__=1",
1267 "__linux__=1",
1268 "__gnu_linux__=1",
1269 "__unix__=1",
1270 "__ELF__=1",
1271 ] {
1272 assert!(linux.contains(&want.to_owned()), "{want} in {linux:?}");
1273 }
1274 assert!(!linux.iter().any(|m| m.starts_with("_WIN")));
1275
1276 let win = macros("x86_64-pc-windows-msvc");
1277 assert!(win.contains(&"_WIN32=1".to_owned()));
1278 assert!(win.contains(&"_WIN64=1".to_owned()));
1279 assert!(!win.iter().any(|m| m.starts_with("__ELF__")));
1280 assert!(!win.iter().any(|m| m.starts_with("__unix")));
1281
1282 let win32 = macros("i686-pc-windows-msvc");
1283 assert!(win32.contains(&"_WIN32=1".to_owned()));
1284 assert!(!win32.contains(&"_WIN64=1".to_owned()));
1285 assert!(win32.contains(&"__i386__=1".to_owned()));
1286
1287 let mac = macros("aarch64-apple-darwin");
1288 assert!(mac.contains(&"__APPLE__=1".to_owned()));
1289 assert!(mac.contains(&"__MACH__=1".to_owned()));
1290 assert!(mac.contains(&"__aarch64__=1".to_owned()));
1291 assert!(!mac.iter().any(|m| m.starts_with("__ELF__")));
1292
1293 let wasm = macros("wasm32-unknown-unknown");
1294 assert!(wasm.contains(&"__wasm32__=1".to_owned()));
1295 assert!(!wasm.iter().any(|m| m.starts_with("__ELF__")));
1296
1297 let riscv = macros("riscv64gc-unknown-linux-gnu");
1298 assert!(riscv.contains(&"__riscv=1".to_owned()));
1299 assert!(riscv.contains(&"__riscv_xlen=64".to_owned()));
1300
1301 let bare = macros("thumbv7em-none-eabihf");
1302 assert!(bare.contains(&"__arm__=1".to_owned()));
1303 assert!(bare.contains(&"__ELF__=1".to_owned()));
1304 assert!(!bare.iter().any(|m| m.starts_with("__linux")));
1305 }
1306
1307 #[test]
1310 fn the_c_library_of_a_linux_triple() {
1311 for (triple, want) in [
1312 ("x86_64-unknown-linux-gnu", Env::Gnu),
1313 ("armv7-unknown-linux-gnueabihf", Env::Gnu),
1314 ("x86_64-unknown-linux-gnux32", Env::Gnu),
1315 ("x86_64-unknown-linux", Env::Gnu),
1318 ("x86_64-unknown-linux-musl", Env::Musl),
1319 ("aarch64-unknown-linux-musl", Env::Musl),
1320 ("aarch64-linux-android", Env::Bionic),
1321 ("armv7-unknown-linux-uclibceabi", Env::Uclibc),
1322 ("x86_64-pc-windows-msvc", Env::Msvc),
1323 ("x86_64-pc-windows-gnu", Env::Gnu),
1326 ("aarch64-apple-darwin", Env::None),
1327 ("x86_64-unknown-freebsd", Env::None),
1328 ("wasm32-unknown-unknown", Env::None),
1329 ] {
1330 assert_eq!(model(triple).env, want, "{triple}");
1331 }
1332
1333 let glibc = "__cinrs_glibc__=1".to_owned();
1334 let musl = "__cinrs_musl__=1".to_owned();
1335 assert!(macros("x86_64-unknown-linux-gnu").contains(&glibc));
1336 assert!(macros("i686-unknown-linux-gnu").contains(&glibc));
1337 assert!(macros("x86_64-unknown-linux-musl").contains(&musl));
1338 for triple in [
1341 "x86_64-unknown-linux-gnu",
1342 "x86_64-unknown-linux-musl",
1343 "aarch64-linux-android",
1344 "x86_64-pc-windows-gnu",
1345 "aarch64-apple-darwin",
1346 "x86_64-unknown-freebsd",
1347 "wasm32-unknown-unknown",
1348 ] {
1349 let macros = macros(triple);
1350 let named = usize::from(macros.contains(&glibc)) + usize::from(macros.contains(&musl));
1351 assert!(named <= 1, "{triple} claims two C libraries");
1352 }
1353 for triple in [
1354 "aarch64-linux-android",
1355 "x86_64-pc-windows-gnu",
1356 "aarch64-apple-darwin",
1357 ] {
1358 let macros = macros(triple);
1359 assert!(!macros.contains(&glibc), "{triple}");
1360 assert!(!macros.contains(&musl), "{triple}");
1361 }
1362 }
1363
1364 #[test]
1365 fn an_unknown_architecture_is_refused() {
1366 let err = TargetModel::from_triple("gizmo-unknown-linux-gnu").unwrap_err();
1367 let message = err.message(&TargetSource::Env("gizmo-unknown-linux-gnu".to_owned()));
1368 assert!(
1369 message.starts_with(
1370 "CINRS_TARGET: unknown architecture 'gizmo' in the target triple \
1371 'gizmo-unknown-linux-gnu'; set #pragma cinrs target or unset the variable; "
1372 ),
1373 "{message}"
1374 );
1375 assert!(message.contains("the architectures cinrs models are x86, x86_64"));
1376 }
1377
1378 #[test]
1379 fn an_unknown_operating_system_is_refused() {
1380 let err = TargetModel::from_triple("x86_64-unknown-plan9").unwrap_err();
1381 let message = err.message(&TargetSource::Pragma("x86_64-unknown-plan9".to_owned()));
1382 assert!(
1383 message.starts_with(
1384 "#pragma cinrs target: unsupported operating system 'plan9' in the target \
1385 triple 'x86_64-unknown-plan9'; "
1386 ),
1387 "{message}"
1388 );
1389 }
1390
1391 #[test]
1392 fn the_exotic_data_models_are_refused_by_name() {
1393 let err = TargetModel::from_triple("avr-none-unknown").unwrap_err();
1394 let message = err.message(&TargetSource::Env("avr-none-unknown".to_owned()));
1395 assert!(
1396 message.starts_with(
1397 "CINRS_TARGET: the target triple 'avr-none-unknown' is not supported: on \
1398 'avr' 'int' is 16 bits and 'double' is 32; set #pragma cinrs target or \
1399 unset the variable; "
1400 ),
1401 "{message}"
1402 );
1403 assert!(TargetModel::from_triple("msp430-none-elf").is_err());
1404 }
1405
1406 #[test]
1407 fn an_empty_triple_is_refused() {
1408 assert!(TargetModel::from_triple("").is_err());
1409 assert!(TargetModel::from_triple("-linux-gnu").is_err());
1410 }
1411
1412 #[test]
1415 fn the_description_names_the_source() {
1416 let t = model("x86_64-pc-windows-msvc");
1417 let said = t.describe(&TargetSource::Env("x86_64-pc-windows-msvc".to_owned()));
1418 assert_eq!(
1419 said,
1420 "LLP64 (x86_64-windows, signed 'char', 16-bit 'wchar_t'), chosen from \
1421 CINRS_TARGET=x86_64-pc-windows-msvc"
1422 );
1423 let host = TargetModel::host().describe(&TargetSource::Host);
1424 assert!(host.contains("CINRS_TARGET being unset"), "{host}");
1425 }
1426}