target_lexicon/
triple.rs

1// This file defines the `Triple` type and support code shared by all targets.
2
3use crate::data_model::CDataModel;
4use crate::parse_error::ParseError;
5use crate::targets::{
6    default_binary_format, Architecture, ArmArchitecture, BinaryFormat, Environment,
7    OperatingSystem, Riscv32Architecture, Vendor,
8};
9#[cfg(not(feature = "std"))]
10use alloc::borrow::ToOwned;
11use core::fmt;
12use core::str::FromStr;
13
14/// The target memory endianness.
15#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
16#[allow(missing_docs)]
17pub enum Endianness {
18    Little,
19    Big,
20}
21
22/// The width of a pointer (in the default address space).
23#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
24#[allow(missing_docs)]
25pub enum PointerWidth {
26    U16,
27    U32,
28    U64,
29}
30
31impl PointerWidth {
32    /// Return the number of bits in a pointer.
33    pub fn bits(self) -> u8 {
34        match self {
35            PointerWidth::U16 => 16,
36            PointerWidth::U32 => 32,
37            PointerWidth::U64 => 64,
38        }
39    }
40
41    /// Return the number of bytes in a pointer.
42    ///
43    /// For these purposes, there are 8 bits in a byte.
44    pub fn bytes(self) -> u8 {
45        match self {
46            PointerWidth::U16 => 2,
47            PointerWidth::U32 => 4,
48            PointerWidth::U64 => 8,
49        }
50    }
51}
52
53/// The calling convention, which specifies things like which registers are
54/// used for passing arguments, which registers are callee-saved, and so on.
55#[cfg_attr(feature = "rust_1_40", non_exhaustive)]
56#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
57pub enum CallingConvention {
58    /// "System V", which is used on most Unix-like platfoms. Note that the
59    /// specific conventions vary between hardware architectures; for example,
60    /// x86-32's "System V" is entirely different from x86-64's "System V".
61    SystemV,
62
63    /// The WebAssembly C ABI.
64    /// https://github.com/WebAssembly/tool-conventions/blob/master/BasicCABI.md
65    WasmBasicCAbi,
66
67    /// "Windows Fastcall", which is used on Windows. Note that like "System V",
68    /// this varies between hardware architectures. On x86-32 it describes what
69    /// Windows documentation calls "fastcall", and on x86-64 it describes what
70    /// Windows documentation often just calls the Windows x64 calling convention
71    /// (though the compiler still recognizes "fastcall" as an alias for it).
72    WindowsFastcall,
73
74    /// Apple Aarch64 platforms use their own variant of the common Aarch64
75    /// calling convention.
76    ///
77    /// <https://developer.apple.com/documentation/xcode/writing_arm64_code_for_apple_platforms>
78    AppleAarch64,
79}
80
81/// A target "triple". Historically such things had three fields, though they've
82/// added additional fields over time.
83///
84/// Note that `Triple` doesn't implement `Default` itself. If you want a type
85/// which defaults to the host triple, or defaults to unknown-unknown-unknown,
86/// use `DefaultToHost` or `DefaultToUnknown`, respectively.
87#[derive(Clone, Debug, PartialEq, Eq, Hash)]
88pub struct Triple {
89    /// The "architecture" (and sometimes the subarchitecture).
90    pub architecture: Architecture,
91    /// The "vendor" (whatever that means).
92    pub vendor: Vendor,
93    /// The "operating system" (sometimes also the environment).
94    pub operating_system: OperatingSystem,
95    /// The "environment" on top of the operating system (often omitted for
96    /// operating systems with a single predominant environment).
97    pub environment: Environment,
98    /// The "binary format" (rarely used).
99    pub binary_format: BinaryFormat,
100}
101
102impl Triple {
103    /// Return the endianness of this target's architecture.
104    pub fn endianness(&self) -> Result<Endianness, ()> {
105        self.architecture.endianness()
106    }
107
108    /// Return the pointer width of this target's architecture.
109    ///
110    /// This function is aware of x32 and ilp32 ABIs on 64-bit architectures.
111    pub fn pointer_width(&self) -> Result<PointerWidth, ()> {
112        // Some ABIs have a different pointer width than the CPU architecture.
113        match self.environment {
114            Environment::Gnux32 | Environment::GnuIlp32 => return Ok(PointerWidth::U32),
115            _ => {}
116        }
117
118        self.architecture.pointer_width()
119    }
120
121    /// Return the default calling convention for the given target triple.
122    pub fn default_calling_convention(&self) -> Result<CallingConvention, ()> {
123        Ok(match self.operating_system {
124            OperatingSystem::Darwin
125            | OperatingSystem::Ios
126            | OperatingSystem::Tvos
127            | OperatingSystem::MacOSX { .. }
128            | OperatingSystem::Watchos => match self.architecture {
129                Architecture::Aarch64(_) => CallingConvention::AppleAarch64,
130                _ => CallingConvention::SystemV,
131            },
132            OperatingSystem::Aix
133            | OperatingSystem::Bitrig
134            | OperatingSystem::Cloudabi
135            | OperatingSystem::Dragonfly
136            | OperatingSystem::Freebsd
137            | OperatingSystem::Fuchsia
138            | OperatingSystem::Haiku
139            | OperatingSystem::Hermit
140            | OperatingSystem::Hurd
141            | OperatingSystem::L4re
142            | OperatingSystem::Linux
143            | OperatingSystem::Netbsd
144            | OperatingSystem::Openbsd
145            | OperatingSystem::Redox
146            | OperatingSystem::Solaris => CallingConvention::SystemV,
147            OperatingSystem::Windows => CallingConvention::WindowsFastcall,
148            OperatingSystem::Nebulet
149            | OperatingSystem::Emscripten
150            | OperatingSystem::Wasi
151            | OperatingSystem::Unknown => match self.architecture {
152                Architecture::Wasm32 => CallingConvention::WasmBasicCAbi,
153                _ => return Err(()),
154            },
155            _ => return Err(()),
156        })
157    }
158
159    /// The C data model for a given target. If the model is not known, returns `Err(())`.
160    pub fn data_model(&self) -> Result<CDataModel, ()> {
161        match self.pointer_width()? {
162            PointerWidth::U64 => {
163                if self.operating_system == OperatingSystem::Windows {
164                    Ok(CDataModel::LLP64)
165                } else if self.default_calling_convention() == Ok(CallingConvention::SystemV)
166                    || self.architecture == Architecture::Wasm64
167                    || self.default_calling_convention() == Ok(CallingConvention::AppleAarch64)
168                {
169                    Ok(CDataModel::LP64)
170                } else {
171                    Err(())
172                }
173            }
174            PointerWidth::U32 => {
175                if self.operating_system == OperatingSystem::Windows
176                    || self.default_calling_convention() == Ok(CallingConvention::SystemV)
177                    || self.architecture == Architecture::Wasm32
178                {
179                    Ok(CDataModel::ILP32)
180                } else {
181                    Err(())
182                }
183            }
184            // TODO: on 16-bit machines there is usually a distinction
185            // between near-pointers and far-pointers.
186            // Additionally, code pointers sometimes have a different size than data pointers.
187            // We don't handle this case.
188            PointerWidth::U16 => Err(()),
189        }
190    }
191
192    /// Return a `Triple` with all unknown fields.
193    pub fn unknown() -> Self {
194        Self {
195            architecture: Architecture::Unknown,
196            vendor: Vendor::Unknown,
197            operating_system: OperatingSystem::Unknown,
198            environment: Environment::Unknown,
199            binary_format: BinaryFormat::Unknown,
200        }
201    }
202}
203
204impl fmt::Display for Triple {
205    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206        if let Some(res) = self.special_case_display(f) {
207            return res;
208        }
209
210        let implied_binary_format = default_binary_format(&self);
211
212        write!(f, "{}", self.architecture)?;
213        if self.vendor == Vendor::Unknown
214            && (self.environment != Environment::HermitKernel
215                && self.environment != Environment::LinuxKernel)
216            && ((self.operating_system == OperatingSystem::Linux
217                && (self.environment == Environment::Android
218                    || self.environment == Environment::Androideabi
219                    || self.environment == Environment::Kernel))
220                || self.operating_system == OperatingSystem::Wasi
221                || self.operating_system == OperatingSystem::WasiP1
222                || self.operating_system == OperatingSystem::WasiP2
223                || (self.operating_system == OperatingSystem::None_
224                    && (self.architecture == Architecture::Arm(ArmArchitecture::Armv4t)
225                        || self.architecture == Architecture::Arm(ArmArchitecture::Armv5te)
226                        || self.architecture == Architecture::Arm(ArmArchitecture::Armebv7r)
227                        || self.architecture == Architecture::Arm(ArmArchitecture::Armv7a)
228                        || self.architecture == Architecture::Arm(ArmArchitecture::Armv7r)
229                        || self.architecture == Architecture::Arm(ArmArchitecture::Armv8r)
230                        || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv4t)
231                        || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv5te)
232                        || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv6m)
233                        || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv7em)
234                        || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv7m)
235                        || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv8mBase)
236                        || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv8mMain)
237                        || self.architecture == Architecture::Msp430)))
238        {
239            // As a special case, omit the vendor for Android, Wasi, and sometimes
240            // None_, depending on the hardware architecture. This logic is entirely
241            // ad-hoc, and is just sufficient to handle the current set of recognized
242            // triples.
243            write!(f, "-{}", self.operating_system)?;
244        } else if self.architecture.is_clever() && self.operating_system == OperatingSystem::Unknown
245        {
246            write!(f, "-{}", self.vendor)?;
247        } else {
248            write!(f, "-{}-{}", self.vendor, self.operating_system)?;
249        }
250
251        match (&self.vendor, self.operating_system, self.environment) {
252            (Vendor::Nintendo, OperatingSystem::Horizon, Environment::Newlib)
253            | (Vendor::Espressif, OperatingSystem::Espidf, Environment::Newlib) => {
254                // The triple representations of these platforms don't have an environment field.
255            }
256            (_, _, Environment::Unknown) => {
257                // Don't print out the environment if it is unknown.
258            }
259            _ => {
260                write!(f, "-{}", self.environment)?;
261            }
262        }
263
264        if self.binary_format != implied_binary_format || show_binary_format_with_no_os(self) {
265            // As a special case, omit a non-default binary format for some
266            // targets which happen to exclude it.
267            write!(f, "-{}", self.binary_format)?;
268        }
269        Ok(())
270    }
271}
272
273fn show_binary_format_with_no_os(triple: &Triple) -> bool {
274    if triple.binary_format == BinaryFormat::Unknown {
275        return false;
276    }
277
278    #[cfg(feature = "arch_zkasm")]
279    {
280        if triple.architecture == Architecture::ZkAsm {
281            return false;
282        }
283    }
284
285    triple.environment != Environment::Eabi
286        && triple.environment != Environment::Eabihf
287        && triple.environment != Environment::Sgx
288        && triple.architecture != Architecture::Avr
289        && triple.architecture != Architecture::Wasm32
290        && triple.architecture != Architecture::Wasm64
291        && (triple.operating_system == OperatingSystem::None_
292            || triple.operating_system == OperatingSystem::Unknown)
293}
294
295impl FromStr for Triple {
296    type Err = ParseError;
297
298    fn from_str(s: &str) -> Result<Self, Self::Err> {
299        if let Some(triple) = Triple::special_case_from_str(s) {
300            return Ok(triple);
301        }
302
303        let mut parts = s.split('-');
304        let mut result = Self::unknown();
305        let mut current_part;
306
307        current_part = parts.next();
308        if let Some(s) = current_part {
309            if let Ok(architecture) = Architecture::from_str(s) {
310                result.architecture = architecture;
311                current_part = parts.next();
312            } else {
313                // Insist that the triple start with a valid architecture.
314                return Err(ParseError::UnrecognizedArchitecture(s.to_owned()));
315            }
316        }
317
318        let mut has_vendor = false;
319        let mut has_operating_system = false;
320        if let Some(s) = current_part {
321            if let Ok(vendor) = Vendor::from_str(s) {
322                has_vendor = true;
323                result.vendor = vendor;
324                current_part = parts.next();
325            }
326        }
327
328        if !has_operating_system {
329            if let Some(s) = current_part {
330                if let Ok(operating_system) = OperatingSystem::from_str(s) {
331                    has_operating_system = true;
332                    result.operating_system = operating_system;
333                    current_part = parts.next();
334                }
335            }
336        }
337
338        let mut has_environment = false;
339
340        if !has_environment {
341            if let Some(s) = current_part {
342                if let Ok(environment) = Environment::from_str(s) {
343                    has_environment = true;
344                    result.environment = environment;
345                    current_part = parts.next();
346                }
347            }
348        }
349
350        let mut has_binary_format = false;
351        if let Some(s) = current_part {
352            if let Ok(binary_format) = BinaryFormat::from_str(s) {
353                has_binary_format = true;
354                result.binary_format = binary_format;
355                current_part = parts.next();
356            }
357        }
358
359        // The binary format is frequently omitted; if that's the case here,
360        // infer it from the other fields.
361        if !has_binary_format {
362            result.binary_format = default_binary_format(&result);
363        }
364
365        if let Some(s) = current_part {
366            Err(
367                if !has_vendor && !has_operating_system && !has_environment && !has_binary_format {
368                    ParseError::UnrecognizedVendor(s.to_owned())
369                } else if !has_operating_system && !has_environment && !has_binary_format {
370                    ParseError::UnrecognizedOperatingSystem(s.to_owned())
371                } else if !has_environment && !has_binary_format {
372                    ParseError::UnrecognizedEnvironment(s.to_owned())
373                } else if !has_binary_format {
374                    ParseError::UnrecognizedBinaryFormat(s.to_owned())
375                } else {
376                    ParseError::UnrecognizedField(s.to_owned())
377                },
378            )
379        } else {
380            Ok(result)
381        }
382    }
383}
384
385impl Triple {
386    /// Handle special cases in the `Display` implementation.
387    fn special_case_display(&self, f: &mut fmt::Formatter) -> Option<fmt::Result> {
388        let res = match self {
389            Triple {
390                architecture: Architecture::Arm(ArmArchitecture::Armv6k),
391                vendor: Vendor::Nintendo,
392                operating_system: OperatingSystem::Horizon,
393                ..
394            } => write!(f, "{}-{}-3ds", self.architecture, self.vendor),
395            Triple {
396                architecture: Architecture::Riscv32(Riscv32Architecture::Riscv32imc),
397                vendor: Vendor::Espressif,
398                operating_system: OperatingSystem::Espidf,
399                ..
400            } => write!(f, "{}-esp-{}", self.architecture, self.operating_system),
401            _ => return None,
402        };
403        Some(res)
404    }
405
406    /// Handle special cases in the `FromStr` implementation.
407    fn special_case_from_str(s: &str) -> Option<Self> {
408        let mut triple = Triple::unknown();
409
410        match s {
411            "armv6k-nintendo-3ds" => {
412                triple.architecture = Architecture::Arm(ArmArchitecture::Armv6k);
413                triple.vendor = Vendor::Nintendo;
414                triple.operating_system = OperatingSystem::Horizon;
415                triple.environment = Environment::Newlib;
416                triple.binary_format = default_binary_format(&triple);
417            }
418            "riscv32imc-esp-espidf" => {
419                triple.architecture = Architecture::Riscv32(Riscv32Architecture::Riscv32imc);
420                triple.vendor = Vendor::Espressif;
421                triple.operating_system = OperatingSystem::Espidf;
422                triple.environment = Environment::Newlib;
423                triple.binary_format = default_binary_format(&triple);
424            }
425            _ => return None,
426        }
427
428        Some(triple)
429    }
430}
431
432/// A convenient syntax for triple literals.
433///
434/// This currently expands to code that just calls `Triple::from_str` and does
435/// an `expect`, though in the future it would be cool to use procedural macros
436/// or so to report errors at compile time instead.
437#[macro_export]
438macro_rules! triple {
439    ($str:tt) => {
440        <$crate::Triple as core::str::FromStr>::from_str($str).expect("invalid triple literal")
441    };
442}
443
444#[cfg(test)]
445mod tests {
446    use super::*;
447
448    #[test]
449    fn parse_errors() {
450        assert_eq!(
451            Triple::from_str(""),
452            Err(ParseError::UnrecognizedArchitecture("".to_owned()))
453        );
454        assert_eq!(
455            Triple::from_str("foo"),
456            Err(ParseError::UnrecognizedArchitecture("foo".to_owned()))
457        );
458        assert_eq!(
459            Triple::from_str("unknown-unknown-foo"),
460            Err(ParseError::UnrecognizedOperatingSystem("foo".to_owned()))
461        );
462        assert_eq!(
463            Triple::from_str("unknown-unknown-unknown-foo"),
464            Err(ParseError::UnrecognizedEnvironment("foo".to_owned()))
465        );
466        assert_eq!(
467            Triple::from_str("unknown-unknown-unknown-unknown-foo"),
468            Err(ParseError::UnrecognizedBinaryFormat("foo".to_owned()))
469        );
470        assert_eq!(
471            Triple::from_str("unknown-unknown-unknown-unknown-unknown-foo"),
472            Err(ParseError::UnrecognizedField("foo".to_owned()))
473        );
474    }
475
476    #[test]
477    fn defaults() {
478        assert_eq!(
479            Triple::from_str("unknown-unknown-unknown"),
480            Ok(Triple::unknown())
481        );
482        assert_eq!(
483            Triple::from_str("unknown-unknown-unknown-unknown"),
484            Ok(Triple::unknown())
485        );
486        assert_eq!(
487            Triple::from_str("unknown-unknown-unknown-unknown-unknown"),
488            Ok(Triple::unknown())
489        );
490    }
491
492    #[test]
493    fn unknown_properties() {
494        assert_eq!(Triple::unknown().endianness(), Err(()));
495        assert_eq!(Triple::unknown().pointer_width(), Err(()));
496        assert_eq!(Triple::unknown().default_calling_convention(), Err(()));
497    }
498
499    #[test]
500    fn apple_calling_convention() {
501        for triple in &[
502            "aarch64-apple-darwin",
503            "aarch64-apple-ios",
504            "aarch64-apple-ios-macabi",
505            "aarch64-apple-tvos",
506            "aarch64-apple-watchos",
507        ] {
508            let triple = Triple::from_str(triple).unwrap();
509            assert_eq!(
510                triple.default_calling_convention().unwrap(),
511                CallingConvention::AppleAarch64
512            );
513            assert_eq!(triple.data_model().unwrap(), CDataModel::LP64);
514        }
515
516        for triple in &["aarch64-linux-android", "x86_64-apple-ios"] {
517            assert_eq!(
518                Triple::from_str(triple)
519                    .unwrap()
520                    .default_calling_convention()
521                    .unwrap(),
522                CallingConvention::SystemV
523            );
524        }
525    }
526
527    #[test]
528    fn p32_abi() {
529        // Test that special 32-bit pointer ABIs on 64-bit architectures are
530        // reported as having 32-bit pointers.
531        for triple in &[
532            "x86_64-unknown-linux-gnux32",
533            "aarch64_be-unknown-linux-gnu_ilp32",
534            "aarch64-unknown-linux-gnu_ilp32",
535        ] {
536            assert_eq!(
537                Triple::from_str(triple).unwrap().pointer_width().unwrap(),
538                PointerWidth::U32
539            );
540        }
541
542        // Test that the corresponding ABIs have 64-bit pointers.
543        for triple in &[
544            "x86_64-unknown-linux-gnu",
545            "aarch64_be-unknown-linux-gnu",
546            "aarch64-unknown-linux-gnu",
547        ] {
548            assert_eq!(
549                Triple::from_str(triple).unwrap().pointer_width().unwrap(),
550                PointerWidth::U64
551            );
552        }
553    }
554}