@asmlift/core 0.5.0 → 0.7.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 (94) 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 +270 -171
  5. package/src/backend/cpp.ts +1 -0
  6. package/src/backend/pascal.ts +26 -12
  7. package/src/contracts.ts +243 -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 +78 -0
  14. package/src/ir/bits.ts +75 -0
  15. package/src/ir/core.ts +345 -2
  16. package/src/ir/opcodes.ts +176 -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/advance.ts +373 -0
  24. package/src/l3/argbase.ts +6 -5
  25. package/src/l3/ast.ts +510 -59
  26. package/src/l3/basecse.ts +686 -78
  27. package/src/l3/coalesce.ts +432 -46
  28. package/src/l3/dce.ts +31 -9
  29. package/src/l3/gates.ts +96 -1
  30. package/src/l3/hoist.ts +293 -14
  31. package/src/l3/homesplit.ts +285 -0
  32. package/src/l3/initfirst.ts +301 -0
  33. package/src/l3/inlinebase.ts +193 -0
  34. package/src/l3/mentions.ts +176 -0
  35. package/src/l3/mulfirst.ts +42 -0
  36. package/src/l3/nearbase.ts +152 -0
  37. package/src/l3/offmember.ts +371 -0
  38. package/src/l3/parkfirst.ts +96 -0
  39. package/src/l3/pollguard.ts +154 -0
  40. package/src/l3/ptrfield.ts +227 -0
  41. package/src/l3/regspell.ts +114 -89
  42. package/src/l3/reindex.ts +722 -80
  43. package/src/l3/scopebase.ts +649 -220
  44. package/src/l3/sinkinit.ts +40 -0
  45. package/src/l3/slotorder.ts +123 -0
  46. package/src/l3/storage.ts +48 -0
  47. package/src/l3/symbol-refs.ts +41 -8
  48. package/src/l3/tailmerge.ts +16 -1
  49. package/src/l3/typing.ts +198 -9
  50. package/src/l3/unmerge.ts +687 -0
  51. package/src/l3/unreduce.ts +971 -0
  52. package/src/l3/volatileptr.ts +207 -0
  53. package/src/l3/volatileval.ts +130 -0
  54. package/src/l3/volstore.ts +229 -0
  55. package/src/l3/zerosub.ts +62 -0
  56. package/src/pattern/engine.ts +239 -16
  57. package/src/pipeline.ts +173 -60
  58. package/src/proto.ts +112 -14
  59. package/src/raise/arrays.ts +6 -1
  60. package/src/raise/const.ts +203 -3
  61. package/src/raise/divpow2.ts +4 -4
  62. package/src/raise/extscale.ts +342 -0
  63. package/src/raise/globalshape.ts +1058 -0
  64. package/src/raise/gvn.ts +33 -18
  65. package/src/raise/latch.ts +126 -0
  66. package/src/raise/magicdiv.ts +2 -2
  67. package/src/raise/memberarrays.ts +594 -0
  68. package/src/raise/narrow.ts +124 -0
  69. package/src/raise/narrowlocal.ts +572 -0
  70. package/src/raise/paramwidth.ts +201 -0
  71. package/src/raise/pre-recovery.ts +169 -21
  72. package/src/raise/recover.ts +56 -23
  73. package/src/raise/retsink.ts +585 -19
  74. package/src/raise/shortcircuit.ts +1050 -89
  75. package/src/raise/struct-arrays.ts +19 -2
  76. package/src/raise/structs.ts +34 -4
  77. package/src/raise/tailsink.ts +126 -0
  78. package/src/rank-declare.ts +256 -0
  79. package/src/rank-variations.ts +760 -0
  80. package/src/rank.ts +2122 -326
  81. package/src/structure/analysis.ts +1398 -150
  82. package/src/structure/bitfields.ts +432 -0
  83. package/src/structure/globalaccess.ts +300 -0
  84. package/src/structure/hazards.ts +411 -20
  85. package/src/structure/loops.ts +2 -49
  86. package/src/structure/namecoalesce.ts +454 -0
  87. package/src/structure/structure.ts +3979 -612
  88. package/src/structure/switch-recover.ts +710 -145
  89. package/src/symbols.ts +188 -6
  90. package/src/target.ts +495 -32
  91. package/src/trace.ts +112 -33
  92. package/src/variation-definitions.ts +1540 -0
  93. package/src/variation-gates.ts +89 -0
  94. package/src/variation-tokens.ts +355 -0
