@asmlift/core 0.5.0 → 0.6.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 (86) hide show
  1. package/README.md +22 -16
  2. package/package.json +1 -1
  3. package/src/backend/c.ts +1 -0
  4. package/src/backend/cfamily.ts +238 -167
  5. package/src/backend/cpp.ts +1 -0
  6. package/src/backend/pascal.ts +26 -12
  7. package/src/contracts.ts +194 -39
  8. package/src/declare.ts +41 -4
  9. package/src/frontend/mips.ts +11 -0
  10. package/src/frontend/ppc.ts +43 -7
  11. package/src/frontend/ssa.ts +404 -29
  12. package/src/frontend/thumb.ts +2176 -686
  13. package/src/ir/alias.ts +54 -0
  14. package/src/ir/bits.ts +75 -0
  15. package/src/ir/core.ts +337 -2
  16. package/src/ir/opcodes.ts +140 -21
  17. package/src/ir/parse.ts +19 -2
  18. package/src/ir/print.ts +27 -2
  19. package/src/ir/simplify.ts +190 -3
  20. package/src/ir/struct-names.ts +42 -0
  21. package/src/ir/verify.ts +43 -49
  22. package/src/l3/address.ts +62 -0
  23. package/src/l3/argbase.ts +2 -1
  24. package/src/l3/ast.ts +464 -57
  25. package/src/l3/basecse.ts +664 -76
  26. package/src/l3/coalesce.ts +429 -43
  27. package/src/l3/dce.ts +31 -9
  28. package/src/l3/gates.ts +21 -0
  29. package/src/l3/hoist.ts +293 -14
  30. package/src/l3/homesplit.ts +285 -0
  31. package/src/l3/initfirst.ts +301 -0
  32. package/src/l3/inlinebase.ts +193 -0
  33. package/src/l3/mentions.ts +113 -0
  34. package/src/l3/mulfirst.ts +42 -0
  35. package/src/l3/nearbase.ts +152 -0
  36. package/src/l3/offmember.ts +371 -0
  37. package/src/l3/parkfirst.ts +96 -0
  38. package/src/l3/pollguard.ts +154 -0
  39. package/src/l3/ptrfield.ts +227 -0
  40. package/src/l3/regspell.ts +110 -85
  41. package/src/l3/reindex.ts +715 -78
  42. package/src/l3/scopebase.ts +644 -218
  43. package/src/l3/sinkinit.ts +40 -0
  44. package/src/l3/slotorder.ts +123 -0
  45. package/src/l3/storage.ts +48 -0
  46. package/src/l3/symbol-refs.ts +41 -8
  47. package/src/l3/tailmerge.ts +15 -0
  48. package/src/l3/typing.ts +198 -9
  49. package/src/l3/unmerge.ts +263 -0
  50. package/src/l3/unreduce.ts +971 -0
  51. package/src/l3/volatileptr.ts +207 -0
  52. package/src/l3/volatileval.ts +130 -0
  53. package/src/l3/volstore.ts +229 -0
  54. package/src/l3/zerosub.ts +62 -0
  55. package/src/pattern/engine.ts +236 -13
  56. package/src/pipeline.ts +157 -56
  57. package/src/proto.ts +112 -14
  58. package/src/raise/arrays.ts +6 -1
  59. package/src/raise/divpow2.ts +2 -2
  60. package/src/raise/globalshape.ts +1038 -0
  61. package/src/raise/gvn.ts +33 -18
  62. package/src/raise/latch.ts +126 -0
  63. package/src/raise/memberarrays.ts +594 -0
  64. package/src/raise/narrow.ts +124 -0
  65. package/src/raise/narrowlocal.ts +556 -0
  66. package/src/raise/paramwidth.ts +179 -0
  67. package/src/raise/pre-recovery.ts +97 -14
  68. package/src/raise/recover.ts +56 -23
  69. package/src/raise/retsink.ts +210 -10
  70. package/src/raise/shortcircuit.ts +474 -74
  71. package/src/raise/struct-arrays.ts +19 -2
  72. package/src/raise/structs.ts +33 -3
  73. package/src/rank-axes.ts +630 -0
  74. package/src/rank-declare.ts +256 -0
  75. package/src/rank.ts +1723 -272
  76. package/src/structure/analysis.ts +1392 -141
  77. package/src/structure/bitfields.ts +332 -0
  78. package/src/structure/globalaccess.ts +274 -0
  79. package/src/structure/hazards.ts +411 -20
  80. package/src/structure/loops.ts +2 -49
  81. package/src/structure/namecoalesce.ts +435 -0
  82. package/src/structure/structure.ts +2678 -526
  83. package/src/structure/switch-recover.ts +616 -144
  84. package/src/symbols.ts +62 -1
  85. package/src/target.ts +367 -24
  86. package/src/trace.ts +111 -32
