sonolus.py 0.1.3__tar.gz → 0.1.4__tar.gz

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 (113) hide show
  1. sonolus_py-0.1.4/.github/workflows/publish.yaml +52 -0
  2. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/LICENSE +21 -21
  3. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/PKG-INFO +1 -1
  4. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/pyproject.toml +1 -1
  5. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/scripts/generate.py +91 -91
  6. sonolus_py-0.1.4/sonolus/backend/allocate.py +125 -0
  7. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/blocks.py +756 -756
  8. sonolus_py-0.1.4/sonolus/backend/coalesce.py +85 -0
  9. sonolus_py-0.1.4/sonolus/backend/constant_evaluation.py +374 -0
  10. sonolus_py-0.1.4/sonolus/backend/dead_code.py +80 -0
  11. sonolus_py-0.1.4/sonolus/backend/dominance.py +111 -0
  12. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/excepthook.py +37 -37
  13. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/finalize.py +69 -69
  14. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/flow.py +121 -92
  15. sonolus_py-0.1.4/sonolus/backend/inlining.py +150 -0
  16. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/ir.py +5 -3
  17. sonolus_py-0.1.4/sonolus/backend/liveness.py +173 -0
  18. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/mode.py +24 -24
  19. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/node.py +40 -40
  20. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/ops.py +197 -197
  21. sonolus_py-0.1.4/sonolus/backend/optimize.py +37 -0
  22. sonolus_py-0.1.4/sonolus/backend/passes.py +52 -0
  23. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/simplify.py +47 -30
  24. sonolus_py-0.1.4/sonolus/backend/ssa.py +187 -0
  25. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/utils.py +48 -48
  26. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/visitor.py +892 -882
  27. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/build/cli.py +7 -1
  28. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/build/compile.py +88 -90
  29. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/build/level.py +24 -23
  30. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/build/node.py +43 -43
  31. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/archetype.py +23 -6
  32. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/array.py +2 -2
  33. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/bucket.py +191 -191
  34. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/callbacks.py +127 -127
  35. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/comptime.py +1 -1
  36. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/containers.py +23 -0
  37. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/debug.py +19 -3
  38. sonolus_py-0.1.4/sonolus/script/easing.py +323 -0
  39. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/effect.py +131 -131
  40. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/globals.py +269 -269
  41. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/graphics.py +200 -150
  42. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/instruction.py +151 -151
  43. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/internal/__init__.py +5 -5
  44. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/internal/builtin_impls.py +144 -144
  45. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/internal/context.py +12 -4
  46. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/internal/descriptor.py +17 -17
  47. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/internal/introspection.py +14 -14
  48. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/internal/native.py +40 -38
  49. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/internal/value.py +3 -3
  50. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/interval.py +120 -112
  51. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/iterator.py +214 -214
  52. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/math.py +30 -1
  53. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/num.py +1 -1
  54. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/options.py +191 -191
  55. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/particle.py +157 -157
  56. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/pointer.py +30 -30
  57. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/print.py +81 -81
  58. sonolus_py-0.1.4/sonolus/script/random.py +14 -0
  59. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/range.py +58 -58
  60. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/record.py +3 -3
  61. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/runtime.py +2 -0
  62. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/sprite.py +333 -333
  63. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/text.py +407 -407
  64. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/timing.py +42 -42
  65. sonolus_py-0.1.4/sonolus/script/transform.py +168 -0
  66. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/ui.py +160 -160
  67. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/vec.py +81 -78
  68. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/tests/script/conftest.py +21 -1
  69. sonolus_py-0.1.4/tests/script/test_array_map.py +253 -0
  70. sonolus_py-0.1.4/tests/script/test_flow.py +243 -0
  71. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/tests/script/test_record.py +30 -0
  72. sonolus_py-0.1.4/tests/script/test_transform.py +255 -0
  73. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/tests/script/test_var_array.py +10 -10
  74. sonolus_py-0.1.4/tests/script/test_vec.py +131 -0
  75. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/uv.lock +1 -1
  76. sonolus_py-0.1.3/sonolus/backend/allocate.py +0 -51
  77. sonolus_py-0.1.3/sonolus/backend/optimize.py +0 -9
  78. sonolus_py-0.1.3/sonolus/backend/passes.py +0 -6
  79. sonolus_py-0.1.3/sonolus/script/transform.py +0 -114
  80. sonolus_py-0.1.3/tests/script/test_array_map.py +0 -59
  81. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/.gitignore +0 -0
  82. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/.python-version +0 -0
  83. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/README.md +0 -0
  84. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/scripts/runtimes/Engine/Tutorial/Blocks.json +0 -0
  85. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/scripts/runtimes/Functions.json +0 -0
  86. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/scripts/runtimes/Level/Play/Blocks.json +0 -0
  87. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/scripts/runtimes/Level/Preview/Blocks.json +0 -0
  88. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/scripts/runtimes/Level/Watch/Blocks.json +0 -0
  89. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/__init__.py +0 -0
  90. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/__init__.py +0 -0
  91. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/interpret.py +0 -0
  92. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/backend/place.py +0 -0
  93. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/build/__init__.py +0 -0
  94. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/build/collection.py +0 -0
  95. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/build/engine.py +0 -0
  96. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/build/project.py +0 -0
  97. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/py.typed +0 -0
  98. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/__init__.py +0 -0
  99. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/engine.py +0 -0
  100. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/internal/error.py +0 -0
  101. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/internal/generic.py +0 -0
  102. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/internal/impl.py +0 -0
  103. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/level.py +0 -0
  104. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/project.py +0 -0
  105. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/sonolus/script/values.py +0 -0
  106. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/tests/__init__.py +0 -0
  107. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/tests/script/__init__.py +0 -0
  108. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/tests/script/test_array.py +0 -0
  109. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/tests/script/test_assert.py +0 -0
  110. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/tests/script/test_helpers.py +0 -0
  111. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/tests/script/test_interval.py +0 -0
  112. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/tests/script/test_num.py +0 -0
  113. {sonolus_py-0.1.3 → sonolus_py-0.1.4}/tests/script/test_range.py +0 -0
