Chapter 3 ILP Formulation for Register Reassignment MethodⅠ
3.4 Formulation
3.4.1 Objective Function
If all the register operands of an L-format instruction belong to small set registers, it can be represented by S-format instruction and thus the code size is reduced. However,
21
reassigning registers may also make program incorrect. Since the basic reassignment unit of our model is a function, it means that different functions may have different reassignment results. In other words, the results may violate the calling convention such that the program is incorrect. Therefore, we may need to insert extra code to make our program correct. However, code insertion counteracts the benefits of the register reassignment, i.e., it is a trade-off between the both. Hence, the objective function of our model is expressed as shown in Eq.
(1):
CRshort represents code size reduction and Reassignmentoverheads shows the extra code insertion for the calling convention.
A. CR
shortThe formulation of CRshort is listed as following:
The above is a nonlinear equation when the register operands of an instruction are larger and equal than two. For this kind of nonlinear boolean function, we can use linearization method to deal with it through replacing the nonlinear part of the function by new variables and adding new constraints to linearize this objective function [19]. The modified objective function after linearization is listed in the following.
22
The added constraints are listed as follows:
Register reassignment may violate the rule of calling convention since each function may have different reassignment results. Therefore, we may need to insert extra code to keep the correctness of the program. In our model, possible register reassignment overheads of each function can be computed in advance. To deal with the calling convention, we classify the general purpose registers into two categories according to whether the registers which will be preserved and not preserved across function call. These two categories are callee-saved registers and caller-saved registers. In addition, caller-saved registers can further be classified into temporary registers, argument registers and return value registers. Different solution of register reassignment would introduce different overheads. To explain the case of reassignment overheads analysis, a conceptual basic function layout after compiler code generation like Figure 3-4 is used. After compiler completing register allocation, the virtual registers will be mapped to the physical registers. Compiler will insert prolog code and epilog code to preserve and restore the callee-saved register used in current function, respectively.
23
Moreover, if the content of caller-saved registers would be used after a function call in current function, compiler will insert preserved code before the function call and retrieved code after function call. In a RISC processor, the calling convention define the argument registers or return value registers to pass parameters or receive the return values. For analyzing the reassignment overheads, those are important and affect the reassignment overheads significantly.
Figure 3-4 Function layouts after code generation
To compute reassignment overheads, we need to know the type of registers before and after reassignment, the function prototype and the liveness of each used register. In assembly code, function prototype information is very hard to retrieve or analyze. In this thesis, we implement our design in the LLVM compiler [22] to retrieve the necessary information such as function prototype. The other important information we need to know is live range of each used physical registers. To achieve this goal, we construct the control flow graph to compute
24
the live-in, live-out, define and use information of each instruction of the function to calculate to calculate liveness of each used register.
The variables used to check the register type before and after reassignment are defined as follows:
∙isCalleeToCallee(Rbi,Raj), Rbi, Raj RegisterSetA: a boolean function which returns whether
a callee-saved register is reassigned to a callee-saved register.
∙isCalleeToCaller(Rbi,Raj), Rbi, Raj RegisterSetA: a boolean function returns whether a
callee-saved register is reassigned to a caller-saved register.
∙isTempToCallee(Rbi,Raj), Rbi, Raj RegisterSetA: a boolean function returns whether a
temporary register is reassigned to a callee-saved register.
∙isTempToCaller(Rbi,Raj), Rbi, Raj RegisterSetA: a boolean function returns whether a
temporary register is reassigned to a caller-saved register.
∙isArgToCallee(Rbi,Raj), Rbi, Raj RegisterSetA: a boolean function returns whether an
argument register is reassigned to a callee-saved register.
∙isArgToCaller(Rbi,Raj), Rbi, Raj RegisterSetA: a boolean function returns whether an
argument register is reassigned to a caller-saved register.
∙isRetToCallee(Rbi,Raj), Rbi, Raj RegisterSetA: a boolean function returns whether a return
value register is reassigned to a callee-saved register.
25
∙isRetToCaller(Rbi,Raj), Rbi, Raj RegisterSetA: a boolean function returns whether a return
value register is reassigned to a caller-saved register.
The following definitions are the overheads for the reassignment of different types of registers:
∙CostofCalleetoCaller: overheads of reassignment callee-saved to caller-saved registers.
∙CostofCalleetoCallee: overheads of reassignment callee-saved to callee-saved registers.
∙CostofTemptoCallee: overheads of reassignment temporary to callee-saved registers.
∙CostofTemptoCaller: overheads of reassignment temporary to caller-saved registers.
∙CostofArgtoCallee: overheads of reassignment argument to callee-saved registers.
∙CostofArgtoCaller: overheads of reassignment argument to callee-saved registers.
∙CostofRettoCallee: overheads of reassignment return value to callee-saved registers.
∙CostofRettoCaller: overheads of reassignment return value to caller-saved registers.
Now we start out presenting the reassignment overheads computation. We will discuss each reassignment case by case.
Case 1: Reassignment of a callee-saved register.
For reassigning a callee-saved register to a callee-saved registers, we may modify the stack offset only since the callee-saved register has been preserved at the prolog and restored at epilog, respectively. The modification of code is shown in Figure 3-5.
26
Figure 3-5 An example of reassignment a callee-saved register to a callee-saved register For reassigning a callee-saved register to a caller-saved register, we need to remove the code for preserving and restoring callee-saved register at prologue and epilogue. Also, for each function call in the current function, if the live ranges of original callee-saved registers cross the function call, the preserved and restored code of the register before and after the function call should be inserted. The modification of code is shown in Figure 3-6.
Figure 3-6 An example of reassignment a callee-saved register to a caller-saved register
27 Case 2: reassignment of a temporary register.
For reassigning a temporary register to a callee-saved register, it needs to insert code at prologue and epilogue for preserving a callee-saved register. If a temporary register spill/reload the value before/after each function call, we need to remove the code. Figure 3-7 shows an example of a temporary register reassigned to callee-saved registers.
Figure 3-7 An example of reassignment a temporary register to a callee-saved register For reassigning a temporary register to a caller-saved register, if a temporary register has been preserved and retrieved before/after each function call, only the stack offset need to be modified and no insertion of code is required. Figure 3-8 shows the example of reassignment temporary registers to caller-saved registers.
28
Figure 3-8 An example of reassignment a temporary register to a caller-saved register Case 3: reassignment of an argument register
For reassigning an argument register to a callee-saved register, we need to insert code at prologue and epilogue for preserving and restoring the content of the callee-saved register and we also need to insert an instruction to move the argument of the current function to the reassigned register. If the argument register has been preserved and retrieved before and after each function call, it should be removed. For each function call, it also needs to check whether the argument register has been used for passing parameter to the called site. If it is used, a move instruction has to be inserted to move the value to the argument register. Figure 3-9 shows an example of reassignment an argument register to a callee-saved register.
29
Figure 3-9 An example of reassignment an argument register to a callee-saved register For reassigning an argument register to a caller-saved register, we have to insert move instructions for an argument passed into the current function and parameter passed to its function call. If the caller-saved register spills/reloads the value before/after function call, the stack offset should modified. Figure 3-10 shows an example of the reassignment of an argument register to a caller-saved register.
Figure 3-10 An example of reassignment an argument register to a caller-saved register Case 4: reassignment of a return value register
30
For reassigning a return value registers to a callee-saved register, the code insert is similar to that of an argument register except the place of code insertion. If the function has to return a value, an instruction needs to be inserted to move the return value from the reassigned register to the return value register. Another case is that for each function call in the function, if the called function has return value, it has to move the return value from the return value register to the reassigned register. Figure 3-11 shows the example of the reassignment of a return value register to a callee-saved register.
For reassigning a return value register to a caller-saved register, the code insertion policy is the same as we depicted in the previous paragraph. There is one thing different: if the return
Figure 3-11 An example of reassignment a return value register to a callee-saved register value register preserved/retrieved before/after each function call, it has to modify the stack offset to make sure that the value is spilled to the correct stack place. Figure 3-12 shows an
31
example of reassignment a return value register to a caller-saved register. The summary of the reassignment overheads of all possible cases is shown in Table 3-1.
Figure 3-12 An example of reassignment a return value register to a caller-saved register
32
1.May modify stack offset 1.Remove prologue/epilogue 2.Insert preserved Caller
33
According to the cases analysis described above, the equationof the reassignment
overheads is expressed as follows: Note that each overhead is all computed in advanced.
We describe the constraints for the method 1 in this subsection.
1. Registers usage constraints: