1use 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#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
16#[allow(missing_docs)]
17pub enum Endianness {
18 Little,
19 Big,
20}
21
22#[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 pub fn bits(self) -> u8 {
34 match self {
35 PointerWidth::U16 => 16,
36 PointerWidth::U32 => 32,
37 PointerWidth::U64 => 64,
38 }
39 }
40
41 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#[cfg_attr(feature = "rust_1_40", non_exhaustive)]
56#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
57pub enum CallingConvention {
58 SystemV,
62
63 WasmBasicCAbi,
66
67 WindowsFastcall,
73
74 AppleAarch64,
79}
80
81#[derive(Clone, Debug, PartialEq, Eq, Hash)]
88pub struct Triple {
89 pub architecture: Architecture,
91 pub vendor: Vendor,
93 pub operating_system: OperatingSystem,
95 pub environment: Environment,
98 pub binary_format: BinaryFormat,
100}
101
102impl Triple {
103 pub fn endianness(&self) -> Result<Endianness, ()> {
105 self.architecture.endianness()
106 }
107
108 pub fn pointer_width(&self) -> Result<PointerWidth, ()> {
112 match self.environment {
114 Environment::Gnux32 | Environment::GnuIlp32 => return Ok(PointerWidth::U32),
115 _ => {}
116 }
117
118 self.architecture.pointer_width()
119 }
120
121 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 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 PointerWidth::U16 => Err(()),
189 }
190 }
191
192 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 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 }
256 (_, _, Environment::Unknown) => {
257 }
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 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 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 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 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 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#[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 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 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}