@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
@@ -0,0 +1,23 @@
1
+ // asmlift structurer — the IR's integer arithmetic opcodes and the L3 operator each one renders
2
+ // as. The float ops are FLOAT_TO_BIN in structure.ts, which says why they are not here.
3
+ import type { BinOp } from '../l3/ast';
4
+
5
+ export const ARITH_TO_BIN: Record<string, BinOp> = {
6
+ add: '+',
7
+ sub: '-',
8
+ mul: '*',
9
+ sdiv: '/',
10
+ // the UNSIGNED quotient/remainder — the C backend spells them `/`/`%` over an operand it casts
11
+ // unsigned (l3/ast.ts BinOp, backend/cfamily.ts C_SPELLING)
12
+ udiv: '/u',
13
+ smod: '%',
14
+ umod: '%u',
15
+ or: '|',
16
+ and: '&',
17
+ xor: '^',
18
+ shl: '<<',
19
+ shr_u: '>>>', // the LOGICAL right shift; the C backend spells it `>>` over an unsigned operand
20
+ shr_s: '>>',
21
+ logic_and: '&&',
22
+ logic_or: '||', // short-circuit connectives (raise/shortcircuit.ts)
23
+ };
@@ -1,17 +1,29 @@
1
1
  // THE GLOBAL-ACCESS ADDRESS DECOMPOSITION — the pure half of the symbol-map access spellings.
2
2
  //
3
- // Everything here is a function of an `Expr`, a `SymbolInfo` and a width: no structurer state, no
4
- // naming walk, no loop context. It answers three questions the access spellings in structure.ts
5
- // ask over and over — is this address `&gSym` plus something; does that something divide into
6
- // whole elements; and do the terms of it name the DECLARED subscripts of a multidimensional array
7
- // — and it answers them the same way for every caller, which is the point of the split: the
8
- // rank-pinning fallback and the declared-subscript recovery share `bareArrayElement`, so the two
9
- // cannot disagree about what a bare element spelling is.
3
+ // Everything here is a function of an `Expr`, the map's declarations and a width: no structurer
4
+ // state, no naming walk, no loop context. It answers three questions the access spellings in
5
+ // structure.ts ask over and over — is this address `&gSym` plus something; does that something
6
+ // divide into whole elements; and do the terms of it name the DECLARED subscripts of a
7
+ // multidimensional array — and it answers them the same way for every caller, which is the point
8
+ // of the split: the rank-pinning fallback and the declared-subscript recovery share
9
+ // `bareArrayElement`, so the two cannot disagree about what a bare element spelling is. The same
10
+ // holds for what the map declares a named member to be (`ptrMemberDecl`), which the access folds
11
+ // and the pointer spelling (pointer-spelling.ts) both read.
10
12
  import type { Expr } from '../l3/ast';
11
- import { type SymbolInfo, arrayInnerExtents } from '../symbols';
13
+ import {
14
+ type DeclaredField,
15
+ type SymbolInfo,
16
+ type SymbolStructField,
17
+ arrayInnerExtents,
18
+ declaredFields,
19
+ isArrayField,
20
+ isBitfieldField,
21
+ isPtrField,
22
+ pointeeFields,
23
+ } from '../symbols';
12
24
 
13
25
  // `&gSym`, possibly wearing the value-context integer cast the additive lowering adds
14
- // (`(u32)&gSym` — see `intifyAddr` in structure.ts's lowerDef): both spell the same link-time
26
+ // (`(u32)&gSym` — see `ARITH_ROWS` in pointer-spelling.ts): both spell the same link-time
15
27
  // constant, so the fold rules match through the cast and every access that CAN spell a named
16
28
  // element still does.
17
29
  // WIDTH 32 ONLY — a NARROWING cast (`(u8)&gSym`, from a zext/sext lowering) is a different
@@ -298,3 +310,93 @@ export function addOffset(idx: Expr, n: number): Expr {
298
310
  ? { k: 'const', value: idx.value + n }
299
311
  : { k: 'bin', op: '+', l: idx, r: { k: 'const', value: n } };
300
312
  }
