From d668ab98aad49d60e2ec1086430fd3d0257a1756 Mon Sep 17 00:00:00 2001 From: augustin64 Date: Mon, 25 Nov 2024 21:57:16 +0100 Subject: [PATCH] TP05b (buggy) --- MiniC/TP05/ExitSSA.py | 33 ++++++++++++++++++----- MiniC/TP05/LivenessSSA.py | 37 ++++++++++++++++++++++---- MiniC/TP05/SequentializeMoves.py | 37 +++++++++++++++++++++----- MiniC/TP05/SmartAllocator.py | 45 +++++++++++++++++++++++++++----- 4 files changed, 127 insertions(+), 25 deletions(-) diff --git a/MiniC/TP05/ExitSSA.py b/MiniC/TP05/ExitSSA.py index bf7209e..123298b 100644 --- a/MiniC/TP05/ExitSSA.py +++ b/MiniC/TP05/ExitSSA.py @@ -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 diff --git a/MiniC/TP05/LivenessSSA.py b/MiniC/TP05/LivenessSSA.py index f59dcca..be19ccf 100644 --- a/MiniC/TP05/LivenessSSA.py +++ b/MiniC/TP05/LivenessSSA.py @@ -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!""" diff --git a/MiniC/TP05/SequentializeMoves.py b/MiniC/TP05/SequentializeMoves.py index 7ab786e..d4df776 100644 --- a/MiniC/TP05/SequentializeMoves.py +++ b/MiniC/TP05/SequentializeMoves.py @@ -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: diff --git a/MiniC/TP05/SmartAllocator.py b/MiniC/TP05/SmartAllocator.py index b3fe819..aa9af7f 100644 --- a/MiniC/TP05/SmartAllocator.py +++ b/MiniC/TP05/SmartAllocator.py @@ -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)