Skip to main content

Module widths

Module widths 

Source
Expand description

The integer widths the machine has, for the ones a program wrote that it does not.

Design: spec/08-ir.md section 8.2 and spec/10-backend.md section 10.2.

The IR has an integer of any width, because C does: _BitInt(40) is forty bits of value and unsigned long long b:40 is a bit-field whose arithmetic happens at forty bits, and an IR that rounded either of those up to sixty four would have thrown away the thing that makes them different from a long long. A machine has four integer widths and forty is not one of them. This is where the gap is closed.

Every value of a width the machine has no register for is put into the narrowest one it does, which is the width rounded up to a byte and then to a power of two, and that is the same width the type’s own layout already has: a _BitInt(40) object is eight bytes, so nothing here changes how wide a load or a store is against the object it reads.

§What the spare bits hold

Nothing, and that is the decision the rest of this file follows from. A forty bit value in a sixty four bit register has twenty four bits above it, and this pass does not say what is in them. The alternative is to keep the value extended and fix up every instruction that produces one, and it costs more: an add, a subtract, a multiply, a shift left and the three bitwise operations all give the right low forty bits whatever is above them, so an invariant would pay for a mask after each of those to buy a mask before the few that need one.

What needs one is every instruction that reads a bit the narrow value does not have. A divide, a remainder, a shift right and a comparison each look at the whole register, so each gets its operands put into shape first, with the sign spread for the signed ones and the spare bits cleared for the unsigned ones, which is the same distinction the opcode already carries. A widening reads the value it widens, so it becomes the shaping itself when the two widths land in the same register. A store writes the spare bits into the object’s padding, and they are cleared first so that the same program run twice writes the same bytes, which C leaves unspecified and a compiler should not.

A shift count is shaped as well, which reads like an oddity and is not. The count has the type of the value being shifted, so a shift by a forty bit count is a count with twenty four spare bits in it, and the machine reads the low five or six bits of whatever register it is handed. A count that is a constant is already in range and is left alone, which is what every shift a C program writes at these widths turns out to be.

§What it does not do

A function whose signature has one of these widths in it, a call that passes or returns one, and anything else that touches one is left exactly as it was, and the selector then refuses the function by name the way it does today. The reason is the boundary rather than the arithmetic: the psABI says a _BitInt(40) argument arrives extended, and which extension it is depends on whether the type was signed, which is a fact the IR deliberately does not carry because the signedness of an integer lives on the operation there and not on the type. That belongs in the ABI lowering, where the C type is still in hand. tamnd/rucc#425 is the issue for it.

Functions§

integers
Puts every integer of a width the machine has no register for into the width that holds it.