Skip to main content

TargetInfo

Struct TargetInfo 

Source
#[non_exhaustive]
pub struct TargetInfo {
Show 20 fields pub tuple: TargetTuple, pub scalars: DataLayout, pub pointer_width: u32, pub little_endian: bool, pub char_is_signed: bool, pub long_width: u32, pub long_double_width: u32, pub long_double_format: Format, pub float64x_format: Option<Format>, pub wchar_width: u32, pub wchar_is_signed: bool, pub bit_int_granule: u32, pub lock_free_width: u32, pub object_format: ObjectFormat, pub bit_field_style: BitFieldStyle, pub unnamed_bit_field_aligns: bool, pub empty_record_size: u64, pub va_list: Option<VaList>, pub regs: &'static RegFile, pub call_regs: Option<&'static CallRegs>,
}
Expand description

The facts about a target that the compiler reads instead of hard-coding.

This is the whole of what a pass is allowed to know about where its output will run. It grows, and every field added here is one fewer #[cfg] somewhere it should not be.

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§tuple: TargetTuple

The machine this describes, as the ten field tuple rather than as a three field triple.

It is the tuple because a record layout is a question every row of the target table has an answer to, and a triple can spell fifteen of the forty two. Nothing else in this type had to change to widen it: every field below is already derived from rucc-abi’s description of this tuple, and the ones that were not were the bugs.

§scalars: DataLayout

The sizes, the alignments and the signedness this target’s headers were written against.

The widths below are views of this and the alignments are not, which is the reason it is kept whole. A long long is eight bytes on every row of the table and is aligned to four on System V i386 and to eight everywhere else, and no width can say that.

§pointer_width: u32

Width of a pointer in bits.

§little_endian: bool

Whether bytes are ordered little end first.

§char_is_signed: bool

Whether a bare char is signed.

Signed on x86-64 and unsigned on AArch64 Linux, which is the classic source of code that works on one and not the other, so it is data rather than an assumption.

§long_width: u32

Width of long in bits. This is the field that separates the LP64 world from Windows LLP64.

§long_double_width: u32

Width of long double in bits: 80 bits of x87 stored in 128 on every x86-64 target but MSVC, 128 of true quad precision on AArch64 Linux and RISC-V, and 64 on Apple’s AArch64 and under MSVC.

Apple’s x86-64 is not one of the 64-bit ones, which is the trap. The change to a double came with AArch64 and the Intel answer stayed as it was, so x86_64-apple-darwin and x86_64-unknown-linux-gnu agree here and aarch64-apple-darwin is the odd one.

§long_double_format: Format

The format long double actually is, which the width does not say.

It is 128 bits wide on SysV x86-64 and on AArch64 Linux and the two are not the same type: one is the x87 eighty bit format padded out to sixteen bytes and the other is true quad precision with a hundred and thirteen bits of significand. Anything that converts a constant or folds one has to know which, and the width alone cannot say.

§float64x_format: Option<Format>

The format _Float64x is, which is the widest format the target has short of a software one.

It follows the architecture and not the operating system, which is what makes it worth a field of its own next to long double. Apple and Windows define long double as a double and neither of them takes _Float64x down with it: the type has to be wider than a _Float64, so it is the x87 eighty bit format on x86-64 and quad precision on AArch64 and RISC-V wherever it is written.

None on a machine whose widest format is a double, which is 32-bit ARM and wasm32. The type does not exist there and neither reference defines the macros that describe it, so the honest answer is that there is no format rather than a double in its place.

§wchar_width: u32

Width of wchar_t in bits, which decides what a wide literal is encoded in.

It is 16 on Windows, so a wide string there is UTF-16 and a character outside the basic plane takes two elements, and 32 everywhere else, where a wide string is UTF-32 and no character takes more than one.

§wchar_is_signed: bool

Whether wchar_t is signed.

x86-64 Linux makes it a signed int and AArch64 Linux makes it an unsigned int, following the psABI’s rule for plain char, so L'\xffffffff' is minus one on one of them and four billion on the other.

§bit_int_granule: u32

The granule a _BitInt wider than 64 bits is laid out in, in bits.

Above 64 bits the psABIs stop treating a _BitInt like a standard integer type and start treating it like an array of these, so its size is rounded up to a multiple of this and its alignment is this. It is 64 on x86-64 and RISC-V and 128 on AArch64, which is why _BitInt(65) is sixteen bytes aligned to eight on one and sixteen bytes aligned to sixteen on the other. Measured with clang 18 on x86-64 Linux and clang on AArch64 Darwin rather than read off the documents.

§lock_free_width: u32

The widest access, in bits, this machine performs atomically without taking a lock.