313
+
314
+ /** May a member be NAMED by an access of this direction, given the qualifiers on its declaration?
315
+ * The named spelling REPLACES a cast through `(u8 *)`, which carries no qualifier at all, so a
316
+ * qualifier the name reintroduces changes what the compiler emits:
317
+ * • `volatile` makes the access observable — the load may no longer be folded or reordered,
318
+ * which is a different instruction sequence (measured: 6 insns where the cast form was 5);
319
+ * • `const` under a STORE is a hard error, where the cast form merely cast the qualifier away.
320
+ * Either way the honest spelling is the cast form, so the member simply is not nameable here. */
321
+ export function memberQualsAllow(f: SymbolStructField, containerConst: boolean | undefined, isStore: boolean): boolean {
322
+ if (f.volatile) {
323
+ return false;
324
+ }
325
+ return !(isStore && (f.const || containerConst));
326
+ }
327
+
328
+ /** The member a `width`-byte access at byte `byte` of a struct global names, `gSym.member`: one at
329
+ * that offset and of that size, no array or bitfield, whose qualifiers allow the access. An ARRAY
330
+ * member would match a byte access by (offset, size) and spell `.x`, which is not an lvalue of
331
+ * that width; a plain read of a BITFIELD's bytes is not a read of its bits. */
332
+ export function structMemberAt(
333
+ si: SymbolInfo,
334
+ byte: number,
335
+ width: number,
336
+ isStore: boolean,
337
+ ): DeclaredField | undefined {
338
+ if (si.shape !== 'struct') {
339
+ return undefined;
340
+ }
341
+ const f = declaredFields(si.layout)?.find(
342
+ (x) => x.offset === byte && x.size === width && !isArrayField(x) && !isBitfieldField(x),
343
+ );
344
+ return f !== undefined && memberQualsAllow(f, si.const, isStore) ? f : undefined;
345
+ }
346
+
347
+ /** The members a symbol's declaration seats: a struct global's own, or a pointer global's
348
+ * pointee's. */
349
+ export interface MemberLookup {
350
+ fieldsOf(name: string): DeclaredField[] | null;
351
+ }
352
+
353
+ /** {@link MemberLookup.fieldsOf} over `info`, MEMOIZED per symbol. `declaredFields` validates every
354
+ * member and returns a fresh sorted copy on every call, and pointer-spelling.ts's `roleOf`
355
+ * asks it for every member operand of every arithmetic op lowered, so an uncached lookup is an
356
+ * O(n log n) allocation on a hot path — inside a `structure()` a ranked run repeats once per
357
+ * candidate, 17,856 times on the largest fan. */
358
+ export function memoFieldsOf(info: (name: string) => SymbolInfo | undefined): MemberLookup['fieldsOf'] {
359
+ const cache = new Map<string, DeclaredField[] | null>();
360
+ return (name) => {
361
+ const hit = cache.get(name);
362
+ if (hit !== undefined) {
363
+ return hit;
364
+ }
365
+ const si = info(name);
366
+ const fields =
367
+ si?.shape === 'struct' ? declaredFields(si.layout) : si?.shape === 'pointer' ? pointeeFields(si.pointee) : null;
368
+ cache.set(name, fields);
369
+ return fields;
370
+ };
371
+ }
372
+
373
+ /** The map member a `field` node NAMES, or null when it names none — THE one resolver for "what
374
+ * does the declaration say about this member", for rules that must reason about a member's type
375
+ * after the access rules have already spelled it. Both named spellings resolve, through the same
376
+ * shared gate their spelling passed: `gSym.member` off a struct global's {@link declaredFields},
377
+ * `gPtr->member` off the pointee's ({@link pointeeFields}). A synthesized `field_K` — the
378
+ * recovered-struct spelling, which no map declares — resolves to null, and so does any base that
379
+ * is not a map-shaped global, which is what makes every caller refuse rather than guess.
380
+ *
381
+ * The pointee arm reaches only what the MAP lets it: `pointeeAccess` gates every `gPtr->member`
382
+ * spelling on `spellsAccessType(f.signed, …)`, so a pointer field declaring no signedness — which
383
+ * is every one in the corpus's vendored maps — is never named, and a pointer read one indirection
384
+ * down spells `((s32 *)gQ)[1]`. That is a fact about those maps, not about this code: `SymbolMap`
385
+ * is a caller-supplied input, and one field flips it (`signed: true` on a 4-byte pointer member
386
+ * of a pointee yields `(u8 *)gQ->pInner`, cast and all, pinned in pointer-members.test.ts). So
387
+ * the arm is live and tested, and the byte-arithmetic rule that reads this answer is correct for
388
+ * it — where resolving a pointee member to null would reopen the double-scaling hole silently. */
389
+ export function declaredMemberOf(x: Expr, sym: MemberLookup | undefined): DeclaredField | null {
390
+ if (x.k !== 'field' || x.base.k !== 'var' || sym === undefined) {
391
+ return null;
392
+ }
393
+ return sym.fieldsOf(x.base.name)?.find((f) => f.name === x.name) ?? null;
394
+ }
395
+
396
+ /** The map member a `field` node names when the declaration makes it a POINTER — {@link
397
+ * isPtrField} being the shared two-fact test, so this and the synthesized declaration cannot
398
+ * disagree about what a member is. */
399
+ export function ptrMemberDecl(x: Expr, sym: MemberLookup | undefined): DeclaredField | null {
400
+ const f = declaredMemberOf(x, sym);
401
+ return f !== null && isPtrField(f) ? f : null;
402
+ }