@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.
- package/README.md +25 -24
- package/package.json +1 -1
- package/src/aggregate.ts +149 -0
- package/src/backend/c.ts +2 -0
- package/src/backend/cfamily.ts +197 -61
- package/src/backend/cpp.ts +3 -1
- package/src/backend/lines.ts +10 -0
- package/src/backend/pascal.ts +47 -16
- package/src/cdecl/parse.ts +21 -0
- package/src/contracts.ts +246 -36
- package/src/declare.ts +69 -20
- package/src/frontend/arm-body.ts +155 -0
- package/src/frontend/asmdata.ts +9 -2
- package/src/frontend/call-plan.ts +1117 -61
- package/src/frontend/callee-body.ts +370 -0
- package/src/frontend/dead-reads.ts +628 -0
- package/src/frontend/device-pins.ts +33 -4
- package/src/frontend/emit.ts +4 -3
- package/src/frontend/fpu.ts +224 -17
- package/src/frontend/frame-objects.ts +1689 -206
- package/src/frontend/frontend.ts +84 -3
- package/src/frontend/high-half.ts +53 -4
- package/src/frontend/jump-table.ts +545 -0
- package/src/frontend/local-object.ts +158 -32
- package/src/frontend/mips.ts +2296 -217
- package/src/frontend/own-struct-return.ts +500 -0
- package/src/frontend/ppc.ts +2748 -245
- package/src/frontend/register-struct-call.ts +399 -0
- package/src/frontend/reloc-symbol.ts +22 -7
- package/src/frontend/ssa.ts +1721 -43
- package/src/frontend/stackargs.ts +199 -72
- package/src/frontend/thumb-entry.ts +340 -0
- package/src/frontend/thumb.ts +2400 -325
- package/src/ir/alias.ts +65 -2
- package/src/ir/bits.ts +1699 -4
- package/src/ir/core.ts +254 -9
- package/src/ir/discipline.ts +86 -21
- package/src/ir/entry.ts +157 -0
- package/src/{structure → ir}/loops.ts +6 -2
- package/src/ir/opcodes.ts +137 -9
- package/src/ir/parse.ts +1 -0
- package/src/ir/simplify.ts +61 -10
- package/src/ir/types.ts +86 -2
- package/src/ir/verify.ts +96 -11
- package/src/l3/argbase.ts +2 -0
- package/src/l3/ast.ts +582 -18
- package/src/l3/basecse.ts +68 -15
- package/src/l3/caseendgoto.ts +223 -0
- package/src/l3/coalesce.ts +42 -14
- package/src/l3/constinit.ts +410 -0
- package/src/l3/dce.ts +53 -9
- package/src/l3/initfirst.ts +42 -11
- package/src/l3/intcell.ts +2 -1
- package/src/l3/narrowdecl.ts +77 -36
- package/src/l3/pollguard.ts +8 -2
- package/src/l3/ptrcell.ts +33 -34
- package/src/l3/ptrfield.ts +123 -8
- package/src/l3/regspell.ts +11 -4
- package/src/l3/reindex.ts +295 -99
- package/src/l3/scopebase.ts +2 -0
- package/src/l3/storage.ts +98 -1
- package/src/l3/symbol-refs.ts +84 -6
- package/src/l3/tailmerge.ts +6 -2
- package/src/l3/typing.ts +108 -15
- package/src/l3/unreduce.ts +6 -5
- package/src/l3/volatileptr.ts +17 -54
- package/src/mangle.ts +30 -0
- package/src/pattern/engine.ts +361 -73
- package/src/pipeline.ts +1039 -38
- package/src/proto-context.ts +1039 -45
- package/src/proto.ts +555 -40
- package/src/raise/aggmembers.ts +290 -0
- package/src/raise/arrays.ts +5 -1
- package/src/raise/const.ts +25 -0
- package/src/raise/declared-volatile.ts +207 -31
- package/src/raise/divpow2.ts +192 -0
- package/src/raise/floathelpers.ts +254 -33
- package/src/raise/gvn.ts +3 -3
- package/src/raise/latch.ts +162 -6
- package/src/raise/memberarrays.ts +10 -4
- package/src/raise/offsetnames.ts +422 -36
- package/src/raise/pairparams.ts +33 -0
- package/src/raise/paramwidth.ts +196 -6
- package/src/raise/pre-recovery.ts +107 -21
- package/src/raise/pre-structure.ts +73 -0
- package/src/raise/recover.ts +12 -5
- package/src/raise/reroll.ts +674 -0
- package/src/raise/shortcircuit.ts +217 -34
- package/src/raise/softdiv.ts +1 -1
- package/src/raise/storesign.ts +153 -0
- package/src/raise/struct-arrays.ts +30 -11
- package/src/raise/structs.ts +396 -33
- package/src/raise/truncload.ts +4 -3
- package/src/raise/unpre.ts +594 -0
- package/src/raise/unthread.ts +400 -0
- package/src/rank-declare.ts +118 -12
- package/src/rank-variations.ts +186 -35
- package/src/rank.ts +536 -68
- package/src/runtime-helpers.ts +140 -22
- package/src/structure/analysis.ts +217 -8
- package/src/structure/arith-ops.ts +23 -0
- package/src/structure/cross-jumped-tail.ts +216 -0
- package/src/structure/globalaccess.ts +157 -10
- package/src/structure/hazards.ts +16 -64
- package/src/structure/local-statics.ts +145 -9
- package/src/structure/namecoalesce.ts +1 -1
- package/src/structure/pointer-spelling.ts +1106 -0
- package/src/structure/redundant-test.ts +1 -1
- package/src/structure/structure.ts +6192 -1213
- package/src/structure/switch-recover.ts +583 -120
- package/src/structure/unfolded-branch-constant.ts +90 -0
- package/src/symbols.ts +58 -15
- package/src/target.ts +1028 -54
- package/src/trace.ts +49 -17
- package/src/variation-definitions.ts +333 -20
- package/src/variation-gates.ts +4 -0
- 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
|
|
7
|
-
// a call whose callee declares one there
|
|
8
|
-
//
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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([
|
|
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 (
|
|
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.
|