It is what __atomic_always_lock_free and __atomic_is_lock_free answer from, and it is a claim about what this compiler emits rather than about what the processor is capable of. Sixty four on every target here. x86-64 does sixteen bytes atomically with cmpxchg16b, which is not in the baseline the psABI names and which nothing in this compiler writes, and AArch64 does the same with its pair instructions, which nothing writes either. A target that answered yes for sixteen bytes and then called a library that has to take a lock for them would have two answers to one question, and the wrong one is the one in the header.

§object_format: ObjectFormat

The object format to emit.

§bit_field_style: BitFieldStyle

How bit-fields are allocated into storage, which is the one record layout question where two targets in this table run different algorithms rather than the same one over different numbers.

§unnamed_bit_field_aligns: bool

Whether an unnamed bit-field raises the record’s alignment the way a named one does.

Almost everywhere it does not, which is why struct { char c; int :20; } is four bytes aligned to one on x86-64 and four aligned to four with the field named. AAPCS64 says otherwise and says it for the zero width member too, so struct { unsigned :0; } is aligned to four on AArch64 Linux and to one on Apple’s AArch64, on Windows on AArch64, on x86-64 and on RISC-V. Measured with the pinned reference across every row that has one, because it is neither an architecture rule nor an operating system rule: it is the ABI, and Apple and Microsoft each dropped it.

Windows says yes as well, and there it is not AAPCS64 but Microsoft’s own rule, which is why the two facts are separate fields rather than one. In a union the Microsoft rule goes further and no bit-field contributes alignment at all, named or not, so this field is only half the answer there and BitFieldStyle carries the other half.

§empty_record_size: u64

How large a record with no storage in it is, in bytes, before its alignment is applied.

Zero everywhere but MSVC, where it is four. A struct with no members is not C at all, it is a GNU extension, and C++ gives it a size of one, so there is no standard to read the answer out of and the number has to come from whatever else compiles for the target. On mingw that is GCC and the answer is zero. On MSVC it is clang, because MSVC itself rejects the declaration outright, and clang’s Microsoft record layout gives it four bytes and gives an array of three of them twelve. So this is a fact about the environment and not about the operating system, which is the one place in this type where those two come apart in that direction.

It covers a record with no members and a record whose only members occupy nothing, which is the zero width bit-field, the zero length array and the flexible array member. All four were measured and all four agree.

§va_list: Option<VaList>

What __builtin_va_list is, which is the type every va_list in every header is a typedef of.

None on a target whose answer is a type this crate does not build yet. 32-bit ARM’s is a structure of one pointer and s390x’s is a structure of four members, and neither is any of the four below. A target with no backend cannot compile a call to va_arg in any case, so saying so beats naming a neighbour’s type and having a header believe it.

§regs: &'static RegFile

The registers the machine has, which is RegFile::EMPTY for an architecture nothing has described yet.

§call_regs: Option<&'static CallRegs>

Which registers the calling convention gives which job, or None while the architecture has no register file to name them out of.

Implementations§

Source§

impl TargetInfo

Source

pub fn call(&self) -> Option<Call>

The start of one call, with every argument register still to spend, and None on a target whose ABI is not described.

The None is AArch64 on Windows and nothing else today. That ABI is AAPCS64 with a different variadic rule and x18 reserved, per spec/cross-compile/06-abis.md section 6.1, and it is not written down yet. This used to answer AAPCS64 for it, which is the almost right answer, and an almost right ABI is the failure mode spec/cross-compile/02-the-goal.md exists to rule out: everything builds, everything links, and a structure crosses a library boundary with its members in the wrong registers. A caller that cannot compile the call says so instead.

Source§

impl TargetInfo

Source

pub fn new(triple: Triple) -> Self

The description of triple.

The three field triple spells fifteen of the forty two rows of the target table, which is every row with a backend and every row a driver will be handed today, so this is what the compiler proper calls. TargetInfo::for_tuple is the one that answers for the whole table.

Source

pub fn for_tuple(target: TargetTuple) -> Self

The description of target.

Every row of the target table has one of these, whether or not there is a backend that can emit code for it, because laying a record out and reading a header are questions that do not need a backend. The fields that genuinely need one say so: TargetInfo::regs is empty and TargetInfo::call_regs is None for an architecture whose register file is not written down.

Source

pub const fn max_object_size(&self) -> u64

The largest an object may be on this target, in bytes.

PTRDIFF_MAX, which is what C 6.5.6 needs it to be: subtracting two pointers into one object has to have an answer, and the answer has a ptrdiff_t to fit in. So an object of exactly this many bytes is allowed and one byte more is not, which is the line GCC draws too. It is the only size limit in the compiler and every layout question that has one asks here rather than at whatever its own arithmetic happens to overflow at.

Trait Implementations§

Source§

impl Clone for TargetInfo

Source§

fn clone(&self) -> TargetInfo

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TargetInfo

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for TargetInfo

Source§

impl PartialEq for TargetInfo

Source§

fn eq(&self, other: &TargetInfo) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for TargetInfo

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.