@asmlift/core 0.8.1 → 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 +36 -31
- package/package.json +1 -1
- package/src/aggregate.ts +397 -0
- package/src/backend/c.ts +6 -2
- package/src/backend/cfamily.ts +370 -56
- package/src/backend/cpp.ts +116 -10
- package/src/backend/pascal.ts +69 -11
- 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 +777 -84
- package/src/declare.ts +132 -13
- package/src/frontend/asmdata.ts +26 -6
- package/src/frontend/call-plan.ts +1590 -0
- package/src/frontend/dead-reads.ts +628 -0
- package/src/frontend/device-pins.ts +296 -0
- package/src/frontend/emit.ts +4 -3
- package/src/frontend/errors.ts +5 -0
- package/src/frontend/flags-edge.ts +101 -0
- package/src/frontend/fpu.ts +374 -0
- package/src/frontend/frame-objects.ts +3171 -0
- package/src/frontend/frontend.ts +64 -3
- package/src/frontend/high-half.ts +55 -5
- package/src/frontend/jump-table.ts +530 -0
- package/src/frontend/local-object.ts +616 -0
- package/src/frontend/mips.ts +2392 -208
- package/src/frontend/opaque.ts +97 -18
- package/src/frontend/own-struct-return.ts +500 -0
- package/src/frontend/ppc.ts +3137 -267
- package/src/frontend/register-struct-call.ts +484 -0
- package/src/frontend/reloc-symbol.ts +109 -13
- package/src/frontend/splat.ts +108 -5
- package/src/frontend/ssa.ts +2098 -89
- package/src/frontend/stackargs.ts +172 -42
- package/src/frontend/thumb.ts +4563 -2091
- package/src/ir/alias.ts +71 -0
- package/src/ir/bits.ts +681 -1
- package/src/ir/core.ts +199 -6
- package/src/ir/discipline.ts +313 -0
- package/src/ir/entry.ts +159 -0
- package/src/ir/float-bits.ts +47 -0
- package/src/{structure → ir}/loops.ts +6 -2
- package/src/ir/opcodes.ts +193 -121
- package/src/ir/parse.ts +1 -0
- package/src/ir/types.ts +202 -2
- package/src/ir/verify.ts +219 -3
- package/src/l3/address.ts +5 -0
- package/src/l3/argcopy.ts +38 -6
- package/src/l3/ast.ts +467 -23
- package/src/l3/basecse.ts +33 -5
- package/src/l3/coalesce.ts +21 -13
- package/src/l3/constinit.ts +382 -0
- package/src/l3/dce.ts +34 -12
- package/src/l3/gates.ts +9 -3
- package/src/l3/hoist.ts +8 -7
- package/src/l3/initfirst.ts +68 -19
- package/src/l3/intcell.ts +98 -0
- package/src/l3/mentions.ts +10 -4
- package/src/l3/narrowdecl.ts +259 -0
- package/src/l3/nearbase.ts +10 -5
- package/src/l3/parkfirst.ts +8 -5
- package/src/l3/pollguard.ts +11 -4
- package/src/l3/ptrcell.ts +354 -0
- package/src/l3/ptrfield.ts +123 -8
- package/src/l3/regspell.ts +26 -7
- package/src/l3/reindex.ts +293 -103
- package/src/l3/storage.ts +98 -1
- package/src/l3/symbol-refs.ts +182 -23
- package/src/l3/tailmerge.ts +9 -20
- package/src/l3/typing.ts +237 -32
- package/src/l3/unreduce.ts +10 -9
- package/src/l3/volatileptr.ts +17 -54
- package/src/l3/volstore.ts +96 -7
- package/src/mangle.ts +41 -1
- package/src/pattern/engine.ts +373 -82
- package/src/pipeline.ts +589 -27
- package/src/proto-context.ts +1648 -0
- package/src/proto.ts +1203 -49
- package/src/raise/aggmembers.ts +290 -0
- package/src/raise/arrays.ts +8 -3
- package/src/raise/declared-volatile.ts +307 -0
- package/src/raise/divpow2.ts +2 -2
- package/src/raise/errors.ts +17 -0
- package/src/raise/floathelpers.ts +464 -0
- package/src/raise/globalshape.ts +11 -5
- package/src/raise/gvn.ts +29 -8
- package/src/raise/latch.ts +162 -6
- package/src/raise/memberarrays.ts +10 -4
- package/src/raise/narrow.ts +29 -2
- package/src/raise/narrowlocal.ts +11 -6
- package/src/raise/offsetnames.ts +494 -0
- package/src/raise/pairparams.ts +114 -0
- package/src/raise/paramwidth.ts +203 -6
- package/src/raise/pre-recovery.ts +174 -17
- package/src/raise/pre-structure.ts +54 -0
- package/src/raise/recover.ts +26 -15
- package/src/raise/reroll.ts +694 -0
- package/src/raise/retsink.ts +1 -1
- package/src/raise/shortcircuit.ts +66 -30
- package/src/raise/softdiv.ts +21 -23
- package/src/raise/storesign.ts +153 -0
- package/src/raise/struct-arrays.ts +34 -14
- package/src/raise/structs.ts +674 -98
- package/src/raise/truncload.ts +367 -0
- package/src/raise/unthread.ts +400 -0
- package/src/raise/widehelpers.ts +106 -0
- package/src/rank-declare.ts +143 -12
- package/src/rank-variations.ts +117 -31
- package/src/rank.ts +563 -122
- package/src/runtime-helpers.ts +405 -0
- package/src/stillborn.ts +289 -0
- package/src/structure/analysis.ts +1449 -449
- package/src/structure/arith-ops.ts +23 -0
- package/src/structure/globalaccess.ts +111 -9
- package/src/structure/hazards.ts +641 -114
- package/src/structure/local-statics.ts +517 -0
- package/src/structure/namecoalesce.ts +2 -2
- package/src/structure/pointer-spelling.ts +1106 -0
- package/src/structure/redundant-test.ts +99 -0
- package/src/structure/structure.ts +3478 -975
- package/src/structure/switch-recover.ts +403 -178
- package/src/symbols.ts +44 -3
- package/src/target.ts +1387 -71
- package/src/trace.ts +66 -28
- package/src/variation-definitions.ts +243 -13
- package/src/variation-gates.ts +2 -0
- package/src/variation-tokens.ts +25 -7
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// asmlift structurer — the IR's integer arithmetic opcodes and the L3 operator each one renders
|
|
2
|
+
// as. The float ops are FLOAT_TO_BIN in structure.ts, which says why they are not here.
|
|
3
|
+
import type { BinOp } from '../l3/ast';
|
|
4
|
+
|
|
5
|
+
export const ARITH_TO_BIN: Record<string, BinOp> = {
|
|
6
|
+
add: '+',
|
|
7
|
+
sub: '-',
|
|
8
|
+
mul: '*',
|
|
9
|
+
sdiv: '/',
|
|
10
|
+
// the UNSIGNED quotient/remainder — the C backend spells them `/`/`%` over an operand it casts
|
|
11
|
+
// unsigned (l3/ast.ts BinOp, backend/cfamily.ts C_SPELLING)
|
|
12
|
+
udiv: '/u',
|
|
13
|
+
smod: '%',
|
|
14
|
+
umod: '%u',
|
|
15
|
+
or: '|',
|
|
16
|
+
and: '&',
|
|
17
|
+
xor: '^',
|
|
18
|
+
shl: '<<',
|
|
19
|
+
shr_u: '>>>', // the LOGICAL right shift; the C backend spells it `>>` over an unsigned operand
|
|
20
|
+
shr_s: '>>',
|
|
21
|
+
logic_and: '&&',
|
|
22
|
+
logic_or: '||', // short-circuit connectives (raise/shortcircuit.ts)
|
|
23
|
+
};
|
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
// THE GLOBAL-ACCESS ADDRESS DECOMPOSITION — the pure half of the symbol-map access spellings.
|
|
2
2
|
//
|
|
3
|
-
// Everything here is a function of an `Expr`,
|
|
4
|
-
// naming walk, no loop context. It answers three questions the access spellings in
|
|
5
|
-
// ask over and over — is this address `&gSym` plus something; does that something
|
|
6
|
-
// whole elements; and do the terms of it name the DECLARED subscripts of a
|
|
7
|
-
// — and it answers them the same way for every caller, which is the point
|
|
8
|
-
// rank-pinning fallback and the declared-subscript recovery share
|
|
9
|
-
// cannot disagree about what a bare element spelling is.
|
|
3
|
+
// Everything here is a function of an `Expr`, the map's declarations and a width: no structurer
|
|
4
|
+
// state, no naming walk, no loop context. It answers three questions the access spellings in
|
|
5
|
+
// structure.ts ask over and over — is this address `&gSym` plus something; does that something
|
|
6
|
+
// divide into whole elements; and do the terms of it name the DECLARED subscripts of a
|
|
7
|
+
// multidimensional array — and it answers them the same way for every caller, which is the point
|
|
8
|
+
// of the split: the rank-pinning fallback and the declared-subscript recovery share
|
|
9
|
+
// `bareArrayElement`, so the two cannot disagree about what a bare element spelling is. The same
|
|
10
|
+
// holds for what the map declares a named member to be (`ptrMemberDecl`), which the access folds
|
|
11
|
+
// and the pointer spelling (pointer-spelling.ts) both read.
|
|
10
12
|
import type { Expr } from '../l3/ast';
|
|
11
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
type DeclaredField,
|
|
15
|
+
type SymbolInfo,
|
|
16
|
+
type SymbolStructField,
|
|
17
|
+
arrayInnerExtents,
|
|
18
|
+
declaredFields,
|
|
19
|
+
isArrayField,
|
|
20
|
+
isBitfieldField,
|
|
21
|
+
isPtrField,
|
|
22
|
+
pointeeFields,
|
|
23
|
+
} from '../symbols';
|
|
12
24
|
|
|
13
25
|
// `&gSym`, possibly wearing the value-context integer cast the additive lowering adds
|
|
14
|
-
// (`(u32)&gSym` — see `
|
|
26
|
+
// (`(u32)&gSym` — see `ARITH_ROWS` in pointer-spelling.ts): both spell the same link-time
|
|
15
27
|
// constant, so the fold rules match through the cast and every access that CAN spell a named
|
|
16
28
|
// element still does.
|
|
17
29
|
// WIDTH 32 ONLY — a NARROWING cast (`(u8)&gSym`, from a zext/sext lowering) is a different
|
|
@@ -298,3 +310,93 @@ export function addOffset(idx: Expr, n: number): Expr {
|
|
|
298
310
|
? { k: 'const', value: idx.value + n }
|
|
299
311
|
: { k: 'bin', op: '+', l: idx, r: { k: 'const', value: n } };
|
|
300
312
|
}
|
|
313
|
+
|
|
314
|
+
/** May a member be NAMED by an access of this direction, given the qualifiers on its declaration?
|
|
315
|
+
* The named spelling REPLACES a cast through `(u8 *)`, which carries no qualifier at all, so a
|
|
316
|
+
* qualifier the name reintroduces changes what the compiler emits:
|
|
317
|
+
* • `volatile` makes the access observable — the load may no longer be folded or reordered,
|
|
318
|
+
* which is a different instruction sequence (measured: 6 insns where the cast form was 5);
|
|
319
|
+
* • `const` under a STORE is a hard error, where the cast form merely cast the qualifier away.
|
|
320
|
+
* Either way the honest spelling is the cast form, so the member simply is not nameable here. */
|
|
321
|
+
export function memberQualsAllow(f: SymbolStructField, containerConst: boolean | undefined, isStore: boolean): boolean {
|
|
322
|
+
if (f.volatile) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
return !(isStore && (f.const || containerConst));
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** The member a `width`-byte access at byte `byte` of a struct global names, `gSym.member`: one at
|
|
329
|
+
* that offset and of that size, no array or bitfield, whose qualifiers allow the access. An ARRAY
|
|
330
|
+
* member would match a byte access by (offset, size) and spell `.x`, which is not an lvalue of
|
|
331
|
+
* that width; a plain read of a BITFIELD's bytes is not a read of its bits. */
|
|
332
|
+
export function structMemberAt(
|
|
333
|
+
si: SymbolInfo,
|
|
334
|
+
byte: number,
|
|
335
|
+
width: number,
|
|
336
|
+
isStore: boolean,
|
|
337
|
+
): DeclaredField | undefined {
|
|
338
|
+
if (si.shape !== 'struct') {
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
const f = declaredFields(si.layout)?.find(
|
|
342
|
+
(x) => x.offset === byte && x.size === width && !isArrayField(x) && !isBitfieldField(x),
|
|
343
|
+
);
|
|
344
|
+
return f !== undefined && memberQualsAllow(f, si.const, isStore) ? f : undefined;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** The members a symbol's declaration seats: a struct global's own, or a pointer global's
|
|
348
|
+
* pointee's. */
|
|
349
|
+
export interface MemberLookup {
|
|
350
|
+
fieldsOf(name: string): DeclaredField[] | null;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** {@link MemberLookup.fieldsOf} over `info`, MEMOIZED per symbol. `declaredFields` validates every
|
|
354
|
+
* member and returns a fresh sorted copy on every call, and pointer-spelling.ts's `roleOf`
|
|
355
|
+
* asks it for every member operand of every arithmetic op lowered, so an uncached lookup is an
|
|
356
|
+
* O(n log n) allocation on a hot path — inside a `structure()` a ranked run repeats once per
|
|
357
|
+
* candidate, 17,856 times on the largest fan. */
|
|
358
|
+
export function memoFieldsOf(info: (name: string) => SymbolInfo | undefined): MemberLookup['fieldsOf'] {
|
|
359
|
+
const cache = new Map<string, DeclaredField[] | null>();
|
|
360
|
+
return (name) => {
|
|
361
|
+
const hit = cache.get(name);
|
|
362
|
+
if (hit !== undefined) {
|
|
363
|
+
return hit;
|
|
364
|
+
}
|
|
365
|
+
const si = info(name);
|
|
366
|
+
const fields =
|
|
367
|
+
si?.shape === 'struct' ? declaredFields(si.layout) : si?.shape === 'pointer' ? pointeeFields(si.pointee) : null;
|
|
368
|
+
cache.set(name, fields);
|
|
369
|
+
return fields;
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** The map member a `field` node NAMES, or null when it names none — THE one resolver for "what
|
|
374
|
+
* does the declaration say about this member", for rules that must reason about a member's type
|
|
375
|
+
* after the access rules have already spelled it. Both named spellings resolve, through the same
|
|
376
|
+
* shared gate their spelling passed: `gSym.member` off a struct global's {@link declaredFields},
|
|
377
|
+
* `gPtr->member` off the pointee's ({@link pointeeFields}). A synthesized `field_K` — the
|
|
378
|
+
* recovered-struct spelling, which no map declares — resolves to null, and so does any base that
|
|
379
|
+
* is not a map-shaped global, which is what makes every caller refuse rather than guess.
|
|
380
|
+
*
|
|
381
|
+
* The pointee arm reaches only what the MAP lets it: `pointeeAccess` gates every `gPtr->member`
|
|
382
|
+
* spelling on `spellsAccessType(f.signed, …)`, so a pointer field declaring no signedness — which
|
|
383
|
+
* is every one in the corpus's vendored maps — is never named, and a pointer read one indirection
|
|
384
|
+
* down spells `((s32 *)gQ)[1]`. That is a fact about those maps, not about this code: `SymbolMap`
|
|
385
|
+
* is a caller-supplied input, and one field flips it (`signed: true` on a 4-byte pointer member
|
|
386
|
+
* of a pointee yields `(u8 *)gQ->pInner`, cast and all, pinned in pointer-members.test.ts). So
|
|
387
|
+
* the arm is live and tested, and the byte-arithmetic rule that reads this answer is correct for
|
|
388
|
+
* it — where resolving a pointee member to null would reopen the double-scaling hole silently. */
|
|
389
|
+
export function declaredMemberOf(x: Expr, sym: MemberLookup | undefined): DeclaredField | null {
|
|
390
|
+
if (x.k !== 'field' || x.base.k !== 'var' || sym === undefined) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
return sym.fieldsOf(x.base.name)?.find((f) => f.name === x.name) ?? null;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/** The map member a `field` node names when the declaration makes it a POINTER — {@link
|
|
397
|
+
* isPtrField} being the shared two-fact test, so this and the synthesized declaration cannot
|
|
398
|
+
* disagree about what a member is. */
|
|
399
|
+
export function ptrMemberDecl(x: Expr, sym: MemberLookup | undefined): DeclaredField | null {
|
|
400
|
+
const f = declaredMemberOf(x, sym);
|
|
401
|
+
return f !== null && isPtrField(f) ? f : null;
|
|
402
|
+
}
|