Add compiler improvement

This commit is contained in:
Jan-Niclas Loosen
2026-03-12 11:32:41 +01:00
parent 438447e6de
commit 9462ccd3ed
10 changed files with 108 additions and 1 deletions

View File

@@ -0,0 +1,46 @@
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

View File

@@ -0,0 +1,7 @@
from __future__ import annotations
from syntax import EXPRESSION
class Optimization:
def setup(self, ast: EXPRESSION) -> None: ...
def apply(self, node: EXPRESSION) -> EXPRESSION: ...

View File

@@ -0,0 +1,2 @@
from optimizations.DeadAssignmentElimination import DeadAssignmentElimination
from optimizations.Optimization import Optimization

View File

@@ -0,0 +1,36 @@
from __future__ import annotations
from optimizations.Optimization import Optimization
from optimizations.DeadAssignmentElimination import DeadAssignmentElimination
from syntax import EXPRESSION
# Traverses the AST and applies the given optimizations
def __apply_optimizations(node: EXPRESSION, optimizations: list[Optimization]) -> EXPRESSION:
for key, value in list(node.__dict__.items()):
if key == "pp":
continue
if isinstance(value, EXPRESSION):
new_child = __apply_optimizations(value, optimizations)
if new_child is not value:
setattr(node, key, new_child)
elif isinstance(value, list):
for i, elem in enumerate(value):
if isinstance(elem, EXPRESSION):
new_elem = __apply_optimizations(elem, optimizations)
if new_elem is not elem:
value[i] = new_elem
result: EXPRESSION = node
for opt in optimizations:
result = opt.apply(result)
return result
# Stores the active optimizations
ACTIVE_OPTIMIZATIONS: list[Optimization] = [
DeadAssignmentElimination(),
]
# Apply optimizations to the given AST node
def optimize(ast: EXPRESSION) -> EXPRESSION:
for opt in ACTIVE_OPTIMIZATIONS:
opt.setup(ast)
return __apply_optimizations(ast, ACTIVE_OPTIMIZATIONS)