Sign in Join
Wiki copytemporary

The register allocator: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11back to contents [[FPC internals]]
22
33## Introduction
44
55Main unit for the register allocation is rgobj.pas Main class for the register allocation is TRgObj located in rgobj.pas
66
77Register allocator provides imaginary registers for the assembler instructions during the code generation. Then it calculates the real registers to replace the imaginary ones.
88
99The FPC register allocator uses Register coloring to determine the real registers. It also uses a register spilling technique - when there is not enough registers it uses memory.
1010
1111## How to use the register allocator
1212
1313This topic describes how to use the register allocator during the code generation. Described like a black box with the public methods you can call to get job done.
1414
1515### Creating the Register allocator
1616
1717Creating register allocator is the first step we make before we can use its functionality.
1818
1919The low level code generator creates several instances of the TRgObj class. Normally this happens in:
2020
21-`tcg.init_register_allocators`
21+```text
22+tcg.init_register_allocators
23+```
2224
2325And the instances are normally freed in:
2426
25-`tcg.done_register_allocators`
27+```text
28+tcg.done_register_allocators
29+```
2630
2731Each TRgObj instance allocates registers of a certain type. For example, one register type is Integer registers. Another one is floating point (FPU) registers. That's why we have a few TRgObj instances, one for every type of register that the cpu supports.
2832
2933They are created when the code generation of specific routine begins. Code generator works on subroutine level. The register allocator also allocates registers for specific method, procedure, function.
3034
3135### Using registers in the code generation
3236
3337To allocate a register use the following method:
3438
35-`TRgObj.getRegister`
39+```text
40+TRgObj.getRegister
41+```
3642
3743for the appropriate register allocator instance (depending on the register type) to get register for some assembler instruction. This way an **imaginary** register is allocated and can be used after that in a specific assembler instruction.
3844
3945The code generator calls it in the following functions:
4046
41-`tcg.GetIntRegister`\
42-`tcg.GetAddressRegister`\
43-`tcg.GetFPURegister`\
44-`tcg.GetMMRegister`
47+```text
48+tcg.GetIntRegister
49+tcg.GetAddressRegister
50+tcg.GetFPURegister
51+tcg.GetMMRegister
52+```
4553
4654Additionally, the following methods:
4755
48-`TRgObj.getCpuRegister`\
49-`TRgObj.ungetCpuRegister`\
50-`TRgObj.allocCpuRegisters`\
51-`TRgObj.deallocCpuRegisters`
56+```text
57+TRgObj.getCpuRegister
58+TRgObj.ungetCpuRegister
59+TRgObj.allocCpuRegisters
60+TRgObj.deallocCpuRegisters
61+```
5262
5363allocate or free one or multiple **real** registers (not imaginary ones!) This is usually used for instructions that require a specific register (for example SHL/SHR/SAR on x86, which always uses ECX). This is also used when doing a function call to indicate that certain registers (which depend on the calling convention) may be destroyed by the called function.
5464
5565After allocating the register we can use it in some assembler instructions. For every instruction that generates, the code generator notifies the register allocator. It passes the instruction, also the imaginary register as parameters to the following method.
5666
57-`         TRgObj.add_reg_instruction(instr, r, cg.executionweight);`
67+```text
68+ TRgObj.add_reg_instruction(instr, r, cg.executionweight);
69+```
5870
5971But for MOV instruction there is specific method that is used
6072
61-`         TRgObj.add_move_instruction(instr:Taicpu);`
73+```text
74+ TRgObj.add_move_instruction(instr:Taicpu);
75+```
6276
6377Also weight is provided to determine which register should be spilled and which is better to use real cpu register.
6478
6579### Generating of real registers
6680
6781At the end when all the assembler instructions are generated we call
6882
69-`       do_register_allocation(list: TAsmList; headerTai: TAi)`
83+```text
84+ do_register_allocation(list: TAsmList; headerTai: TAi)
85+```
7086
7187It calculates real registers for the imaginary ones.
7288
7389Some instructions can use only certain types of registers. So, the register allocator can not choose from all the Cpu registers. For these instructions we notify the register allocator by calling the method:
7490
75-`TRgObj.add_Edge`
91+```text
92+TRgObj.add_Edge
93+```
7694
7795These interferences are build during the register allocation. Every descendent that wants to add some interferences overrides the method
7896
7997``` pascal
8098procedure TRgObj.add_cpu_interferences(p: TAi); virtual;
8199```
82100
83101We have descendents of TRgObj for the different kind of processors. They call add_edge in add_cpu_interferences depending on the specific CPU architecture. It is used for Arm processors, x86 processors etc.
84102
85103In method tcgprocinfo.generate_code which is located in unit psub.pas is implemented the creation of register allocators, getting the registers, generation of real registers. This method is central place for code generation which includes the register allocation. Inside it you can find call to hlcg.init_register_allocators to create the Register Allocators call to cg.do_register_allocation(aktproccode,headertai); - to do the actual allocation also to hlcg.done_register_allocators; - to free the allocators
86104
87105## Tips
88106
89107You can compile your project by using the -sr switch. This will leave the immaginary register names in the generated .s file
90108
91109## Calls hierarchy for the public methods
92110
93111``` pascal
94112Public
95113
96114constructor
97115
98116destructor
99117
100118do_register_allocation - level 1 is ordered by calling
101119 insert_regalloc_info_all
102120 generate_interference_graph
103121 add_edges_used(1, 2) get_alias add_edge
104122 add_edge ibitmap.s
105123 prepare_colouring
106124 make_work_list ri_coalesced
107125 sort_simplify_worklist
108126 colour_registers
109127 simplify ri_coalesced
110128 decrement_degree ri_coalesced
111129 coalesce get_alias simplifyworklist.add(v); ibitmap
112130 conservative ri_coalesced
113131 adjacent_ok ri_coalesced ibitmap
114132 add_worklist simplifyworklist.add(u);
115133 combine ibitmap, add_edge
116134 enable_moves
117135 decrement_degree ri_coalesced simplifyworklist.add(m)
118136 add_edge ibitmap.s
119137 freeze
120138 freeze_moves(, 2) get_alias(3)
121139 select_spill
122140 freeze_moves(, 2) get_alias(3)
123141 assign_colours(, 2) get_alias
124142 epilogue_colouring Destroys the objects used during the coloring - worklist_moves, active_moves, frozen_moves, coalesced_moves, constrained_moves, reginfo.movelist
125143 spill_registers
126144 clear_interferences ibitmap.s
127145 instr_spill_register get_alias
128146 getregisterinline
129147 add_edges_used(2, 2) get_alias
130148 add_edge ibitmap.s
131149 ungetregisterinline
132150 get_spill_subreg
133151 do_spill_replace
134152 do_spill_read
135153 do_spill_written
136154 translate_registers
137155 assign_colours(, 2) get_alias
138156
139157getregister
140158
141159add_move_instruction
142160 add_to_movelist
143161
144162combine ri_coalesced(s)
145163 enable_moves
146164 decrement_degree ri_coalesced
147165
148166-----------------------------------------
149167Properties
150168
151169live_range_direction
152170 set_live_range_direction
153171
154172live_start
155173 get_live_start
156174 set_live_start
157175
158176live_end
159177 get_live_end
160178 set_live_end
161179```
162180
163181## Class TRgObj
164182
165183### Description
166184
167185Main class for register allocation in FPC
168186
169187### Public methods
170188
171189### Protected methods
172190
173191### Private methods
174192
175193## Imaginary registers
176194
177195### Storing imaginary registers
178196
179197TRgObj stores the imaginary registers in list of TRegInfo structure.
180198
181199### Life of imaginary register
182200
183201Life of imaginary register starts when it is first used by assembler instruction. When it first appears in an assembler instruction in the assembler list for the specific routine. And ends when it is last used in an assembler instruction. In the TRegInfo structure for the specific imaginary register there are 2 fields for the life of the register. live_start: TAi; indicates which is the assembler instruction where the register is first used. live_end: TAi; indicates which is the assembler instruction where the register is last used.
184202
185203## Interferences
186204
187205Interferences
188206
189207Call hierarchy
190208
191-` generate_interference_graph`\
192-`   add_edges_used(1, 2)            get_alias                       add_edge`\
193-`     add_edge                                              ibitmap.s`
209+```text
210+ generate_interference_graph
211+ add_edges_used(1, 2) get_alias add_edge
212+ add_edge ibitmap.s
213+```
194214
195215### generate_interference_graph
196216
197217This method iterates the assembler list. Looking for reg_alloc instruction, with the proper regType.
198218
199219During the iteration we keep all register that are live in list - live_registers. if its allocating it adds the superregister to the live_registers. if its dealloc it removes it from the live_registers.
200220
201221After adding or removing in live_registers it calls add_edges_used
202222
203223### add_edges_used
204224
205225Iterates all live_registers and calls add_edge it gets the alias of the current live_register. and the super register u that is passed to the method.
206226
207227### add_edge
208228
209229When we add edge we keep information on 2 places. One is in the interferenceBitmap And the other is in the adjList of the register information in
210230
211231``` pascal
212232reginfo[u].adjList.add(v);
213233reginfo[v].adjList.add(u);
214234ibitmap[v, u] := true;
215235ibitmap[u, v] := true;
216236```
217237
218238In the relation between these 2 registers there is no dependant. There is no primary one. They are both equal in it.
219239
220240In reginfo you access the information by index. And in bitmap you do the same. This is prity much the same as speed. Why we have this duplicate information on 2 places.
221241
222242Interferences work with super registers.
223243
224244## Backgrounds
225245
226246### Grouping the instructions
227247
228248More abstract look at the CPU instructions shows us that we have mainly few kinds of instructions. - The first group contains instructions that changes some data. This data remains on the same place but it is changed. Also do not need extra data ot perform the change Some instructions like these are inc, dec, CLC, CLD
229249
230250\- Second group are instructions that move date from one place to another. Such as MOV, LAHF, POP, PUSH, LEA
231251
232252\- Third group of instructions are those to compare data. They do not change the data. just check it. Such instructions are TEST, COMPARE
233253
234254\- Fourth group of instructions are those who change the flow of the execution of the instructions such as: JMP, Jcc, JCXZ,
235255
236256\- Fifth group are instructions that changes data based on other data like ADD
237257
238258Another grouping of instructions could be by the cpu version where the instruction first appear.
239259
240260Another grouping of instructions could be based on the registers that can be used in the instruction. Some instructions can use only specific registers for the address - mov ax, \[bx + si + 1000\] You can choose between for example only these registers - BP, BX, SI and DI. Some instructions can use only registers, some addresses, some both. Some registers have subregisters some do not.
241261
242262### Data in the instructions
243263
244264Almost all of the instructions operate on some data. Also uses some data to perform operation. The data can be in the cpu registers, memory, cpu flags.
245265
246266### Programming using the instructions
247267
248268The instructions are independant one from other. However when a program is created there are some logical dependencies that programmer introduce. Programmer wants a few modifications to be performed on some data. Which can be performed by few instructions executed one after another. And at the end the needed data will be produced. But data is not exactly Cpu register, or memory address. Data is something the the programmer cares about. For example, we have some starting data that equals 1000. we can put it in cpu register we want to add to it 200. We can use Asm instruction add, but then we can move the value that we reached in another register. And then to increment it by 300 at the end we will have the needed value 1500. But we used two registers for one date. Normally we don't have to use two registers. We can do it using one. Cause we have to move the data to second register somewhere in the middle. And this is extra instruction. However more than one data is parallel processed. And sometimes we can use this extra move. Just to speed up executions on other data. This is normally needed when to datas are produced that will calculate third one.
249269
250270This is not very important for the register allocator. It is more dedicated to the code generator. Code generator could decide where to add extra instruction, to speed other executions. It look like register allocator should not care about this. But it's good to thing about it.
251271
252272### Object Design guidelines
253273
254274In the current implementation the getting of the registers and the allocation are mixed together. Also they share same structures, same class. But the data for each of them is used on the completely different phases. Mixing them togather brings more code on one place. Also its getting difficult to separate them when you work on them. They can be easily devided without lost of speed. Also different types of register allocations can be used. The getting of the registers and setting the weights, also life … can be on one place. But at the end different allocators can process this data. It is difficult for further develop the register allocation cause many phases and logic was brought together on one place.
255275
256276In the
257277
258278``` pascal
259279TRgObj.colour_registers
260280```
261281
262282method different approach can be performed. Entering different states can be implemented. Instead checking the counts of all lists. It will be faster. Clear code. Easy for debugging. Easy to develop further.
263283
264284Improvements can be made in the weights of the instructions. Or in the way the allocator decides which register to spill and which not.
265285
266286---
267287*From the Free Pascal wiki, page "The register allocator", as it stood on 2020-12-28 after a change by E-ric, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history