@asmlift/core 0.9.0 → 0.11.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 (117) hide show
  1. package/README.md +25 -24
  2. package/package.json +1 -1
  3. package/src/aggregate.ts +149 -0
  4. package/src/backend/c.ts +2 -0
  5. package/src/backend/cfamily.ts +197 -61
  6. package/src/backend/cpp.ts +3 -1
  7. package/src/backend/lines.ts +10 -0
  8. package/src/backend/pascal.ts +47 -16
  9. package/src/cdecl/parse.ts +21 -0
  10. package/src/contracts.ts +246 -36
  11. package/src/declare.ts +69 -20
  12. package/src/frontend/arm-body.ts +155 -0
  13. package/src/frontend/asmdata.ts +9 -2
  14. package/src/frontend/call-plan.ts +1117 -61
  15. package/src/frontend/callee-body.ts +370 -0
  16. package/src/frontend/dead-reads.ts +628 -0
  17. package/src/frontend/device-pins.ts +33 -4
  18. package/src/frontend/emit.ts +4 -3
  19. package/src/frontend/fpu.ts +224 -17
  20. package/src/frontend/frame-objects.ts +1689 -206
  21. package/src/frontend/frontend.ts +84 -3
  22. package/src/frontend/high-half.ts +53 -4
  23. package/src/frontend/jump-table.ts +545 -0
  24. package/src/frontend/local-object.ts +158 -32
  25. package/src/frontend/mips.ts +2296 -217
  26. package/src/frontend/own-struct-return.ts +500 -0
  27. package/src/frontend/ppc.ts +2748 -245
  28. package/src/frontend/register-struct-call.ts +399 -0
  29. package/src/frontend/reloc-symbol.ts +22 -7
  30. package/src/frontend/ssa.ts +1721 -43
  31. package/src/frontend/stackargs.ts +199 -72
  32. package/src/frontend/thumb-entry.ts +340 -0
  33. package/src/frontend/thumb.ts +2400 -325
  34. package/src/ir/alias.ts +65 -2
  35. package/src/ir/bits.ts +1699 -4
  36. package/src/ir/core.ts +254 -9
  37. package/src/ir/discipline.ts +86 -21
  38. package/src/ir/entry.ts +157 -0
  39. package/src/{structure → ir}/loops.ts +6 -2
  40. package/src/ir/opcodes.ts +137 -9
  41. package/src/ir/parse.ts +1 -0
  42. package/src/ir/simplify.ts +61 -10
  43. package/src/ir/types.ts +86 -2
  44. package/src/ir/verify.ts +96 -11
  45. package/src/l3/argbase.ts +2 -0
  46. package/src/l3/ast.ts +582 -18
  47. package/src/l3/basecse.ts +68 -15
  48. package/src/l3/caseendgoto.ts +223 -0
  49. package/src/l3/coalesce.ts +42 -14
  50. package/src/l3/constinit.ts +410 -0
  51. package/src/l3/dce.ts +53 -9
  52. package/src/l3/initfirst.ts +42 -11
  53. package/src/l3/intcell.ts +2 -1
  54. package/src/l3/narrowdecl.ts +77 -36
  55. package/src/l3/pollguard.ts +8 -2
  56. package/src/l3/ptrcell.ts +33 -34
  57. package/src/l3/ptrfield.ts +123 -8
  58. package/src/l3/regspell.ts +11 -4
  59. package/src/l3/reindex.ts +295 -99
  60. package/src/l3/scopebase.ts +2 -0
  61. package/src/l3/storage.ts +98 -1
  62. package/src/l3/symbol-refs.ts +84 -6
  63. package/src/l3/tailmerge.ts +6 -2
  64. package/src/l3/typing.ts +108 -15
  65. package/src/l3/unreduce.ts +6 -5
  66. package/src/l3/volatileptr.ts +17 -54
  67. package/src/mangle.ts +30 -0
  68. package/src/pattern/engine.ts +361 -73
  69. package/src/pipeline.ts +1039 -38
  70. package/src/proto-context.ts +1039 -45
  71. package/src/proto.ts +555 -40
  72. package/src/raise/aggmembers.ts +290 -0
  73. package/src/raise/arrays.ts +5 -1
  74. package/src/raise/const.ts +25 -0
  75. package/src/raise/declared-volatile.ts +207 -31
  76. package/src/raise/divpow2.ts +192 -0
  77. package/src/raise/floathelpers.ts +254 -33
  78. package/src/raise/gvn.ts +3 -3
  79. package/src/raise/latch.ts +162 -6
  80. package/src/raise/memberarrays.ts +10 -4
  81. package/src/raise/offsetnames.ts +422 -36
  82. package/src/raise/pairparams.ts +33 -0
  83. package/src/raise/paramwidth.ts +196 -6
  84. package/src/raise/pre-recovery.ts +107 -21
  85. package/src/raise/pre-structure.ts +73 -0
  86. package/src/raise/recover.ts +12 -5
  87. package/src/raise/reroll.ts +674 -0
  88. package/src/raise/shortcircuit.ts +217 -34
  89. package/src/raise/softdiv.ts +1 -1
  90. package/src/raise/storesign.ts +153 -0
  91. package/src/raise/struct-arrays.ts +30 -11
  92. package/src/raise/structs.ts +396 -33
  93. package/src/raise/truncload.ts +4 -3
  94. package/src/raise/unpre.ts +594 -0
  95. package/src/raise/unthread.ts +400 -0
  96. package/src/rank-declare.ts +118 -12
  97. package/src/rank-variations.ts +186 -35
  98. package/src/rank.ts +536 -68
  99. package/src/runtime-helpers.ts +140 -22
  100. package/src/structure/analysis.ts +217 -8
  101. package/src/structure/arith-ops.ts +23 -0
  102. package/src/structure/cross-jumped-tail.ts +216 -0
  103. package/src/structure/globalaccess.ts +157 -10
  104. package/src/structure/hazards.ts +16 -64
  105. package/src/structure/local-statics.ts +145 -9
  106. package/src/structure/namecoalesce.ts +1 -1
  107. package/src/structure/pointer-spelling.ts +1106 -0
  108. package/src/structure/redundant-test.ts +1 -1
  109. package/src/structure/structure.ts +6192 -1213
  110. package/src/structure/switch-recover.ts +583 -120
  111. package/src/structure/unfolded-branch-constant.ts +90 -0
  112. package/src/symbols.ts +58 -15
  113. package/src/target.ts +1028 -54
  114. package/src/trace.ts +49 -17
  115. package/src/variation-definitions.ts +333 -20
  116. package/src/variation-gates.ts +4 -0
  117. package/src/variation-tokens.ts +68 -3
package/src/ir/verify.ts CHANGED
@@ -3,30 +3,57 @@
3
3
  // 1. every block ends in exactly one terminator (and it is the last op)
4
4
  // 2. operands well-formed: opcode registered, correct arity/attrs
5
5
  // 3. SSA: each value defined once; every use is defined; def dominates use
6
- // 4. a float value is an operand or result of a float op or `ret` only — or a `double` operand of
7
- // a call whose callee declares one there — and crosses an edge only into a block parameter of
8
- // its own type; an `fconst` is a double, sixteen hex digits into an f64
6
+ // 4. a float value is an operand or result of a float op, a conversion or a compare, or `ret` only
7
+ // — or a `double` operand of a call whose callee declares one there, or the result of a call
8
+ // whose callee is declared to return a `float` — and crosses an edge only
9
+ // into a block parameter of its own type; an `fconst` is a double, sixteen hex digits into an f64
9
10
  // 5. side data: a fn that carries a write-order record carries one for EVERY block, with every
10
11
  // ordinal inside that block's own write count (ir/core.ts `WriteOrder`)
11
12
  import { Block, Fn, Op, Value, dominators } from './core';
12
13
  import { isDoubleBits } from './float-bits';
13
- import { FLOAT_OPS, WIDE_BITS, opSig } from './opcodes';
14
+ import { FLOAT_COMPUTING, FLOAT_OPS, FLOAT_RESIZE, FLOAT_TO_INT, INT_TO_FLOAT, WIDE_BITS, opSig } from './opcodes';
14
15
  import { type IrType, T, intWidth, typeEquals, typeToString } from './types';
15
16
 
16
17
  export class VerifyError extends Error {}
17
18
 
