diff --git a/Project-02-03-04-05/Source.gv.png b/Project-02-03-04-05/Source.gv.png index 9ffc31f..e3792f5 100644 Binary files a/Project-02-03-04-05/Source.gv.png and b/Project-02-03-04-05/Source.gv.png differ diff --git a/Project-02-03-04-05/images/getOptimized/after.png b/Project-02-03-04-05/images/getOptimized/after.png new file mode 100644 index 0000000..02c30f1 Binary files /dev/null and b/Project-02-03-04-05/images/getOptimized/after.png differ diff --git a/Project-02-03-04-05/images/getOptimized/before.png b/Project-02-03-04-05/images/getOptimized/before.png new file mode 100644 index 0000000..931d402 Binary files /dev/null and b/Project-02-03-04-05/images/getOptimized/before.png differ diff --git a/Project-02-03-04-05/main.py b/Project-02-03-04-05/main.py index a430da7..c3577ec 100644 --- a/Project-02-03-04-05/main.py +++ b/Project-02-03-04-05/main.py @@ -17,6 +17,7 @@ from cfa.to_dot import analysis_to_dot from cfg.CFG import CFG from vistram.tram import * from vistram.vistram import MachineUI +from optimizations.optimize import optimize matplotlib.use("TkAgg") @@ -129,6 +130,9 @@ if __name__ == "__main__": ast = yacc.parser.parse(source) if mode == 0: + if cnsl.prompt_confirmation("\nOptimize AST?", default="y"): + ast = optimize(ast) + # Pretty print if cnsl.prompt_confirmation("\nPretty-print AST?"): print("") @@ -156,6 +160,9 @@ if __name__ == "__main__": print("Rendered AST diagram.") elif mode == 1: + if cnsl.prompt_confirmation("\nOptimize AST?", default="y"): + ast = optimize(ast) + tram_code = assemble(ast) # Print TRAM code diff --git a/Project-02-03-04-05/optimizations/DeadAssignmentElimination.py b/Project-02-03-04-05/optimizations/DeadAssignmentElimination.py new file mode 100644 index 0000000..f8e2d73 --- /dev/null +++ b/Project-02-03-04-05/optimizations/DeadAssignmentElimination.py @@ -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 \ No newline at end of file diff --git a/Project-02-03-04-05/optimizations/Optimization.py b/Project-02-03-04-05/optimizations/Optimization.py new file mode 100644 index 0000000..b60502f --- /dev/null +++ b/Project-02-03-04-05/optimizations/Optimization.py @@ -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: ... diff --git a/Project-02-03-04-05/optimizations/__init__.py b/Project-02-03-04-05/optimizations/__init__.py new file mode 100644 index 0000000..73ced98 --- /dev/null +++ b/Project-02-03-04-05/optimizations/__init__.py @@ -0,0 +1,2 @@ +from optimizations.DeadAssignmentElimination import DeadAssignmentElimination +from optimizations.Optimization import Optimization \ No newline at end of file diff --git a/Project-02-03-04-05/optimizations/optimize.py b/Project-02-03-04-05/optimizations/optimize.py new file mode 100644 index 0000000..8fdc6be --- /dev/null +++ b/Project-02-03-04-05/optimizations/optimize.py @@ -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) \ No newline at end of file diff --git a/Project-02-03-04-05/triplaprograms/getOptimized.tripla b/Project-02-03-04-05/triplaprograms/getOptimized.tripla new file mode 100644 index 0000000..4563388 --- /dev/null +++ b/Project-02-03-04-05/triplaprograms/getOptimized.tripla @@ -0,0 +1,9 @@ +let f(x, y) +{ + let g(x) + { + y = x + 7 + } + in x = g(5); y +} +in f(1, 2) \ No newline at end of file diff --git a/Project-02-03-04-05/triplaprograms/p1.tripla b/Project-02-03-04-05/triplaprograms/p1.tripla index 7c2fd1d..d65600f 100644 --- a/Project-02-03-04-05/triplaprograms/p1.tripla +++ b/Project-02-03-04-05/triplaprograms/p1.tripla @@ -1,3 +1,3 @@ let g(a) { a*a -} in g(5) \ No newline at end of file +} in b = g(5) \ No newline at end of file