blob: 0a9e91fd971f1e0e7ca4de6d5ccd18b6cd5e532e [file] [log] [blame]
// This file defines the `Triple` type and support code shared by all targets.
use crate::data_model::CDataModel;
use crate::parse_error::ParseError;
use crate::targets::{
default_binary_format, Architecture, ArmArchitecture, BinaryFormat, Environment,
OperatingSystem, Riscv32Architecture, Vendor,
};
#[cfg(not(feature = "std"))]
use alloc::borrow::ToOwned;
use core::fmt;
use core::str::FromStr;
/// The target memory endianness.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
pub enum Endianness {
Little,
Big,
}
/// The width of a pointer (in the default address space).
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
pub enum PointerWidth {
U16,
U32,
U64,
}
impl PointerWidth {
/// Return the number of bits in a pointer.
pub fn bits(self) -> u8 {
match self {
PointerWidth::U16 => 16,
PointerWidth::U32 => 32,
PointerWidth::U64 => 64,
}
}
/// Return the number of bytes in a pointer.
///
/// For these purposes, there are 8 bits in a byte.
pub fn bytes(self) -> u8 {
match self {
PointerWidth::U16 => 2,
PointerWidth::U32 => 4,
PointerWidth::U64 => 8,
}
}
}
/// The calling convention, which specifies things like which registers are
/// used for passing arguments, which registers are callee-saved, and so on.
#[cfg_attr(feature = "rust_1_40", non_exhaustive)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum CallingConvention {
/// "System V", which is used on most Unix-like platfoms. Note that the
/// specific conventions vary between hardware architectures; for example,
/// x86-32's "System V" is entirely different from x86-64's "System V".
SystemV,
/// The WebAssembly C ABI.
/// https://github.com/WebAssembly/tool-conventions/blob/master/BasicCABI.md
WasmBasicCAbi,
/// "Windows Fastcall", which is used on Windows. Note that like "System V",
/// this varies between hardware architectures. On x86-32 it describes what
/// Windows documentation calls "fastcall", and on x86-64 it describes what
/// Windows documentation often just calls the Windows x64 calling convention
/// (though the compiler still recognizes "fastcall" as an alias for it).
WindowsFastcall,
/// Apple Aarch64 platforms use their own variant of the common Aarch64
/// calling convention.
///
/// <https://developer.apple.com/documentation/xcode/writing_arm64_code_for_apple_platforms>
AppleAarch64,
}
/// A target "triple". Historically such things had three fields, though they've
/// added additional fields over time.
///
/// Note that `Triple` doesn't implement `Default` itself. If you want a type
/// which defaults to the host triple, or defaults to unknown-unknown-unknown,
/// use `DefaultToHost` or `DefaultToUnknown`, respectively.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Triple {
/// The "architecture" (and sometimes the subarchitecture).
pub architecture: Architecture,
/// The "vendor" (whatever that means).
pub vendor: Vendor,
/// The "operating system" (sometimes also the environment).
pub operating_system: OperatingSystem,
/// The "environment" on top of the operating system (often omitted for
/// operating systems with a single predominant environment).
pub environment: Environment,
/// The "binary format" (rarely used).
pub binary_format: BinaryFormat,
}
impl Triple {
/// Return the endianness of this target's architecture.
pub fn endianness(&self) -> Result<Endianness, ()> {
self.architecture.endianness()
}
/// Return the pointer width of this target's architecture.
pub fn pointer_width(&self) -> Result<PointerWidth, ()> {
self.architecture.pointer_width()
}
/// Return the default calling convention for the given target triple.
pub fn default_calling_convention(&self) -> Result<CallingConvention, ()> {
Ok(match self.operating_system {
OperatingSystem::Darwin
| OperatingSystem::Ios
| OperatingSystem::Tvos
| OperatingSystem::MacOSX { .. }
| OperatingSystem::Watchos => match self.architecture {
Architecture::Aarch64(_) => CallingConvention::AppleAarch64,
_ => CallingConvention::SystemV,
},
OperatingSystem::Aix
| OperatingSystem::Bitrig
| OperatingSystem::Cloudabi
| OperatingSystem::Dragonfly
| OperatingSystem::Freebsd
| OperatingSystem::Fuchsia
| OperatingSystem::Haiku
| OperatingSystem::Hermit
| OperatingSystem::L4re
| OperatingSystem::Linux
| OperatingSystem::Netbsd
| OperatingSystem::Openbsd
| OperatingSystem::Redox
| OperatingSystem::Solaris => CallingConvention::SystemV,
OperatingSystem::Windows => CallingConvention::WindowsFastcall,
OperatingSystem::Nebulet
| OperatingSystem::Emscripten
| OperatingSystem::Wasi
| OperatingSystem::Unknown => match self.architecture {
Architecture::Wasm32 => CallingConvention::WasmBasicCAbi,
_ => return Err(()),
},
_ => return Err(()),
})
}
/// The C data model for a given target. If the model is not known, returns `Err(())`.
pub fn data_model(&self) -> Result<CDataModel, ()> {
match self.pointer_width()? {
PointerWidth::U64 => {
if self.operating_system == OperatingSystem::Windows {
Ok(CDataModel::LLP64)
} else if self.default_calling_convention() == Ok(CallingConvention::SystemV)
|| self.architecture == Architecture::Wasm64
{
Ok(CDataModel::LP64)
} else {
Err(())
}
}
PointerWidth::U32 => {
if self.operating_system == OperatingSystem::Windows
|| self.default_calling_convention() == Ok(CallingConvention::SystemV)
|| self.architecture == Architecture::Wasm32
{
Ok(CDataModel::ILP32)
} else {
Err(())
}
}
// TODO: on 16-bit machines there is usually a distinction
// between near-pointers and far-pointers.
// Additionally, code pointers sometimes have a different size than data pointers.
// We don't handle this case.
PointerWidth::U16 => Err(()),
}
}
/// Return a `Triple` with all unknown fields.
pub fn unknown() -> Self {
Self {
architecture: Architecture::Unknown,
vendor: Vendor::Unknown,
operating_system: OperatingSystem::Unknown,
environment: Environment::Unknown,
binary_format: BinaryFormat::Unknown,
}
}
}
impl fmt::Display for Triple {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(res) = self.special_case_display(f) {
return res;
}
let implied_binary_format = default_binary_format(&self);
write!(f, "{}", self.architecture)?;
if self.vendor == Vendor::Unknown
&& (self.environment != Environment::HermitKernel
&& self.environment != Environment::LinuxKernel)
&& ((self.operating_system == OperatingSystem::Linux
&& (self.environment == Environment::Android
|| self.environment == Environment::Androideabi
|| self.environment == Environment::Kernel))
|| self.operating_system == OperatingSystem::Fuchsia
|| self.operating_system == OperatingSystem::Wasi
|| (self.operating_system == OperatingSystem::None_
&& (self.architecture == Architecture::Arm(ArmArchitecture::Armv4t)
|| self.architecture == Architecture::Arm(ArmArchitecture::Armv5te)
|| self.architecture == Architecture::Arm(ArmArchitecture::Armebv7r)
|| self.architecture == Architecture::Arm(ArmArchitecture::Armv7a)
|| self.architecture == Architecture::Arm(ArmArchitecture::Armv7r)
|| self.architecture == Architecture::Arm(ArmArchitecture::Thumbv4t)
|| self.architecture == Architecture::Arm(ArmArchitecture::Thumbv5te)
|| self.architecture == Architecture::Arm(ArmArchitecture::Thumbv6m)
|| self.architecture == Architecture::Arm(ArmArchitecture::Thumbv7em)
|| self.architecture == Architecture::Arm(ArmArchitecture::Thumbv7m)
|| self.architecture == Architecture::Arm(ArmArchitecture::Thumbv8mBase)
|| self.architecture == Architecture::Arm(ArmArchitecture::Thumbv8mMain)
|| self.architecture == Architecture::Msp430)))
{
// As a special case, omit the vendor for Android, Fuchsia, Wasi, and sometimes
// None_, depending on the hardware architecture. This logic is entirely
// ad-hoc, and is just sufficient to handle the current set of recognized
// triples.
write!(f, "-{}", self.operating_system)?;
} else if self.architecture.is_clever() && self.operating_system == OperatingSystem::Unknown
{
write!(f, "-{}", self.vendor)?;
} else {
write!(f, "-{}-{}", self.vendor, self.operating_system)?;
}
match (&self.vendor, self.operating_system, self.environment) {
(Vendor::Nintendo, OperatingSystem::Horizon, Environment::Newlib)
| (Vendor::Espressif, OperatingSystem::Espidf, Environment::Newlib) => {
// The triple representations of these platforms don't have an environment field.
}
(_, _, Environment::Unknown) => {
// Don't print out the environment if it is unknown.
}
_ => {
write!(f, "-{}", self.environment)?;
}
}
if self.binary_format != implied_binary_format {
write!(f, "-{}", self.binary_format)?;
}
Ok(())
}
}
impl FromStr for Triple {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(triple) = Triple::special_case_from_str(s) {
return Ok(triple);
}
let mut parts = s.split('-');
let mut result = Self::unknown();
let mut current_part;
current_part = parts.next();
if let Some(s) = current_part {
if let Ok(architecture) = Architecture::from_str(s) {
result.architecture = architecture;
current_part = parts.next();
} else {
// Insist that the triple start with a valid architecture.
return Err(ParseError::UnrecognizedArchitecture(s.to_owned()));
}
}
let mut has_vendor = false;
let mut has_operating_system = false;
if let Some(s) = current_part {
if let Ok(vendor) = Vendor::from_str(s) {
has_vendor = true;
result.vendor = vendor;
current_part = parts.next();
}
}
if !has_operating_system {
if let Some(s) = current_part {
if let Ok(operating_system) = OperatingSystem::from_str(s) {
has_operating_system = true;
result.operating_system = operating_system;
current_part = parts.next();
}
}
}
let mut has_environment = false;
if !has_environment {
if let Some(s) = current_part {
if let Ok(environment) = Environment::from_str(s) {
has_environment = true;
result.environment = environment;
current_part = parts.next();
}
}
}
let mut has_binary_format = false;
if let Some(s) = current_part {
if let Ok(binary_format) = BinaryFormat::from_str(s) {
has_binary_format = true;
result.binary_format = binary_format;
current_part = parts.next();
}
}
// The binary format is frequently omitted; if that's the case here,
// infer it from the other fields.
if !has_binary_format {
result.binary_format = default_binary_format(&result);
}
if let Some(s) = current_part {
Err(
if !has_vendor && !has_operating_system && !has_environment && !has_binary_format {
ParseError::UnrecognizedVendor(s.to_owned())
} else if !has_operating_system && !has_environment && !has_binary_format {
ParseError::UnrecognizedOperatingSystem(s.to_owned())
} else if !has_environment && !has_binary_format {
ParseError::UnrecognizedEnvironment(s.to_owned())
} else if !has_binary_format {
ParseError::UnrecognizedBinaryFormat(s.to_owned())
} else {
ParseError::UnrecognizedField(s.to_owned())
},
)
} else {
Ok(result)
}
}
}
impl Triple {
/// Handle special cases in the `Display` implementation.
fn special_case_display(&self, f: &mut fmt::Formatter) -> Option<fmt::Result> {
let res = match self {
Triple {
architecture: Architecture::Arm(ArmArchitecture::Armv6k),
vendor: Vendor::Nintendo,
operating_system: OperatingSystem::Horizon,
..
} => write!(f, "{}-{}-3ds", self.architecture, self.vendor),
Triple {
architecture: Architecture::Riscv32(Riscv32Architecture::Riscv32imc),
vendor: Vendor::Espressif,
operating_system: OperatingSystem::Espidf,
..
} => write!(f, "{}-esp-{}", self.architecture, self.operating_system),
_ => return None,
};
Some(res)
}
/// Handle special cases in the `FromStr` implementation.
fn special_case_from_str(s: &str) -> Option<Self> {
let mut triple = Triple::unknown();
match s {
"armv6k-nintendo-3ds" => {
triple.architecture = Architecture::Arm(ArmArchitecture::Armv6k);
triple.vendor = Vendor::Nintendo;
triple.operating_system = OperatingSystem::Horizon;
triple.environment = Environment::Newlib;
triple.binary_format = default_binary_format(&triple);
}
"riscv32imc-esp-espidf" => {
triple.architecture = Architecture::Riscv32(Riscv32Architecture::Riscv32imc);
triple.vendor = Vendor::Espressif;
triple.operating_system = OperatingSystem::Espidf;
triple.environment = Environment::Newlib;
triple.binary_format = default_binary_format(&triple);
}
_ => return None,
}
Some(triple)
}
}
/// A convenient syntax for triple literals.
///
/// This currently expands to code that just calls `Triple::from_str` and does
/// an `expect`, though in the future it would be cool to use procedural macros
/// or so to report errors at compile time instead.
#[macro_export]
macro_rules! triple {
($str:tt) => {
<$crate::Triple as core::str::FromStr>::from_str($str).expect("invalid triple literal")
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_errors() {
assert_eq!(
Triple::from_str(""),
Err(ParseError::UnrecognizedArchitecture("".to_owned()))
);
assert_eq!(
Triple::from_str("foo"),
Err(ParseError::UnrecognizedArchitecture("foo".to_owned()))
);
assert_eq!(
Triple::from_str("unknown-unknown-foo"),
Err(ParseError::UnrecognizedOperatingSystem("foo".to_owned()))
);
assert_eq!(
Triple::from_str("unknown-unknown-unknown-foo"),
Err(ParseError::UnrecognizedEnvironment("foo".to_owned()))
);
assert_eq!(
Triple::from_str("unknown-unknown-unknown-unknown-foo"),
Err(ParseError::UnrecognizedBinaryFormat("foo".to_owned()))
);
assert_eq!(
Triple::from_str("unknown-unknown-unknown-unknown-unknown-foo"),
Err(ParseError::UnrecognizedField("foo".to_owned()))
);
}
#[test]
fn defaults() {
assert_eq!(
Triple::from_str("unknown-unknown-unknown"),
Ok(Triple::unknown())
);
assert_eq!(
Triple::from_str("unknown-unknown-unknown-unknown"),
Ok(Triple::unknown())
);
assert_eq!(
Triple::from_str("unknown-unknown-unknown-unknown-unknown"),
Ok(Triple::unknown())
);
}
#[test]
fn unknown_properties() {
assert_eq!(Triple::unknown().endianness(), Err(()));
assert_eq!(Triple::unknown().pointer_width(), Err(()));
assert_eq!(Triple::unknown().default_calling_convention(), Err(()));
}
#[test]
fn apple_calling_convention() {
for triple in &[
"aarch64-apple-darwin",
"aarch64-apple-ios",
"aarch64-apple-ios-macabi",
"aarch64-apple-tvos",
"aarch64-apple-watchos",
] {
assert_eq!(
Triple::from_str(triple)
.unwrap()
.default_calling_convention()
.unwrap(),
CallingConvention::AppleAarch64
);
}
for triple in &["aarch64-linux-android", "x86_64-apple-ios"] {
assert_eq!(
Triple::from_str(triple)
.unwrap()
.default_calling_convention()
.unwrap(),
CallingConvention::SystemV
);
}
}
}