Skip to main content

Module lower

Module lower 

Source
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.