sonolus.py 0.1.4__py3-none-any.whl → 0.1.5__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.

Files changed (77) hide show
  1. sonolus/backend/finalize.py +18 -10
  2. sonolus/backend/interpret.py +7 -7
  3. sonolus/backend/ir.py +24 -0
  4. sonolus/backend/optimize/__init__.py +0 -0
  5. sonolus/backend/{allocate.py → optimize/allocate.py} +4 -3
  6. sonolus/backend/{constant_evaluation.py → optimize/constant_evaluation.py} +7 -7
  7. sonolus/backend/{coalesce.py → optimize/copy_coalesce.py} +3 -3
  8. sonolus/backend/optimize/dead_code.py +185 -0
  9. sonolus/backend/{dominance.py → optimize/dominance.py} +2 -17
  10. sonolus/backend/{flow.py → optimize/flow.py} +6 -5
  11. sonolus/backend/{inlining.py → optimize/inlining.py} +4 -17
  12. sonolus/backend/{liveness.py → optimize/liveness.py} +69 -65
  13. sonolus/backend/optimize/optimize.py +44 -0
  14. sonolus/backend/{passes.py → optimize/passes.py} +1 -1
  15. sonolus/backend/optimize/simplify.py +191 -0
  16. sonolus/backend/{ssa.py → optimize/ssa.py} +31 -18
  17. sonolus/backend/place.py +17 -25
  18. sonolus/backend/utils.py +10 -0
  19. sonolus/backend/visitor.py +360 -101
  20. sonolus/build/compile.py +8 -8
  21. sonolus/build/engine.py +10 -5
  22. sonolus/script/archetype.py +419 -137
  23. sonolus/script/array.py +25 -8
  24. sonolus/script/array_like.py +297 -0
  25. sonolus/script/bucket.py +73 -11
  26. sonolus/script/containers.py +234 -51
  27. sonolus/script/debug.py +8 -8
  28. sonolus/script/easing.py +147 -105
  29. sonolus/script/effect.py +60 -0
  30. sonolus/script/engine.py +71 -4
  31. sonolus/script/globals.py +66 -32
  32. sonolus/script/instruction.py +79 -25
  33. sonolus/script/internal/builtin_impls.py +138 -27
  34. sonolus/script/internal/constant.py +139 -0
  35. sonolus/script/internal/context.py +14 -5
  36. sonolus/script/internal/dict_impl.py +65 -0
  37. sonolus/script/internal/generic.py +6 -9
  38. sonolus/script/internal/impl.py +38 -13
  39. sonolus/script/internal/introspection.py +5 -2
  40. sonolus/script/{math.py → internal/math_impls.py} +28 -28
  41. sonolus/script/internal/native.py +3 -3
  42. sonolus/script/internal/random.py +67 -0
  43. sonolus/script/internal/range.py +81 -0
  44. sonolus/script/internal/transient.py +51 -0
  45. sonolus/script/internal/tuple_impl.py +113 -0
  46. sonolus/script/interval.py +234 -16
  47. sonolus/script/iterator.py +120 -167
  48. sonolus/script/level.py +24 -0
  49. sonolus/script/num.py +79 -47
  50. sonolus/script/options.py +78 -12
  51. sonolus/script/particle.py +37 -4
  52. sonolus/script/pointer.py +4 -4
  53. sonolus/script/print.py +22 -1
  54. sonolus/script/project.py +8 -0
  55. sonolus/script/{graphics.py → quad.py} +75 -12
  56. sonolus/script/record.py +44 -13
  57. sonolus/script/runtime.py +50 -1
  58. sonolus/script/sprite.py +197 -112
  59. sonolus/script/text.py +2 -0
  60. sonolus/script/timing.py +72 -0
  61. sonolus/script/transform.py +296 -66
  62. sonolus/script/ui.py +134 -78
  63. sonolus/script/values.py +6 -13
  64. sonolus/script/vec.py +118 -3
  65. {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
  66. sonolus_py-0.1.5.dist-info/RECORD +89 -0
  67. sonolus/backend/dead_code.py +0 -80
  68. sonolus/backend/optimize.py +0 -37
  69. sonolus/backend/simplify.py +0 -47
  70. sonolus/script/comptime.py +0 -160
  71. sonolus/script/random.py +0 -14
  72. sonolus/script/range.py +0 -58
  73. sonolus_py-0.1.4.dist-info/RECORD +0 -84
  74. /sonolus/script/{callbacks.py → internal/callbacks.py} +0 -0
  75. {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +0 -0
  76. {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
  77. {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +0 -0
@@ -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(traverse_cfg_preorder(entry))}
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 tgt:
31
+ case dict() as targets:
32
32
  args = [ir_to_engine_node(block.test)]
33
33
  default = len(block_indexes)
34
- for cond, target in tgt.items():
35
- if cond is None:
36
- default = block_indexes[target]
37
- args.append(ConstantNode(value=cond))
38
- args.append(ConstantNode(value=block_indexes[target]))
39
- args.append(ConstantNode(value=default))
40
- statements.append(FunctionNode(Op.SwitchWithDefault, args))
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)])
@@ -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 = int(self.run(test))
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 = int(self.run(test))
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 + (hi - lo) * random.random()
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.randint(lo, hi)
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.liveness import LivenessAnalysis, get_live
4
- from sonolus.backend.passes import CompilerPass
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.math as smath
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.passes import CompilerPass
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(edge for edge in p.outgoing if edge.cond == new_test_value) or next(
168
- (edge for edge in p.outgoing if edge.cond is None), None
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.liveness import LivenessAnalysis, get_live
4
- from sonolus.backend.passes import CompilerPass
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(traverse_cfg_preorder(entry))}
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 traverse_cfg_preorder(entry):
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(f"{index}_ --> |{pre(fmt([cond or "default"]))}| {block_indexes[target]}")
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.passes import CompilerPass
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
- if (
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 = []