Skip to main content

CallRegs

Struct CallRegs 

Source
pub struct CallRegs {
Show 27 fields pub abi: &'static AbiDescription, pub int_class: RegClass, pub sse_class: RegClass, pub int_args: &'static [PhysReg], pub sse_args: &'static [PhysReg], pub shared_positions: bool, pub int_returns: &'static [PhysReg], pub sse_returns: &'static [PhysReg], pub x87_returns: &'static [PhysReg], pub int_saved: &'static [PhysReg], pub sse_saved: &'static [PhysReg], pub int_order: &'static [PhysReg], pub sse_order: &'static [PhysReg], pub stack_pointer: PhysReg, pub frame_pointer: PhysReg, pub late_frame_pointer: bool, pub vector_count: Option<PhysReg>, pub red_zone: u32, pub shadow: u32, pub stack_align: u32, pub return_address: u32, pub word: u32, pub dwarf: &'static [&'static [u16]], pub dwarf_return_address: u16, pub guard: Option<Guard>, pub trace: Option<Trace>, pub chkstk: Option<Chkstk>,
}
Expand description

Which registers a calling convention gives which job.

This is the second half of a target description and it is separate from RegFile because the two do not vary together. x86-64 has one register file and two conventions over it, and they disagree about nearly everything below: rdi is where the first argument arrives on SysV and a register a callee has to preserve on Windows, and a Windows caller reserves thirty two bytes below the call that a SysV caller does not.

The allocation order is here rather than on a class because it is a consequence of what a call clobbers. A value that does not live across a call belongs in a register the callee is free to destroy, because putting it in a preserved one costs a push and a pop in the prologue of whichever function ends up owning it.

Every register named here is a register of the file the same target describes, and each list is in the order the convention uses them, so the fourth integer argument is int_args[3] and nothing has to count.

Fields§

§abi: &'static AbiDescription

The ABI these registers are the register half of.

The two halves of one convention: this structure says which register a value goes in and the description says what form it travels in on the way there, and a target that has one has the other. It is a link rather than a copy of the fact anybody wants, because a back end pass that writes a call to a runtime routine has to ask the same classification question a call in the program is asked and there is one place that answers it. A target’s registers and a target’s ABI are still chosen by two separate matches, one in crate::TargetInfo and one in rucc_abi::abis::for_target, and the crate’s test that walks every triple asking both is what holds them together.

§int_class: RegClass

The class the general purpose registers named here are in.

A register is a number inside its class, so a list of them says nothing about which registers they are without this. Everything else could get the class from the operand it came off, and a frame cannot, because a saved register is not an operand of anything.

§sse_class: RegClass

The class the vector registers named here are in.

§int_args: &'static [PhysReg]

The general purpose registers integer arguments arrive in, in order.

§sse_args: &'static [PhysReg]

The vector registers floating point arguments arrive in, in order.

Whether an argument’s position counts against both lists or only against its own is CallRegs::shared_positions.

§shared_positions: bool

Whether an argument’s position counts against both argument lists or only against its own.

False on SysV, which counts each separately, so a double after six integers is still in xmm0. True on Windows, which counts one position for both, so a double in the third position is in xmm2 and r8 is skipped.

§int_returns: &'static [PhysReg]

The general purpose registers an integer return value comes back in.

§sse_returns: &'static [PhysReg]

The vector registers a floating point return value comes back in.

§x87_returns: &'static [PhysReg]

The x87 registers a long double comes back in, which is empty on a target whose long double is a double.

§int_saved: &'static [PhysReg]

The general purpose registers a call leaves alone, so a value in one survives it.

§sse_saved: &'static [PhysReg]

The vector registers a call leaves alone, which is none of them on SysV.

§int_order: &'static [PhysReg]

The general purpose registers the allocator may hand out, in the order it prefers them.

The stack pointer is never in this list, and neither is the frame pointer, which a target could allocate when nothing needs a frame and which nothing here does yet.

§sse_order: &'static [PhysReg]

The vector registers the allocator may hand out, in the order it prefers them.

§stack_pointer: PhysReg

The stack pointer.

§frame_pointer: PhysReg

The frame pointer, which is the register a prologue puts the old stack pointer in.

§late_frame_pointer: bool

Whether the prologue points the frame pointer at the frame after taking it rather than before taking it.

False everywhere but Windows, and there it is the unwind table asking rather than a preference. The record a function carries on that platform counts every slot in it from where the stack pointer ends the prologue, and it finds that place by taking a constant off the frame pointer, so a register pushed after the pointer was established is below the place the record counts from and has no row the format can write. The order that does work is the pushes, then the frame, and only then the pointer, which is what Microsoft’s compiler emits and what gcc emits for this target in every function that saves anything besides the pointer itself.

