sonolus.py 0.1.4__py3-none-any.whl → 0.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sonolus.py might be problematic. Click here for more details.
- sonolus/backend/finalize.py +18 -10
- sonolus/backend/interpret.py +7 -7
- sonolus/backend/ir.py +24 -0
- sonolus/backend/optimize/__init__.py +0 -0
- sonolus/backend/{allocate.py → optimize/allocate.py} +4 -3
- sonolus/backend/{constant_evaluation.py → optimize/constant_evaluation.py} +7 -7
- sonolus/backend/{coalesce.py → optimize/copy_coalesce.py} +3 -3
- sonolus/backend/optimize/dead_code.py +185 -0
- sonolus/backend/{dominance.py → optimize/dominance.py} +2 -17
- sonolus/backend/{flow.py → optimize/flow.py} +6 -5
- sonolus/backend/{inlining.py → optimize/inlining.py} +4 -17
- sonolus/backend/{liveness.py → optimize/liveness.py} +69 -65
- sonolus/backend/optimize/optimize.py +44 -0
- sonolus/backend/{passes.py → optimize/passes.py} +1 -1
- sonolus/backend/optimize/simplify.py +191 -0
- sonolus/backend/{ssa.py → optimize/ssa.py} +31 -18
- sonolus/backend/place.py +17 -25
- sonolus/backend/utils.py +10 -0
- sonolus/backend/visitor.py +360 -101
- sonolus/build/cli.py +14 -3
- sonolus/build/compile.py +8 -8
- sonolus/build/engine.py +10 -5
- sonolus/build/project.py +30 -1
- sonolus/script/archetype.py +429 -138
- sonolus/script/array.py +25 -8
- sonolus/script/array_like.py +297 -0
- sonolus/script/bucket.py +73 -11
- sonolus/script/containers.py +234 -51
- sonolus/script/debug.py +8 -8
- sonolus/script/easing.py +147 -105
- sonolus/script/effect.py +60 -0
- sonolus/script/engine.py +71 -4
- sonolus/script/globals.py +66 -32
- sonolus/script/instruction.py +79 -25
- sonolus/script/internal/builtin_impls.py +138 -27
- sonolus/script/internal/constant.py +139 -0
- sonolus/script/internal/context.py +14 -5
- sonolus/script/internal/dict_impl.py +65 -0
- sonolus/script/internal/generic.py +6 -9
- sonolus/script/internal/impl.py +38 -13
- sonolus/script/internal/introspection.py +5 -2
- sonolus/script/{math.py → internal/math_impls.py} +28 -28
- sonolus/script/internal/native.py +3 -3
- sonolus/script/internal/random.py +67 -0
- sonolus/script/internal/range.py +81 -0
- sonolus/script/internal/transient.py +51 -0
- sonolus/script/internal/tuple_impl.py +113 -0
- sonolus/script/interval.py +234 -16
- sonolus/script/iterator.py +120 -167
- sonolus/script/level.py +24 -0
- sonolus/script/num.py +79 -47
- sonolus/script/options.py +78 -12
- sonolus/script/particle.py +37 -4
- sonolus/script/pointer.py +4 -4
- sonolus/script/print.py +22 -1
- sonolus/script/project.py +59 -0
- sonolus/script/{graphics.py → quad.py} +75 -12
- sonolus/script/record.py +44 -13
- sonolus/script/runtime.py +50 -1
- sonolus/script/sprite.py +198 -115
- sonolus/script/text.py +2 -0
- sonolus/script/timing.py +72 -0
- sonolus/script/transform.py +296 -66
- sonolus/script/ui.py +134 -78
- sonolus/script/values.py +6 -13
- sonolus/script/vec.py +118 -3
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.6.dist-info}/METADATA +1 -1
- sonolus_py-0.1.6.dist-info/RECORD +89 -0
- sonolus/backend/dead_code.py +0 -80
- sonolus/backend/optimize.py +0 -37
- sonolus/backend/simplify.py +0 -47
- sonolus/script/comptime.py +0 -160
- sonolus/script/random.py +0 -14
- sonolus/script/range.py +0 -58
- sonolus_py-0.1.4.dist-info/RECORD +0 -84
- /sonolus/script/{callbacks.py → internal/callbacks.py} +0 -0
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.6.dist-info}/WHEEL +0 -0
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.6.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.6.dist-info}/licenses/LICENSE +0 -0
sonolus/backend/finalize.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from sonolus.backend.flow import BasicBlock, traverse_cfg_preorder
|
|
2
1
|
from sonolus.backend.ir import IRConst, IRGet, IRInstr, IRPureInstr, IRSet
|
|
3
2
|
from sonolus.backend.node import ConstantNode, EngineNode, FunctionNode
|
|
4
3
|
from sonolus.backend.ops import Op
|
|
4
|
+
from sonolus.backend.optimize.flow import BasicBlock, traverse_cfg_reverse_postorder
|
|
5
5
|
from sonolus.backend.place import BlockPlace
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def cfg_to_engine_node(entry: BasicBlock):
|
|
9
|
-
block_indexes = {block: i for i, block in enumerate(
|
|
9
|
+
block_indexes = {block: i for i, block in enumerate(traverse_cfg_reverse_postorder(entry))}
|
|
10
10
|
block_statements = []
|
|
11
11
|
for block in block_indexes:
|
|
12
12
|
statements = []
|
|
@@ -28,16 +28,24 @@ def cfg_to_engine_node(entry: BasicBlock):
|
|
|
28
28
|
],
|
|
29
29
|
)
|
|
30
30
|
)
|
|
31
|
-
case dict() as
|
|
31
|
+
case dict() as targets:
|
|
32
32
|
args = [ir_to_engine_node(block.test)]
|
|
33
33
|
default = len(block_indexes)
|
|
34
|
-
for cond
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
conds = [cond for cond in targets if cond is not None]
|
|
35
|
+
if min(conds) == 0 and max(conds) == len(conds) - 1 and all(int(cond) == cond for cond in conds):
|
|
36
|
+
args.extend(ConstantNode(value=block_indexes[targets[cond]]) for cond in range(len(conds)))
|
|
37
|
+
if None in targets:
|
|
38
|
+
default = block_indexes[targets[None]]
|
|
39
|
+
args.append(ConstantNode(value=default))
|
|
40
|
+
statements.append(FunctionNode(Op.SwitchIntegerWithDefault, args))
|
|
41
|
+
else:
|
|
42
|
+
for cond, target in targets.items():
|
|
43
|
+
if cond is None:
|
|
44
|
+
default = block_indexes[target]
|
|
45
|
+
args.append(ConstantNode(value=cond))
|
|
46
|
+
args.append(ConstantNode(value=block_indexes[target]))
|
|
47
|
+
args.append(ConstantNode(value=default))
|
|
48
|
+
statements.append(FunctionNode(Op.SwitchWithDefault, args))
|
|
41
49
|
block_statements.append(FunctionNode(Op.Execute, statements))
|
|
42
50
|
block_statements.append(ConstantNode(value=0))
|
|
43
51
|
return FunctionNode(Op.Block, [FunctionNode(Op.JumpLoop, block_statements)])
|
sonolus/backend/interpret.py
CHANGED
|
@@ -62,16 +62,16 @@ class Interpreter:
|
|
|
62
62
|
return self.run(default)
|
|
63
63
|
case Op.SwitchInteger:
|
|
64
64
|
test, *branches = args
|
|
65
|
-
test_result =
|
|
66
|
-
if 0 <= test_result < len(branches):
|
|
65
|
+
test_result = self.run(test)
|
|
66
|
+
if 0 <= test_result < len(branches) and int(test_result) == test_result:
|
|
67
67
|
return self.run(branches[test_result])
|
|
68
68
|
else:
|
|
69
69
|
return 0.0
|
|
70
70
|
case Op.SwitchIntegerWithDefault:
|
|
71
71
|
test, *branches, default = args
|
|
72
|
-
test_result =
|
|
73
|
-
if 0 <= test_result < len(branches):
|
|
74
|
-
return self.run(branches[test_result])
|
|
72
|
+
test_result = self.run(test)
|
|
73
|
+
if 0 <= test_result < len(branches) and int(test_result) == test_result:
|
|
74
|
+
return self.run(branches[int(test_result)])
|
|
75
75
|
else:
|
|
76
76
|
return self.run(default)
|
|
77
77
|
case Op.While:
|
|
@@ -242,10 +242,10 @@ class Interpreter:
|
|
|
242
242
|
return math.radians(self.run(args[0]))
|
|
243
243
|
case Op.Random:
|
|
244
244
|
lo, hi = (self.run(arg) for arg in args)
|
|
245
|
-
return lo
|
|
245
|
+
return random.uniform(lo, hi)
|
|
246
246
|
case Op.RandomInteger:
|
|
247
247
|
lo, hi = (self.ensure_int(self.run(arg)) for arg in args)
|
|
248
|
-
return random.
|
|
248
|
+
return random.randrange(lo, hi)
|
|
249
249
|
case Op.Rem:
|
|
250
250
|
return self.reduce_args(args, math.remainder)
|
|
251
251
|
case Op.Remap:
|
sonolus/backend/ir.py
CHANGED
|
@@ -41,6 +41,12 @@ class IRPureInstr:
|
|
|
41
41
|
def __str__(self):
|
|
42
42
|
return f"{self.op.name}({', '.join(map(str, self.args))})"
|
|
43
43
|
|
|
44
|
+
def __eq__(self, other):
|
|
45
|
+
return isinstance(other, IRPureInstr) and self.op == other.op and self.args == other.args
|
|
46
|
+
|
|
47
|
+
def __hash__(self):
|
|
48
|
+
return hash((self.op, tuple(self.args)))
|
|
49
|
+
|
|
44
50
|
|
|
45
51
|
class IRInstr:
|
|
46
52
|
op: Op
|
|
@@ -56,6 +62,12 @@ class IRInstr:
|
|
|
56
62
|
def __str__(self):
|
|
57
63
|
return f"{self.op.name}({', '.join(map(str, self.args))})"
|
|
58
64
|
|
|
65
|
+
def __eq__(self, other):
|
|
66
|
+
return isinstance(other, IRInstr) and self.op == other.op and self.args == other.args
|
|
67
|
+
|
|
68
|
+
def __hash__(self):
|
|
69
|
+
return hash((self.op, tuple(self.args)))
|
|
70
|
+
|
|
59
71
|
|
|
60
72
|
class IRGet:
|
|
61
73
|
place: Place
|
|
@@ -69,6 +81,12 @@ class IRGet:
|
|
|
69
81
|
def __str__(self):
|
|
70
82
|
return f"{self.place}"
|
|
71
83
|
|
|
84
|
+
def __eq__(self, other):
|
|
85
|
+
return isinstance(other, IRGet) and self.place == other.place
|
|
86
|
+
|
|
87
|
+
def __hash__(self):
|
|
88
|
+
return hash(self.place)
|
|
89
|
+
|
|
72
90
|
|
|
73
91
|
class IRSet:
|
|
74
92
|
place: Place
|
|
@@ -89,3 +107,9 @@ class IRSet:
|
|
|
89
107
|
return f"{self.place} := {self.value}"
|
|
90
108
|
case _:
|
|
91
109
|
raise TypeError(f"Invalid place: {self.place}")
|
|
110
|
+
|
|
111
|
+
def __eq__(self, other):
|
|
112
|
+
return isinstance(other, IRSet) and self.place == other.place and self.value == other.value
|
|
113
|
+
|
|
114
|
+
def __hash__(self):
|
|
115
|
+
return hash((self.place, self.value))
|
|
File without changes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from sonolus.backend.flow import BasicBlock, traverse_cfg_preorder
|
|
2
1
|
from sonolus.backend.ir import IRConst, IRGet, IRInstr, IRPureInstr, IRSet
|
|
3
|
-
from sonolus.backend.
|
|
4
|
-
from sonolus.backend.
|
|
2
|
+
from sonolus.backend.optimize.flow import BasicBlock, traverse_cfg_preorder
|
|
3
|
+
from sonolus.backend.optimize.liveness import LivenessAnalysis, get_live
|
|
4
|
+
from sonolus.backend.optimize.passes import CompilerPass
|
|
5
5
|
from sonolus.backend.place import BlockPlace, TempBlock
|
|
6
6
|
|
|
7
7
|
TEMP_SIZE = 4096
|
|
@@ -86,6 +86,7 @@ class Allocate(CompilerPass):
|
|
|
86
86
|
if block.size == 0:
|
|
87
87
|
offset = -1
|
|
88
88
|
else:
|
|
89
|
+
assert block in mapping, f"Missing mapping for {block}, dead variable not removed?"
|
|
89
90
|
offset = mapping[block] + offset
|
|
90
91
|
return BlockPlace(block=10000, index=self.update_stmt(index, mapping), offset=offset)
|
|
91
92
|
return BlockPlace(
|
|
@@ -4,11 +4,11 @@ import math
|
|
|
4
4
|
import operator
|
|
5
5
|
from typing import ClassVar
|
|
6
6
|
|
|
7
|
-
import sonolus.script.
|
|
8
|
-
from sonolus.backend.flow import BasicBlock, FlowEdge, traverse_cfg_preorder
|
|
7
|
+
import sonolus.script.internal.math_impls as smath
|
|
9
8
|
from sonolus.backend.ir import IRConst, IRGet, IRInstr, IRPureInstr, IRSet, IRStmt
|
|
10
9
|
from sonolus.backend.ops import Op
|
|
11
|
-
from sonolus.backend.
|
|
10
|
+
from sonolus.backend.optimize.flow import BasicBlock, FlowEdge, traverse_cfg_preorder
|
|
11
|
+
from sonolus.backend.optimize.passes import CompilerPass
|
|
12
12
|
from sonolus.backend.place import BlockPlace, SSAPlace, TempBlock
|
|
13
13
|
|
|
14
14
|
|
|
@@ -68,7 +68,7 @@ class SparseConditionalConstantPropagation(CompilerPass):
|
|
|
68
68
|
executable_edges: set[FlowEdge] = set()
|
|
69
69
|
|
|
70
70
|
# BasicBlock key means the block's test
|
|
71
|
-
values: dict[SSAPlace | BasicBlock, Value] = {}
|
|
71
|
+
values: dict[SSAPlace | BasicBlock, Value] = {SSAPlace("err", 0): UNDEF}
|
|
72
72
|
defs: dict[SSAPlace | BasicBlock, IRStmt | dict[FlowEdge, SSAPlace]] = {}
|
|
73
73
|
places_to_blocks: dict[SSAPlace, BasicBlock] = {}
|
|
74
74
|
reachable_blocks: set[BasicBlock] = set()
|
|
@@ -164,9 +164,9 @@ class SparseConditionalConstantPropagation(CompilerPass):
|
|
|
164
164
|
flow_worklist.update(p.outgoing)
|
|
165
165
|
reachable_blocks.update(e.dst for e in p.outgoing)
|
|
166
166
|
else:
|
|
167
|
-
taken_edge = next(
|
|
168
|
-
(edge for edge in p.outgoing if edge.cond
|
|
169
|
-
)
|
|
167
|
+
taken_edge = next(
|
|
168
|
+
(edge for edge in p.outgoing if edge.cond == new_test_value), None
|
|
169
|
+
) or next((edge for edge in p.outgoing if edge.cond is None), None)
|
|
170
170
|
if taken_edge:
|
|
171
171
|
flow_worklist.add(taken_edge)
|
|
172
172
|
reachable_blocks.add(taken_edge.dst)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from sonolus.backend.flow import BasicBlock, traverse_cfg_preorder
|
|
2
1
|
from sonolus.backend.ir import IRConst, IRGet, IRInstr, IRPureInstr, IRSet
|
|
3
|
-
from sonolus.backend.
|
|
4
|
-
from sonolus.backend.
|
|
2
|
+
from sonolus.backend.optimize.flow import BasicBlock, traverse_cfg_preorder
|
|
3
|
+
from sonolus.backend.optimize.liveness import LivenessAnalysis, get_live
|
|
4
|
+
from sonolus.backend.optimize.passes import CompilerPass
|
|
5
5
|
from sonolus.backend.place import BlockPlace, SSAPlace, TempBlock
|
|
6
6
|
|
|
7
7
|
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from sonolus.backend.ir import IRConst, IRGet, IRInstr, IRPureInstr, IRSet, IRStmt
|
|
2
|
+
from sonolus.backend.optimize.flow import BasicBlock, traverse_cfg_preorder
|
|
3
|
+
from sonolus.backend.optimize.liveness import HasLiveness, LivenessAnalysis, get_live, get_live_phi_targets
|
|
4
|
+
from sonolus.backend.optimize.passes import CompilerPass
|
|
5
|
+
from sonolus.backend.place import BlockPlace, SSAPlace, TempBlock
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class UnreachableCodeElimination(CompilerPass):
|
|
9
|
+
def run(self, entry: BasicBlock) -> BasicBlock:
|
|
10
|
+
original_blocks = [*traverse_cfg_preorder(entry)]
|
|
11
|
+
worklist = {entry}
|
|
12
|
+
visited = set()
|
|
13
|
+
while worklist:
|
|
14
|
+
block = worklist.pop()
|
|
15
|
+
if block in visited:
|
|
16
|
+
continue
|
|
17
|
+
visited.add(block)
|
|
18
|
+
match block.test:
|
|
19
|
+
case IRConst(value=value):
|
|
20
|
+
block.test = IRConst(0)
|
|
21
|
+
taken_edge = next(
|
|
22
|
+
(edge for edge in block.outgoing if edge.cond == value),
|
|
23
|
+
None,
|
|
24
|
+
) or next((edge for edge in block.outgoing if edge.cond is None), None)
|
|
25
|
+
assert not block.outgoing or taken_edge
|
|
26
|
+
for edge in [*block.outgoing]:
|
|
27
|
+
if edge is not taken_edge:
|
|
28
|
+
edge.dst.incoming.remove(edge)
|
|
29
|
+
block.outgoing.remove(edge)
|
|
30
|
+
if taken_edge:
|
|
31
|
+
taken_edge.cond = None
|
|
32
|
+
block.outgoing.add(taken_edge)
|
|
33
|
+
worklist.add(taken_edge.dst)
|
|
34
|
+
case _:
|
|
35
|
+
worklist.update(edge.dst for edge in block.outgoing)
|
|
36
|
+
for block in original_blocks:
|
|
37
|
+
if block not in visited:
|
|
38
|
+
for edge in block.outgoing:
|
|
39
|
+
edge.dst.incoming.remove(edge)
|
|
40
|
+
else:
|
|
41
|
+
for args in block.phis.values():
|
|
42
|
+
for src_block in [*args]:
|
|
43
|
+
if src_block not in visited:
|
|
44
|
+
args.pop(src_block)
|
|
45
|
+
return entry
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class DeadCodeElimination(CompilerPass):
|
|
49
|
+
def run(self, entry: BasicBlock) -> BasicBlock:
|
|
50
|
+
uses = set()
|
|
51
|
+
defs = {}
|
|
52
|
+
for block in traverse_cfg_preorder(entry):
|
|
53
|
+
for statement in block.statements:
|
|
54
|
+
self.handle_statement(statement, uses, defs)
|
|
55
|
+
for target, args in block.phis.items():
|
|
56
|
+
if target not in defs:
|
|
57
|
+
defs[target] = []
|
|
58
|
+
defs[target].append(tuple(args.values()))
|
|
59
|
+
self.update_uses(block.test, uses)
|
|
60
|
+
|
|
61
|
+
queue = [*uses]
|
|
62
|
+
while queue:
|
|
63
|
+
val = queue.pop()
|
|
64
|
+
if val not in defs:
|
|
65
|
+
continue
|
|
66
|
+
for stmt in defs[val]:
|
|
67
|
+
if isinstance(stmt, tuple):
|
|
68
|
+
stmt_uses = stmt
|
|
69
|
+
else:
|
|
70
|
+
stmt_uses = self.update_uses(stmt, set())
|
|
71
|
+
for use in stmt_uses:
|
|
72
|
+
if use not in uses:
|
|
73
|
+
uses.add(use)
|
|
74
|
+
queue.append(use)
|
|
75
|
+
|
|
76
|
+
for block in traverse_cfg_preorder(entry):
|
|
77
|
+
live_stmts = []
|
|
78
|
+
for statement in block.statements:
|
|
79
|
+
match statement:
|
|
80
|
+
case IRSet(place=place, value=value):
|
|
81
|
+
is_live = not (
|
|
82
|
+
(isinstance(place, SSAPlace) and place not in uses)
|
|
83
|
+
or (
|
|
84
|
+
isinstance(place, BlockPlace)
|
|
85
|
+
and isinstance(place.block, TempBlock)
|
|
86
|
+
and place.block not in uses
|
|
87
|
+
)
|
|
88
|
+
or (isinstance(value, IRGet) and place == value.place)
|
|
89
|
+
)
|
|
90
|
+
if is_live:
|
|
91
|
+
live_stmts.append(statement)
|
|
92
|
+
elif isinstance(value, IRInstr) and value.op.side_effects:
|
|
93
|
+
live_stmts.append(value)
|
|
94
|
+
case other:
|
|
95
|
+
live_stmts.append(other)
|
|
96
|
+
block.statements = live_stmts
|
|
97
|
+
block.phis = {place: phi for place, phi in block.phis.items() if place in uses}
|
|
98
|
+
return entry
|
|
99
|
+
|
|
100
|
+
def handle_statement(
|
|
101
|
+
self,
|
|
102
|
+
stmt: IRStmt | BlockPlace | SSAPlace | TempBlock | int,
|
|
103
|
+
uses: set[HasLiveness],
|
|
104
|
+
defs: dict[HasLiveness, list[IRStmt | tuple[HasLiveness]]],
|
|
105
|
+
):
|
|
106
|
+
if isinstance(stmt, IRSet):
|
|
107
|
+
place = stmt.place
|
|
108
|
+
value = stmt.value
|
|
109
|
+
if isinstance(place, SSAPlace):
|
|
110
|
+
if place not in defs:
|
|
111
|
+
defs[place] = []
|
|
112
|
+
defs[place].append(stmt)
|
|
113
|
+
if isinstance(value, IRInstr) and value.op.side_effects:
|
|
114
|
+
self.update_uses(value, uses)
|
|
115
|
+
elif isinstance(place, BlockPlace) and isinstance(place.block, TempBlock):
|
|
116
|
+
if place.block not in defs:
|
|
117
|
+
defs[place.block] = []
|
|
118
|
+
defs[place.block].append(stmt)
|
|
119
|
+
if isinstance(value, IRInstr) and value.op.side_effects:
|
|
120
|
+
self.update_uses(value, uses)
|
|
121
|
+
else:
|
|
122
|
+
self.update_uses(place, uses)
|
|
123
|
+
self.update_uses(value, uses)
|
|
124
|
+
else:
|
|
125
|
+
self.update_uses(stmt, uses)
|
|
126
|
+
|
|
127
|
+
def update_uses(
|
|
128
|
+
self, stmt: IRStmt | BlockPlace | SSAPlace | TempBlock | int, uses: set[HasLiveness]
|
|
129
|
+
) -> set[HasLiveness]:
|
|
130
|
+
match stmt:
|
|
131
|
+
case IRPureInstr(op=_, args=args) | IRInstr(op=_, args=args):
|
|
132
|
+
for arg in args:
|
|
133
|
+
self.update_uses(arg, uses)
|
|
134
|
+
case IRGet(place=place):
|
|
135
|
+
self.update_uses(place, uses)
|
|
136
|
+
case IRSet(place=place, value=value):
|
|
137
|
+
if isinstance(place, BlockPlace):
|
|
138
|
+
if not isinstance(place.block, TempBlock):
|
|
139
|
+
self.update_uses(place.block, uses)
|
|
140
|
+
self.update_uses(place.index, uses)
|
|
141
|
+
self.update_uses(value, uses)
|
|
142
|
+
case IRConst() | int():
|
|
143
|
+
pass
|
|
144
|
+
case BlockPlace(block=block, index=index, offset=_):
|
|
145
|
+
self.update_uses(block, uses)
|
|
146
|
+
self.update_uses(index, uses)
|
|
147
|
+
case TempBlock() | SSAPlace():
|
|
148
|
+
uses.add(stmt)
|
|
149
|
+
case _:
|
|
150
|
+
raise TypeError(f"Unexpected statement type: {type(stmt)}")
|
|
151
|
+
return uses
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class AdvancedDeadCodeElimination(CompilerPass):
|
|
155
|
+
"""Slower than regular DeadCodeElimination but can handle cases like definitions after the last use and so on."""
|
|
156
|
+
|
|
157
|
+
def requires(self) -> set[CompilerPass]:
|
|
158
|
+
return {LivenessAnalysis()}
|
|
159
|
+
|
|
160
|
+
def run(self, entry: BasicBlock) -> BasicBlock:
|
|
161
|
+
for block in traverse_cfg_preorder(entry):
|
|
162
|
+
live_stmts = []
|
|
163
|
+
for statement in block.statements:
|
|
164
|
+
live = get_live(statement)
|
|
165
|
+
match statement:
|
|
166
|
+
case IRSet(place=place, value=value):
|
|
167
|
+
is_live = not (
|
|
168
|
+
(isinstance(place, SSAPlace) and place not in live)
|
|
169
|
+
or (
|
|
170
|
+
isinstance(place, BlockPlace)
|
|
171
|
+
and isinstance(place.block, TempBlock)
|
|
172
|
+
and place.block not in live
|
|
173
|
+
)
|
|
174
|
+
or (isinstance(value, IRGet) and place == value.place)
|
|
175
|
+
)
|
|
176
|
+
if is_live:
|
|
177
|
+
live_stmts.append(statement)
|
|
178
|
+
elif isinstance(value, IRInstr) and value.op.side_effects:
|
|
179
|
+
live_stmts.append(value)
|
|
180
|
+
value.live = live
|
|
181
|
+
case other:
|
|
182
|
+
live_stmts.append(other)
|
|
183
|
+
block.statements = live_stmts
|
|
184
|
+
block.phis = {place: phi for place, phi in block.phis.items() if place in get_live_phi_targets(block)}
|
|
185
|
+
return entry
|
|
@@ -1,23 +1,8 @@
|
|
|
1
|
-
from sonolus.backend.flow import (
|
|
1
|
+
from sonolus.backend.optimize.flow import (
|
|
2
2
|
BasicBlock,
|
|
3
3
|
traverse_cfg_reverse_postorder,
|
|
4
4
|
)
|
|
5
|
-
from sonolus.backend.passes import CompilerPass
|
|
6
|
-
|
|
7
|
-
# traverse_cfg_preorder(entry: BasicBlock) -> Iterator[BasicBlock]
|
|
8
|
-
# traverse_cfg_postorder(entry: BasicBlock) -> Iterator[BasicBlock]
|
|
9
|
-
# traverse_cfg_reverse_postorder(entry: BasicBlock) -> Iterator[BasicBlock]
|
|
10
|
-
|
|
11
|
-
# class BasicBlock:
|
|
12
|
-
# phis: dict[SSAPlace, dict[Self, SSAPlace]]
|
|
13
|
-
# statements: list[IRStmt]
|
|
14
|
-
# test: IRExpr
|
|
15
|
-
# incoming: set[FlowEdge]
|
|
16
|
-
# outgoing: set[FlowEdge]
|
|
17
|
-
|
|
18
|
-
# class FlowEdge:
|
|
19
|
-
# src: "BasicBlock"
|
|
20
|
-
# dst: "BasicBlock"
|
|
5
|
+
from sonolus.backend.optimize.passes import CompilerPass
|
|
21
6
|
|
|
22
7
|
|
|
23
8
|
class DominanceFrontiers(CompilerPass):
|
|
@@ -87,11 +87,10 @@ def cfg_to_mermaid(entry: BasicBlock):
|
|
|
87
87
|
else:
|
|
88
88
|
return "{}"
|
|
89
89
|
|
|
90
|
-
block_indexes = {block: i for i, block in enumerate(
|
|
90
|
+
block_indexes = {block: i for i, block in enumerate(traverse_cfg_reverse_postorder(entry))}
|
|
91
91
|
|
|
92
92
|
lines = ["Entry([Entry]) --> 0"]
|
|
93
|
-
for block in
|
|
94
|
-
index = block_indexes[block]
|
|
93
|
+
for block, index in block_indexes.items():
|
|
95
94
|
lines.append(f"{index}[{pre(fmt([f'#{index}', *(
|
|
96
95
|
f"{dst} := phi({", ".join(f"{block_indexes.get(src_block, "<dead>")}: {src_place}"
|
|
97
96
|
for src_block, src_place
|
|
@@ -106,7 +105,7 @@ def cfg_to_mermaid(entry: BasicBlock):
|
|
|
106
105
|
case {None: target, **other} if not other:
|
|
107
106
|
lines.append(f"{index} --> {block_indexes[target]}")
|
|
108
107
|
case {0: f_branch, None: t_branch, **other} if not other:
|
|
109
|
-
lines.append(f"{index}_{{{pre(fmt([block.test]))}}}")
|
|
108
|
+
lines.append(f"{index}_{{{{{pre(fmt([block.test]))}}}}}")
|
|
110
109
|
lines.append(f"{index} --> {index}_")
|
|
111
110
|
lines.append(f"{index}_ --> |true| {block_indexes[t_branch]}")
|
|
112
111
|
lines.append(f"{index}_ --> |false| {block_indexes[f_branch]}")
|
|
@@ -114,7 +113,9 @@ def cfg_to_mermaid(entry: BasicBlock):
|
|
|
114
113
|
lines.append(f"{index}_{{{{{pre(fmt([block.test]))}}}}}")
|
|
115
114
|
lines.append(f"{index} --> {index}_")
|
|
116
115
|
for cond, target in tgt.items():
|
|
117
|
-
lines.append(
|
|
116
|
+
lines.append(
|
|
117
|
+
f"{index}_ --> |{pre(fmt([cond if cond is not None else "default"]))}| {block_indexes[target]}"
|
|
118
|
+
)
|
|
118
119
|
lines.append("Exit([Exit])")
|
|
119
120
|
|
|
120
121
|
body = textwrap.indent("\n".join(lines), " ")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from sonolus.backend.flow import BasicBlock, traverse_cfg_preorder
|
|
2
1
|
from sonolus.backend.ir import IRConst, IRGet, IRInstr, IRPureInstr, IRSet, IRStmt
|
|
3
|
-
from sonolus.backend.
|
|
2
|
+
from sonolus.backend.optimize.flow import BasicBlock, traverse_cfg_preorder
|
|
3
|
+
from sonolus.backend.optimize.passes import CompilerPass
|
|
4
4
|
from sonolus.backend.place import BlockPlace, SSAPlace, TempBlock
|
|
5
5
|
|
|
6
6
|
|
|
@@ -11,16 +11,7 @@ class InlineVars(CompilerPass):
|
|
|
11
11
|
|
|
12
12
|
for block in traverse_cfg_preorder(entry):
|
|
13
13
|
for stmt in block.statements:
|
|
14
|
-
|
|
15
|
-
isinstance(stmt, IRSet)
|
|
16
|
-
and isinstance(stmt.place, SSAPlace)
|
|
17
|
-
and isinstance(stmt.value, IRGet)
|
|
18
|
-
and isinstance(stmt.value.place, SSAPlace)
|
|
19
|
-
):
|
|
20
|
-
# This is effectively an alias
|
|
21
|
-
pass
|
|
22
|
-
else:
|
|
23
|
-
self.count_uses(stmt, use_counts)
|
|
14
|
+
self.count_uses(stmt, use_counts)
|
|
24
15
|
if isinstance(stmt, IRSet) and isinstance(stmt.place, SSAPlace):
|
|
25
16
|
definitions[stmt.place] = stmt.value
|
|
26
17
|
self.count_uses(block.test, use_counts)
|
|
@@ -50,11 +41,7 @@ class InlineVars(CompilerPass):
|
|
|
50
41
|
defn = self.substitute(defn, subs)
|
|
51
42
|
definitions[p] = defn
|
|
52
43
|
|
|
53
|
-
valid = {
|
|
54
|
-
p
|
|
55
|
-
for p, count in use_counts.items()
|
|
56
|
-
if (p in definitions and count == 1 and self.is_inlinable(definitions[p]))
|
|
57
|
-
}
|
|
44
|
+
valid = {p for p in definitions if self.is_inlinable(definitions[p]) and use_counts.get(p, 0) <= 1}
|
|
58
45
|
|
|
59
46
|
for block in traverse_cfg_preorder(entry):
|
|
60
47
|
new_statements = []
|