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::L4re
141 | OperatingSystem::Linux
142 | OperatingSystem::Netbsd
143 | OperatingSystem::Openbsd
144 | OperatingSystem::Redox
145 | OperatingSystem::Solaris => CallingConvention::SystemV,
146 OperatingSystem::Windows => CallingConvention::WindowsFastcall,
147 OperatingSystem::Nebulet
148 | OperatingSystem::Emscripten
149 | OperatingSystem::Wasi
150 | OperatingSystem::Unknown => match self.architecture {
151 Architecture::Wasm32 => CallingConvention::WasmBasicCAbi,
152 _ => return Err(()),
153 },
154 _ => return Err(()),
155 })
156 }
157
158 /// The C data model for a given target. If the model is not known, returns `Err(())`.
159 pub fn data_model(&self) -> Result<CDataModel, ()> {
160 match self.pointer_width()? {
161 PointerWidth::U64 => {
162 if self.operating_system == OperatingSystem::Windows {
163 Ok(CDataModel::LLP64)
164 } else if self.default_calling_convention() == Ok(CallingConvention::SystemV)
165 || self.architecture == Architecture::Wasm64
166 {
167 Ok(CDataModel::LP64)
168 } else {
169 Err(())
170 }
171 }
172 PointerWidth::U32 => {
173 if self.operating_system == OperatingSystem::Windows
174 || self.default_calling_convention() == Ok(CallingConvention::SystemV)
175 || self.architecture == Architecture::Wasm32
176 {
177 Ok(CDataModel::ILP32)
178 } else {
179 Err(())
180 }
181 }
182 // TODO: on 16-bit machines there is usually a distinction
183 // between near-pointers and far-pointers.
184 // Additionally, code pointers sometimes have a different size than data pointers.
185 // We don't handle this case.
186 PointerWidth::U16 => Err(()),
187 }
188 }
189
190 /// Return a `Triple` with all unknown fields.
191 pub fn unknown() -> Self {
192 Self {
193 architecture: Architecture::Unknown,
194 vendor: Vendor::Unknown,
195 operating_system: OperatingSystem::Unknown,
196 environment: Environment::Unknown,
197 binary_format: BinaryFormat::Unknown,
198 }
199 }
200}
201
202impl fmt::Display for Triple {
203 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204 if let Some(res) = self.special_case_display(f) {
205 return res;
206 }
207
208 let implied_binary_format = default_binary_format(&self);
209
210 write!(f, "{}", self.architecture)?;
211 if self.vendor == Vendor::Unknown
212 && (self.environment != Environment::HermitKernel
213 && self.environment != Environment::LinuxKernel)
214 && ((self.operating_system == OperatingSystem::Linux
215 && (self.environment == Environment::Android
216 || self.environment == Environment::Androideabi
217 || self.environment == Environment::Kernel))
218 || self.operating_system == OperatingSystem::Fuchsia
219 || self.operating_system == OperatingSystem::Wasi
220 || self.operating_system == OperatingSystem::WasiP1
221 || self.operating_system == OperatingSystem::WasiP2
222 || (self.operating_system == OperatingSystem::None_
223 && (self.architecture == Architecture::Arm(ArmArchitecture::Armv4t)
224 || self.architecture == Architecture::Arm(ArmArchitecture::Armv5te)
225 || self.architecture == Architecture::Arm(ArmArchitecture::Armebv7r)
226 || self.architecture == Architecture::Arm(ArmArchitecture::Armv7a)
227 || self.architecture == Architecture::Arm(ArmArchitecture::Armv7r)
228 || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv4t)
229 || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv5te)
230 || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv6m)
231 || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv7em)
232 || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv7m)
233 || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv8mBase)
234 || self.architecture == Architecture::Arm(ArmArchitecture::Thumbv8mMain)
235 || self.architecture == Architecture::Msp430)))
236 {
237 // As a special case, omit the vendor for Android, Fuchsia, Wasi, and sometimes
238 // None_, depending on the hardware architecture. This logic is entirely
239 // ad-hoc, and is just sufficient to handle the current set of recognized
240 // triples.
241 write!(f, "-{}", self.operating_system)?;
242 } else if self.architecture.is_clever() && self.operating_system == OperatingSystem::Unknown
243 {
244 write!(f, "-{}", self.vendor)?;
245 } else {
246 write!(f, "-{}-{}", self.vendor, self.operating_system)?;
247 }
248
249 match (&self.vendor, self.operating_system, self.environment) {
250 (Vendor::Nintendo, OperatingSystem::Horizon, Environment::Newlib)
251 | (Vendor::Espressif, OperatingSystem::Espidf, Environment::Newlib) => {
252 // The triple representations of these platforms don't have an environment field.
253 }
254 (_, _, Environment::Unknown) => {
255 // Don't print out the environment if it is unknown.
256 }
257 _ => {
258 write!(f, "-{}", self.environment)?;
259 }
260 }
261
262 if self.binary_format != implied_binary_format {
263 write!(f, "-{}", self.binary_format)?;
264 }
265 Ok(())
266 }
267}
268
269impl FromStr for Triple {
270 type Err = ParseError;
271
272 fn from_str(s: &str) -> Result<Self, Self::Err> {
273 if let Some(triple) = Triple::special_case_from_str(s) {
274 return Ok(triple);
275 }
276
277 let mut parts = s.split('-');
278 let mut result = Self::unknown();
279 let mut current_part;
280
281 current_part = parts.next();
282 if let Some(s) = current_part {
283 if let Ok(architecture) = Architecture::from_str(s) {
284 result.architecture = architecture;
285 current_part = parts.next();
286 } else {
287 // Insist that the triple start with a valid architecture.
288 return Err(ParseError::UnrecognizedArchitecture(s.to_owned()));
289 }
290 }
291
292 let mut has_vendor = false;
293 let mut has_operating_system = false;
294 if let Some(s) = current_part {
295 if let Ok(vendor) = Vendor::from_str(s) {
296 has_vendor = true;
297 result.vendor = vendor;
298 current_part = parts.next();
299 }
300 }
301
302 if !has_operating_system {
303 if let Some(s) = current_part {
304 if let Ok(operating_system) = OperatingSystem::from_str(s) {
305 has_operating_system = true;
306 result.operating_system = operating_system;
307 current_part = parts.next();
308 }
309 }
310 }
311
312 let mut has_environment = false;
313
314 if !has_environment {
315 if let Some(s) = current_part {
316 if let Ok(environment) = Environment::from_str(s) {
317 has_environment = true;
318 result.environment = environment;
319 current_part = parts.next();
320 }
321 }
322 }
323
324 let mut has_binary_format = false;
325 if let Some(s) = current_part {
326 if let Ok(binary_format) = BinaryFormat::from_str(s) {
327 has_binary_format = true;
328 result.binary_format = binary_format;
329 current_part = parts.next();
330 }
331 }
332
333 // The binary format is frequently omitted; if that's the case here,
334 // infer it from the other fields.
335 if !has_binary_format {
336 result.binary_format = default_binary_format(&result);
337 }
338
339 if let Some(s) = current_part {
340 Err(
341 if !has_vendor && !has_operating_system && !has_environment && !has_binary_format {
342 ParseError::UnrecognizedVendor(s.to_owned())
343 } else if !has_operating_system && !has_environment && !has_binary_format {
344 ParseError::UnrecognizedOperatingSystem(s.to_owned())
345 } else if !has_environment && !has_binary_format {
346 ParseError::UnrecognizedEnvironment(s.to_owned())
347 } else if !has_binary_format {
348 ParseError::UnrecognizedBinaryFormat(s.to_owned())
349 } else {
350 ParseError::UnrecognizedField(s.to_owned())
351 },
352 )
353 } else {
354 Ok(result)
355 }
356 }
357}
358
359impl Triple {
360 /// Handle special cases in the `Display` implementation.
361 fn special_case_display(&self, f: &mut fmt::Formatter) -> Option<fmt::Result> {
362 let res = match self {
363 Triple {
364 architecture: Architecture::Arm(ArmArchitecture::Armv6k),
365 vendor: Vendor::Nintendo,
366 operating_system: OperatingSystem::Horizon,
367 ..
368 } => write!(f, "{}-{}-3ds", self.architecture, self.vendor),
369 Triple {
370 architecture: Architecture::Riscv32(Riscv32Architecture::Riscv32imc),
371 vendor: Vendor::Espressif,
372 operating_system: OperatingSystem::Espidf,
373 ..
374 } => write!(f, "{}-esp-{}", self.architecture, self.operating_system),
375 _ => return None,
376 };
377 Some(res)
378 }
379
380 /// Handle special cases in the `FromStr` implementation.
381 fn special_case_from_str(s: &str) -> Option<Self> {
382 let mut triple = Triple::unknown();
383
384 match s {
385 "armv6k-nintendo-3ds" => {
386 triple.architecture = Architecture::Arm(ArmArchitecture::Armv6k);
387 triple.vendor = Vendor::Nintendo;
388 triple.operating_system = OperatingSystem::Horizon;
389 triple.environment = Environment::Newlib;
390 triple.binary_format = default_binary_format(&triple);
391 }
392 "riscv32imc-esp-espidf" => {
393 triple.architecture = Architecture::Riscv32(Riscv32Architecture::Riscv32imc);
394 triple.vendor = Vendor::Espressif;
395 triple.operating_system = OperatingSystem::Espidf;
396 triple.environment = Environment::Newlib;
397 triple.binary_format = default_binary_format(&triple);
398 }
399 _ => return None,
400 }
401
402 Some(triple)
403 }
404}
405
406/// A convenient syntax for triple literals.
407///
408/// This currently expands to code that just calls `Triple::from_str` and does
409/// an `expect`, though in the future it would be cool to use procedural macros
410/// or so to report errors at compile time instead.
411#[macro_export]
412macro_rules! triple {
413 ($str:tt) => {
414 <$crate::Triple as core::str::FromStr>::from_str($str).expect("invalid triple literal")
415 };
416}
417
418#[cfg(test)]
419mod tests {
420 use super::*;
421
422 #[test]
423 fn parse_errors() {
424 assert_eq!(
425 Triple::from_str(""),
426 Err(ParseError::UnrecognizedArchitecture("".to_owned()))
427 );
428 assert_eq!(
429 Triple::from_str("foo"),
430 Err(ParseError::UnrecognizedArchitecture("foo".to_owned()))
431 );
432 assert_eq!(
433 Triple::from_str("unknown-unknown-foo"),
434 Err(ParseError::UnrecognizedOperatingSystem("foo".to_owned()))
435 );
436 assert_eq!(
437 Triple::from_str("unknown-unknown-unknown-foo"),
438 Err(ParseError::UnrecognizedEnvironment("foo".to_owned()))
439 );
440 assert_eq!(
441 Triple::from_str("unknown-unknown-unknown-unknown-foo"),
442 Err(ParseError::UnrecognizedBinaryFormat("foo".to_owned()))
443 );
444 assert_eq!(
445 Triple::from_str("unknown-unknown-unknown-unknown-unknown-foo"),
446 Err(ParseError::UnrecognizedField("foo".to_owned()))
447 );
448 }
449
450 #[test]
451 fn defaults() {
452 assert_eq!(
453 Triple::from_str("unknown-unknown-unknown"),
454 Ok(Triple::unknown())
455 );
456 assert_eq!(
457 Triple::from_str("unknown-unknown-unknown-unknown"),
458 Ok(Triple::unknown())
459 );
460 assert_eq!(
461 Triple::from_str("unknown-unknown-unknown-unknown-unknown"),
462 Ok(Triple::unknown())
463 );
464 }
465
466 #[test]
467 fn unknown_properties() {
468 assert_eq!(Triple::unknown().endianness(), Err(()));
469 assert_eq!(Triple::unknown().pointer_width(), Err(()));
470 assert_eq!(Triple::unknown().default_calling_convention(), Err(()));
471 }
472
473 #[test]
474 fn apple_calling_convention() {
475 for triple in &[
476 "aarch64-apple-darwin",
477 "aarch64-apple-ios",
478 "aarch64-apple-ios-macabi",
479 "aarch64-apple-tvos",
480 "aarch64-apple-watchos",
481 ] {
482 assert_eq!(
483 Triple::from_str(triple)
484 .unwrap()
485 .default_calling_convention()
486 .unwrap(),
487 CallingConvention::AppleAarch64
488 );
489 }
490
491 for triple in &["aarch64-linux-android", "x86_64-apple-ios"] {
492 assert_eq!(
493 Triple::from_str(triple)
494 .unwrap()
495 .default_calling_convention()
496 .unwrap(),
497 CallingConvention::SystemV
498 );
499 }
500 }
501
502 #[test]
503 fn p32_abi() {
504 // Test that special 32-bit pointer ABIs on 64-bit architectures are
505 // reported as having 32-bit pointers.
506 for triple in &[
507 "x86_64-unknown-linux-gnux32",
508 "aarch64_be-unknown-linux-gnu_ilp32",
509 "aarch64-unknown-linux-gnu_ilp32",
510 ] {
511 assert_eq!(
512 Triple::from_str(triple).unwrap().pointer_width().unwrap(),
513 PointerWidth::U32
514 );
515 }
516
517 // Test that the corresponding ABIs have 64-bit pointers.
518 for triple in &[
519 "x86_64-unknown-linux-gnu",
520 "aarch64_be-unknown-linux-gnu",
521 "aarch64-unknown-linux-gnu",
522 ] {
523 assert_eq!(
524 Triple::from_str(triple).unwrap().pointer_width().unwrap(),
525 PointerWidth::U64
526 );
527 }
528 }
529}
530