1 | // This file defines the `Triple` type and support code shared by all targets. |
2 | |
3 | use crate::data_model::CDataModel; |
4 | use crate::parse_error::ParseError; |
5 | use crate::targets::{ |
6 | default_binary_format, Architecture, ArmArchitecture, BinaryFormat, Environment, |
7 | OperatingSystem, Riscv32Architecture, Vendor, |
8 | }; |
9 | #[cfg (not(feature = "std" ))] |
10 | use alloc::borrow::ToOwned; |
11 | use core::fmt; |
12 | use core::str::FromStr; |
13 | |
14 | /// The target memory endianness. |
15 | #[derive (Copy, Clone, Debug, PartialEq, Eq, Hash)] |
16 | #[allow (missing_docs)] |
17 | pub 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)] |
25 | pub enum PointerWidth { |
26 | U16, |
27 | U32, |
28 | U64, |
29 | } |
30 | |
31 | impl 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)] |
57 | pub 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)] |
88 | pub 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 | |
102 | impl 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 | |
204 | impl 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 | |
273 | fn 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 | |
295 | impl 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 | |
385 | impl 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 ] |
438 | macro_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)] |
445 | mod 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 | } |
555 | |