@@ -0,0 +1,52 @@
1
+ name: Publish to PyPI
2
+
3
+ on: push
4
+
5
+ jobs:
6
+ build:
7
+ name: Build
8
+ runs-on: ubuntu-latest
9
+
10
+ steps:
11
+ - name: Checkout
12
+ uses: actions/checkout@v4
13
+ - name: Install uv
14
+ uses: astral-sh/setup-uv@v3
15
+ with:
16
+ version: 0.5.2
17
+ - name: Install Python
18
+ run: |
19
+ uv python install
20
+ - name: Install project
21
+ run: |
22
+ uv sync --all-extras --dev
23
+ - name: Run tests
24
+ run: |
25
+ uv run pytest tests -n auto
26
+ - name: Build
27
+ run: |
28
+ uv build
29
+ - name: Store distribution packages
30
+ uses: actions/upload-artifact@v4
31
+ with:
32
+ name: python-package-distributions
33
+ path: dist/
34
+ publish:
35
+ name: Publish
36
+ if: startsWith(github.ref, 'refs/tags/')
37
+ needs:
38
+ - build
39
+ runs-on: ubuntu-latest
40
+ environment:
41
+ name: pypi
42
+ url: https://pypi.org/project/sonolus.py
43
+ permissions:
44
+ id-token: write
45
+ steps:
46
+ - name: Download distribution packages
47
+ uses: actions/download-artifact@v4
48
+ with:
49
+ name: python-package-distributions
50
+ path: dist/
51
+ - name: Publish to PyPI
52
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Kyle Chang
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Kyle Chang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sonolus.py
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Sonolus engine development in Python
5
5
  Requires-Python: >=3.13
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sonolus.py"
3
- version = "0.1.3"
3
+ version = "0.1.4"
4
4
  description = "Sonolus engine development in Python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -1,91 +1,91 @@
