diff --git a/MiniC/README-SSA.md b/MiniC/README-SSA.md index 7b2e8c3..637c29a 100644 --- a/MiniC/README-SSA.md +++ b/MiniC/README-SSA.md @@ -11,10 +11,15 @@ TODO: - Explain any design choices you may have made. - Do not forget to remove all debug traces from your code! + + # Test design TODO: give the main objectives of your tests. # Known bugs -TODO: bugs you could not fix (if any). +There was an issue with the subject, because we implemented «smart moves» in ExitSSA, we had to replace +temporaries at the exit of the SSA. So when the SSA is exited, some temporaries are already replaced +with real data locations (that is, the instructions that comes from the reduction +of phi-statements. diff --git a/MiniC/TP04/AllInMemAllocator.py b/MiniC/TP04/AllInMemAllocator.py index b79fb9d..412f4c9 100644 --- a/MiniC/TP04/AllInMemAllocator.py +++ b/MiniC/TP04/AllInMemAllocator.py @@ -29,12 +29,6 @@ class AllInMemAllocator(Allocator): if(isinstance(arg, Temporary)): after.append(RiscV.sd(subst[arg],self._fdata._pool.get_alloced_loc(arg))) - - # TODO (Exercise 7): compute before,after,args. - # TODO (Exercise 7): iterate over old_args, check which argument - # TODO (Exercise 7): is a temporary (e.g. isinstance(..., Temporary)), - # TODO (Exercise 7): and if so, generate ld/sd accordingly. Replace the - # TODO (Exercise 7): temporary with S[1], S[2] or S[3] physical registers. try: new_instr = old_instr.substitute(subst) except Exception: diff --git a/MiniC/TP05/ExitSSA.py b/MiniC/TP05/ExitSSA.py index c319a13..15ce9f0 100644 --- a/MiniC/TP05/ExitSSA.py +++ b/MiniC/TP05/ExitSSA.py @@ -10,10 +10,11 @@ from Lib.Graphes import DiGraph from Lib.CFG import Block, BlockInstr, CFG from Lib.Operands import ( Register, DataLocation, - Temporary) + 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]: @@ -32,6 +33,31 @@ def generate_moves_from_phis(phis: List[PhiNode], parent: Block) -> List[BlockIn 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. @@ -44,7 +70,7 @@ def exit_ssa(cfg: CFG, is_smart: bool) -> None: 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(phis, p) + 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 diff --git a/MiniC/TP05/SequentializeMoves.py b/MiniC/TP05/SequentializeMoves.py index 40e2e57..c6d39d8 100644 --- a/MiniC/TP05/SequentializeMoves.py +++ b/MiniC/TP05/SequentializeMoves.py @@ -1,8 +1,9 @@ from typing import List, Set, Tuple +from Lib.Errors import MiniCInternalError from Lib import RiscV from Lib.Graphes import DiGraph from Lib.CFG import BlockInstr -from Lib.Operands import (Register, DataLocation, S) +from Lib.Operands import (Offset, Register, DataLocation, S) def generate_smart_move(dest: DataLocation, src: DataLocation) -> List[BlockInstr]: @@ -12,8 +13,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] + if(isinstance(dest,Offset) and isinstance(src,Offset)): + # We read the source in tmp and write it to destination + instr.append(RiscV.ld(tmp,src)) + instr.append(RiscV.sd(tmp,dest)) + elif(isinstance(dest,Offset) and isinstance(src,Register)): + # We write the register to destination + instr.append(RiscV.sd(src,dest)) + elif(isinstance(dest,Register) and isinstance(src,Offset)): + # We read the source into the register + instr.append(RiscV.ld(dest,src)) + elif(isinstance(dest,Register) and isinstance(src,Register)): + # Classic move + instr.append(RiscV.mv(src,dest)) + else: + raise MiniCInternalError("Cannot generate smart move from parameters that are no Offset or Register:",src,dest) return instr @@ -39,13 +54,33 @@ def sequentialize_moves(parallel_moves: Set[Tuple[DataLocation, DataLocation]] 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") + head = vars_without_successor.pop() + while(head is not None): + preds = move_graph.pred(head) + if(len(preds)==0): + head = None + else: + pred = preds.pop() + moves.append((pred,head)) + move_graph.delete_vertex(head) + head = pred + # 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") + if(len(cycle)==1): + # No moves to do + pass + else: + head = cycle[0] + moves.append((head,tmp)) + while True: + pred = move_graph.pred(head).pop() + if(pred==cycle[0]): + break + moves.append((pred,head)) + head = pred + moves.append((tmp,head)) # 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 bd33371..34f5099 100644 --- a/MiniC/TP05/SmartAllocator.py +++ b/MiniC/TP05/SmartAllocator.py @@ -28,13 +28,44 @@ class SmartAllocator(Allocator): before: List[Instruction] = [] after: List[Instruction] = [] subst: Dict[Operand, 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 + + old_args = old_instr.args() + numreg = 1 + for arg in old_args: + if(isinstance(arg, Temporary)): # Else we don't change anything + dest = self._fdata._pool.get_alloced_loc(arg) + if(isinstance(dest,Register)): + # We use the provided register + subst[arg] = dest + elif(isinstance(dest,Offset)): + # We use a s register in which we will read|write + subst[arg] = S[numreg] + numreg += 1 + else: + raise MiniCInternalError("Unsupported dataLocation for smart replace") + for arg in old_instr.used(): + if(isinstance(arg, Temporary)): + # If the argument has an Offset substitution + if(isinstance(self._fdata._pool.get_alloced_loc(arg),Offset)): + # We have to read it from memory + before.append(RiscV.ld(subst[arg],self._fdata._pool.get_alloced_loc(arg))) + for arg in old_instr.defined(): + if(isinstance(arg, Temporary)): + # If the argument has an Offset substitution + if(isinstance(self._fdata._pool.get_alloced_loc(arg),Offset)): + # We have to write them after to memory + after.append(RiscV.sd(subst[arg],self._fdata._pool.get_alloced_loc(arg))) + # And now return the new list! - instr = old_instr.substitute(subst) - return before + [instr] + after - + + print(old_instr,"->",before,subst,after) + try: + new_instr = old_instr.substitute(subst) + except Exception: + # We have an instruction that doesn't need substitution + return [old_instr] + return before + [new_instr] + after + def prepare(self) -> None: """ Perform all preparatory steps related to smart register allocation: @@ -70,7 +101,17 @@ 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 st,varz in self._liveness._liveout.items(): + for x in varz: + if(isinstance(x,Temporary)): + # First condition + for y in varz: + if x!=y and isinstance(y,Temporary): + self._igraph.add_edge((x,y)) + # Second/third condition + for y in st.defined(): + if x!=y and isinstance(y,Temporary): # x is alive after x definition + self._igraph.add_edge((x,y)) def smart_alloc(self) -> None: """ @@ -92,7 +133,16 @@ 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 + moreColors_alloc_dict: Dict[int, DataLocation] = dict() + + for var,col in coloringreg.items(): + if(col