@asmlift/core 0.8.1 → 0.10.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 (130) hide show
  1. package/README.md +36 -31
  2. package/package.json +1 -1
  3. package/src/aggregate.ts +397 -0
  4. package/src/backend/c.ts +6 -2
  5. package/src/backend/cfamily.ts +370 -56
  6. package/src/backend/cpp.ts +116 -10
  7. package/src/backend/pascal.ts +69 -11
  8. package/src/cdecl/constant.ts +59 -0
  9. package/src/cdecl/lex.ts +209 -0
  10. package/src/cdecl/parse.ts +975 -0
  11. package/src/cdecl/spell.ts +69 -0
  12. package/src/codegen-flags.ts +38 -6
  13. package/src/compiler-diagnostics.ts +194 -0
  14. package/src/contracts.ts +777 -84
  15. package/src/declare.ts +132 -13
  16. package/src/frontend/asmdata.ts +26 -6
  17. package/src/frontend/call-plan.ts +1590 -0
  18. package/src/frontend/dead-reads.ts +628 -0
  19. package/src/frontend/device-pins.ts +296 -0
  20. package/src/frontend/emit.ts +4 -3
  21. package/src/frontend/errors.ts +5 -0
  22. package/src/frontend/flags-edge.ts +101 -0
  23. package/src/frontend/fpu.ts +374 -0
  24. package/src/frontend/frame-objects.ts +3171 -0
  25. package/src/frontend/frontend.ts +64 -3
  26. package/src/frontend/high-half.ts +55 -5
  27. package/src/frontend/jump-table.ts +530 -0
  28. package/src/frontend/local-object.ts +616 -0
  29. package/src/frontend/mips.ts +2392 -208
  30. package/src/frontend/opaque.ts +97 -18
  31. package/src/frontend/own-struct-return.ts +500 -0
  32. package/src/frontend/ppc.ts +3137 -267
  33. package/src/frontend/register-struct-call.ts +484 -0
  34. package/src/frontend/reloc-symbol.ts +109 -13
  35. package/src/frontend/splat.ts +108 -5
  36. package/src/frontend/ssa.ts +2098 -89
  37. package/src/frontend/stackargs.ts +172 -42
  38. package/src/frontend/thumb.ts +4563 -2091
  39. package/src/ir/alias.ts +71 -0
  40. package/src/ir/bits.ts +681 -1
  41. package/src/ir/core.ts +199 -6
  42. package/src/ir/discipline.ts +313 -0
  43. package/src/ir/entry.ts +159 -0
  44. package/src/ir/float-bits.ts +47 -0
  45. package/src/{structure → ir}/loops.ts +6 -2
  46. package/src/ir/opcodes.ts +193 -121
  47. package/src/ir/parse.ts +1 -0
  48. package/src/ir/types.ts +202 -2
  49. package/src/ir/verify.ts +219 -3
  50. package/src/l3/address.ts +5 -0
  51. package/src/l3/argcopy.ts +38 -6
  52. package/src/l3/ast.ts +467 -23
  53. package/src/l3/basecse.ts +33 -5
  54. package/src/l3/coalesce.ts +21 -13
  55. package/src/l3/constinit.ts +382 -0
  56. package/src/l3/dce.ts +34 -12
  57. package/src/l3/gates.ts +9 -3
  58. package/src/l3/hoist.ts +8 -7
  59. package/src/l3/initfirst.ts +68 -19
  60. package/src/l3/intcell.ts +98 -0
  61. package/src/l3/mentions.ts +10 -4
  62. package/src/l3/narrowdecl.ts +259 -0
  63. package/src/l3/nearbase.ts +10 -5
  64. package/src/l3/parkfirst.ts +8 -5
  65. package/src/l3/pollguard.ts +11 -4
  66. package/src/l3/ptrcell.ts +354 -0
  67. package/src/l3/ptrfield.ts +123 -8
  68. package/src/l3/regspell.ts +26 -7
  69. package/src/l3/reindex.ts +293 -103
  70. package/src/l3/storage.ts +98 -1
  71. package/src/l3/symbol-refs.ts +182 -23
  72. package/src/l3/tailmerge.ts +9 -20
  73. package/src/l3/typing.ts +237 -32
  74. package/src/l3/unreduce.ts +10 -9
  75. package/src/l3/volatileptr.ts +17 -54
  76. package/src/l3/volstore.ts +96 -7
  77. package/src/mangle.ts +41 -1
  78. package/src/pattern/engine.ts +373 -82
  79. package/src/pipeline.ts +589 -27
  80. package/src/proto-context.ts +1648 -0
  81. package/src/proto.ts +1203 -49
  82. package/src/raise/aggmembers.ts +290 -0
  83. package/src/raise/arrays.ts +8 -3
  84. package/src/raise/declared-volatile.ts +307 -0
  85. package/src/raise/divpow2.ts +2 -2
  86. package/src/raise/errors.ts +17 -0
  87. package/src/raise/floathelpers.ts +464 -0
  88. package/src/raise/globalshape.ts +11 -5
  89. package/src/raise/gvn.ts +29 -8
  90. package/src/raise/latch.ts +162 -6
  91. package/src/raise/memberarrays.ts +10 -4
  92. package/src/raise/narrow.ts +29 -2
  93. package/src/raise/narrowlocal.ts +11 -6
  94. package/src/raise/offsetnames.ts +494 -0
  95. package/src/raise/pairparams.ts +114 -0
  96. package/src/raise/paramwidth.ts +203 -6
  97. package/src/raise/pre-recovery.ts +174 -17
  98. package/src/raise/pre-structure.ts +54 -0
  99. package/src/raise/recover.ts +26 -15
  100. package/src/raise/reroll.ts +694 -0
  101. package/src/raise/retsink.ts +1 -1
  102. package/src/raise/shortcircuit.ts +66 -30
  103. package/src/raise/softdiv.ts +21 -23
  104. package/src/raise/storesign.ts +153 -0
  105. package/src/raise/struct-arrays.ts +34 -14
  106. package/src/raise/structs.ts +674 -98
  107. package/src/raise/truncload.ts +367 -0
  108. package/src/raise/unthread.ts +400 -0
  109. package/src/raise/widehelpers.ts +106 -0
  110. package/src/rank-declare.ts +143 -12
  111. package/src/rank-variations.ts +117 -31
  112. package/src/rank.ts +563 -122
  113. package/src/runtime-helpers.ts +405 -0
  114. package/src/stillborn.ts +289 -0
  115. package/src/structure/analysis.ts +1449 -449
  116. package/src/structure/arith-ops.ts +23 -0
  117. package/src/structure/globalaccess.ts +111 -9
  118. package/src/structure/hazards.ts +641 -114
  119. package/src/structure/local-statics.ts +517 -0
  120. package/src/structure/namecoalesce.ts +2 -2
  121. package/src/structure/pointer-spelling.ts +1106 -0
  122. package/src/structure/redundant-test.ts +99 -0
  123. package/src/structure/structure.ts +3478 -975
  124. package/src/structure/switch-recover.ts +403 -178
  125. package/src/symbols.ts +44 -3
  126. package/src/target.ts +1387 -71
  127. package/src/trace.ts +66 -28
  128. package/src/variation-definitions.ts +243 -13
  129. package/src/variation-gates.ts +2 -0
  130. package/src/variation-tokens.ts +25 -7