@@ -18,7 +18,9 @@
18
18
  // UNAMBIGUOUS here (the scaled operand is the index, read from the machine code) — the unscaled
19
19
  // `add(x, y)` byte form stays out of scope (genuinely ambiguous without types).
20
20
  import { Fn, Op, Value, defOpMap, mkOp } from '../ir/core';
21
+ import { nextStructIndex } from '../ir/struct-names';
21
22
  import { IrType, StructField, T, scalarTypeForAccess } from '../ir/types';
23
+ import { collectStructs } from './structs';
22
24
 
23
25
  interface Scaled {
24
26
  base: Value;
@@ -94,6 +96,15 @@ function withPadding(dataFields: StructField[], stride: number): StructField[] {
94
96
  export function recognizeStructArrays(fn: Fn): number {
95
97
  const defs = defOpMap(fn);
96
98
  let count = 0;
99
+ // The NAME index is not the recognition count: `count` is this function's return value — how
100
+ // many groups it recognized — and seeding it would report the wrong number. The index starts
101
+ // past every `Elem<N>` the graph already mentions; over the corpus that scan returns 0 every
102
+ // time, because this pass runs once per function and nothing else mints the prefix, and
103
+ // ir/struct-names.ts states why the computed 0 is kept over the assumed one.
104
+ let name = nextStructIndex(
105
+ [...collectStructs(fn)].map((s) => s.name),
106
+ 'Elem',
107
+ );
97
108
 
98
109
  // group candidate element pointers by base, tracking each add's own index and stride
99
110
  const byBase = new Map<Value, { add: Op; index: Value; stride: number }[]>();
@@ -222,7 +233,7 @@ export function recognizeStructArrays(fn: Fn): number {
222
233
  type: scalarTypeForAccess(width, loadSigned ?? width === 4),
223
234
  name: `field_${off}`,
224
235
  }));
225
- const elemStruct = T.struct(`Elem${count}`, withPadding(dataFields, stride), stride);
236
+ const elemStruct = T.struct(`Elem${name++}`, withPadding(dataFields, stride), stride);
226
237
  base.type = T.ptr(elemStruct);
227
238
 
228
239
  // Rewrite each field load/store into an aload/astore carrying base, ITS elem's index,
@@ -242,7 +253,13 @@ export function recognizeStructArrays(fn: Fn): number {
242
253
  bb.ops[i] = mkOp('aload', {
243
254
  operands: [base, index],
244
255
  results: [op.results[0]],
245
- attrs: { elemSize: stride, signed: op.attrs.signed as boolean, fieldOff: op.attrs.off as number },
256
+ // listOrder rides along: an ldmia-expanded load keeps its stream-order caveat as an aload
257
+ attrs: {
258
+ elemSize: stride,
259
+ signed: op.attrs.signed as boolean,
260
+ fieldOff: op.attrs.off as number,
261
+ ...(op.attrs.listOrder === true && { listOrder: true }),
262
+ },
246
263
  });
247
264
  } else if (op.opcode === 'store') {
248
265
  bb.ops[i] = mkOp('astore', {
@@ -22,8 +22,22 @@
22
22
  // base @ {off8 w4} -> array (single aligned access — no struct evidence)
23
23
  // base @ aload(index) -> array (variable index — untouched)
24
24
  //
25
- // This recovery is BYTE-NEUTRAL — `->field_N` and `[idx]` compile identically, so it is a
26
- // representation upgrade driven by access evidence, not a scored lever. GAPS between accessed
25
+ // This recovery is USUALLY BYTE-NEUTRAL — `->field_N` and `[idx]` mostly compile identically, so it
26
+ // is a representation upgrade driven by access evidence rather than a scored lever. TWO
27
+ // MEASUREMENTS SAY "USUALLY" IS THE RIGHT WORD, and both were made on agbcc against a real target
28
+ // object, each pair differing in ONE token:
29
+ // • THE SPELLING. `synthetic:dmanest`'s reference compiles from `((struct Elem0 *)K)[a1].field_4`
30
+ // to a byte-exact match and from `((s32 *)((a1 << 3) + K))[1]` to a 2-point diff — a
31
+ // COMPONENT_REF keeps the offset in the load displacement, an index folds it into the pool
32
+ // literal. The row's own dataset entry carries the reproduction.
33
+ // • THE FIELD TYPE, which this file assigns from the ACCESS WIDTH alone. A word field declared
34
+ // `void *` rather than `s32` changes agbcc's alias set and lets a loop-invariant load leave the
35
+ // loop: `synthetic:dmaptrsrc` matches with the pointer declaration and diffs by 35 without it
36
+ // (its own fan: `/vol-store/unreduce/ptr-field` 0, `/vol-store/unreduce` 35).
37
+ // That is what `l3/ptrfield.ts` offers as a differ-ranked lever.
38
+ // So the neutrality claim is CONDITIONAL, and nothing here says on what. Until it does, read it as
39
+ // "no candidate is enumerated for this axis", not as "the differ could not referee one" — the
40
+ // second reading is the one both measurements above falsify. GAPS between accessed
27
41
  // offsets (unaccessed leading/interior fields) are filled with `u8[N]` PAD fields so the declared
28
42
  // struct reproduces the observed offsets byte-for-byte and is self-describing (the same
29
43
  // discipline raise/struct-arrays.ts withPadding uses). Each accessed field must still be
@@ -31,6 +45,7 @@
31
45
  // at an offset natural C alignment could not place it at) is rejected LOUD, as is an
32
46
  // overlap/union.
33
47
  import { Fn, Op, Value } from '../ir/core';
48
+ import { nextStructIndex } from '../ir/struct-names';
34
49
  import { IrType, StructField, T, scalarTypeForAccess } from '../ir/types';
35
50
  import type { StructType } from '../l3/ast';
36
51
  import { RaiseUnsupportedError } from './errors';
@@ -189,6 +204,10 @@ export function recognizeStructs(fn: Fn): number {
189
204
  }
190
205
  }
191
206
 
207
+ // The NAME counter is seeded from the names already in the graph, not from this pass's own
208
+ // success count: raise/memberarrays.ts runs first and mints `Struct<N>` types of its own, and two
209
+ // different layouts under one name would leave `collectStructs` declaring only one of them.
210
+ let name = firstFreeStructIndex(fn);
192
211
  let count = 0;
193
212
  for (const base of order) {
194
213
  if (arrayBases.has(base)) {
@@ -203,7 +222,7 @@ export function recognizeStructs(fn: Fn): number {
203
222
  continue;
204
223
  } // uniform stride / single aligned access → array
205
224
  try {
206
- base.type = T.ptr(buildStruct(`Struct${count}`, accesses));
225
+ base.type = T.ptr(buildStruct(`Struct${name}`, accesses));
207
226
  } catch (e) {
208
227
  // A NAMED global whose accesses synthesis cannot reconcile is not a reason to decline the
209
228
  // function: its declaration belongs to the project's own headers, and its constant-offset
@@ -219,11 +238,22 @@ export function recognizeStructs(fn: Fn): number {
219
238
  }
220
239
  throw e;
221
240
  }
241
+ name++;
222
242
  count++;
223
243
  }
224
244
  return count;
225
245
  }
226
246
 
247
+ /** The next `Struct<N>` index past every one this function's graph already uses — the shared name
248
+ * allocator for the two passes that synthesize a struct pointee. The scan itself is
249
+ * `ir/struct-names.ts`, which the three struct minters share; this names the prefix. */
250
+ export function firstFreeStructIndex(fn: Fn): number {
251
+ return nextStructIndex(
252
+ [...collectStructs(fn)].map((s) => s.name),
253
+ 'Struct',
254
+ );
255
+ }
256
+
227
257
  /** The distinct struct types this function's L2 GRAPH mentions (unwrapping struct pointers on every
228
258
  * value), deduped by name and sorted, for the backend to declare above the function.
229
259
  *