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::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 | |
202 | impl 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 | |
269 | impl 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 | |
359 | impl 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 ] |
412 | macro_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)] |
419 | mod 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 | |