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.
- package/.claudeignore +21 -0
- package/.github/workflows/ci.yml +1 -0
- package/README.md +12 -16
- package/README.zh.md +2 -2
- package/demo.gif +0 -0
- package/dist/cli.js +2 -554
- package/dist/compile.js +2 -266
- package/dist/index.js +2 -159
- package/dist/src/__tests__/budget.test.js +261 -0
- package/dist/src/__tests__/cli.test.js +104 -0
- package/dist/{__tests__ → src/__tests__}/dce.test.js +11 -47
- package/dist/{__tests__ → src/__tests__}/diagnostics.test.js +67 -40
- package/dist/src/__tests__/e2e/basic.test.d.ts +8 -0
- package/dist/src/__tests__/e2e/basic.test.js +140 -0
- package/dist/src/__tests__/e2e/coroutine.test.d.ts +7 -0
- package/dist/src/__tests__/e2e/coroutine.test.js +132 -0
- package/dist/src/__tests__/e2e/macros.test.d.ts +9 -0
- package/dist/src/__tests__/e2e/macros.test.js +182 -0
- package/dist/src/__tests__/e2e/migrate.test.d.ts +13 -0
- package/dist/src/__tests__/e2e/migrate.test.js +2739 -0
- package/dist/src/__tests__/e2e/stdlib-e2e.test.d.ts +10 -0
- package/dist/src/__tests__/e2e/stdlib-e2e.test.js +324 -0
- package/dist/src/__tests__/enum.test.d.ts +10 -0
- package/dist/src/__tests__/enum.test.js +389 -0
- package/dist/src/__tests__/generics.test.d.ts +14 -0
- package/dist/src/__tests__/generics.test.js +367 -0
- package/dist/src/__tests__/hir/desugar.test.js +234 -0
- package/dist/src/__tests__/incremental.test.d.ts +5 -0
- package/dist/src/__tests__/incremental.test.js +308 -0
- package/dist/src/__tests__/lir/lower.test.js +559 -0
- package/dist/src/__tests__/lir/types.test.js +185 -0
- package/dist/src/__tests__/lir/verify.test.js +221 -0
- package/dist/src/__tests__/lsp.test.d.ts +7 -0
- package/dist/src/__tests__/lsp.test.js +245 -0
- package/dist/{__tests__ → src/__tests__}/mc-integration.test.js +1 -3
- package/dist/src/__tests__/mc-version.test.d.ts +10 -0
- package/dist/src/__tests__/mc-version.test.js +154 -0
- package/dist/src/__tests__/mir/arithmetic.test.js +130 -0
- package/dist/src/__tests__/mir/control-flow.test.js +205 -0
- package/dist/src/__tests__/mir/verify.test.js +223 -0
- package/dist/src/__tests__/modules.test.d.ts +7 -0
- package/dist/src/__tests__/modules.test.js +333 -0
- package/dist/src/__tests__/optimizer/block_merge.test.js +78 -0
- package/dist/src/__tests__/optimizer/branch_simplify.test.js +58 -0
- package/dist/src/__tests__/optimizer/constant_fold.test.js +131 -0
- package/dist/src/__tests__/optimizer/copy_prop.test.js +91 -0
- package/dist/src/__tests__/optimizer/coroutine.test.d.ts +12 -0
- package/dist/src/__tests__/optimizer/coroutine.test.js +251 -0
- package/dist/src/__tests__/optimizer/dce.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/dce.test.js +76 -0
- package/dist/src/__tests__/optimizer/interprocedural.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/interprocedural.test.js +145 -0
- package/dist/src/__tests__/optimizer/lir/const_imm.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/lir/const_imm.test.js +138 -0
- package/dist/src/__tests__/optimizer/lir/dead_slot.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/lir/dead_slot.test.js +141 -0
- package/dist/src/__tests__/optimizer/lir/peephole.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/lir/peephole.test.js +126 -0
- package/dist/src/__tests__/optimizer/lir/pipeline.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/lir/pipeline.test.js +84 -0
- package/dist/src/__tests__/optimizer/nbt-batch.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/nbt-batch.test.js +110 -0
- package/dist/src/__tests__/optimizer/pipeline.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/pipeline.test.js +102 -0
- package/dist/src/__tests__/optimizer/selector-cache.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/selector-cache.test.js +103 -0
- package/dist/src/__tests__/optimizer/unroll.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/unroll.test.js +206 -0
- package/dist/src/__tests__/option.test.d.ts +14 -0
- package/dist/src/__tests__/option.test.js +275 -0
- package/dist/src/__tests__/parser.test.d.ts +1 -0
- package/dist/src/__tests__/repl.test.d.ts +1 -0
- package/dist/src/__tests__/schedule.test.d.ts +7 -0
- package/dist/src/__tests__/schedule.test.js +98 -0
- package/dist/src/__tests__/sourcemap.test.d.ts +7 -0
- package/dist/src/__tests__/sourcemap.test.js +227 -0
- package/dist/src/__tests__/tuple.test.d.ts +11 -0
- package/dist/src/__tests__/tuple.test.js +202 -0
- package/dist/src/__tests__/typechecker-strict.test.d.ts +10 -0
- package/dist/src/__tests__/typechecker-strict.test.js +197 -0
- package/dist/src/__tests__/typechecker.test.d.ts +1 -0
- package/dist/{ast → src/ast}/types.d.ts +58 -3
- package/dist/src/cache/deps.d.ts +41 -0
- package/dist/src/cache/deps.js +158 -0
- package/dist/src/cache/incremental.d.ts +35 -0
- package/dist/src/cache/incremental.js +165 -0
- package/dist/src/cache/index.d.ts +37 -0
- package/dist/src/cache/index.js +152 -0
- package/dist/{cli.d.ts → src/cli.d.ts} +1 -1
- package/dist/src/cli.js +474 -0
- package/dist/src/compile.d.ts +37 -0
- package/dist/src/compile.js +165 -0
- package/dist/{diagnostics → src/diagnostics}/index.d.ts +1 -1
- package/dist/{diagnostics → src/diagnostics}/index.js +8 -11
- package/dist/src/emit/compile.d.ts +29 -0
- package/dist/src/emit/compile.js +143 -0
- package/dist/src/emit/index.d.ts +26 -0
- package/dist/src/emit/index.js +223 -0
- package/dist/src/emit/modules.d.ts +29 -0
- package/dist/src/emit/modules.js +492 -0
- package/dist/src/emit/sourcemap.d.ts +53 -0
- package/dist/src/emit/sourcemap.js +73 -0
- package/dist/src/hir/lower.d.ts +15 -0
- package/dist/src/hir/lower.js +399 -0
- package/dist/src/hir/monomorphize.d.ts +22 -0
- package/dist/src/hir/monomorphize.js +379 -0
- package/dist/src/hir/types.d.ts +406 -0
- package/dist/src/hir/types.js +16 -0
- package/dist/src/index.d.ts +39 -0
- package/dist/src/index.js +67 -0
- package/dist/{lexer → src/lexer}/index.d.ts +1 -1
- package/dist/{lexer → src/lexer}/index.js +1 -0
- package/dist/src/lir/budget.d.ts +37 -0
- package/dist/src/lir/budget.js +280 -0
- package/dist/src/lir/lower.d.ts +15 -0
- package/dist/src/lir/lower.js +472 -0
- package/dist/src/lir/types.d.ts +139 -0
- package/dist/src/lir/types.js +11 -0
- package/dist/src/lir/verify.d.ts +14 -0
- package/dist/src/lir/verify.js +113 -0
- package/dist/src/lsp/main.d.ts +8 -0
- package/dist/src/lsp/main.js +11 -0
- package/dist/src/lsp/server.d.ts +11 -0
- package/dist/src/lsp/server.js +352 -0
- package/dist/{mc-test → src/mc-test}/runner.js +4 -3
- package/dist/src/mir/lower.d.ts +9 -0
- package/dist/src/mir/lower.js +1264 -0
- package/dist/src/mir/macro.d.ts +22 -0
- package/dist/src/mir/macro.js +168 -0
- package/dist/src/mir/types.d.ts +191 -0
- package/dist/src/mir/types.js +11 -0
- package/dist/src/mir/verify.d.ts +16 -0
- package/dist/src/mir/verify.js +216 -0
- package/dist/src/optimizer/block_merge.d.ts +12 -0
- package/dist/src/optimizer/block_merge.js +84 -0
- package/dist/src/optimizer/branch_simplify.d.ts +9 -0
- package/dist/src/optimizer/branch_simplify.js +28 -0
- package/dist/src/optimizer/constant_fold.d.ts +10 -0
- package/dist/src/optimizer/constant_fold.js +85 -0
- package/dist/src/optimizer/copy_prop.d.ts +9 -0
- package/dist/src/optimizer/copy_prop.js +113 -0
- package/dist/src/optimizer/coroutine.d.ts +34 -0
- package/dist/src/optimizer/coroutine.js +789 -0
- package/dist/src/optimizer/dce.d.ts +8 -0
- package/dist/src/optimizer/dce.js +156 -0
- package/dist/src/optimizer/interprocedural.d.ts +14 -0
- package/dist/src/optimizer/interprocedural.js +186 -0
- package/dist/src/optimizer/lir/const_imm.d.ts +12 -0
- package/dist/src/optimizer/lir/const_imm.js +139 -0
- package/dist/src/optimizer/lir/dead_slot.d.ts +14 -0
- package/dist/src/optimizer/lir/dead_slot.js +130 -0
- package/dist/src/optimizer/lir/peephole.d.ts +21 -0
- package/dist/src/optimizer/lir/peephole.js +52 -0
- package/dist/src/optimizer/lir/pipeline.d.ts +10 -0
- package/dist/src/optimizer/lir/pipeline.js +34 -0
- package/dist/src/optimizer/nbt-batch.d.ts +11 -0
- package/dist/src/optimizer/nbt-batch.js +51 -0
- package/dist/src/optimizer/pipeline.d.ts +14 -0
- package/dist/src/optimizer/pipeline.js +58 -0
- package/dist/src/optimizer/selector-cache.d.ts +22 -0
- package/dist/src/optimizer/selector-cache.js +100 -0
- package/dist/src/optimizer/unroll.d.ts +32 -0
- package/dist/src/optimizer/unroll.js +348 -0
- package/dist/{parser → src/parser}/index.d.ts +8 -0
- package/dist/{parser → src/parser}/index.js +204 -14
- package/dist/{repl.d.ts → src/repl.d.ts} +1 -1
- package/dist/{runtime → src/runtime}/index.js +1 -1
- package/dist/{typechecker → src/typechecker}/index.d.ts +4 -0
- package/dist/{typechecker → src/typechecker}/index.js +198 -13
- package/dist/src/types/mc-version.d.ts +24 -0
- package/dist/src/types/mc-version.js +49 -0
- package/docs/ROADMAP.md +395 -0
- package/docs/compiler-pipeline-redesign.md +2260 -0
- package/docs/optimization-ideas.md +1076 -0
- package/editors/vscode/out/extension.js +25176 -8000
- package/editors/vscode/package-lock.json +90 -6
- package/editors/vscode/package.json +3 -2
- package/editors/vscode/src/extension.ts +97 -67
- package/examples/showcase.mcrs +3 -3
- package/package.json +13 -6
- package/scripts/postbuild.js +15 -0
- package/src/__tests__/budget.test.ts +297 -0
- package/src/__tests__/cli.test.ts +8 -220
- package/src/__tests__/dce.test.ts +11 -56
- package/src/__tests__/diagnostics.test.ts +61 -41
- package/src/__tests__/e2e/basic.test.ts +154 -0
- package/src/__tests__/e2e/coroutine.test.ts +142 -0
- package/src/__tests__/e2e/macros.test.ts +199 -0
- package/src/__tests__/e2e/migrate.test.ts +3008 -0
- package/src/__tests__/e2e/stdlib-e2e.test.ts +348 -0
- package/src/__tests__/enum.test.ts +425 -0
- package/src/__tests__/generics.test.ts +390 -0
- package/src/__tests__/hir/desugar.test.ts +263 -0
- package/src/__tests__/incremental.test.ts +337 -0
- package/src/__tests__/lir/lower.test.ts +619 -0
- package/src/__tests__/lir/types.test.ts +207 -0
- package/src/__tests__/lir/verify.test.ts +249 -0
- package/src/__tests__/lsp.test.ts +270 -0
- package/src/__tests__/mc-integration.test.ts +1 -2
- package/src/__tests__/mc-version.test.ts +178 -0
- package/src/__tests__/mir/arithmetic.test.ts +156 -0
- package/src/__tests__/mir/control-flow.test.ts +242 -0
- package/src/__tests__/mir/verify.test.ts +254 -0
- package/src/__tests__/modules.test.ts +365 -0
- package/src/__tests__/optimizer/block_merge.test.ts +84 -0
- package/src/__tests__/optimizer/branch_simplify.test.ts +64 -0
- package/src/__tests__/optimizer/constant_fold.test.ts +145 -0
- package/src/__tests__/optimizer/copy_prop.test.ts +99 -0
- package/src/__tests__/optimizer/coroutine.test.ts +312 -0
- package/src/__tests__/optimizer/dce.test.ts +83 -0
- package/src/__tests__/optimizer/interprocedural.test.ts +174 -0
- package/src/__tests__/optimizer/lir/const_imm.test.ts +151 -0
- package/src/__tests__/optimizer/lir/dead_slot.test.ts +156 -0
- package/src/__tests__/optimizer/lir/peephole.test.ts +136 -0
- package/src/__tests__/optimizer/lir/pipeline.test.ts +113 -0
- package/src/__tests__/optimizer/nbt-batch.test.ts +119 -0
- package/src/__tests__/optimizer/pipeline.test.ts +116 -0
- package/src/__tests__/optimizer/selector-cache.test.ts +112 -0
- package/src/__tests__/optimizer/unroll.test.ts +231 -0
- package/src/__tests__/option.test.ts +299 -0
- package/src/__tests__/schedule.test.ts +105 -0
- package/src/__tests__/sourcemap.test.ts +254 -0
- package/src/__tests__/tuple.test.ts +220 -0
- package/src/__tests__/typechecker-strict.test.ts +216 -0
- package/src/ast/types.ts +39 -3
- package/src/cache/deps.ts +132 -0
- package/src/cache/incremental.ts +173 -0
- package/src/cache/index.ts +135 -0
- package/src/cli.ts +111 -195
- package/src/compile.ts +6 -162
- package/src/diagnostics/index.ts +8 -11
- package/src/emit/compile.ts +177 -0
- package/src/emit/index.ts +286 -0
- package/src/emit/modules.ts +581 -0
- package/src/emit/sourcemap.ts +101 -0
- package/src/hir/lower.ts +455 -0
- package/src/hir/monomorphize.ts +416 -0
- package/src/hir/types.ts +228 -0
- package/src/index.ts +37 -182
- package/src/lexer/index.ts +2 -1
- package/src/lir/budget.ts +321 -0
- package/src/lir/lower.ts +587 -0
- package/src/lir/types.ts +113 -0
- package/src/lir/verify.ts +129 -0
- package/src/lsp/main.ts +9 -0
- package/src/lsp/server.ts +414 -0
- package/src/mc-test/runner.ts +4 -3
- package/src/mir/lower.ts +1403 -0
- package/src/mir/macro.ts +167 -0
- package/src/mir/types.ts +117 -0
- package/src/mir/verify.ts +218 -0
- package/src/optimizer/block_merge.ts +93 -0
- package/src/optimizer/branch_simplify.ts +27 -0
- package/src/optimizer/constant_fold.ts +88 -0
- package/src/optimizer/copy_prop.ts +106 -0
- package/src/optimizer/coroutine.ts +996 -0
- package/src/optimizer/dce.ts +108 -653
- package/src/optimizer/interprocedural.ts +177 -0
- package/src/optimizer/lir/const_imm.ts +143 -0
- package/src/optimizer/lir/dead_slot.ts +123 -0
- package/src/optimizer/lir/peephole.ts +57 -0
- package/src/optimizer/lir/pipeline.ts +37 -0
- package/src/optimizer/nbt-batch.ts +50 -0
- package/src/optimizer/pipeline.ts +59 -0
- package/src/optimizer/selector-cache.ts +103 -0
- package/src/optimizer/unroll.ts +386 -0
- package/src/parser/index.ts +213 -16
- package/src/repl.ts +1 -1
- package/src/runtime/index.ts +1 -1
- package/src/stdlib/math.mcrs +4 -4
- package/src/templates/quest.mcrs +4 -4
- package/src/typechecker/index.ts +215 -15
- package/src/types/mc-version.ts +46 -0
- package/tsconfig.json +1 -1
- package/dist/__tests__/cli.test.js +0 -278
- package/dist/__tests__/codegen.test.js +0 -152
- package/dist/__tests__/e2e.test.d.ts +0 -6
- package/dist/__tests__/e2e.test.js +0 -1847
- package/dist/__tests__/entity-types.test.js +0 -203
- package/dist/__tests__/lowering.test.js +0 -1015
- package/dist/__tests__/macro.test.d.ts +0 -8
- package/dist/__tests__/macro.test.js +0 -305
- package/dist/__tests__/nbt.test.js +0 -82
- package/dist/__tests__/optimizer-advanced.test.js +0 -124
- package/dist/__tests__/optimizer.test.js +0 -149
- package/dist/__tests__/runtime.test.js +0 -289
- package/dist/__tests__/stdlib-advanced.test.d.ts +0 -4
- package/dist/__tests__/stdlib-advanced.test.js +0 -378
- package/dist/__tests__/stdlib-bigint.test.d.ts +0 -7
- package/dist/__tests__/stdlib-bigint.test.js +0 -428
- package/dist/__tests__/stdlib-math.test.d.ts +0 -7
- package/dist/__tests__/stdlib-math.test.js +0 -352
- package/dist/__tests__/stdlib-vec.test.d.ts +0 -4
- package/dist/__tests__/stdlib-vec.test.js +0 -264
- package/dist/__tests__/structure-optimizer.test.js +0 -33
- package/dist/__tests__/var-allocator.test.js +0 -69
- package/dist/codegen/cmdblock/index.d.ts +0 -26
- package/dist/codegen/cmdblock/index.js +0 -45
- package/dist/codegen/mcfunction/index.d.ts +0 -40
- package/dist/codegen/mcfunction/index.js +0 -606
- package/dist/codegen/structure/index.d.ts +0 -24
- package/dist/codegen/structure/index.js +0 -279
- package/dist/codegen/var-allocator.d.ts +0 -45
- package/dist/codegen/var-allocator.js +0 -104
- package/dist/compile.d.ts +0 -68
- package/dist/data/arena/function/__load.mcfunction +0 -6
- package/dist/data/arena/function/__tick.mcfunction +0 -2
- package/dist/data/arena/function/announce_leaders/else_1.mcfunction +0 -3
- package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +0 -1
- package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +0 -3
- package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +0 -7
- package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +0 -1
- package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +0 -4
- package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +0 -6
- package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +0 -1
- package/dist/data/arena/function/announce_leaders/then_0.mcfunction +0 -4
- package/dist/data/arena/function/announce_leaders.mcfunction +0 -6
- package/dist/data/arena/function/arena_tick/merge_2.mcfunction +0 -1
- package/dist/data/arena/function/arena_tick/then_0.mcfunction +0 -4
- package/dist/data/arena/function/arena_tick.mcfunction +0 -11
- package/dist/data/counter/function/__load.mcfunction +0 -5
- package/dist/data/counter/function/__tick.mcfunction +0 -2
- package/dist/data/counter/function/counter_tick/merge_2.mcfunction +0 -1
- package/dist/data/counter/function/counter_tick/then_0.mcfunction +0 -3
- package/dist/data/counter/function/counter_tick.mcfunction +0 -11
- package/dist/data/gcd2/function/__load.mcfunction +0 -3
- package/dist/data/gcd2/function/abs/merge_2.mcfunction +0 -3
- package/dist/data/gcd2/function/abs/then_0.mcfunction +0 -5
- package/dist/data/gcd2/function/abs.mcfunction +0 -7
- package/dist/data/gcd2/function/gcd/loop_body_1.mcfunction +0 -7
- package/dist/data/gcd2/function/gcd/loop_check_0.mcfunction +0 -5
- package/dist/data/gcd2/function/gcd/loop_exit_2.mcfunction +0 -3
- package/dist/data/gcd2/function/gcd.mcfunction +0 -14
- package/dist/data/gcd3/function/__load.mcfunction +0 -3
- package/dist/data/gcd3/function/abs/merge_2.mcfunction +0 -3
- package/dist/data/gcd3/function/abs/then_0.mcfunction +0 -5
- package/dist/data/gcd3/function/abs.mcfunction +0 -7
- package/dist/data/gcd3/function/gcd/loop_body_1.mcfunction +0 -7
- package/dist/data/gcd3/function/gcd/loop_check_0.mcfunction +0 -5
- package/dist/data/gcd3/function/gcd/loop_exit_2.mcfunction +0 -3
- package/dist/data/gcd3/function/gcd.mcfunction +0 -14
- package/dist/data/gcd3/function/test.mcfunction +0 -7
- package/dist/data/gcd3nm/function/__load.mcfunction +0 -3
- package/dist/data/gcd3nm/function/abs/merge_2.mcfunction +0 -3
- package/dist/data/gcd3nm/function/abs/then_0.mcfunction +0 -5
- package/dist/data/gcd3nm/function/abs.mcfunction +0 -7
- package/dist/data/gcd3nm/function/gcd/loop_body_1.mcfunction +0 -7
- package/dist/data/gcd3nm/function/gcd/loop_check_0.mcfunction +0 -5
- package/dist/data/gcd3nm/function/gcd/loop_exit_2.mcfunction +0 -3
- package/dist/data/gcd3nm/function/gcd.mcfunction +0 -14
- package/dist/data/gcd3nm/function/test.mcfunction +0 -7
- package/dist/data/gcd_test/function/__load.mcfunction +0 -3
- package/dist/data/gcd_test/function/abs/merge_2.mcfunction +0 -3
- package/dist/data/gcd_test/function/abs/then_0.mcfunction +0 -5
- package/dist/data/gcd_test/function/abs.mcfunction +0 -7
- package/dist/data/gcd_test/function/gcd/loop_body_1.mcfunction +0 -7
- package/dist/data/gcd_test/function/gcd/loop_check_0.mcfunction +0 -5
- package/dist/data/gcd_test/function/gcd/loop_exit_2.mcfunction +0 -3
- package/dist/data/gcd_test/function/gcd.mcfunction +0 -14
- package/dist/data/isqrttest/function/__load.mcfunction +0 -6
- package/dist/data/isqrttest/function/isqrt/loop_body_4.mcfunction +0 -12
- package/dist/data/isqrttest/function/isqrt/loop_check_3.mcfunction +0 -5
- package/dist/data/isqrttest/function/isqrt/loop_exit_5.mcfunction +0 -3
- package/dist/data/isqrttest/function/isqrt/merge_2.mcfunction +0 -4
- package/dist/data/isqrttest/function/isqrt/merge_8.mcfunction +0 -6
- package/dist/data/isqrttest/function/isqrt/then_0.mcfunction +0 -3
- package/dist/data/isqrttest/function/isqrt/then_6.mcfunction +0 -3
- package/dist/data/isqrttest/function/isqrt.mcfunction +0 -7
- package/dist/data/isqrttest/function/test.mcfunction +0 -6
- package/dist/data/mathtest/function/__load.mcfunction +0 -3
- package/dist/data/mathtest/function/abs/merge_2.mcfunction +0 -3
- package/dist/data/mathtest/function/abs/then_0.mcfunction +0 -5
- package/dist/data/mathtest/function/abs.mcfunction +0 -6
- package/dist/data/mathtest/function/test.mcfunction +0 -5
- package/dist/data/minecraft/tags/function/load.json +0 -5
- package/dist/data/minecraft/tags/function/tick.json +0 -5
- package/dist/data/mypack/function/__load.mcfunction +0 -13
- package/dist/data/mypack/function/_atan_init.mcfunction +0 -2
- package/dist/data/mypack/function/abs/merge_2.mcfunction +0 -3
- package/dist/data/mypack/function/abs/then_0.mcfunction +0 -5
- package/dist/data/mypack/function/abs.mcfunction +0 -6
- package/dist/data/mypack/function/atan2_fixed/__sgi_1.mcfunction +0 -2
- package/dist/data/mypack/function/atan2_fixed/else_34.mcfunction +0 -3
- package/dist/data/mypack/function/atan2_fixed/loop_body_31.mcfunction +0 -19
- package/dist/data/mypack/function/atan2_fixed/loop_check_30.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/loop_exit_32.mcfunction +0 -6
- package/dist/data/mypack/function/atan2_fixed/merge_11.mcfunction +0 -6
- package/dist/data/mypack/function/atan2_fixed/merge_14.mcfunction +0 -3
- package/dist/data/mypack/function/atan2_fixed/merge_17.mcfunction +0 -6
- package/dist/data/mypack/function/atan2_fixed/merge_2.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/merge_20.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/merge_23.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/merge_26.mcfunction +0 -6
- package/dist/data/mypack/function/atan2_fixed/merge_29.mcfunction +0 -4
- package/dist/data/mypack/function/atan2_fixed/merge_38.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/merge_41.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/merge_44.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/merge_47.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/merge_5.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/merge_8.mcfunction +0 -3
- package/dist/data/mypack/function/atan2_fixed/then_0.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/then_12.mcfunction +0 -3
- package/dist/data/mypack/function/atan2_fixed/then_15.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/then_18.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/then_21.mcfunction +0 -3
- package/dist/data/mypack/function/atan2_fixed/then_24.mcfunction +0 -3
- package/dist/data/mypack/function/atan2_fixed/then_27.mcfunction +0 -6
- package/dist/data/mypack/function/atan2_fixed/then_3.mcfunction +0 -3
- package/dist/data/mypack/function/atan2_fixed/then_33.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/then_36.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/then_39.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/then_42.mcfunction +0 -3
- package/dist/data/mypack/function/atan2_fixed/then_45.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed/then_6.mcfunction +0 -3
- package/dist/data/mypack/function/atan2_fixed/then_9.mcfunction +0 -5
- package/dist/data/mypack/function/atan2_fixed.mcfunction +0 -7
- package/dist/data/mypack/function/my_game.mcfunction +0 -10
- package/dist/data/quiz/function/__load.mcfunction +0 -16
- package/dist/data/quiz/function/__tick.mcfunction +0 -6
- package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +0 -4
- package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +0 -4
- package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +0 -4
- package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +0 -4
- package/dist/data/quiz/function/answer_a.mcfunction +0 -4
- package/dist/data/quiz/function/answer_b.mcfunction +0 -4
- package/dist/data/quiz/function/answer_c.mcfunction +0 -4
- package/dist/data/quiz/function/ask_question/else_1.mcfunction +0 -5
- package/dist/data/quiz/function/ask_question/else_4.mcfunction +0 -5
- package/dist/data/quiz/function/ask_question/else_7.mcfunction +0 -4
- package/dist/data/quiz/function/ask_question/merge_2.mcfunction +0 -1
- package/dist/data/quiz/function/ask_question/merge_5.mcfunction +0 -2
- package/dist/data/quiz/function/ask_question/merge_8.mcfunction +0 -2
- package/dist/data/quiz/function/ask_question/then_0.mcfunction +0 -4
- package/dist/data/quiz/function/ask_question/then_3.mcfunction +0 -4
- package/dist/data/quiz/function/ask_question/then_6.mcfunction +0 -4
- package/dist/data/quiz/function/ask_question.mcfunction +0 -7
- package/dist/data/quiz/function/finish_quiz.mcfunction +0 -6
- package/dist/data/quiz/function/handle_answer/else_1.mcfunction +0 -5
- package/dist/data/quiz/function/handle_answer/else_10.mcfunction +0 -3
- package/dist/data/quiz/function/handle_answer/else_16.mcfunction +0 -3
- package/dist/data/quiz/function/handle_answer/else_4.mcfunction +0 -3
- package/dist/data/quiz/function/handle_answer/else_7.mcfunction +0 -5
- package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +0 -2
- package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +0 -2
- package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +0 -2
- package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +0 -8
- package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +0 -2
- package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +0 -2
- package/dist/data/quiz/function/handle_answer/then_0.mcfunction +0 -5
- package/dist/data/quiz/function/handle_answer/then_12.mcfunction +0 -5
- package/dist/data/quiz/function/handle_answer/then_15.mcfunction +0 -6
- package/dist/data/quiz/function/handle_answer/then_3.mcfunction +0 -6
- package/dist/data/quiz/function/handle_answer/then_6.mcfunction +0 -5
- package/dist/data/quiz/function/handle_answer/then_9.mcfunction +0 -6
- package/dist/data/quiz/function/handle_answer.mcfunction +0 -11
- package/dist/data/quiz/function/start_quiz.mcfunction +0 -5
- package/dist/data/reqtest/function/__load.mcfunction +0 -4
- package/dist/data/reqtest/function/_table_init.mcfunction +0 -2
- package/dist/data/reqtest/function/no_trig.mcfunction +0 -3
- package/dist/data/reqtest/function/use_table.mcfunction +0 -4
- package/dist/data/reqtest2/function/__load.mcfunction +0 -3
- package/dist/data/reqtest2/function/no_trig.mcfunction +0 -3
- package/dist/data/runtime/function/__load.mcfunction +0 -5
- package/dist/data/runtime/function/__tick.mcfunction +0 -2
- package/dist/data/runtime/function/counter_tick/then_0.mcfunction +0 -3
- package/dist/data/runtime/function/counter_tick.mcfunction +0 -13
- package/dist/data/shop/function/__load.mcfunction +0 -7
- package/dist/data/shop/function/__tick.mcfunction +0 -3
- package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +0 -4
- package/dist/data/shop/function/complete_purchase/else_1.mcfunction +0 -5
- package/dist/data/shop/function/complete_purchase/else_4.mcfunction +0 -5
- package/dist/data/shop/function/complete_purchase/else_7.mcfunction +0 -3
- package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +0 -2
- package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +0 -2
- package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +0 -2
- package/dist/data/shop/function/complete_purchase/then_0.mcfunction +0 -4
- package/dist/data/shop/function/complete_purchase/then_3.mcfunction +0 -4
- package/dist/data/shop/function/complete_purchase/then_6.mcfunction +0 -4
- package/dist/data/shop/function/complete_purchase.mcfunction +0 -7
- package/dist/data/shop/function/handle_shop_trigger.mcfunction +0 -3
- package/dist/data/swap_test/function/__load.mcfunction +0 -3
- package/dist/data/swap_test/function/gcd_old/loop_body_1.mcfunction +0 -7
- package/dist/data/swap_test/function/gcd_old/loop_check_0.mcfunction +0 -5
- package/dist/data/swap_test/function/gcd_old/loop_exit_2.mcfunction +0 -3
- package/dist/data/swap_test/function/gcd_old.mcfunction +0 -8
- package/dist/data/turret/function/__load.mcfunction +0 -5
- package/dist/data/turret/function/__tick.mcfunction +0 -4
- package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +0 -4
- package/dist/data/turret/function/deploy_turret.mcfunction +0 -8
- package/dist/data/turret/function/turret_tick/at_1.mcfunction +0 -2
- package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +0 -2
- package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +0 -2
- package/dist/data/turret/function/turret_tick/tick_body.mcfunction +0 -3
- package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +0 -1
- package/dist/data/turret/function/turret_tick.mcfunction +0 -5
- package/dist/gcd2.map.json +0 -15
- package/dist/gcd3.map.json +0 -17
- package/dist/gcd_test.map.json +0 -15
- package/dist/index.d.ts +0 -62
- package/dist/ir/builder.d.ts +0 -33
- package/dist/ir/builder.js +0 -99
- package/dist/ir/types.d.ts +0 -132
- package/dist/ir/types.js +0 -15
- package/dist/isqrttest.map.json +0 -15
- package/dist/lowering/index.d.ts +0 -188
- package/dist/lowering/index.js +0 -3403
- package/dist/mathtest.map.json +0 -6
- package/dist/mypack.map.json +0 -27
- package/dist/optimizer/commands.d.ts +0 -38
- package/dist/optimizer/commands.js +0 -451
- package/dist/optimizer/dce.d.ts +0 -34
- package/dist/optimizer/dce.js +0 -639
- package/dist/optimizer/passes.d.ts +0 -34
- package/dist/optimizer/passes.js +0 -243
- package/dist/optimizer/structure.d.ts +0 -9
- package/dist/optimizer/structure.js +0 -356
- package/dist/pack.mcmeta +0 -6
- package/dist/reqtest.map.json +0 -4
- package/dist/reqtest2.map.json +0 -4
- package/dist/runtime.map.json +0 -7
- package/dist/swap_test.map.json +0 -14
- package/src/__tests__/codegen.test.ts +0 -161
- package/src/__tests__/e2e.test.ts +0 -2039
- package/src/__tests__/entity-types.test.ts +0 -236
- package/src/__tests__/lowering.test.ts +0 -1185
- package/src/__tests__/macro.test.ts +0 -343
- package/src/__tests__/nbt.test.ts +0 -58
- package/src/__tests__/optimizer-advanced.test.ts +0 -144
- package/src/__tests__/optimizer.test.ts +0 -162
- package/src/__tests__/runtime.test.ts +0 -305
- package/src/__tests__/stdlib-advanced.test.ts +0 -379
- package/src/__tests__/stdlib-bigint.test.ts +0 -427
- package/src/__tests__/stdlib-math.test.ts +0 -374
- package/src/__tests__/stdlib-vec.test.ts +0 -259
- package/src/__tests__/structure-optimizer.test.ts +0 -38
- package/src/__tests__/var-allocator.test.ts +0 -75
- package/src/codegen/cmdblock/index.ts +0 -63
- package/src/codegen/mcfunction/index.ts +0 -662
- package/src/codegen/structure/index.ts +0 -346
- package/src/codegen/var-allocator.ts +0 -104
- package/src/ir/builder.ts +0 -116
- package/src/ir/types.ts +0 -134
- package/src/lowering/index.ts +0 -3876
- package/src/optimizer/commands.ts +0 -534
- package/src/optimizer/passes.ts +0 -250
- package/src/optimizer/structure.ts +0 -450
- /package/dist/{__tests__/cli.test.d.ts → src/__tests__/budget.test.d.ts} +0 -0
- /package/dist/{__tests__/codegen.test.d.ts → src/__tests__/cli.test.d.ts} +0 -0
- /package/dist/{__tests__ → src/__tests__}/compile-all.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/compile-all.test.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/dce.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/diagnostics.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/formatter.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/formatter.test.js +0 -0
- /package/dist/{__tests__/entity-types.test.d.ts → src/__tests__/hir/desugar.test.d.ts} +0 -0
- /package/dist/{__tests__ → src/__tests__}/lexer.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/lexer.test.js +0 -0
- /package/dist/{__tests__/lowering.test.d.ts → src/__tests__/lir/lower.test.d.ts} +0 -0
- /package/dist/{__tests__/mc-syntax.test.d.ts → src/__tests__/lir/types.test.d.ts} +0 -0
- /package/dist/{__tests__/nbt.test.d.ts → src/__tests__/lir/verify.test.d.ts} +0 -0
- /package/dist/{__tests__ → src/__tests__}/mc-integration.test.d.ts +0 -0
- /package/dist/{__tests__/optimizer-advanced.test.d.ts → src/__tests__/mc-syntax.test.d.ts} +0 -0
- /package/dist/{__tests__ → src/__tests__}/mc-syntax.test.js +0 -0
- /package/dist/{__tests__/optimizer.test.d.ts → src/__tests__/mir/arithmetic.test.d.ts} +0 -0
- /package/dist/{__tests__/parser.test.d.ts → src/__tests__/mir/control-flow.test.d.ts} +0 -0
- /package/dist/{__tests__/repl.test.d.ts → src/__tests__/mir/verify.test.d.ts} +0 -0
- /package/dist/{__tests__/runtime.test.d.ts → src/__tests__/optimizer/block_merge.test.d.ts} +0 -0
- /package/dist/{__tests__/structure-optimizer.test.d.ts → src/__tests__/optimizer/branch_simplify.test.d.ts} +0 -0
- /package/dist/{__tests__/typechecker.test.d.ts → src/__tests__/optimizer/constant_fold.test.d.ts} +0 -0
- /package/dist/{__tests__/var-allocator.test.d.ts → src/__tests__/optimizer/copy_prop.test.d.ts} +0 -0
- /package/dist/{__tests__ → src/__tests__}/parser.test.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/repl.test.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/typechecker.test.js +0 -0
- /package/dist/{ast → src/ast}/types.js +0 -0
- /package/dist/{builtins → src/builtins}/metadata.d.ts +0 -0
- /package/dist/{builtins → src/builtins}/metadata.js +0 -0
- /package/dist/{events → src/events}/types.d.ts +0 -0
- /package/dist/{events → src/events}/types.js +0 -0
- /package/dist/{formatter → src/formatter}/index.d.ts +0 -0
- /package/dist/{formatter → src/formatter}/index.js +0 -0
- /package/dist/{mc-test → src/mc-test}/client.d.ts +0 -0
- /package/dist/{mc-test → src/mc-test}/client.js +0 -0
- /package/dist/{mc-test → src/mc-test}/runner.d.ts +0 -0
- /package/dist/{mc-test → src/mc-test}/setup.d.ts +0 -0
- /package/dist/{mc-test → src/mc-test}/setup.js +0 -0
- /package/dist/{mc-validator → src/mc-validator}/index.d.ts +0 -0
- /package/dist/{mc-validator → src/mc-validator}/index.js +0 -0
- /package/dist/{nbt → src/nbt}/index.d.ts +0 -0
- /package/dist/{nbt → src/nbt}/index.js +0 -0
- /package/dist/{repl.js → src/repl.js} +0 -0
- /package/dist/{runtime → src/runtime}/index.d.ts +0 -0
- /package/dist/{types → src/types}/entity-hierarchy.d.ts +0 -0
- /package/dist/{types → src/types}/entity-hierarchy.js +0 -0
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Coroutine Transform — MIR module-level pass.
|
|
4
|
+
*
|
|
5
|
+
* Transforms functions annotated with @coroutine(batch=N, onDone=fn) into
|
|
6
|
+
* tick-spread state machines. Each loop back-edge becomes a yield point,
|
|
7
|
+
* and the function is split into continuation functions dispatched by a
|
|
8
|
+
* @tick function via a pc (program counter) scoreboard slot.
|
|
9
|
+
*
|
|
10
|
+
* Algorithm:
|
|
11
|
+
* 1. Compute dominator tree → find back edges (yield points)
|
|
12
|
+
* 2. Backward liveness analysis at yield points
|
|
13
|
+
* 3. Split CFG into continuations at yield points
|
|
14
|
+
* 4. Promote live variables to persistent scoreboard slots
|
|
15
|
+
* 5. Generate @tick dispatcher function
|
|
16
|
+
*
|
|
17
|
+
* Spec: docs/compiler-pipeline-redesign.md § "Coroutine Transform"
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.coroutineTransform = coroutineTransform;
|
|
21
|
+
/**
|
|
22
|
+
* Apply the coroutine transform to all functions in `infos`.
|
|
23
|
+
* Returns a new module with continuations + dispatchers injected,
|
|
24
|
+
* and the original function replaced with initialization code.
|
|
25
|
+
*/
|
|
26
|
+
function coroutineTransform(mod, infos) {
|
|
27
|
+
if (infos.length === 0)
|
|
28
|
+
return { module: mod, generatedTickFunctions: [] };
|
|
29
|
+
const infoMap = new Map(infos.map(i => [i.fnName, i]));
|
|
30
|
+
const newFunctions = [];
|
|
31
|
+
const tickFns = [];
|
|
32
|
+
for (const fn of mod.functions) {
|
|
33
|
+
const info = infoMap.get(fn.name);
|
|
34
|
+
if (!info) {
|
|
35
|
+
newFunctions.push(fn);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const transformed = transformCoroutine(fn, info, mod.objective);
|
|
39
|
+
newFunctions.push(transformed.initFn);
|
|
40
|
+
newFunctions.push(...transformed.continuations);
|
|
41
|
+
newFunctions.push(transformed.dispatcher);
|
|
42
|
+
tickFns.push(transformed.dispatcher.name);
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
module: { ...mod, functions: newFunctions },
|
|
46
|
+
generatedTickFunctions: tickFns,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function transformCoroutine(fn, info, objective) {
|
|
50
|
+
const prefix = `_coro_${fn.name}`;
|
|
51
|
+
const pcTemp = `${prefix}_pc`;
|
|
52
|
+
const batchCountTemp = `${prefix}_batch_count`;
|
|
53
|
+
// Step 1: Build dominator tree and find back edges
|
|
54
|
+
const blockMap = new Map(fn.blocks.map(b => [b.id, b]));
|
|
55
|
+
const doms = computeDominators(fn.blocks, fn.entry);
|
|
56
|
+
const backEdges = findBackEdges(fn.blocks, doms);
|
|
57
|
+
// If no back edges (no loops), the function doesn't need coroutine splitting.
|
|
58
|
+
// Just wrap it as a single continuation.
|
|
59
|
+
if (backEdges.length === 0) {
|
|
60
|
+
return buildSingleContinuation(fn, info, prefix, pcTemp, objective);
|
|
61
|
+
}
|
|
62
|
+
// Step 2: Liveness analysis — find live variables at each yield point
|
|
63
|
+
const liveAtYield = computeLivenessAtYieldPoints(fn.blocks, backEdges, fn.params);
|
|
64
|
+
// Collect all live variables across all yield points (these need promotion)
|
|
65
|
+
const allLiveVars = new Set();
|
|
66
|
+
for (const liveSet of liveAtYield.values()) {
|
|
67
|
+
for (const v of liveSet)
|
|
68
|
+
allLiveVars.add(v);
|
|
69
|
+
}
|
|
70
|
+
// Build promoted variable names: original temp → persistent slot name
|
|
71
|
+
const promoted = new Map();
|
|
72
|
+
for (const v of allLiveVars) {
|
|
73
|
+
promoted.set(v, `${prefix}_${v}`);
|
|
74
|
+
}
|
|
75
|
+
// Step 3: Split CFG into continuations
|
|
76
|
+
// Each continuation runs from a loop header to the next yield point.
|
|
77
|
+
// For simplicity, we split at loop headers (targets of back edges).
|
|
78
|
+
const loopHeaders = new Set(backEdges.map(e => e.target));
|
|
79
|
+
// Partition blocks into continuation groups.
|
|
80
|
+
// Continuation 1: entry block → until first yield
|
|
81
|
+
// Continuation N: from loop header → until next yield or exit
|
|
82
|
+
const continuations = partitionIntoContinuations(fn, loopHeaders, backEdges);
|
|
83
|
+
// Step 4: Build continuation functions with batch counting and variable promotion
|
|
84
|
+
const contFunctions = [];
|
|
85
|
+
for (let i = 0; i < continuations.length; i++) {
|
|
86
|
+
const contId = i + 1;
|
|
87
|
+
const cont = continuations[i];
|
|
88
|
+
const contFn = buildContinuationFunction(`${prefix}_cont_${contId}`, cont, info.batch, contId, continuations.length, promoted, pcTemp, batchCountTemp, objective, info.onDone, fn.name);
|
|
89
|
+
contFunctions.push(contFn);
|
|
90
|
+
}
|
|
91
|
+
// Step 5: Build the init function (replaces original)
|
|
92
|
+
const initFn = buildInitFunction(fn, promoted, pcTemp, prefix, objective);
|
|
93
|
+
// Step 6: Build the @tick dispatcher
|
|
94
|
+
const dispatcher = buildDispatcher(`${prefix}_tick`, contFunctions, pcTemp, objective, fn.name);
|
|
95
|
+
return { initFn, continuations: contFunctions, dispatcher };
|
|
96
|
+
}
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Step 1: Dominator tree & back-edge detection
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
function computeDominators(blocks, entry) {
|
|
101
|
+
// Simple iterative dominator algorithm (Cooper, Harvey, Kennedy)
|
|
102
|
+
const blockIds = blocks.map(b => b.id);
|
|
103
|
+
const idom = new Map();
|
|
104
|
+
// RPO ordering
|
|
105
|
+
const rpo = reversePostorder(blocks, entry);
|
|
106
|
+
const rpoIndex = new Map(rpo.map((id, i) => [id, i]));
|
|
107
|
+
// Initialize: entry dominates itself
|
|
108
|
+
idom.set(entry, entry);
|
|
109
|
+
let changed = true;
|
|
110
|
+
while (changed) {
|
|
111
|
+
changed = false;
|
|
112
|
+
for (const bId of rpo) {
|
|
113
|
+
if (bId === entry)
|
|
114
|
+
continue;
|
|
115
|
+
const block = blocks.find(b => b.id === bId);
|
|
116
|
+
if (!block)
|
|
117
|
+
continue;
|
|
118
|
+
// Find first processed predecessor
|
|
119
|
+
const processedPreds = block.preds.filter(p => idom.has(p));
|
|
120
|
+
if (processedPreds.length === 0)
|
|
121
|
+
continue;
|
|
122
|
+
let newIdom = processedPreds[0];
|
|
123
|
+
for (let i = 1; i < processedPreds.length; i++) {
|
|
124
|
+
newIdom = intersect(newIdom, processedPreds[i], idom, rpoIndex);
|
|
125
|
+
}
|
|
126
|
+
if (idom.get(bId) !== newIdom) {
|
|
127
|
+
idom.set(bId, newIdom);
|
|
128
|
+
changed = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return idom;
|
|
133
|
+
}
|
|
134
|
+
function intersect(a, b, idom, rpoIndex) {
|
|
135
|
+
let f1 = a;
|
|
136
|
+
let f2 = b;
|
|
137
|
+
while (f1 !== f2) {
|
|
138
|
+
while ((rpoIndex.get(f1) ?? 0) > (rpoIndex.get(f2) ?? 0)) {
|
|
139
|
+
f1 = idom.get(f1) ?? f1;
|
|
140
|
+
}
|
|
141
|
+
while ((rpoIndex.get(f2) ?? 0) > (rpoIndex.get(f1) ?? 0)) {
|
|
142
|
+
f2 = idom.get(f2) ?? f2;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return f1;
|
|
146
|
+
}
|
|
147
|
+
function reversePostorder(blocks, entry) {
|
|
148
|
+
const visited = new Set();
|
|
149
|
+
const order = [];
|
|
150
|
+
const blockMap = new Map(blocks.map(b => [b.id, b]));
|
|
151
|
+
function dfs(id) {
|
|
152
|
+
if (visited.has(id))
|
|
153
|
+
return;
|
|
154
|
+
visited.add(id);
|
|
155
|
+
const block = blockMap.get(id);
|
|
156
|
+
if (!block)
|
|
157
|
+
return;
|
|
158
|
+
for (const succ of getSuccessors(block.term)) {
|
|
159
|
+
dfs(succ);
|
|
160
|
+
}
|
|
161
|
+
order.push(id);
|
|
162
|
+
}
|
|
163
|
+
dfs(entry);
|
|
164
|
+
return order.reverse();
|
|
165
|
+
}
|
|
166
|
+
function findBackEdges(blocks, doms) {
|
|
167
|
+
const edges = [];
|
|
168
|
+
for (const block of blocks) {
|
|
169
|
+
for (const succ of getSuccessors(block.term)) {
|
|
170
|
+
if (dominates(succ, block.id, doms)) {
|
|
171
|
+
edges.push({ source: block.id, target: succ });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return edges;
|
|
176
|
+
}
|
|
177
|
+
function dominates(a, b, doms) {
|
|
178
|
+
let cur = b;
|
|
179
|
+
while (cur !== a) {
|
|
180
|
+
const idom = doms.get(cur);
|
|
181
|
+
if (!idom || idom === cur)
|
|
182
|
+
return false;
|
|
183
|
+
cur = idom;
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
// Step 2: Liveness analysis
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
function computeLivenessAtYieldPoints(blocks, backEdges, params) {
|
|
191
|
+
// Standard backward liveness: live_in[B] = use[B] ∪ (live_out[B] \ def[B])
|
|
192
|
+
const blockMap = new Map(blocks.map(b => [b.id, b]));
|
|
193
|
+
// Compute use/def for each block
|
|
194
|
+
const useSets = new Map();
|
|
195
|
+
const defSets = new Map();
|
|
196
|
+
for (const block of blocks) {
|
|
197
|
+
const use = new Set();
|
|
198
|
+
const def = new Set();
|
|
199
|
+
for (const instr of [...block.instrs, block.term]) {
|
|
200
|
+
// Uses before defs
|
|
201
|
+
for (const t of getUsedTemps(instr)) {
|
|
202
|
+
if (!def.has(t))
|
|
203
|
+
use.add(t);
|
|
204
|
+
}
|
|
205
|
+
const dst = getDst(instr);
|
|
206
|
+
if (dst)
|
|
207
|
+
def.add(dst);
|
|
208
|
+
}
|
|
209
|
+
useSets.set(block.id, use);
|
|
210
|
+
defSets.set(block.id, def);
|
|
211
|
+
}
|
|
212
|
+
// Iterative liveness
|
|
213
|
+
const liveIn = new Map();
|
|
214
|
+
const liveOut = new Map();
|
|
215
|
+
for (const block of blocks) {
|
|
216
|
+
liveIn.set(block.id, new Set());
|
|
217
|
+
liveOut.set(block.id, new Set());
|
|
218
|
+
}
|
|
219
|
+
let changed = true;
|
|
220
|
+
while (changed) {
|
|
221
|
+
changed = false;
|
|
222
|
+
for (const block of [...blocks].reverse()) {
|
|
223
|
+
// live_out = ∪ live_in[succ]
|
|
224
|
+
const newOut = new Set();
|
|
225
|
+
for (const succ of getSuccessors(block.term)) {
|
|
226
|
+
const succIn = liveIn.get(succ);
|
|
227
|
+
if (succIn)
|
|
228
|
+
for (const t of succIn)
|
|
229
|
+
newOut.add(t);
|
|
230
|
+
}
|
|
231
|
+
// live_in = use ∪ (live_out \ def)
|
|
232
|
+
const newIn = new Set(useSets.get(block.id) ?? []);
|
|
233
|
+
const def = defSets.get(block.id) ?? new Set();
|
|
234
|
+
for (const t of newOut) {
|
|
235
|
+
if (!def.has(t))
|
|
236
|
+
newIn.add(t);
|
|
237
|
+
}
|
|
238
|
+
const prevIn = liveIn.get(block.id);
|
|
239
|
+
const prevOut = liveOut.get(block.id);
|
|
240
|
+
if (!setsEqual(newIn, prevIn) || !setsEqual(newOut, prevOut)) {
|
|
241
|
+
liveIn.set(block.id, newIn);
|
|
242
|
+
liveOut.set(block.id, newOut);
|
|
243
|
+
changed = true;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// At each yield point (back edge source → target), the live vars
|
|
248
|
+
// are the live-out of the source block
|
|
249
|
+
const result = new Map();
|
|
250
|
+
for (const edge of backEdges) {
|
|
251
|
+
const lo = liveOut.get(edge.source) ?? new Set();
|
|
252
|
+
result.set(edge.source, lo);
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
function setsEqual(a, b) {
|
|
257
|
+
if (a.size !== b.size)
|
|
258
|
+
return false;
|
|
259
|
+
for (const v of a)
|
|
260
|
+
if (!b.has(v))
|
|
261
|
+
return false;
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
function partitionIntoContinuations(fn, loopHeaders, backEdges) {
|
|
265
|
+
const blockMap = new Map(fn.blocks.map(b => [b.id, b]));
|
|
266
|
+
const backEdgeTargets = new Set(backEdges.map(e => e.target));
|
|
267
|
+
const backEdgeSources = new Set(backEdges.map(e => e.source));
|
|
268
|
+
// Find which blocks belong to which loop (blocks between header and back edge)
|
|
269
|
+
// We use a simple approach: BFS from entry, split at loop headers
|
|
270
|
+
const loopBlocks = new Map(); // header → blocks in loop
|
|
271
|
+
for (const header of loopHeaders) {
|
|
272
|
+
const inLoop = new Set();
|
|
273
|
+
// Find all blocks that can reach the back edge source without leaving the loop
|
|
274
|
+
// i.e., blocks dominated by the header that can reach a back edge source
|
|
275
|
+
const sources = backEdges.filter(e => e.target === header).map(e => e.source);
|
|
276
|
+
// Backward BFS from back-edge sources to header
|
|
277
|
+
const queue = [...sources];
|
|
278
|
+
for (const s of sources)
|
|
279
|
+
inLoop.add(s);
|
|
280
|
+
inLoop.add(header);
|
|
281
|
+
while (queue.length > 0) {
|
|
282
|
+
const bid = queue.shift();
|
|
283
|
+
const block = blockMap.get(bid);
|
|
284
|
+
if (!block)
|
|
285
|
+
continue;
|
|
286
|
+
for (const pred of block.preds) {
|
|
287
|
+
if (!inLoop.has(pred) && pred !== header) {
|
|
288
|
+
inLoop.add(pred);
|
|
289
|
+
queue.push(pred);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
inLoop.add(header);
|
|
294
|
+
loopBlocks.set(header, inLoop);
|
|
295
|
+
}
|
|
296
|
+
// Build continuations:
|
|
297
|
+
// Continuation 1 = loop body (the main one — header + loop blocks)
|
|
298
|
+
// Continuation 2 = post-loop (blocks after loop exit)
|
|
299
|
+
const conts = [];
|
|
300
|
+
// For each loop, create a loop-body continuation
|
|
301
|
+
for (const header of loopHeaders) {
|
|
302
|
+
const lb = loopBlocks.get(header);
|
|
303
|
+
const loopBlocksList = fn.blocks.filter(b => lb.has(b.id));
|
|
304
|
+
const exitBlocks = new Set();
|
|
305
|
+
// Find exit blocks: blocks in the loop that branch to blocks outside the loop
|
|
306
|
+
for (const block of loopBlocksList) {
|
|
307
|
+
for (const succ of getSuccessors(block.term)) {
|
|
308
|
+
if (!lb.has(succ))
|
|
309
|
+
exitBlocks.add(block.id);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
conts.push({
|
|
313
|
+
blocks: loopBlocksList,
|
|
314
|
+
isLoopBody: true,
|
|
315
|
+
loopHeaderId: header,
|
|
316
|
+
exitBlocks,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
// Post-loop continuation: all blocks not in any loop
|
|
320
|
+
const allLoopBlockIds = new Set();
|
|
321
|
+
for (const lb of loopBlocks.values()) {
|
|
322
|
+
for (const id of lb)
|
|
323
|
+
allLoopBlockIds.add(id);
|
|
324
|
+
}
|
|
325
|
+
// Also exclude the entry block if it's not in a loop (it becomes the init fn)
|
|
326
|
+
const postLoopBlocks = fn.blocks.filter(b => !allLoopBlockIds.has(b.id) && b.id !== fn.entry);
|
|
327
|
+
if (postLoopBlocks.length > 0) {
|
|
328
|
+
conts.push({
|
|
329
|
+
blocks: postLoopBlocks,
|
|
330
|
+
isLoopBody: false,
|
|
331
|
+
exitBlocks: new Set(),
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
return conts;
|
|
335
|
+
}
|
|
336
|
+
// ---------------------------------------------------------------------------
|
|
337
|
+
// Step 4: Build continuation functions
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
function buildContinuationFunction(name, cont, batch, contId, totalConts, promoted, pcTemp, batchCountTemp, objective, onDone, originalFnName) {
|
|
340
|
+
if (cont.isLoopBody) {
|
|
341
|
+
return buildLoopContinuation(name, cont, batch, contId, totalConts, promoted, pcTemp, batchCountTemp, objective, onDone);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
return buildPostLoopContinuation(name, cont, contId, promoted, pcTemp, objective, onDone);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function buildLoopContinuation(name, cont, batch, contId, totalConts, promoted, pcTemp, batchCountTemp, objective, onDone) {
|
|
348
|
+
// Build a new function that:
|
|
349
|
+
// 1. Initializes batch_count = 0
|
|
350
|
+
// 2. Runs the loop body up to `batch` iterations
|
|
351
|
+
// 3. At back edge: if batch_count >= batch, yield (return)
|
|
352
|
+
// 4. On loop exit: return (dispatcher handles next continuation via pc)
|
|
353
|
+
//
|
|
354
|
+
// IMPORTANT: The LIR lowerer handles multi-pred blocks via `jump` terminators
|
|
355
|
+
// correctly (emits a function call) but `branch → multi-pred` can cause infinite
|
|
356
|
+
// recursion. So we ensure back edges use `jump` via a trampoline block, and
|
|
357
|
+
// batch-done checks use `branch → yield_block | continue_block` where both
|
|
358
|
+
// targets have single predecessors.
|
|
359
|
+
const blocks = [];
|
|
360
|
+
const batchCmpTemp = `${batchCountTemp}_cmp`;
|
|
361
|
+
// Entry: set batch_count = 0, then jump to loop header
|
|
362
|
+
const entryBlock = {
|
|
363
|
+
id: 'entry',
|
|
364
|
+
instrs: [
|
|
365
|
+
{ kind: 'const', dst: batchCountTemp, value: 0 },
|
|
366
|
+
],
|
|
367
|
+
term: { kind: 'jump', target: cont.loopHeaderId ?? cont.blocks[0].id },
|
|
368
|
+
preds: [],
|
|
369
|
+
};
|
|
370
|
+
blocks.push(entryBlock);
|
|
371
|
+
// Clone and rewrite the loop blocks with promoted variable names
|
|
372
|
+
for (const block of cont.blocks) {
|
|
373
|
+
const rewritten = rewriteBlock(block, promoted);
|
|
374
|
+
const succs = getSuccessors(rewritten.term);
|
|
375
|
+
const isBackEdgeBlock = cont.loopHeaderId && succs.includes(cont.loopHeaderId);
|
|
376
|
+
if (isBackEdgeBlock) {
|
|
377
|
+
// This block has a back edge to the loop header.
|
|
378
|
+
// Append batch counting to the body, then branch:
|
|
379
|
+
// batch_done → yield (return), !batch_done → continue (jump → header)
|
|
380
|
+
//
|
|
381
|
+
// body_block → branch(batch_done) → yield_block | continue_block
|
|
382
|
+
// continue_block → jump → loop_header (uses 'jump', safe for multi-pred)
|
|
383
|
+
// yield_block → return
|
|
384
|
+
const continueBlockId = `${block.id}_continue`;
|
|
385
|
+
const yieldBlockId = `${block.id}_yield`;
|
|
386
|
+
// Append batch check instructions to the body block
|
|
387
|
+
const bodyInstrs = [
|
|
388
|
+
...rewritten.instrs,
|
|
389
|
+
{ kind: 'add', dst: batchCountTemp, a: { kind: 'temp', name: batchCountTemp }, b: { kind: 'const', value: 1 } },
|
|
390
|
+
{ kind: 'cmp', dst: batchCmpTemp, op: 'ge', a: { kind: 'temp', name: batchCountTemp }, b: { kind: 'const', value: batch } },
|
|
391
|
+
];
|
|
392
|
+
blocks.push({
|
|
393
|
+
...rewritten,
|
|
394
|
+
instrs: bodyInstrs,
|
|
395
|
+
// Rewrite terminator: instead of jumping to header, branch on batch check
|
|
396
|
+
term: {
|
|
397
|
+
kind: 'branch',
|
|
398
|
+
cond: { kind: 'temp', name: batchCmpTemp },
|
|
399
|
+
then: yieldBlockId,
|
|
400
|
+
else: continueBlockId,
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
// Continue block: jump back to loop header (uses jump → multi-pred = call)
|
|
404
|
+
blocks.push({
|
|
405
|
+
id: continueBlockId,
|
|
406
|
+
instrs: [],
|
|
407
|
+
term: { kind: 'jump', target: cont.loopHeaderId },
|
|
408
|
+
preds: [block.id],
|
|
409
|
+
});
|
|
410
|
+
// Yield block: return (resume next tick)
|
|
411
|
+
blocks.push({
|
|
412
|
+
id: yieldBlockId,
|
|
413
|
+
instrs: [],
|
|
414
|
+
term: { kind: 'return', value: null },
|
|
415
|
+
preds: [block.id],
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
// Check if this block exits the loop
|
|
420
|
+
const exitSuccs = succs.filter(s => !cont.blocks.some(b => b.id === s));
|
|
421
|
+
if (exitSuccs.length > 0) {
|
|
422
|
+
// Redirect exit branch to an exit block that returns
|
|
423
|
+
const exitBlockId = `${block.id}_exit`;
|
|
424
|
+
const exitBlock = {
|
|
425
|
+
id: exitBlockId,
|
|
426
|
+
instrs: [],
|
|
427
|
+
term: { kind: 'return', value: null },
|
|
428
|
+
preds: [block.id],
|
|
429
|
+
};
|
|
430
|
+
if (rewritten.term.kind === 'branch') {
|
|
431
|
+
const branchTerm = rewritten.term;
|
|
432
|
+
const thenInLoop = cont.blocks.some(b => b.id === branchTerm.then);
|
|
433
|
+
const elseInLoop = cont.blocks.some(b => b.id === branchTerm.else);
|
|
434
|
+
if (!thenInLoop && elseInLoop) {
|
|
435
|
+
blocks.push({
|
|
436
|
+
...rewritten,
|
|
437
|
+
term: { ...branchTerm, then: exitBlockId },
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
else if (thenInLoop && !elseInLoop) {
|
|
441
|
+
blocks.push({
|
|
442
|
+
...rewritten,
|
|
443
|
+
term: { ...branchTerm, else: exitBlockId },
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
blocks.push(rewritten);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
blocks.push(rewritten);
|
|
452
|
+
}
|
|
453
|
+
blocks.push(exitBlock);
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
blocks.push(rewritten);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// Deduplicate blocks by ID
|
|
461
|
+
const seenIds = new Set();
|
|
462
|
+
const dedupBlocks = [];
|
|
463
|
+
for (const b of blocks) {
|
|
464
|
+
if (!seenIds.has(b.id)) {
|
|
465
|
+
seenIds.add(b.id);
|
|
466
|
+
dedupBlocks.push(b);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
name,
|
|
471
|
+
params: [],
|
|
472
|
+
blocks: dedupBlocks,
|
|
473
|
+
entry: 'entry',
|
|
474
|
+
isMacro: false,
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function buildPostLoopContinuation(name, cont, contId, promoted, pcTemp, objective, onDone) {
|
|
478
|
+
const blocks = [];
|
|
479
|
+
// Rewrite blocks with promoted variables
|
|
480
|
+
for (const block of cont.blocks) {
|
|
481
|
+
blocks.push(rewriteBlock(block, promoted));
|
|
482
|
+
}
|
|
483
|
+
// The entry is the first block in the continuation
|
|
484
|
+
const entry = cont.blocks[0]?.id ?? 'entry';
|
|
485
|
+
// Add onDone call if this is the last continuation
|
|
486
|
+
if (onDone) {
|
|
487
|
+
// Find blocks with return terminators and add onDone call before them
|
|
488
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
489
|
+
if (blocks[i].term.kind === 'return') {
|
|
490
|
+
blocks[i] = {
|
|
491
|
+
...blocks[i],
|
|
492
|
+
instrs: [
|
|
493
|
+
...blocks[i].instrs,
|
|
494
|
+
{ kind: 'call', dst: null, fn: onDone, args: [] },
|
|
495
|
+
],
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
name,
|
|
502
|
+
params: [],
|
|
503
|
+
blocks,
|
|
504
|
+
entry,
|
|
505
|
+
isMacro: false,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
// ---------------------------------------------------------------------------
|
|
509
|
+
// Step 5: Build init function (replaces original)
|
|
510
|
+
// ---------------------------------------------------------------------------
|
|
511
|
+
function buildInitFunction(originalFn, promoted, pcTemp, prefix, objective) {
|
|
512
|
+
// The init function:
|
|
513
|
+
// 1. Sets pc = 1 (start from continuation 1)
|
|
514
|
+
// 2. Initializes promoted variables from the entry block's pre-loop code
|
|
515
|
+
const instrs = [];
|
|
516
|
+
// Set pc = 1
|
|
517
|
+
instrs.push({ kind: 'const', dst: pcTemp, value: 1 });
|
|
518
|
+
// Initialize promoted variables from entry block instructions
|
|
519
|
+
// Walk the entry block and copy any const/copy instructions for promoted vars
|
|
520
|
+
const entryBlock = originalFn.blocks.find(b => b.id === originalFn.entry);
|
|
521
|
+
if (entryBlock) {
|
|
522
|
+
for (const instr of entryBlock.instrs) {
|
|
523
|
+
const dst = getDst(instr);
|
|
524
|
+
if (dst && promoted.has(dst)) {
|
|
525
|
+
// Rewrite to use promoted name
|
|
526
|
+
instrs.push(rewriteInstr(instr, promoted));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
const block = {
|
|
531
|
+
id: 'entry',
|
|
532
|
+
instrs,
|
|
533
|
+
term: { kind: 'return', value: null },
|
|
534
|
+
preds: [],
|
|
535
|
+
};
|
|
536
|
+
return {
|
|
537
|
+
name: originalFn.name,
|
|
538
|
+
params: originalFn.params,
|
|
539
|
+
blocks: [block],
|
|
540
|
+
entry: 'entry',
|
|
541
|
+
isMacro: originalFn.isMacro,
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
// ---------------------------------------------------------------------------
|
|
545
|
+
// Step 6: Build dispatcher
|
|
546
|
+
// ---------------------------------------------------------------------------
|
|
547
|
+
function buildDispatcher(name, continuations, pcTemp, objective, originalFnName) {
|
|
548
|
+
// Generates a dispatcher function that checks pc and calls the right continuation.
|
|
549
|
+
// For each continuation i (1-indexed):
|
|
550
|
+
// execute if score $coro_pc __ns matches i run function ns:_coro_cont_i
|
|
551
|
+
//
|
|
552
|
+
// We model this as a chain of branches in MIR.
|
|
553
|
+
const blocks = [];
|
|
554
|
+
if (continuations.length === 0) {
|
|
555
|
+
// No continuations — just return
|
|
556
|
+
blocks.push({
|
|
557
|
+
id: 'entry',
|
|
558
|
+
instrs: [],
|
|
559
|
+
term: { kind: 'return', value: null },
|
|
560
|
+
preds: [],
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
// Build a chain: check pc==1 → call cont_1, else check pc==2 → call cont_2, ...
|
|
565
|
+
for (let i = 0; i < continuations.length; i++) {
|
|
566
|
+
const contFn = continuations[i];
|
|
567
|
+
const blockId = i === 0 ? 'entry' : `check_${i + 1}`;
|
|
568
|
+
const cmpTemp = `${name}_cmp_${i + 1}`;
|
|
569
|
+
const nextBlock = i < continuations.length - 1 ? `check_${i + 2}` : 'done';
|
|
570
|
+
const callBlock = `call_${i + 1}`;
|
|
571
|
+
blocks.push({
|
|
572
|
+
id: blockId,
|
|
573
|
+
instrs: [
|
|
574
|
+
{ kind: 'cmp', dst: cmpTemp, op: 'eq', a: { kind: 'temp', name: pcTemp }, b: { kind: 'const', value: i + 1 } },
|
|
575
|
+
],
|
|
576
|
+
term: { kind: 'branch', cond: { kind: 'temp', name: cmpTemp }, then: callBlock, else: nextBlock },
|
|
577
|
+
preds: i === 0 ? [] : [`check_${i}`],
|
|
578
|
+
});
|
|
579
|
+
blocks.push({
|
|
580
|
+
id: callBlock,
|
|
581
|
+
instrs: [
|
|
582
|
+
{ kind: 'call', dst: null, fn: contFn.name, args: [] },
|
|
583
|
+
],
|
|
584
|
+
term: { kind: 'return', value: null },
|
|
585
|
+
preds: [blockId],
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
// Done block (pc doesn't match any continuation — coroutine finished)
|
|
589
|
+
blocks.push({
|
|
590
|
+
id: 'done',
|
|
591
|
+
instrs: [],
|
|
592
|
+
term: { kind: 'return', value: null },
|
|
593
|
+
preds: [continuations.length === 1 ? 'entry' : `check_${continuations.length}`],
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
return {
|
|
597
|
+
name,
|
|
598
|
+
params: [],
|
|
599
|
+
blocks,
|
|
600
|
+
entry: 'entry',
|
|
601
|
+
isMacro: false,
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
// ---------------------------------------------------------------------------
|
|
605
|
+
// Single-continuation fallback (no loops)
|
|
606
|
+
// ---------------------------------------------------------------------------
|
|
607
|
+
function buildSingleContinuation(fn, info, prefix, pcTemp, objective) {
|
|
608
|
+
// If there are no loops, the entire function body runs in one tick.
|
|
609
|
+
// We still wrap it in the coroutine pattern for consistency (init → cont_1 → done).
|
|
610
|
+
const contName = `${prefix}_cont_1`;
|
|
611
|
+
// Continuation = entire original function body, plus set pc=-1 at the end
|
|
612
|
+
const contBlocks = fn.blocks.map(block => {
|
|
613
|
+
if (block.term.kind === 'return') {
|
|
614
|
+
const instrs = [...block.instrs];
|
|
615
|
+
if (info.onDone) {
|
|
616
|
+
instrs.push({ kind: 'call', dst: null, fn: info.onDone, args: [] });
|
|
617
|
+
}
|
|
618
|
+
return { ...block, instrs };
|
|
619
|
+
}
|
|
620
|
+
return block;
|
|
621
|
+
});
|
|
622
|
+
const contFn = {
|
|
623
|
+
name: contName,
|
|
624
|
+
params: [],
|
|
625
|
+
blocks: contBlocks,
|
|
626
|
+
entry: fn.entry,
|
|
627
|
+
isMacro: false,
|
|
628
|
+
};
|
|
629
|
+
// Init: set pc = 1
|
|
630
|
+
const initBlock = {
|
|
631
|
+
id: 'entry',
|
|
632
|
+
instrs: [
|
|
633
|
+
{ kind: 'const', dst: pcTemp, value: 1 },
|
|
634
|
+
],
|
|
635
|
+
term: { kind: 'return', value: null },
|
|
636
|
+
preds: [],
|
|
637
|
+
};
|
|
638
|
+
const initFn = {
|
|
639
|
+
name: fn.name,
|
|
640
|
+
params: fn.params,
|
|
641
|
+
blocks: [initBlock],
|
|
642
|
+
entry: 'entry',
|
|
643
|
+
isMacro: fn.isMacro,
|
|
644
|
+
};
|
|
645
|
+
const dispatcher = buildDispatcher(`${prefix}_tick`, [contFn], pcTemp, objective, fn.name);
|
|
646
|
+
return { initFn, continuations: [contFn], dispatcher };
|
|
647
|
+
}
|
|
648
|
+
// ---------------------------------------------------------------------------
|
|
649
|
+
// Helpers: instruction rewriting
|
|
650
|
+
// ---------------------------------------------------------------------------
|
|
651
|
+
function rewriteBlock(block, promoted) {
|
|
652
|
+
return {
|
|
653
|
+
...block,
|
|
654
|
+
instrs: block.instrs.map(i => rewriteInstr(i, promoted)),
|
|
655
|
+
term: rewriteInstr(block.term, promoted),
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
function rewriteInstr(instr, promoted) {
|
|
659
|
+
// Deep-rewrite all temp references through the promoted map
|
|
660
|
+
const rTemp = (t) => promoted.get(t) ?? t;
|
|
661
|
+
const rOp = (op) => op.kind === 'temp' ? { kind: 'temp', name: rTemp(op.name) } : op;
|
|
662
|
+
switch (instr.kind) {
|
|
663
|
+
case 'const':
|
|
664
|
+
return { ...instr, dst: rTemp(instr.dst) };
|
|
665
|
+
case 'copy':
|
|
666
|
+
return { ...instr, dst: rTemp(instr.dst), src: rOp(instr.src) };
|
|
667
|
+
case 'add':
|
|
668
|
+
case 'sub':
|
|
669
|
+
case 'mul':
|
|
670
|
+
case 'div':
|
|
671
|
+
case 'mod':
|
|
672
|
+
return { ...instr, dst: rTemp(instr.dst), a: rOp(instr.a), b: rOp(instr.b) };
|
|
673
|
+
case 'neg':
|
|
674
|
+
return { ...instr, dst: rTemp(instr.dst), src: rOp(instr.src) };
|
|
675
|
+
case 'cmp':
|
|
676
|
+
return { ...instr, dst: rTemp(instr.dst), a: rOp(instr.a), b: rOp(instr.b) };
|
|
677
|
+
case 'and':
|
|
678
|
+
case 'or':
|
|
679
|
+
return { ...instr, dst: rTemp(instr.dst), a: rOp(instr.a), b: rOp(instr.b) };
|
|
680
|
+
case 'not':
|
|
681
|
+
return { ...instr, dst: rTemp(instr.dst), src: rOp(instr.src) };
|
|
682
|
+
case 'nbt_read':
|
|
683
|
+
return { ...instr, dst: rTemp(instr.dst) };
|
|
684
|
+
case 'nbt_write':
|
|
685
|
+
return { ...instr, src: rOp(instr.src) };
|
|
686
|
+
case 'call':
|
|
687
|
+
return { ...instr, dst: instr.dst ? rTemp(instr.dst) : null, args: instr.args.map(rOp) };
|
|
688
|
+
case 'call_macro':
|
|
689
|
+
return { ...instr, dst: instr.dst ? rTemp(instr.dst) : null, args: instr.args.map(a => ({ ...a, value: rOp(a.value) })) };
|
|
690
|
+
case 'call_context':
|
|
691
|
+
return instr;
|
|
692
|
+
case 'branch':
|
|
693
|
+
return { ...instr, cond: rOp(instr.cond) };
|
|
694
|
+
case 'return':
|
|
695
|
+
return { ...instr, value: instr.value ? rOp(instr.value) : null };
|
|
696
|
+
case 'jump':
|
|
697
|
+
return instr;
|
|
698
|
+
default:
|
|
699
|
+
return instr;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function rewriteTerminator(term, from, to) {
|
|
703
|
+
switch (term.kind) {
|
|
704
|
+
case 'jump':
|
|
705
|
+
return term.target === from ? { ...term, target: to } : term;
|
|
706
|
+
case 'branch':
|
|
707
|
+
return {
|
|
708
|
+
...term,
|
|
709
|
+
then: term.then === from ? to : term.then,
|
|
710
|
+
else: term.else === from ? to : term.else,
|
|
711
|
+
};
|
|
712
|
+
default:
|
|
713
|
+
return term;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
// ---------------------------------------------------------------------------
|
|
717
|
+
// Helpers: MIR instruction queries
|
|
718
|
+
// ---------------------------------------------------------------------------
|
|
719
|
+
function getSuccessors(term) {
|
|
720
|
+
switch (term.kind) {
|
|
721
|
+
case 'jump': return [term.target];
|
|
722
|
+
case 'branch': return [term.then, term.else];
|
|
723
|
+
default: return [];
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
function getDst(instr) {
|
|
727
|
+
switch (instr.kind) {
|
|
728
|
+
case 'const':
|
|
729
|
+
case 'copy':
|
|
730
|
+
case 'add':
|
|
731
|
+
case 'sub':
|
|
732
|
+
case 'mul':
|
|
733
|
+
case 'div':
|
|
734
|
+
case 'mod':
|
|
735
|
+
case 'neg':
|
|
736
|
+
case 'cmp':
|
|
737
|
+
case 'and':
|
|
738
|
+
case 'or':
|
|
739
|
+
case 'not':
|
|
740
|
+
case 'nbt_read':
|
|
741
|
+
return instr.dst;
|
|
742
|
+
case 'call':
|
|
743
|
+
case 'call_macro':
|
|
744
|
+
return instr.dst;
|
|
745
|
+
default:
|
|
746
|
+
return null;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
function getUsedTemps(instr) {
|
|
750
|
+
const temps = [];
|
|
751
|
+
const addOp = (op) => { if (op.kind === 'temp')
|
|
752
|
+
temps.push(op.name); };
|
|
753
|
+
switch (instr.kind) {
|
|
754
|
+
case 'copy':
|
|
755
|
+
case 'neg':
|
|
756
|
+
case 'not':
|
|
757
|
+
addOp(instr.src);
|
|
758
|
+
break;
|
|
759
|
+
case 'add':
|
|
760
|
+
case 'sub':
|
|
761
|
+
case 'mul':
|
|
762
|
+
case 'div':
|
|
763
|
+
case 'mod':
|
|
764
|
+
case 'cmp':
|
|
765
|
+
case 'and':
|
|
766
|
+
case 'or':
|
|
767
|
+
addOp(instr.a);
|
|
768
|
+
addOp(instr.b);
|
|
769
|
+
break;
|
|
770
|
+
case 'nbt_write':
|
|
771
|
+
addOp(instr.src);
|
|
772
|
+
break;
|
|
773
|
+
case 'call':
|
|
774
|
+
instr.args.forEach(addOp);
|
|
775
|
+
break;
|
|
776
|
+
case 'call_macro':
|
|
777
|
+
instr.args.forEach(a => addOp(a.value));
|
|
778
|
+
break;
|
|
779
|
+
case 'branch':
|
|
780
|
+
addOp(instr.cond);
|
|
781
|
+
break;
|
|
782
|
+
case 'return':
|
|
783
|
+
if (instr.value)
|
|
784
|
+
addOp(instr.value);
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
return temps;
|
|
788
|
+
}
|
|
789
|
+
//# sourceMappingURL=coroutine.js.map
|