What it costs is the chain: the frame pointer holds a copy of the body’s stack pointer rather than the address of the caller’s copy of itself, so the words that used to lead from one frame to the next no longer do. Nothing on this platform walks that chain. Unwinding reads the table, there is no profiler’s hook that reads the pointer, and gcc gives the chain up on the same functions for the same reason.

§vector_count: Option<PhysReg>

Where a variadic call says how many vector registers it passed arguments in, when the convention makes it say.

SysV puts the count in al and a variadic callee reads it to decide whether to save the vector argument registers at all, which is what makes a call to printf with no floating point argument cheap.

§red_zone: u32

How many bytes below the stack pointer a leaf function may use without moving it.

A hundred and twenty eight on SysV and nothing on Windows. It is nothing in kernel code on either, because an interrupt handler runs on the interrupted stack and writes over exactly this, which is what -mno-red-zone is for.

§shadow: u32

How many bytes a caller reserves below the call for the callee to spill its register arguments into, which is thirty two on Windows and nothing on SysV.

§stack_align: u32

What the stack pointer has to be a multiple of at the instruction that makes a call.

Sixteen on every convention here, and it is a real obligation rather than a preference, because a callee is entitled to use an aligned vector store on its own frame and gets a fault rather than a wrong answer when a caller got this wrong.

§return_address: u32

How many bytes the call instruction itself pushes before the callee starts running.

Eight on x86-64, where the return address is on the stack, and nothing on a machine that leaves it in a register. It is what makes the stack pointer misaligned on entry by exactly one word, which every frame layout has to undo.

§word: u32

How many bytes one general purpose register takes when it is saved on the stack.

§dwarf: &'static [&'static [u16]]

What DWARF calls each register, one list per class in the order the file numbers the classes, and inside a list in the order the class numbers its registers.

The two numberings are a real difference and not a formality. On x86-64 the machine puts rcx at one and DWARF puts rdx there, so a table written with the machine’s numbers is well formed and describes the wrong registers, which is a backtrace with plausible nonsense in it rather than an error. Shorter than the file when the classes at the end are ones DWARF has no column for, and empty on a target nobody has written this down for yet.

§dwarf_return_address: u16

The column an unwind table files the return address under.

Not a register on x86-64, where it is sixteen and rip is not a register anything can name, and a real one on a machine that returns through a link register.

§guard: Option<Guard>

Where the word a stack protector’s canary is copied from lives, on a convention that has somewhere to put one.

Here rather than beside the frame instructions because it is a fact about the runtime the code is linked against rather than about the machine. The two x86-64 conventions share every instruction the check is made of and disagree about this.

§trace: Option<Trace>

What a profiler’s hook is called on this platform, on one that has one.

Here for the same reason CallRegs::guard is: the names are the runtime’s rather than the machine’s, and the two x86-64 conventions write the same call instruction and disagree about what goes in it.

§chkstk: Option<Chkstk>

What this platform calls the routine a prologue reaches the pages of a large frame with, on one where reaching them is the convention.

Here for the same reason the two above are, and it is the clearest case of the three: the walk itself is written out of the machine’s own instructions and both x86-64 conventions have them, and what the two disagree about is whether a frame may be taken in one step at all. A prologue that leaves this out on Windows writes a function that faults on its own locals.

Implementations§

Source§

impl CallRegs

Source

pub fn dwarf(&self, class: RegClass, reg: PhysReg) -> Option<u16>

The number DWARF gives that register, or None for one it has no column for.

The x87 stack is the case that answers None on x86-64, and it is not an omission: a register whose name means whichever one is on top of the stack is not one a table can have a column for. Nothing saves one across a call either, so nothing ever asks.

Source

pub fn machine(&self, class: RegClass, dwarf: u16) -> Option<PhysReg>

Which register of that class DWARF gave that number to, which is Self::dwarf the other way round, and None for a number no register of the class holds.

What asks is a table written in the machine’s own numbering rather than DWARF’s, which is what Windows unwinds from. A row a prologue produces carries DWARF’s number, because that is what the format two of the three platforms want is written in, and the machine’s number is this register’s place in its class. The two disagree over the first eight general purpose registers on x86-64 and nowhere else, which is the worst shape a disagreement can have: every number is a register either way, so the table comes out well formed and about the wrong registers.

Source

pub fn preserves_int(&self, reg: PhysReg) -> bool

Whether a call preserves that general purpose register.

Source

pub fn preserves_sse(&self, reg: PhysReg) -> bool

Whether a call preserves that vector register.

Trait Implementations§

Source§

impl Clone for CallRegs

Source§

fn clone(&self) -> Self

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 Copy for CallRegs

Source§

impl Debug for CallRegs

Source§

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

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

impl Eq for CallRegs

Source§

impl PartialEq for CallRegs

Source§

fn eq(&self, other: &Self) -> 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 CallRegs

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.