@asmlift/core 0.8.1 → 0.9.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 +34 -30
- package/package.json +1 -1
- package/src/aggregate.ts +248 -0
- package/src/backend/c.ts +5 -2
- package/src/backend/cfamily.ts +236 -37
- package/src/backend/cpp.ts +114 -9
- package/src/backend/pascal.ts +39 -0
- package/src/cdecl/constant.ts +59 -0
- package/src/cdecl/lex.ts +209 -0
- package/src/cdecl/parse.ts +975 -0
- package/src/cdecl/spell.ts +69 -0
- package/src/codegen-flags.ts +38 -6
- package/src/compiler-diagnostics.ts +194 -0
- package/src/contracts.ts +615 -79
- package/src/declare.ts +75 -5
- package/src/frontend/asmdata.ts +19 -5
- package/src/frontend/call-plan.ts +686 -0
- package/src/frontend/device-pins.ts +277 -0
- package/src/frontend/errors.ts +5 -0
- package/src/frontend/flags-edge.ts +101 -0
- package/src/frontend/fpu.ts +167 -0
- package/src/frontend/frame-objects.ts +2131 -0
- package/src/frontend/high-half.ts +2 -1
- package/src/frontend/local-object.ts +490 -0
- package/src/frontend/mips.ts +363 -40
- package/src/frontend/opaque.ts +97 -18
- package/src/frontend/ppc.ts +520 -129
- package/src/frontend/reloc-symbol.ts +94 -13
- package/src/frontend/splat.ts +108 -5
- package/src/frontend/ssa.ts +669 -76
- package/src/frontend/stackargs.ts +120 -39
- package/src/frontend/thumb.ts +2764 -2018
- package/src/ir/alias.ts +29 -0
- package/src/ir/core.ts +101 -3
- package/src/ir/discipline.ts +248 -0
- package/src/ir/float-bits.ts +47 -0
- package/src/ir/opcodes.ts +84 -112
- package/src/ir/parse.ts +1 -0
- package/src/ir/types.ts +118 -2
- package/src/ir/verify.ts +150 -3
- package/src/l3/address.ts +5 -0
- package/src/l3/argcopy.ts +38 -6
- package/src/l3/ast.ts +186 -14
- package/src/l3/coalesce.ts +21 -13
- package/src/l3/dce.ts +32 -10
- package/src/l3/gates.ts +9 -3
- package/src/l3/hoist.ts +8 -7
- package/src/l3/initfirst.ts +27 -9
- package/src/l3/intcell.ts +97 -0
- package/src/l3/mentions.ts +10 -4
- package/src/l3/narrowdecl.ts +218 -0
- package/src/l3/nearbase.ts +10 -5
- package/src/l3/parkfirst.ts +8 -5
- package/src/l3/pollguard.ts +3 -2
- package/src/l3/ptrcell.ts +357 -0
- package/src/l3/regspell.ts +17 -3
- package/src/l3/reindex.ts +10 -7
- package/src/l3/symbol-refs.ts +104 -23
- package/src/l3/tailmerge.ts +6 -19
- package/src/l3/typing.ts +137 -25
- package/src/l3/unreduce.ts +4 -4
- package/src/l3/volstore.ts +96 -7
- package/src/mangle.ts +11 -1
- package/src/pattern/engine.ts +19 -16
- package/src/pipeline.ts +57 -12
- package/src/proto-context.ts +788 -0
- package/src/proto.ts +689 -50
- package/src/raise/arrays.ts +4 -3
- package/src/raise/declared-volatile.ts +131 -0
- package/src/raise/divpow2.ts +2 -2
- package/src/raise/errors.ts +17 -0
- package/src/raise/floathelpers.ts +243 -0
- package/src/raise/globalshape.ts +11 -5
- package/src/raise/gvn.ts +26 -5
- package/src/raise/narrow.ts +29 -2
- package/src/raise/narrowlocal.ts +11 -6
- package/src/raise/offsetnames.ts +510 -0
- package/src/raise/pairparams.ts +81 -0
- package/src/raise/paramwidth.ts +7 -0
- package/src/raise/pre-recovery.ts +78 -4
- package/src/raise/recover.ts +14 -10
- package/src/raise/retsink.ts +1 -1
- package/src/raise/shortcircuit.ts +41 -19
- package/src/raise/softdiv.ts +21 -23
- package/src/raise/struct-arrays.ts +5 -4
- package/src/raise/structs.ts +311 -98
- package/src/raise/truncload.ts +366 -0
- package/src/raise/widehelpers.ts +106 -0
- package/src/rank-declare.ts +30 -5
- package/src/rank-variations.ts +36 -8
- package/src/rank.ts +267 -79
- package/src/runtime-helpers.ts +287 -0
- package/src/stillborn.ts +289 -0
- package/src/structure/analysis.ts +1285 -448
- package/src/structure/hazards.ts +633 -58
- package/src/structure/local-statics.ts +381 -0
- package/src/structure/namecoalesce.ts +1 -1
- package/src/structure/redundant-test.ts +99 -0
- package/src/structure/structure.ts +1772 -395
- package/src/structure/switch-recover.ts +159 -78
- package/src/symbols.ts +38 -3
- package/src/target.ts +662 -59
- package/src/trace.ts +44 -24
- package/src/variation-definitions.ts +119 -1
- package/src/variation-tokens.ts +20 -6
package/src/frontend/opaque.ts
CHANGED
|
@@ -50,13 +50,70 @@ export interface OpaquePolicy {
|
|
|
50
50
|
* destination — so there is nothing to degrade and the instruction is REFUSED, not skipped
|
|
51
51
|
* (`teq zero, zero` is a trap). Default: nothing is zero. */
|
|
52
52
|
isZero?: (r: string) => boolean;
|
|
53
|
+
/** The FPU's register operands in this ISA, and the FPU's ALONE — the message this produces
|
|
54
|
+
* names the floating-point register file in so many words, so a second unmodelled file (MIPS
|
|
55
|
+
* CP0, `hi`/`lo`, PowerPC's condition registers and SPRs) needs its own field and its own
|
|
56
|
+
* sentence rather than one more alternative here. An unmodelled instruction touching one is
|
|
57
|
+
* refused with a message naming that file, because the missing capability is the FILE, not the
|
|
58
|
+
* instruction: there is no value kind to hold what it computes, no ABI home to receive it, and
|
|
59
|
+
* no C spelling to print it. `docs/floating-point.md` measures the gap and prices the layers.
|
|
60
|
+
*
|
|
61
|
+
* Tested against EVERY operand, not just `ops[0]`, and the two shapes that force it are on
|
|
62
|
+
* opposite sides: `fcmpo cr0,f1,f2` has a destination `isReg` REJECTS (a condition register) and
|
|
63
|
+
* `mfc1 v0,$f12` one it ACCEPTS. A destination-only test files the first under "no register
|
|
64
|
+
* destination to degrade" and lets the second fabricate an opaque whose sources silently drop
|
|
65
|
+
* the FP operand it actually read. Both are floating point and neither is about the destination.
|
|
66
|
+
*
|
|
67
|
+
* IT MUST COVER EVERY DIALECT THE FRONTEND READS AND EVERY SPELLING EACH OF THEM USES, because
|
|
68
|
+
* a frontend can be fed more than one name for the same register: MIPS objdump numbers the file
|
|
69
|
+
* (`$f12`), the Splat trees use o32 ABI names (`$ft2`), and those names take a trailing `f` for
|
|
70
|
+
* the odd half of a double-precision pair (`$ft0f`) — so a pattern anchored on a final digit
|
|
71
|
+
* covers two of the three. Every miss is SILENT in the same way: the reader strips the sigil
|
|
72
|
+
* off a token it did not recognise, `isMipsReg` ACCEPTS the bare result, and the gap ends as an
|
|
73
|
+
* opaque on a register in a file nothing models. MIPS answers this with ONE predicate — the
|
|
74
|
+
* exported `MIPS_FP_REG` in `frontend/splat.ts`, which the reader and the frontend both use —
|
|
75
|
+
* because which tokens keep their sigil and which tokens are the FPU's are two halves of one
|
|
76
|
+
* decision, and two copies of it can disagree silently in either direction.
|
|
77
|
+
*
|
|
78
|
+
* A CONTROL TRANSFER IS NOT THIS MODULE'S, even when the FPU is what it is missing. MIPS
|
|
79
|
+
* `bc1t`/`bc1f` test an FP condition code, and PowerPC branch targets are bare lower-case hex
|
|
80
|
+
* that PowerPC's own `fpReg` matches — but a branch has no destination to degrade, so nothing
|
|
81
|
+
* here could answer one, and both frontends refuse unmodelled control flow in a whole-function
|
|
82
|
+
* pre-pass before a block is filled. The report keeps them as their own class for the same
|
|
83
|
+
* reason (`fp-cond-branch` in `apps/web/src/pages/benchmark/lib/declines.ts`): the missing file
|
|
84
|
+
* is shared, the mechanism is not.
|
|
85
|
+
*
|
|
86
|
+
* It is consulted FIRST, ahead of `storeClass`, so `swc1`/`stfs` are named by the file they move
|
|
87
|
+
* rather than by the fact that they move it to memory. That ordering is what the web report's
|
|
88
|
+
* decline table already assumes, and it could only assume it by re-deriving "is this floating
|
|
89
|
+
* point?" from a list of mnemonics this module never told it.
|
|
90
|
+
*
|
|
91
|
+
* REQUIRED, and `null` is the way to say "this ISA has no FPU" — an optional refusal is one the
|
|
92
|
+
* next frontend switches off by forgetting it, and the symptom is the message this field exists
|
|
93
|
+
* to remove. */
|
|
94
|
+
fpReg: RegExp | null;
|
|
95
|
+
/** FPU instructions that name no `fpReg` operand at all, matched on the MNEMONIC because there
|
|
96
|
+
* is nothing else to match: they address the FPU's CONTROL register, which is spelled in the
|
|
97
|
+
* ISA's OTHER namespace — MIPS `cfc1 rt,$31` / `ctc1 rt,$31` take a GPR and a coprocessor
|
|
98
|
+
* control number, PowerPC's `mtfsfi`/`mtfsb0`/`mtfsb1`/`mcrfs` take a field number and a
|
|
99
|
+
* condition register. A register-name predicate cannot see them, so without this they fall to
|
|
100
|
+
* the general arms and are filed as the generic opaque gap — and `ctc1`'s `ops[0]` is the
|
|
101
|
+
* instruction's SOURCE, so an opaque would be fabricated on a register it only reads.
|
|
102
|
+
*
|
|
103
|
+
* The FPU moves that DO name a data register (`mfc1`, `mtc1`, `mffs`, `mtfsf`) are `fpReg`'s,
|
|
104
|
+
* not this one's. Same message phrase either way, because it is the same missing capability.
|
|
105
|
+
*
|
|
106
|
+
* REQUIRED for the same reason as `fpReg`; `null` says the ISA has no FPU control register to
|
|
107
|
+
* reach. */
|
|
108
|
+
fpControl: RegExp | null;
|
|
53
109
|
/** Mnemonics that WRITE MEMORY in this ISA: the "no register destination ⇒ safe to fall
|
|
54
110
|
* through" premise is FALSE for stores — skipping one silently deletes the write (Thumb
|
|
55
111
|
* `stmia r0!, {…}`), and a store whose FIRST token is a register (MIPS `swl rt, off(base)`)
|
|
56
112
|
* would otherwise fabricate an opaque write to what is actually a SOURCE. An unmodelled
|
|
57
|
-
* instruction matching this pattern throws instead.
|
|
58
|
-
* every store
|
|
59
|
-
|
|
113
|
+
* instruction matching this pattern throws instead. REQUIRED, with `null` for a frontend that
|
|
114
|
+
* models every store in its ISA — held to it by the type, because the sentence that said
|
|
115
|
+
* "registered frontends are held to supplying one" was held to it by nothing. */
|
|
116
|
+
storeClass: RegExp | null;
|
|
60
117
|
/** attribution for thrown declines: the function being lifted (optionally "+ site"). */
|
|
61
118
|
context?: string;
|
|
62
119
|
/** How to SPELL the mnemonic in messages, when that differs from the name used to classify it.
|
|
@@ -80,10 +137,14 @@ export interface OpaqueDest {
|
|
|
80
137
|
|
|
81
138
|
/** Decide the opaque destination + register source operands for an unmodelled instruction
|
|
82
139
|
* `mnemonic` with operand list `ops` (operands in objdump order — destination first). Returns
|
|
83
|
-
* `null` only for a policy.skipSafe mnemonic.
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
140
|
+
* `null` only for a policy.skipSafe mnemonic. Three arms throw instead, in this order: an
|
|
141
|
+
* instruction that touches the FLOATING-POINT unit — naming one of its registers (policy.fpReg)
|
|
142
|
+
* or, for the control-register moves that name none, matching policy.fpControl — is refused
|
|
143
|
+
* first, because the file is the capability and the instruction is only where it surfaced; an
|
|
144
|
+
* unmodelled STORE-CLASS instruction (policy.storeClass) throws next (a skipped memory write is a
|
|
145
|
+
* silent miscompile); and so does any instruction with no degradable destination: with no register
|
|
146
|
+
* to carry the live-`?` sentinel, skipping would silently delete a side effect (swi/syscall/sync/
|
|
147
|
+
* cache).
|
|
87
148
|
*
|
|
88
149
|
* When non-null, the caller MUST emit an `opaque` op that writes `dst` and consumes `srcRegs`
|
|
89
150
|
* (read through the frontend's own SSA). It reaches structuring as the sentinel `?` and trips
|
|
@@ -97,23 +158,42 @@ export interface OpaqueDest {
|
|
|
97
158
|
* So `skipSafe`, a short per-ISA list of provably transparent mnemonics, is the only way an
|
|
98
159
|
* unmodelled instruction leaves no trace.
|
|
99
160
|
*
|
|
100
|
-
* `storeClass` is
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
161
|
+
* None of `fpReg`, `fpControl` and `storeClass` is load-bearing for SOUNDNESS — every instruction
|
|
162
|
+
* that reaches this function declines whatever arm takes it, because an `opaque` carries
|
|
163
|
+
* `effects: true` and reaches `assertResolved` whether or not anything reads it. What they buy is
|
|
164
|
+
* WHICH capability the decline names, and that is the whole content of a decline. Two of them buy
|
|
165
|
+
* one thing more: they catch the shapes where `ops[0]` is a SOURCE (MIPS `swl rt, off(base)`,
|
|
166
|
+
* `ctc1 rt, $31`) before a `dst` is fabricated from a register the instruction only reads.
|
|
167
|
+
* They and `skipSafe` match case-INSENSITIVELY: mnemonic case is a property of the disassembler,
|
|
168
|
+
* not of the instruction. */
|
|
105
169
|
export function opaqueDest(mnemonic: string, ops: string[], policy: OpaquePolicy): OpaqueDest | null {
|
|
106
170
|
const shown = policy.display ?? mnemonic;
|
|
171
|
+
const norm = policy.normalize ?? ((s) => s);
|
|
172
|
+
// `context` names the function (and, where the ISA has addresses, the site) — every message
|
|
173
|
+
// below lands verbatim in annotate-mode stub headers, where an un-attributed decline is
|
|
174
|
+
// unactionable in a multi-function run.
|
|
175
|
+
const where = policy.context ? `cannot lift '${policy.context}': ` : '';
|
|
176
|
+
const fpReg = policy.fpReg;
|
|
177
|
+
// DEDUPED: `fadds f1,f1,f2` names two registers, not three, and the message is read by a human
|
|
178
|
+
// asking which file is missing.
|
|
179
|
+
const fp = fpReg ? [...new Set(ops.map(norm).filter((o) => fpReg.test(o)))] : [];
|
|
180
|
+
if (fp.length > 0) {
|
|
181
|
+
throw new FrontendUnsupportedError(
|
|
182
|
+
`${where}unmodelled floating-point instruction '${shown}' — it uses the floating-point ` +
|
|
183
|
+
`register file (${fp.join(', ')}), which this frontend does not model`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
if (policy.fpControl?.test(mnemonic)) {
|
|
187
|
+
throw new FrontendUnsupportedError(
|
|
188
|
+
`${where}unmodelled floating-point instruction '${shown}' — it uses the floating-point ` +
|
|
189
|
+
`control register, which this frontend does not model`,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
107
192
|
if (policy.storeClass?.test(mnemonic)) {
|
|
108
|
-
// `context` names the function (and, where the ISA has addresses, the site) — this message
|
|
109
|
-
// lands verbatim in annotate-mode stub headers, where an un-attributed decline is
|
|
110
|
-
// unactionable in a multi-function run.
|
|
111
|
-
const where = policy.context ? `cannot lift '${policy.context}': ` : '';
|
|
112
193
|
throw new FrontendUnsupportedError(
|
|
113
194
|
`${where}unmodelled store-class instruction '${shown}' — a memory write cannot be skipped or degraded to a register opaque`,
|
|
114
195
|
);
|
|
115
196
|
}
|
|
116
|
-
const norm = policy.normalize ?? ((s) => s);
|
|
117
197
|
const dst = norm(ops[0] ?? '');
|
|
118
198
|
// No DEGRADABLE destination: `ops[0]` is not a register, or it is the hardwired zero. Same answer
|
|
119
199
|
// for both — a zero write is a genuine no-op only for a MODELLED instruction, and by construction
|
|
@@ -122,7 +202,6 @@ export function opaqueDest(mnemonic: string, ops: string[], policy: OpaquePolicy
|
|
|
122
202
|
if (policy.skipSafe?.test(mnemonic)) {
|
|
123
203
|
return null;
|
|
124
204
|
} // explicitly transparent for this ISA
|
|
125
|
-
const where = policy.context ? `cannot lift '${policy.context}': ` : '';
|
|
126
205
|
throw new FrontendUnsupportedError(
|
|
127
206
|
`${where}unmodelled effect instruction '${shown}' — no register destination to degrade, and skipping it would silently delete its effect`,
|
|
128
207
|
);
|