@asmlift/core 0.9.0 → 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.
- package/README.md +25 -24
- package/package.json +1 -1
- package/src/aggregate.ts +149 -0
- package/src/backend/c.ts +1 -0
- package/src/backend/cfamily.ts +165 -50
- package/src/backend/cpp.ts +2 -1
- package/src/backend/pascal.ts +31 -12
- package/src/contracts.ts +173 -16
- package/src/declare.ts +69 -20
- package/src/frontend/asmdata.ts +7 -1
- package/src/frontend/call-plan.ts +964 -60
- package/src/frontend/dead-reads.ts +628 -0
- package/src/frontend/device-pins.ts +23 -4
- package/src/frontend/emit.ts +4 -3
- package/src/frontend/fpu.ts +224 -17
- package/src/frontend/frame-objects.ts +1232 -192
- package/src/frontend/frontend.ts +64 -3
- package/src/frontend/high-half.ts +53 -4
- package/src/frontend/jump-table.ts +530 -0
- package/src/frontend/local-object.ts +158 -32
- package/src/frontend/mips.ts +2076 -215
- package/src/frontend/own-struct-return.ts +500 -0
- package/src/frontend/ppc.ts +2711 -232
- package/src/frontend/register-struct-call.ts +484 -0
- package/src/frontend/reloc-symbol.ts +22 -7
- package/src/frontend/ssa.ts +1460 -44
- package/src/frontend/stackargs.ts +106 -57
- package/src/frontend/thumb.ts +2044 -318
- package/src/ir/alias.ts +42 -0
- package/src/ir/bits.ts +681 -1
- package/src/ir/core.ts +100 -5
- package/src/ir/discipline.ts +86 -21
- package/src/ir/entry.ts +159 -0
- package/src/{structure → ir}/loops.ts +6 -2
- package/src/ir/opcodes.ts +109 -9
- package/src/ir/types.ts +86 -2
- package/src/ir/verify.ts +80 -11
- package/src/l3/ast.ts +290 -18
- package/src/l3/basecse.ts +33 -5
- package/src/l3/constinit.ts +382 -0
- package/src/l3/dce.ts +2 -2
- 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 +31 -34
- package/src/l3/ptrfield.ts +123 -8
- package/src/l3/regspell.ts +9 -4
- package/src/l3/reindex.ts +283 -96
- package/src/l3/storage.ts +98 -1
- package/src/l3/symbol-refs.ts +84 -6
- package/src/l3/tailmerge.ts +4 -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 +541 -24
- package/src/proto-context.ts +905 -45
- package/src/proto.ts +555 -40
- package/src/raise/aggmembers.ts +290 -0
- package/src/raise/arrays.ts +5 -1
- package/src/raise/declared-volatile.ts +207 -31
- 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 +10 -26
- package/src/raise/pairparams.ts +33 -0
- package/src/raise/paramwidth.ts +196 -6
- package/src/raise/pre-recovery.ts +102 -19
- package/src/raise/pre-structure.ts +54 -0
- package/src/raise/recover.ts +12 -5
- package/src/raise/reroll.ts +694 -0
- package/src/raise/shortcircuit.ts +26 -12
- 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/unthread.ts +400 -0
- package/src/rank-declare.ts +118 -12
- package/src/rank-variations.ts +89 -31
- package/src/rank.ts +307 -54
- package/src/runtime-helpers.ts +140 -22
- package/src/structure/analysis.ts +165 -2
- package/src/structure/arith-ops.ts +23 -0
- package/src/structure/globalaccess.ts +111 -9
- 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 +2180 -1054
- package/src/structure/switch-recover.ts +244 -100
- package/src/symbols.ts +6 -0
- package/src/target.ts +765 -52
- package/src/trace.ts +26 -8
- package/src/variation-definitions.ts +126 -14
- package/src/variation-gates.ts +2 -0
- package/src/variation-tokens.ts +5 -1
package/src/frontend/frontend.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// neutral and consumes the Fn without knowing which frontend produced it. See registry.ts
|
|
4
4
|
// for target→frontend dispatch.
|
|
5
5
|
import type { Fn } from '../ir/core';
|
|
6
|
-
import type { Prototypes } from '../proto';
|
|
6
|
+
import type { FnProto, Prototypes } from '../proto';
|
|
7
|
+
import type { ContextGlobals } from '../proto-context';
|
|
7
8
|
import type { SymbolMap } from '../symbols';
|
|
8
9
|
import type { TargetDescription } from '../target';
|
|
9
10
|
import type { AsmData } from './asmdata';
|
|
@@ -19,8 +20,10 @@ export interface Frontend {
|
|
|
19
20
|
* (and any other header facts the frontend needs); an empty map is valid. `asmData` is the
|
|
20
21
|
* OPTIONAL Regime-B side-table (data-section jump tables + relocations); absent ⇒ a
|
|
21
22
|
* dense-switch dispatch declines/loud-fails. `symbols` is the OPTIONAL address→symbol map
|
|
22
|
-
* (symbols.ts)
|
|
23
|
-
* MIPS/PPC objdump dialect already carries symbol names in the asm text
|
|
23
|
+
* (symbols.ts): the Thumb frontend promotes a numeric pool word through it — the
|
|
24
|
+
* MIPS/PPC objdump dialect already carries symbol names in the asm text — and every frontend
|
|
25
|
+
* hands it, with the OPTIONAL declaration `context`, to the dead-read pin, which reads a global's
|
|
26
|
+
* declaration there (frontend/dead-reads.ts `declared-plain`). */
|
|
24
27
|
lift(
|
|
25
28
|
name: string,
|
|
26
29
|
asm: string,
|
|
@@ -28,5 +31,63 @@ export interface Frontend {
|
|
|
28
31
|
prototypes: Prototypes,
|
|
29
32
|
asmData?: AsmData,
|
|
30
33
|
symbols?: SymbolMap,
|
|
34
|
+
context?: ContextGlobals,
|
|
35
|
+
options?: LiftOptions,
|
|
31
36
|
): Fn;
|
|
32
37
|
}
|
|
38
|
+
|
|
39
|
+
/** THE FUNCTION'S OWN DECLARATION, as a call lowering reads it (frontend/call-plan.ts
|
|
40
|
+
* `callDeclarations`): a stated entry, which `proves`, or the declaration context's
|
|
41
|
+
* (`LiftOptions.contextOwnProto`). */
|
|
42
|
+
export interface OwnDeclaration {
|
|
43
|
+
readonly proto: FnProto;
|
|
44
|
+
/** whether it may rule a reading OUT (a hole, a shadow, a float) as well as refuse one */
|
|
45
|
+
readonly proves: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The own declaration of `name`: the stated entry in `prototypes`, else the context's. */
|
|
49
|
+
export function ownDeclarationOf(
|
|
50
|
+
name: string,
|
|
51
|
+
prototypes: Prototypes,
|
|
52
|
+
context: OwnDeclaration | undefined,
|
|
53
|
+
): OwnDeclaration | undefined {
|
|
54
|
+
return Object.hasOwn(prototypes, name) ? { proto: prototypes[name], proves: true } : context;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** A question the asm leaves open that a frontend answers one way by default and, asked, the other
|
|
58
|
+
* way: the LIFT variations of rank.ts that live in the frontend. A frontend with no such question
|
|
59
|
+
* ignores the whole bag. Two inputs ride beside them that no variation sets, both from the
|
|
60
|
+
* declaration context's own entry: its bound (`contextOwnWords`) and the entry itself
|
|
61
|
+
* (`contextOwnProto`). */
|
|
62
|
+
export interface LiftOptions {
|
|
63
|
+
/** The argument words the declaration context's own entry for the function fills, where no
|
|
64
|
+
* stated entry does (pipeline.ts `liftWithinContextBound`, which raises it past a register the
|
|
65
|
+
* lift reads, and lifts without it where it refuses), so a frontend given it reads it in place
|
|
66
|
+
* of the stated entry's words (frontend/ssa.ts `registerLiveIns`). Every frontend reads it; PowerPC,
|
|
67
|
+
* as with a stated entry, only for a body a compiler wrote. */
|
|
68
|
+
contextOwnWords?: number;
|
|
69
|
+
/** The declaration context's own entry itself, where no stated entry binds the body. Read where
|
|
70
|
+
* the TYPES of the own parameters decide something the word count cannot (frontend/call-plan.ts
|
|
71
|
+
* `decideGap`). `proves` says whether it may serve as positive proof as well. It holds only where
|
|
72
|
+
* the lift is bounded by exactly the entry's words, or the entry fills every argument register,
|
|
73
|
+
* and never once the bound was raised past a register the body reads or the lift fell back to
|
|
74
|
+
* no bound, which says the entry is wrong. Without `proves`, it only refuses. */
|
|
75
|
+
contextOwnProto?: OwnDeclaration;
|
|
76
|
+
/** The size in bytes of a struct the function's own declaration returns through a hidden pointer
|
|
77
|
+
* (pipeline.ts `liftStamped`), and where to hand a refusal that declaration may answer: one about
|
|
78
|
+
* the RETURN's width that such a struct explains, which the frontend then defers instead of
|
|
79
|
+
* throwing, as the function that throws it. The pipeline calls that after all unless the lift
|
|
80
|
+
* proves the pointer is that struct's storage (frontend/own-struct-return.ts), handing it why the
|
|
81
|
+
* proof failed, which the refusal then says. Thumb only: agbcc's interworking epilogue sizes an
|
|
82
|
+
* 8-byte struct returned through memory as it sizes a `long long` (`eightByteReturnScratch`). */
|
|
83
|
+
ownMemoryReturn?: { bytes: number; defer(refuse: (unproved: string) => never): void };
|
|
84
|
+
/** MIPS: keep the slot of a `b`/`j` whose slot is the last word of a common tail its two arms
|
|
85
|
+
* both spell (`sll; b T; sra` against `sll; sra; T:`), rather than retarget it to the copy at
|
|
86
|
+
* T-4 and merge the arms. The default retargets (frontend/mips.ts `normaliseBranchLikely`). An
|
|
87
|
+
* IDO question: GCC 2.7.2 and KMC cross-jump such a tail into one copy before filling a slot, so
|
|
88
|
+
* on their output the slot is never a tail written twice. */
|
|
89
|
+
keepTailSlots?: boolean;
|
|
90
|
+
/** Called when the lift met such a slot, whichever way it answered: what rank.ts asks before
|
|
91
|
+
* enumerating `/kept-slot` at all. */
|
|
92
|
+
onTailSlot?: () => void;
|
|
93
|
+
}
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
// lives entirely in the two relocation records — so the high half is NOT A VALUE but a placeholder
|
|
7
7
|
// that means nothing until its matching low half completes it. Letting one reach the IR as an
|
|
8
8
|
// ordinary number hands the pipeline a plausible address that compiles and is simply wrong, so the
|
|
9
|
-
// invariant is absolute: a placeholder is either consumed by its low half or refuses.
|
|
9
|
+
// invariant is absolute: a placeholder is either consumed by its low half or refuses. The one other
|
|
10
|
+
// consumer is `index`, and it does not break the rule: it consumes the half only to define a NEW
|
|
11
|
+
// placeholder, the half plus a runtime index, which is held to the same rule in turn.
|
|
10
12
|
//
|
|
11
13
|
// KEYED BY VALUE, NOT BY REGISTER, and the difference is the whole point. A register-keyed map
|
|
12
14
|
// answers "was a high half put in rX?", which is not the question; the question is "does the half
|
|
@@ -35,6 +37,11 @@ export interface HighHalfInfo {
|
|
|
35
37
|
addend: number;
|
|
36
38
|
addr: number;
|
|
37
39
|
mnemonic: string;
|
|
40
|
+
/** A RUNTIME BYTE INDEX added between the halves (`lui rH,%hi(S); addu rH,rH,idx; lw x,%lo(S)(rH)`,
|
|
41
|
+
* which is how ido, KMC and GCC 2.7.2 address `S[i]`), set only on a half `index` derived. The
|
|
42
|
+
* index sits outside both relocated immediates, so the `%lo` carry is the same with and without
|
|
43
|
+
* it and the completed address is exactly `S + addend + lo + index`. */
|
|
44
|
+
index?: Value;
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
/** How one ISA spells the two halves in a refusal, and how it fails loud. */
|
|
@@ -57,8 +64,28 @@ export interface HighHalves {
|
|
|
57
64
|
* placeholder a producer defined, and the two halves must name the same symbol (`alsoMatches`
|
|
58
65
|
* adds the per-ISA rest of the match — PowerPC's RELA addend). Anything else refuses, naming
|
|
59
66
|
* `reg` and what it really holds. The half is CONSUMED on the way out, which is why this is a
|
|
60
|
-
* call and not a public flag: the mark and the proof cannot come apart.
|
|
61
|
-
|
|
67
|
+
* call and not a public flag: the mark and the proof cannot come apart.
|
|
68
|
+
*
|
|
69
|
+
* An INDEXED half (`index`) pairs only for a caller that passes `takesIndex`, and the index and
|
|
70
|
+
* the proof cannot come apart either: a consumer that completes the address as `gaddr(sym) +
|
|
71
|
+
* addend` without reading `index` would drop the index and still compile. So a caller that has
|
|
72
|
+
* not said it adds `index` back gets a refusal in place of that address. */
|
|
73
|
+
pair(
|
|
74
|
+
site: string,
|
|
75
|
+
reg: string,
|
|
76
|
+
v: Value,
|
|
77
|
+
loSym: string,
|
|
78
|
+
alsoMatches?: (hi: HighHalfInfo) => boolean,
|
|
79
|
+
takesIndex?: boolean,
|
|
80
|
+
): HighHalfInfo;
|
|
81
|
+
/** AN INDEXED HIGH HALF: `v` is the half `base` plus the runtime byte index `idx`, made by the
|
|
82
|
+
* instruction at `at`. It is a placeholder like the half it came from — read as a value, left
|
|
83
|
+
* uncompleted or carried into a merge it refuses through the same guards, because it lives in the
|
|
84
|
+
* same value-keyed map — and only its `%lo` completes it, `pair` handing the index back. Deriving
|
|
85
|
+
* CONSUMES `base`: the derivation is the use the base half was waiting for. Indexing a half that
|
|
86
|
+
* is already indexed refuses — no compiler measured chains two (lui;addu;addu); a frontend that
|
|
87
|
+
* meets one has hand-written or unmodelled code, not `S[i][j]`, which every compiler sums first. */
|
|
88
|
+
index(v: Value, base: Value, idx: Value, at: { addr: number; mnemonic: string }): void;
|
|
62
89
|
/** True when `v` is a placeholder — for a caller that must EXCLUDE one (a call's argument count
|
|
63
90
|
* must not treat a half parked in an argument register as an argument). */
|
|
64
91
|
has(v: Value): boolean;
|
|
@@ -82,8 +109,30 @@ export function makeHighHalves(d: HighHalfDialect): HighHalves {
|
|
|
82
109
|
return {
|
|
83
110
|
record: (v, info) => void halves.set(v, { info, consumed: false }),
|
|
84
111
|
has: (v) => halves.has(v),
|
|
85
|
-
|
|
112
|
+
index(v, base, idx, at) {
|
|
113
|
+
const entry = halves.get(base);
|
|
114
|
+
if (!entry) {
|
|
115
|
+
d.fail(`the '${at.mnemonic}' at 0x${at.addr.toString(16)} indexes a value that holds no high half`);
|
|
116
|
+
}
|
|
117
|
+
if (entry!.info.index !== undefined) {
|
|
118
|
+
d.fail(
|
|
119
|
+
`the '${at.mnemonic}' at 0x${at.addr.toString(16)} adds a second index to the high half of ` +
|
|
120
|
+
`'${entry!.info.sym}' (the '${entry!.info.mnemonic}' at 0x${entry!.info.addr.toString(16)}) — ` +
|
|
121
|
+
`a chain of indexes between the halves is not modelled`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
entry!.consumed = true;
|
|
125
|
+
halves.set(v, { info: { ...entry!.info, addr: at.addr, mnemonic: at.mnemonic, index: idx }, consumed: false });
|
|
126
|
+
},
|
|
127
|
+
pair(site, reg, v, loSym, alsoMatches, takesIndex = false) {
|
|
86
128
|
const entry = halves.get(v);
|
|
129
|
+
if (entry?.info.index !== undefined && !takesIndex) {
|
|
130
|
+
d.fail(
|
|
131
|
+
`${site} carries the '${d.lo}' half of '${loSym}' but ${reg} holds the high half of ` +
|
|
132
|
+
`'${entry.info.sym}' plus a runtime index (the '${entry.info.mnemonic}' at ` +
|
|
133
|
+
`0x${entry.info.addr.toString(16)}), and this consumer completes no index`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
87
136
|
const hi = entry && entry.info.sym === loSym && (alsoMatches?.(entry.info) ?? true) ? entry.info : undefined;
|
|
88
137
|
if (!hi) {
|
|
89
138
|
d.fail(
|