18
19
  /** A call hands a float only to a parameter its callee is declared to take a `double` in — the
19
- * operands the frontend lists in `doubles` — and returns none. */
20
- function doublesDeclared(op: Op): boolean {
21
- const at = op.attrs.doubles;
20
+ * operands the frontend lists in `doubles` — and returns one only where its callee is declared to
21
+ * return a `float` (`floatReturn`), as that single. */
22
+ function floatsDeclared(op: Op): boolean {
23
+ const at = Array.isArray(op.attrs.doubles) ? op.attrs.doubles : [];
22
24
  return (
23
25
  op.opcode === 'call' &&
24
- Array.isArray(at) &&
25
- op.results.every((r) => r.type.kind !== 'float') &&
26
+ op.results.every(
27
+ (r) => r.type.kind !== 'float' || (op.attrs.floatReturn === 'float' && typeEquals(r.type, T.f32())),
28
+ ) &&
26
29
  op.operands.every((o, i) => o.type.kind !== 'float' || (at.includes(i) && typeEquals(o.type, T.f64())))
27
30
  );
28
31
  }
29
32
 
33
+ /** The ops where a float meets an integer or the other precision, each held to the one shape it
34
+ * has: a conversion takes a float of either stated precision or none and gives an integer, or the
35
+ * reverse; a resize takes its own source width to its own destination width; a compare takes two
36
+ * floats of one type and gives a 32-bit integer. The integer side may be narrower than a word once
37
+ * recovery has typed it (`(float)x` of a `short` parameter), which converts the value it promotes
38
+ * to; the C spelling casts through `s32`/`s64`. */
39
+ function verifyFloatCrossing(op: Op): void {
40
+ const ins = op.operands.map((o) => o.type);
41
+ const out = op.results[0].type;
42
+ const isInt = (t: IrType) => intWidth(t) !== null;
43
+ const resize = FLOAT_RESIZE[op.opcode];
44
+ const ok = FLOAT_TO_INT.has(op.opcode)
45
+ ? ins[0].kind === 'float' && isInt(out)
46
+ : INT_TO_FLOAT.has(op.opcode)
47
+ ? isInt(ins[0]) && out.kind === 'float'
48
+ : resize !== undefined
49
+ ? typeEquals(ins[0], resize.from === 64 ? T.f64() : T.f32()) &&
50
+ typeEquals(out, resize.to === 64 ? T.f64() : T.f32())
51
+ : ins[0].kind === 'float' && typeEquals(ins[0], ins[1]) && intWidth(out) === 32;
52
+ if (!ok) {
53
+ throw new VerifyError(`'${op.opcode}' does not take ${ins.map(typeToString).join(', ')} to ${typeToString(out)}`);
54
+ }
55
+ }
56
+
30
57
  // Opcodes admitting EITHER a 2-operand register form OR a 1-operand + `imm` attr form.
31
58
  const TWO_OR_IMM = new Set(['sdiv', 'shl', 'shr_u', 'shr_s']);
32
59
 
@@ -41,7 +68,21 @@ const TWO_OR_IMM = new Set(['sdiv', 'shl', 'shr_u', 'shr_s']);
41
68
  // `unknown` PARTICIPATES, and that is the whole point: at L1 every value is `unknown`, so a rule
42
69
  // that skipped it would be vacuous exactly where this is meant to be red. The shifts count only
43
70
  // their SHIFTED operand — a 64-bit shift by a 32-bit count is what the machine does.
44
- const WIDTH_UNIFORM = new Set(['add', 'sub', 'mul', 'and', 'or', 'xor', 'neg', 'not', 'sdiv', 'udiv', 'smod', 'umod']);
71
+ const WIDTH_UNIFORM = new Set([
72
+ 'add',
73
+ 'sub',
74
+ 'mul',
75
+ 'and',
76
+ 'or',
77
+ 'xor',
78
+ 'neg',
79
+ 'neg_s',
80
+ 'not',
81
+ 'sdiv',
82
+ 'udiv',
83
+ 'smod',
84
+ 'umod',
85
+ ]);
45
86
  // The shifts count only their SHIFTED operand: a 64-bit value shifted by a 32-bit count is what
46
87
  // every one of these machines does. The comparisons count only their OPERANDS: the result of a C
47
88
  // comparison is an `int` whatever it compared, which is what `raise/recover.ts` already types it.
@@ -170,6 +211,7 @@ export function verify(fn: Fn): void {
170
211
  // second as `u8 name[count]`, so a typed element there would be a type nothing pinned —
171
212
  // the exact answer the audit refuses to invent. (One BYTE encodes like a real `u8`
172
213
  // scalar either way; the declaration is the same one, so nothing has to tell them apart.)
214
+ // A part's element, where its own accesses pin one, rides beside them as `elem` below.
173
215
  if (op.opcode === 'laddr') {
174
216
  const count = op.attrs.count;
175
217
  if (typeof count !== 'number' || !Number.isInteger(count) || count < 1) {
@@ -181,6 +223,47 @@ export function verify(fn: Fn): void {
181
223
  `got width ${String(op.attrs.width)}, signed ${String(op.attrs.signed)}`,
182
224
  );
183
225
  }
226
+ // …and a PART of the one object (frame-objects.ts `provenParts`) is such storage too
227
+ if (op.attrs.part !== undefined && op.attrs.part !== true) {
228
+ throw new VerifyError(`'laddr' part must be true, got ${String(op.attrs.part)}`);
229
+ }
230
+ if (op.attrs.part === true && count < 2) {
231
+ throw new VerifyError(`'laddr' part must be storage of two or more bytes, got ${count}`);
232
+ }
233
+ // …whose ELEMENT its accesses may agree on (frame-objects.ts `stampPartElements`): a
234
+ // width that tiles the part, negative for a signed narrow one, never the default `u8`
235
+ const elem = op.attrs.elem;
236
+ if (
237
+ elem !== undefined &&
238
+ (op.attrs.part !== true ||
239
+ op.attrs.views !== undefined ||
240
+ ![2, 4, -1, -2].includes(elem as number) ||
241
+ count % Math.abs(elem as number) !== 0)
242
+ ) {
243
+ throw new VerifyError(
244
+ `'laddr' elem is a part's element width (2, 4; -1, -2 signed) tiling its ${count} bytes, ` +
245
+ `got ${String(elem)}${op.attrs.part === true ? '' : ' on storage that is no part'}`,
246
+ );
247
+ }
248
+ // …and storage accessed at two widths over one byte names the VIEWS a union of it
249
+ // declares (frame-objects.ts `unionViews`): one per element width in bytes, negative
250
+ // for a signed narrow one, each tiling the storage — so at least two, distinct, of a
251
+ // width a view is named for, and a word never signed-marked (it has one view).
252
+ const views = op.attrs.views;
253
+ if (views !== undefined) {
254
+ const ok =
255
+ Array.isArray(views) &&
256
+ count > 1 &&
257
+ views.length >= 2 &&
258
+ new Set(views).size === views.length &&
259
+ views.every((v) => [1, 2, 4, -1, -2].includes(v) && count % Math.abs(v) === 0);
260
+ if (!ok) {
261
+ throw new VerifyError(
262
+ `'laddr' views must be two or more distinct element widths (1, 2, 4; negative for a signed ` +
263
+ `narrow view) tiling its ${String(count)} bytes, got ${JSON.stringify(views)}`,
264
+ );
265
+ }
266
+ }
184
267
  }
185
268
  // A 64-bit value is BUILT and TAKEN APART by exactly these three, so their shape is
186
269
  // checked here rather than left to whoever reads a half. `concat` is the only producer.
@@ -223,7 +306,9 @@ export function verify(fn: Fn): void {
223
306
  if (floats.length !== all.length || all.some((t) => !typeEquals(t, all[0]))) {
224
307
  throw new VerifyError(`'${op.opcode}' computes on floats only, got ${all.map(typeToString).join(', ')}`);
225
308
  }
226
- } else if (floats.length > 0 && op.opcode !== 'ret' && !doublesDeclared(op)) {
309
+ } else if (FLOAT_COMPUTING.has(op.opcode)) {
310
+ verifyFloatCrossing(op);
311
+ } else if (floats.length > 0 && op.opcode !== 'ret' && !floatsDeclared(op)) {
227
312
  throw new VerifyError(`a float value reaches '${op.opcode}', which does not compute on floats`);
228
313
  }
229
314
  // A literal's width is its pattern's, so the pattern and the result must name one width.
package/src/l3/argbase.ts CHANGED
@@ -208,6 +208,8 @@ export function materializeArgBases(sfn: SFn): SFn | null {
208
208
  break;
209
209
  case 'break':
210
210
  case 'continue':
211
+ case 'goto':
212
+ case 'caseEndGoto':
211
213
  out = s;
212
214
  break;
213
215
  }