redscript-mc 1.2.30 → 2.1.0

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.
Files changed (593) hide show
  1. package/.claudeignore +21 -0
  2. package/.github/workflows/ci.yml +1 -0
  3. package/README.md +12 -16
  4. package/README.zh.md +2 -2
  5. package/demo.gif +0 -0
  6. package/dist/cli.js +2 -554
  7. package/dist/compile.js +2 -266
  8. package/dist/index.js +2 -159
  9. package/dist/src/__tests__/budget.test.js +261 -0
  10. package/dist/src/__tests__/cli.test.js +104 -0
  11. package/dist/{__tests__ → src/__tests__}/dce.test.js +11 -47
  12. package/dist/{__tests__ → src/__tests__}/diagnostics.test.js +67 -40
  13. package/dist/src/__tests__/e2e/basic.test.d.ts +8 -0
  14. package/dist/src/__tests__/e2e/basic.test.js +140 -0
  15. package/dist/src/__tests__/e2e/coroutine.test.d.ts +7 -0
  16. package/dist/src/__tests__/e2e/coroutine.test.js +132 -0
  17. package/dist/src/__tests__/e2e/macros.test.d.ts +9 -0
  18. package/dist/src/__tests__/e2e/macros.test.js +182 -0
  19. package/dist/src/__tests__/e2e/migrate.test.d.ts +13 -0
  20. package/dist/src/__tests__/e2e/migrate.test.js +2739 -0
  21. package/dist/src/__tests__/e2e/stdlib-e2e.test.d.ts +10 -0
  22. package/dist/src/__tests__/e2e/stdlib-e2e.test.js +324 -0
  23. package/dist/src/__tests__/enum.test.d.ts +10 -0
  24. package/dist/src/__tests__/enum.test.js +389 -0
  25. package/dist/src/__tests__/generics.test.d.ts +14 -0
  26. package/dist/src/__tests__/generics.test.js +367 -0
  27. package/dist/src/__tests__/hir/desugar.test.js +234 -0
  28. package/dist/src/__tests__/incremental.test.d.ts +5 -0
  29. package/dist/src/__tests__/incremental.test.js +308 -0
  30. package/dist/src/__tests__/lir/lower.test.js +559 -0
  31. package/dist/src/__tests__/lir/types.test.js +185 -0
  32. package/dist/src/__tests__/lir/verify.test.js +221 -0
  33. package/dist/src/__tests__/lsp.test.d.ts +7 -0
  34. package/dist/src/__tests__/lsp.test.js +245 -0
  35. package/dist/{__tests__ → src/__tests__}/mc-integration.test.js +1 -3
  36. package/dist/src/__tests__/mc-version.test.d.ts +10 -0
  37. package/dist/src/__tests__/mc-version.test.js +154 -0
  38. package/dist/src/__tests__/mir/arithmetic.test.js +130 -0
  39. package/dist/src/__tests__/mir/control-flow.test.js +205 -0
  40. package/dist/src/__tests__/mir/verify.test.js +223 -0
  41. package/dist/src/__tests__/modules.test.d.ts +7 -0
  42. package/dist/src/__tests__/modules.test.js +333 -0
  43. package/dist/src/__tests__/optimizer/block_merge.test.js +78 -0
  44. package/dist/src/__tests__/optimizer/branch_simplify.test.js +58 -0
  45. package/dist/src/__tests__/optimizer/constant_fold.test.js +131 -0
  46. package/dist/src/__tests__/optimizer/copy_prop.test.js +91 -0
  47. package/dist/src/__tests__/optimizer/coroutine.test.d.ts +12 -0
  48. package/dist/src/__tests__/optimizer/coroutine.test.js +251 -0
  49. package/dist/src/__tests__/optimizer/dce.test.d.ts +1 -0
  50. package/dist/src/__tests__/optimizer/dce.test.js +76 -0
  51. package/dist/src/__tests__/optimizer/interprocedural.test.d.ts +1 -0
  52. package/dist/src/__tests__/optimizer/interprocedural.test.js +145 -0
  53. package/dist/src/__tests__/optimizer/lir/const_imm.test.d.ts +1 -0
  54. package/dist/src/__tests__/optimizer/lir/const_imm.test.js +138 -0
  55. package/dist/src/__tests__/optimizer/lir/dead_slot.test.d.ts +1 -0
  56. package/dist/src/__tests__/optimizer/lir/dead_slot.test.js +141 -0
  57. package/dist/src/__tests__/optimizer/lir/peephole.test.d.ts +1 -0
  58. package/dist/src/__tests__/optimizer/lir/peephole.test.js +126 -0
  59. package/dist/src/__tests__/optimizer/lir/pipeline.test.d.ts +1 -0
  60. package/dist/src/__tests__/optimizer/lir/pipeline.test.js +84 -0
  61. package/dist/src/__tests__/optimizer/nbt-batch.test.d.ts +1 -0
  62. package/dist/src/__tests__/optimizer/nbt-batch.test.js +110 -0
  63. package/dist/src/__tests__/optimizer/pipeline.test.d.ts +1 -0
  64. package/dist/src/__tests__/optimizer/pipeline.test.js +102 -0
  65. package/dist/src/__tests__/optimizer/selector-cache.test.d.ts +1 -0
  66. package/dist/src/__tests__/optimizer/selector-cache.test.js +103 -0
  67. package/dist/src/__tests__/optimizer/unroll.test.d.ts +1 -0
  68. package/dist/src/__tests__/optimizer/unroll.test.js +206 -0
  69. package/dist/src/__tests__/option.test.d.ts +14 -0
  70. package/dist/src/__tests__/option.test.js +275 -0
  71. package/dist/src/__tests__/parser.test.d.ts +1 -0
  72. package/dist/src/__tests__/repl.test.d.ts +1 -0
  73. package/dist/src/__tests__/schedule.test.d.ts +7 -0
  74. package/dist/src/__tests__/schedule.test.js +98 -0
  75. package/dist/src/__tests__/sourcemap.test.d.ts +7 -0
  76. package/dist/src/__tests__/sourcemap.test.js +227 -0
  77. package/dist/src/__tests__/tuple.test.d.ts +11 -0
  78. package/dist/src/__tests__/tuple.test.js +202 -0
  79. package/dist/src/__tests__/typechecker-strict.test.d.ts +10 -0
  80. package/dist/src/__tests__/typechecker-strict.test.js +197 -0
  81. package/dist/src/__tests__/typechecker.test.d.ts +1 -0
  82. package/dist/{ast → src/ast}/types.d.ts +58 -3
  83. package/dist/src/cache/deps.d.ts +41 -0
  84. package/dist/src/cache/deps.js +158 -0
  85. package/dist/src/cache/incremental.d.ts +35 -0
  86. package/dist/src/cache/incremental.js +165 -0
  87. package/dist/src/cache/index.d.ts +37 -0
  88. package/dist/src/cache/index.js +152 -0
  89. package/dist/{cli.d.ts → src/cli.d.ts} +1 -1
  90. package/dist/src/cli.js +474 -0
  91. package/dist/src/compile.d.ts +37 -0
  92. package/dist/src/compile.js +165 -0
  93. package/dist/{diagnostics → src/diagnostics}/index.d.ts +1 -1
  94. package/dist/{diagnostics → src/diagnostics}/index.js +8 -11
  95. package/dist/src/emit/compile.d.ts +29 -0
  96. package/dist/src/emit/compile.js +143 -0
  97. package/dist/src/emit/index.d.ts +26 -0
  98. package/dist/src/emit/index.js +223 -0
  99. package/dist/src/emit/modules.d.ts +29 -0
  100. package/dist/src/emit/modules.js +492 -0
  101. package/dist/src/emit/sourcemap.d.ts +53 -0
  102. package/dist/src/emit/sourcemap.js +73 -0
  103. package/dist/src/hir/lower.d.ts +15 -0
  104. package/dist/src/hir/lower.js +399 -0
  105. package/dist/src/hir/monomorphize.d.ts +22 -0
  106. package/dist/src/hir/monomorphize.js +379 -0
  107. package/dist/src/hir/types.d.ts +406 -0
  108. package/dist/src/hir/types.js +16 -0
  109. package/dist/src/index.d.ts +39 -0
  110. package/dist/src/index.js +67 -0
  111. package/dist/{lexer → src/lexer}/index.d.ts +1 -1
  112. package/dist/{lexer → src/lexer}/index.js +1 -0
  113. package/dist/src/lir/budget.d.ts +37 -0
  114. package/dist/src/lir/budget.js +280 -0
  115. package/dist/src/lir/lower.d.ts +15 -0
  116. package/dist/src/lir/lower.js +472 -0
  117. package/dist/src/lir/types.d.ts +139 -0
  118. package/dist/src/lir/types.js +11 -0
  119. package/dist/src/lir/verify.d.ts +14 -0
  120. package/dist/src/lir/verify.js +113 -0
  121. package/dist/src/lsp/main.d.ts +8 -0
  122. package/dist/src/lsp/main.js +11 -0
  123. package/dist/src/lsp/server.d.ts +11 -0
  124. package/dist/src/lsp/server.js +352 -0
  125. package/dist/{mc-test → src/mc-test}/runner.js +4 -3
  126. package/dist/src/mir/lower.d.ts +9 -0
  127. package/dist/src/mir/lower.js +1264 -0
  128. package/dist/src/mir/macro.d.ts +22 -0
  129. package/dist/src/mir/macro.js +168 -0
  130. package/dist/src/mir/types.d.ts +191 -0
  131. package/dist/src/mir/types.js +11 -0
  132. package/dist/src/mir/verify.d.ts +16 -0
  133. package/dist/src/mir/verify.js +216 -0
  134. package/dist/src/optimizer/block_merge.d.ts +12 -0
  135. package/dist/src/optimizer/block_merge.js +84 -0
  136. package/dist/src/optimizer/branch_simplify.d.ts +9 -0
  137. package/dist/src/optimizer/branch_simplify.js +28 -0
  138. package/dist/src/optimizer/constant_fold.d.ts +10 -0
  139. package/dist/src/optimizer/constant_fold.js +85 -0
  140. package/dist/src/optimizer/copy_prop.d.ts +9 -0
  141. package/dist/src/optimizer/copy_prop.js +113 -0
  142. package/dist/src/optimizer/coroutine.d.ts +34 -0
  143. package/dist/src/optimizer/coroutine.js +789 -0
  144. package/dist/src/optimizer/dce.d.ts +8 -0
  145. package/dist/src/optimizer/dce.js +156 -0
  146. package/dist/src/optimizer/interprocedural.d.ts +14 -0
  147. package/dist/src/optimizer/interprocedural.js +186 -0
  148. package/dist/src/optimizer/lir/const_imm.d.ts +12 -0
  149. package/dist/src/optimizer/lir/const_imm.js +139 -0
  150. package/dist/src/optimizer/lir/dead_slot.d.ts +14 -0
  151. package/dist/src/optimizer/lir/dead_slot.js +130 -0
  152. package/dist/src/optimizer/lir/peephole.d.ts +21 -0
  153. package/dist/src/optimizer/lir/peephole.js +52 -0
  154. package/dist/src/optimizer/lir/pipeline.d.ts +10 -0
  155. package/dist/src/optimizer/lir/pipeline.js +34 -0
  156. package/dist/src/optimizer/nbt-batch.d.ts +11 -0
  157. package/dist/src/optimizer/nbt-batch.js +51 -0
  158. package/dist/src/optimizer/pipeline.d.ts +14 -0
  159. package/dist/src/optimizer/pipeline.js +58 -0
  160. package/dist/src/optimizer/selector-cache.d.ts +22 -0
  161. package/dist/src/optimizer/selector-cache.js +100 -0
  162. package/dist/src/optimizer/unroll.d.ts +32 -0
  163. package/dist/src/optimizer/unroll.js +348 -0
  164. package/dist/{parser → src/parser}/index.d.ts +8 -0
  165. package/dist/{parser → src/parser}/index.js +204 -14
  166. package/dist/{repl.d.ts → src/repl.d.ts} +1 -1
  167. package/dist/{runtime → src/runtime}/index.js +1 -1
  168. package/dist/{typechecker → src/typechecker}/index.d.ts +4 -0
  169. package/dist/{typechecker → src/typechecker}/index.js +198 -13
  170. package/dist/src/types/mc-version.d.ts +24 -0
  171. package/dist/src/types/mc-version.js +49 -0
  172. package/docs/ROADMAP.md +395 -0
  173. package/docs/compiler-pipeline-redesign.md +2260 -0
  174. package/docs/optimization-ideas.md +1076 -0
  175. package/editors/vscode/out/extension.js +25176 -8000
  176. package/editors/vscode/package-lock.json +90 -6
  177. package/editors/vscode/package.json +3 -2
  178. package/editors/vscode/src/extension.ts +97 -67
  179. package/examples/showcase.mcrs +3 -3
  180. package/package.json +13 -6
  181. package/scripts/postbuild.js +15 -0
  182. package/src/__tests__/budget.test.ts +297 -0
  183. package/src/__tests__/cli.test.ts +8 -220
  184. package/src/__tests__/dce.test.ts +11 -56
  185. package/src/__tests__/diagnostics.test.ts +61 -41
  186. package/src/__tests__/e2e/basic.test.ts +154 -0
  187. package/src/__tests__/e2e/coroutine.test.ts +142 -0
  188. package/src/__tests__/e2e/macros.test.ts +199 -0
  189. package/src/__tests__/e2e/migrate.test.ts +3008 -0
  190. package/src/__tests__/e2e/stdlib-e2e.test.ts +348 -0
  191. package/src/__tests__/enum.test.ts +425 -0
  192. package/src/__tests__/generics.test.ts +390 -0
  193. package/src/__tests__/hir/desugar.test.ts +263 -0
  194. package/src/__tests__/incremental.test.ts +337 -0
  195. package/src/__tests__/lir/lower.test.ts +619 -0
  196. package/src/__tests__/lir/types.test.ts +207 -0
  197. package/src/__tests__/lir/verify.test.ts +249 -0
  198. package/src/__tests__/lsp.test.ts +270 -0
  199. package/src/__tests__/mc-integration.test.ts +1 -2
  200. package/src/__tests__/mc-version.test.ts +178 -0
  201. package/src/__tests__/mir/arithmetic.test.ts +156 -0
  202. package/src/__tests__/mir/control-flow.test.ts +242 -0
  203. package/src/__tests__/mir/verify.test.ts +254 -0
  204. package/src/__tests__/modules.test.ts +365 -0
  205. package/src/__tests__/optimizer/block_merge.test.ts +84 -0
  206. package/src/__tests__/optimizer/branch_simplify.test.ts +64 -0
  207. package/src/__tests__/optimizer/constant_fold.test.ts +145 -0
  208. package/src/__tests__/optimizer/copy_prop.test.ts +99 -0
  209. package/src/__tests__/optimizer/coroutine.test.ts +312 -0
  210. package/src/__tests__/optimizer/dce.test.ts +83 -0
  211. package/src/__tests__/optimizer/interprocedural.test.ts +174 -0
  212. package/src/__tests__/optimizer/lir/const_imm.test.ts +151 -0
  213. package/src/__tests__/optimizer/lir/dead_slot.test.ts +156 -0
  214. package/src/__tests__/optimizer/lir/peephole.test.ts +136 -0
  215. package/src/__tests__/optimizer/lir/pipeline.test.ts +113 -0
  216. package/src/__tests__/optimizer/nbt-batch.test.ts +119 -0
  217. package/src/__tests__/optimizer/pipeline.test.ts +116 -0
  218. package/src/__tests__/optimizer/selector-cache.test.ts +112 -0
  219. package/src/__tests__/optimizer/unroll.test.ts +231 -0
  220. package/src/__tests__/option.test.ts +299 -0
  221. package/src/__tests__/schedule.test.ts +105 -0
  222. package/src/__tests__/sourcemap.test.ts +254 -0
  223. package/src/__tests__/tuple.test.ts +220 -0
  224. package/src/__tests__/typechecker-strict.test.ts +216 -0
  225. package/src/ast/types.ts +39 -3
  226. package/src/cache/deps.ts +132 -0
  227. package/src/cache/incremental.ts +173 -0
  228. package/src/cache/index.ts +135 -0
  229. package/src/cli.ts +111 -195
  230. package/src/compile.ts +6 -162
  231. package/src/diagnostics/index.ts +8 -11
  232. package/src/emit/compile.ts +177 -0
  233. package/src/emit/index.ts +286 -0
  234. package/src/emit/modules.ts +581 -0
  235. package/src/emit/sourcemap.ts +101 -0
  236. package/src/hir/lower.ts +455 -0
  237. package/src/hir/monomorphize.ts +416 -0
  238. package/src/hir/types.ts +228 -0
  239. package/src/index.ts +37 -182
  240. package/src/lexer/index.ts +2 -1
  241. package/src/lir/budget.ts +321 -0
  242. package/src/lir/lower.ts +587 -0
  243. package/src/lir/types.ts +113 -0
  244. package/src/lir/verify.ts +129 -0
  245. package/src/lsp/main.ts +9 -0
  246. package/src/lsp/server.ts +414 -0
  247. package/src/mc-test/runner.ts +4 -3
  248. package/src/mir/lower.ts +1403 -0
  249. package/src/mir/macro.ts +167 -0
  250. package/src/mir/types.ts +117 -0
  251. package/src/mir/verify.ts +218 -0
  252. package/src/optimizer/block_merge.ts +93 -0
  253. package/src/optimizer/branch_simplify.ts +27 -0
  254. package/src/optimizer/constant_fold.ts +88 -0
  255. package/src/optimizer/copy_prop.ts +106 -0
  256. package/src/optimizer/coroutine.ts +996 -0
  257. package/src/optimizer/dce.ts +108 -653
  258. package/src/optimizer/interprocedural.ts +177 -0
  259. package/src/optimizer/lir/const_imm.ts +143 -0
  260. package/src/optimizer/lir/dead_slot.ts +123 -0
  261. package/src/optimizer/lir/peephole.ts +57 -0
  262. package/src/optimizer/lir/pipeline.ts +37 -0
  263. package/src/optimizer/nbt-batch.ts +50 -0
  264. package/src/optimizer/pipeline.ts +59 -0
  265. package/src/optimizer/selector-cache.ts +103 -0
  266. package/src/optimizer/unroll.ts +386 -0
  267. package/src/parser/index.ts +213 -16
  268. package/src/repl.ts +1 -1
  269. package/src/runtime/index.ts +1 -1
  270. package/src/stdlib/math.mcrs +4 -4
  271. package/src/templates/quest.mcrs +4 -4
  272. package/src/typechecker/index.ts +215 -15
  273. package/src/types/mc-version.ts +46 -0
  274. package/tsconfig.json +1 -1
  275. package/dist/__tests__/cli.test.js +0 -278
  276. package/dist/__tests__/codegen.test.js +0 -152
  277. package/dist/__tests__/e2e.test.d.ts +0 -6
  278. package/dist/__tests__/e2e.test.js +0 -1847
  279. package/dist/__tests__/entity-types.test.js +0 -203
  280. package/dist/__tests__/lowering.test.js +0 -1015
  281. package/dist/__tests__/macro.test.d.ts +0 -8
  282. package/dist/__tests__/macro.test.js +0 -305
  283. package/dist/__tests__/nbt.test.js +0 -82
  284. package/dist/__tests__/optimizer-advanced.test.js +0 -124
  285. package/dist/__tests__/optimizer.test.js +0 -149
  286. package/dist/__tests__/runtime.test.js +0 -289
  287. package/dist/__tests__/stdlib-advanced.test.d.ts +0 -4
  288. package/dist/__tests__/stdlib-advanced.test.js +0 -378
  289. package/dist/__tests__/stdlib-bigint.test.d.ts +0 -7
  290. package/dist/__tests__/stdlib-bigint.test.js +0 -428
  291. package/dist/__tests__/stdlib-math.test.d.ts +0 -7
  292. package/dist/__tests__/stdlib-math.test.js +0 -352
  293. package/dist/__tests__/stdlib-vec.test.d.ts +0 -4
  294. package/dist/__tests__/stdlib-vec.test.js +0 -264
  295. package/dist/__tests__/structure-optimizer.test.js +0 -33
  296. package/dist/__tests__/var-allocator.test.js +0 -69
  297. package/dist/codegen/cmdblock/index.d.ts +0 -26
  298. package/dist/codegen/cmdblock/index.js +0 -45
  299. package/dist/codegen/mcfunction/index.d.ts +0 -40
  300. package/dist/codegen/mcfunction/index.js +0 -606
  301. package/dist/codegen/structure/index.d.ts +0 -24
  302. package/dist/codegen/structure/index.js +0 -279
  303. package/dist/codegen/var-allocator.d.ts +0 -45
  304. package/dist/codegen/var-allocator.js +0 -104
  305. package/dist/compile.d.ts +0 -68
  306. package/dist/data/arena/function/__load.mcfunction +0 -6
  307. package/dist/data/arena/function/__tick.mcfunction +0 -2
  308. package/dist/data/arena/function/announce_leaders/else_1.mcfunction +0 -3
  309. package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +0 -1
  310. package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +0 -3
  311. package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +0 -7
  312. package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +0 -1
  313. package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +0 -4
  314. package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +0 -6
  315. package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +0 -1
  316. package/dist/data/arena/function/announce_leaders/then_0.mcfunction +0 -4
  317. package/dist/data/arena/function/announce_leaders.mcfunction +0 -6
  318. package/dist/data/arena/function/arena_tick/merge_2.mcfunction +0 -1
  319. package/dist/data/arena/function/arena_tick/then_0.mcfunction +0 -4
  320. package/dist/data/arena/function/arena_tick.mcfunction +0 -11
  321. package/dist/data/counter/function/__load.mcfunction +0 -5
  322. package/dist/data/counter/function/__tick.mcfunction +0 -2
  323. package/dist/data/counter/function/counter_tick/merge_2.mcfunction +0 -1
  324. package/dist/data/counter/function/counter_tick/then_0.mcfunction +0 -3
  325. package/dist/data/counter/function/counter_tick.mcfunction +0 -11
  326. package/dist/data/gcd2/function/__load.mcfunction +0 -3
  327. package/dist/data/gcd2/function/abs/merge_2.mcfunction +0 -3
  328. package/dist/data/gcd2/function/abs/then_0.mcfunction +0 -5
  329. package/dist/data/gcd2/function/abs.mcfunction +0 -7
  330. package/dist/data/gcd2/function/gcd/loop_body_1.mcfunction +0 -7
  331. package/dist/data/gcd2/function/gcd/loop_check_0.mcfunction +0 -5
  332. package/dist/data/gcd2/function/gcd/loop_exit_2.mcfunction +0 -3
  333. package/dist/data/gcd2/function/gcd.mcfunction +0 -14
  334. package/dist/data/gcd3/function/__load.mcfunction +0 -3
  335. package/dist/data/gcd3/function/abs/merge_2.mcfunction +0 -3
  336. package/dist/data/gcd3/function/abs/then_0.mcfunction +0 -5
  337. package/dist/data/gcd3/function/abs.mcfunction +0 -7
  338. package/dist/data/gcd3/function/gcd/loop_body_1.mcfunction +0 -7
  339. package/dist/data/gcd3/function/gcd/loop_check_0.mcfunction +0 -5
  340. package/dist/data/gcd3/function/gcd/loop_exit_2.mcfunction +0 -3
  341. package/dist/data/gcd3/function/gcd.mcfunction +0 -14
  342. package/dist/data/gcd3/function/test.mcfunction +0 -7
  343. package/dist/data/gcd3nm/function/__load.mcfunction +0 -3
  344. package/dist/data/gcd3nm/function/abs/merge_2.mcfunction +0 -3
  345. package/dist/data/gcd3nm/function/abs/then_0.mcfunction +0 -5
  346. package/dist/data/gcd3nm/function/abs.mcfunction +0 -7
  347. package/dist/data/gcd3nm/function/gcd/loop_body_1.mcfunction +0 -7
  348. package/dist/data/gcd3nm/function/gcd/loop_check_0.mcfunction +0 -5
  349. package/dist/data/gcd3nm/function/gcd/loop_exit_2.mcfunction +0 -3
  350. package/dist/data/gcd3nm/function/gcd.mcfunction +0 -14
  351. package/dist/data/gcd3nm/function/test.mcfunction +0 -7
  352. package/dist/data/gcd_test/function/__load.mcfunction +0 -3
  353. package/dist/data/gcd_test/function/abs/merge_2.mcfunction +0 -3
  354. package/dist/data/gcd_test/function/abs/then_0.mcfunction +0 -5
  355. package/dist/data/gcd_test/function/abs.mcfunction +0 -7
  356. package/dist/data/gcd_test/function/gcd/loop_body_1.mcfunction +0 -7
  357. package/dist/data/gcd_test/function/gcd/loop_check_0.mcfunction +0 -5
  358. package/dist/data/gcd_test/function/gcd/loop_exit_2.mcfunction +0 -3
  359. package/dist/data/gcd_test/function/gcd.mcfunction +0 -14
  360. package/dist/data/isqrttest/function/__load.mcfunction +0 -6
  361. package/dist/data/isqrttest/function/isqrt/loop_body_4.mcfunction +0 -12
  362. package/dist/data/isqrttest/function/isqrt/loop_check_3.mcfunction +0 -5
  363. package/dist/data/isqrttest/function/isqrt/loop_exit_5.mcfunction +0 -3
  364. package/dist/data/isqrttest/function/isqrt/merge_2.mcfunction +0 -4
  365. package/dist/data/isqrttest/function/isqrt/merge_8.mcfunction +0 -6
  366. package/dist/data/isqrttest/function/isqrt/then_0.mcfunction +0 -3
  367. package/dist/data/isqrttest/function/isqrt/then_6.mcfunction +0 -3
  368. package/dist/data/isqrttest/function/isqrt.mcfunction +0 -7
  369. package/dist/data/isqrttest/function/test.mcfunction +0 -6
  370. package/dist/data/mathtest/function/__load.mcfunction +0 -3
  371. package/dist/data/mathtest/function/abs/merge_2.mcfunction +0 -3
  372. package/dist/data/mathtest/function/abs/then_0.mcfunction +0 -5
  373. package/dist/data/mathtest/function/abs.mcfunction +0 -6
  374. package/dist/data/mathtest/function/test.mcfunction +0 -5
  375. package/dist/data/minecraft/tags/function/load.json +0 -5
  376. package/dist/data/minecraft/tags/function/tick.json +0 -5
  377. package/dist/data/mypack/function/__load.mcfunction +0 -13
  378. package/dist/data/mypack/function/_atan_init.mcfunction +0 -2
  379. package/dist/data/mypack/function/abs/merge_2.mcfunction +0 -3
  380. package/dist/data/mypack/function/abs/then_0.mcfunction +0 -5
  381. package/dist/data/mypack/function/abs.mcfunction +0 -6
  382. package/dist/data/mypack/function/atan2_fixed/__sgi_1.mcfunction +0 -2
  383. package/dist/data/mypack/function/atan2_fixed/else_34.mcfunction +0 -3
  384. package/dist/data/mypack/function/atan2_fixed/loop_body_31.mcfunction +0 -19
  385. package/dist/data/mypack/function/atan2_fixed/loop_check_30.mcfunction +0 -5
  386. package/dist/data/mypack/function/atan2_fixed/loop_exit_32.mcfunction +0 -6
  387. package/dist/data/mypack/function/atan2_fixed/merge_11.mcfunction +0 -6
  388. package/dist/data/mypack/function/atan2_fixed/merge_14.mcfunction +0 -3
  389. package/dist/data/mypack/function/atan2_fixed/merge_17.mcfunction +0 -6
  390. package/dist/data/mypack/function/atan2_fixed/merge_2.mcfunction +0 -5
  391. package/dist/data/mypack/function/atan2_fixed/merge_20.mcfunction +0 -5
  392. package/dist/data/mypack/function/atan2_fixed/merge_23.mcfunction +0 -5
  393. package/dist/data/mypack/function/atan2_fixed/merge_26.mcfunction +0 -6
  394. package/dist/data/mypack/function/atan2_fixed/merge_29.mcfunction +0 -4
  395. package/dist/data/mypack/function/atan2_fixed/merge_38.mcfunction +0 -5
  396. package/dist/data/mypack/function/atan2_fixed/merge_41.mcfunction +0 -5
  397. package/dist/data/mypack/function/atan2_fixed/merge_44.mcfunction +0 -5
  398. package/dist/data/mypack/function/atan2_fixed/merge_47.mcfunction +0 -5
  399. package/dist/data/mypack/function/atan2_fixed/merge_5.mcfunction +0 -5
  400. package/dist/data/mypack/function/atan2_fixed/merge_8.mcfunction +0 -3
  401. package/dist/data/mypack/function/atan2_fixed/then_0.mcfunction +0 -5
  402. package/dist/data/mypack/function/atan2_fixed/then_12.mcfunction +0 -3
  403. package/dist/data/mypack/function/atan2_fixed/then_15.mcfunction +0 -5
  404. package/dist/data/mypack/function/atan2_fixed/then_18.mcfunction +0 -5
  405. package/dist/data/mypack/function/atan2_fixed/then_21.mcfunction +0 -3
  406. package/dist/data/mypack/function/atan2_fixed/then_24.mcfunction +0 -3
  407. package/dist/data/mypack/function/atan2_fixed/then_27.mcfunction +0 -6
  408. package/dist/data/mypack/function/atan2_fixed/then_3.mcfunction +0 -3
  409. package/dist/data/mypack/function/atan2_fixed/then_33.mcfunction +0 -5
  410. package/dist/data/mypack/function/atan2_fixed/then_36.mcfunction +0 -5
  411. package/dist/data/mypack/function/atan2_fixed/then_39.mcfunction +0 -5
  412. package/dist/data/mypack/function/atan2_fixed/then_42.mcfunction +0 -3
  413. package/dist/data/mypack/function/atan2_fixed/then_45.mcfunction +0 -5
  414. package/dist/data/mypack/function/atan2_fixed/then_6.mcfunction +0 -3
  415. package/dist/data/mypack/function/atan2_fixed/then_9.mcfunction +0 -5
  416. package/dist/data/mypack/function/atan2_fixed.mcfunction +0 -7
  417. package/dist/data/mypack/function/my_game.mcfunction +0 -10
  418. package/dist/data/quiz/function/__load.mcfunction +0 -16
  419. package/dist/data/quiz/function/__tick.mcfunction +0 -6
  420. package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +0 -4
  421. package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +0 -4
  422. package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +0 -4
  423. package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +0 -4
  424. package/dist/data/quiz/function/answer_a.mcfunction +0 -4
  425. package/dist/data/quiz/function/answer_b.mcfunction +0 -4
  426. package/dist/data/quiz/function/answer_c.mcfunction +0 -4
  427. package/dist/data/quiz/function/ask_question/else_1.mcfunction +0 -5
  428. package/dist/data/quiz/function/ask_question/else_4.mcfunction +0 -5
  429. package/dist/data/quiz/function/ask_question/else_7.mcfunction +0 -4
  430. package/dist/data/quiz/function/ask_question/merge_2.mcfunction +0 -1
  431. package/dist/data/quiz/function/ask_question/merge_5.mcfunction +0 -2
  432. package/dist/data/quiz/function/ask_question/merge_8.mcfunction +0 -2
  433. package/dist/data/quiz/function/ask_question/then_0.mcfunction +0 -4
  434. package/dist/data/quiz/function/ask_question/then_3.mcfunction +0 -4
  435. package/dist/data/quiz/function/ask_question/then_6.mcfunction +0 -4
  436. package/dist/data/quiz/function/ask_question.mcfunction +0 -7
  437. package/dist/data/quiz/function/finish_quiz.mcfunction +0 -6
  438. package/dist/data/quiz/function/handle_answer/else_1.mcfunction +0 -5
  439. package/dist/data/quiz/function/handle_answer/else_10.mcfunction +0 -3
  440. package/dist/data/quiz/function/handle_answer/else_16.mcfunction +0 -3
  441. package/dist/data/quiz/function/handle_answer/else_4.mcfunction +0 -3
  442. package/dist/data/quiz/function/handle_answer/else_7.mcfunction +0 -5
  443. package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +0 -2
  444. package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +0 -2
  445. package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +0 -2
  446. package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +0 -8
  447. package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +0 -2
  448. package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +0 -2
  449. package/dist/data/quiz/function/handle_answer/then_0.mcfunction +0 -5
  450. package/dist/data/quiz/function/handle_answer/then_12.mcfunction +0 -5
  451. package/dist/data/quiz/function/handle_answer/then_15.mcfunction +0 -6
  452. package/dist/data/quiz/function/handle_answer/then_3.mcfunction +0 -6
  453. package/dist/data/quiz/function/handle_answer/then_6.mcfunction +0 -5
  454. package/dist/data/quiz/function/handle_answer/then_9.mcfunction +0 -6
  455. package/dist/data/quiz/function/handle_answer.mcfunction +0 -11
  456. package/dist/data/quiz/function/start_quiz.mcfunction +0 -5
  457. package/dist/data/reqtest/function/__load.mcfunction +0 -4
  458. package/dist/data/reqtest/function/_table_init.mcfunction +0 -2
  459. package/dist/data/reqtest/function/no_trig.mcfunction +0 -3
  460. package/dist/data/reqtest/function/use_table.mcfunction +0 -4
  461. package/dist/data/reqtest2/function/__load.mcfunction +0 -3
  462. package/dist/data/reqtest2/function/no_trig.mcfunction +0 -3
  463. package/dist/data/runtime/function/__load.mcfunction +0 -5
  464. package/dist/data/runtime/function/__tick.mcfunction +0 -2
  465. package/dist/data/runtime/function/counter_tick/then_0.mcfunction +0 -3
  466. package/dist/data/runtime/function/counter_tick.mcfunction +0 -13
  467. package/dist/data/shop/function/__load.mcfunction +0 -7
  468. package/dist/data/shop/function/__tick.mcfunction +0 -3
  469. package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +0 -4
  470. package/dist/data/shop/function/complete_purchase/else_1.mcfunction +0 -5
  471. package/dist/data/shop/function/complete_purchase/else_4.mcfunction +0 -5
  472. package/dist/data/shop/function/complete_purchase/else_7.mcfunction +0 -3
  473. package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +0 -2
  474. package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +0 -2
  475. package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +0 -2
  476. package/dist/data/shop/function/complete_purchase/then_0.mcfunction +0 -4
  477. package/dist/data/shop/function/complete_purchase/then_3.mcfunction +0 -4
  478. package/dist/data/shop/function/complete_purchase/then_6.mcfunction +0 -4
  479. package/dist/data/shop/function/complete_purchase.mcfunction +0 -7
  480. package/dist/data/shop/function/handle_shop_trigger.mcfunction +0 -3
  481. package/dist/data/swap_test/function/__load.mcfunction +0 -3
  482. package/dist/data/swap_test/function/gcd_old/loop_body_1.mcfunction +0 -7
  483. package/dist/data/swap_test/function/gcd_old/loop_check_0.mcfunction +0 -5
  484. package/dist/data/swap_test/function/gcd_old/loop_exit_2.mcfunction +0 -3
  485. package/dist/data/swap_test/function/gcd_old.mcfunction +0 -8
  486. package/dist/data/turret/function/__load.mcfunction +0 -5
  487. package/dist/data/turret/function/__tick.mcfunction +0 -4
  488. package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +0 -4
  489. package/dist/data/turret/function/deploy_turret.mcfunction +0 -8
  490. package/dist/data/turret/function/turret_tick/at_1.mcfunction +0 -2
  491. package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +0 -2
  492. package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +0 -2
  493. package/dist/data/turret/function/turret_tick/tick_body.mcfunction +0 -3
  494. package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +0 -1
  495. package/dist/data/turret/function/turret_tick.mcfunction +0 -5
  496. package/dist/gcd2.map.json +0 -15
  497. package/dist/gcd3.map.json +0 -17
  498. package/dist/gcd_test.map.json +0 -15
  499. package/dist/index.d.ts +0 -62
  500. package/dist/ir/builder.d.ts +0 -33
  501. package/dist/ir/builder.js +0 -99
  502. package/dist/ir/types.d.ts +0 -132
  503. package/dist/ir/types.js +0 -15
  504. package/dist/isqrttest.map.json +0 -15
  505. package/dist/lowering/index.d.ts +0 -188
  506. package/dist/lowering/index.js +0 -3403
  507. package/dist/mathtest.map.json +0 -6
  508. package/dist/mypack.map.json +0 -27
  509. package/dist/optimizer/commands.d.ts +0 -38
  510. package/dist/optimizer/commands.js +0 -451
  511. package/dist/optimizer/dce.d.ts +0 -34
  512. package/dist/optimizer/dce.js +0 -639
  513. package/dist/optimizer/passes.d.ts +0 -34
  514. package/dist/optimizer/passes.js +0 -243
  515. package/dist/optimizer/structure.d.ts +0 -9
  516. package/dist/optimizer/structure.js +0 -356
  517. package/dist/pack.mcmeta +0 -6
  518. package/dist/reqtest.map.json +0 -4
  519. package/dist/reqtest2.map.json +0 -4
  520. package/dist/runtime.map.json +0 -7
  521. package/dist/swap_test.map.json +0 -14
  522. package/src/__tests__/codegen.test.ts +0 -161
  523. package/src/__tests__/e2e.test.ts +0 -2039
  524. package/src/__tests__/entity-types.test.ts +0 -236
  525. package/src/__tests__/lowering.test.ts +0 -1185
  526. package/src/__tests__/macro.test.ts +0 -343
  527. package/src/__tests__/nbt.test.ts +0 -58
  528. package/src/__tests__/optimizer-advanced.test.ts +0 -144
  529. package/src/__tests__/optimizer.test.ts +0 -162
  530. package/src/__tests__/runtime.test.ts +0 -305
  531. package/src/__tests__/stdlib-advanced.test.ts +0 -379
  532. package/src/__tests__/stdlib-bigint.test.ts +0 -427
  533. package/src/__tests__/stdlib-math.test.ts +0 -374
  534. package/src/__tests__/stdlib-vec.test.ts +0 -259
  535. package/src/__tests__/structure-optimizer.test.ts +0 -38
  536. package/src/__tests__/var-allocator.test.ts +0 -75
  537. package/src/codegen/cmdblock/index.ts +0 -63
  538. package/src/codegen/mcfunction/index.ts +0 -662
  539. package/src/codegen/structure/index.ts +0 -346
  540. package/src/codegen/var-allocator.ts +0 -104
  541. package/src/ir/builder.ts +0 -116
  542. package/src/ir/types.ts +0 -134
  543. package/src/lowering/index.ts +0 -3876
  544. package/src/optimizer/commands.ts +0 -534
  545. package/src/optimizer/passes.ts +0 -250
  546. package/src/optimizer/structure.ts +0 -450
  547. /package/dist/{__tests__/cli.test.d.ts → src/__tests__/budget.test.d.ts} +0 -0
  548. /package/dist/{__tests__/codegen.test.d.ts → src/__tests__/cli.test.d.ts} +0 -0
  549. /package/dist/{__tests__ → src/__tests__}/compile-all.test.d.ts +0 -0
  550. /package/dist/{__tests__ → src/__tests__}/compile-all.test.js +0 -0
  551. /package/dist/{__tests__ → src/__tests__}/dce.test.d.ts +0 -0
  552. /package/dist/{__tests__ → src/__tests__}/diagnostics.test.d.ts +0 -0
  553. /package/dist/{__tests__ → src/__tests__}/formatter.test.d.ts +0 -0
  554. /package/dist/{__tests__ → src/__tests__}/formatter.test.js +0 -0
  555. /package/dist/{__tests__/entity-types.test.d.ts → src/__tests__/hir/desugar.test.d.ts} +0 -0
  556. /package/dist/{__tests__ → src/__tests__}/lexer.test.d.ts +0 -0
  557. /package/dist/{__tests__ → src/__tests__}/lexer.test.js +0 -0
  558. /package/dist/{__tests__/lowering.test.d.ts → src/__tests__/lir/lower.test.d.ts} +0 -0
  559. /package/dist/{__tests__/mc-syntax.test.d.ts → src/__tests__/lir/types.test.d.ts} +0 -0
  560. /package/dist/{__tests__/nbt.test.d.ts → src/__tests__/lir/verify.test.d.ts} +0 -0
  561. /package/dist/{__tests__ → src/__tests__}/mc-integration.test.d.ts +0 -0
  562. /package/dist/{__tests__/optimizer-advanced.test.d.ts → src/__tests__/mc-syntax.test.d.ts} +0 -0
  563. /package/dist/{__tests__ → src/__tests__}/mc-syntax.test.js +0 -0
  564. /package/dist/{__tests__/optimizer.test.d.ts → src/__tests__/mir/arithmetic.test.d.ts} +0 -0
  565. /package/dist/{__tests__/parser.test.d.ts → src/__tests__/mir/control-flow.test.d.ts} +0 -0
  566. /package/dist/{__tests__/repl.test.d.ts → src/__tests__/mir/verify.test.d.ts} +0 -0
  567. /package/dist/{__tests__/runtime.test.d.ts → src/__tests__/optimizer/block_merge.test.d.ts} +0 -0
  568. /package/dist/{__tests__/structure-optimizer.test.d.ts → src/__tests__/optimizer/branch_simplify.test.d.ts} +0 -0
  569. /package/dist/{__tests__/typechecker.test.d.ts → src/__tests__/optimizer/constant_fold.test.d.ts} +0 -0
  570. /package/dist/{__tests__/var-allocator.test.d.ts → src/__tests__/optimizer/copy_prop.test.d.ts} +0 -0
  571. /package/dist/{__tests__ → src/__tests__}/parser.test.js +0 -0
  572. /package/dist/{__tests__ → src/__tests__}/repl.test.js +0 -0
  573. /package/dist/{__tests__ → src/__tests__}/typechecker.test.js +0 -0
  574. /package/dist/{ast → src/ast}/types.js +0 -0
  575. /package/dist/{builtins → src/builtins}/metadata.d.ts +0 -0
  576. /package/dist/{builtins → src/builtins}/metadata.js +0 -0
  577. /package/dist/{events → src/events}/types.d.ts +0 -0
  578. /package/dist/{events → src/events}/types.js +0 -0
  579. /package/dist/{formatter → src/formatter}/index.d.ts +0 -0
  580. /package/dist/{formatter → src/formatter}/index.js +0 -0
  581. /package/dist/{mc-test → src/mc-test}/client.d.ts +0 -0
  582. /package/dist/{mc-test → src/mc-test}/client.js +0 -0
  583. /package/dist/{mc-test → src/mc-test}/runner.d.ts +0 -0
  584. /package/dist/{mc-test → src/mc-test}/setup.d.ts +0 -0
  585. /package/dist/{mc-test → src/mc-test}/setup.js +0 -0
  586. /package/dist/{mc-validator → src/mc-validator}/index.d.ts +0 -0
  587. /package/dist/{mc-validator → src/mc-validator}/index.js +0 -0
  588. /package/dist/{nbt → src/nbt}/index.d.ts +0 -0
  589. /package/dist/{nbt → src/nbt}/index.js +0 -0
  590. /package/dist/{repl.js → src/repl.js} +0 -0
  591. /package/dist/{runtime → src/runtime}/index.d.ts +0 -0
  592. /package/dist/{types → src/types}/entity-hierarchy.d.ts +0 -0
  593. /package/dist/{types → src/types}/entity-hierarchy.js +0 -0
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Interprocedural Constant Propagation — MIR module-level optimization pass.
3
+ *
4
+ * For each call site where all arguments are compile-time constants, creates
5
+ * a specialized clone of the callee with the parameters substituted as
6
+ * constants, then runs constant folding on the clone.
7
+ *
8
+ * Specialized function naming: `original__const_N1_N2_...` where N1, N2 are
9
+ * the constant argument values.
10
+ *
11
+ * This pass iterates to fixpoint to handle transitively specialized callees.
12
+ */
13
+
14
+ import type { MIRFunction, MIRModule, MIRBlock, MIRInstr, Operand, Temp } from '../mir/types'
15
+ import { constantFold } from './constant_fold'
16
+
17
+ export function interproceduralConstProp(mod: MIRModule): MIRModule {
18
+ let current = mod
19
+ // Iterate to fixpoint (new specializations may enable further specializations)
20
+ for (let i = 0; i < 10; i++) {
21
+ const next = runOnePass(current)
22
+ if (next.functions.length === current.functions.length &&
23
+ JSON.stringify(next.functions.map(f => f.name)) === JSON.stringify(current.functions.map(f => f.name))) {
24
+ // No new specializations
25
+ return next
26
+ }
27
+ current = next
28
+ }
29
+ return current
30
+ }
31
+
32
+ function runOnePass(mod: MIRModule): MIRModule {
33
+ const fnMap = new Map<string, MIRFunction>(mod.functions.map(f => [f.name, f]))
34
+ const newFunctions: MIRFunction[] = [...mod.functions]
35
+ const added = new Set<string>()
36
+
37
+ for (const fn of mod.functions) {
38
+ for (const block of fn.blocks) {
39
+ for (const instr of block.instrs) {
40
+ if (instr.kind !== 'call') continue
41
+ if (instr.args.length === 0) continue
42
+ if (!instr.args.every(a => a.kind === 'const')) continue
43
+
44
+ const callee = fnMap.get(instr.fn)
45
+ if (!callee) continue
46
+ if (callee.isMacro) continue
47
+ if (callee.name === fn.name) continue // no direct recursion at call site
48
+ if (callee.params.length !== instr.args.length) continue
49
+ // Skip if the callee is self-recursive (would cause infinite specialization)
50
+ if (isSelfRecursive(callee)) continue
51
+ // Only specialize single-block (loop-free) functions — loop bodies mutate
52
+ // variables in ways that make constant-param substitution unsafe.
53
+ if (callee.blocks.length > 1) continue
54
+
55
+ const constArgs = instr.args as { kind: 'const'; value: number }[]
56
+ const mangledName = mangleName(instr.fn, constArgs.map(a => a.value))
57
+
58
+ if (fnMap.has(mangledName) || added.has(mangledName)) continue
59
+
60
+ // Create specialized clone
61
+ const specialized = specialize(callee, constArgs.map(a => a.value), mangledName)
62
+ newFunctions.push(specialized)
63
+ added.add(mangledName)
64
+ fnMap.set(mangledName, specialized)
65
+ }
66
+ }
67
+ }
68
+
69
+ if (added.size === 0) return mod
70
+
71
+ // Rewrite call sites to use specialized names
72
+ const updatedFunctions = newFunctions.map(fn => rewriteCallSites(fn, fnMap))
73
+
74
+ return { ...mod, functions: updatedFunctions }
75
+ }
76
+
77
+ /** Returns true if the function contains a call to itself (direct recursion). */
78
+ function isSelfRecursive(fn: MIRFunction): boolean {
79
+ for (const block of fn.blocks) {
80
+ for (const instr of block.instrs) {
81
+ if (instr.kind === 'call' && instr.fn === fn.name) return true
82
+ if (instr.kind === 'call_macro' && instr.fn === fn.name) return true
83
+ }
84
+ }
85
+ return false
86
+ }
87
+
88
+ function mangleName(name: string, args: number[]): string {
89
+ return `${name}__const_${args.map(v => v < 0 ? `n${Math.abs(v)}` : String(v)).join('_')}`
90
+ }
91
+
92
+ function specialize(fn: MIRFunction, args: number[], newName: string): MIRFunction {
93
+ // Build substitution map: param.name → const operand
94
+ const sub = new Map<Temp, Operand>()
95
+ for (let i = 0; i < fn.params.length; i++) {
96
+ sub.set(fn.params[i].name, { kind: 'const', value: args[i] })
97
+ }
98
+
99
+ const newBlocks = fn.blocks.map(block => substituteBlock(block, sub))
100
+ const specialized: MIRFunction = {
101
+ ...fn,
102
+ name: newName,
103
+ params: [], // no params — all specialized
104
+ blocks: newBlocks,
105
+ isMacro: false,
106
+ }
107
+
108
+ // Run constant folding on the specialized function
109
+ return constantFold(specialized)
110
+ }
111
+
112
+ function substituteBlock(block: MIRBlock, sub: Map<Temp, Operand>): MIRBlock {
113
+ const instrs = block.instrs.map(instr => substituteInstr(instr, sub))
114
+ const term = substituteInstr(block.term, sub)
115
+ return { ...block, instrs, term }
116
+ }
117
+
118
+ function substituteOp(op: Operand, sub: Map<Temp, Operand>): Operand {
119
+ if (op.kind === 'temp') {
120
+ const replacement = sub.get(op.name)
121
+ if (replacement !== undefined) return replacement
122
+ }
123
+ return op
124
+ }
125
+
126
+ function substituteInstr(instr: MIRInstr, sub: Map<Temp, Operand>): MIRInstr {
127
+ switch (instr.kind) {
128
+ case 'copy': return { ...instr, src: substituteOp(instr.src, sub) }
129
+ case 'neg': case 'not': return { ...instr, src: substituteOp(instr.src, sub) }
130
+ case 'add': case 'sub': case 'mul': case 'div': case 'mod':
131
+ case 'and': case 'or':
132
+ return { ...instr, a: substituteOp(instr.a, sub), b: substituteOp(instr.b, sub) }
133
+ case 'cmp':
134
+ return { ...instr, a: substituteOp(instr.a, sub), b: substituteOp(instr.b, sub) }
135
+ case 'nbt_write':
136
+ return { ...instr, src: substituteOp(instr.src, sub) }
137
+ case 'call':
138
+ return { ...instr, args: instr.args.map(a => substituteOp(a, sub)) }
139
+ case 'call_macro':
140
+ return { ...instr, args: instr.args.map(a => ({ ...a, value: substituteOp(a.value, sub) })) }
141
+ case 'branch':
142
+ return { ...instr, cond: substituteOp(instr.cond, sub) }
143
+ case 'return':
144
+ return { ...instr, value: instr.value ? substituteOp(instr.value, sub) : null }
145
+ default:
146
+ return instr
147
+ }
148
+ }
149
+
150
+ function rewriteCallSites(fn: MIRFunction, fnMap: Map<string, MIRFunction>): MIRFunction {
151
+ return {
152
+ ...fn,
153
+ blocks: fn.blocks.map(block => ({
154
+ ...block,
155
+ instrs: block.instrs.map(instr => {
156
+ if (instr.kind !== 'call') return instr
157
+ if (!instr.args.every(a => a.kind === 'const')) return instr
158
+ const callee = fnMap.get(instr.fn)
159
+ if (!callee) return instr
160
+ if (callee.isMacro) return instr
161
+ if (callee.name === fn.name) return instr
162
+ if (callee.params.length !== instr.args.length) return instr
163
+
164
+ const constArgs = instr.args as { kind: 'const'; value: number }[]
165
+ const mangledName = mangleName(instr.fn, constArgs.map(a => a.value))
166
+ if (!fnMap.has(mangledName)) return instr
167
+
168
+ return { ...instr, fn: mangledName, args: [] }
169
+ }),
170
+ term: (() => {
171
+ const term = block.term
172
+ if (term.kind !== 'call') return term
173
+ return term
174
+ })(),
175
+ })),
176
+ }
177
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Constant Immediate Folding — LIR optimization pass.
3
+ *
4
+ * Peephole: finds score_set($__const_C, C) immediately followed by
5
+ * score_add/score_sub(dst, $__const_C), where $__const_C has no other
6
+ * uses in the function, and replaces the pair with a single
7
+ * `scoreboard players add/remove` raw command.
8
+ *
9
+ * This saves one command per arithmetic-with-constant operation.
10
+ */
11
+
12
+ import type { LIRFunction, LIRInstr, Slot } from '../../lir/types'
13
+
14
+ function slotKey(s: Slot): string {
15
+ return `${s.player}\0${s.obj}`
16
+ }
17
+
18
+ /** Count how many times a slot is used as a source operand. */
19
+ function countSlotUses(instrs: LIRInstr[], target: string): number {
20
+ let count = 0
21
+ for (const instr of instrs) {
22
+ for (const s of getReadSlots(instr)) {
23
+ if (slotKey(s) === target) count++
24
+ }
25
+ }
26
+ return count
27
+ }
28
+
29
+ function extractSlotsFromRaw(cmd: string): Slot[] {
30
+ const slots: Slot[] = []
31
+ const re = /(\$[\w.]+)\s+(\S+)/g
32
+ let m
33
+ while ((m = re.exec(cmd)) !== null) {
34
+ slots.push({ player: m[1], obj: m[2] })
35
+ }
36
+ return slots
37
+ }
38
+
39
+ function getReadSlots(instr: LIRInstr): Slot[] {
40
+ switch (instr.kind) {
41
+ case 'score_copy': return [instr.src]
42
+ case 'score_add': case 'score_sub':
43
+ case 'score_mul': case 'score_div': case 'score_mod':
44
+ case 'score_min': case 'score_max':
45
+ return [instr.src]
46
+ case 'score_swap': return [instr.a, instr.b]
47
+ case 'store_cmd_to_score': return getReadSlots(instr.cmd)
48
+ case 'store_score_to_nbt': return [instr.src]
49
+ case 'return_value': return [instr.slot]
50
+ case 'call_if_matches': case 'call_unless_matches':
51
+ return [instr.slot]
52
+ case 'call_if_score': case 'call_unless_score':
53
+ return [instr.a, instr.b]
54
+ case 'raw': return extractSlotsFromRaw(instr.cmd)
55
+ case 'macro_line': return extractSlotsFromRaw(instr.template)
56
+ default: return []
57
+ }
58
+ }
59
+
60
+ export function constImmFold(fn: LIRFunction): LIRFunction {
61
+ const instrs = fn.instructions
62
+ if (instrs.length < 2) return fn
63
+
64
+ // Pre-compute use counts for all const slots
65
+ const useCounts = new Map<string, number>()
66
+ for (const instr of instrs) {
67
+ for (const s of getReadSlots(instr)) {
68
+ const key = slotKey(s)
69
+ useCounts.set(key, (useCounts.get(key) || 0) + 1)
70
+ }
71
+ }
72
+
73
+ const result: LIRInstr[] = []
74
+ let changed = false
75
+ let i = 0
76
+
77
+ while (i < instrs.length) {
78
+ const curr = instrs[i]
79
+ const next = instrs[i + 1]
80
+
81
+ // Pattern: score_set(constSlot, C) + score_add/sub(dst, constSlot)
82
+ if (
83
+ next &&
84
+ curr.kind === 'score_set' &&
85
+ curr.dst.player.startsWith('$__const_') &&
86
+ (next.kind === 'score_add' || next.kind === 'score_sub') &&
87
+ slotKey(next.src) === slotKey(curr.dst) &&
88
+ (useCounts.get(slotKey(curr.dst)) || 0) === 1
89
+ ) {
90
+ const C = curr.value
91
+ const dst = next.dst
92
+
93
+ if (C === 0 && next.kind === 'score_add') {
94
+ // add 0 is a no-op — skip both
95
+ changed = true
96
+ i += 2
97
+ continue
98
+ }
99
+
100
+ if (C === 0 && next.kind === 'score_sub') {
101
+ // sub 0 is a no-op — skip both
102
+ changed = true
103
+ i += 2
104
+ continue
105
+ }
106
+
107
+ // Determine add vs remove
108
+ let op: string
109
+ let val: number
110
+ if (next.kind === 'score_add') {
111
+ if (C > 0) {
112
+ op = 'add'
113
+ val = C
114
+ } else {
115
+ op = 'remove'
116
+ val = -C
117
+ }
118
+ } else {
119
+ // score_sub
120
+ if (C > 0) {
121
+ op = 'remove'
122
+ val = C
123
+ } else {
124
+ op = 'add'
125
+ val = -C
126
+ }
127
+ }
128
+
129
+ result.push({
130
+ kind: 'raw',
131
+ cmd: `scoreboard players ${op} ${dst.player} ${dst.obj} ${val}`,
132
+ })
133
+ changed = true
134
+ i += 2
135
+ continue
136
+ }
137
+
138
+ result.push(curr)
139
+ i++
140
+ }
141
+
142
+ return changed ? { ...fn, instructions: result } : fn
143
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Dead Slot Elimination — LIR optimization pass.
3
+ *
4
+ * Removes score_set / score_copy instructions where the destination slot
5
+ * is never read anywhere in the function.
6
+ *
7
+ * Preserves writes to:
8
+ * - $ret (return value)
9
+ * - $p0, $p1, … (parameter passing slots)
10
+ * - slots used in side-effectful instructions (calls, stores, nbt ops, raw)
11
+ */
12
+
13
+ import type { LIRFunction, LIRInstr, LIRModule, Slot } from '../../lir/types'
14
+
15
+ /** Canonical key for a slot (player + obj). */
16
+ function slotKey(s: Slot): string {
17
+ return `${s.player}\0${s.obj}`
18
+ }
19
+
20
+ /**
21
+ * Extract slot references from a raw MC command string.
22
+ * Matches patterns like `$player_name objective_name` used in scoreboard commands.
23
+ */
24
+ function extractSlotsFromRaw(cmd: string): Slot[] {
25
+ const slots: Slot[] = []
26
+ // Match $<player> <obj> patterns (scoreboard slot references)
27
+ const re = /(\$[\w.]+)\s+(\S+)/g
28
+ let m
29
+ while ((m = re.exec(cmd)) !== null) {
30
+ slots.push({ player: m[1], obj: m[2] })
31
+ }
32
+ return slots
33
+ }
34
+
35
+ /** Collect all slots that are *read* (used as source) by an instruction. */
36
+ function getReadSlots(instr: LIRInstr): Slot[] {
37
+ switch (instr.kind) {
38
+ case 'score_copy': return [instr.src]
39
+ case 'score_add': case 'score_sub':
40
+ case 'score_mul': case 'score_div': case 'score_mod':
41
+ case 'score_min': case 'score_max':
42
+ return [instr.src]
43
+ case 'score_swap': return [instr.a, instr.b]
44
+ case 'store_cmd_to_score': return getReadSlots(instr.cmd)
45
+ case 'store_score_to_nbt': return [instr.src]
46
+ case 'store_nbt_to_score': return []
47
+ case 'return_value': return [instr.slot]
48
+ case 'call_if_matches': case 'call_unless_matches':
49
+ return [instr.slot]
50
+ case 'call_if_score': case 'call_unless_score':
51
+ return [instr.a, instr.b]
52
+ case 'raw': return extractSlotsFromRaw(instr.cmd)
53
+ case 'macro_line': return extractSlotsFromRaw(instr.template)
54
+ default: return []
55
+ }
56
+ }
57
+
58
+ /** Returns the destination slot if the instruction is a pure write (no side effects). */
59
+ function getPureWriteDst(instr: LIRInstr): Slot | null {
60
+ switch (instr.kind) {
61
+ case 'score_set': return instr.dst
62
+ case 'score_copy': return instr.dst
63
+ default: return null
64
+ }
65
+ }
66
+
67
+ /** True if a slot should never be eliminated (externally visible). */
68
+ function isProtectedSlot(s: Slot): boolean {
69
+ const p = s.player
70
+ if (p === '$ret' || p.startsWith('$ret_') || /^\$p\d+$/.test(p)) return true
71
+ // Option slots: __opt_ prefix ensures they're always written to scoreboard
72
+ // even when the variable appears unused locally
73
+ if (p.includes('__opt_')) return true
74
+ return false
75
+ }
76
+
77
+ export function deadSlotElim(fn: LIRFunction): LIRFunction {
78
+ // 1. Collect all read slots across the function
79
+ const readSet = new Set<string>()
80
+ for (const instr of fn.instructions) {
81
+ for (const s of getReadSlots(instr)) {
82
+ readSet.add(slotKey(s))
83
+ }
84
+ }
85
+
86
+ // 2. Filter out pure writes to slots that are never read
87
+ const filtered = fn.instructions.filter(instr => {
88
+ const dst = getPureWriteDst(instr)
89
+ if (dst === null) return true // not a pure write → keep
90
+ if (isProtectedSlot(dst)) return true
91
+ return readSet.has(slotKey(dst))
92
+ })
93
+
94
+ if (filtered.length === fn.instructions.length) return fn
95
+ return { ...fn, instructions: filtered }
96
+ }
97
+
98
+ export function deadSlotElimModule(mod: LIRModule): LIRModule {
99
+ // Collect all slots read across ALL functions (cross-function visibility)
100
+ const globalReadSet = new Set<string>()
101
+ for (const fn of mod.functions) {
102
+ for (const instr of fn.instructions) {
103
+ for (const s of getReadSlots(instr)) {
104
+ globalReadSet.add(slotKey(s))
105
+ }
106
+ }
107
+ }
108
+
109
+ let changed = false
110
+ const functions = mod.functions.map(fn => {
111
+ const filtered = fn.instructions.filter(instr => {
112
+ const dst = getPureWriteDst(instr)
113
+ if (dst === null) return true
114
+ if (isProtectedSlot(dst)) return true
115
+ return globalReadSet.has(slotKey(dst))
116
+ })
117
+ if (filtered.length !== fn.instructions.length) changed = true
118
+ if (filtered.length === fn.instructions.length) return fn
119
+ return { ...fn, instructions: filtered }
120
+ })
121
+
122
+ return changed ? { ...mod, functions } : mod
123
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Execute-Store Peephole Pass — LIR optimization pass.
3
+ *
4
+ * Merges a `call_context` immediately followed by `score_set(dst, value)`
5
+ * into a single `store_cmd_to_score` instruction:
6
+ *
7
+ * execute [subcommands] run function ns:fn ← call_context
8
+ * scoreboard players set $result __ns N ← score_set
9
+ *
10
+ * becomes:
11
+ *
12
+ * execute store result score $result __ns [subcommands] run function ns:fn
13
+ *
14
+ * This saves one command when the caller only needs to know whether the
15
+ * execute chain ran (success = 1) or to capture the function's return value.
16
+ *
17
+ * Safety: only merges when the two instructions are immediately adjacent
18
+ * (no instructions between them).
19
+ */
20
+
21
+ import type { LIRFunction, LIRInstr } from '../../lir/types'
22
+
23
+ export function execStorePeephole(fn: LIRFunction): LIRFunction {
24
+ const instrs = fn.instructions
25
+ if (instrs.length < 2) return fn
26
+
27
+ const result: LIRInstr[] = []
28
+ let changed = false
29
+ let i = 0
30
+
31
+ while (i < instrs.length) {
32
+ const curr = instrs[i]
33
+ const next = instrs[i + 1]
34
+
35
+ // Pattern: call_context immediately followed by score_set
36
+ if (
37
+ next &&
38
+ curr.kind === 'call_context' &&
39
+ next.kind === 'score_set'
40
+ ) {
41
+ // Merge: execute store result score <dst> [subcommands] run function <fn>
42
+ result.push({
43
+ kind: 'store_cmd_to_score',
44
+ dst: next.dst,
45
+ cmd: curr,
46
+ })
47
+ changed = true
48
+ i += 2
49
+ continue
50
+ }
51
+
52
+ result.push(curr)
53
+ i++
54
+ }
55
+
56
+ return changed ? { ...fn, instructions: result } : fn
57
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * LIR Optimization Pipeline — runs all LIR passes on a module.
3
+ *
4
+ * Pass order: dead_slot → const_imm
5
+ * Dead slot runs first to remove unused writes, exposing more
6
+ * single-use const slots for const_imm to fold.
7
+ */
8
+
9
+ import type { LIRFunction, LIRModule } from '../../lir/types'
10
+ import { deadSlotElimModule } from './dead_slot'
11
+ import { constImmFold } from './const_imm'
12
+ import { execStorePeephole } from './peephole'
13
+
14
+ export type LIRPass = (fn: LIRFunction) => LIRFunction
15
+
16
+ const perFunctionPasses: LIRPass[] = [
17
+ execStorePeephole,
18
+ constImmFold,
19
+ ]
20
+
21
+ export function lirOptimizeModule(mod: LIRModule): LIRModule {
22
+ // Module-level pass: dead slot elimination (cross-function analysis)
23
+ let result = deadSlotElimModule(mod)
24
+
25
+ // Per-function passes
26
+ let changed = false
27
+ const functions = result.functions.map(fn => {
28
+ let current = fn
29
+ for (const pass of perFunctionPasses) {
30
+ current = pass(current)
31
+ }
32
+ if (current !== fn) changed = true
33
+ return current
34
+ })
35
+
36
+ return changed ? { ...result, functions } : result
37
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * NBT Batch Read — MIR optimization pass.
3
+ *
4
+ * Eliminates redundant nbt_read instructions within a basic block:
5
+ * if the same (ns, path, scale) is read twice with no intervening nbt_write
6
+ * to that ns, the second read is replaced with a copy from the first read's dst.
7
+ *
8
+ * This reduces expensive `data get` commands in generated .mcfunction files.
9
+ */
10
+
11
+ import type { MIRFunction, MIRBlock, MIRInstr, Temp } from '../mir/types'
12
+
13
+ export function nbtBatchRead(fn: MIRFunction): MIRFunction {
14
+ return {
15
+ ...fn,
16
+ blocks: fn.blocks.map(deduplicateBlock),
17
+ }
18
+ }
19
+
20
+ function deduplicateBlock(block: MIRBlock): MIRBlock {
21
+ // Map from "ns\0path\0scale" → dst temp of the first read
22
+ const cache = new Map<string, Temp>()
23
+
24
+ const instrs: MIRInstr[] = []
25
+ for (const instr of block.instrs) {
26
+ if (instr.kind === 'nbt_read') {
27
+ const key = `${instr.ns}\0${instr.path}\0${instr.scale}`
28
+ const cached = cache.get(key)
29
+ if (cached !== undefined) {
30
+ // Replace with copy from cached temp
31
+ instrs.push({ kind: 'copy', dst: instr.dst, src: { kind: 'temp', name: cached } })
32
+ } else {
33
+ cache.set(key, instr.dst)
34
+ instrs.push(instr)
35
+ }
36
+ } else if (instr.kind === 'nbt_write') {
37
+ // Invalidate all cached reads for this ns
38
+ for (const key of [...cache.keys()]) {
39
+ if (key.startsWith(instr.ns + '\0')) {
40
+ cache.delete(key)
41
+ }
42
+ }
43
+ instrs.push(instr)
44
+ } else {
45
+ instrs.push(instr)
46
+ }
47
+ }
48
+
49
+ return { ...block, instrs }
50
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * MIR Optimization Pipeline — runs all passes to a fixpoint.
3
+ *
4
+ * Each pass is a function MIRFunction → MIRFunction.
5
+ * The pipeline iterates until no pass changes the function (fixpoint).
6
+ *
7
+ * Module-level passes (interproceduralConstProp) run once after the
8
+ * per-function fixpoint loop.
9
+ */
10
+
11
+ import type { MIRFunction, MIRModule } from '../mir/types'
12
+ import { constantFold } from './constant_fold'
13
+ import { copyProp } from './copy_prop'
14
+ import { dce } from './dce'
15
+ import { blockMerge } from './block_merge'
16
+ import { branchSimplify } from './branch_simplify'
17
+ import { loopUnroll } from './unroll'
18
+ import { nbtBatchRead } from './nbt-batch'
19
+ import { interproceduralConstProp } from './interprocedural'
20
+
21
+ // selectorCache is intentionally excluded from the default pipeline:
22
+ // it emits synthetic __sel_cleanup_* / __sel_tag_* call_context instructions
23
+ // that require codegen support before being used end-to-end.
24
+ export { selectorCache } from './selector-cache'
25
+
26
+ export type Pass = (fn: MIRFunction) => MIRFunction
27
+
28
+ const defaultPasses: Pass[] = [
29
+ loopUnroll,
30
+ nbtBatchRead,
31
+ constantFold,
32
+ copyProp,
33
+ branchSimplify,
34
+ dce,
35
+ blockMerge,
36
+ ]
37
+
38
+ const MAX_ITERATIONS = 20
39
+
40
+ export function optimizeFunction(fn: MIRFunction, passes: Pass[] = defaultPasses): MIRFunction {
41
+ let current = fn
42
+ for (let i = 0; i < MAX_ITERATIONS; i++) {
43
+ const before = JSON.stringify(current)
44
+ for (const pass of passes) {
45
+ current = pass(current)
46
+ }
47
+ if (JSON.stringify(current) === before) break
48
+ }
49
+ return current
50
+ }
51
+
52
+ export function optimizeModule(mod: MIRModule, passes?: Pass[]): MIRModule {
53
+ const perFnOptimized = {
54
+ ...mod,
55
+ functions: mod.functions.map(fn => optimizeFunction(fn, passes)),
56
+ }
57
+ // Module-level pass: interprocedural constant propagation
58
+ return interproceduralConstProp(perFnOptimized)
59
+ }