redscript-mc 1.2.30 → 2.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.
Files changed (269) hide show
  1. package/.claude/commands/build-test.md +10 -0
  2. package/.claude/commands/deploy-demo.md +12 -0
  3. package/.claude/commands/stage-status.md +13 -0
  4. package/.claude/settings.json +12 -0
  5. package/.github/workflows/ci.yml +1 -0
  6. package/CLAUDE.md +231 -0
  7. package/demo.gif +0 -0
  8. package/dist/cli.js +2 -554
  9. package/dist/compile.js +2 -266
  10. package/dist/index.js +2 -159
  11. package/dist/lowering/index.js +5 -3
  12. package/dist/src/__tests__/cli.test.d.ts +1 -0
  13. package/dist/src/__tests__/cli.test.js +104 -0
  14. package/dist/src/__tests__/codegen.test.d.ts +1 -0
  15. package/dist/src/__tests__/codegen.test.js +152 -0
  16. package/dist/src/__tests__/compile-all.test.d.ts +10 -0
  17. package/dist/src/__tests__/compile-all.test.js +108 -0
  18. package/dist/src/__tests__/dce.test.d.ts +1 -0
  19. package/dist/src/__tests__/dce.test.js +102 -0
  20. package/dist/src/__tests__/diagnostics.test.d.ts +4 -0
  21. package/dist/src/__tests__/diagnostics.test.js +177 -0
  22. package/dist/src/__tests__/e2e.test.d.ts +6 -0
  23. package/dist/src/__tests__/e2e.test.js +1789 -0
  24. package/dist/src/__tests__/entity-types.test.d.ts +1 -0
  25. package/dist/src/__tests__/entity-types.test.js +203 -0
  26. package/dist/src/__tests__/formatter.test.d.ts +1 -0
  27. package/dist/src/__tests__/formatter.test.js +40 -0
  28. package/dist/src/__tests__/lexer.test.d.ts +1 -0
  29. package/dist/src/__tests__/lexer.test.js +343 -0
  30. package/dist/src/__tests__/lowering.test.d.ts +1 -0
  31. package/dist/src/__tests__/lowering.test.js +1015 -0
  32. package/dist/src/__tests__/macro.test.d.ts +8 -0
  33. package/dist/src/__tests__/macro.test.js +306 -0
  34. package/dist/src/__tests__/mc-integration.test.d.ts +12 -0
  35. package/dist/src/__tests__/mc-integration.test.js +817 -0
  36. package/dist/src/__tests__/mc-syntax.test.d.ts +1 -0
  37. package/dist/src/__tests__/mc-syntax.test.js +124 -0
  38. package/dist/src/__tests__/nbt.test.d.ts +1 -0
  39. package/dist/src/__tests__/nbt.test.js +82 -0
  40. package/dist/src/__tests__/optimizer-advanced.test.d.ts +1 -0
  41. package/dist/src/__tests__/optimizer-advanced.test.js +124 -0
  42. package/dist/src/__tests__/optimizer.test.d.ts +1 -0
  43. package/dist/src/__tests__/optimizer.test.js +149 -0
  44. package/dist/src/__tests__/parser.test.d.ts +1 -0
  45. package/dist/src/__tests__/parser.test.js +807 -0
  46. package/dist/src/__tests__/repl.test.d.ts +1 -0
  47. package/dist/src/__tests__/repl.test.js +27 -0
  48. package/dist/src/__tests__/runtime.test.d.ts +1 -0
  49. package/dist/src/__tests__/runtime.test.js +289 -0
  50. package/dist/src/__tests__/stdlib-advanced.test.d.ts +4 -0
  51. package/dist/src/__tests__/stdlib-advanced.test.js +374 -0
  52. package/dist/src/__tests__/stdlib-bigint.test.d.ts +7 -0
  53. package/dist/src/__tests__/stdlib-bigint.test.js +426 -0
  54. package/dist/src/__tests__/stdlib-math.test.d.ts +7 -0
  55. package/dist/src/__tests__/stdlib-math.test.js +351 -0
  56. package/dist/src/__tests__/stdlib-vec.test.d.ts +4 -0
  57. package/dist/src/__tests__/stdlib-vec.test.js +263 -0
  58. package/dist/src/__tests__/structure-optimizer.test.d.ts +1 -0
  59. package/dist/src/__tests__/structure-optimizer.test.js +33 -0
  60. package/dist/src/__tests__/typechecker.test.d.ts +1 -0
  61. package/dist/src/__tests__/typechecker.test.js +552 -0
  62. package/dist/src/__tests__/var-allocator.test.d.ts +1 -0
  63. package/dist/src/__tests__/var-allocator.test.js +69 -0
  64. package/dist/src/ast/types.d.ts +515 -0
  65. package/dist/src/ast/types.js +9 -0
  66. package/dist/src/builtins/metadata.d.ts +36 -0
  67. package/dist/src/builtins/metadata.js +1014 -0
  68. package/dist/src/cli.d.ts +11 -0
  69. package/dist/src/cli.js +443 -0
  70. package/dist/src/codegen/cmdblock/index.d.ts +26 -0
  71. package/dist/src/codegen/cmdblock/index.js +45 -0
  72. package/dist/src/codegen/mcfunction/index.d.ts +40 -0
  73. package/dist/src/codegen/mcfunction/index.js +606 -0
  74. package/dist/src/codegen/structure/index.d.ts +24 -0
  75. package/dist/src/codegen/structure/index.js +279 -0
  76. package/dist/src/codegen/var-allocator.d.ts +45 -0
  77. package/dist/src/codegen/var-allocator.js +104 -0
  78. package/dist/src/compile.d.ts +37 -0
  79. package/dist/src/compile.js +165 -0
  80. package/dist/src/diagnostics/index.d.ts +44 -0
  81. package/dist/src/diagnostics/index.js +140 -0
  82. package/dist/src/events/types.d.ts +35 -0
  83. package/dist/src/events/types.js +59 -0
  84. package/dist/src/formatter/index.d.ts +1 -0
  85. package/dist/src/formatter/index.js +26 -0
  86. package/dist/src/index.d.ts +22 -0
  87. package/dist/src/index.js +45 -0
  88. package/dist/src/ir/builder.d.ts +33 -0
  89. package/dist/src/ir/builder.js +99 -0
  90. package/dist/src/ir/types.d.ts +132 -0
  91. package/dist/src/ir/types.js +15 -0
  92. package/dist/src/lexer/index.d.ts +37 -0
  93. package/dist/src/lexer/index.js +569 -0
  94. package/dist/src/lowering/index.d.ts +188 -0
  95. package/dist/src/lowering/index.js +3405 -0
  96. package/dist/src/mc-test/client.d.ts +128 -0
  97. package/dist/src/mc-test/client.js +174 -0
  98. package/dist/src/mc-test/runner.d.ts +28 -0
  99. package/dist/src/mc-test/runner.js +151 -0
  100. package/dist/src/mc-test/setup.d.ts +11 -0
  101. package/dist/src/mc-test/setup.js +98 -0
  102. package/dist/src/mc-validator/index.d.ts +17 -0
  103. package/dist/src/mc-validator/index.js +322 -0
  104. package/dist/src/nbt/index.d.ts +86 -0
  105. package/dist/src/nbt/index.js +250 -0
  106. package/dist/src/optimizer/commands.d.ts +38 -0
  107. package/dist/src/optimizer/commands.js +451 -0
  108. package/dist/src/optimizer/dce.d.ts +34 -0
  109. package/dist/src/optimizer/dce.js +639 -0
  110. package/dist/src/optimizer/passes.d.ts +34 -0
  111. package/dist/src/optimizer/passes.js +243 -0
  112. package/dist/src/optimizer/structure.d.ts +9 -0
  113. package/dist/src/optimizer/structure.js +356 -0
  114. package/dist/src/parser/index.d.ts +93 -0
  115. package/dist/src/parser/index.js +1687 -0
  116. package/dist/src/repl.d.ts +16 -0
  117. package/dist/src/repl.js +165 -0
  118. package/dist/src/runtime/index.d.ts +107 -0
  119. package/dist/src/runtime/index.js +1409 -0
  120. package/dist/src/typechecker/index.d.ts +61 -0
  121. package/dist/src/typechecker/index.js +1034 -0
  122. package/dist/src/types/entity-hierarchy.d.ts +29 -0
  123. package/dist/src/types/entity-hierarchy.js +107 -0
  124. package/dist/src2/__tests__/e2e/basic.test.d.ts +8 -0
  125. package/dist/src2/__tests__/e2e/basic.test.js +140 -0
  126. package/dist/src2/__tests__/e2e/macros.test.d.ts +9 -0
  127. package/dist/src2/__tests__/e2e/macros.test.js +182 -0
  128. package/dist/src2/__tests__/e2e/migrate.test.d.ts +13 -0
  129. package/dist/src2/__tests__/e2e/migrate.test.js +2739 -0
  130. package/dist/src2/__tests__/hir/desugar.test.d.ts +1 -0
  131. package/dist/src2/__tests__/hir/desugar.test.js +234 -0
  132. package/dist/src2/__tests__/lir/lower.test.d.ts +1 -0
  133. package/dist/src2/__tests__/lir/lower.test.js +559 -0
  134. package/dist/src2/__tests__/lir/types.test.d.ts +1 -0
  135. package/dist/src2/__tests__/lir/types.test.js +185 -0
  136. package/dist/src2/__tests__/lir/verify.test.d.ts +1 -0
  137. package/dist/src2/__tests__/lir/verify.test.js +221 -0
  138. package/dist/src2/__tests__/mir/arithmetic.test.d.ts +1 -0
  139. package/dist/src2/__tests__/mir/arithmetic.test.js +130 -0
  140. package/dist/src2/__tests__/mir/control-flow.test.d.ts +1 -0
  141. package/dist/src2/__tests__/mir/control-flow.test.js +205 -0
  142. package/dist/src2/__tests__/mir/verify.test.d.ts +1 -0
  143. package/dist/src2/__tests__/mir/verify.test.js +223 -0
  144. package/dist/src2/__tests__/optimizer/block_merge.test.d.ts +1 -0
  145. package/dist/src2/__tests__/optimizer/block_merge.test.js +78 -0
  146. package/dist/src2/__tests__/optimizer/branch_simplify.test.d.ts +1 -0
  147. package/dist/src2/__tests__/optimizer/branch_simplify.test.js +58 -0
  148. package/dist/src2/__tests__/optimizer/constant_fold.test.d.ts +1 -0
  149. package/dist/src2/__tests__/optimizer/constant_fold.test.js +131 -0
  150. package/dist/src2/__tests__/optimizer/copy_prop.test.d.ts +1 -0
  151. package/dist/src2/__tests__/optimizer/copy_prop.test.js +91 -0
  152. package/dist/src2/__tests__/optimizer/dce.test.d.ts +1 -0
  153. package/dist/src2/__tests__/optimizer/dce.test.js +76 -0
  154. package/dist/src2/__tests__/optimizer/pipeline.test.d.ts +1 -0
  155. package/dist/src2/__tests__/optimizer/pipeline.test.js +102 -0
  156. package/dist/src2/emit/compile.d.ts +19 -0
  157. package/dist/src2/emit/compile.js +80 -0
  158. package/dist/src2/emit/index.d.ts +17 -0
  159. package/dist/src2/emit/index.js +172 -0
  160. package/dist/src2/hir/lower.d.ts +15 -0
  161. package/dist/src2/hir/lower.js +378 -0
  162. package/dist/src2/hir/types.d.ts +373 -0
  163. package/dist/src2/hir/types.js +16 -0
  164. package/dist/src2/lir/lower.d.ts +15 -0
  165. package/dist/src2/lir/lower.js +453 -0
  166. package/dist/src2/lir/types.d.ts +136 -0
  167. package/dist/src2/lir/types.js +11 -0
  168. package/dist/src2/lir/verify.d.ts +14 -0
  169. package/dist/src2/lir/verify.js +113 -0
  170. package/dist/src2/mir/lower.d.ts +9 -0
  171. package/dist/src2/mir/lower.js +1030 -0
  172. package/dist/src2/mir/macro.d.ts +22 -0
  173. package/dist/src2/mir/macro.js +168 -0
  174. package/dist/src2/mir/types.d.ts +183 -0
  175. package/dist/src2/mir/types.js +11 -0
  176. package/dist/src2/mir/verify.d.ts +16 -0
  177. package/dist/src2/mir/verify.js +216 -0
  178. package/dist/src2/optimizer/block_merge.d.ts +12 -0
  179. package/dist/src2/optimizer/block_merge.js +84 -0
  180. package/dist/src2/optimizer/branch_simplify.d.ts +9 -0
  181. package/dist/src2/optimizer/branch_simplify.js +28 -0
  182. package/dist/src2/optimizer/constant_fold.d.ts +10 -0
  183. package/dist/src2/optimizer/constant_fold.js +85 -0
  184. package/dist/src2/optimizer/copy_prop.d.ts +9 -0
  185. package/dist/src2/optimizer/copy_prop.js +113 -0
  186. package/dist/src2/optimizer/dce.d.ts +8 -0
  187. package/dist/src2/optimizer/dce.js +155 -0
  188. package/dist/src2/optimizer/pipeline.d.ts +10 -0
  189. package/dist/src2/optimizer/pipeline.js +42 -0
  190. package/dist/tsconfig.tsbuildinfo +1 -0
  191. package/docs/compiler-pipeline-redesign.md +2243 -0
  192. package/docs/optimization-ideas.md +1076 -0
  193. package/editors/vscode/package-lock.json +3 -3
  194. package/editors/vscode/package.json +1 -1
  195. package/jest.config.js +1 -1
  196. package/package.json +6 -5
  197. package/scripts/postbuild.js +15 -0
  198. package/src/__tests__/cli.test.ts +8 -220
  199. package/src/__tests__/dce.test.ts +11 -56
  200. package/src/__tests__/diagnostics.test.ts +59 -38
  201. package/src/__tests__/mc-integration.test.ts +1 -2
  202. package/src/ast/types.ts +6 -1
  203. package/src/cli.ts +29 -156
  204. package/src/compile.ts +6 -162
  205. package/src/index.ts +14 -178
  206. package/src/mc-test/runner.ts +4 -3
  207. package/src/parser/index.ts +1 -1
  208. package/src/repl.ts +1 -1
  209. package/src/runtime/index.ts +1 -1
  210. package/src2/__tests__/e2e/basic.test.ts +154 -0
  211. package/src2/__tests__/e2e/macros.test.ts +199 -0
  212. package/src2/__tests__/e2e/migrate.test.ts +3008 -0
  213. package/src2/__tests__/hir/desugar.test.ts +263 -0
  214. package/src2/__tests__/lir/lower.test.ts +619 -0
  215. package/src2/__tests__/lir/types.test.ts +207 -0
  216. package/src2/__tests__/lir/verify.test.ts +249 -0
  217. package/src2/__tests__/mir/arithmetic.test.ts +156 -0
  218. package/src2/__tests__/mir/control-flow.test.ts +242 -0
  219. package/src2/__tests__/mir/verify.test.ts +254 -0
  220. package/src2/__tests__/optimizer/block_merge.test.ts +84 -0
  221. package/src2/__tests__/optimizer/branch_simplify.test.ts +64 -0
  222. package/src2/__tests__/optimizer/constant_fold.test.ts +145 -0
  223. package/src2/__tests__/optimizer/copy_prop.test.ts +99 -0
  224. package/src2/__tests__/optimizer/dce.test.ts +83 -0
  225. package/src2/__tests__/optimizer/pipeline.test.ts +116 -0
  226. package/src2/emit/compile.ts +99 -0
  227. package/src2/emit/index.ts +222 -0
  228. package/src2/hir/lower.ts +428 -0
  229. package/src2/hir/types.ts +216 -0
  230. package/src2/lir/lower.ts +556 -0
  231. package/src2/lir/types.ts +109 -0
  232. package/src2/lir/verify.ts +129 -0
  233. package/src2/mir/lower.ts +1160 -0
  234. package/src2/mir/macro.ts +167 -0
  235. package/src2/mir/types.ts +106 -0
  236. package/src2/mir/verify.ts +218 -0
  237. package/src2/optimizer/block_merge.ts +93 -0
  238. package/src2/optimizer/branch_simplify.ts +27 -0
  239. package/src2/optimizer/constant_fold.ts +88 -0
  240. package/src2/optimizer/copy_prop.ts +106 -0
  241. package/src2/optimizer/dce.ts +133 -0
  242. package/src2/optimizer/pipeline.ts +44 -0
  243. package/tsconfig.json +2 -2
  244. package/src/__tests__/codegen.test.ts +0 -161
  245. package/src/__tests__/e2e.test.ts +0 -2039
  246. package/src/__tests__/entity-types.test.ts +0 -236
  247. package/src/__tests__/lowering.test.ts +0 -1185
  248. package/src/__tests__/macro.test.ts +0 -343
  249. package/src/__tests__/nbt.test.ts +0 -58
  250. package/src/__tests__/optimizer-advanced.test.ts +0 -144
  251. package/src/__tests__/optimizer.test.ts +0 -162
  252. package/src/__tests__/runtime.test.ts +0 -305
  253. package/src/__tests__/stdlib-advanced.test.ts +0 -379
  254. package/src/__tests__/stdlib-bigint.test.ts +0 -427
  255. package/src/__tests__/stdlib-math.test.ts +0 -374
  256. package/src/__tests__/stdlib-vec.test.ts +0 -259
  257. package/src/__tests__/structure-optimizer.test.ts +0 -38
  258. package/src/__tests__/var-allocator.test.ts +0 -75
  259. package/src/codegen/cmdblock/index.ts +0 -63
  260. package/src/codegen/mcfunction/index.ts +0 -662
  261. package/src/codegen/structure/index.ts +0 -346
  262. package/src/codegen/var-allocator.ts +0 -104
  263. package/src/ir/builder.ts +0 -116
  264. package/src/ir/types.ts +0 -134
  265. package/src/lowering/index.ts +0 -3876
  266. package/src/optimizer/commands.ts +0 -534
  267. package/src/optimizer/dce.ts +0 -679
  268. package/src/optimizer/passes.ts +0 -250
  269. package/src/optimizer/structure.ts +0 -450
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Copy Propagation — MIR optimization pass.
3
+ *
4
+ * Within each block, tracks `copy dst, src` and `const dst, value` instructions
5
+ * and replaces subsequent uses of `dst` with the known operand.
6
+ * Invalidates mappings when a temp is redefined (written to).
7
+ */
8
+
9
+ import type { MIRFunction, MIRBlock, MIRInstr, Operand, Temp } from '../mir/types'
10
+
11
+ export function copyProp(fn: MIRFunction): MIRFunction {
12
+ return {
13
+ ...fn,
14
+ blocks: fn.blocks.map(propBlock),
15
+ }
16
+ }
17
+
18
+ function propBlock(block: MIRBlock): MIRBlock {
19
+ // Map from temp → the operand it was copied from
20
+ const copies = new Map<Temp, Operand>()
21
+
22
+ const instrs: MIRInstr[] = []
23
+ for (const instr of block.instrs) {
24
+ // Rewrite uses first
25
+ const rewritten = rewriteUses(instr, copies)
26
+
27
+ // Invalidate any mapping whose source was just redefined
28
+ const dst = getDst(rewritten)
29
+ if (dst) {
30
+ // Remove any mapping that points TO this dst (as a temp source)
31
+ for (const [k, v] of copies) {
32
+ if (v.kind === 'temp' && v.name === dst) {
33
+ copies.delete(k)
34
+ }
35
+ }
36
+ // Remove dst's own previous mapping (will be re-added below if applicable)
37
+ copies.delete(dst)
38
+ }
39
+
40
+ // Track new propagatable definitions
41
+ if (rewritten.kind === 'const') {
42
+ // const dst, value → record dst → const operand
43
+ copies.set(rewritten.dst, { kind: 'const', value: rewritten.value })
44
+ } else if (rewritten.kind === 'copy') {
45
+ // copy dst, src → record dst → src (temp or const)
46
+ copies.set(rewritten.dst, rewritten.src)
47
+ }
48
+
49
+ instrs.push(rewritten)
50
+ }
51
+
52
+ // Also rewrite terminator uses
53
+ const term = rewriteUses(block.term, copies)
54
+
55
+ return { ...block, instrs, term }
56
+ }
57
+
58
+ function resolve(op: Operand, copies: Map<Temp, Operand>): Operand {
59
+ if (op.kind === 'temp') {
60
+ const replacement = copies.get(op.name)
61
+ if (replacement) return replacement
62
+ }
63
+ return op
64
+ }
65
+
66
+ function rewriteUses(instr: MIRInstr, copies: Map<Temp, Operand>): MIRInstr {
67
+ switch (instr.kind) {
68
+ case 'copy':
69
+ return { ...instr, src: resolve(instr.src, copies) }
70
+ case 'neg':
71
+ case 'not':
72
+ return { ...instr, src: resolve(instr.src, copies) }
73
+ case 'add': case 'sub': case 'mul': case 'div': case 'mod':
74
+ case 'and': case 'or':
75
+ return { ...instr, a: resolve(instr.a, copies), b: resolve(instr.b, copies) }
76
+ case 'cmp':
77
+ return { ...instr, a: resolve(instr.a, copies), b: resolve(instr.b, copies) }
78
+ case 'nbt_write':
79
+ return { ...instr, src: resolve(instr.src, copies) }
80
+ case 'call':
81
+ return { ...instr, args: instr.args.map(a => resolve(a, copies)) }
82
+ case 'call_macro':
83
+ return { ...instr, args: instr.args.map(a => ({ ...a, value: resolve(a.value, copies) })) }
84
+ case 'branch':
85
+ return { ...instr, cond: resolve(instr.cond, copies) }
86
+ case 'return':
87
+ return { ...instr, value: instr.value ? resolve(instr.value, copies) : null }
88
+ default:
89
+ return instr
90
+ }
91
+ }
92
+
93
+ function getDst(instr: MIRInstr): Temp | null {
94
+ switch (instr.kind) {
95
+ case 'const': case 'copy':
96
+ case 'add': case 'sub': case 'mul': case 'div': case 'mod':
97
+ case 'neg': case 'cmp':
98
+ case 'and': case 'or': case 'not':
99
+ case 'nbt_read':
100
+ return instr.dst
101
+ case 'call': case 'call_macro':
102
+ return instr.dst
103
+ default:
104
+ return null
105
+ }
106
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Dead Code Elimination — MIR optimization pass.
3
+ *
4
+ * 1. Removes definitions of temps that are never used anywhere in the function.
5
+ * 2. Removes unreachable blocks (no predecessors and not the entry block).
6
+ */
7
+
8
+ import type { MIRFunction, MIRBlock, MIRInstr, Operand, Temp, BlockId } from '../mir/types'
9
+
10
+ export function dce(fn: MIRFunction): MIRFunction {
11
+ // Phase 1: Remove unreachable blocks
12
+ let blocks = removeUnreachable(fn)
13
+
14
+ // Phase 2: Remove unused temp definitions
15
+ blocks = removeDeadDefs(fn.params, blocks)
16
+
17
+ // Phase 3: Recompute preds after block removal
18
+ blocks = recomputePreds(blocks)
19
+
20
+ return { ...fn, blocks }
21
+ }
22
+
23
+ function removeUnreachable(fn: MIRFunction): MIRBlock[] {
24
+ const reachable = new Set<BlockId>()
25
+ const queue: BlockId[] = [fn.entry]
26
+ const blockMap = new Map(fn.blocks.map(b => [b.id, b]))
27
+
28
+ while (queue.length > 0) {
29
+ const id = queue.shift()!
30
+ if (reachable.has(id)) continue
31
+ reachable.add(id)
32
+ const block = blockMap.get(id)
33
+ if (block) {
34
+ for (const target of getTermTargets(block.term)) {
35
+ if (!reachable.has(target)) queue.push(target)
36
+ }
37
+ }
38
+ }
39
+
40
+ return fn.blocks.filter(b => reachable.has(b.id))
41
+ }
42
+
43
+ function removeDeadDefs(params: { name: Temp }[], blocks: MIRBlock[]): MIRBlock[] {
44
+ // Collect all used temps across the entire function
45
+ const used = new Set<Temp>()
46
+ for (const block of blocks) {
47
+ for (const instr of block.instrs) {
48
+ for (const t of getUsedTemps(instr)) used.add(t)
49
+ }
50
+ for (const t of getUsedTemps(block.term)) used.add(t)
51
+ }
52
+
53
+ // Remove instructions whose dst is never used, unless they have side effects
54
+ return blocks.map(block => ({
55
+ ...block,
56
+ instrs: block.instrs.filter(instr => {
57
+ const dst = getDst(instr)
58
+ if (dst === null) return true // no dst → keep (side-effectful)
59
+ if (hasSideEffects(instr)) return true
60
+ return used.has(dst)
61
+ }),
62
+ }))
63
+ }
64
+
65
+ function recomputePreds(blocks: MIRBlock[]): MIRBlock[] {
66
+ const predMap = new Map<BlockId, BlockId[]>()
67
+ for (const b of blocks) predMap.set(b.id, [])
68
+
69
+ for (const block of blocks) {
70
+ for (const target of getTermTargets(block.term)) {
71
+ const preds = predMap.get(target)
72
+ if (preds) preds.push(block.id)
73
+ }
74
+ }
75
+
76
+ return blocks.map(b => ({ ...b, preds: predMap.get(b.id) ?? [] }))
77
+ }
78
+
79
+ function hasSideEffects(instr: MIRInstr): boolean {
80
+ if (instr.kind === 'call' || instr.kind === 'call_macro' ||
81
+ instr.kind === 'call_context' || instr.kind === 'nbt_write') return true
82
+ // Return field temps (__rf_) write to global return slots — not dead even if unused locally
83
+ const dst = getDst(instr)
84
+ if (dst && dst.startsWith('__rf_')) return true
85
+ return false
86
+ }
87
+
88
+ function getTermTargets(term: MIRInstr): BlockId[] {
89
+ switch (term.kind) {
90
+ case 'jump': return [term.target]
91
+ case 'branch': return [term.then, term.else]
92
+ default: return []
93
+ }
94
+ }
95
+
96
+ function getDst(instr: MIRInstr): Temp | null {
97
+ switch (instr.kind) {
98
+ case 'const': case 'copy':
99
+ case 'add': case 'sub': case 'mul': case 'div': case 'mod':
100
+ case 'neg': case 'cmp':
101
+ case 'and': case 'or': case 'not':
102
+ case 'nbt_read':
103
+ return instr.dst
104
+ case 'call': case 'call_macro':
105
+ return instr.dst
106
+ default:
107
+ return null
108
+ }
109
+ }
110
+
111
+ function getUsedTemps(instr: MIRInstr): Temp[] {
112
+ const temps: Temp[] = []
113
+ const addOp = (op: Operand) => { if (op.kind === 'temp') temps.push(op.name) }
114
+
115
+ switch (instr.kind) {
116
+ case 'copy': case 'neg': case 'not':
117
+ addOp(instr.src); break
118
+ case 'add': case 'sub': case 'mul': case 'div': case 'mod':
119
+ case 'cmp': case 'and': case 'or':
120
+ addOp(instr.a); addOp(instr.b); break
121
+ case 'nbt_write':
122
+ addOp(instr.src); break
123
+ case 'call':
124
+ instr.args.forEach(addOp); break
125
+ case 'call_macro':
126
+ instr.args.forEach(a => addOp(a.value)); break
127
+ case 'branch':
128
+ addOp(instr.cond); break
129
+ case 'return':
130
+ if (instr.value) addOp(instr.value); break
131
+ }
132
+ return temps
133
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * MIR Optimization Pipeline — runs all passes to a fixpoint.
3
+ *
4
+ * Each pass is a function MIRFunction → MIRFunction.
5
+ * The pipeline iterates until no pass changes the function (fixpoint).
6
+ */
7
+
8
+ import type { MIRFunction, MIRModule } from '../mir/types'
9
+ import { constantFold } from './constant_fold'
10
+ import { copyProp } from './copy_prop'
11
+ import { dce } from './dce'
12
+ import { blockMerge } from './block_merge'
13
+ import { branchSimplify } from './branch_simplify'
14
+
15
+ export type Pass = (fn: MIRFunction) => MIRFunction
16
+
17
+ const defaultPasses: Pass[] = [
18
+ constantFold,
19
+ copyProp,
20
+ branchSimplify,
21
+ dce,
22
+ blockMerge,
23
+ ]
24
+
25
+ const MAX_ITERATIONS = 20
26
+
27
+ export function optimizeFunction(fn: MIRFunction, passes: Pass[] = defaultPasses): MIRFunction {
28
+ let current = fn
29
+ for (let i = 0; i < MAX_ITERATIONS; i++) {
30
+ const before = JSON.stringify(current)
31
+ for (const pass of passes) {
32
+ current = pass(current)
33
+ }
34
+ if (JSON.stringify(current) === before) break
35
+ }
36
+ return current
37
+ }
38
+
39
+ export function optimizeModule(mod: MIRModule, passes?: Pass[]): MIRModule {
40
+ return {
41
+ ...mod,
42
+ functions: mod.functions.map(fn => optimizeFunction(fn, passes)),
43
+ }
44
+ }
package/tsconfig.json CHANGED
@@ -4,13 +4,13 @@
4
4
  "module": "commonjs",
5
5
  "lib": ["ES2021"],
6
6
  "outDir": "dist",
7
- "rootDir": "src",
7
+ "rootDir": ".",
8
8
  "declaration": true,
9
9
  "sourceMap": true,
10
10
  "strict": true,
11
11
  "esModuleInterop": true,
12
12
  "skipLibCheck": true
13
13
  },
14
- "include": ["src/**/*"],
14
+ "include": ["src/**/*", "src2/**/*"],
15
15
  "exclude": ["dist", "node_modules"]
16
16
  }
