Expand description
The selector: an IR function becomes a machine IR function.
Design: spec/10-backend.md sections 10.2 and 10.3.
What the matcher in crate::select does is answer one question about one term. What this
does is ask it: walk a function, decide which terms are worth asking about, and build machine
instructions out of what comes back. Nothing here decides what an IR term lowers to. That is
in rules/x86-64.rules and it is proved before it is used, which is the whole point of the
arrangement and the reason this file is short.
§What it does with an instruction
It tries the ways the instruction can be shown to the matcher, in order, and takes the first
that a rule fires on. crate::term is what a way of showing one is, and the order is the
most specific first: an operand that is a constant is offered as a constant before it is
offered as a register, and an operand computed by an instruction of its own is offered as
that instruction before it is offered as a register. A rule that wants an immediate too wide
for the machine has a guard that turns it down, and the search carries on to the way of
showing it that puts the constant in a register, which is the right answer and is one nobody
had to write down.
A constant is not lowered where it is written. It is materialized where a register for it is first wanted, which is what keeps a constant that every use folded into an immediate from leaving a dead instruction behind, and it also gives the value the shortest live range it could have. The instruction that materializes it comes from the rule set like everything else.
§What it does not do yet
Everything is in the general purpose registers, because every rule in the set is about an
integer, so a call that passes a double and a function that returns one are both reported
rather than lowered. So is an argument that travels on the stack, on either side of a call,
and so is a call through an address rather than to a name.
§A call
Not a rule, because a rule pattern sees one term and what a call’s operands are is whatever
the signature made them. crate::abi builds one instead, out of the same description of the
convention the arguments come from: the values it passes are reads constrained to the
registers the convention places them in, what comes back is a write constrained to the
register it comes back in, and every other register the callee is free to destroy is a write
of that register and nothing else, which is all the allocator needs to keep a value out of it.
What that costs the frame is an argument area, and nothing after selection could work out how big, so the size of the widest call is given back with the function. A function that makes no call at all is a leaf, and a leaf is the function that may use the red zone.
§Where a block goes
On the block, which is what machine IR does with an edge and is why the branches need no more rule language than the arithmetic did. A rule never names a block, so an unconditional jump has no rule at all and a conditional branch has one that is about its condition and nothing else. The arms are copied across after the block is filled, arguments and all, because an argument that is a constant is materialized where a register for it is first wanted and the end of the block is where an edge wants it.
What this leaves behind is a function whose blocks are in the order the IR held them and whose
branches are still branches on a register. Turning one into a test and a jcc is the block
layout’s, since which of the two arms falls through is the layout’s answer, and crate::split
has to run before allocation so that every edge carrying a value has somewhere to put it.
A store and a return are the two things here that write no register. A store is emitted like
everything else and the only difference is that there is no result to put anywhere, so the
operands the target describes are all reads. A return is the same, and what it is for is its
one operand: the target constrains it to the register the caller reads the value out of, and
the allocator is what gets it there. The instruction that leaves is not chosen here at all,
because the epilogue has to give the frame back first and crate::finish writes that after
allocation, so a return of nothing is lowered to nothing.
The entry block is the one block whose parameters are not block parameters here. They are the
function’s arguments, they are already somewhere when it starts, and crate::abi is what
says where. An argument that arrives on the stack is reported rather than read, because where
the stack put it is a distance into a frame and no frame exists until after allocation.
Blocks are walked in the order the function holds them and a value is expected to be defined before it is used, which is true of the IR this is given because every pass before it keeps definitions ahead of uses.
Structs§
- Lowered
- A lowered function, and what the frame needs that the machine IR does not hold.
- Stack
- What a function’s stack has to hold, as far as selection is able to say.
Enums§
- Unsupported
- Why a function could not be lowered.
Functions§
- func
- The x86-64 machine IR for that function.