CAP/MiniC/TP05/SequentializeMoves.py

85 lines
3.1 KiB
Python

"""
CAP, SSA Intro, Elimination and Optimisations
Helper functions to convert a CFG out of SSA Form
for the Smart Allocator.
"""
from typing import List, Set, Tuple
from Lib import RiscV
from Lib.Graphes import DiGraph
from Lib.CFG import BlockInstr
from Lib.Operands import Register, Offset, DataLocation, S
def generate_smart_move(dest: DataLocation, src: DataLocation) -> List[BlockInstr]:
"""
Generate a list of move, store and load instructions, depending on
whether the operands are registers or memory locations.
This is an helper function for `sequentialize_moves`.
"""
instr: List[BlockInstr] = []
tmp: Register = S[1]
match dest, src:
case Register(), Register():
instr.append(RiscV.mv(dest, src))
case Register(), Offset():
instr.append(RiscV.ld(dest, src))
case Offset(), Register():
instr.append(RiscV.sd(src, dest))
case Offset(), Offset():
instr.append(RiscV.ld(tmp, src))
instr.append(RiscV.sd(tmp, dest))
case _, _:
raise ValueError(f"Unsupported operands {type(dest)} <- {type(src)}")
return instr
def sequentialize_moves(parallel_moves: Set[Tuple[DataLocation, DataLocation]]
) -> List[BlockInstr]:
"""
Take a set of parallel moves represented as (destination, source) pairs,
and return a list of sequential moves which respect the cycles.
Use the register `tmp` S2 for the cycles.
Return a corresponding list of RiscV instructions.
This is an helper function called during SSA exit.
"""
tmp: Register = S[2] # S2 is not a general purpose register
# Build the graph of the moves
move_graph: DiGraph = DiGraph()
for dest, src in parallel_moves:
move_graph.add_edge((src, dest))
# List for the sequentialized moves to do
# Convention: in moves we put (dest, src) for each move
moves: List[Tuple[DataLocation, DataLocation]] = []
# First iteratively remove all the vertices without successors
vars_without_successor = {src
for src, dests in move_graph.neighbourhoods()
if len(dests) == 0}
while vars_without_successor:
var = vars_without_successor.pop()
for src in move_graph.pred(var):
moves.append((var, src))
dests = [dests for sr, dests in move_graph.neighbourhoods() if sr == src][0]
if len(dests) == 1:
vars_without_successor.add(src)
move_graph.delete_vertex(var)
# Then handle the cycles
cycles: List = move_graph.connected_components()
for cycle in cycles:
if len(cycle) == 1:
continue
previous = tmp
for var in reversed(cycle):
moves.append((previous, var))
previous = var
moves.append((previous, tmp))
# Transform the moves to do in actual RiscV instructions
moves_instr: List[BlockInstr] = []
for dest, src in moves:
instrs = generate_smart_move(dest, src)
moves_instr.extend(instrs)
return moves_instr