CAP/MiniC/Lib/Allocator.py

80 lines
3.0 KiB
Python
Raw Normal View History

2024-10-06 19:58:11 +02:00
"""
This file defines the base class :py:class:`Allocator`
and the naïve implementation :py:class:`NaiveAllocator`.
"""
from Lib.Operands import Temporary, Operand, DataLocation, GP_REGS
from Lib.Statement import Instruction
from Lib.Errors import AllocationError
from Lib.FunctionData import FunctionData
from typing import Dict, List
class Allocator():
"""General base class for Naive, AllInMem and Smart Allocators.
Replace all temporaries in the code with actual data locations.
Allocation is done in two steps:
- First, :py:meth:`prepare` is responsible for calling
:py:meth:`Lib.Operands.TemporaryPool.set_temp_allocation`
with a mapping from temporaries to where they should actually be stored
(in registers or in memory).
- Then, :py:meth:`replace` is called for each instruction in order to
replace the temporary operands with the previously assigned locations
(and possibly add some instructions before or after).
Concretely, it returns a list of instructions that should replace the original
instruction. The actual iteration over all the instructions is handled transparently
by :py:meth:`Lib.LinearCode.LinearCode.iter_statements`.
"""
_fdata: FunctionData
def __init__(self, fdata: FunctionData):
self._fdata = fdata
def prepare(self) -> None: # pragma: no cover
pass
def replace(self, old_instr: Instruction) -> List[Instruction]:
"""Transform an instruction with temporaries into a list of instructions."""
return [old_instr]
def rewriteCode(self, listcode) -> None:
"""Modify the code to replace temporaries with
registers or memory locations.
"""
listcode.iter_statements(self.replace)
class NaiveAllocator(Allocator):
"""Naive Allocator: try to assign a register to each temporary,
fails if there are more temporaries than registers.
"""
def replace(self, old_instr: Instruction) -> List[Instruction]:
"""Replace Temporary operands with the corresponding allocated Register."""
new_args: List[Operand] = []
for arg in old_instr.args():
if isinstance(arg, Temporary):
new_args.append(arg.get_alloced_loc())
else:
new_args.append(arg)
new_instr = old_instr.with_args(new_args)
return [new_instr]
def prepare(self) -> None:
"""Allocate all temporaries to registers.
Fail if there are too many temporaries."""
regs = list(GP_REGS) # Get a writable copy
temp_allocation: Dict[Temporary, DataLocation] = dict()
for tmp in self._fdata._pool.get_all_temps():
try:
reg = regs.pop()
except IndexError:
raise AllocationError(
"Too many temporaries ({}) for the naive allocation, sorry."
.format(len(self._fdata._pool.get_all_temps())))
temp_allocation[tmp] = reg
self._fdata._pool.set_temp_allocation(temp_allocation)