TP05b (buggy)

This commit is contained in:
augustin64 2024-11-25 21:57:16 +01:00
parent 9e371fb015
commit d668ab98aa
4 changed files with 127 additions and 25 deletions

View File

@ -6,14 +6,14 @@ Functions to convert a CFG out of SSA Form.
from typing import cast, List
from Lib import RiscV
from Lib.CFG import Block, BlockInstr, CFG
from Lib.Operands import Temporary
from Lib.Operands import Temporary, DataLocation
from Lib.Statement import AbsoluteJump, Label
from Lib.Terminator import BranchingTerminator, Return
from Lib.PhiNode import PhiNode
from TP05.SequentializeMoves import sequentialize_moves
from TP05.SequentializeMoves import sequentialize_moves, generate_smart_move
def generate_moves_from_phis(phis: List[PhiNode], parent: Block) -> List[BlockInstr]:
def generate_moves_from_phis(phis: List[PhiNode], parent: Block, is_smart: bool=False) -> 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
@ -22,11 +22,30 @@ def generate_moves_from_phis(phis: List[PhiNode], parent: Block) -> List[BlockIn
This is an helper function called during SSA exit.
"""
# (Lab 5a, Exercise 6)
lbl = parent.get_label()
if is_smart:
parallel_moves: set[tuple[DataLocation, DataLocation]] = set()
moves: List[BlockInstr] = []
for phi in phis:
if lbl not in phi.get_srcs():
continue
src = phi.get_srcs()[lbl]
if isinstance(src, Temporary) or isinstance(phi.var, Temporary):
loc1, loc2 = phi.var, src
if isinstance(loc1, Temporary):
loc1 = loc1.get_alloced_loc()
if isinstance(loc2, Temporary):
loc2 = loc2.get_alloced_loc()
moves += generate_smart_move(loc1, cast(DataLocation, loc2))
else:
src = phi.get_srcs()[lbl]
parallel_moves.add((phi.var, cast(DataLocation, src)))
return sequentialize_moves(parallel_moves)+moves
return [
RiscV.mv(
phi.defined()[0],
phi.get_srcs()[parent.get_label()]
) for phi in phis if parent.get_label() in phi.get_srcs()
phi.var,
phi.get_srcs()[lbl]
) for phi in phis if lbl in phi.get_srcs()
]
@ -43,7 +62,7 @@ def exit_ssa(cfg: CFG, is_smart: bool) -> None:
for parent in parents:
# Add the block containing 'moves' to 'cfg'
moves_label = cfg.fdata.fresh_label("merge_phi")
block_moves = Block(moves_label, generate_moves_from_phis(phis, parent), AbsoluteJump(b.get_label()))
block_moves = Block(moves_label, generate_moves_from_phis(phis, parent, is_smart=is_smart), AbsoluteJump(b.get_label()))
cfg.add_block(block_moves)
# Update parent terminator

View File

@ -1,6 +1,6 @@
from typing import Dict, Set, Tuple
from Lib.Operands import Temporary
from Lib.Statement import Statement, regset_to_string
from Lib.Statement import Statement, regset_to_string, Instru3A
from Lib.CFG import Block, CFG
from Lib.PhiNode import PhiNode
@ -35,16 +35,41 @@ class LivenessSSA:
def livein_at_instruction(self, block: Block, pos: int, var: Temporary) -> None:
"""Backward propagation of liveness information at the beginning of an instruction."""
raise NotImplementedError("LivenessSSA") # TODO (Lab 5b, Exercise 1)
instr = block.get_all_statements()[pos]
if isinstance(instr, PhiNode) and var in instr.srcs.values():
bpred = self._cfg.get_block(
next(lbl for lbl, value in instr.srcs.items() if value == var)
)
self.liveout_at_block(bpred, var)
elif pos == 0:
for bpred in block.get_in():
self.liveout_at_block(bpred, var)
else:
self.liveout_at_instruction(block, pos-1, var)
def liveout_at_instruction(self, block: Block, pos: int, var: Temporary) -> None:
"""Backward propagation of liveness information at the end of an instruction."""
instr = block.get_all_statements()[pos]
raise NotImplementedError("LivenessSSA") # TODO (Lab 5b, Exercise 1)
self._liveout[block, instr].add(var)
if var in instr.defined():
#self._seen[block].add(var)
return
self.livein_at_instruction(block, pos, var)
#self._seen[block].add(var)
def liveout_at_block(self, block: Block, var: Temporary) -> None:
"""Backward propagation of liveness information at the end of a block."""
raise NotImplementedError("LivenessSSA") # TODO (Lab 5b, Exercise 1)
if var not in self._seen[block]:
self._seen[block].add(var)
self.liveout_at_instruction(block, len(block.get_all_statements())-1, var)
def gather_uses(self) -> Dict[Temporary, Set[Tuple[Block, int]]]:
"""
@ -67,7 +92,9 @@ class LivenessSSA:
def conflict_on_phis(self) -> None:
"""Ensures that variables defined by φ instructions are in conflict with one-another."""
raise NotImplementedError("LivenessSSA") # TODO (Lab 5b, Exercise 1)
for block in self._cfg.get_blocks():
for phinode in block.get_phis():
self._liveout[block, phinode] = set([v for v in phinode.used() if isinstance(v, Temporary)])
def print_map_in_out(self) -> None: # pragma: no cover
"""Print live out sets at each instruction, group by block, useful for debugging!"""

View File

@ -18,8 +18,22 @@ def generate_smart_move(dest: DataLocation, src: DataLocation) -> List[BlockInst
This is an helper function for `sequentialize_moves`.
"""
instr: List[BlockInstr] = []
# TODO Compute the moves (Lab 5b, Exercise 4)
raise NotImplementedError("generate_smart_move")
tmp: Register = S[1]
match dest, src:
case Register(), Register():
if dest != src:
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
@ -40,18 +54,27 @@ def sequentialize_moves(parallel_moves: Set[Tuple[DataLocation, DataLocation]]
# 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 vetices without successors
# 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:
# TODO Remove the leaves iteratively (Lab 5b, Exercise 4)
raise NotImplementedError("sequentialize_moves: leaves")
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:
# TODO Handle each cycle (Lab 5b, Exercise 4)
raise NotImplementedError("sequentialize_moves: cycles")
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:

View File

@ -1,11 +1,12 @@
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.Statement import Instruction, Instru3A
from Lib.Allocator import Allocator
from Lib.FunctionData import FunctionData
from Lib import RiscV
from Lib.Graphes import Graph # For Graph coloring utility functions
from TP05.SequentializeMoves import generate_smart_move
class SmartAllocator(Allocator):
@ -28,9 +29,27 @@ class SmartAllocator(Allocator):
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
for arg in old_instr.defined():
match arg:
case Offset():
after.append(RiscV.sd(S[3], arg))
new_args.append(S[3])
case Temporary():
new_args.append(arg.get_alloced_loc())
case _: # Contains Register()
new_args.append(arg)
for i, arg in enumerate(old_instr.used(), 1):
match arg:
case Offset():
before.append(RiscV.ld(S[i], arg))
new_args.append(S[i])
case Temporary():
new_args.append(arg.get_alloced_loc())
case _: # Contains Register()
new_args.append(arg)
# And now return the new list!
instr = old_instr.with_args(new_args)
return before + [instr] + after
@ -70,7 +89,12 @@ class SmartAllocator(Allocator):
# 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
for (block, statement), vars in self._liveness._liveout.items():
for t1 in list(vars)+statement.defined():
for t2 in vars:
if t1 == t2:
continue
self._igraph.add_edge((t1, t2))
def smart_alloc(self) -> None:
"""
@ -79,6 +103,7 @@ class SmartAllocator(Allocator):
Precondition: the interference graph _igraph must have been built.
"""
regs = list(GP_REGS) # Get a writable copy
# Checking the interference graph has been built
if not self._igraph:
raise MiniCInternalError("Empty interference graph in the Smart Allocator")
@ -92,7 +117,15 @@ class SmartAllocator(Allocator):
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
color_dict: Dict[int, DataLocation] = dict()
for temp in self._fdata._pool.get_all_temps():
if coloringreg[temp] not in color_dict:
color_dict[coloringreg[temp]] = (
regs.pop() if len(regs) > 0 else
self._fdata.fresh_offset()
)
alloc_dict[temp] = color_dict[coloringreg[temp]]
if self._debug:
print("Allocation:")
print(alloc_dict)