sonolus.py 0.1.2__py3-none-any.whl → 0.1.4__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 (72) hide show
  1. sonolus/backend/allocate.py +125 -51
  2. sonolus/backend/blocks.py +756 -756
  3. sonolus/backend/coalesce.py +85 -0
  4. sonolus/backend/constant_evaluation.py +374 -0
  5. sonolus/backend/dead_code.py +80 -0
  6. sonolus/backend/dominance.py +111 -0
  7. sonolus/backend/excepthook.py +37 -37
  8. sonolus/backend/finalize.py +69 -69
  9. sonolus/backend/flow.py +121 -92
  10. sonolus/backend/inlining.py +150 -0
  11. sonolus/backend/ir.py +5 -3
  12. sonolus/backend/liveness.py +173 -0
  13. sonolus/backend/mode.py +24 -24
  14. sonolus/backend/node.py +40 -40
  15. sonolus/backend/ops.py +197 -197
  16. sonolus/backend/optimize.py +37 -9
  17. sonolus/backend/passes.py +52 -6
  18. sonolus/backend/simplify.py +47 -30
  19. sonolus/backend/ssa.py +187 -0
  20. sonolus/backend/utils.py +48 -48
  21. sonolus/backend/visitor.py +892 -880
  22. sonolus/build/cli.py +7 -1
  23. sonolus/build/compile.py +88 -90
  24. sonolus/build/engine.py +55 -5
  25. sonolus/build/level.py +24 -23
  26. sonolus/build/node.py +43 -43
  27. sonolus/script/archetype.py +23 -6
  28. sonolus/script/array.py +2 -2
  29. sonolus/script/bucket.py +191 -191
  30. sonolus/script/callbacks.py +127 -115
  31. sonolus/script/comptime.py +1 -1
  32. sonolus/script/containers.py +23 -0
  33. sonolus/script/debug.py +19 -3
  34. sonolus/script/easing.py +323 -0
  35. sonolus/script/effect.py +131 -131
  36. sonolus/script/engine.py +37 -1
  37. sonolus/script/globals.py +269 -269
  38. sonolus/script/graphics.py +200 -150
  39. sonolus/script/instruction.py +151 -0
  40. sonolus/script/internal/__init__.py +5 -5
  41. sonolus/script/internal/builtin_impls.py +144 -144
  42. sonolus/script/internal/context.py +12 -4
  43. sonolus/script/internal/descriptor.py +17 -17
  44. sonolus/script/internal/introspection.py +14 -14
  45. sonolus/script/internal/native.py +40 -38
  46. sonolus/script/internal/value.py +3 -3
  47. sonolus/script/interval.py +120 -112
  48. sonolus/script/iterator.py +214 -211
  49. sonolus/script/math.py +30 -1
  50. sonolus/script/num.py +1 -1
  51. sonolus/script/options.py +191 -191
  52. sonolus/script/particle.py +157 -157
  53. sonolus/script/pointer.py +30 -30
  54. sonolus/script/{preview.py → print.py} +81 -81
  55. sonolus/script/random.py +14 -0
  56. sonolus/script/range.py +58 -58
  57. sonolus/script/record.py +3 -3
  58. sonolus/script/runtime.py +45 -6
  59. sonolus/script/sprite.py +333 -333
  60. sonolus/script/text.py +407 -407
  61. sonolus/script/timing.py +42 -42
  62. sonolus/script/transform.py +77 -23
  63. sonolus/script/ui.py +160 -160
  64. sonolus/script/vec.py +81 -72
  65. {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/METADATA +1 -2
  66. sonolus_py-0.1.4.dist-info/RECORD +84 -0
  67. {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/WHEEL +1 -1
  68. {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/licenses/LICENSE +21 -21
  69. sonolus/build/defaults.py +0 -32
  70. sonolus/script/icon.py +0 -73
  71. sonolus_py-0.1.2.dist-info/RECORD +0 -76
  72. {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/entry_points.txt +0 -0
@@ -1,51 +1,125 @@
1
- from sonolus.backend.flow import BasicBlock, traverse_cfg_preorder
2
- from sonolus.backend.ir import IRConst, IRGet, IRInstr, IRPureInstr, IRSet
3
- from sonolus.backend.passes import CompilerPass
4
- from sonolus.backend.place import BlockPlace, TempBlock
5
-
6
-
7
- class AllocateBasic(CompilerPass):
8
- def run(self, entry: BasicBlock):
9
- offsets = {}
10
- index = 16
11
-
12
- def process(stmt):
13
- nonlocal index
14
- match stmt:
15
- case int():
16
- return stmt
17
- case IRConst():
18
- return stmt
19
- case IRPureInstr(op=op, args=args):
20
- return IRPureInstr(
21
- op=op,
22
- args=[process(arg) for arg in args],
23
- )
24
- case IRInstr(op=op, args=args):
25
- return IRInstr(
26
- op=op,
27
- args=[process(arg) for arg in args],
28
- )
29
- case IRGet(place=place):
30
- return IRGet(place=process(place))
31
- case IRSet(place=place, value=value):
32
- return IRSet(place=process(place), value=process(value))
33
- case BlockPlace() as place:
34
- if isinstance(place.block, TempBlock):
35
- if place.block not in offsets:
36
- offsets[place.block] = index
37
- index += place.block.size
38
- return BlockPlace(10000, process(place.index), place.offset + offsets[place.block])
39
- return BlockPlace(
40
- process(place.block) if isinstance(place.block, BlockPlace) else place.block,
41
- process(place.index),
42
- process(place.offset),
43
- )
44
- case _:
45
- raise NotImplementedError
46
-
47
- for block in traverse_cfg_preorder(entry):
48
- block.statements = [process(statement) for statement in block.statements]
49
- block.test = process(block.test)
50
-
51
- return entry
1
+ from sonolus.backend.flow import BasicBlock, traverse_cfg_preorder
2
+ 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
5
+ from sonolus.backend.place import BlockPlace, TempBlock
6
+
7
+ TEMP_SIZE = 4096
8
+
9
+
10
+ class AllocateBasic(CompilerPass):
11
+ """Allocate temporary memory for temporary variables without considering lifetimes."""
12
+
13
+ def run(self, entry: BasicBlock):
14
+ offsets = {}
15
+ index = 0
16
+
17
+ def process(stmt):
18
+ nonlocal index
19
+ match stmt:
20
+ case int():
21
+ return stmt
22
+ case IRConst():
23
+ return stmt
24
+ case IRPureInstr(op=op, args=args):
25
+ return IRPureInstr(
26
+ op=op,
27
+ args=[process(arg) for arg in args],
28
+ )
29
+ case IRInstr(op=op, args=args):
30
+ return IRInstr(
31
+ op=op,
32
+ args=[process(arg) for arg in args],
33
+ )
34
+ case IRGet(place=place):
35
+ return IRGet(place=process(place))
36
+ case IRSet(place=place, value=value):
37
+ return IRSet(place=process(place), value=process(value))
38
+ case BlockPlace() as place:
39
+ if isinstance(place.block, TempBlock):
40
+ if place.block not in offsets:
41
+ offsets[place.block] = index
42
+ index += place.block.size
43
+ if index >= TEMP_SIZE:
44
+ raise ValueError("Temporary memory limit exceeded")
45
+ return BlockPlace(10000, process(place.index), place.offset + offsets[place.block])
46
+ return BlockPlace(
47
+ process(place.block) if isinstance(place.block, BlockPlace) else place.block,
48
+ process(place.index),
49
+ process(place.offset),
50
+ )
51
+ case _:
52
+ raise TypeError(f"Unsupported statement: {stmt}")
53
+
54
+ for block in traverse_cfg_preorder(entry):
55
+ block.statements = [process(statement) for statement in block.statements]
56
+ block.test = process(block.test)
57
+
58
+ return entry
59
+
60
+
61
+ class Allocate(CompilerPass):
62
+ def requires(self) -> set[CompilerPass]:
63
+ return {LivenessAnalysis()}
64
+
65
+ def run(self, entry: BasicBlock):
66
+ mapping = self.get_mapping(entry)
67
+ for block in traverse_cfg_preorder(entry):
68
+ block.statements = [self.update_stmt(statement, mapping) for statement in block.statements]
69
+ block.test = self.update_stmt(block.test, mapping)
70
+ return entry
71
+
72
+ def update_stmt(self, stmt, mapping: dict[TempBlock, int]):
73
+ match stmt:
74
+ case int() | float() | IRConst():
75
+ return stmt
76
+ case IRInstr(op=op, args=args):
77
+ return IRInstr(op=op, args=[self.update_stmt(arg, mapping) for arg in args])
78
+ case IRPureInstr(op=op, args=args):
79
+ return IRPureInstr(op=op, args=[self.update_stmt(arg, mapping) for arg in args])
80
+ case IRGet(place=place):
81
+ return IRGet(place=self.update_stmt(place, mapping))
82
+ case IRSet(place=place, value=value):
83
+ return IRSet(place=self.update_stmt(place, mapping), value=self.update_stmt(value, mapping))
84
+ case BlockPlace(block=block, index=index, offset=offset):
85
+ if isinstance(block, TempBlock):
86
+ if block.size == 0:
87
+ offset = -1
88
+ else:
89
+ offset = mapping[block] + offset
90
+ return BlockPlace(block=10000, index=self.update_stmt(index, mapping), offset=offset)
91
+ return BlockPlace(
92
+ block=self.update_stmt(block, mapping),
93
+ index=self.update_stmt(index, mapping),
94
+ offset=self.update_stmt(offset, mapping),
95
+ )
96
+ case _:
97
+ raise TypeError(f"Unsupported statement: {stmt}")
98
+
99
+ def get_mapping(self, entry: BasicBlock) -> dict[TempBlock, int]:
100
+ interference = self.get_interference(entry)
101
+ offsets: dict[TempBlock, int] = {}
102
+
103
+ for block, others in sorted(interference.items(), key=lambda x: -x[0].size):
104
+ size = block.size
105
+ offset = 0
106
+ for other in sorted(others, key=lambda x: offsets.get(x, 0) + x.size):
107
+ if other not in offsets:
108
+ continue
109
+ other_offset = offsets[other]
110
+ if offset + size > other_offset or other_offset + other.size > offset:
111
+ offset = other_offset + other.size
112
+ if offset + size > TEMP_SIZE:
113
+ raise ValueError("Temporary memory limit exceeded")
114
+ offsets[block] = offset
115
+
116
+ return offsets
117
+
118
+ def get_interference(self, entry: BasicBlock) -> dict[TempBlock, set[TempBlock]]:
119
+ result = {}
120
+ for block in traverse_cfg_preorder(entry):
121
+ for stmt in [*block.statements, block.test]:
122
+ live = {p for p in get_live(stmt) if isinstance(p, TempBlock) and p.size > 0}
123
+ for place in live:
124
+ result.setdefault(place, set()).update(live - {place})
125
+ return result