@@ -1,161 +0,0 @@
1
- import { generateDatapack, generateDatapackWithStats } from '../codegen/mcfunction'
2
- import type { IRModule } from '../ir/types'
3
-
4
- describe('generateDatapack', () => {
5
- it('generates pack.mcmeta', () => {
6
- const mod: IRModule = { namespace: 'test', functions: [], globals: [] }
7
- const files = generateDatapack(mod)
8
- const meta = files.find(f => f.path === 'pack.mcmeta')
9
- expect(meta).toBeDefined()
10
- expect(JSON.parse(meta!.content).pack.pack_format).toBe(26)
11
- })
12
-
13
- it('generates __load.mcfunction with objective setup', () => {
14
- const mod: IRModule = { namespace: 'mypack', functions: [], globals: [{ name: 'counter', init: 0 }] }
15
- const files = generateDatapack(mod)
16
- const load = files.find(f => f.path.includes('__load.mcfunction'))
17
- expect(load?.content).toContain('scoreboard objectives add rs dummy')
18
- expect(load?.content).toContain('scoreboard players set $counter rs 0')
19
- })
20
-
21
- it('generates function file for simple add(a, b)', () => {
22
- // IR now uses { kind: 'param', index: i } for param-copy instructions,
23
- // matching what the lowering emits. Pass mangle:false so we can check
24
- // readable names without worrying about sequential mangled names.
25
- const mod: IRModule = {
26
- namespace: 'mypack',
27
- globals: [],
28
- functions: [{
29
- name: 'add',
30
- params: ['$a', '$b'],
31
- locals: ['$a', '$b', '$result'],
32
- blocks: [{
33
- label: 'entry',
34
- instrs: [
35
- // param-copy instructions (what the lowering now emits)
36
- { op: 'assign', dst: '$a', src: { kind: 'param', index: 0 } },
37
- { op: 'assign', dst: '$b', src: { kind: 'param', index: 1 } },
38
- { op: 'binop', dst: '$result', lhs: { kind: 'var', name: '$a' }, bop: '+', rhs: { kind: 'var', name: '$b' } },
39
- ],
40
- term: { op: 'return', value: { kind: 'var', name: '$result' } },
41
- }],
42
- }],
43
- }
44
- const files = generateDatapackWithStats(mod, { mangle: false }).files
45
- const fn = files.find(f => f.path.includes('add.mcfunction'))
46
- expect(fn).toBeDefined()
47
- // param setup emitted from the IR
48
- expect(fn!.content).toContain('scoreboard players operation $a rs = $p0 rs')
49
- expect(fn!.content).toContain('scoreboard players operation $b rs = $p1 rs')
50
- // Should have add operation
51
- expect(fn!.content).toContain('+=')
52
- })
53
-
54
- it('generates tick tag for tick loop function', () => {
55
- const mod: IRModule = {
56
- namespace: 'mypack',
57
- globals: [],
58
- functions: [{
59
- name: 'game_loop',
60
- params: [],
61
- locals: [],
62
- blocks: [{ label: 'entry', instrs: [], term: { op: 'return' } }],
63
- isTickLoop: true,
64
- }],
65
- }
66
- const files = generateDatapack(mod)
67
-
68
- // tick.json should point to __tick
69
- const tickTag = files.find(f => f.path.includes('tick.json'))
70
- expect(tickTag).toBeDefined()
71
- expect(JSON.parse(tickTag!.content).values).toContain('mypack:__tick')
72
-
73
- // __tick.mcfunction should call the game_loop function
74
- const tickFn = files.find(f => f.path.includes('__tick.mcfunction'))
75
- expect(tickFn).toBeDefined()
76
- expect(tickFn!.content).toContain('function mypack:game_loop')
77
- })
78
-
79
- it('generates conditional branches with execute if/unless', () => {
80
- const mod: IRModule = {
81
- namespace: 'mypack',
82
- globals: [],
83
- functions: [{
84
- name: 'check',
85
- params: [],
86
- locals: ['cond'],
87
- blocks: [
88
- {
89
- label: 'entry',
90
- instrs: [
91
- { op: 'assign', dst: 'cond', src: { kind: 'const', value: 1 } },
92
- ],
93
- term: { op: 'jump_if', cond: 'cond', then: 'then_block', else_: 'else_block' },
94
- },
95
- {
96
- label: 'then_block',
97
- instrs: [{ op: 'raw', cmd: 'say hello' }],
98
- term: { op: 'return' },
99
- },
100
- {
101
- label: 'else_block',
102
- instrs: [{ op: 'raw', cmd: 'say goodbye' }],
103
- term: { op: 'return' },
104
- },
105
- ],
106
- }],
107
- }
108
- const files = generateDatapack(mod)
109
- const entry = files.find(f => f.path.endsWith('check.mcfunction'))
110
- expect(entry?.content).toContain('execute if score $cond rs matches 1..')
111
- expect(entry?.content).toContain('execute if score $cond rs matches ..0')
112
- })
113
-
114
- it('generates advancement json for event decorators', () => {
115
- const mod: IRModule = {
116
- namespace: 'mypack',
117
- globals: [],
118
- functions: [{
119
- name: 'on_mine_diamond',
120
- params: [],
121
- locals: [],
122
- blocks: [{ label: 'entry', instrs: [], term: { op: 'return' } }],
123
- eventTrigger: { kind: 'advancement', value: 'story/mine_diamond' },
124
- }],
125
- }
126
-
127
- const result = generateDatapackWithStats(mod)
128
- const advancement = result.advancements.find(f => f.path === 'data/mypack/advancements/on_advancement_on_mine_diamond.json')
129
- expect(advancement).toBeDefined()
130
- const json = JSON.parse(advancement!.content)
131
- expect(json.criteria.trigger.trigger).toBe('minecraft:story/mine_diamond')
132
- expect(json.rewards.function).toBe('mypack:on_mine_diamond')
133
- })
134
-
135
- it('generates static event dispatcher in __tick', () => {
136
- const mod: IRModule = {
137
- namespace: 'mypack',
138
- globals: [],
139
- functions: [{
140
- name: 'handle_death',
141
- params: [],
142
- locals: [],
143
- blocks: [{ label: 'entry', instrs: [], term: { op: 'return' } }],
144
- eventHandler: { eventType: 'PlayerDeath', tag: 'rs.just_died' },
145
- }, {
146
- name: 'handle_death_2',
147
- params: [],
148
- locals: [],
149
- blocks: [{ label: 'entry', instrs: [], term: { op: 'return' } }],
150
- eventHandler: { eventType: 'PlayerDeath', tag: 'rs.just_died' },
151
- }],
152
- }
153
-
154
- const files = generateDatapack(mod)
155
- const tickFn = files.find(f => f.path.includes('__tick.mcfunction'))
156
- expect(tickFn).toBeDefined()
157
- expect(tickFn!.content).toContain('execute as @a[tag=rs.just_died] run function mypack:handle_death')
158
- expect(tickFn!.content).toContain('execute as @a[tag=rs.just_died] run function mypack:handle_death_2')
159
- expect(tickFn!.content).toContain('tag @a[tag=rs.just_died] remove rs.just_died')
160
- })
161
- })