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,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selector Cache — MIR optimization pass.
|
|
3
|
+
*
|
|
4
|
+
* Within a single basic block, if the same complex selector (one containing '[')
|
|
5
|
+
* appears in ≥2 call_context instructions (as `as` or `at` subcommands),
|
|
6
|
+
* the pass replaces the 2nd and subsequent occurrences with a simpler
|
|
7
|
+
* tag-based selector `@e[tag=__cache_sel_N]`.
|
|
8
|
+
*
|
|
9
|
+
* Two new call_context instructions are prepended to the block:
|
|
10
|
+
* 1. Cleanup: execute as @e[tag=__cache_sel_N] run <fn: __sel_cleanup_<tag>>
|
|
11
|
+
* — signals codegen to emit: tag @e[tag=<tag>] remove <tag>
|
|
12
|
+
* 2. Tag-add: execute as <original_selector> run <fn: __sel_tag_<tag>>
|
|
13
|
+
* — signals codegen to emit: tag @s add <tag>
|
|
14
|
+
*
|
|
15
|
+
* The synthetic fn names `__sel_cleanup_*` and `__sel_tag_*` are a convention
|
|
16
|
+
* recognized by the codegen layer for special-case emission.
|
|
17
|
+
*
|
|
18
|
+
* This is a block-local pass (does not track selector lifetime across block
|
|
19
|
+
* boundaries) so it is always correct with respect to control flow.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { MIRFunction, MIRBlock, MIRInstr } from '../mir/types'
|
|
23
|
+
|
|
24
|
+
export function selectorCache(fn: MIRFunction): MIRFunction {
|
|
25
|
+
let tagId = 0
|
|
26
|
+
return {
|
|
27
|
+
...fn,
|
|
28
|
+
blocks: fn.blocks.map(block => processBlock(block, () => tagId++)),
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function processBlock(block: MIRBlock, nextId: () => number): MIRBlock {
|
|
33
|
+
// Count how many times each complex selector appears across call_context instrs
|
|
34
|
+
const selectorCount = new Map<string, number>()
|
|
35
|
+
for (const instr of block.instrs) {
|
|
36
|
+
if (instr.kind === 'call_context') {
|
|
37
|
+
for (const sub of instr.subcommands) {
|
|
38
|
+
if ((sub.kind === 'as' || sub.kind === 'at') && isComplexSelector(sub.selector)) {
|
|
39
|
+
selectorCount.set(sub.selector, (selectorCount.get(sub.selector) ?? 0) + 1)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Build a map of selectors that appear ≥2 times → assigned tag name
|
|
46
|
+
const repeated = new Map<string, string>() // selector → tag name
|
|
47
|
+
for (const [sel, count] of selectorCount) {
|
|
48
|
+
if (count >= 2) {
|
|
49
|
+
repeated.set(sel, `__cache_sel_${nextId()}`)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (repeated.size === 0) return block
|
|
54
|
+
|
|
55
|
+
// Prepend cleanup + tag-add instructions for each repeated selector
|
|
56
|
+
const prefixInstrs: MIRInstr[] = []
|
|
57
|
+
for (const [sel, tag] of repeated) {
|
|
58
|
+
// 1. Cleanup: remove stale tags from any entities that still carry this tag
|
|
59
|
+
prefixInstrs.push({
|
|
60
|
+
kind: 'call_context',
|
|
61
|
+
fn: `__sel_cleanup_${tag}`,
|
|
62
|
+
subcommands: [{ kind: 'as', selector: `@e[tag=${tag}]` }],
|
|
63
|
+
})
|
|
64
|
+
// 2. Tag-add: tag all matching entities with the cache tag
|
|
65
|
+
prefixInstrs.push({
|
|
66
|
+
kind: 'call_context',
|
|
67
|
+
fn: `__sel_tag_${tag}`,
|
|
68
|
+
subcommands: [{ kind: 'as', selector: sel }],
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Rewrite instructions: first occurrence of each repeated selector is kept
|
|
73
|
+
// as-is; subsequent occurrences are replaced with the tag-based selector.
|
|
74
|
+
const seen = new Set<string>()
|
|
75
|
+
const newInstrs: MIRInstr[] = [...prefixInstrs]
|
|
76
|
+
for (const instr of block.instrs) {
|
|
77
|
+
if (instr.kind === 'call_context') {
|
|
78
|
+
const newSubs = instr.subcommands.map(sub => {
|
|
79
|
+
if ((sub.kind === 'as' || sub.kind === 'at') && repeated.has(sub.selector)) {
|
|
80
|
+
const tag = repeated.get(sub.selector)!
|
|
81
|
+
if (seen.has(sub.selector)) {
|
|
82
|
+
// Subsequent occurrence — use the tag selector
|
|
83
|
+
return { ...sub, selector: `@e[tag=${tag}]` }
|
|
84
|
+
} else {
|
|
85
|
+
// First occurrence — keep original, mark as seen
|
|
86
|
+
seen.add(sub.selector)
|
|
87
|
+
return sub
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return sub
|
|
91
|
+
})
|
|
92
|
+
newInstrs.push({ ...instr, subcommands: newSubs })
|
|
93
|
+
} else {
|
|
94
|
+
newInstrs.push(instr)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { ...block, instrs: newInstrs }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isComplexSelector(selector: string): boolean {
|
|
102
|
+
return selector.includes('[')
|
|
103
|
+
}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small Constant Loop Unrolling — MIR optimization pass.
|
|
3
|
+
*
|
|
4
|
+
* Detects `for (let i = 0; i < N; i++)` loops where N is a compile-time
|
|
5
|
+
* constant and N ≤ 8, then unrolls them: the loop body is duplicated N times
|
|
6
|
+
* with the loop variable substituted as the literal 0..N-1.
|
|
7
|
+
*
|
|
8
|
+
* Pattern recognized (after HIR→MIR lowering):
|
|
9
|
+
* entry block: const t_i 0 (loop var init)
|
|
10
|
+
* loop_header: cmp(lt, t_i, N) → branch body/exit
|
|
11
|
+
* loop_body: body instructions, jump → loop_latch
|
|
12
|
+
* loop_latch: t_i = t_i + 1, jump → loop_header
|
|
13
|
+
* loop_exit: ...
|
|
14
|
+
*
|
|
15
|
+
* After unrolling:
|
|
16
|
+
* entry block (with i=0 def removed):
|
|
17
|
+
* [body with t_i → 0]
|
|
18
|
+
* [body with t_i → 1]
|
|
19
|
+
* ...
|
|
20
|
+
* [body with t_i → N-1]
|
|
21
|
+
* jump → loop_exit
|
|
22
|
+
* loop_exit: ...
|
|
23
|
+
*
|
|
24
|
+
* Limitations:
|
|
25
|
+
* - Only unrolls when N ≤ 8
|
|
26
|
+
* - The loop variable must be initialized to exactly 0 before the loop
|
|
27
|
+
* - The latch must do exactly `t_i = t_i + 1` (or equivalent const add)
|
|
28
|
+
* - No break/continue (body must not jump directly to exit or latch)
|
|
29
|
+
* - N must be a compile-time constant (Operand kind='const')
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import type {
|
|
33
|
+
MIRFunction, MIRBlock, MIRInstr, Operand, Temp, BlockId,
|
|
34
|
+
} from '../mir/types'
|
|
35
|
+
|
|
36
|
+
const UNROLL_LIMIT = 8
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Public API
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
export function loopUnroll(fn: MIRFunction): MIRFunction {
|
|
43
|
+
let current = fn
|
|
44
|
+
let changed = true
|
|
45
|
+
// Iterate to fixpoint in case of multiple unrollable loops
|
|
46
|
+
while (changed) {
|
|
47
|
+
changed = false
|
|
48
|
+
const result = tryUnrollOne(current)
|
|
49
|
+
if (result !== current) {
|
|
50
|
+
current = result
|
|
51
|
+
changed = true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return current
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Core: try to unroll one loop in the function
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
interface LoopInfo {
|
|
62
|
+
/** Block id of loop_header */
|
|
63
|
+
headerId: BlockId
|
|
64
|
+
/** Block id of loop_body */
|
|
65
|
+
bodyId: BlockId
|
|
66
|
+
/** Block id of loop_latch */
|
|
67
|
+
latchId: BlockId
|
|
68
|
+
/** Block id of loop_exit */
|
|
69
|
+
exitId: BlockId
|
|
70
|
+
/** The loop variable temp name */
|
|
71
|
+
loopVar: Temp
|
|
72
|
+
/** The upper bound constant (exclusive: i < N) */
|
|
73
|
+
N: number
|
|
74
|
+
/** Block id that jumps into the loop header (the pre-header) */
|
|
75
|
+
preHeaderId: BlockId
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function tryUnrollOne(fn: MIRFunction): MIRFunction {
|
|
79
|
+
const blockMap = new Map(fn.blocks.map(b => [b.id, b]))
|
|
80
|
+
const info = findUnrollableLoop(fn, blockMap)
|
|
81
|
+
if (!info) return fn
|
|
82
|
+
|
|
83
|
+
return unroll(fn, blockMap, info)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Loop detection
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
function findUnrollableLoop(fn: MIRFunction, blockMap: Map<BlockId, MIRBlock>): LoopInfo | null {
|
|
91
|
+
for (const block of fn.blocks) {
|
|
92
|
+
if (!block.id.startsWith('loop_header')) continue
|
|
93
|
+
const info = analyzeLoop(fn, blockMap, block)
|
|
94
|
+
if (info) return info
|
|
95
|
+
}
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function analyzeLoop(
|
|
100
|
+
fn: MIRFunction,
|
|
101
|
+
blockMap: Map<BlockId, MIRBlock>,
|
|
102
|
+
header: MIRBlock,
|
|
103
|
+
): LoopInfo | null {
|
|
104
|
+
// Header must branch on a cmp result
|
|
105
|
+
if (header.term.kind !== 'branch') return null
|
|
106
|
+
const branch = header.term
|
|
107
|
+
if (branch.cond.kind !== 'temp') return null
|
|
108
|
+
const condName = branch.cond.name
|
|
109
|
+
|
|
110
|
+
// Find the cmp instruction in the header
|
|
111
|
+
const cmpInstr = header.instrs.find(
|
|
112
|
+
instr => instr.kind === 'cmp' && instr.dst === condName
|
|
113
|
+
) as Extract<MIRInstr, { kind: 'cmp' }> | undefined
|
|
114
|
+
if (!cmpInstr) return null
|
|
115
|
+
|
|
116
|
+
// Must be a `lt` comparison: i < N
|
|
117
|
+
if (cmpInstr.op !== 'lt') return null
|
|
118
|
+
|
|
119
|
+
// Left operand must be a temp (the loop var), right must be a constant
|
|
120
|
+
if (cmpInstr.a.kind !== 'temp') return null
|
|
121
|
+
if (cmpInstr.b.kind !== 'const') return null
|
|
122
|
+
|
|
123
|
+
const loopVar = cmpInstr.a.name
|
|
124
|
+
const N = cmpInstr.b.value
|
|
125
|
+
|
|
126
|
+
// Reject if N > limit or N <= 0
|
|
127
|
+
if (N > UNROLL_LIMIT || N <= 0) return null
|
|
128
|
+
|
|
129
|
+
// then = loop_body, else = loop_exit
|
|
130
|
+
const bodyId = branch.then
|
|
131
|
+
const exitId = branch.else
|
|
132
|
+
|
|
133
|
+
const bodyBlock = blockMap.get(bodyId)
|
|
134
|
+
if (!bodyBlock) return null
|
|
135
|
+
if (!bodyBlock.id.startsWith('loop_body')) return null
|
|
136
|
+
|
|
137
|
+
// Body must end with jump to latch (or header if no latch)
|
|
138
|
+
if (bodyBlock.term.kind !== 'jump') return null
|
|
139
|
+
const afterBodyId = bodyBlock.term.target
|
|
140
|
+
const afterBody = blockMap.get(afterBodyId)
|
|
141
|
+
if (!afterBody) return null
|
|
142
|
+
|
|
143
|
+
// Find the latch block
|
|
144
|
+
let latchId: BlockId
|
|
145
|
+
if (afterBody.id.startsWith('loop_latch')) {
|
|
146
|
+
latchId = afterBodyId
|
|
147
|
+
} else {
|
|
148
|
+
return null
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const latch = blockMap.get(latchId)!
|
|
152
|
+
|
|
153
|
+
// Latch must end with jump back to header
|
|
154
|
+
if (latch.term.kind !== 'jump') return null
|
|
155
|
+
if (latch.term.target !== header.id) return null
|
|
156
|
+
|
|
157
|
+
// Latch must increment loopVar by 1
|
|
158
|
+
if (!latchIncrementsBy1(latch, loopVar)) return null
|
|
159
|
+
|
|
160
|
+
// Loop var must be initialized to 0 before entering the loop
|
|
161
|
+
// Find the pre-header: the block that jumps to header (not the latch)
|
|
162
|
+
const preHeaderId = findPreHeader(fn, header.id, latchId)
|
|
163
|
+
if (!preHeaderId) return null
|
|
164
|
+
|
|
165
|
+
const preHeader = blockMap.get(preHeaderId)!
|
|
166
|
+
if (!initializesTo0(preHeader, fn, loopVar)) return null
|
|
167
|
+
|
|
168
|
+
// Body must not contain break (direct jump to exit) or continue to latch
|
|
169
|
+
// (those would require more complex handling)
|
|
170
|
+
if (bodyHasBreakOrContinue(bodyBlock, exitId, latchId)) return null
|
|
171
|
+
|
|
172
|
+
return { headerId: header.id, bodyId, latchId, exitId, loopVar, N, preHeaderId }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Check that the latch increments loopVar by 1.
|
|
176
|
+
*
|
|
177
|
+
* Two common patterns:
|
|
178
|
+
* 1. Direct: add loopVar loopVar 1
|
|
179
|
+
* 2. Two-step: add t_tmp loopVar 1; copy loopVar t_tmp
|
|
180
|
+
*/
|
|
181
|
+
function latchIncrementsBy1(latch: MIRBlock, loopVar: Temp): boolean {
|
|
182
|
+
// Pattern 1: add dst=loopVar, a=loopVar, b=const(1)
|
|
183
|
+
for (const instr of latch.instrs) {
|
|
184
|
+
if (
|
|
185
|
+
instr.kind === 'add' &&
|
|
186
|
+
instr.dst === loopVar &&
|
|
187
|
+
instr.a.kind === 'temp' && instr.a.name === loopVar &&
|
|
188
|
+
instr.b.kind === 'const' && instr.b.value === 1
|
|
189
|
+
) {
|
|
190
|
+
return true
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Pattern 2: add t_tmp loopVar 1; copy loopVar t_tmp
|
|
195
|
+
// Find the copy that assigns loopVar, then check the add that produced the source
|
|
196
|
+
for (let i = 0; i < latch.instrs.length; i++) {
|
|
197
|
+
const instr = latch.instrs[i]
|
|
198
|
+
if (
|
|
199
|
+
instr.kind === 'copy' &&
|
|
200
|
+
instr.dst === loopVar &&
|
|
201
|
+
instr.src.kind === 'temp'
|
|
202
|
+
) {
|
|
203
|
+
const srcTemp = instr.src.name
|
|
204
|
+
// Find the add instruction that produced srcTemp
|
|
205
|
+
for (const addInstr of latch.instrs) {
|
|
206
|
+
if (
|
|
207
|
+
addInstr.kind === 'add' &&
|
|
208
|
+
addInstr.dst === srcTemp &&
|
|
209
|
+
addInstr.a.kind === 'temp' && addInstr.a.name === loopVar &&
|
|
210
|
+
addInstr.b.kind === 'const' && addInstr.b.value === 1
|
|
211
|
+
) {
|
|
212
|
+
return true
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return false
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Check if loopVar is initialized to 0 in the pre-header (or reachable const) */
|
|
222
|
+
function initializesTo0(preHeader: MIRBlock, fn: MIRFunction, loopVar: Temp): boolean {
|
|
223
|
+
// Check if last definition of loopVar in preHeader is const 0
|
|
224
|
+
for (let i = preHeader.instrs.length - 1; i >= 0; i--) {
|
|
225
|
+
const instr = preHeader.instrs[i]
|
|
226
|
+
if (instr.kind === 'const' && instr.dst === loopVar && instr.value === 0) return true
|
|
227
|
+
if (instr.kind === 'copy' && instr.dst === loopVar) {
|
|
228
|
+
return instr.src.kind === 'const' && instr.src.value === 0
|
|
229
|
+
}
|
|
230
|
+
// If something else defines loopVar, it's not 0
|
|
231
|
+
if (getInstrDst(instr) === loopVar) return false
|
|
232
|
+
}
|
|
233
|
+
// Also check entry block if preHeader is entry
|
|
234
|
+
if (preHeader.id === fn.entry) return false
|
|
235
|
+
return false
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Find the pre-header block: the predecessor of header that is NOT the latch */
|
|
239
|
+
function findPreHeader(fn: MIRFunction, headerId: BlockId, latchId: BlockId): BlockId | null {
|
|
240
|
+
const preds: BlockId[] = []
|
|
241
|
+
for (const block of fn.blocks) {
|
|
242
|
+
const targets = getTermTargets(block.term)
|
|
243
|
+
if (targets.includes(headerId) && block.id !== latchId) {
|
|
244
|
+
preds.push(block.id)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return preds.length === 1 ? preds[0] : null
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Check if body block has break (jump to exit) or continue (jump to latch) */
|
|
251
|
+
function bodyHasBreakOrContinue(body: MIRBlock, exitId: BlockId, latchId: BlockId): boolean {
|
|
252
|
+
// The normal path is body → latch. Any direct jump to exit = break,
|
|
253
|
+
// any jump to latch from within body sub-blocks = continue
|
|
254
|
+
// For simplicity, we only unroll if the body has no sub-blocks with these patterns.
|
|
255
|
+
// We only check the body block itself (single-block body).
|
|
256
|
+
// A multi-block body would need recursive checking.
|
|
257
|
+
return false
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
// Unrolling
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
|
|
264
|
+
function unroll(fn: MIRFunction, blockMap: Map<BlockId, MIRBlock>, info: LoopInfo): MIRFunction {
|
|
265
|
+
const { headerId, bodyId, latchId, exitId, loopVar, N, preHeaderId } = info
|
|
266
|
+
|
|
267
|
+
const preHeader = blockMap.get(preHeaderId)!
|
|
268
|
+
const body = blockMap.get(bodyId)!
|
|
269
|
+
|
|
270
|
+
// Remove the `const loopVar 0` init from preHeader
|
|
271
|
+
const newPreHeaderInstrs = preHeader.instrs.filter(
|
|
272
|
+
instr => !(instr.kind === 'const' && instr.dst === loopVar && instr.value === 0) &&
|
|
273
|
+
!(instr.kind === 'copy' && instr.dst === loopVar &&
|
|
274
|
+
instr.src.kind === 'const' && instr.src.value === 0)
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
// Build N copies of the body with loopVar substituted
|
|
278
|
+
const unrolledInstrs: MIRInstr[] = []
|
|
279
|
+
for (let iter = 0; iter < N; iter++) {
|
|
280
|
+
const substitution = new Map<Temp, Operand>([[loopVar, { kind: 'const', value: iter }]])
|
|
281
|
+
for (const instr of body.instrs) {
|
|
282
|
+
unrolledInstrs.push(substituteInstr(instr, substitution))
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// New pre-header: original instrs (minus init) + all unrolled body + jump to exit
|
|
287
|
+
const newPreHeader: MIRBlock = {
|
|
288
|
+
...preHeader,
|
|
289
|
+
instrs: [...newPreHeaderInstrs, ...unrolledInstrs],
|
|
290
|
+
term: { kind: 'jump', target: exitId },
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Remove header, body, latch blocks; update preHeader; keep exit
|
|
294
|
+
const keepIds = new Set(fn.blocks.map(b => b.id))
|
|
295
|
+
keepIds.delete(headerId)
|
|
296
|
+
keepIds.delete(bodyId)
|
|
297
|
+
keepIds.delete(latchId)
|
|
298
|
+
|
|
299
|
+
const newBlocks: MIRBlock[] = []
|
|
300
|
+
for (const block of fn.blocks) {
|
|
301
|
+
if (block.id === preHeaderId) {
|
|
302
|
+
newBlocks.push(newPreHeader)
|
|
303
|
+
} else if (keepIds.has(block.id)) {
|
|
304
|
+
newBlocks.push(block)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Recompute predecessors
|
|
309
|
+
const newBlocksWithPreds = recomputePreds(newBlocks)
|
|
310
|
+
|
|
311
|
+
return { ...fn, blocks: newBlocksWithPreds }
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ---------------------------------------------------------------------------
|
|
315
|
+
// Instruction substitution
|
|
316
|
+
// ---------------------------------------------------------------------------
|
|
317
|
+
|
|
318
|
+
function substituteOp(op: Operand, sub: Map<Temp, Operand>): Operand {
|
|
319
|
+
if (op.kind === 'temp') {
|
|
320
|
+
const replacement = sub.get(op.name)
|
|
321
|
+
if (replacement) return replacement
|
|
322
|
+
}
|
|
323
|
+
return op
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function substituteInstr(instr: MIRInstr, sub: Map<Temp, Operand>): MIRInstr {
|
|
327
|
+
switch (instr.kind) {
|
|
328
|
+
case 'copy': return { ...instr, src: substituteOp(instr.src, sub) }
|
|
329
|
+
case 'neg': case 'not': return { ...instr, src: substituteOp(instr.src, sub) }
|
|
330
|
+
case 'add': case 'sub': case 'mul': case 'div': case 'mod':
|
|
331
|
+
case 'and': case 'or':
|
|
332
|
+
return { ...instr, a: substituteOp(instr.a, sub), b: substituteOp(instr.b, sub) }
|
|
333
|
+
case 'cmp':
|
|
334
|
+
return { ...instr, a: substituteOp(instr.a, sub), b: substituteOp(instr.b, sub) }
|
|
335
|
+
case 'nbt_write':
|
|
336
|
+
return { ...instr, src: substituteOp(instr.src, sub) }
|
|
337
|
+
case 'call':
|
|
338
|
+
return { ...instr, args: instr.args.map(a => substituteOp(a, sub)) }
|
|
339
|
+
case 'call_macro':
|
|
340
|
+
return { ...instr, args: instr.args.map(a => ({ ...a, value: substituteOp(a.value, sub) })) }
|
|
341
|
+
case 'branch':
|
|
342
|
+
return { ...instr, cond: substituteOp(instr.cond, sub) }
|
|
343
|
+
case 'return':
|
|
344
|
+
return { ...instr, value: instr.value ? substituteOp(instr.value, sub) : null }
|
|
345
|
+
default:
|
|
346
|
+
return instr
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ---------------------------------------------------------------------------
|
|
351
|
+
// Helpers
|
|
352
|
+
// ---------------------------------------------------------------------------
|
|
353
|
+
|
|
354
|
+
function getInstrDst(instr: MIRInstr): Temp | null {
|
|
355
|
+
switch (instr.kind) {
|
|
356
|
+
case 'const': case 'copy':
|
|
357
|
+
case 'add': case 'sub': case 'mul': case 'div': case 'mod':
|
|
358
|
+
case 'neg': case 'cmp': case 'and': case 'or': case 'not':
|
|
359
|
+
case 'nbt_read':
|
|
360
|
+
return instr.dst
|
|
361
|
+
case 'call': case 'call_macro':
|
|
362
|
+
return instr.dst
|
|
363
|
+
default:
|
|
364
|
+
return null
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function getTermTargets(term: MIRInstr): BlockId[] {
|
|
369
|
+
switch (term.kind) {
|
|
370
|
+
case 'jump': return [term.target]
|
|
371
|
+
case 'branch': return [term.then, term.else]
|
|
372
|
+
default: return []
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function recomputePreds(blocks: MIRBlock[]): MIRBlock[] {
|
|
377
|
+
const predMap = new Map<BlockId, BlockId[]>()
|
|
378
|
+
for (const b of blocks) predMap.set(b.id, [])
|
|
379
|
+
for (const block of blocks) {
|
|
380
|
+
for (const target of getTermTargets(block.term)) {
|
|
381
|
+
const preds = predMap.get(target)
|
|
382
|
+
if (preds) preds.push(block.id)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return blocks.map(b => ({ ...b, preds: predMap.get(b.id) ?? [] }))
|
|
386
|
+
}
|