100 lines
4.1 KiB
Python
100 lines
4.1 KiB
Python
from typing import List, Dict
|
|
from Lib.Errors import MiniCInternalError
|
|
from Lib.Operands import Temporary, Operand, S, Offset, DataLocation, GP_REGS
|
|
from Lib.Statement import Instruction
|
|
from Lib.Allocator import Allocator
|
|
from Lib.FunctionData import FunctionData
|
|
from Lib import RiscV
|
|
from Lib.Graphes import Graph # For Graph coloring utility functions
|
|
|
|
|
|
class SmartAllocator(Allocator):
|
|
|
|
_igraph: Graph # interference graph
|
|
|
|
def __init__(self, fdata: FunctionData, basename: str, liveness,
|
|
debug=False, debug_graphs=False):
|
|
self._liveness = liveness
|
|
self._basename: str = basename
|
|
self._debug: bool = debug
|
|
self._debug_graphs: bool = debug_graphs
|
|
super().__init__(fdata)
|
|
|
|
def replace(self, old_instr: Instruction) -> List[Instruction]:
|
|
"""
|
|
Replace Temporary operands with the corresponding allocated
|
|
physical register (Register) OR memory location (Offset).
|
|
"""
|
|
before: List[Instruction] = []
|
|
after: List[Instruction] = []
|
|
new_args: List[Operand] = []
|
|
# TODO (lab5): Compute before, after, subst. This is similar to what
|
|
# TODO (lab5): replace from the Naive and AllInMem Allocators do (Lab 4).
|
|
raise NotImplementedError("Smart Replace (lab5)") # TODO
|
|
# And now return the new list!
|
|
instr = old_instr.with_args(new_args)
|
|
return before + [instr] + after
|
|
|
|
def prepare(self) -> None:
|
|
"""
|
|
Perform all preparatory steps related to smart register allocation:
|
|
|
|
- Dataflow analysis to compute the liveness range of each
|
|
temporary.
|
|
- Interference graph construction.
|
|
- Graph coloring.
|
|
- Associating temporaries with actual locations.
|
|
"""
|
|
# Liveness analysis
|
|
self._liveness.run()
|
|
# Interference graph
|
|
self.build_interference_graph()
|
|
if self._debug_graphs:
|
|
print("Printing the interference graph")
|
|
self._igraph.print_dot(self._basename + "interference.dot")
|
|
# Smart Allocation via graph coloring
|
|
self.smart_alloc()
|
|
|
|
def build_interference_graph(self) -> None:
|
|
"""
|
|
Build the interference graph (in self._igraph).
|
|
Vertices of the graph are temporaries,
|
|
and an edge exists between temporaries iff they are in conflict.
|
|
"""
|
|
self._igraph: Graph = Graph()
|
|
# Create a vertex for every temporary
|
|
# There may be temporaries the code does not use anymore,
|
|
# but it does not matter as they interfere with no one.
|
|
for v in self._fdata._pool.get_all_temps():
|
|
self._igraph.add_vertex(v)
|
|
# Iterate over self._liveness._liveout (dictionary containing all
|
|
# live out temporaries for each instruction), and for each conflict use
|
|
# self._igraph.add_edge((t1, t2)) to add the corresponding edge.
|
|
raise NotImplementedError("build_interference_graph (lab5)") # TODO
|
|
|
|
def smart_alloc(self) -> None:
|
|
"""
|
|
Allocates all temporaries via graph coloring.
|
|
Prints the colored graph if self._debug_graphs is True.
|
|
|
|
Precondition: the interference graph _igraph must have been built.
|
|
"""
|
|
# Checking the interference graph has been built
|
|
if not self._igraph:
|
|
raise MiniCInternalError("Empty interference graph in the Smart Allocator")
|
|
# Coloring of the interference graph
|
|
coloringreg: Dict[Temporary, int] = self._igraph.color()
|
|
if self._debug_graphs:
|
|
print("coloring = " + str(coloringreg))
|
|
self._igraph.print_dot(self._basename + "_colored.dot", coloringreg)
|
|
# Temporary -> DataLocation (Register or Offset) dictionary,
|
|
# specifying where a given Temporary should be allocated:
|
|
alloc_dict: Dict[Temporary, DataLocation] = dict()
|
|
# Use the coloring `coloringreg` to fill `alloc_dict`.
|
|
# Our version is less than 5 lines of code.
|
|
raise NotImplementedError("Allocation based on graph coloring (lab5)") # TODO
|
|
if self._debug:
|
|
print("Allocation:")
|
|
print(alloc_dict)
|
|
self._fdata._pool.set_temp_allocation(alloc_dict)
|