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,996 @@
1
+ /**
2
+ * Coroutine Transform — MIR module-level pass.
3
+ *
4
+ * Transforms functions annotated with @coroutine(batch=N, onDone=fn) into
5
+ * tick-spread state machines. Each loop back-edge becomes a yield point,
6
+ * and the function is split into continuation functions dispatched by a
7
+ * @tick function via a pc (program counter) scoreboard slot.
8
+ *
9
+ * Algorithm:
10
+ * 1. Compute dominator tree → find back edges (yield points)
11
+ * 2. Backward liveness analysis at yield points
12
+ * 3. Split CFG into continuations at yield points
13
+ * 4. Promote live variables to persistent scoreboard slots
14
+ * 5. Generate @tick dispatcher function
15
+ *
16
+ * Spec: docs/compiler-pipeline-redesign.md § "Coroutine Transform"
17
+ */
18
+
19
+ import type {
20
+ MIRFunction,
21
+ MIRModule,
22
+ MIRBlock,
23
+ MIRInstr,
24
+ Operand,
25
+ Temp,
26
+ BlockId,
27
+ } from '../mir/types'
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Public API
31
+ // ---------------------------------------------------------------------------
32
+
33
+ export interface CoroutineInfo {
34
+ fnName: string
35
+ batch: number
36
+ onDone?: string
37
+ }
38
+
39
+ export interface CoroutineResult {
40
+ module: MIRModule
41
+ /** Names of generated @tick dispatcher functions (caller must add to tick list). */
42
+ generatedTickFunctions: string[]
43
+ }
44
+
45
+ /**
46
+ * Apply the coroutine transform to all functions in `infos`.
47
+ * Returns a new module with continuations + dispatchers injected,
48
+ * and the original function replaced with initialization code.
49
+ */
50
+ export function coroutineTransform(
51
+ mod: MIRModule,
52
+ infos: CoroutineInfo[],
53
+ ): CoroutineResult {
54
+ if (infos.length === 0) return { module: mod, generatedTickFunctions: [] }
55
+
56
+ const infoMap = new Map(infos.map(i => [i.fnName, i]))
57
+ const newFunctions: MIRFunction[] = []
58
+ const tickFns: string[] = []
59
+
60
+ for (const fn of mod.functions) {
61
+ const info = infoMap.get(fn.name)
62
+ if (!info) {
63
+ newFunctions.push(fn)
64
+ continue
65
+ }
66
+
67
+ const transformed = transformCoroutine(fn, info, mod.objective)
68
+ newFunctions.push(transformed.initFn)
69
+ newFunctions.push(...transformed.continuations)
70
+ newFunctions.push(transformed.dispatcher)
71
+ tickFns.push(transformed.dispatcher.name)
72
+ }
73
+
74
+ return {
75
+ module: { ...mod, functions: newFunctions },
76
+ generatedTickFunctions: tickFns,
77
+ }
78
+ }
79
+
80
+ // ---------------------------------------------------------------------------
81
+ // Core transform
82
+ // ---------------------------------------------------------------------------
83
+
84
+ interface TransformResult {
85
+ initFn: MIRFunction
86
+ continuations: MIRFunction[]
87
+ dispatcher: MIRFunction
88
+ }
89
+
90
+ function transformCoroutine(
91
+ fn: MIRFunction,
92
+ info: CoroutineInfo,
93
+ objective: string,
94
+ ): TransformResult {
95
+ const prefix = `_coro_${fn.name}`
96
+ const pcTemp = `${prefix}_pc`
97
+ const batchCountTemp = `${prefix}_batch_count`
98
+
99
+ // Step 1: Build dominator tree and find back edges
100
+ const blockMap = new Map(fn.blocks.map(b => [b.id, b]))
101
+ const doms = computeDominators(fn.blocks, fn.entry)
102
+ const backEdges = findBackEdges(fn.blocks, doms)
103
+
104
+ // If no back edges (no loops), the function doesn't need coroutine splitting.
105
+ // Just wrap it as a single continuation.
106
+ if (backEdges.length === 0) {
107
+ return buildSingleContinuation(fn, info, prefix, pcTemp, objective)
108
+ }
109
+
110
+ // Step 2: Liveness analysis — find live variables at each yield point
111
+ const liveAtYield = computeLivenessAtYieldPoints(fn.blocks, backEdges, fn.params)
112
+
113
+ // Collect all live variables across all yield points (these need promotion)
114
+ const allLiveVars = new Set<Temp>()
115
+ for (const liveSet of liveAtYield.values()) {
116
+ for (const v of liveSet) allLiveVars.add(v)
117
+ }
118
+
119
+ // Build promoted variable names: original temp → persistent slot name
120
+ const promoted = new Map<Temp, Temp>()
121
+ for (const v of allLiveVars) {
122
+ promoted.set(v, `${prefix}_${v}`)
123
+ }
124
+
125
+ // Step 3: Split CFG into continuations
126
+ // Each continuation runs from a loop header to the next yield point.
127
+ // For simplicity, we split at loop headers (targets of back edges).
128
+ const loopHeaders = new Set(backEdges.map(e => e.target))
129
+
130
+ // Partition blocks into continuation groups.
131
+ // Continuation 1: entry block → until first yield
132
+ // Continuation N: from loop header → until next yield or exit
133
+ const continuations = partitionIntoContinuations(fn, loopHeaders, backEdges)
134
+
135
+ // Step 4: Build continuation functions with batch counting and variable promotion
136
+ const contFunctions: MIRFunction[] = []
137
+
138
+ for (let i = 0; i < continuations.length; i++) {
139
+ const contId = i + 1
140
+ const cont = continuations[i]
141
+ const contFn = buildContinuationFunction(
142
+ `${prefix}_cont_${contId}`,
143
+ cont,
144
+ info.batch,
145
+ contId,
146
+ continuations.length,
147
+ promoted,
148
+ pcTemp,
149
+ batchCountTemp,
150
+ objective,
151
+ info.onDone,
152
+ fn.name,
153
+ )
154
+ contFunctions.push(contFn)
155
+ }
156
+
157
+ // Step 5: Build the init function (replaces original)
158
+ const initFn = buildInitFunction(fn, promoted, pcTemp, prefix, objective)
159
+
160
+ // Step 6: Build the @tick dispatcher
161
+ const dispatcher = buildDispatcher(
162
+ `${prefix}_tick`,
163
+ contFunctions,
164
+ pcTemp,
165
+ objective,
166
+ fn.name,
167
+ )
168
+
169
+ return { initFn, continuations: contFunctions, dispatcher }
170
+ }
171
+
172
+ // ---------------------------------------------------------------------------
173
+ // Step 1: Dominator tree & back-edge detection
174
+ // ---------------------------------------------------------------------------
175
+
176
+ function computeDominators(
177
+ blocks: MIRBlock[],
178
+ entry: BlockId,
179
+ ): Map<BlockId, BlockId> {
180
+ // Simple iterative dominator algorithm (Cooper, Harvey, Kennedy)
181
+ const blockIds = blocks.map(b => b.id)
182
+ const idom = new Map<BlockId, BlockId>()
183
+
184
+ // RPO ordering
185
+ const rpo = reversePostorder(blocks, entry)
186
+ const rpoIndex = new Map(rpo.map((id, i) => [id, i]))
187
+
188
+ // Initialize: entry dominates itself
189
+ idom.set(entry, entry)
190
+
191
+ let changed = true
192
+ while (changed) {
193
+ changed = false
194
+ for (const bId of rpo) {
195
+ if (bId === entry) continue
196
+ const block = blocks.find(b => b.id === bId)
197
+ if (!block) continue
198
+
199
+ // Find first processed predecessor
200
+ const processedPreds = block.preds.filter(p => idom.has(p))
201
+ if (processedPreds.length === 0) continue
202
+
203
+ let newIdom = processedPreds[0]
204
+ for (let i = 1; i < processedPreds.length; i++) {
205
+ newIdom = intersect(newIdom, processedPreds[i], idom, rpoIndex)
206
+ }
207
+
208
+ if (idom.get(bId) !== newIdom) {
209
+ idom.set(bId, newIdom)
210
+ changed = true
211
+ }
212
+ }
213
+ }
214
+
215
+ return idom
216
+ }
217
+
218
+ function intersect(
219
+ a: BlockId,
220
+ b: BlockId,
221
+ idom: Map<BlockId, BlockId>,
222
+ rpoIndex: Map<BlockId, number>,
223
+ ): BlockId {
224
+ let f1 = a
225
+ let f2 = b
226
+ while (f1 !== f2) {
227
+ while ((rpoIndex.get(f1) ?? 0) > (rpoIndex.get(f2) ?? 0)) {
228
+ f1 = idom.get(f1) ?? f1
229
+ }
230
+ while ((rpoIndex.get(f2) ?? 0) > (rpoIndex.get(f1) ?? 0)) {
231
+ f2 = idom.get(f2) ?? f2
232
+ }
233
+ }
234
+ return f1
235
+ }
236
+
237
+ function reversePostorder(blocks: MIRBlock[], entry: BlockId): BlockId[] {
238
+ const visited = new Set<BlockId>()
239
+ const order: BlockId[] = []
240
+ const blockMap = new Map(blocks.map(b => [b.id, b]))
241
+
242
+ function dfs(id: BlockId) {
243
+ if (visited.has(id)) return
244
+ visited.add(id)
245
+ const block = blockMap.get(id)
246
+ if (!block) return
247
+ for (const succ of getSuccessors(block.term)) {
248
+ dfs(succ)
249
+ }
250
+ order.push(id)
251
+ }
252
+
253
+ dfs(entry)
254
+ return order.reverse()
255
+ }
256
+
257
+ interface BackEdge {
258
+ source: BlockId
259
+ target: BlockId // the loop header (dominator)
260
+ }
261
+
262
+ function findBackEdges(
263
+ blocks: MIRBlock[],
264
+ doms: Map<BlockId, BlockId>,
265
+ ): BackEdge[] {
266
+ const edges: BackEdge[] = []
267
+ for (const block of blocks) {
268
+ for (const succ of getSuccessors(block.term)) {
269
+ if (dominates(succ, block.id, doms)) {
270
+ edges.push({ source: block.id, target: succ })
271
+ }
272
+ }
273
+ }
274
+ return edges
275
+ }
276
+
277
+ function dominates(a: BlockId, b: BlockId, doms: Map<BlockId, BlockId>): boolean {
278
+ let cur = b
279
+ while (cur !== a) {
280
+ const idom = doms.get(cur)
281
+ if (!idom || idom === cur) return false
282
+ cur = idom
283
+ }
284
+ return true
285
+ }
286
+
287
+ // ---------------------------------------------------------------------------
288
+ // Step 2: Liveness analysis
289
+ // ---------------------------------------------------------------------------
290
+
291
+ function computeLivenessAtYieldPoints(
292
+ blocks: MIRBlock[],
293
+ backEdges: BackEdge[],
294
+ params: { name: Temp }[],
295
+ ): Map<BlockId, Set<Temp>> {
296
+ // Standard backward liveness: live_in[B] = use[B] ∪ (live_out[B] \ def[B])
297
+ const blockMap = new Map(blocks.map(b => [b.id, b]))
298
+
299
+ // Compute use/def for each block
300
+ const useSets = new Map<BlockId, Set<Temp>>()
301
+ const defSets = new Map<BlockId, Set<Temp>>()
302
+
303
+ for (const block of blocks) {
304
+ const use = new Set<Temp>()
305
+ const def = new Set<Temp>()
306
+
307
+ for (const instr of [...block.instrs, block.term]) {
308
+ // Uses before defs
309
+ for (const t of getUsedTemps(instr)) {
310
+ if (!def.has(t)) use.add(t)
311
+ }
312
+ const dst = getDst(instr)
313
+ if (dst) def.add(dst)
314
+ }
315
+
316
+ useSets.set(block.id, use)
317
+ defSets.set(block.id, def)
318
+ }
319
+
320
+ // Iterative liveness
321
+ const liveIn = new Map<BlockId, Set<Temp>>()
322
+ const liveOut = new Map<BlockId, Set<Temp>>()
323
+ for (const block of blocks) {
324
+ liveIn.set(block.id, new Set())
325
+ liveOut.set(block.id, new Set())
326
+ }
327
+
328
+ let changed = true
329
+ while (changed) {
330
+ changed = false
331
+ for (const block of [...blocks].reverse()) {
332
+ // live_out = ∪ live_in[succ]
333
+ const newOut = new Set<Temp>()
334
+ for (const succ of getSuccessors(block.term)) {
335
+ const succIn = liveIn.get(succ)
336
+ if (succIn) for (const t of succIn) newOut.add(t)
337
+ }
338
+
339
+ // live_in = use ∪ (live_out \ def)
340
+ const newIn = new Set(useSets.get(block.id) ?? [])
341
+ const def = defSets.get(block.id) ?? new Set()
342
+ for (const t of newOut) {
343
+ if (!def.has(t)) newIn.add(t)
344
+ }
345
+
346
+ const prevIn = liveIn.get(block.id)!
347
+ const prevOut = liveOut.get(block.id)!
348
+ if (!setsEqual(newIn, prevIn) || !setsEqual(newOut, prevOut)) {
349
+ liveIn.set(block.id, newIn)
350
+ liveOut.set(block.id, newOut)
351
+ changed = true
352
+ }
353
+ }
354
+ }
355
+
356
+ // At each yield point (back edge source → target), the live vars
357
+ // are the live-out of the source block
358
+ const result = new Map<BlockId, Set<Temp>>()
359
+ for (const edge of backEdges) {
360
+ const lo = liveOut.get(edge.source) ?? new Set()
361
+ result.set(edge.source, lo)
362
+ }
363
+
364
+ return result
365
+ }
366
+
367
+ function setsEqual<T>(a: Set<T>, b: Set<T>): boolean {
368
+ if (a.size !== b.size) return false
369
+ for (const v of a) if (!b.has(v)) return false
370
+ return true
371
+ }
372
+
373
+ // ---------------------------------------------------------------------------
374
+ // Step 3: Partition into continuations
375
+ // ---------------------------------------------------------------------------
376
+
377
+ interface Continuation {
378
+ blocks: MIRBlock[]
379
+ isLoopBody: boolean
380
+ loopHeaderId?: BlockId
381
+ /** Block IDs that exit the loop (branch to blocks outside this continuation) */
382
+ exitBlocks: Set<BlockId>
383
+ }
384
+
385
+ function partitionIntoContinuations(
386
+ fn: MIRFunction,
387
+ loopHeaders: Set<BlockId>,
388
+ backEdges: BackEdge[],
389
+ ): Continuation[] {
390
+ const blockMap = new Map(fn.blocks.map(b => [b.id, b]))
391
+ const backEdgeTargets = new Set(backEdges.map(e => e.target))
392
+ const backEdgeSources = new Set(backEdges.map(e => e.source))
393
+
394
+ // Find which blocks belong to which loop (blocks between header and back edge)
395
+ // We use a simple approach: BFS from entry, split at loop headers
396
+ const loopBlocks = new Map<BlockId, Set<BlockId>>() // header → blocks in loop
397
+
398
+ for (const header of loopHeaders) {
399
+ const inLoop = new Set<BlockId>()
400
+ // Find all blocks that can reach the back edge source without leaving the loop
401
+ // i.e., blocks dominated by the header that can reach a back edge source
402
+ const sources = backEdges.filter(e => e.target === header).map(e => e.source)
403
+
404
+ // Backward BFS from back-edge sources to header
405
+ const queue = [...sources]
406
+ for (const s of sources) inLoop.add(s)
407
+ inLoop.add(header)
408
+ while (queue.length > 0) {
409
+ const bid = queue.shift()!
410
+ const block = blockMap.get(bid)
411
+ if (!block) continue
412
+ for (const pred of block.preds) {
413
+ if (!inLoop.has(pred) && pred !== header) {
414
+ inLoop.add(pred)
415
+ queue.push(pred)
416
+ }
417
+ }
418
+ }
419
+ inLoop.add(header)
420
+ loopBlocks.set(header, inLoop)
421
+ }
422
+
423
+ // Build continuations:
424
+ // Continuation 1 = loop body (the main one — header + loop blocks)
425
+ // Continuation 2 = post-loop (blocks after loop exit)
426
+ const conts: Continuation[] = []
427
+
428
+ // For each loop, create a loop-body continuation
429
+ for (const header of loopHeaders) {
430
+ const lb = loopBlocks.get(header)!
431
+ const loopBlocksList = fn.blocks.filter(b => lb.has(b.id))
432
+ const exitBlocks = new Set<BlockId>()
433
+
434
+ // Find exit blocks: blocks in the loop that branch to blocks outside the loop
435
+ for (const block of loopBlocksList) {
436
+ for (const succ of getSuccessors(block.term)) {
437
+ if (!lb.has(succ)) exitBlocks.add(block.id)
438
+ }
439
+ }
440
+
441
+ conts.push({
442
+ blocks: loopBlocksList,
443
+ isLoopBody: true,
444
+ loopHeaderId: header,
445
+ exitBlocks,
446
+ })
447
+ }
448
+
449
+ // Post-loop continuation: all blocks not in any loop
450
+ const allLoopBlockIds = new Set<BlockId>()
451
+ for (const lb of loopBlocks.values()) {
452
+ for (const id of lb) allLoopBlockIds.add(id)
453
+ }
454
+ // Also exclude the entry block if it's not in a loop (it becomes the init fn)
455
+ const postLoopBlocks = fn.blocks.filter(b =>
456
+ !allLoopBlockIds.has(b.id) && b.id !== fn.entry
457
+ )
458
+ if (postLoopBlocks.length > 0) {
459
+ conts.push({
460
+ blocks: postLoopBlocks,
461
+ isLoopBody: false,
462
+ exitBlocks: new Set(),
463
+ })
464
+ }
465
+
466
+ return conts
467
+ }
468
+
469
+ // ---------------------------------------------------------------------------
470
+ // Step 4: Build continuation functions
471
+ // ---------------------------------------------------------------------------
472
+
473
+ function buildContinuationFunction(
474
+ name: string,
475
+ cont: Continuation,
476
+ batch: number,
477
+ contId: number,
478
+ totalConts: number,
479
+ promoted: Map<Temp, Temp>,
480
+ pcTemp: Temp,
481
+ batchCountTemp: Temp,
482
+ objective: string,
483
+ onDone: string | undefined,
484
+ originalFnName: string,
485
+ ): MIRFunction {
486
+ if (cont.isLoopBody) {
487
+ return buildLoopContinuation(
488
+ name, cont, batch, contId, totalConts,
489
+ promoted, pcTemp, batchCountTemp, objective, onDone,
490
+ )
491
+ } else {
492
+ return buildPostLoopContinuation(
493
+ name, cont, contId, promoted, pcTemp, objective, onDone,
494
+ )
495
+ }
496
+ }
497
+
498
+ function buildLoopContinuation(
499
+ name: string,
500
+ cont: Continuation,
501
+ batch: number,
502
+ contId: number,
503
+ totalConts: number,
504
+ promoted: Map<Temp, Temp>,
505
+ pcTemp: Temp,
506
+ batchCountTemp: Temp,
507
+ objective: string,
508
+ onDone: string | undefined,
509
+ ): MIRFunction {
510
+ // Build a new function that:
511
+ // 1. Initializes batch_count = 0
512
+ // 2. Runs the loop body up to `batch` iterations
513
+ // 3. At back edge: if batch_count >= batch, yield (return)
514
+ // 4. On loop exit: return (dispatcher handles next continuation via pc)
515
+ //
516
+ // IMPORTANT: The LIR lowerer handles multi-pred blocks via `jump` terminators
517
+ // correctly (emits a function call) but `branch → multi-pred` can cause infinite
518
+ // recursion. So we ensure back edges use `jump` via a trampoline block, and
519
+ // batch-done checks use `branch → yield_block | continue_block` where both
520
+ // targets have single predecessors.
521
+
522
+ const blocks: MIRBlock[] = []
523
+ const batchCmpTemp = `${batchCountTemp}_cmp`
524
+
525
+ // Entry: set batch_count = 0, then jump to loop header
526
+ const entryBlock: MIRBlock = {
527
+ id: 'entry',
528
+ instrs: [
529
+ { kind: 'const', dst: batchCountTemp, value: 0 },
530
+ ],
531
+ term: { kind: 'jump', target: cont.loopHeaderId ?? cont.blocks[0].id },
532
+ preds: [],
533
+ }
534
+ blocks.push(entryBlock)
535
+
536
+ // Clone and rewrite the loop blocks with promoted variable names
537
+ for (const block of cont.blocks) {
538
+ const rewritten = rewriteBlock(block, promoted)
539
+ const succs = getSuccessors(rewritten.term)
540
+ const isBackEdgeBlock = cont.loopHeaderId && succs.includes(cont.loopHeaderId)
541
+
542
+ if (isBackEdgeBlock) {
543
+ // This block has a back edge to the loop header.
544
+ // Append batch counting to the body, then branch:
545
+ // batch_done → yield (return), !batch_done → continue (jump → header)
546
+ //
547
+ // body_block → branch(batch_done) → yield_block | continue_block
548
+ // continue_block → jump → loop_header (uses 'jump', safe for multi-pred)
549
+ // yield_block → return
550
+
551
+ const continueBlockId = `${block.id}_continue`
552
+ const yieldBlockId = `${block.id}_yield`
553
+
554
+ // Append batch check instructions to the body block
555
+ const bodyInstrs = [
556
+ ...rewritten.instrs,
557
+ { kind: 'add' as const, dst: batchCountTemp, a: { kind: 'temp' as const, name: batchCountTemp }, b: { kind: 'const' as const, value: 1 } },
558
+ { kind: 'cmp' as const, dst: batchCmpTemp, op: 'ge' as const, a: { kind: 'temp' as const, name: batchCountTemp }, b: { kind: 'const' as const, value: batch } },
559
+ ]
560
+
561
+ blocks.push({
562
+ ...rewritten,
563
+ instrs: bodyInstrs,
564
+ // Rewrite terminator: instead of jumping to header, branch on batch check
565
+ term: {
566
+ kind: 'branch',
567
+ cond: { kind: 'temp', name: batchCmpTemp },
568
+ then: yieldBlockId,
569
+ else: continueBlockId,
570
+ },
571
+ })
572
+
573
+ // Continue block: jump back to loop header (uses jump → multi-pred = call)
574
+ blocks.push({
575
+ id: continueBlockId,
576
+ instrs: [],
577
+ term: { kind: 'jump', target: cont.loopHeaderId! },
578
+ preds: [block.id],
579
+ })
580
+
581
+ // Yield block: return (resume next tick)
582
+ blocks.push({
583
+ id: yieldBlockId,
584
+ instrs: [],
585
+ term: { kind: 'return', value: null },
586
+ preds: [block.id],
587
+ })
588
+ } else {
589
+ // Check if this block exits the loop
590
+ const exitSuccs = succs.filter(s => !cont.blocks.some(b => b.id === s))
591
+ if (exitSuccs.length > 0) {
592
+ // Redirect exit branch to an exit block that returns
593
+ const exitBlockId = `${block.id}_exit`
594
+ const exitBlock: MIRBlock = {
595
+ id: exitBlockId,
596
+ instrs: [],
597
+ term: { kind: 'return', value: null },
598
+ preds: [block.id],
599
+ }
600
+
601
+ if (rewritten.term.kind === 'branch') {
602
+ const branchTerm = rewritten.term
603
+ const thenInLoop = cont.blocks.some(b => b.id === branchTerm.then)
604
+ const elseInLoop = cont.blocks.some(b => b.id === branchTerm.else)
605
+
606
+ if (!thenInLoop && elseInLoop) {
607
+ blocks.push({
608
+ ...rewritten,
609
+ term: { ...branchTerm, then: exitBlockId },
610
+ })
611
+ } else if (thenInLoop && !elseInLoop) {
612
+ blocks.push({
613
+ ...rewritten,
614
+ term: { ...branchTerm, else: exitBlockId },
615
+ })
616
+ } else {
617
+ blocks.push(rewritten)
618
+ }
619
+ } else {
620
+ blocks.push(rewritten)
621
+ }
622
+ blocks.push(exitBlock)
623
+ } else {
624
+ blocks.push(rewritten)
625
+ }
626
+ }
627
+ }
628
+
629
+ // Deduplicate blocks by ID
630
+ const seenIds = new Set<BlockId>()
631
+ const dedupBlocks: MIRBlock[] = []
632
+ for (const b of blocks) {
633
+ if (!seenIds.has(b.id)) {
634
+ seenIds.add(b.id)
635
+ dedupBlocks.push(b)
636
+ }
637
+ }
638
+
639
+ return {
640
+ name,
641
+ params: [],
642
+ blocks: dedupBlocks,
643
+ entry: 'entry',
644
+ isMacro: false,
645
+ }
646
+ }
647
+
648
+ function buildPostLoopContinuation(
649
+ name: string,
650
+ cont: Continuation,
651
+ contId: number,
652
+ promoted: Map<Temp, Temp>,
653
+ pcTemp: Temp,
654
+ objective: string,
655
+ onDone: string | undefined,
656
+ ): MIRFunction {
657
+ const blocks: MIRBlock[] = []
658
+
659
+ // Rewrite blocks with promoted variables
660
+ for (const block of cont.blocks) {
661
+ blocks.push(rewriteBlock(block, promoted))
662
+ }
663
+
664
+ // The entry is the first block in the continuation
665
+ const entry = cont.blocks[0]?.id ?? 'entry'
666
+
667
+ // Add onDone call if this is the last continuation
668
+ if (onDone) {
669
+ // Find blocks with return terminators and add onDone call before them
670
+ for (let i = 0; i < blocks.length; i++) {
671
+ if (blocks[i].term.kind === 'return') {
672
+ blocks[i] = {
673
+ ...blocks[i],
674
+ instrs: [
675
+ ...blocks[i].instrs,
676
+ { kind: 'call', dst: null, fn: onDone, args: [] },
677
+ ],
678
+ }
679
+ }
680
+ }
681
+ }
682
+
683
+ return {
684
+ name,
685
+ params: [],
686
+ blocks,
687
+ entry,
688
+ isMacro: false,
689
+ }
690
+ }
691
+
692
+ // ---------------------------------------------------------------------------
693
+ // Step 5: Build init function (replaces original)
694
+ // ---------------------------------------------------------------------------
695
+
696
+ function buildInitFunction(
697
+ originalFn: MIRFunction,
698
+ promoted: Map<Temp, Temp>,
699
+ pcTemp: Temp,
700
+ prefix: string,
701
+ objective: string,
702
+ ): MIRFunction {
703
+ // The init function:
704
+ // 1. Sets pc = 1 (start from continuation 1)
705
+ // 2. Initializes promoted variables from the entry block's pre-loop code
706
+
707
+ const instrs: MIRInstr[] = []
708
+
709
+ // Set pc = 1
710
+ instrs.push({ kind: 'const', dst: pcTemp, value: 1 })
711
+
712
+ // Initialize promoted variables from entry block instructions
713
+ // Walk the entry block and copy any const/copy instructions for promoted vars
714
+ const entryBlock = originalFn.blocks.find(b => b.id === originalFn.entry)
715
+ if (entryBlock) {
716
+ for (const instr of entryBlock.instrs) {
717
+ const dst = getDst(instr)
718
+ if (dst && promoted.has(dst)) {
719
+ // Rewrite to use promoted name
720
+ instrs.push(rewriteInstr(instr, promoted))
721
+ }
722
+ }
723
+ }
724
+
725
+ const block: MIRBlock = {
726
+ id: 'entry',
727
+ instrs,
728
+ term: { kind: 'return', value: null },
729
+ preds: [],
730
+ }
731
+
732
+ return {
733
+ name: originalFn.name,
734
+ params: originalFn.params,
735
+ blocks: [block],
736
+ entry: 'entry',
737
+ isMacro: originalFn.isMacro,
738
+ }
739
+ }
740
+
741
+ // ---------------------------------------------------------------------------
742
+ // Step 6: Build dispatcher
743
+ // ---------------------------------------------------------------------------
744
+
745
+ function buildDispatcher(
746
+ name: string,
747
+ continuations: MIRFunction[],
748
+ pcTemp: Temp,
749
+ objective: string,
750
+ originalFnName: string,
751
+ ): MIRFunction {
752
+ // Generates a dispatcher function that checks pc and calls the right continuation.
753
+ // For each continuation i (1-indexed):
754
+ // execute if score $coro_pc __ns matches i run function ns:_coro_cont_i
755
+ //
756
+ // We model this as a chain of branches in MIR.
757
+
758
+ const blocks: MIRBlock[] = []
759
+
760
+ if (continuations.length === 0) {
761
+ // No continuations — just return
762
+ blocks.push({
763
+ id: 'entry',
764
+ instrs: [],
765
+ term: { kind: 'return', value: null },
766
+ preds: [],
767
+ })
768
+ } else {
769
+ // Build a chain: check pc==1 → call cont_1, else check pc==2 → call cont_2, ...
770
+ for (let i = 0; i < continuations.length; i++) {
771
+ const contFn = continuations[i]
772
+ const blockId = i === 0 ? 'entry' : `check_${i + 1}`
773
+ const cmpTemp = `${name}_cmp_${i + 1}`
774
+ const nextBlock = i < continuations.length - 1 ? `check_${i + 2}` : 'done'
775
+ const callBlock = `call_${i + 1}`
776
+
777
+ blocks.push({
778
+ id: blockId,
779
+ instrs: [
780
+ { kind: 'cmp', dst: cmpTemp, op: 'eq', a: { kind: 'temp', name: pcTemp }, b: { kind: 'const', value: i + 1 } },
781
+ ],
782
+ term: { kind: 'branch', cond: { kind: 'temp', name: cmpTemp }, then: callBlock, else: nextBlock },
783
+ preds: i === 0 ? [] : [`check_${i}`],
784
+ })
785
+
786
+ blocks.push({
787
+ id: callBlock,
788
+ instrs: [
789
+ { kind: 'call', dst: null, fn: contFn.name, args: [] },
790
+ ],
791
+ term: { kind: 'return', value: null },
792
+ preds: [blockId],
793
+ })
794
+ }
795
+
796
+ // Done block (pc doesn't match any continuation — coroutine finished)
797
+ blocks.push({
798
+ id: 'done',
799
+ instrs: [],
800
+ term: { kind: 'return', value: null },
801
+ preds: [continuations.length === 1 ? 'entry' : `check_${continuations.length}`],
802
+ })
803
+ }
804
+
805
+ return {
806
+ name,
807
+ params: [],
808
+ blocks,
809
+ entry: 'entry',
810
+ isMacro: false,
811
+ }
812
+ }
813
+
814
+ // ---------------------------------------------------------------------------
815
+ // Single-continuation fallback (no loops)
816
+ // ---------------------------------------------------------------------------
817
+
818
+ function buildSingleContinuation(
819
+ fn: MIRFunction,
820
+ info: CoroutineInfo,
821
+ prefix: string,
822
+ pcTemp: Temp,
823
+ objective: string,
824
+ ): TransformResult {
825
+ // If there are no loops, the entire function body runs in one tick.
826
+ // We still wrap it in the coroutine pattern for consistency (init → cont_1 → done).
827
+
828
+ const contName = `${prefix}_cont_1`
829
+
830
+ // Continuation = entire original function body, plus set pc=-1 at the end
831
+ const contBlocks = fn.blocks.map(block => {
832
+ if (block.term.kind === 'return') {
833
+ const instrs = [...block.instrs]
834
+ if (info.onDone) {
835
+ instrs.push({ kind: 'call', dst: null, fn: info.onDone, args: [] })
836
+ }
837
+ return { ...block, instrs }
838
+ }
839
+ return block
840
+ })
841
+
842
+ const contFn: MIRFunction = {
843
+ name: contName,
844
+ params: [],
845
+ blocks: contBlocks,
846
+ entry: fn.entry,
847
+ isMacro: false,
848
+ }
849
+
850
+ // Init: set pc = 1
851
+ const initBlock: MIRBlock = {
852
+ id: 'entry',
853
+ instrs: [
854
+ { kind: 'const', dst: pcTemp, value: 1 },
855
+ ],
856
+ term: { kind: 'return', value: null },
857
+ preds: [],
858
+ }
859
+ const initFn: MIRFunction = {
860
+ name: fn.name,
861
+ params: fn.params,
862
+ blocks: [initBlock],
863
+ entry: 'entry',
864
+ isMacro: fn.isMacro,
865
+ }
866
+
867
+ const dispatcher = buildDispatcher(
868
+ `${prefix}_tick`,
869
+ [contFn],
870
+ pcTemp,
871
+ objective,
872
+ fn.name,
873
+ )
874
+
875
+ return { initFn, continuations: [contFn], dispatcher }
876
+ }
877
+
878
+ // ---------------------------------------------------------------------------
879
+ // Helpers: instruction rewriting
880
+ // ---------------------------------------------------------------------------
881
+
882
+ function rewriteBlock(block: MIRBlock, promoted: Map<Temp, Temp>): MIRBlock {
883
+ return {
884
+ ...block,
885
+ instrs: block.instrs.map(i => rewriteInstr(i, promoted)),
886
+ term: rewriteInstr(block.term, promoted) as MIRInstr,
887
+ }
888
+ }
889
+
890
+ function rewriteInstr(instr: MIRInstr, promoted: Map<Temp, Temp>): MIRInstr {
891
+ // Deep-rewrite all temp references through the promoted map
892
+ const rTemp = (t: Temp): Temp => promoted.get(t) ?? t
893
+ const rOp = (op: Operand): Operand =>
894
+ op.kind === 'temp' ? { kind: 'temp', name: rTemp(op.name) } : op
895
+
896
+ switch (instr.kind) {
897
+ case 'const':
898
+ return { ...instr, dst: rTemp(instr.dst) }
899
+ case 'copy':
900
+ return { ...instr, dst: rTemp(instr.dst), src: rOp(instr.src) }
901
+ case 'add': case 'sub': case 'mul': case 'div': case 'mod':
902
+ return { ...instr, dst: rTemp(instr.dst), a: rOp(instr.a), b: rOp(instr.b) }
903
+ case 'neg':
904
+ return { ...instr, dst: rTemp(instr.dst), src: rOp(instr.src) }
905
+ case 'cmp':
906
+ return { ...instr, dst: rTemp(instr.dst), a: rOp(instr.a), b: rOp(instr.b) }
907
+ case 'and': case 'or':
908
+ return { ...instr, dst: rTemp(instr.dst), a: rOp(instr.a), b: rOp(instr.b) }
909
+ case 'not':
910
+ return { ...instr, dst: rTemp(instr.dst), src: rOp(instr.src) }
911
+ case 'nbt_read':
912
+ return { ...instr, dst: rTemp(instr.dst) }
913
+ case 'nbt_write':
914
+ return { ...instr, src: rOp(instr.src) }
915
+ case 'call':
916
+ return { ...instr, dst: instr.dst ? rTemp(instr.dst) : null, args: instr.args.map(rOp) }
917
+ case 'call_macro':
918
+ return { ...instr, dst: instr.dst ? rTemp(instr.dst) : null, args: instr.args.map(a => ({ ...a, value: rOp(a.value) })) }
919
+ case 'call_context':
920
+ return instr
921
+ case 'branch':
922
+ return { ...instr, cond: rOp(instr.cond) }
923
+ case 'return':
924
+ return { ...instr, value: instr.value ? rOp(instr.value) : null }
925
+ case 'jump':
926
+ return instr
927
+ default:
928
+ return instr
929
+ }
930
+ }
931
+
932
+ function rewriteTerminator(term: MIRInstr, from: BlockId, to: BlockId): MIRInstr {
933
+ switch (term.kind) {
934
+ case 'jump':
935
+ return term.target === from ? { ...term, target: to } : term
936
+ case 'branch':
937
+ return {
938
+ ...term,
939
+ then: term.then === from ? to : term.then,
940
+ else: term.else === from ? to : term.else,
941
+ }
942
+ default:
943
+ return term
944
+ }
945
+ }
946
+
947
+ // ---------------------------------------------------------------------------
948
+ // Helpers: MIR instruction queries
949
+ // ---------------------------------------------------------------------------
950
+
951
+ function getSuccessors(term: MIRInstr): BlockId[] {
952
+ switch (term.kind) {
953
+ case 'jump': return [term.target]
954
+ case 'branch': return [term.then, term.else]
955
+ default: return []
956
+ }
957
+ }
958
+
959
+ function getDst(instr: MIRInstr): Temp | null {
960
+ switch (instr.kind) {
961
+ case 'const': case 'copy':
962
+ case 'add': case 'sub': case 'mul': case 'div': case 'mod':
963
+ case 'neg': case 'cmp':
964
+ case 'and': case 'or': case 'not':
965
+ case 'nbt_read':
966
+ return instr.dst
967
+ case 'call': case 'call_macro':
968
+ return instr.dst
969
+ default:
970
+ return null
971
+ }
972
+ }
973
+
974
+ function getUsedTemps(instr: MIRInstr): Temp[] {
975
+ const temps: Temp[] = []
976
+ const addOp = (op: Operand) => { if (op.kind === 'temp') temps.push(op.name) }
977
+
978
+ switch (instr.kind) {
979
+ case 'copy': case 'neg': case 'not':
980
+ addOp(instr.src); break
981
+ case 'add': case 'sub': case 'mul': case 'div': case 'mod':
982
+ case 'cmp': case 'and': case 'or':
983
+ addOp(instr.a); addOp(instr.b); break
984
+ case 'nbt_write':
985
+ addOp(instr.src); break
986
+ case 'call':
987
+ instr.args.forEach(addOp); break
988
+ case 'call_macro':
989
+ instr.args.forEach(a => addOp(a.value)); break
990
+ case 'branch':
991
+ addOp(instr.cond); break
992
+ case 'return':
993
+ if (instr.value) addOp(instr.value); break
994
+ }
995
+ return temps
996
+ }