97 lines
4.3 KiB
Python
97 lines
4.3 KiB
Python
import copy
|
|
|
|
from ScEpTIC.AST.register_allocation.linear_scan.linear_scan import LinearScanRegisterAllocator
|
|
|
|
def allocate_registers(functions, registers_number, config):
|
|
"""
|
|
This functions performs the register allocation on the whole code (analysis, pre-processing, register allocation, and post-processing)
|
|
"""
|
|
|
|
# perform register allocation on each function
|
|
for function_name in functions:
|
|
func = functions[function_name]
|
|
|
|
# set the register allocator and run it
|
|
func.register_allocator = LinearScanRegisterAllocator(func, registers_number, config.reg_prefix, config.spill_prefix, config.spill_type)
|
|
func.register_allocator.run_register_allocation()
|
|
|
|
# analyze register usage, to support call_post_processing
|
|
compute_registers_usage(functions, registers_number)
|
|
|
|
# do post-processing of functions calls. (insert register saving/restoring operations and set their tick_count)
|
|
for function_name in functions:
|
|
func = functions[function_name]
|
|
func.register_allocator.do_call_post_processing()
|
|
|
|
|
|
def compute_registers_usage(functions, registers_number):
|
|
"""
|
|
This function estimates the maximum register usage of each code's function and is used to
|
|
estimate the actual number of operation performed for saving and restoring registers before
|
|
a function call.
|
|
Register allocation must be performed before running this function, so to have the number of registers used.
|
|
|
|
A good partitioned register allocation will make registers from frequent functions call to not overlap, so to minimize
|
|
the register saving operations before a function call.
|
|
|
|
To estimate this scenario, is sufficient to find the number of registers used by each function (including the ones from the
|
|
calls present in such function).
|
|
|
|
The number of registers needed by a function is computed as:
|
|
max(registers_used_by_internal_calls) + registers_used_by_function
|
|
NB: recursive calls are ignored, since they will save the number of registers used by the current function.
|
|
|
|
If this number is lower than the number of available registers, no saving is needed before the call.
|
|
|
|
The overall number of register usage is calculated iteratively, since cycles can be possible (a calls b; b calls a;)
|
|
If the number of needed registers exceed the number of available ones, a cycle-dependecy is found and thus all registers should be
|
|
saved before such call.
|
|
|
|
Initial setup: each function uses reg_count as max_reg_usage, calls is initialized with the functions called
|
|
|
|
Iterate:
|
|
find the max_reg_usage among the functions in calls
|
|
set max_reg_usage = max_reg_usage of function calls + reg_count
|
|
if max_reg_usage > max_available_registers -> max_reg_usage = max_available_registers
|
|
|
|
until a fixed point is reached
|
|
|
|
Once the max_reg_usage is found, call post-processing can be done (addition of registers saving/restoring routines)
|
|
"""
|
|
|
|
function_data = {}
|
|
old_function_data = {}
|
|
|
|
# initialization step
|
|
for function_name in functions:
|
|
func = functions[function_name]
|
|
function_data[function_name] = {'reg_count': func.reg_count, 'calls': func._calls, 'max_reg_usage': func.reg_count}
|
|
|
|
# iterate until a fixed point is reached
|
|
while function_data != old_function_data:
|
|
old_function_data = copy.deepcopy(function_data)
|
|
|
|
# update each value
|
|
for name in function_data:
|
|
f_data = function_data[name]
|
|
|
|
# if the considered function exceed registers_number, skip (computation already done)
|
|
if len(f_data['calls']) == 0 or f_data['max_reg_usage'] >= registers_number:
|
|
continue
|
|
|
|
# find max reg usage for called functions and compute current max_reg_usage
|
|
calls = []
|
|
|
|
for sub in f_data['calls']:
|
|
calls.append(function_data[sub]['max_reg_usage'])
|
|
|
|
max_reg_usage = max(calls) + f_data['reg_count']
|
|
|
|
# limit max_reg_usage to registers_number
|
|
f_data['max_reg_usage'] = min(max_reg_usage, registers_number)
|
|
|
|
# update functions with final results.
|
|
for function_name in functions:
|
|
func = functions[function_name]
|
|
func.max_reg_usage = function_data[function_name]['max_reg_usage']
|