1
- import json
2
- from pathlib import Path
3
- from typing import TextIO
4
-
5
- functions_file = "Functions.json"
6
- block_files = {
7
- "Tutorial": "Engine/Tutorial/Blocks.json",
8
- "Play": "Level/Play/Blocks.json",
9
- "Preview": "Level/Preview/Blocks.json",
10
- "Watch": "Level/Watch/Blocks.json",
11
- }
12
- base_dir = Path(__file__).parent
13
- out_dir = base_dir / "out"
14
- runtimes_dir = base_dir / "runtimes"
15
-
16
-
17
- def functions():
18
- with (
19
- (runtimes_dir / functions_file).open("r", encoding="utf-8") as f,
20
- (out_dir / "ops.py").open("w", encoding="utf-8") as out,
21
- ):
22
- data = json.load(f)
23
- out.write(
24
- "from enum import Enum\n"
25
- "\n"
26
- "\n"
27
- "class Op(str, Enum):\n"
28
- " def __new__(cls, name: str, side_effects: bool, pure: bool, control_flow: bool):\n"
29
- " obj = str.__new__(cls, name)\n"
30
- " obj._value_ = name\n"
31
- " obj.side_effects = side_effects\n"
32
- " obj.pure = pure\n"
33
- " obj.control_flow = control_flow\n"
34
- " return obj\n"
35
- "\n"
36
- )
37
- for function in data:
38
- name = function["name"]
39
- side_effects = function["sideEffects"]
40
- pure = function["pure"]
41
- control_flow = function["controlFlow"]
42
- out.write(f' {name} = ("{name}", {side_effects}, {pure}, {control_flow})\n')
43
-
44
-
45
- def block(name: str, f: TextIO, out: TextIO):
46
- data = json.load(f)
47
- out.write(f"class {name}Block(_Block, Enum):\n")
48
- for block in data:
49
- name = block["name"]
50
- id_ = block["id"]
51
- readable = block["readable"]
52
- writable = block["writable"]
53
- out.write(
54
- f' {name} = ({id_}, {{{
55
- ", ".join(f'"{e}"' for e in readable)
56
- }}}, {{{
57
- ", ".join(f'"{e}"' for e in writable)
58
- }}})\n'
59
- )
60
-
61
-
62
- def blocks():
63
- with (out_dir / "blocks.py").open("w", encoding="utf-8") as out:
64
- out.write(
65
- "from enum import Enum\n"
66
- "\n"
67
- "\n"
68
- "class _Block(int):\n"
69
- " def __new__(cls, id_: int, readable: set[str], writable: set[str]):\n"
70
- " obj = int.__new__(cls, id_)\n"
71
- " obj.readable = readable\n"
72
- " obj.writable = writable\n"
73
- " return obj\n"
74
- )
75
- for name, file in block_files.items():
76
- out.write("\n\n")
77
- with (
78
- (runtimes_dir / file).open("r", encoding="utf-8") as f,
79
- ):
80
- block(name, f, out)
81
- out.write(f"\n" f"\n" f"type Block = {" | ".join(f'{name}Block' for name in block_files)}\n")
82
-
83
-
84
- def main():
85
- out_dir.mkdir(exist_ok=True)
86
- functions()
87
- blocks()
88
-
89
-
90
- if __name__ == "__main__":
91
- main()
1
+ import json
2
+ from pathlib import Path
3
+ from typing import TextIO
4
+
5
+ functions_file = "Functions.json"
6
+ block_files = {
7
+ "Tutorial": "Engine/Tutorial/Blocks.json",
8
+ "Play": "Level/Play/Blocks.json",
9
+ "Preview": "Level/Preview/Blocks.json",
10
+ "Watch": "Level/Watch/Blocks.json",
11
+ }
12
+ base_dir = Path(__file__).parent
13
+ out_dir = base_dir / "out"
14
+ runtimes_dir = base_dir / "runtimes"
15
+
16
+
17
+ def functions():
18
+ with (
19
+ (runtimes_dir / functions_file).open("r", encoding="utf-8") as f,
20
+ (out_dir / "ops.py").open("w", encoding="utf-8") as out,
21
+ ):
22
+ data = json.load(f)
23
+ out.write(
24
+ "from enum import Enum\n"
25
+ "\n"
26
+ "\n"
27
+ "class Op(str, Enum):\n"
28
+ " def __new__(cls, name: str, side_effects: bool, pure: bool, control_flow: bool):\n"
29
+ " obj = str.__new__(cls, name)\n"
30
+ " obj._value_ = name\n"
31
+ " obj.side_effects = side_effects\n"
32
+ " obj.pure = pure\n"
33
+ " obj.control_flow = control_flow\n"
34
+ " return obj\n"
35
+ "\n"
36
+ )
37
+ for function in data:
38
+ name = function["name"]
39
+ side_effects = function["sideEffects"]
40
+ pure = function["pure"]
41
+ control_flow = function["controlFlow"]
42
+ out.write(f' {name} = ("{name}", {side_effects}, {pure}, {control_flow})\n')
43
+
44
+
45
+ def block(name: str, f: TextIO, out: TextIO):
46
+ data = json.load(f)
47
+ out.write(f"class {name}Block(_Block, Enum):\n")
48
+ for block in data:
49
+ name = block["name"]
50
+ id_ = block["id"]
51
+ readable = block["readable"]
52
+ writable = block["writable"]
53
+ out.write(
54
+ f' {name} = ({id_}, {{{
55
+ ", ".join(f'"{e}"' for e in readable)
56
+ }}}, {{{
57
+ ", ".join(f'"{e}"' for e in writable)
58
+ }}})\n'
59
+ )
60
+
61
+
62
+ def blocks():
63
+ with (out_dir / "blocks.py").open("w", encoding="utf-8") as out:
64
+ out.write(
65
+ "from enum import Enum\n"
66
+ "\n"
67
+ "\n"
68
+ "class _Block(int):\n"
69
+ " def __new__(cls, id_: int, readable: set[str], writable: set[str]):\n"
70
+ " obj = int.__new__(cls, id_)\n"
71
+ " obj.readable = readable\n"
72
+ " obj.writable = writable\n"
73
+ " return obj\n"
74
+ )
75
+ for name, file in block_files.items():
76
+ out.write("\n\n")
77
+ with (
78
+ (runtimes_dir / file).open("r", encoding="utf-8") as f,
79
+ ):
80
+ block(name, f, out)
81
+ out.write(f"\n" f"\n" f"type Block = {" | ".join(f'{name}Block' for name in block_files)}\n")
82
+
83
+
84
+ def main():
85
+ out_dir.mkdir(exist_ok=True)
86
+ functions()
87
+ blocks()
88
+
89
+
90
+ if __name__ == "__main__":
91
+ main()
@@ -0,0 +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.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