@@ -210,13 +210,80 @@ function splitOperands(s: string): string[] {
210
210
  return out;
211
211
  }
212
212
 
213
+ /** A COPROCESSOR-1 REGISTER, in every spelling either MIPS dialect writes. THE ONE COPY: this
214
+ * reader decides which tokens keep the `$` sigil and `frontend/mips.ts` passes the same object as
215
+ * `OpaquePolicy.fpReg`, so the two questions — "does this survive normalisation with its sigil?"
216
+ * and "is this the FPU's file?" — are answered by one predicate. Two copies are sequenced rather
217
+ * than merely duplicated: widening the frontend's alone changes nothing, because this one has
218
+ * already stripped the token, and widening this one alone hands the frontend a sigil its own
219
+ * predicate rejects. Each half is silent on its own and the result is an opaque on a register in
220
+ * a file nothing models.
221
+ *
222
+ * Why the sigil: objdump writes a GPR bare (`v0`) and an FPU register with the sigil (`$f12`), so
223
+ * an FPU register is the one operand shape this reader must NOT strip, or the two dialects hand
224
+ * the frontend two different spellings of the same register. It is REQUIRED rather than optional
225
+ * because the bare form is indistinguishable from an objdump branch target, which is also bare
226
+ * lower-case hex (`f4`, `fa0`).
227
+ *
228
+ * The spellings, and all three are live in the trees: objdump's numbers (`$f12`), this dialect's
229
+ * o32 ABI names (`$ft2`, `$fv0`, `$fa0`, `$fs0`), and the trailing `f` those names take for the
230
+ * ODD HALF of a double-precision pair (`$ft0f`, `$fa1f`). `$fp` is the frame pointer and is
231
+ * stripped like any GPR: the digit is what decides, and it is what excludes it.
232
+ *
233
+ * Across every `$`-token in the three MIPS `asm/` trees this matches 52 tokens and every one is an
234
+ * FPU register:
235
+ * `grep -rhoE '\$[A-Za-z0-9_]+' apps/benchmark/checkouts/{af,marioparty3,snowboardkids2-decomp}/asm
236
+ * | sort -u | grep -icE '^\$f[vats]?[0-9]+f?$'` */
237
+ export const MIPS_FP_REG = /^\$f[vats]?\d+f?$/i;
238
+
239
+ /** The o32 ABI names of the EVEN FPU registers, by register number — the only ones a
240
+ * single-precision arithmetic instruction names under the FR=0 register model these compilers
241
+ * target. Read off the instruction words, not a manual: over 9,981 `add.s`/`sub.s`/`mul.s`/`div.s`
242
+ * lines in the `af` and `marioparty3` trees, decoding each word's `fd`/`fs`/`ft` fields gives every
243
+ * name one number and no name two. */
244
+ const O32_FP_NAMES: ReadonlyMap<string, number> = new Map([
245
+ ['fv0', 0],
246
+ ['fv1', 2],
247
+ ['ft0', 4],
248
+ ['ft1', 6],
249
+ ['ft2', 8],
250
+ ['ft3', 10],
251
+ ['fa0', 12],
252
+ ['fa1', 14],
253
+ ['ft4', 16],
254
+ ['ft5', 18],
255
+ ['fs0', 20],
256
+ ['fs1', 22],
257
+ ['fs2', 24],
258
+ ['fs3', 26],
259
+ ['fs4', 28],
260
+ ['fs5', 30],
261
+ ]);
262
+
263
+ /** An FPU register token, in either dialect's spelling, as the ONE key both dialects share — the
264
+ * objdump number (`$fa0` and `$f12` are both `$f12`) — or null for anything this does not name: a
265
+ * GPR, or an ODD half (`$f13`, `$fa0f`), which only a double-precision value or a word move uses.
266
+ * A key is what the SSA builder names a register by, so two spellings of one register that did
267
+ * not meet here would be two variables. */
268
+ export function mipsEvenFpKey(tok: string): string | null {
269
+ if (!MIPS_FP_REG.test(tok)) {
270
+ return null;
271
+ }
272
+ const bare = tok.slice(1).toLowerCase();
273
+ const num = /^f\d+$/.test(bare) ? Number(bare.slice(1)) : O32_FP_NAMES.get(bare);
274
+ return num !== undefined && num < 32 && num % 2 === 0 ? `$f${num}` : null;
275
+ }
276
+
213
277
  // Rewrite one Splat operand into the canonical objdump spelling the frontend consumes: strip the
