Skip to main content

rucc_target/
typenames.rs

1//! The type names a target's compiler has before any header has been read.
2//!
3//! gcc registers a handful of names per architecture as if a typedef had declared them at file
4//! scope, and headers are written against them. AArch64 glibc is the one that matters: its
5//! `bits/math-vector.h`, which `<math.h>` includes, writes `typedef __Float32x4_t __f32x4_t;` when
6//! `__GNUC__` is nine or more and `typedef __SVFloat32_t __sv_f32_t;` when it is ten or more. A
7//! compiler without them cannot include `<math.h>` for that target at all.
8//!
9//! x86 and PowerPC have one more, `__float128`, which is gcc's older name for `_Float128` and the
10//! one code written before C23 reaches for. gcc has it on those two and nowhere else, and
11//! `__SIZEOF_FLOAT128__` is defined exactly where it is.
12//!
13//! They are names and not keywords, as they are in gcc, so a program may declare something of
14//! the same name and hide one, and on every other target they are ordinary identifiers.
15
16use rucc_tuple::Arch;
17
18/// What one lane of a vector is.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum Lane {
21    /// `signed char`.
22    I8,
23    /// `unsigned char`, which is also the lane of a polynomial of eight bits.
24    U8,
25    /// `short`.
26    I16,
27    /// `unsigned short`.
28    U16,
29    /// `int`.
30    I32,
31    /// `unsigned int`.
32    U32,
33    /// `long`, which is sixty four bits on every target these are for.
34    I64,
35    /// `unsigned long`.
36    U64,
37    /// `_Float16`.
38    F16,
39    /// `float`.
40    F32,
41    /// `double`.
42    F64,
43}
44
45/// The type one of the names is.
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum TypeName {
48    /// A GNU vector of this many lanes, which is the type `__attribute__((vector_size))` builds.
49    Vector(Lane, u8),
50    /// One of SVE's types, whose size depends on the machine the program runs on. C has no such
51    /// type, and gcc allows one in a declaration and nearly nowhere else, so it is an incomplete
52    /// type here: a prototype can name it, and an object of it is refused.
53    Sizeless,
54    /// `_Float128`, under the name gcc had for it before the ISO one.
55    Float128,
56}
57
58/// The Advanced SIMD vectors under the names gcc gives them in `aarch64-simd-builtin-types.def`,
59/// which `arm_neon.h` builds `float32x4_t` and the rest out of, and then SVE's. The `__Bfloat16`
60/// ones are left out until `__bf16` is a type here.
61const AARCH64: &[(&str, TypeName)] = &[
62    ("__Int8x8_t", TypeName::Vector(Lane::I8, 8)),
63    ("__Int16x4_t", TypeName::Vector(Lane::I16, 4)),
64    ("__Int32x2_t", TypeName::Vector(Lane::I32, 2)),
65    ("__Int64x1_t", TypeName::Vector(Lane::I64, 1)),
66    ("__Uint8x8_t", TypeName::Vector(Lane::U8, 8)),
67    ("__Uint16x4_t", TypeName::Vector(Lane::U16, 4)),
68    ("__Uint32x2_t", TypeName::Vector(Lane::U32, 2)),
69    ("__Uint64x1_t", TypeName::Vector(Lane::U64, 1)),
70    ("__Poly8x8_t", TypeName::Vector(Lane::U8, 8)),
71    ("__Poly16x4_t", TypeName::Vector(Lane::U16, 4)),
72    ("__Poly64x1_t", TypeName::Vector(Lane::U64, 1)),
73    ("__Float16x4_t", TypeName::Vector(Lane::F16, 4)),
74    ("__Float32x2_t", TypeName::Vector(Lane::F32, 2)),
75    ("__Float64x1_t", TypeName::Vector(Lane::F64, 1)),
76    ("__Int8x16_t", TypeName::Vector(Lane::I8, 16)),
77    ("__Int16x8_t", TypeName::Vector(Lane::I16, 8)),
78    ("__Int32x4_t", TypeName::Vector(Lane::I32, 4)),
79    ("__Int64x2_t", TypeName::Vector(Lane::I64, 2)),
80    ("__Uint8x16_t", TypeName::Vector(Lane::U8, 16)),
81    ("__Uint16x8_t", TypeName::Vector(Lane::U16, 8)),
82    ("__Uint32x4_t", TypeName::Vector(Lane::U32, 4)),
83    ("__Uint64x2_t", TypeName::Vector(Lane::U64, 2)),
84    ("__Poly8x16_t", TypeName::Vector(Lane::U8, 16)),
85    ("__Poly16x8_t", TypeName::Vector(Lane::U16, 8)),
86    ("__Poly64x2_t", TypeName::Vector(Lane::U64, 2)),
87    ("__Float16x8_t", TypeName::Vector(Lane::F16, 8)),
88    ("__Float32x4_t", TypeName::Vector(Lane::F32, 4)),
89    ("__Float64x2_t", TypeName::Vector(Lane::F64, 2)),
90    ("__SVInt8_t", TypeName::Sizeless),
91    ("__SVInt16_t", TypeName::Sizeless),
92    ("__SVInt32_t", TypeName::Sizeless),
93    ("__SVInt64_t", TypeName::Sizeless),
94    ("__SVUint8_t", TypeName::Sizeless),
95    ("__SVUint16_t", TypeName::Sizeless),
96    ("__SVUint32_t", TypeName::Sizeless),
97    ("__SVUint64_t", TypeName::Sizeless),
98    ("__SVFloat16_t", TypeName::Sizeless),
99    ("__SVFloat32_t", TypeName::Sizeless),
100    ("__SVFloat64_t", TypeName::Sizeless),
101    ("__SVBool_t", TypeName::Sizeless),
102];
103
104/// The name x86 and PowerPC have for quad precision.
105const FLOAT128: &[(&str, TypeName)] = &[("__float128", TypeName::Float128)];
106
107/// The names `arch` has before anything is read, and what each one is.
108#[must_use]
109pub(crate) fn type_names(arch: Arch) -> &'static [(&'static str, TypeName)] {
110    match arch {
111        Arch::Aarch64 => AARCH64,
112        Arch::X86_64 | Arch::X86 | Arch::PowerPc64 => FLOAT128,
113        _ => &[],
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn every_vector_is_one_of_the_two_widths_the_registers_have() {
123        let width = |lane: Lane| match lane {
124            Lane::I8 | Lane::U8 => 1,
125            Lane::I16 | Lane::U16 | Lane::F16 => 2,
126            Lane::I32 | Lane::U32 | Lane::F32 => 4,
127            Lane::I64 | Lane::U64 | Lane::F64 => 8,
128        };
129        for &(name, ty) in AARCH64 {
130            if let TypeName::Vector(lane, lanes) = ty {
131                let bytes = width(lane) * u32::from(lanes);
132                assert!(bytes == 8 || bytes == 16, "{name} is {bytes} bytes");
133                let q = name.ends_with(&format!("x{lanes}_t"));
134                assert!(q, "{name} does not say it has {lanes} lanes");
135            }
136        }
137    }
138
139    #[test]
140    fn quad_precision_has_its_old_name_where_gcc_has_it() {
141        let has = |arch| type_names(arch).iter().any(|&(name, _)| name == "__float128");
142        assert!(has(Arch::X86_64));
143        assert!(has(Arch::X86));
144        assert!(has(Arch::PowerPc64));
145        assert!(!has(Arch::Aarch64));
146        assert!(!has(Arch::Riscv64));
147        assert!(type_names(Arch::Arm).is_empty());
148    }
149}