101 lines
3.9 KiB
Python

"""
CAP, SSA Intro, Elimination and Optimisations
Functions to convert a CFG out of SSA Form.
"""
from typing import cast, List, Set, Tuple
from Lib.Errors import MiniCInternalError
from Lib import RiscV
from Lib.Graphes import DiGraph
from Lib.CFG import Block, BlockInstr, CFG
from Lib.Operands import (
Register, DataLocation,
Temporary, TemporaryPool)
from Lib.Statement import AbsoluteJump
from Lib.Terminator import BranchingTerminator, Return
from Lib.PhiNode import PhiNode
from TP05.SequentializeMoves import sequentialize_moves
def generate_moves_from_phis(phis: List[PhiNode], parent: Block) -> List[BlockInstr]:
"""
`generate_moves_from_phis(phis, parent)` builds a list of move instructions
to be inserted in a new block between `parent` and the block with phi nodes
`phis`.
This is an helper function called during SSA exit.
"""
moves: List[BlockInstr] = []
plabel = parent.get_label()
for phi in phis:
if(plabel in phi.used()):
moves.append(RiscV.mv(phi.var,phi.used()[plabel]))
return moves
def generate_moves_from_phis_smart(pool: TemporaryPool, phis: List[PhiNode], parent: Block) -> List[BlockInstr]:
"""
`generate_moves_from_phis_smart(phis, parent)` builds a list of move instructions
to be inserted in a new block between `parent` and the block with phi nodes
`phis`. This function is working with smart allocation, unlike its «dump» counterpart
that makes allocations in no particular order.
This is an helper function called during SSA exit.
"""
moves: List[Tuple[DataLocation,DataLocation]] = []
plabel = parent.get_label()
for phi in phis:
if(plabel in phi.used()):
src = phi.var
dest = phi.used()[plabel]
if(isinstance(src,Temporary)):
src = pool.get_alloced_loc(src)
if(isinstance(dest,Temporary)):
dest = pool.get_alloced_loc(dest)
assert(isinstance(dest,DataLocation))
moves.append((dest,src))
realmoves = sequentialize_moves(set(moves))
return realmoves
def exit_ssa(cfg: CFG, is_smart: bool) -> None:
"""
`exit_ssa(cfg)` replaces phi nodes with move instructions to exit SSA form.
`is_smart` is set to true when smart register allocation is enabled (Lab 5b).
"""
for b in cfg.get_blocks():
phis = cast(List[PhiNode], b._phis) # Use cast for Pyright
b._phis = [] # Remove all phi nodes in the block
blabel = b.get_label()
parents: List[Block] = b.get_in().copy() # Copy as we modify it by adding blocks
for p in parents:
moves = generate_moves_from_phis_smart(cfg.fdata._pool,phis, p) if is_smart else generate_moves_from_phis(phis, p)
if(len(moves)==0):
continue
# Creating the block
ilabel = cfg.fdata.fresh_label(p.get_label().name+"_to_"+b.get_label().name)
i = Block(ilabel,moves,AbsoluteJump(blabel))
# Add the block
cfg.add_block(i)
# Changing the jumps
ot = p.get_terminator()
if(isinstance(ot,BranchingTerminator)):
if(ot.label_then == blabel):
ot.label_then = ilabel
if(ot.label_else == blabel):
ot.label_else = ilabel
elif(isinstance(ot,AbsoluteJump)):
assert(ot.label == blabel)
ot = AbsoluteJump(ilabel)
elif(isinstance(ot,Return)):
raise MiniCInternalError("Malformed CFG, cannot have a return in a parent block")
else:
raise MiniCInternalError("I don't know of this terminator type:",type(ot))
p.set_terminator(ot) # This instruction might be useless
# Moving from p -> b to p -> i -> b
cfg.remove_edge(p,b)
cfg.add_edge(p,i)
cfg.add_edge(i,b)