@asmlift/core 0.8.0 → 0.9.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 (105) hide show
  1. package/README.md +34 -30
  2. package/package.json +1 -1
  3. package/src/aggregate.ts +248 -0
  4. package/src/backend/c.ts +5 -2
  5. package/src/backend/cfamily.ts +236 -37
  6. package/src/backend/cpp.ts +114 -9
  7. package/src/backend/pascal.ts +39 -0
  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 +615 -79
  15. package/src/declare.ts +75 -5
  16. package/src/frontend/asmdata.ts +19 -5
  17. package/src/frontend/call-plan.ts +686 -0
  18. package/src/frontend/device-pins.ts +277 -0
  19. package/src/frontend/errors.ts +5 -0
  20. package/src/frontend/flags-edge.ts +101 -0
  21. package/src/frontend/fpu.ts +167 -0
  22. package/src/frontend/frame-objects.ts +2131 -0
  23. package/src/frontend/high-half.ts +2 -1
  24. package/src/frontend/local-object.ts +490 -0
  25. package/src/frontend/mips.ts +363 -40
  26. package/src/frontend/opaque.ts +97 -18
  27. package/src/frontend/ppc.ts +520 -129
  28. package/src/frontend/reloc-symbol.ts +94 -13
  29. package/src/frontend/splat.ts +108 -5
  30. package/src/frontend/ssa.ts +669 -76
  31. package/src/frontend/stackargs.ts +120 -39
  32. package/src/frontend/thumb.ts +2764 -2018
  33. package/src/ir/alias.ts +29 -0
  34. package/src/ir/core.ts +101 -3
  35. package/src/ir/discipline.ts +248 -0
  36. package/src/ir/float-bits.ts +47 -0
  37. package/src/ir/opcodes.ts +84 -112
  38. package/src/ir/parse.ts +1 -0
  39. package/src/ir/types.ts +118 -2
  40. package/src/ir/verify.ts +150 -3
  41. package/src/l3/address.ts +5 -0
  42. package/src/l3/argcopy.ts +38 -6
  43. package/src/l3/ast.ts +186 -14
  44. package/src/l3/coalesce.ts +21 -13
  45. package/src/l3/dce.ts +32 -10
  46. package/src/l3/gates.ts +9 -3
  47. package/src/l3/hoist.ts +8 -7
  48. package/src/l3/initfirst.ts +27 -9
  49. package/src/l3/intcell.ts +97 -0
  50. package/src/l3/mentions.ts +10 -4
  51. package/src/l3/narrowdecl.ts +218 -0
  52. package/src/l3/nearbase.ts +10 -5
  53. package/src/l3/parkfirst.ts +8 -5
  54. package/src/l3/pollguard.ts +3 -2
  55. package/src/l3/ptrcell.ts +357 -0
  56. package/src/l3/regspell.ts +17 -3
  57. package/src/l3/reindex.ts +10 -7
  58. package/src/l3/symbol-refs.ts +104 -23
  59. package/src/l3/tailmerge.ts +6 -19
  60. package/src/l3/typing.ts +137 -25
  61. package/src/l3/unreduce.ts +4 -4
  62. package/src/l3/volstore.ts +96 -7
  63. package/src/mangle.ts +11 -1
  64. package/src/pattern/engine.ts +19 -16
  65. package/src/pipeline.ts +57 -12
  66. package/src/proto-context.ts +788 -0
  67. package/src/proto.ts +689 -50
  68. package/src/raise/arrays.ts +4 -3
  69. package/src/raise/declared-volatile.ts +131 -0
  70. package/src/raise/divpow2.ts +2 -2
  71. package/src/raise/errors.ts +17 -0
  72. package/src/raise/floathelpers.ts +243 -0
  73. package/src/raise/globalshape.ts +11 -5
  74. package/src/raise/gvn.ts +26 -5
  75. package/src/raise/narrow.ts +29 -2
  76. package/src/raise/narrowlocal.ts +11 -6
  77. package/src/raise/offsetnames.ts +510 -0
  78. package/src/raise/pairparams.ts +81 -0
  79. package/src/raise/paramwidth.ts +7 -0
  80. package/src/raise/pre-recovery.ts +78 -4
  81. package/src/raise/recover.ts +14 -10
  82. package/src/raise/retsink.ts +1 -1
  83. package/src/raise/shortcircuit.ts +41 -19
  84. package/src/raise/softdiv.ts +21 -23
  85. package/src/raise/struct-arrays.ts +5 -4
  86. package/src/raise/structs.ts +311 -98
  87. package/src/raise/truncload.ts +366 -0
  88. package/src/raise/widehelpers.ts +106 -0
  89. package/src/rank-declare.ts +30 -5
  90. package/src/rank-variations.ts +36 -8
  91. package/src/rank.ts +276 -80
  92. package/src/runtime-helpers.ts +287 -0
  93. package/src/stillborn.ts +289 -0
  94. package/src/structure/analysis.ts +1285 -448
  95. package/src/structure/hazards.ts +633 -58
  96. package/src/structure/local-statics.ts +381 -0
  97. package/src/structure/namecoalesce.ts +1 -1
  98. package/src/structure/redundant-test.ts +99 -0
  99. package/src/structure/structure.ts +1772 -395
  100. package/src/structure/switch-recover.ts +159 -78
  101. package/src/symbols.ts +38 -3
  102. package/src/target.ts +662 -59
  103. package/src/trace.ts +44 -24
  104. package/src/variation-definitions.ts +119 -1
  105. package/src/variation-tokens.ts +20 -6
