46 lines
1.7 KiB
Python
46 lines
1.7 KiB
Python
from __future__ import annotations
|
|
|
|
import cfg_build
|
|
from cfg.CFG import CFG
|
|
from cfg.CFG_Node import CFG_Node
|
|
from cfa.ReachedUses import ReachedUses
|
|
from syntax import EXPRESSION, ASSIGN
|
|
from optimizations.Optimization import Optimization
|
|
|
|
class DeadAssignmentElimination(Optimization):
|
|
def __init__(self) -> None:
|
|
self._node_by_ast_id: dict[int, CFG_Node] = {}
|
|
self.__ru: ReachedUses | None = None
|
|
|
|
def setup(self, ast: EXPRESSION) -> None:
|
|
cfg_build.FUNCTIONS.clear()
|
|
cfg_build.CURRENT_FUNCTION = None
|
|
cfg = CFG(ast)
|
|
self.__ru = ReachedUses(cfg)
|
|
self._node_by_ast_id = {id(n.ast_node): n for n in self.__ru.cfg.nodes() if n.ast_node is not None}
|
|
|
|
def apply(self, node: EXPRESSION) -> EXPRESSION:
|
|
# Only ASSIGN nodes can be dead assignments.
|
|
if not isinstance(node, ASSIGN):
|
|
return node
|
|
|
|
# Find the CFG node corresponding to this AST node.
|
|
# If none is found, the node was not analyzed — leave it unchanged.
|
|
cfg_node = self._node_by_ast_id.get(id(node))
|
|
if cfg_node is None:
|
|
return node
|
|
|
|
ru = self.__ru
|
|
nid = cfg_node.id
|
|
defined_vars = ru.defs.get(nid, set())
|
|
out = ru.out_sets.get(nid, set())
|
|
|
|
# Check if any use-fact in OUT(n) belongs to a variable defined at n.
|
|
# If so, the assignment is live — at least one use is reachable from here.
|
|
for (_, var) in out:
|
|
if var in defined_vars:
|
|
return node
|
|
|
|
# No reached use found for the assigned variable — the assignment is dead.
|
|
# Replace ASSIGN(var, expr) with expr to preserve side effects on the RHS.
|
|
return node.expr |