redscript-mc 2.6.2 → 3.0.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/.github/workflows/ci.yml +11 -0
- package/CHANGELOG.md +18 -9
- package/README-benchmarks.md +48 -0
- package/README-vscode-test.md +251 -0
- package/RELEASE_NOTES.md +74 -0
- package/ROADMAP.md +131 -167
- package/benchmarks/_shared.ts +468 -0
- package/benchmarks/baseline.json +2816 -0
- package/benchmarks/baseline.md +13 -0
- package/benchmarks/compiler-perf.report.json +207 -0
- package/benchmarks/compiler-perf.ts +76 -0
- package/benchmarks/results.md +13 -0
- package/benchmarks/stdlib-complexity.report.json +2606 -0
- package/benchmarks/stdlib-complexity.ts +54 -0
- package/benchmarks/stdlib-size.md +57 -0
- package/benchmarks/stdlib-size.ts +91 -0
- package/coverage-report.md +177 -0
- package/dist/src/__tests__/budget.test.js +4 -0
- package/dist/src/__tests__/cache/cache-behavior.test.d.ts +10 -0
- package/dist/src/__tests__/cache/cache-behavior.test.js +425 -0
- package/dist/src/__tests__/cache-extra.test.d.ts +5 -0
- package/dist/src/__tests__/cache-extra.test.js +211 -0
- package/dist/src/__tests__/cli-init.test.d.ts +1 -0
- package/dist/src/__tests__/cli-init.test.js +97 -0
- package/dist/src/__tests__/cli-publish.test.d.ts +9 -0
- package/dist/src/__tests__/cli-publish.test.js +189 -0
- package/dist/src/__tests__/cli.test.js +76 -0
- package/dist/src/__tests__/compile-preprocess.test.d.ts +11 -0
- package/dist/src/__tests__/compile-preprocess.test.js +328 -0
- package/dist/src/__tests__/compiler/break-stmt.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/break-stmt.test.js +58 -0
- package/dist/src/__tests__/compiler/const-decl.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/const-decl.test.js +123 -0
- package/dist/src/__tests__/compiler/continue-stmt.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/continue-stmt.test.js +67 -0
- package/dist/src/__tests__/compiler/coroutine-extended.test.d.ts +17 -0
- package/dist/src/__tests__/compiler/coroutine-extended.test.js +565 -0
- package/dist/src/__tests__/compiler/deprecated.test.d.ts +4 -0
- package/dist/src/__tests__/compiler/deprecated.test.js +285 -0
- package/dist/src/__tests__/compiler/do-while.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/do-while.test.js +120 -0
- package/dist/src/__tests__/compiler/enum-payload.test.d.ts +9 -0
- package/dist/src/__tests__/compiler/enum-payload.test.js +272 -0
- package/dist/src/__tests__/compiler/interface.test.d.ts +10 -0
- package/dist/src/__tests__/compiler/interface.test.js +258 -0
- package/dist/src/__tests__/compiler/labeled-loops.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/labeled-loops.test.js +263 -0
- package/dist/src/__tests__/compiler/match-string.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/match-string.test.js +43 -0
- package/dist/src/__tests__/compiler/memoize.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/memoize.test.js +113 -0
- package/dist/src/__tests__/compiler/method-chain.test.d.ts +5 -0
- package/dist/src/__tests__/compiler/method-chain.test.js +115 -0
- package/dist/src/__tests__/compiler/module-import.test.d.ts +12 -0
- package/dist/src/__tests__/compiler/module-import.test.js +261 -0
- package/dist/src/__tests__/compiler/option-extensions.test.d.ts +6 -0
- package/dist/src/__tests__/compiler/option-extensions.test.js +191 -0
- package/dist/src/__tests__/compiler/profile-decorator.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/profile-decorator.test.js +69 -0
- package/dist/src/__tests__/compiler/string-advanced.test.d.ts +7 -0
- package/dist/src/__tests__/compiler/string-advanced.test.js +281 -0
- package/dist/src/__tests__/compiler/struct-extends.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/struct-extends.test.js +95 -0
- package/dist/src/__tests__/compiler/throttle-retry.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/throttle-retry.test.js +166 -0
- package/dist/src/__tests__/compiler/tuple-type.test.d.ts +10 -0
- package/dist/src/__tests__/compiler/tuple-type.test.js +229 -0
- package/dist/src/__tests__/compiler/watch-decorator.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/watch-decorator.test.js +65 -0
- package/dist/src/__tests__/config/project-config.test.d.ts +1 -0
- package/dist/src/__tests__/config/project-config.test.js +199 -0
- package/dist/src/__tests__/config-decorator.test.d.ts +8 -0
- package/dist/src/__tests__/config-decorator.test.js +142 -0
- package/dist/src/__tests__/diagnostics-extra.test.d.ts +6 -0
- package/dist/src/__tests__/diagnostics-extra.test.js +132 -0
- package/dist/src/__tests__/emit/compile-branches.test.d.ts +1 -0
- package/dist/src/__tests__/emit/compile-branches.test.js +123 -0
- package/dist/src/__tests__/emit/compile-coverage.test.d.ts +25 -0
- package/dist/src/__tests__/emit/compile-coverage.test.js +617 -0
- package/dist/src/__tests__/emit/compile-extra-branches.test.d.ts +12 -0
- package/dist/src/__tests__/emit/compile-extra-branches.test.js +225 -0
- package/dist/src/__tests__/emit/compile-mocked-branches.test.d.ts +0 -0
- package/dist/src/__tests__/emit/compile-mocked-branches.test.js +238 -0
- package/dist/src/__tests__/emit/execute-chain.test.d.ts +10 -0
- package/dist/src/__tests__/emit/execute-chain.test.js +94 -0
- package/dist/src/__tests__/emit/index.test.js +2 -1
- package/dist/src/__tests__/emit/modules-branches.test.d.ts +1 -0
- package/dist/src/__tests__/emit/modules-branches.test.js +88 -0
- package/dist/src/__tests__/emit/modules-coverage.test.d.ts +15 -0
- package/dist/src/__tests__/emit/modules-coverage.test.js +221 -0
- package/dist/src/__tests__/emit/modules-errors.test.d.ts +12 -0
- package/dist/src/__tests__/emit/modules-errors.test.js +169 -0
- package/dist/src/__tests__/emit/modules-rewrite.test.d.ts +17 -0
- package/dist/src/__tests__/emit/modules-rewrite.test.js +204 -0
- package/dist/src/__tests__/emit/source-map.test.d.ts +1 -0
- package/dist/src/__tests__/emit/source-map.test.js +167 -0
- package/dist/src/__tests__/enum.test.js +9 -4
- package/dist/src/__tests__/error-recovery.test.d.ts +7 -0
- package/dist/src/__tests__/error-recovery.test.js +217 -0
- package/dist/src/__tests__/events-types-extra.test.d.ts +10 -0
- package/dist/src/__tests__/events-types-extra.test.js +91 -0
- package/dist/src/__tests__/events-types.test.d.ts +4 -0
- package/dist/src/__tests__/events-types.test.js +56 -0
- package/dist/src/__tests__/formatter.test.js +13 -5
- package/dist/src/__tests__/hir/lower-extra.test.d.ts +9 -0
- package/dist/src/__tests__/hir/lower-extra.test.js +140 -0
- package/dist/src/__tests__/hir/monomorphize-extra.test.d.ts +15 -0
- package/dist/src/__tests__/hir/monomorphize-extra.test.js +200 -0
- package/dist/src/__tests__/hir/monomorphize-extra2.test.d.ts +16 -0
- package/dist/src/__tests__/hir/monomorphize-extra2.test.js +316 -0
- package/dist/src/__tests__/incremental.test.js +10 -2
- package/dist/src/__tests__/index-extra.test.d.ts +10 -0
- package/dist/src/__tests__/index-extra.test.js +71 -0
- package/dist/src/__tests__/lexer.test.js +2 -2
- package/dist/src/__tests__/lint/rules.test.d.ts +5 -0
- package/dist/src/__tests__/lint/rules.test.js +208 -0
- package/dist/src/__tests__/lir/lower.test.js +29 -0
- package/dist/src/__tests__/lir/verify.test.js +30 -0
- package/dist/src/__tests__/lsp/completion.test.d.ts +7 -0
- package/dist/src/__tests__/lsp/completion.test.js +583 -0
- package/dist/src/__tests__/lsp/definition.test.d.ts +7 -0
- package/dist/src/__tests__/lsp/definition.test.js +454 -0
- package/dist/src/__tests__/lsp/diagnostics.test.d.ts +10 -0
- package/dist/src/__tests__/lsp/diagnostics.test.js +98 -0
- package/dist/src/__tests__/lsp/hover-docs.test.d.ts +10 -0
- package/dist/src/__tests__/lsp/hover-docs.test.js +210 -0
- package/dist/src/__tests__/lsp.test.js +4 -1
- package/dist/src/__tests__/mc-integration/item-entity-events.test.js +4 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-2.test.js +4 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.d.ts +13 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.js +1227 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.d.ts +13 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.js +1509 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.d.ts +14 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.js +1374 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.d.ts +10 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.js +759 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.d.ts +13 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.js +855 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage.test.js +4 -0
- package/dist/src/__tests__/mc-integration/syntax-coverage.test.js +4 -0
- package/dist/src/__tests__/mc-validator-coverage.test.d.ts +13 -0
- package/dist/src/__tests__/mc-validator-coverage.test.js +296 -0
- package/dist/src/__tests__/mc-validator-extra.test.d.ts +13 -0
- package/dist/src/__tests__/mc-validator-extra.test.js +245 -0
- package/dist/src/__tests__/mir/lower-extra.test.d.ts +20 -0
- package/dist/src/__tests__/mir/lower-extra.test.js +361 -0
- package/dist/src/__tests__/mir/lower-extra2.test.d.ts +17 -0
- package/dist/src/__tests__/mir/lower-extra2.test.js +317 -0
- package/dist/src/__tests__/mir/lower-extra3.test.d.ts +19 -0
- package/dist/src/__tests__/mir/lower-extra3.test.js +249 -0
- package/dist/src/__tests__/mir/lower-extra4.test.d.ts +23 -0
- package/dist/src/__tests__/mir/lower-extra4.test.js +606 -0
- package/dist/src/__tests__/mir/lower-extra5.test.d.ts +25 -0
- package/dist/src/__tests__/mir/lower-extra5.test.js +543 -0
- package/dist/src/__tests__/mir/lower-extra6.test.d.ts +16 -0
- package/dist/src/__tests__/mir/lower-extra6.test.js +471 -0
- package/dist/src/__tests__/mir/lower-extra7.test.d.ts +35 -0
- package/dist/src/__tests__/mir/lower-extra7.test.js +921 -0
- package/dist/src/__tests__/mir/lower-extra8.test.d.ts +19 -0
- package/dist/src/__tests__/mir/lower-extra8.test.js +626 -0
- package/dist/src/__tests__/mir/lower-extra9.test.d.ts +14 -0
- package/dist/src/__tests__/mir/lower-extra9.test.js +717 -0
- package/dist/src/__tests__/optimizer/auto-inline.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/auto-inline.test.js +176 -0
- package/dist/src/__tests__/optimizer/cse.test.d.ts +4 -0
- package/dist/src/__tests__/optimizer/cse.test.js +178 -0
- package/dist/src/__tests__/optimizer/inline_fn.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/inline_fn.test.js +221 -0
- package/dist/src/__tests__/optimizer/licm.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/licm.test.js +244 -0
- package/dist/src/__tests__/optimizer/optimizer-extended.test.d.ts +12 -0
- package/dist/src/__tests__/optimizer/optimizer-extended.test.js +993 -0
- package/dist/src/__tests__/optimizer/strength-reduction.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/strength-reduction.test.js +86 -0
- package/dist/src/__tests__/optimizer/tco.test.d.ts +14 -0
- package/dist/src/__tests__/optimizer/tco.test.js +203 -0
- package/dist/src/__tests__/parser-coverage.test.d.ts +25 -0
- package/dist/src/__tests__/parser-coverage.test.js +491 -0
- package/dist/src/__tests__/parser-extra.test.d.ts +6 -0
- package/dist/src/__tests__/parser-extra.test.js +451 -0
- package/dist/src/__tests__/parser.test.js +12 -0
- package/dist/src/__tests__/repl-extra.test.d.ts +13 -0
- package/dist/src/__tests__/repl-extra.test.js +174 -0
- package/dist/src/__tests__/repl-server-extra.test.d.ts +10 -0
- package/dist/src/__tests__/repl-server-extra.test.js +161 -0
- package/dist/src/__tests__/repl-server.test.d.ts +6 -0
- package/dist/src/__tests__/repl-server.test.js +146 -0
- package/dist/src/__tests__/runtime-extra.test.d.ts +15 -0
- package/dist/src/__tests__/runtime-extra.test.js +732 -0
- package/dist/src/__tests__/singleton-decorator.test.d.ts +11 -0
- package/dist/src/__tests__/singleton-decorator.test.js +260 -0
- package/dist/src/__tests__/sourcemap.test.js +1 -1
- package/dist/src/__tests__/stdlib/advanced.test.d.ts +5 -0
- package/dist/src/__tests__/stdlib/advanced.test.js +301 -0
- package/dist/src/__tests__/stdlib/bigint.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/bigint.test.js +83 -0
- package/dist/src/__tests__/stdlib/bits.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/bits.test.js +96 -0
- package/dist/src/__tests__/stdlib/bossbar.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/bossbar.test.js +72 -0
- package/dist/src/__tests__/stdlib/color.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/color.test.js +84 -0
- package/dist/src/__tests__/stdlib/combat.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/combat.test.js +64 -0
- package/dist/src/__tests__/stdlib/cooldown.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/cooldown.test.js +64 -0
- package/dist/src/__tests__/stdlib/dialog.test.js +15 -7
- package/dist/src/__tests__/stdlib/ecs.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/ecs.test.js +81 -0
- package/dist/src/__tests__/stdlib/effects.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/effects.test.js +72 -0
- package/dist/src/__tests__/stdlib/events.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/events.test.js +55 -0
- package/dist/src/__tests__/stdlib/expr.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/expr.test.js +77 -0
- package/dist/src/__tests__/stdlib/fft.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/fft.test.js +82 -0
- package/dist/src/__tests__/stdlib/graph.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/graph.test.js +102 -0
- package/dist/src/__tests__/stdlib/interactions.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/interactions.test.js +60 -0
- package/dist/src/__tests__/stdlib/inventory.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/inventory.test.js +68 -0
- package/dist/src/__tests__/stdlib/linalg.test.d.ts +5 -0
- package/dist/src/__tests__/stdlib/linalg.test.js +78 -0
- package/dist/src/__tests__/stdlib/map.test.d.ts +1 -0
- package/dist/src/__tests__/stdlib/map.test.js +84 -0
- package/dist/src/__tests__/stdlib/math.test.js +19 -6
- package/dist/src/__tests__/stdlib/math_hp.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/math_hp.test.js +80 -0
- package/dist/src/__tests__/stdlib/mobs.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/mobs.test.js +61 -0
- package/dist/src/__tests__/stdlib/noise.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/noise.test.js +73 -0
- package/dist/src/__tests__/stdlib/ode.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/ode.test.js +68 -0
- package/dist/src/__tests__/stdlib/parabola.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/parabola.test.js +77 -0
- package/dist/src/__tests__/stdlib/particles.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/particles.test.js +68 -0
- package/dist/src/__tests__/stdlib/physics.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/physics.test.js +76 -0
- package/dist/src/__tests__/stdlib/player.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/player.test.js +64 -0
- package/dist/src/__tests__/stdlib/quaternion.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/quaternion.test.js +73 -0
- package/dist/src/__tests__/stdlib/queue.test.d.ts +1 -0
- package/dist/src/__tests__/stdlib/queue.test.js +97 -0
- package/dist/src/__tests__/stdlib/random.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/random.test.js +76 -0
- package/dist/src/__tests__/stdlib/result.test.d.ts +12 -0
- package/dist/src/__tests__/stdlib/result.test.js +329 -0
- package/dist/src/__tests__/stdlib/scheduler.test.js +19 -8
- package/dist/src/__tests__/stdlib/set_int.test.d.ts +1 -0
- package/dist/src/__tests__/stdlib/set_int.test.js +88 -0
- package/dist/src/__tests__/stdlib/sets.test.d.ts +6 -0
- package/dist/src/__tests__/stdlib/sets.test.js +60 -0
- package/dist/src/__tests__/stdlib/signal.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/signal.test.js +84 -0
- package/dist/src/__tests__/stdlib/spawn.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/spawn.test.js +68 -0
- package/dist/src/__tests__/stdlib/string.test.d.ts +12 -0
- package/dist/src/__tests__/stdlib/string.test.js +231 -0
- package/dist/src/__tests__/stdlib/strings.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/strings.test.js +83 -0
- package/dist/src/__tests__/stdlib/tags.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/tags.test.js +57 -0
- package/dist/src/__tests__/stdlib/teams.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/teams.test.js +72 -0
- package/dist/src/__tests__/stdlib/timer.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/timer.test.js +79 -0
- package/dist/src/__tests__/stdlib/vec.test.d.ts +5 -0
- package/dist/src/__tests__/stdlib/vec.test.js +94 -0
- package/dist/src/__tests__/stdlib/world.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/world.test.js +72 -0
- package/dist/src/__tests__/struct-display.test.d.ts +1 -0
- package/dist/src/__tests__/struct-display.test.js +64 -0
- package/dist/src/__tests__/test-framework/runner.test.d.ts +10 -0
- package/dist/src/__tests__/test-framework/runner.test.js +193 -0
- package/dist/src/__tests__/tuner/adapters.test.d.ts +14 -0
- package/dist/src/__tests__/tuner/adapters.test.js +194 -0
- package/dist/src/__tests__/tuner/simulator-extra.test.d.ts +4 -0
- package/dist/src/__tests__/tuner/simulator-extra.test.js +193 -0
- package/dist/src/__tests__/typechecker-coverage.test.d.ts +30 -0
- package/dist/src/__tests__/typechecker-coverage.test.js +627 -0
- package/dist/src/__tests__/typechecker.test.js +3 -3
- package/dist/src/__tests__/watch-decorator.test.d.ts +1 -0
- package/dist/src/__tests__/watch-decorator.test.js +54 -0
- package/dist/src/ast/types.d.ts +102 -3
- package/dist/src/cache/incremental.d.ts +13 -14
- package/dist/src/cache/incremental.js +106 -89
- package/dist/src/cache/index.d.ts +8 -2
- package/dist/src/cache/index.js +18 -6
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.js +466 -17
- package/dist/src/config/project-config.d.ts +29 -0
- package/dist/src/config/project-config.js +180 -0
- package/dist/src/diagnostics/index.d.ts +9 -0
- package/dist/src/diagnostics/index.js +18 -1
- package/dist/src/emit/compile.d.ts +10 -0
- package/dist/src/emit/compile.js +395 -50
- package/dist/src/emit/index.d.ts +40 -0
- package/dist/src/emit/index.js +307 -14
- package/dist/src/emit/modules.js +21 -3
- package/dist/src/emit/sourcemap.d.ts +23 -27
- package/dist/src/emit/sourcemap.js +52 -30
- package/dist/src/formatter/index.js +33 -8
- package/dist/src/hir/deprecated.d.ts +13 -0
- package/dist/src/hir/deprecated.js +218 -0
- package/dist/src/hir/lower.js +114 -8
- package/dist/src/hir/monomorphize.js +22 -2
- package/dist/src/hir/types.d.ts +65 -1
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +18 -3
- package/dist/src/lexer/index.d.ts +2 -1
- package/dist/src/lexer/index.js +39 -3
- package/dist/src/lint/index.d.ts +45 -0
- package/dist/src/lint/index.js +930 -0
- package/dist/src/lir/lower.js +29 -2
- package/dist/src/lir/types.d.ts +2 -0
- package/dist/src/lsp/server.js +92 -5
- package/dist/src/mir/lower.js +775 -34
- package/dist/src/mir/macro.js +36 -2
- package/dist/src/mir/types.d.ts +12 -0
- package/dist/src/mir/verify.js +9 -0
- package/dist/src/optimizer/auto-inline.d.ts +2 -0
- package/dist/src/optimizer/auto-inline.js +67 -0
- package/dist/src/optimizer/cse.d.ts +20 -0
- package/dist/src/optimizer/cse.js +234 -0
- package/dist/src/optimizer/inline.d.ts +26 -0
- package/dist/src/optimizer/inline.js +286 -0
- package/dist/src/optimizer/interprocedural.js +4 -0
- package/dist/src/optimizer/licm.d.ts +32 -0
- package/dist/src/optimizer/licm.js +371 -0
- package/dist/src/optimizer/pipeline.js +12 -2
- package/dist/src/optimizer/strength_reduction.d.ts +15 -0
- package/dist/src/optimizer/strength_reduction.js +90 -0
- package/dist/src/optimizer/tco.d.ts +53 -0
- package/dist/src/optimizer/tco.js +238 -0
- package/dist/src/parser/index.d.ts +32 -0
- package/dist/src/parser/index.js +421 -59
- package/dist/src/repl-server.d.ts +13 -0
- package/dist/src/repl-server.js +127 -0
- package/dist/src/structs/expand.d.ts +15 -0
- package/dist/src/structs/expand.js +46 -0
- package/dist/src/testing/runner.d.ts +40 -0
- package/dist/src/testing/runner.js +237 -0
- package/dist/src/typechecker/index.d.ts +3 -0
- package/dist/src/typechecker/index.js +254 -9
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/doc-drafts/redscript-docs/docs/en/stdlib/graph.md +104 -0
- package/doc-drafts/redscript-docs/docs/en/stdlib/parabola.md +113 -0
- package/doc-drafts/redscript-docs/docs/en/stdlib/pathfind.md +104 -0
- package/doc-drafts/redscript-docs/docs/en/stdlib/physics.md +134 -0
- package/doc-drafts/redscript-docs/docs/en/stdlib/quaternion.md +135 -0
- package/doc-drafts/redscript-docs/docs/zh/stdlib/graph.md +104 -0
- package/doc-drafts/redscript-docs/docs/zh/stdlib/parabola.md +113 -0
- package/doc-drafts/redscript-docs/docs/zh/stdlib/pathfind.md +104 -0
- package/doc-drafts/redscript-docs/docs/zh/stdlib/physics.md +134 -0
- package/doc-drafts/redscript-docs/docs/zh/stdlib/quaternion.md +135 -0
- package/docs/stdlib/result.md +156 -0
- package/docs/stdlib/result.zh.md +156 -0
- package/editors/vscode/fixtures/test.mcrs +7 -0
- package/editors/vscode/out/extension.js +2095 -225
- package/editors/vscode/out/lsp-server.js +519 -51
- package/editors/vscode/package-lock.json +9 -4
- package/editors/vscode/package.json +1 -1
- package/examples/display-demo.mcrs +64 -0
- package/examples/game/racing.mcrs +301 -0
- package/examples/game/tower_defense.mcrs +311 -0
- package/examples/math/physics_sim.mcrs +322 -0
- package/examples/rpg/boss_fight.mcrs +313 -0
- package/examples/rpg/health_system.mcrs +237 -0
- package/examples/rpg/inventory.mcrs +265 -0
- package/examples/util/debug_hud.mcrs +279 -0
- package/jest.config.js +10 -0
- package/package.json +12 -3
- package/playground/index.html +823 -0
- package/scripts/gen-docs.ts +533 -0
- package/scripts/update-redscript-docs-stdlib.sh +770 -0
- package/src/__tests__/budget.test.ts +5 -0
- package/src/__tests__/cache/cache-behavior.test.ts +480 -0
- package/src/__tests__/cache-extra.test.ts +199 -0
- package/src/__tests__/cli-docs.test.ts +77 -0
- package/src/__tests__/cli-init.test.ts +91 -0
- package/src/__tests__/cli-publish.test.ts +190 -0
- package/src/__tests__/cli.test.ts +117 -1
- package/src/__tests__/compile-preprocess.test.ts +366 -0
- package/src/__tests__/compiler/break-stmt.test.ts +66 -0
- package/src/__tests__/compiler/const-decl.test.ts +141 -0
- package/src/__tests__/compiler/continue-stmt.test.ts +81 -0
- package/src/__tests__/compiler/coroutine-extended.test.ts +723 -0
- package/src/__tests__/compiler/deprecated.test.ts +305 -0
- package/src/__tests__/compiler/do-while.test.ts +130 -0
- package/src/__tests__/compiler/enum-payload.test.ts +299 -0
- package/src/__tests__/compiler/interface.test.ts +287 -0
- package/src/__tests__/compiler/labeled-loops.test.ts +279 -0
- package/src/__tests__/compiler/match-string.test.ts +45 -0
- package/src/__tests__/compiler/memoize.test.ts +126 -0
- package/src/__tests__/compiler/method-chain.test.ts +121 -0
- package/src/__tests__/compiler/module-import.test.ts +240 -0
- package/src/__tests__/compiler/option-extensions.test.ts +207 -0
- package/src/__tests__/compiler/profile-decorator.test.ts +79 -0
- package/src/__tests__/compiler/string-advanced.test.ts +310 -0
- package/src/__tests__/compiler/struct-extends.test.ts +109 -0
- package/src/__tests__/compiler/throttle-retry.test.ts +191 -0
- package/src/__tests__/compiler/tuple-type.test.ts +263 -0
- package/src/__tests__/compiler/watch-decorator.test.ts +72 -0
- package/src/__tests__/config/project-config.test.ts +181 -0
- package/src/__tests__/config-decorator.test.ts +157 -0
- package/src/__tests__/diagnostics-extra.test.ts +155 -0
- package/src/__tests__/emit/compile-branches.test.ts +135 -0
- package/src/__tests__/emit/compile-coverage.test.ts +696 -0
- package/src/__tests__/emit/compile-extra-branches.test.ts +228 -0
- package/src/__tests__/emit/compile-mocked-branches.test.ts +249 -0
- package/src/__tests__/emit/compile.test.ts +6 -1
- package/src/__tests__/emit/execute-chain.test.ts +114 -0
- package/src/__tests__/emit/index.test.ts +2 -1
- package/src/__tests__/emit/modules-branches.test.ts +90 -0
- package/src/__tests__/emit/modules-coverage.test.ts +241 -0
- package/src/__tests__/emit/modules-errors.test.ts +192 -0
- package/src/__tests__/emit/modules-rewrite.test.ts +232 -0
- package/src/__tests__/emit/source-map.test.ts +152 -0
- package/src/__tests__/enum.test.ts +9 -4
- package/src/__tests__/error-recovery.test.ts +226 -0
- package/src/__tests__/events-types-extra.test.ts +110 -0
- package/src/__tests__/events-types.test.ts +66 -0
- package/src/__tests__/formatter.test.ts +15 -5
- package/src/__tests__/generics.test.ts +16 -9
- package/src/__tests__/hir/lower-extra.test.ts +151 -0
- package/src/__tests__/hir/monomorphize-coverage.test.ts +432 -0
- package/src/__tests__/hir/monomorphize-extra.test.ts +220 -0
- package/src/__tests__/hir/monomorphize-extra2.test.ts +350 -0
- package/src/__tests__/impl.test.ts +12 -8
- package/src/__tests__/incremental.test.ts +10 -2
- package/src/__tests__/index-extra.test.ts +79 -0
- package/src/__tests__/lexer.test.ts +2 -2
- package/src/__tests__/lint/hir-coverage.test.ts +1716 -0
- package/src/__tests__/lint/rules-coverage.test.ts +598 -0
- package/src/__tests__/lint/rules.test.ts +230 -0
- package/src/__tests__/lir/lower.test.ts +33 -0
- package/src/__tests__/lir/verify.test.ts +33 -0
- package/src/__tests__/lsp/completion.test.ts +687 -0
- package/src/__tests__/lsp/definition.test.ts +499 -0
- package/src/__tests__/lsp/diagnostics.test.ts +108 -0
- package/src/__tests__/lsp/hover-docs.test.ts +222 -0
- package/src/__tests__/lsp.test.ts +4 -1
- package/src/__tests__/mc-integration/item-entity-events.test.ts +5 -0
- package/src/__tests__/mc-integration/stdlib-coverage-2.test.ts +5 -0
- package/src/__tests__/mc-integration/stdlib-coverage-3.test.ts +1105 -0
- package/src/__tests__/mc-integration/stdlib-coverage-4.test.ts +1366 -0
- package/src/__tests__/mc-integration/stdlib-coverage-5.test.ts +1245 -0
- package/src/__tests__/mc-integration/stdlib-coverage-6.test.ts +755 -0
- package/src/__tests__/mc-integration/stdlib-coverage-7.test.ts +771 -0
- package/src/__tests__/mc-integration/stdlib-coverage.test.ts +5 -0
- package/src/__tests__/mc-integration/syntax-coverage.test.ts +5 -0
- package/src/__tests__/mc-validator-coverage.test.ts +325 -0
- package/src/__tests__/mc-validator-extra.test.ts +252 -0
- package/src/__tests__/mir/lower-extra.test.ts +402 -0
- package/src/__tests__/mir/lower-extra2.test.ts +348 -0
- package/src/__tests__/mir/lower-extra3.test.ts +277 -0
- package/src/__tests__/mir/lower-extra4.test.ts +636 -0
- package/src/__tests__/mir/lower-extra5.test.ts +612 -0
- package/src/__tests__/mir/lower-extra6.test.ts +520 -0
- package/src/__tests__/mir/lower-extra7.test.ts +1045 -0
- package/src/__tests__/mir/lower-extra8.test.ts +704 -0
- package/src/__tests__/mir/lower-extra9.test.ts +821 -0
- package/src/__tests__/optimizer/auto-inline.test.ts +206 -0
- package/src/__tests__/optimizer/cse.test.ts +195 -0
- package/src/__tests__/optimizer/inline_fn.test.ts +263 -0
- package/src/__tests__/optimizer/licm.test.ts +358 -0
- package/src/__tests__/optimizer/nbt-coalesce.test.ts +147 -0
- package/src/__tests__/optimizer/optimizer-extended.test.ts +1081 -0
- package/src/__tests__/optimizer/scoreboard-batch.test.ts +141 -0
- package/src/__tests__/optimizer/strength-reduction.test.ts +111 -0
- package/src/__tests__/optimizer/tco-coverage.test.ts +309 -0
- package/src/__tests__/optimizer/tco.test.ts +238 -0
- package/src/__tests__/option.test.ts +14 -7
- package/src/__tests__/parser-coverage.test.ts +576 -0
- package/src/__tests__/parser-extra.test.ts +531 -0
- package/src/__tests__/parser.test.ts +14 -0
- package/src/__tests__/repl-extra.test.ts +195 -0
- package/src/__tests__/repl-server-extra.test.ts +150 -0
- package/src/__tests__/repl-server.test.ts +122 -0
- package/src/__tests__/runtime-extra.test.ts +862 -0
- package/src/__tests__/singleton-decorator.test.ts +285 -0
- package/src/__tests__/sourcemap.test.ts +1 -1
- package/src/__tests__/stdlib/advanced.test.ts +312 -0
- package/src/__tests__/stdlib/bigint.test.ts +57 -0
- package/src/__tests__/stdlib/bits.test.ts +75 -0
- package/src/__tests__/stdlib/bossbar.test.ts +45 -0
- package/src/__tests__/stdlib/color.test.ts +60 -0
- package/src/__tests__/stdlib/combat.test.ts +35 -0
- package/src/__tests__/stdlib/cooldown.test.ts +35 -0
- package/src/__tests__/stdlib/dialog.test.ts +14 -6
- package/src/__tests__/stdlib/ecs.test.ts +54 -0
- package/src/__tests__/stdlib/effects.test.ts +45 -0
- package/src/__tests__/stdlib/events.test.ts +23 -0
- package/src/__tests__/stdlib/expr.test.ts +48 -0
- package/src/__tests__/stdlib/fft.test.ts +54 -0
- package/src/__tests__/stdlib/graph.test.ts +77 -0
- package/src/__tests__/stdlib/interactions.test.ts +30 -0
- package/src/__tests__/stdlib/inventory.test.ts +40 -0
- package/src/__tests__/stdlib/linalg.test.ts +52 -0
- package/src/__tests__/stdlib/map.test.ts +55 -0
- package/src/__tests__/stdlib/math.test.ts +19 -5
- package/src/__tests__/stdlib/math_hp.test.ts +55 -0
- package/src/__tests__/stdlib/mobs.test.ts +40 -0
- package/src/__tests__/stdlib/noise.test.ts +46 -0
- package/src/__tests__/stdlib/ode.test.ts +40 -0
- package/src/__tests__/stdlib/parabola.test.ts +51 -0
- package/src/__tests__/stdlib/particles.test.ts +40 -0
- package/src/__tests__/stdlib/physics.test.ts +50 -0
- package/src/__tests__/stdlib/player.test.ts +35 -0
- package/src/__tests__/stdlib/quaternion.test.ts +46 -0
- package/src/__tests__/stdlib/queue.test.ts +73 -0
- package/src/__tests__/stdlib/random.test.ts +50 -0
- package/src/__tests__/stdlib/result.test.ts +326 -0
- package/src/__tests__/stdlib/scheduler.test.ts +18 -7
- package/src/__tests__/stdlib/set_int.test.ts +62 -0
- package/src/__tests__/stdlib/sets.test.ts +28 -0
- package/src/__tests__/stdlib/signal.test.ts +60 -0
- package/src/__tests__/stdlib/spawn.test.ts +40 -0
- package/src/__tests__/stdlib/string.test.ts +224 -0
- package/src/__tests__/stdlib/strings.test.ts +55 -0
- package/src/__tests__/stdlib/tags.test.ts +32 -0
- package/src/__tests__/stdlib/teams.test.ts +45 -0
- package/src/__tests__/stdlib/timer.test.ts +53 -0
- package/src/__tests__/stdlib/vec.test.ts +72 -0
- package/src/__tests__/stdlib/world.test.ts +45 -0
- package/src/__tests__/struct-display.test.ts +69 -0
- package/src/__tests__/test-framework/runner.test.ts +208 -0
- package/src/__tests__/tuner/adapters.test.ts +232 -0
- package/src/__tests__/tuner/simulator-extra.test.ts +222 -0
- package/src/__tests__/tuple.test.ts +11 -4
- package/src/__tests__/typechecker-coverage.test.ts +671 -0
- package/src/__tests__/typechecker.test.ts +4 -3
- package/src/__tests__/watch-decorator.test.ts +59 -0
- package/src/ast/types.ts +65 -3
- package/src/cache/incremental.ts +128 -99
- package/src/cache/index.ts +35 -8
- package/src/cli.ts +538 -29
- package/src/config/project-config.ts +176 -0
- package/src/diagnostics/index.ts +22 -0
- package/src/docs.ts +98 -0
- package/src/emit/compile.ts +408 -51
- package/src/emit/index.ts +366 -18
- package/src/emit/modules.ts +19 -3
- package/src/emit/sourcemap.ts +64 -43
- package/src/formatter/index.ts +35 -8
- package/src/hir/deprecated.ts +212 -0
- package/src/hir/lower.ts +128 -8
- package/src/hir/monomorphize.ts +24 -2
- package/src/hir/types.ts +26 -1
- package/src/index.ts +23 -3
- package/src/lexer/index.ts +45 -6
- package/src/lint/index.ts +922 -0
- package/src/lir/lower.ts +30 -2
- package/src/lir/types.ts +4 -0
- package/src/lsp/server.ts +100 -1
- package/src/mir/lower.ts +785 -40
- package/src/mir/macro.ts +30 -2
- package/src/mir/types.ts +13 -0
- package/src/mir/verify.ts +10 -2
- package/src/optimizer/auto-inline.ts +86 -0
- package/src/optimizer/copy_prop.ts +2 -2
- package/src/optimizer/coroutine.ts +3 -3
- package/src/optimizer/cse.ts +205 -0
- package/src/optimizer/dce.ts +2 -2
- package/src/optimizer/inline.ts +335 -0
- package/src/optimizer/interprocedural.ts +5 -1
- package/src/optimizer/licm.ts +454 -0
- package/src/optimizer/nbt-coalesce.ts +109 -0
- package/src/optimizer/pipeline.ts +16 -2
- package/src/optimizer/scoreboard-batch.ts +52 -0
- package/src/optimizer/strength_reduction.ts +95 -0
- package/src/optimizer/tco.ts +267 -0
- package/src/optimizer/unroll.ts +2 -2
- package/src/parser/index.ts +426 -53
- package/src/repl-server.ts +102 -0
- package/src/stdlib/advanced.mcrs +271 -101
- package/src/stdlib/bigint.mcrs +97 -11
- package/src/stdlib/bits.mcrs +75 -12
- package/src/stdlib/bossbar.mcrs +37 -8
- package/src/stdlib/calculus.mcrs +82 -26
- package/src/stdlib/color.mcrs +98 -16
- package/src/stdlib/combat.mcrs +23 -5
- package/src/stdlib/cooldown.mcrs +19 -0
- package/src/stdlib/dialog.mcrs +45 -7
- package/src/stdlib/easing.mcrs +132 -12
- package/src/stdlib/ecs.mcrs +142 -25
- package/src/stdlib/effects.mcrs +88 -12
- package/src/stdlib/events.mcrs +21 -2
- package/src/stdlib/expr.mcrs +18 -3
- package/src/stdlib/fft.mcrs +66 -56
- package/src/stdlib/geometry.mcrs +137 -39
- package/src/stdlib/graph.mcrs +73 -0
- package/src/stdlib/heap.mcrs +49 -8
- package/src/stdlib/i18n/zh.yaml +2891 -0
- package/src/stdlib/interactions.mcrs +43 -20
- package/src/stdlib/inventory.mcrs +14 -3
- package/src/stdlib/linalg.mcrs +185 -30
- package/src/stdlib/list.mcrs +168 -18
- package/src/stdlib/map.mcrs +112 -0
- package/src/stdlib/math.mcrs +68 -18
- package/src/stdlib/math_hp.mcrs +124 -33
- package/src/stdlib/matrix.mcrs +133 -20
- package/src/stdlib/mobs.mcrs +87 -0
- package/src/stdlib/noise.mcrs +65 -21
- package/src/stdlib/ode.mcrs +96 -0
- package/src/stdlib/parabola.mcrs +104 -29
- package/src/stdlib/particles.mcrs +78 -21
- package/src/stdlib/pathfind.mcrs +89 -35
- package/src/stdlib/physics.mcrs +134 -26
- package/src/stdlib/player.mcrs +18 -0
- package/src/stdlib/quaternion.mcrs +213 -9
- package/src/stdlib/queue.mcrs +123 -0
- package/src/stdlib/random.mcrs +63 -18
- package/src/stdlib/result.mcrs +111 -0
- package/src/stdlib/scheduler.mcrs +59 -10
- package/src/stdlib/set_int.mcrs +240 -0
- package/src/stdlib/sets.mcrs +49 -19
- package/src/stdlib/signal.mcrs +151 -79
- package/src/stdlib/sort.mcrs +44 -24
- package/src/stdlib/spawn.mcrs +30 -7
- package/src/stdlib/state.mcrs +40 -5
- package/src/stdlib/strings.mcrs +131 -3
- package/src/stdlib/tags.mcrs +2 -2
- package/src/stdlib/teams.mcrs +22 -10
- package/src/stdlib/timer.mcrs +36 -6
- package/src/stdlib/vec.mcrs +44 -9
- package/src/stdlib/world.mcrs +57 -25
- package/src/structs/expand.ts +64 -0
- package/src/testing/runner.ts +271 -0
- package/src/typechecker/index.ts +273 -9
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loop-Invariant Code Motion (LICM) — MIR optimization pass.
|
|
3
|
+
*
|
|
4
|
+
* For each natural loop (detected by the loop_header / loop_body / loop_latch
|
|
5
|
+
* naming convention used by the RedScript HIR→MIR lowering), instructions
|
|
6
|
+
* whose operands are not modified inside the loop and that have no side
|
|
7
|
+
* effects are hoisted to a newly-inserted preheader block that jumps to the
|
|
8
|
+
* loop header.
|
|
9
|
+
*
|
|
10
|
+
* Algorithm:
|
|
11
|
+
* 1. Identify all loops via the loop_header block naming convention.
|
|
12
|
+
* 2. Collect all temps defined anywhere in the loop (variant set).
|
|
13
|
+
* 3. Walk each loop block's instructions; an instruction is loop-invariant
|
|
14
|
+
* when:
|
|
15
|
+
* a. It has no side effects (no call, nbt_write, score_write, etc.).
|
|
16
|
+
* b. Every operand is either a constant OR a temp not in the variant set.
|
|
17
|
+
* 4. Insert a fresh preheader block before the loop header, redirect the
|
|
18
|
+
* predecessor(s) of the header (excluding the back-edge latch) to the
|
|
19
|
+
* preheader, and move the hoisted instructions there.
|
|
20
|
+
* 5. Remove hoisted instructions from their original blocks.
|
|
21
|
+
* 6. Recompute predecessor lists.
|
|
22
|
+
*
|
|
23
|
+
* Limitations:
|
|
24
|
+
* - Only handles the canonical loop shape produced by the RedScript compiler
|
|
25
|
+
* (loop_header / loop_body / loop_latch block id prefixes).
|
|
26
|
+
* - Requires that the loop have exactly one non-back-edge predecessor of
|
|
27
|
+
* the header (i.e. one entry edge).
|
|
28
|
+
* - Does not hoist instructions that could trap (div/mod with a variable
|
|
29
|
+
* denominator) — we conservatively keep those in place.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import type { MIRFunction, MIRBlock, MIRInstr, Operand, Temp, BlockId } from '../mir/types'
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Public API
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
export function licm(fn: MIRFunction): MIRFunction {
|
|
39
|
+
let current = fn
|
|
40
|
+
let changed = true
|
|
41
|
+
// Iterate to fixpoint — multiple loops, or newly-exposed invariants
|
|
42
|
+
while (changed) {
|
|
43
|
+
changed = false
|
|
44
|
+
const result = tryHoistOne(current)
|
|
45
|
+
if (result !== current) {
|
|
46
|
+
current = result
|
|
47
|
+
changed = true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return current
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Core: try to hoist invariant instructions from one loop
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
interface LoopInfo {
|
|
58
|
+
headerId: BlockId
|
|
59
|
+
/** All block ids that belong to the loop body (header + body blocks + latch) */
|
|
60
|
+
loopBlockIds: Set<BlockId>
|
|
61
|
+
/** The single non-back-edge predecessor of the header */
|
|
62
|
+
preHeaderPredId: BlockId
|
|
63
|
+
/** The latch block id (back-edge source) */
|
|
64
|
+
latchId: BlockId
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function tryHoistOne(fn: MIRFunction): MIRFunction {
|
|
68
|
+
const blockMap = new Map(fn.blocks.map(b => [b.id, b]))
|
|
69
|
+
|
|
70
|
+
for (const block of fn.blocks) {
|
|
71
|
+
if (!block.id.startsWith('loop_header')) continue
|
|
72
|
+
const info = analyzeLoop(fn, blockMap, block)
|
|
73
|
+
if (!info) continue
|
|
74
|
+
|
|
75
|
+
const hoisted = collectInvariant(info, blockMap, fn)
|
|
76
|
+
if (hoisted.length === 0) continue
|
|
77
|
+
|
|
78
|
+
return applyHoist(fn, blockMap, info, hoisted)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return fn
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Loop detection
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
function analyzeLoop(
|
|
89
|
+
fn: MIRFunction,
|
|
90
|
+
blockMap: Map<BlockId, MIRBlock>,
|
|
91
|
+
header: MIRBlock,
|
|
92
|
+
): LoopInfo | null {
|
|
93
|
+
// Header must branch (loop condition check)
|
|
94
|
+
if (header.term.kind !== 'branch') return null
|
|
95
|
+
|
|
96
|
+
// Find the latch: the block that jumps back to the header
|
|
97
|
+
const latchId = findLatch(fn, header.id)
|
|
98
|
+
if (!latchId) return null
|
|
99
|
+
|
|
100
|
+
// Find the non-back-edge predecessor (the block that first enters the loop)
|
|
101
|
+
const preHeaderPredId = findPreHeaderPred(fn, header.id, latchId)
|
|
102
|
+
if (!preHeaderPredId) return null
|
|
103
|
+
|
|
104
|
+
// Collect all loop block ids: header + any block reachable from header
|
|
105
|
+
// that can reach the latch (simple: everything up to and including latch)
|
|
106
|
+
const loopBlockIds = collectLoopBlocks(fn, blockMap, header.id, latchId)
|
|
107
|
+
|
|
108
|
+
return { headerId: header.id, loopBlockIds, preHeaderPredId, latchId }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Find the latch: a predecessor of header that is dominated by header.
|
|
112
|
+
* In the canonical shape, it's the block with id prefix loop_latch that
|
|
113
|
+
* jumps back to the header. Fall back to any predecessor != preheader. */
|
|
114
|
+
function findLatch(fn: MIRFunction, headerId: BlockId): BlockId | null {
|
|
115
|
+
for (const block of fn.blocks) {
|
|
116
|
+
if (block.id.startsWith('loop_latch')) {
|
|
117
|
+
const targets = getTermTargets(block.term)
|
|
118
|
+
if (targets.includes(headerId)) return block.id
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Fallback: any predecessor that comes after the header in block order
|
|
122
|
+
// (a back-edge in the CFG)
|
|
123
|
+
const headerIdx = fn.blocks.findIndex(b => b.id === headerId)
|
|
124
|
+
for (let i = headerIdx + 1; i < fn.blocks.length; i++) {
|
|
125
|
+
const block = fn.blocks[i]
|
|
126
|
+
const targets = getTermTargets(block.term)
|
|
127
|
+
if (targets.includes(headerId)) return block.id
|
|
128
|
+
}
|
|
129
|
+
return null
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Find the single non-latch predecessor of the header. */
|
|
133
|
+
function findPreHeaderPred(fn: MIRFunction, headerId: BlockId, latchId: BlockId): BlockId | null {
|
|
134
|
+
const preds: BlockId[] = []
|
|
135
|
+
for (const block of fn.blocks) {
|
|
136
|
+
if (block.id === latchId) continue
|
|
137
|
+
const targets = getTermTargets(block.term)
|
|
138
|
+
if (targets.includes(headerId)) preds.push(block.id)
|
|
139
|
+
}
|
|
140
|
+
return preds.length === 1 ? preds[0] : null
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Collect all block ids that belong to the loop using backward reachability.
|
|
145
|
+
*
|
|
146
|
+
* Algorithm: start from the latch and follow predecessors backward until we
|
|
147
|
+
* reach the header. Every visited block (including header and latch) is part
|
|
148
|
+
* of the loop. This is correct regardless of block naming conventions, so it
|
|
149
|
+
* handles branches, merges, and nested conditionals inside the loop body.
|
|
150
|
+
*/
|
|
151
|
+
function collectLoopBlocks(
|
|
152
|
+
fn: MIRFunction,
|
|
153
|
+
blockMap: Map<BlockId, MIRBlock>,
|
|
154
|
+
headerId: BlockId,
|
|
155
|
+
latchId: BlockId,
|
|
156
|
+
): Set<BlockId> {
|
|
157
|
+
// Build predecessor map
|
|
158
|
+
const predsMap = new Map<BlockId, BlockId[]>()
|
|
159
|
+
for (const b of fn.blocks) predsMap.set(b.id, [])
|
|
160
|
+
for (const b of fn.blocks) {
|
|
161
|
+
for (const tgt of getTermTargets(b.term)) {
|
|
162
|
+
const list = predsMap.get(tgt)
|
|
163
|
+
if (list) list.push(b.id)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Backward BFS from latch to header
|
|
168
|
+
const result = new Set<BlockId>()
|
|
169
|
+
const queue: BlockId[] = [latchId]
|
|
170
|
+
while (queue.length > 0) {
|
|
171
|
+
const id = queue.shift()!
|
|
172
|
+
if (result.has(id)) continue
|
|
173
|
+
result.add(id)
|
|
174
|
+
if (id === headerId) continue // don't go past the header
|
|
175
|
+
for (const pred of predsMap.get(id) ?? []) {
|
|
176
|
+
if (!result.has(pred)) queue.push(pred)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return result
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// Invariant detection
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
interface HoistedInstr {
|
|
188
|
+
fromBlockId: BlockId
|
|
189
|
+
instrIndex: number
|
|
190
|
+
instr: MIRInstr
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function collectInvariant(
|
|
194
|
+
info: LoopInfo,
|
|
195
|
+
blockMap: Map<BlockId, MIRBlock>,
|
|
196
|
+
fn: MIRFunction,
|
|
197
|
+
): HoistedInstr[] {
|
|
198
|
+
// Step 1: collect the set of all temps defined (written) anywhere in the loop
|
|
199
|
+
const variantTemps = new Set<Temp>()
|
|
200
|
+
for (const id of info.loopBlockIds) {
|
|
201
|
+
const block = blockMap.get(id)
|
|
202
|
+
if (!block) continue
|
|
203
|
+
for (const instr of block.instrs) {
|
|
204
|
+
const dst = getInstrDst(instr)
|
|
205
|
+
if (dst !== null) variantTemps.add(dst)
|
|
206
|
+
}
|
|
207
|
+
// Terminator dsts (there are none in practice, but be safe)
|
|
208
|
+
const termDst = getInstrDst(block.term)
|
|
209
|
+
if (termDst !== null) variantTemps.add(termDst)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Step 2: iteratively find invariant instructions
|
|
213
|
+
// (an instr whose operands are all non-variant can itself be treated as
|
|
214
|
+
// non-variant after it is removed, exposing further candidates)
|
|
215
|
+
const hoisted: HoistedInstr[] = []
|
|
216
|
+
let changed = true
|
|
217
|
+
const removedKeys = new Set<string>() // "blockId:index"
|
|
218
|
+
|
|
219
|
+
// Compute the set of temps defined OUTSIDE the loop (in preheader or earlier).
|
|
220
|
+
// An instruction can only be safely hoisted from the loop body if its dst is
|
|
221
|
+
// NOT already defined outside the loop — otherwise hoisting would shadow the
|
|
222
|
+
// prior value when the loop iterates zero times (e.g. while(false)).
|
|
223
|
+
const definedOutsideLoop = new Set<Temp>()
|
|
224
|
+
for (const block of fn.blocks) {
|
|
225
|
+
if (info.loopBlockIds.has(block.id)) continue // skip loop blocks
|
|
226
|
+
for (const instr of block.instrs) {
|
|
227
|
+
const d = getInstrDst(instr)
|
|
228
|
+
if (d !== null) definedOutsideLoop.add(d)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
while (changed) {
|
|
233
|
+
changed = false
|
|
234
|
+
for (const id of info.loopBlockIds) {
|
|
235
|
+
const block = blockMap.get(id)
|
|
236
|
+
if (!block) continue
|
|
237
|
+
|
|
238
|
+
for (let i = 0; i < block.instrs.length; i++) {
|
|
239
|
+
const key = `${id}:${i}`
|
|
240
|
+
if (removedKeys.has(key)) continue
|
|
241
|
+
|
|
242
|
+
const instr = block.instrs[i]
|
|
243
|
+
const dst = getInstrDst(instr)
|
|
244
|
+
|
|
245
|
+
// Must have a dst (otherwise it's a side-effectful no-dst instr)
|
|
246
|
+
if (dst === null) continue
|
|
247
|
+
|
|
248
|
+
// Must not have side effects
|
|
249
|
+
if (hasSideEffects(instr)) continue
|
|
250
|
+
|
|
251
|
+
// Skip div/mod with variable denominator (potential trap)
|
|
252
|
+
if ((instr.kind === 'div' || instr.kind === 'mod') && instr.b.kind === 'temp') continue
|
|
253
|
+
|
|
254
|
+
// All source operands must be invariant (not in variantTemps)
|
|
255
|
+
if (!allOperandsInvariant(instr, variantTemps)) continue
|
|
256
|
+
|
|
257
|
+
// Don't hoist if there are OTHER writers of dst in the loop (excluding
|
|
258
|
+
// this instruction itself). If another non-hoisted instruction also writes
|
|
259
|
+
// dst, hoisting this one would leave stale values on subsequent iterations.
|
|
260
|
+
const currentKeyForCheck = key // this instruction's key (not yet in removedKeys)
|
|
261
|
+
if (hasOtherWriters(dst, info.loopBlockIds, blockMap, removedKeys, currentKeyForCheck)) continue
|
|
262
|
+
|
|
263
|
+
// Don't hoist if dst is also defined outside the loop. Hoisting would
|
|
264
|
+
// shadow the prior value when the loop executes zero times.
|
|
265
|
+
if (definedOutsideLoop.has(dst)) continue
|
|
266
|
+
|
|
267
|
+
// This instruction is loop-invariant — hoist it
|
|
268
|
+
hoisted.push({ fromBlockId: id, instrIndex: i, instr })
|
|
269
|
+
removedKeys.add(key)
|
|
270
|
+
// After hoisting, the dst is no longer variant inside the loop
|
|
271
|
+
// (we already checked above that no other writer remains).
|
|
272
|
+
variantTemps.delete(dst)
|
|
273
|
+
changed = true
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return hoisted
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Returns true if there exists another non-hoisted instruction in the loop
|
|
282
|
+
* (other than `excludeKey`) that also writes to `dst`. */
|
|
283
|
+
function hasOtherWriters(
|
|
284
|
+
dst: Temp,
|
|
285
|
+
loopBlockIds: Set<BlockId>,
|
|
286
|
+
blockMap: Map<BlockId, MIRBlock>,
|
|
287
|
+
removedKeys: Set<string>,
|
|
288
|
+
excludeKey: string,
|
|
289
|
+
): boolean {
|
|
290
|
+
for (const id of loopBlockIds) {
|
|
291
|
+
const block = blockMap.get(id)
|
|
292
|
+
if (!block) continue
|
|
293
|
+
for (let i = 0; i < block.instrs.length; i++) {
|
|
294
|
+
const k = `${id}:${i}`
|
|
295
|
+
if (k === excludeKey) continue // skip the current candidate
|
|
296
|
+
if (removedKeys.has(k)) continue // skip already-hoisted
|
|
297
|
+
const d = getInstrDst(block.instrs[i])
|
|
298
|
+
if (d === dst) return true
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return false
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function allOperandsInvariant(instr: MIRInstr, variantTemps: Set<Temp>): boolean {
|
|
305
|
+
for (const op of getSourceOperands(instr)) {
|
|
306
|
+
if (op.kind === 'temp' && variantTemps.has(op.name)) return false
|
|
307
|
+
}
|
|
308
|
+
return true
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
// Applying the hoist
|
|
313
|
+
// ---------------------------------------------------------------------------
|
|
314
|
+
|
|
315
|
+
function applyHoist(
|
|
316
|
+
fn: MIRFunction,
|
|
317
|
+
blockMap: Map<BlockId, MIRBlock>,
|
|
318
|
+
info: LoopInfo,
|
|
319
|
+
hoisted: HoistedInstr[],
|
|
320
|
+
): MIRFunction {
|
|
321
|
+
const { headerId, preHeaderPredId, latchId } = info
|
|
322
|
+
|
|
323
|
+
// Build a key set for quick lookup: "blockId:instrIndex"
|
|
324
|
+
const hoistedKeys = new Set(hoisted.map(h => `${h.fromBlockId}:${h.instrIndex}`))
|
|
325
|
+
// Collect hoisted instrs in order (they were collected left-to-right)
|
|
326
|
+
const hoistedInstrs = hoisted.map(h => h.instr)
|
|
327
|
+
|
|
328
|
+
// Insert a fresh preheader block
|
|
329
|
+
const preHeaderId = headerId.replace('loop_header', 'loop_preheader')
|
|
330
|
+
|
|
331
|
+
const preHeaderBlock: MIRBlock = {
|
|
332
|
+
id: preHeaderId,
|
|
333
|
+
instrs: hoistedInstrs,
|
|
334
|
+
term: { kind: 'jump', target: headerId },
|
|
335
|
+
preds: [], // will be recomputed
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Rewrite the predecessor's jump from header → preHeader
|
|
339
|
+
const newBlocks: MIRBlock[] = []
|
|
340
|
+
for (const block of fn.blocks) {
|
|
341
|
+
if (block.id === preHeaderPredId) {
|
|
342
|
+
// Redirect its outgoing edge(s) from headerId to preHeaderId
|
|
343
|
+
newBlocks.push({
|
|
344
|
+
...block,
|
|
345
|
+
term: redirectTerm(block.term, headerId, preHeaderId),
|
|
346
|
+
})
|
|
347
|
+
// Insert the new preheader right after this block
|
|
348
|
+
newBlocks.push(preHeaderBlock)
|
|
349
|
+
} else {
|
|
350
|
+
// Remove hoisted instructions from their source blocks
|
|
351
|
+
if (info.loopBlockIds.has(block.id)) {
|
|
352
|
+
const newInstrs = block.instrs.filter((_, i) => !hoistedKeys.has(`${block.id}:${i}`))
|
|
353
|
+
newBlocks.push({ ...block, instrs: newInstrs })
|
|
354
|
+
} else {
|
|
355
|
+
newBlocks.push(block)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return { ...fn, blocks: recomputePreds(newBlocks) }
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function redirectTerm(term: MIRInstr, from: BlockId, to: BlockId): MIRInstr {
|
|
364
|
+
switch (term.kind) {
|
|
365
|
+
case 'jump':
|
|
366
|
+
return term.target === from ? { ...term, target: to } : term
|
|
367
|
+
case 'branch':
|
|
368
|
+
return {
|
|
369
|
+
...term,
|
|
370
|
+
then: term.then === from ? to : term.then,
|
|
371
|
+
else: term.else === from ? to : term.else,
|
|
372
|
+
}
|
|
373
|
+
default:
|
|
374
|
+
return term
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// ---------------------------------------------------------------------------
|
|
379
|
+
// Helpers
|
|
380
|
+
// ---------------------------------------------------------------------------
|
|
381
|
+
|
|
382
|
+
function hasSideEffects(instr: MIRInstr): boolean {
|
|
383
|
+
return (
|
|
384
|
+
instr.kind === 'call' ||
|
|
385
|
+
instr.kind === 'call_macro' ||
|
|
386
|
+
instr.kind === 'call_context' ||
|
|
387
|
+
instr.kind === 'nbt_write' ||
|
|
388
|
+
instr.kind === 'nbt_write_dynamic' ||
|
|
389
|
+
instr.kind === 'score_write'
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function getInstrDst(instr: MIRInstr): Temp | null {
|
|
394
|
+
switch (instr.kind) {
|
|
395
|
+
case 'const': case 'copy':
|
|
396
|
+
case 'add': case 'sub': case 'mul': case 'div': case 'mod': case 'pow':
|
|
397
|
+
case 'neg': case 'cmp': case 'and': case 'or': case 'not':
|
|
398
|
+
case 'nbt_read': case 'nbt_read_dynamic': case 'nbt_list_len':
|
|
399
|
+
case 'string_match': case 'score_read':
|
|
400
|
+
return instr.dst
|
|
401
|
+
case 'call': case 'call_macro':
|
|
402
|
+
return instr.dst
|
|
403
|
+
default:
|
|
404
|
+
return null
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function getSourceOperands(instr: MIRInstr): Operand[] {
|
|
409
|
+
switch (instr.kind) {
|
|
410
|
+
case 'copy': case 'neg': case 'not':
|
|
411
|
+
return [instr.src]
|
|
412
|
+
case 'add': case 'sub': case 'mul': case 'div': case 'mod': case 'pow':
|
|
413
|
+
case 'cmp': case 'and': case 'or':
|
|
414
|
+
return [instr.a, instr.b]
|
|
415
|
+
case 'nbt_write':
|
|
416
|
+
return [instr.src]
|
|
417
|
+
case 'nbt_write_dynamic':
|
|
418
|
+
return [instr.indexSrc, instr.valueSrc]
|
|
419
|
+
case 'nbt_read_dynamic':
|
|
420
|
+
return [instr.indexSrc]
|
|
421
|
+
case 'call':
|
|
422
|
+
return [...instr.args]
|
|
423
|
+
case 'call_macro':
|
|
424
|
+
return instr.args.map(a => a.value)
|
|
425
|
+
case 'branch':
|
|
426
|
+
return [instr.cond]
|
|
427
|
+
case 'return':
|
|
428
|
+
return instr.value ? [instr.value] : []
|
|
429
|
+
case 'score_write':
|
|
430
|
+
return [instr.src]
|
|
431
|
+
default:
|
|
432
|
+
return []
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function getTermTargets(term: MIRInstr): BlockId[] {
|
|
437
|
+
switch (term.kind) {
|
|
438
|
+
case 'jump': return [term.target]
|
|
439
|
+
case 'branch': return [term.then, term.else]
|
|
440
|
+
default: return []
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function recomputePreds(blocks: MIRBlock[]): MIRBlock[] {
|
|
445
|
+
const predMap = new Map<BlockId, BlockId[]>()
|
|
446
|
+
for (const b of blocks) predMap.set(b.id, [])
|
|
447
|
+
for (const block of blocks) {
|
|
448
|
+
for (const target of getTermTargets(block.term)) {
|
|
449
|
+
const preds = predMap.get(target)
|
|
450
|
+
if (preds) preds.push(block.id)
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return blocks.map(b => ({ ...b, preds: predMap.get(b.id) ?? [] }))
|
|
454
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NBT Write Coalescing — MIR backward analysis pass.
|
|
3
|
+
*
|
|
4
|
+
* Removes redundant consecutive writes to the same NBT storage path.
|
|
5
|
+
* If a path is written multiple times with no intervening read, only the
|
|
6
|
+
* last write (in program order) has any observable effect.
|
|
7
|
+
*
|
|
8
|
+
* Algorithm (backward scan per basic block):
|
|
9
|
+
* 1. Scan instructions from last to first.
|
|
10
|
+
* 2. Maintain `writtenPaths: Set<string>` — paths that are definitely
|
|
11
|
+
* overwritten later in the block.
|
|
12
|
+
* 3. nbt_write(path, ...):
|
|
13
|
+
* - If path is in writtenPaths → redundant, drop it.
|
|
14
|
+
* - Otherwise → keep it, add path to writtenPaths.
|
|
15
|
+
* 4. nbt_read(path) / nbt_read_dynamic / nbt_list_len → remove path
|
|
16
|
+
* from writtenPaths (the value is observed, cannot be dropped).
|
|
17
|
+
* 5. call / call_macro / call_context → conservatively clear writtenPaths
|
|
18
|
+
* (callee may read any NBT path).
|
|
19
|
+
*
|
|
20
|
+
* This is a single-block analysis; cross-block dataflow is left to
|
|
21
|
+
* future work (inter-block backward analysis).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { MIRBlock, MIRFunction, MIRInstr } from '../mir/types'
|
|
25
|
+
|
|
26
|
+
export function nbtCoalesce(fn: MIRFunction): MIRFunction {
|
|
27
|
+
return {
|
|
28
|
+
...fn,
|
|
29
|
+
blocks: fn.blocks.map(coalesceBlock),
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function coalesceBlock(block: MIRBlock): MIRBlock {
|
|
34
|
+
const writtenPaths = new Set<string>()
|
|
35
|
+
// Scan backwards and mark which instructions to keep
|
|
36
|
+
const keep: boolean[] = new Array(block.instrs.length).fill(true)
|
|
37
|
+
|
|
38
|
+
for (let i = block.instrs.length - 1; i >= 0; i--) {
|
|
39
|
+
const instr = block.instrs[i]
|
|
40
|
+
processInstr(instr, writtenPaths, keep, i)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
...block,
|
|
45
|
+
instrs: block.instrs.filter((_, i) => keep[i]),
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function nbtKey(ns: string, path: string): string {
|
|
50
|
+
return `${ns}\0${path}`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function processInstr(
|
|
54
|
+
instr: MIRInstr,
|
|
55
|
+
writtenPaths: Set<string>,
|
|
56
|
+
keep: boolean[],
|
|
57
|
+
idx: number,
|
|
58
|
+
): void {
|
|
59
|
+
switch (instr.kind) {
|
|
60
|
+
case 'nbt_write': {
|
|
61
|
+
const key = nbtKey(instr.ns, instr.path)
|
|
62
|
+
if (writtenPaths.has(key)) {
|
|
63
|
+
// This write will be overwritten before being read — drop it.
|
|
64
|
+
keep[idx] = false
|
|
65
|
+
} else {
|
|
66
|
+
writtenPaths.add(key)
|
|
67
|
+
}
|
|
68
|
+
break
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
case 'nbt_read': {
|
|
72
|
+
// Value is consumed here; the write that produces it is no longer redundant.
|
|
73
|
+
writtenPaths.delete(nbtKey(instr.ns, instr.path))
|
|
74
|
+
break
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
case 'nbt_read_dynamic': {
|
|
78
|
+
// Dynamic read — conservatively invalidate all paths with the same ns+prefix.
|
|
79
|
+
// Simpler: clear everything (safe, just less precise).
|
|
80
|
+
writtenPaths.clear()
|
|
81
|
+
break
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
case 'nbt_list_len': {
|
|
85
|
+
writtenPaths.delete(nbtKey(instr.ns, instr.path))
|
|
86
|
+
break
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
case 'nbt_write_dynamic': {
|
|
90
|
+
// Dynamic write — we cannot determine the exact path statically.
|
|
91
|
+
// Conservatively do NOT add anything to writtenPaths, and do NOT remove
|
|
92
|
+
// anything (a dynamic write could alias any path).
|
|
93
|
+
writtenPaths.clear()
|
|
94
|
+
break
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
case 'call':
|
|
98
|
+
case 'call_macro':
|
|
99
|
+
case 'call_context': {
|
|
100
|
+
// Callee may read any NBT path — conservatively flush.
|
|
101
|
+
writtenPaths.clear()
|
|
102
|
+
break
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
default:
|
|
106
|
+
// Arithmetic, comparisons, copies, score ops, etc. — no NBT effect.
|
|
107
|
+
break
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -10,13 +10,20 @@
|
|
|
10
10
|
|
|
11
11
|
import type { MIRFunction, MIRModule } from '../mir/types'
|
|
12
12
|
import { constantFold } from './constant_fold'
|
|
13
|
+
import { strengthReduction } from './strength_reduction'
|
|
13
14
|
import { copyProp } from './copy_prop'
|
|
14
15
|
import { dce } from './dce'
|
|
15
16
|
import { blockMerge } from './block_merge'
|
|
16
17
|
import { branchSimplify } from './branch_simplify'
|
|
17
18
|
import { loopUnroll } from './unroll'
|
|
19
|
+
import { licm } from './licm'
|
|
18
20
|
import { nbtBatchRead } from './nbt-batch'
|
|
21
|
+
import { scoreboardBatchRead } from './scoreboard-batch'
|
|
19
22
|
import { interproceduralConstProp } from './interprocedural'
|
|
23
|
+
import { autoInlineSmallFunctions } from './auto-inline'
|
|
24
|
+
import { inlinePass } from './inline'
|
|
25
|
+
import { cse } from './cse'
|
|
26
|
+
import { nbtCoalesce } from './nbt-coalesce'
|
|
20
27
|
|
|
21
28
|
// selectorCache is intentionally excluded from the default pipeline:
|
|
22
29
|
// it emits synthetic __sel_cleanup_* / __sel_tag_* call_context instructions
|
|
@@ -27,8 +34,13 @@ export type Pass = (fn: MIRFunction) => MIRFunction
|
|
|
27
34
|
|
|
28
35
|
const defaultPasses: Pass[] = [
|
|
29
36
|
loopUnroll,
|
|
37
|
+
licm,
|
|
30
38
|
nbtBatchRead,
|
|
39
|
+
nbtCoalesce,
|
|
40
|
+
scoreboardBatchRead,
|
|
31
41
|
constantFold,
|
|
42
|
+
strengthReduction,
|
|
43
|
+
cse,
|
|
32
44
|
copyProp,
|
|
33
45
|
branchSimplify,
|
|
34
46
|
dce,
|
|
@@ -50,9 +62,11 @@ export function optimizeFunction(fn: MIRFunction, passes: Pass[] = defaultPasses
|
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
export function optimizeModule(mod: MIRModule, passes?: Pass[]): MIRModule {
|
|
65
|
+
// Module-level pass: inline @inline-marked functions before per-function opts
|
|
66
|
+
const inlined = autoInlineSmallFunctions(inlinePass(mod))
|
|
53
67
|
const perFnOptimized = {
|
|
54
|
-
...
|
|
55
|
-
functions:
|
|
68
|
+
...inlined,
|
|
69
|
+
functions: inlined.functions.map(fn => optimizeFunction(fn, passes)),
|
|
56
70
|
}
|
|
57
71
|
// Module-level pass: interprocedural constant propagation
|
|
58
72
|
return interproceduralConstProp(perFnOptimized)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scoreboard Read Batching — MIR optimization pass.
|
|
3
|
+
*
|
|
4
|
+
* Eliminates redundant score_read instructions within a basic block:
|
|
5
|
+
* if the same (player, obj) is read twice with no intervening score_write
|
|
6
|
+
* to that player+obj, the second read is replaced with a copy from the
|
|
7
|
+
* first read's dst.
|
|
8
|
+
*
|
|
9
|
+
* This reduces expensive `scoreboard players get` commands in generated
|
|
10
|
+
* .mcfunction files.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { MIRFunction, MIRBlock, MIRInstr, Temp } from '../mir/types'
|
|
14
|
+
|
|
15
|
+
export function scoreboardBatchRead(fn: MIRFunction): MIRFunction {
|
|
16
|
+
return {
|
|
17
|
+
...fn,
|
|
18
|
+
blocks: fn.blocks.map(deduplicateBlock),
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function deduplicateBlock(block: MIRBlock): MIRBlock {
|
|
23
|
+
// Map from "player\0obj" → dst temp of the first read
|
|
24
|
+
const cache = new Map<string, Temp>()
|
|
25
|
+
|
|
26
|
+
const instrs: MIRInstr[] = []
|
|
27
|
+
for (const instr of block.instrs) {
|
|
28
|
+
if (instr.kind === 'score_read') {
|
|
29
|
+
const key = `${instr.player}\0${instr.obj}`
|
|
30
|
+
const cached = cache.get(key)
|
|
31
|
+
if (cached !== undefined) {
|
|
32
|
+
// Replace with copy from cached temp
|
|
33
|
+
instrs.push({ kind: 'copy', dst: instr.dst, src: { kind: 'temp', name: cached } })
|
|
34
|
+
} else {
|
|
35
|
+
cache.set(key, instr.dst)
|
|
36
|
+
instrs.push(instr)
|
|
37
|
+
}
|
|
38
|
+
} else if (instr.kind === 'score_write') {
|
|
39
|
+
// Invalidate cache for this player+obj
|
|
40
|
+
cache.delete(`${instr.player}\0${instr.obj}`)
|
|
41
|
+
instrs.push(instr)
|
|
42
|
+
} else if (instr.kind === 'call' || instr.kind === 'call_macro') {
|
|
43
|
+
// Conservative: calls may have side effects that modify scoreboards
|
|
44
|
+
cache.clear()
|
|
45
|
+
instrs.push(instr)
|
|
46
|
+
} else {
|
|
47
|
+
instrs.push(instr)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { ...block, instrs }
|
|
52
|
+
}
|