Ajout du smart allocator .... disfonctionnel

This commit is contained in:
MysaaJava 2022-11-14 23:42:42 +01:00
parent 2c50bbaaa0
commit 0a8c02ec83
Signed by: Mysaa
GPG Key ID: DBA23608F23F5A10
6 changed files with 174 additions and 24 deletions

View File

@ -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.

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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<len(GP_REGS)):
alloc_dict[var] = GP_REGS[col]
else:
if(not col in moreColors_alloc_dict):
moreColors_alloc_dict[col] = self._fdata.fresh_offset()
alloc_dict[var] = moreColors_alloc_dict[col]
if self._debug:
print("Allocation:")
print(alloc_dict)

View File

@ -0,0 +1,40 @@
#include "printlib.h"
int main() {
// Testing ==
if( 11 == 11 ){
println_int(61);
}else{
println_int(420);
}
if( -22 == -22 ){
println_int(62);
}else{
println_int(420);
}
if( true == true ){
println_int(63);
}else{
println_int(420);
}
if( true == false ){
println_int(420);
}else{
println_int(64);
}
if( false == false ){
println_int(65);
}else{
println_int(420);
}
return 0;
}
// EXPECTED
// 61
// 62
// 63
// 64
// 65