Expand description
The integer that is wider than a register, as the two registers it is held in.
__int128 is the one integer a C program on this machine writes that no register holds.
Everything else the front end produces is a width the machine has, or is a width
crate::widths rounds up into one, and neither of those is true here: there is nothing to
round up into above sixty four bits. What there is, is two registers, and the convention already
says so. System V classifies a __int128 as two eightbytes of class INTEGER, so it travels in a
pair of general purpose registers, comes back in the pair a return comes back in, and sits in
memory as two words with the low one first. That is what this pass writes down.
Every value a hundred and twenty eight bits wide becomes two values of sixty four, a low half and a high half, and every instruction over such a value becomes instructions over the halves. After it there is no value of that width left anywhere in the function, which is what lets the rest of the back end stay written about widths the machine has. Nothing below this knows the type existed.
§Why a pass and not a rule
A rule matches a term and rewrites it into instructions of the machine, and the selector works a
value at a time. There is no register a value this wide can be selected into, so there is
nothing for a rule to produce, and a rule that produced a pair would have to say which register
each half landed in, which is the allocator’s answer and not a rule’s. So the splitting happens
before selection, in the IR, where a value is still something a pass may make two of. That is
the same reasoning crate::widths follows from the other end, and the two are the two halves
of one sentence: nothing reaching the selector is at a width the machine has no register for.
§What crosses the boundary
A parameter and a return value are agreed with something this compilation is not looking at, so
splitting one is a claim about where the two halves are. The claim is true when both halves land
in registers, because the convention hands out argument registers in order and two halves in a
row take the two registers the whole value would have taken. It is not true when they do not: a
value the convention could not fit in registers travels in the argument area as sixteen bytes
aligned to sixteen, and two independent words travel as two words each aligned to eight, which
is a different place as soon as an odd number of words went before them. So a function whose
wide parameter would run out of registers is left exactly as it was and refused by name, the
same as a function this pass does not understand. tamnd/rucc#351 carries what passing one in
memory would take, which is a form of parameter the IR has no way to spell today.
§What it does not do yet
Dividing, which at this width is a call into the compiler runtime rather than arithmetic at all and waits on the runtime having the four entry points to call. A function that divides one of these is left alone here and refused by the selector, which is the same answer it got before this pass existed.
Functions§
- halves
- Splits every integer the machine holds in two registers into the two halves it holds it in.