CAP/MiniC/Lib/Dominators.py

129 lines
4.4 KiB
Python
Raw Normal View History

2024-10-06 19:58:11 +02:00
"""
Utility functions to work with dominators in a :py:class:`CFG <Lib.CFG.CFG>`.
Do not hesitate to look at the source of the functions
to get a better understanding of the algorithms.
"""
from typing import Dict, Set
from graphviz import Digraph
from Lib.CFG import Block, CFG
def computeDom(cfg: CFG) -> Dict[Block, Set[Block]]:
"""
`computeDom(cfg)` computes the table associating blocks to their
dominators in `cfg`.
It works by solving the equation system.
This is an helper function called during SSA entry.
"""
all_blocks: Set[Block] = set(cfg.get_blocks())
dominators: Dict[Block, Set[Block]] = dict()
for b in all_blocks:
if b.get_in(): # If b has some predecessor
dominators[b] = all_blocks
else: # If b has no predecessors
dominators[b] = {b}
new_dominators: Dict[Block, Set[Block]] = dict()
while True:
for b in all_blocks:
if b.get_in():
dom_preds = [dominators[b2] for b2 in b.get_in()]
new_dominators[b] = {b}.union(set.intersection(*dom_preds))
else:
new_dominators[b] = {b}
if dominators == new_dominators:
break
else:
dominators = new_dominators
new_dominators = dict()
return dominators
def printDT(filename: str, graph: Dict[Block, Set[Block]]) -> None: # pragma: no cover
"""Display a graphical rendering of the given domination tree."""
dot = Digraph()
for k in graph:
dot.node(str(k.get_label()))
for k in graph:
for v in graph[k]:
dot.edge(str(k.get_label()), str(v.get_label()))
dot.render(filename, view=True)
def computeDT(cfg: CFG, dominators: Dict[Block, Set[Block]],
dom_graphs: bool, basename: str) -> Dict[Block, Set[Block]]:
"""
`computeDT(cfg, dominators)` computes the domination tree of `cfg`
using the previously computed `dominators`.
It returns `DT`, a dictionary which associates a block with its children
in the dominator tree.
This is an helper function called during SSA entry.
"""
# First, compute the immediate dominators
idominators: Dict[Block, Block] = {}
for b, doms in dominators.items():
# The immediate dominator of b is the unique vertex n ≠ b
# which dominates b and is dominated by all vertices in Dom(b) b.
strict_doms = doms - {b}
idoms = set()
for n in strict_doms:
if strict_doms.issubset(dominators[n]):
idoms.add(n)
if idoms:
assert (len(idoms) == 1)
idominators[b] = idoms.pop()
# Then, simply inverse the relation to obtain the domination tree
DT = {b: set() for b in cfg.get_blocks()}
for i, idominator in idominators.items():
DT[idominator].add(i)
# Print the domination tree if asked
if dom_graphs:
s = "{}.{}.ssa.DT.dot".format(basename, cfg.fdata.get_name())
print("SSA - domination tree graph:", s)
printDT(s, DT)
return DT
def _computeDF_at_block(
cfg: CFG,
dominators: Dict[Block, Set[Block]],
DT: Dict[Block, Set[Block]],
b: Block,
DF: Dict[Block, Set[Block]]) -> None:
"""
`_computeDF_at_block(...)` computes the dominance frontier at the given block,
by updating `DF`.
This is an helper function called during SSA entry.
"""
S: Set[Block] = {succ for succ in cfg.out_blocks(b) if succ not in DT[b]}
for b_succ in DT[b]:
_computeDF_at_block(cfg, dominators, DT, b_succ, DF)
for b_frontier in DF[b_succ]:
if b not in (dominators[b_frontier] - {b_frontier}):
S.add(b_frontier)
DF[b] = S
def computeDF(cfg: CFG, dominators: Dict[Block, Set[Block]],
DT: Dict[Block, Set[Block]], dom_graphs: bool, basename: str
) -> Dict[Block, Set[Block]]:
"""
`computeDF(...)` computes the dominance frontier of a CFG.
It returns `DF` which associates a block to its frontier.
This is an helper function called during SSA entry.
"""
DF: Dict[Block, Set[Block]] = dict()
for b_entry in cfg.get_entries():
_computeDF_at_block(cfg, dominators, DT, b_entry, DF)
# Print the domination frontier on the CFG if asked
if dom_graphs:
s = "{}.{}.ssa.DF.dot".format(basename, cfg.fdata.get_name())
print("SSA - dominance frontier graph:", s)
cfg.print_dot(s, DF, True)
return DF