@@ -0,0 +1,124 @@
1
+ // A NARROW LOOP VARIABLE, kept zero-extended and read sign-extended.
2
+ //
3
+ // agbcc holds an `s16`/`s8` local in its ZERO-extended form and sign-extends it at each signed use,
4
+ // so `for (s16 i = 0; i <= 5; i++)` leaves two extensions of the same increment side by side — one
5
+ // for the back edge, one for the loop test:
6
+ //
7
+ // %21 = zext %20 {16} what `i` holds next iteration
8
+ // %22 = sext %20 {16} the loop test
9
+ //
10
+ // `sext(zext(v,w),w) === sext(v,w)` — the zext keeps the low `w` bits and the sext reads only
11
+ // those — so `%22` IS `sext %21`: a function of the value the loop variable will hold. Spelled
12
+ // through `%20` it reaches the variable's PRE-update value, and the structurer's pre-update guard
13
+ // refuses that (rightly — rendered as the loop variable it would be one iteration off). Re-rooted
14
+ // on `%21` it is `(s16)i`, which is what the source wrote.
15
+ //
16
+ // The same fact has a second spelling, which the cast idiom cannot fold because the value under the
17
+ // shifts is not a bare `shl`: agbcc keeps `i << 16` live when the body wants `i` scaled, and adds
18
+ // the increment in the shifted domain.
19
+ //
20
+ // %133 = shr_u %132 {16} what `i` holds next iteration
21
+ // %134 = shr_s %132 {16} the loop test
22
+ //
23
+ // A signed and an unsigned right shift of the same value by the same amount differ only in the bits
24
+ // the shift brings in, so `%134` is `sext(%133, 32 - 16)` — the same rewrite, and the reason this
25
+ // pass matches the pair rather than either extension on its own.
26
+ import { Fn, Op, Value, defOpMap } from '../ir/core';
27
+ import { CAST_WIDTHS, Opcode } from '../ir/opcodes';
28
+
29
+ /** WHICH BITS a narrowing op keeps, as a key two ops can be compared on. The two spellings live in
30
+ * different bit-domains — `zext {width:w}` keeps the LOW `w` bits, `shr_u {imm:k}` keeps bits
31
+ * `[k,32)` and moves them down — so a shared integer key would pair a `zext` with a `shr_s` of the
32
+ * same operand and rewrite `x >> 16` into `(s16)(u16)x`: a different value, silently. `width` is
33
+ * the C cast the pair collapses to. */
34
+ const domainOf = (op: Op, low: Opcode, high: Opcode): { key: string; width: number } | null => {
35
+ if (op.opcode === low && CAST_WIDTHS.has(op.attrs.width as number)) {
36
+ return { key: `low${op.attrs.width}`, width: op.attrs.width as number };
37
+ }
38
+ if (op.opcode === high && typeof op.attrs.imm === 'number' && CAST_WIDTHS.has(32 - op.attrs.imm)) {
39
+ return { key: `high${op.attrs.imm}`, width: 32 - (op.attrs.imm as number) };
40
+ }
41
+ return null;
42
+ };
43
+
44
+ /** Re-root a sign extension on the co-existing zero extension of the same bits. Returns the number
45
+ * of rewrites. */
46
+ export function rerootNarrowReads(fn: Fn): number {
47
+ // LOOP-CARRIED edge arguments only. The rewrite is sound for ANY co-existing pair — it only
48
+ // re-associates two extensions of the same bits — so this gate is about what it BUYS, not about
49
+ // what is safe. It buys a name: an argument whose own operand chain reads the parameter it feeds
50
+ // is that parameter's next value, i.e. a loop variable, which the structurer materializes and
51
+ // renders `(s16)i`. Anywhere else the re-root only puts `(s16)(u16)x` where `(s16)x` stood —
52
+ // byte-identical through agbcc, so noise for nothing.
53
+ const defs = defOpMap(fn);
54
+ const feedsBack = (from: Value, param: Value): boolean => {
55
+ const seen = new Set<Value>();
56
+ for (const stack = [from]; stack.length;) {
57
+ const v = stack.pop()!;
58
+ if (v === param) {
59
+ return true;
60
+ }
61
+ if (seen.has(v)) {
62
+ continue;
63
+ }
64
+ seen.add(v);
65
+ for (const o of defs.get(v)?.operands ?? []) {
66
+ stack.push(o);
67
+ }
68
+ }
69
+ return false;
70
+ };
71
+ const carried = new Set<Value>();
72
+ for (const b of fn.blocks) {
73
+ for (const op of b.ops) {
74
+ for (const s of op.successors) {
75
+ s.args.forEach((a, i) => {
76
+ const param = s.block.params[i];
77
+ if (param !== undefined && feedsBack(a, param)) {
78
+ carried.add(a);
79
+ }
80
+ });
81
+ }
82
+ }
83
+ }
84
+ let rewritten = 0;
85
+ for (const b of fn.blocks) {
86
+ // Same block, zero extension FIRST: the rewrite makes the sign extension read it, so anywhere
87
+ // else this could be a use before a def. That is the shape the idiom emits — one increment,
88
+ // both extensions of it, in the block that computes it.
89
+ const unsigned = new Map<Value, Map<string, Value>>();
90
+ for (const op of b.ops) {
91
+ const u = domainOf(op, 'zext', 'shr_u');
92
+ if (u !== null && carried.has(op.results[0])) {
93
+ const byDomain = unsigned.get(op.operands[0]) ?? new Map<string, Value>();
94
+ byDomain.set(u.key, op.results[0]);
95
+ unsigned.set(op.operands[0], byDomain);
96
+ continue;
97
+ }
98
+ const sgn = domainOf(op, 'sext', 'shr_s');
99
+ if (sgn === null) {
100
+ continue;
101
+ }
102
+ const zx = unsigned.get(op.operands[0])?.get(sgn.key);
103
+ if (zx === undefined) {
104
+ continue;
105
+ }
106
+ // Both spellings collapse to the one op the backend prints as a cast, so the `shr_s` form
107
+ // drops its `imm` with the rest of its attrs. Rewritten IN PLACE, against the engine's usual
108
+ // discipline (pattern/engine.ts), because the result `Value` identity has to survive: every
109
+ // existing use of the sign extension must keep reading it.
110
+ //
111
+ // SLOT HOMES (ir/core.ts `SlotHomes`) do not move here, and unlike the other two passes that
112
+ // write an operand directly this one had to be checked rather than read off the site. It
113
+ // substitutes a new READ operand, it does not retire a value: the extension's source keeps
114
+ // its use in the `zext` that `zx` is the result of, so a home stamped on it is still carried
115
+ // by a value the graph reaches, and no function-wide replacement is owed the propagation
116
+ // `replaceAllUsesWith` does.
117
+ op.opcode = 'sext';
118
+ op.operands = [zx];
119
+ op.attrs = { width: sgn.width };
120
+ rewritten++;
121
+ }
122
+ }
123
+ return rewritten;
124
+ }