This is an old revision of the document!
Starting with the SSA form for each result of an SSA instruction a register has to be assigned. However, it must be decided how the registers of a processor will be used generally.
Registers can be classified as dedicated, volatile or nonvolatile. Dedicated are register with a special purpose, such as the stackpointer. Volatile means, that this register might be used any time. It does not have to be stored when another method is called. Nonvolatile registers must be saved in the prologue of a method before they can be used and after use they must be restored. The number of volatile and nonvolatile registers must be equal for all methods.
Register | State | Use |
R0 | volatile | method prologue, scratch register |
R1 | dedicated | stack pointer |
R2 | volatile | 1. parameter, return value |
R3 | volatile | 2. parameter, return value (if long, lower 4 bytes of long) |
R4-R10 | volatile | further parameters |
R11-R12 | volatile | local variables, working registers |
R13-R31 | nonvolatile | local variables, working registers |
F0 | volatile | working register |
F1 | volatile | 1. parameter, return value |
F2-F8 | volatile | further parameters |
F9-F12 | volatile | local variables, working registers |
F13-F19 | nonvolatile | local variables, working registers |
F20-F22 | volatile | utility register for compiler specific subroutines |
F23-F31 | nonvolatile | local variables, working registers |
Local variables are assigned volatile registers. However, if the live range of an SSA value incorporates a method call, a nonvolatile register has to be used. These will be assigned in decreasing order from R31/F31. As working registers volatile registers are used. If there are not enough of them, nonvolatiles (just below the locals) are assigned.
Optimization: If a method is a leaf method, it would not be necessary to copy the parameters from their volatile registers into nonvolatile registers.
For the translation of certain SSA instructions (e.g. of type long) further auxiliary registers are needed. They are assigned and reserved by the register allocator as well. If FPR's are needed for auxiliary registers, they are not reserved but F20..F22 will be used. They are exclusively used in compiler specific subroutines and never need saving.
Parameters are passes in R2..R10 and F1..F8.
In the interface cgPPC/Registers you can find the definitions about which registers are used for volatiles and nonvolatiles and which are used for parameter passing. Important: the number of parameter registers must be smaller or equal than the number of volatiles.
We determine for the results of all SSA instructions up to which instruction they are used (field end). Results of phi-functions must be handled separately. Such results might be used as operands of instructions which are placed further upstream. For this reason the live range of a phi-function does not start with its instruction but with its first use of an operand or the phi-function itself. Hence, phi-function need a field start.
Before beeing able to assign registers we have to decide whether to use a volatile or nonvolatile register. As soon there is a call to a method within the live range of a value (holds also for phi-functions) then this value must be assigned a nonvolatile Register. Other values are assigned a volatile register. The same is true for exceptions. If the live range of a variable extends into (or over) a catch block we must assign a nonvolatile register, as the handling of the exception destroys volatiles.
A special case is the SSA instruction sCloadLocal. With this we must ckeck if a method call occurs between the start of the method to the end of the live range. If this is not the case we can leave the parameter in the the register where it was passed, which is a volatile register already.
Parameters are passed in volatile registers. Some of them have to be copied to nonvolatile registers. Some parameters might not be used at all in a method. This can be determined in the exit set of the last node. If a value is null in that set the parameter with this index was never loaded and hence no register must be reserved. The following example demonstrates parameter usage.
float m2(long a, float b, double c, byte[] d, short e, int f, int g) { a = 0x7545 & a; e += 100; T08Calls.classMethod(d[2]); e = (short)(20 + e); g |= 0x223344; c = 3.2; int h = g - e; T08Calls.classMethod(h); short i = (short)h; return b; }
Variable | this | a | b | c | d | e | f | g |
Typ | ref | Long | Float | Double | ByteArray | Short | Integer | Integer |
Register Typ | vol | nonVol | vol | vol | vol | nonVol | ||
not used | x | x | ||||||
Register | R3,R4 | FR31 | FR2 | R5 | R6 | R31 |
Finally the results of all SSA instructions will be assigned a register.
As described above, phi-functions have a field last which is used to make sure that a registers of a phi-function lives to the very end of its loop. At the this end the register of the phi-function (which is actually given in its join value) could be released. However, at this stage we already passed the phi-function. For this purpose all phi-functions are referenced in a linked list (with the field next) in the first SSA instruction of the node following the node with the phi-function.
All sCloadLocal instructions use the same register as their parameter. In case of join != null, the referenced phi function gets the same register.
Some instruction need 1 or 2 additional (auxiliary) registers. These will be reserved and stored in regAux1 and regAux2. At the end of the instruction they can be released again. It's possible that the result of the SSA instruction occupies the same register as one of the auxiliary registers.
Registers of operands, whose live range expires, must be released. If they are of type Long, this step must happen at the very end.
The register pool will be searched for a free register for the result. The result type determines if 1 or 2 GPR's or a FPR must be reserved. The following two cases must be considered.
The same register must be assigned as the result of the phi function. If not handled yet the phi function has to be dealt with first. It can happen that several phi funtions occupy the same index in the state array:
switch(i) { case 0: return 0; case 1: i++; break; case 2: return 2; case 3: i += 3; case 4: i += 4; break; default: return -1; .. return i + 3;
In those cases all phi functions with the same index get the same register assigned.
If a constant can be used as immediate operand by the machine instruction no register has to be reserved. However, if the value shows up in the state array (index >= 0), it must be loaded into a register anyway. This is the case with the conditional operator. A register must be assigned also in case of join != null.
The result register can be released immediately if the live range already expires.