package/src/pipeline.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  assertDerefsTyped,
7
7
  assertEffectsPreserved,
8
8
  assertLocalsWritten,
9
+ assertPostIncrUnshared,
9
10
  assertResolved,
10
11
  assertTypesRecovered,
11
12
  } from './contracts';
@@ -23,10 +24,12 @@ import { eliminateDeadStores } from './l3/dce';
23
24
  import { mergeCommonTails } from './l3/tailmerge';
24
25
  import { dropUnspelledReturns } from './l3/tailret';
25
26
  import { DEFAULT_IDIOM_PATTERNS, RewritePattern, applyPattern, dce, patternApplies } from './pattern/engine';
26
- import { type FnProto, type Prototypes, prototypesFromSymbols } from './proto';
27
+ import { type FnProto, type Prototypes, declaresVoidReturn, prototypesFromSymbols } from './proto';
28
+ import { stampDeclaredVolatileUnder } from './raise/declared-volatile';
27
29
  import { RaiseUnsupportedError } from './raise/errors';
28
30
  import { assumedShapes, inferGlobalArrays, orderLicensedGlobals } from './raise/globalshape';
29
31
  import { foldEmptyLatches } from './raise/latch';
32
+ import { OFFSET_NAME_PASS } from './raise/offsetnames';
30
33
  import { type PreRecoveryOptions, type PreRecoveryPass, runPreRecovery } from './raise/pre-recovery';
31
34
  import { recoverTypes } from './raise/recover';
32
35
  import { sinkReturns } from './raise/retsink';
@@ -109,6 +112,14 @@ export interface DecompileResult {
109
112
  * bare, and every name the caller's own map described (raise/globalshape.ts `assumedShapes`
110
113
  * computes that narrowing and names the corpus row behind each half). */
111
114
  assumedSymbols: SymbolInfo[];
115
+ /** THE NAMES THIS SOURCE REACHED BY ARITHMETIC, not by the map alone (raise/offsetnames.ts).
116
+ * A pool-loaded `gaddr` is the map answering about an address the machine code carries; a
117
+ * walked one is asmlift computing `addr(base) + K` and asking about THAT — so the base's own
118
+ * address in the map is load-bearing for a name the reader sees spelled bare. Sibling of
119
+ * `assumedSymbols` and reported beside it (`main.ts`'s `[walked]` block, the trace's
120
+ * `stage:offsetnames`), and it is also the only thing in a result that says a walk spelling
121
+ * stopped being enumerable here. Empty on every run with no symbol map. */
122
+ walkedNames: string[];
112
123
  }
113
124
 
