CAP/MiniC/TP05/LivenessSSA.py

109 lines
4.6 KiB
Python
Raw Normal View History

2024-10-06 19:58:11 +02:00
from typing import Dict, Set, Tuple
from Lib.Operands import Temporary
2024-11-25 21:57:16 +01:00
from Lib.Statement import Statement, regset_to_string, Instru3A
2024-10-06 19:58:11 +02:00
from Lib.CFG import Block, CFG
from Lib.PhiNode import PhiNode
class LivenessSSA:
"""Liveness Analysis on a CFG under SSA Form."""
def __init__(self, cfg: CFG, debug=False):
self._cfg: CFG = cfg
self._debug: bool = debug
# Temporary already propagated, by Block
self._seen: Dict[Block, Set[Temporary]] = dict()
# Live Temporary at outputs of Statement
self._liveout: Dict[tuple[Block, Statement], Set[Temporary]] = dict()
def run(self) -> None:
"""Compute the liveness: fill out self._seen and self._liveout."""
# Initialization
for block in self._cfg.get_blocks():
self._seen[block] = set()
for instr in block.get_all_statements():
self._liveout[block, instr] = set()
# Start the used-defined chains with backward propagation of liveness information
for var, uses in self.gather_uses().items():
for block, pos in uses:
self.livein_at_instruction(block, pos, var)
# Add conflicts on phis
self.conflict_on_phis()
# Final debugging print
if self._debug:
self.print_map_in_out()
def livein_at_instruction(self, block: Block, pos: int, var: Temporary) -> None:
"""Backward propagation of liveness information at the beginning of an instruction."""
2024-11-25 21:57:16 +01:00
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)
2024-10-06 19:58:11 +02:00
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]
2024-11-25 21:57:16 +01:00
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)
2024-10-06 19:58:11 +02:00
def liveout_at_block(self, block: Block, var: Temporary) -> None:
"""Backward propagation of liveness information at the end of a block."""
2024-11-25 21:57:16 +01:00
if var not in self._seen[block]:
self._seen[block].add(var)
self.liveout_at_instruction(block, len(block.get_all_statements())-1, var)
2024-10-06 19:58:11 +02:00
def gather_uses(self) -> Dict[Temporary, Set[Tuple[Block, int]]]:
"""
Return a dictionnary giving for each variable the set of statements using it,
with a statement identified by its block and its position inside the latter.
Phi instructions are at the beginning of their block, while a Terminator is at
the last position of its block.
"""
uses: Dict[Temporary, Set[Tuple[Block, int]]] = dict()
for block in self._cfg.get_blocks():
for pos, instr in enumerate(block.get_all_statements()):
# Variables used by a statement `s` are `s.used()`
for var in instr.used():
if isinstance(var, Temporary):
# Already computed uses of the var if any, otherwise the empty set
var_uses = uses.get(var, set())
# Add for a statement its block and its position inside to the uses of var
uses[var] = var_uses.union({(block, pos)})
return uses
def conflict_on_phis(self) -> None:
"""Ensures that variables defined by φ instructions are in conflict with one-another."""
2024-11-25 21:57:16 +01:00
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)])
2024-10-06 19:58:11 +02:00
def print_map_in_out(self) -> None: # pragma: no cover
"""Print live out sets at each instruction, group by block, useful for debugging!"""
print("Liveout: [")
for block in self._cfg.get_blocks():
print("Block " + str(block.get_label()) + ": {\n "
+ ",\n ".join("\"{}\": {}"
.format(instr, regset_to_string(self._liveout[block, instr]))
for instr in block.get_all_statements()) +
"}")
print("]")