214
- // `$` register sigil, fold a memory operand's displacement expression, evaluate a bare constant
215
- // expression, split a `%hi`/`%lo` reference into an immediate plus its record, decline a PIC one.
278
+ // `$` register sigil (except on an FPU register, where objdump keeps it), fold a memory operand's
279
+ // displacement expression, evaluate a bare constant expression, split a `%hi`/`%lo` reference into
280
+ // an immediate plus its record, decline a PIC one.
216
281
  function normalizeOperand(name: string, op: string): { op: string; reloc?: DisasmReloc } {
217
282
  // `%hi(SYM)` / `%lo(SYM + N)` / `%lo(SYM)(base)` — a global's address. Becomes the relocation
218
283
  // record an object file would carry plus the immediate the instruction really encodes, so the
219
284
  // frontend folds this dialect through the same path as objdump; NOT declined like the PIC relocs.
285
+ // The addend is a constant expression like any other here, so its RADIX is {@link evalConst}'s
286
+ // question rather than this pattern's — {@link refuseOctal} says why the placement is the rule.
220
287
  const hilo = op.match(
221
288
  /^%(hi|lo)\(\s*([A-Za-z_.$][\w.$]*)\s*(?:([+-])\s*(0x[0-9a-fA-F]+|\d+))?\s*\)(?:\((\$?[A-Za-z]\w*)\))?$/,
222
289
  );
@@ -235,8 +302,8 @@ function normalizeOperand(name: string, op: string): { op: string; reloc?: Disas
235
302
  if (HILO_OP.test(op)) {
236
303
  throw new FrontendUnsupportedError(
237
304
  `cannot lift '${name}': relocation operand '${op}' — this reader resolves a '%hi'/'%lo' half ` +
238
- `only against a symbol ('SYM' or 'SYM ± <integer>'), and will not treat one it cannot resolve ` +
239
- `as arithmetic`,
305
+ `only against a symbol ('SYM' or 'SYM ± <integer>'), and will not treat one it cannot ` +
306
+ `resolve as arithmetic`,
240
307
  );
241
308
  }
242
309
  if (RELOC_OP.test(op)) {
@@ -256,9 +323,42 @@ function normalizeOperand(name: string, op: string): { op: string; reloc?: Disas
256
323
  if (op.startsWith('(')) {
257
324
  return plain(String(evalConst(name, op)));
258
325
  }
259
- return plain(op.replace(/^\$/, ''));
326
+ // The one path that hands an operand on UNREAD, to a reader somewhere else: `addiu`/`addi`'s
327
+ // re-sign above is one, the MIPS frontend's `parseImm` another, and neither is told the radix, so
328
+ // a leading-zero immediate stops here — `addiu $v0, $a0, 020` lifted as `a0 + 20` against the
329
+ // assembler's 16.
330
+ if (OCTAL_MAGNITUDE.test(op)) {
331
+ refuseOctal(name, op, op);
332
+ }
333
+ return plain(MIPS_FP_REG.test(op) ? op : op.replace(/^\$/, ''));
260
334
  }
261
335
 
336
+ /** A magnitude with a LEADING ZERO is octal to the assembler and decimal to every `parseInt(…, 10)`
337
+ * in this file. Both readings are measured rather than assumed: under `mips-linux-gnu-as` 2.45,
338
+ * `.word 020` assembles to 0x10 and `.word 010` to 8 read back out of `.data`, and
339
+ * `lw $v0, 020($a0)` encodes the displacement 16 — against the 20 and the 10 a decimal reader
340
+ * answers. The difference reached the emitted C as a different ELEMENT: `%lo(gTab + 020)` lifted
341
+ * as `((s32 *)&gTab)[5]` where the assembler's addend gives `[4]`, and `%lo(gTab + 010)` as the
342
+ * fractional index `2.5`. A wrong address compiles and scores, so this is not something to guess.
343
+ *
344
+ * This reader models hex and decimal, so the third radix REFUSES rather than being resolved:
345
+ * nothing produces the shape — 0 operand occurrences carrying a leading-zero integer over the
346
+ * 4,200,524 operands of the 20,260 `.s` files that carry a `glabel` or a `.set noreorder` in the
347
+ * nine benchmark checkouts (counted with python; a recursive `grep` here skips `build/`) — so a
348
+ * reading for it would be a capability nothing could referee.
349
+ *
350
+ * THE PLACEMENT IS THE POINT. The rule belongs to the readers that turn a digit string into a
351
+ * value, not to the patterns that feed them: a guard on one caller's regex leaves the next caller
352
+ * reading base 10, and the three readers that do — `evalConst`'s literal, {@link signExtend16} and
353
+ * the shared `parseImm` — are not all in this file. */
354
+ const OCTAL_MAGNITUDE = /^[-+]?0\d/;
355
+ const refuseOctal = (name: string, tok: string, operand: string): never => {
356
+ throw new FrontendUnsupportedError(
357
+ `cannot lift '${name}': operand '${operand}' has a leading-zero magnitude ('${tok}'), which is ` +
358
+ `octal to the assembler and a radix this reader does not model`,
359
+ );
360
+ };
361
+
262
362
  // Evaluate a constant integer expression (the assembler's hi/lo split: hex/dec literals with
263
363
  // `+ - * << >> & | ^ ~` and parentheses). Precedence-climbing; C-like precedence. A shift `>>` is
264
364
  // LOGICAL — Splat's operands are unsigned 32-bit constants. Anything unparsable declines LOUD
@@ -292,6 +392,9 @@ function evalConst(name: string, expr: string): number {
292
392
  return ~unary();
293
393
  }
294
394
  if (/^(0x[0-9a-fA-F]+|\d+)$/.test(t)) {
395
+ if (OCTAL_MAGNITUDE.test(t)) {
396
+ refuseOctal(name, t, expr);
397
+ }
295
398
  return t.toLowerCase().startsWith('0x') ? parseInt(t, 16) : parseInt(t, 10);
296
399
  }
297
400
  return fail();