114
125
  export function decompile(
@@ -144,8 +155,7 @@ function runTower(
144
155
  // CALLEES (a function still in assembly has none), which is what makes this transferable.
145
156
  const prototypes = prototypesFromSymbols(opts.symbols, opts.prototypes ?? {});
146
157
  // (1) lift: ISA frontend (resolved by target) → L1 with block-argument SSA
147
- const fn = frontendFor(target).lift(name, asm, target, prototypes, opts.asmData, opts.symbols);
148
- verify(fn);
158
+ const fn = liftStamped(name, asm, target, prototypes, opts.asmData, opts.symbols);
149
159
  // Every dump carries the write-order record (ir/print.ts `PrintOptions`): it decides the
150
160
  // edge-copy order and the raising folds mutate it, so two dumps compare whole program states.
151
161
  const raw = print(fn, { writeOrder: true });
@@ -157,6 +167,18 @@ function runTower(
157
167
  // …and the ORDER half of the same reading, which reaches names the shape derivation refuses (a
158
168
  // struct element among them). Read off the same lifted fn, for the same reason.
159
169
  const orderLicensed = orderLicensedGlobals(fn, target);
170
+ // (1.6) …and the NAMES the map holds for the addresses this function's machine code built by
171
+ // arithmetic off a named one (raise/offsetnames.ts). After the two readings above, which are
172
+ // about the lift as the frontend emitted it; before everything below, so the whole raising
173
+ // tower sees a walked-to cell as the named global it is.
174
+ //
175
+ // The two readings therefore attribute a walked access to the base rather than to the name this
176
+ // pass gives it, and that costs nothing either way: a SHAPE is a declaration, so a name the map
177
+ // describes — which every base here is, or the pass could not have fired — is dropped from
178
+ // `inferredSymbols`, and the ORDER licence only enables a cast spelling that is byte-correct
179
+ // under any declaration. Measured: with this call moved ABOVE both readings, the three corpus
180
+ // rows the pass fires on keep their fan size, their winner and their score exactly.
181
+ const walkedNames = opts.symbols ? OFFSET_NAME_PASS.run(fn, opts.symbols, target.compilerBehaviors) : [];
160
182
 
161
183
  // (2) idiom fold: apply serializable patterns on the IR (the AI-improvement surface),
162
184
  // gated generically by the Target's capabilities (not an `arch ==` branch).
@@ -172,7 +194,7 @@ function runTower(
172
194
  // every unresolved value spelled as a loud ASMLIFT_ERROR marker (annotate).
173
195
  const mapSymbols = opts.symbols ? symbolsByName(opts.symbols) : undefined;
174
196
  const sfn = structureChecked(fn, {
175
- ...structureOptionsFor(target, prototypes[name]?.returnsVoid ?? false),
197
+ ...structureOptionsFor(target, declaresVoidReturn(prototypes[name]), prototypes),
176
198
  // What the EMITTED LANGUAGE can say is a structuring input wherever two recoveries of one
177
199
  // shape are behaviourally identical and only one of them is printable (switch fall-through
178
200
  // vs plain if-nesting): recovery must not mint a tree this backend would refuse.
@@ -196,9 +218,28 @@ function runTower(
196
218
  // spell bare, and a name the caller's own map described, are both obligations this reader
197
219
  // does not have (raise/globalshape.ts `assumedShapes`).
198
220
  assumedSymbols: assumedShapes(inferredSymbols, sfn, mapSymbols),
221
+ walkedNames,
199
222
  };
200
223
  }
201
224
 
225
+ /** Stage 1 — lift: the target's ISA frontend → L1, verified, with every read of an object
226
+ * `symbols` declares volatile stamped `declared` (raise/declared-volatile.ts) before any pass asks
227
+ * whether it may be deleted. Every entry path lifts through here, each lift under the map its
228
+ * structuring will be handed. */
229
+ export function liftStamped(
230
+ name: string,
231
+ asm: string,
232
+ target: TargetDescription,
233
+ prototypes: Prototypes,
234
+ asmData: AsmData | undefined,
235
+ symbols: SymbolMap | undefined,
236
+ ): Fn {
237
+ const fn = frontendFor(target).lift(name, asm, target, prototypes, asmData, symbols);
238
+ verify(fn);
239
+ stampDeclaredVolatileUnder(fn, symbols, target.compilerBehaviors);
240
+ return fn;
241
+ }
242
+
202
243
  // ── the shared raising tower ────────────────────────────────────────────────────────────────
203
244
  // decompile(), decompileTraced (trace.ts), rank.ts's decompileRanked and the cli's
204
245
  // decompileWithReport + its score probe (report.ts) all raise a lifted fn through the SAME stage
@@ -314,7 +355,7 @@ export function raiseRecovered(
314
355
  /** Run `body`; if it declines, name the unmodelled instructions the function carries.
315
356
  *
316
357
  * An `opaque` degrades its own value AND makes its block impure, so a shape recognizer refuses:
317
- * `headerPure` rejects a header holding one, and the loop declines with "unrecovered back-edge …".
358
+ * a loop none of whose recognizers takes it declines with "unrecovered back-edge …".
318
359
  * True and useless — the shape is fine, an instruction is missing — and the benchmark classifies
319
360
  * declines by that text, so the round is filed as a loop-capability gap and the improvement loop
320
361
  * builds the wrong thing.
@@ -331,20 +372,21 @@ function attributeOpaques<T>(fn: Fn, body: () => T): T {
331
372
  throw e;
332
373
  }
333
374
  const seen = reachableBlocks(fn);
334
- const names = new Set<string>();
375
+ // THE REASONS, not the mnemonics: an `opaque` a recogniser left behind for a runtime helper it
376
+ // could not fold carries no mnemonic at all, and asking for one would attribute the decline to
377
+ // an instruction named `?`.
378
+ const reasons = new Set<string>();
335
379
  for (const b of seen) {
336
380
  for (const op of b.ops) {
337
381
  if (op.opcode === 'opaque') {
338
- names.add(typeof op.attrs.mnemonic === 'string' ? op.attrs.mnemonic : '?');
382
+ reasons.add(gapReasonFor(op.attrs));
339
383
  }
340
384
  }
341
385
  }
342
- if (!names.size || /unmodelled instruction/.test(e.message)) {
386
+ if (!reasons.size || /unmodelled instruction|no model for the runtime helper/.test(e.message)) {
343
387
  throw e;
344
388
  }
345
- // Through `gapReasonFor`, so the classifier sees its canonical text — a hand-written spelling
346
- // misses the mnemonic-anchored classes and every attributed decline lands in the generic bucket.
347
- const list = [...names].sort().map(gapReasonFor).join(', ');
389
+ const list = [...reasons].sort().join(', ');
348
390
  throw new StructureError(`${e.message} — and the function carries ${list}, which is the more likely cause`);
349
391
  }
350
392
  }
@@ -382,11 +424,13 @@ export function structureChecked(
382
424
  assertResolved(raw);
383
425
  assertDerefsTyped(raw);
384
426
  assertLocalsWritten(raw);
427
+ assertPostIncrUnshared(raw);
385
428
  assertEffectsPreserved(fn, raw);
386
429
  // The hoist moves the deref cast from each `index` node onto the local's initializer, so
387
430
  // re-validate deref typing on the rewritten tree.
388
431
  const sfn = readabilityRewrites(raw);
389
432
  assertDerefsTyped(sfn);
433
+ assertPostIncrUnshared(sfn);
390
434
  // Re-checked after the readability rewrites for the same reason deref typing is: a pass that
391
435
  // merges arms or drops statements must not be able to lose or duplicate a call.
392
436
  assertEffectsPreserved(fn, sfn);
@@ -445,8 +489,9 @@ export function stubResult(name: string, asm: string, backend: LanguageBackend,
445
489
  ir: { raw: '', folded: '', recovered: '' },
446
490
  patternHits: 0,
447
491
  diagnostics: [{ stage, reason: msg }],
448
- // A stub spells no global, so it assumes nothing about one.
492
+ // A stub spells no global, so it assumes nothing about one and walked to none.
449
493
  assumedSymbols: [],
494
+ walkedNames: [],
450
495
  };
451
496
  }
452
497