@asmlift/core 0.4.0 → 0.6.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 +22 -16
- package/package.json +1 -1
- package/src/backend/c.ts +1 -0
- package/src/backend/cfamily.ts +238 -164
- package/src/backend/cpp.ts +1 -0
- package/src/backend/pascal.ts +26 -12
- package/src/contracts.ts +341 -22
- package/src/declare.ts +41 -4
- package/src/frontend/mips.ts +24 -6
- package/src/frontend/opaque.ts +31 -18
- package/src/frontend/ppc.ts +54 -7
- package/src/frontend/ssa.ts +632 -13
- package/src/frontend/thumb.ts +2786 -286
- package/src/ir/alias.ts +129 -0
- package/src/ir/bits.ts +75 -0
- package/src/ir/core.ts +337 -2
- package/src/ir/opcodes.ts +156 -27
- package/src/ir/parse.ts +19 -2
- package/src/ir/print.ts +27 -2
- package/src/ir/simplify.ts +190 -3
- package/src/ir/struct-names.ts +42 -0
- package/src/ir/verify.ts +43 -49
- package/src/l3/address.ts +62 -0
- package/src/l3/argbase.ts +8 -2
- package/src/l3/ast.ts +464 -49
- package/src/l3/basecse.ts +709 -88
- package/src/l3/coalesce.ts +521 -66
- package/src/l3/dce.ts +54 -19
- package/src/l3/gates.ts +88 -0
- package/src/l3/hoist.ts +293 -14
- package/src/l3/homesplit.ts +285 -0
- package/src/l3/initfirst.ts +301 -0
- package/src/l3/inlinebase.ts +193 -0
- package/src/l3/mentions.ts +113 -0
- package/src/l3/mulfirst.ts +42 -0
- package/src/l3/nearbase.ts +152 -0
- package/src/l3/offmember.ts +371 -0
- package/src/l3/parkfirst.ts +96 -0
- package/src/l3/pollguard.ts +154 -0
- package/src/l3/ptrfield.ts +227 -0
- package/src/l3/regspell.ts +110 -85
- package/src/l3/reindex.ts +715 -78
- package/src/l3/scopebase.ts +649 -219
- package/src/l3/sinkinit.ts +40 -0
- package/src/l3/slotorder.ts +123 -0
- package/src/l3/storage.ts +48 -0
- package/src/l3/symbol-refs.ts +41 -8
- package/src/l3/tailmerge.ts +23 -4
- package/src/l3/typing.ts +198 -9
- package/src/l3/unmerge.ts +263 -0
- package/src/l3/unreduce.ts +971 -0
- package/src/l3/volatileptr.ts +207 -0
- package/src/l3/volatileval.ts +130 -0
- package/src/l3/volstore.ts +229 -0
- package/src/l3/zerosub.ts +62 -0
- package/src/pattern/engine.ts +236 -13
- package/src/pipeline.ts +206 -49
- package/src/proto.ts +112 -14
- package/src/raise/arrays.ts +6 -1
- package/src/raise/divpow2.ts +4 -3
- package/src/raise/globalshape.ts +1038 -0
- package/src/raise/gvn.ts +44 -19
- package/src/raise/latch.ts +126 -0
- package/src/raise/memberarrays.ts +594 -0
- package/src/raise/narrow.ts +124 -0
- package/src/raise/narrowlocal.ts +556 -0
- package/src/raise/paramwidth.ts +179 -0
- package/src/raise/pre-recovery.ts +101 -16
- package/src/raise/recover.ts +56 -23
- package/src/raise/retsink.ts +215 -14
- package/src/raise/shortcircuit.ts +477 -79
- package/src/raise/struct-arrays.ts +21 -3
- package/src/raise/structs.ts +61 -3
- package/src/rank-axes.ts +630 -0
- package/src/rank-declare.ts +256 -0
- package/src/rank.ts +1726 -251
- package/src/structure/analysis.ts +1516 -220
- package/src/structure/bitfields.ts +332 -0
- package/src/structure/globalaccess.ts +274 -0
- package/src/structure/hazards.ts +411 -20
- package/src/structure/loops.ts +2 -49
- package/src/structure/namecoalesce.ts +435 -0
- package/src/structure/structure.ts +2850 -533
- package/src/structure/switch-recover.ts +688 -147
- package/src/symbols.ts +62 -1
- package/src/target.ts +367 -24
- package/src/trace.ts +111 -32
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
// UNAMBIGUOUS here (the scaled operand is the index, read from the machine code) — the unscaled
|
|
19
19
|
// `add(x, y)` byte form stays out of scope (genuinely ambiguous without types).
|
|
20
20
|
import { Fn, Op, Value, defOpMap, mkOp } from '../ir/core';
|
|
21
|
+
import { nextStructIndex } from '../ir/struct-names';
|
|
21
22
|
import { IrType, StructField, T, scalarTypeForAccess } from '../ir/types';
|
|
23
|
+
import { collectStructs } from './structs';
|
|
22
24
|
|
|
23
25
|
interface Scaled {
|
|
24
26
|
base: Value;
|
|
@@ -87,12 +89,22 @@ function withPadding(dataFields: StructField[], stride: number): StructField[] {
|
|
|
87
89
|
* rematerializes the same element address (several `add(base, i*stride)` ops for one logical
|
|
88
90
|
* array), and recovering them one-by-one would let the first claim the base and force its
|
|
89
91
|
* twins to decline — a mixed spelling that is worse than either pure form (found live on
|
|
90
|
-
* pokeemerald:
|
|
92
|
+
* pokeemerald:GetGenderFromSpeciesAndPersonality, whose address is materialized twice). A base
|
|
93
|
+
* whose element pointers
|
|
91
94
|
* disagree on stride declines entirely: two strides over one base is a reinterpreted view or
|
|
92
95
|
* a 2D layout, genuinely ambiguous — decline over guess. */
|
|
93
96
|
export function recognizeStructArrays(fn: Fn): number {
|
|
94
97
|
const defs = defOpMap(fn);
|
|
95
98
|
let count = 0;
|
|
99
|
+
// The NAME index is not the recognition count: `count` is this function's return value — how
|
|
100
|
+
// many groups it recognized — and seeding it would report the wrong number. The index starts
|
|
101
|
+
// past every `Elem<N>` the graph already mentions; over the corpus that scan returns 0 every
|
|
102
|
+
// time, because this pass runs once per function and nothing else mints the prefix, and
|
|
103
|
+
// ir/struct-names.ts states why the computed 0 is kept over the assumed one.
|
|
104
|
+
let name = nextStructIndex(
|
|
105
|
+
[...collectStructs(fn)].map((s) => s.name),
|
|
106
|
+
'Elem',
|
|
107
|
+
);
|
|
96
108
|
|
|
97
109
|
// group candidate element pointers by base, tracking each add's own index and stride
|
|
98
110
|
const byBase = new Map<Value, { add: Op; index: Value; stride: number }[]>();
|
|
@@ -221,7 +233,7 @@ export function recognizeStructArrays(fn: Fn): number {
|
|
|
221
233
|
type: scalarTypeForAccess(width, loadSigned ?? width === 4),
|
|
222
234
|
name: `field_${off}`,
|
|
223
235
|
}));
|
|
224
|
-
const elemStruct = T.struct(`Elem${
|
|
236
|
+
const elemStruct = T.struct(`Elem${name++}`, withPadding(dataFields, stride), stride);
|
|
225
237
|
base.type = T.ptr(elemStruct);
|
|
226
238
|
|
|
227
239
|
// Rewrite each field load/store into an aload/astore carrying base, ITS elem's index,
|
|
@@ -241,7 +253,13 @@ export function recognizeStructArrays(fn: Fn): number {
|
|
|
241
253
|
bb.ops[i] = mkOp('aload', {
|
|
242
254
|
operands: [base, index],
|
|
243
255
|
results: [op.results[0]],
|
|
244
|
-
|
|
256
|
+
// listOrder rides along: an ldmia-expanded load keeps its stream-order caveat as an aload
|
|
257
|
+
attrs: {
|
|
258
|
+
elemSize: stride,
|
|
259
|
+
signed: op.attrs.signed as boolean,
|
|
260
|
+
fieldOff: op.attrs.off as number,
|
|
261
|
+
...(op.attrs.listOrder === true && { listOrder: true }),
|
|
262
|
+
},
|
|
245
263
|
});
|
|
246
264
|
} else if (op.opcode === 'store') {
|
|
247
265
|
bb.ops[i] = mkOp('astore', {
|
package/src/raise/structs.ts
CHANGED
|
@@ -22,8 +22,22 @@
|
|
|
22
22
|
// base @ {off8 w4} -> array (single aligned access — no struct evidence)
|
|
23
23
|
// base @ aload(index) -> array (variable index — untouched)
|
|
24
24
|
//
|
|
25
|
-
// This recovery is BYTE-NEUTRAL — `->field_N` and `[idx]` compile identically, so it
|
|
26
|
-
// representation upgrade driven by access evidence
|
|
25
|
+
// This recovery is USUALLY BYTE-NEUTRAL — `->field_N` and `[idx]` mostly compile identically, so it
|
|
26
|
+
// is a representation upgrade driven by access evidence rather than a scored lever. TWO
|
|
27
|
+
// MEASUREMENTS SAY "USUALLY" IS THE RIGHT WORD, and both were made on agbcc against a real target
|
|
28
|
+
// object, each pair differing in ONE token:
|
|
29
|
+
// • THE SPELLING. `synthetic:dmanest`'s reference compiles from `((struct Elem0 *)K)[a1].field_4`
|
|
30
|
+
// to a byte-exact match and from `((s32 *)((a1 << 3) + K))[1]` to a 2-point diff — a
|
|
31
|
+
// COMPONENT_REF keeps the offset in the load displacement, an index folds it into the pool
|
|
32
|
+
// literal. The row's own dataset entry carries the reproduction.
|
|
33
|
+
// • THE FIELD TYPE, which this file assigns from the ACCESS WIDTH alone. A word field declared
|
|
34
|
+
// `void *` rather than `s32` changes agbcc's alias set and lets a loop-invariant load leave the
|
|
35
|
+
// loop: `synthetic:dmaptrsrc` matches with the pointer declaration and diffs by 35 without it
|
|
36
|
+
// (its own fan: `/vol-store/unreduce/ptr-field` 0, `/vol-store/unreduce` 35).
|
|
37
|
+
// That is what `l3/ptrfield.ts` offers as a differ-ranked lever.
|
|
38
|
+
// So the neutrality claim is CONDITIONAL, and nothing here says on what. Until it does, read it as
|
|
39
|
+
// "no candidate is enumerated for this axis", not as "the differ could not referee one" — the
|
|
40
|
+
// second reading is the one both measurements above falsify. GAPS between accessed
|
|
27
41
|
// offsets (unaccessed leading/interior fields) are filled with `u8[N]` PAD fields so the declared
|
|
28
42
|
// struct reproduces the observed offsets byte-for-byte and is self-describing (the same
|
|
29
43
|
// discipline raise/struct-arrays.ts withPadding uses). Each accessed field must still be
|
|
@@ -31,6 +45,7 @@
|
|
|
31
45
|
// at an offset natural C alignment could not place it at) is rejected LOUD, as is an
|
|
32
46
|
// overlap/union.
|
|
33
47
|
import { Fn, Op, Value } from '../ir/core';
|
|
48
|
+
import { nextStructIndex } from '../ir/struct-names';
|
|
34
49
|
import { IrType, StructField, T, scalarTypeForAccess } from '../ir/types';
|
|
35
50
|
import type { StructType } from '../l3/ast';
|
|
36
51
|
import { RaiseUnsupportedError } from './errors';
|
|
@@ -178,6 +193,21 @@ export function recognizeStructs(fn: Fn): number {
|
|
|
178
193
|
}
|
|
179
194
|
}
|
|
180
195
|
|
|
196
|
+
// Which values are the address of a NAMED global (`gaddr`)? Consulted only when synthesis
|
|
197
|
+
// DECLINES: see the catch below.
|
|
198
|
+
const namedGlobal = new Set<Value>();
|
|
199
|
+
for (const b of fn.blocks) {
|
|
200
|
+
for (const op of b.ops as Op[]) {
|
|
201
|
+
if (op.opcode === 'gaddr') {
|
|
202
|
+
namedGlobal.add(op.results[0]);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// The NAME counter is seeded from the names already in the graph, not from this pass's own
|
|
208
|
+
// success count: raise/memberarrays.ts runs first and mints `Struct<N>` types of its own, and two
|
|
209
|
+
// different layouts under one name would leave `collectStructs` declaring only one of them.
|
|
210
|
+
let name = firstFreeStructIndex(fn);
|
|
181
211
|
let count = 0;
|
|
182
212
|
for (const base of order) {
|
|
183
213
|
if (arrayBases.has(base)) {
|
|
@@ -186,16 +216,44 @@ export function recognizeStructs(fn: Fn): number {
|
|
|
186
216
|
if (base.type.kind !== 'unknown') {
|
|
187
217
|
continue;
|
|
188
218
|
} // already typed (not a bare recovery target)
|
|
219
|
+
|
|
189
220
|
const accesses = accessesOf.get(base)!;
|
|
190
221
|
if (isArray(accesses)) {
|
|
191
222
|
continue;
|
|
192
223
|
} // uniform stride / single aligned access → array
|
|
193
|
-
|
|
224
|
+
try {
|
|
225
|
+
base.type = T.ptr(buildStruct(`Struct${name}`, accesses));
|
|
226
|
+
} catch (e) {
|
|
227
|
+
// A NAMED global whose accesses synthesis cannot reconcile is not a reason to decline the
|
|
228
|
+
// function: its declaration belongs to the project's own headers, and its constant-offset
|
|
229
|
+
// accesses render at L3 through the symbol context (member spelling when the map knows the
|
|
230
|
+
// layout, the honest cast spelling when it does not). The inhabitant is agbcc FUSING two
|
|
231
|
+
// adjacent u8 compares into one ldrh — `s.level == 8 && s.world == 6` reads offset 12 at
|
|
232
|
+
// widths 1 AND 2, which is not a union, just two spellings of declared bytes. An ANONYMOUS
|
|
233
|
+
// base (a loaded pointer, a parameter) has no other source of truth, so for it the decline
|
|
234
|
+
// stands exactly as before — this catch narrows nothing for the shapes that already worked,
|
|
235
|
+
// because a base synthesis succeeds on takes the same path it always took.
|
|
236
|
+
if (e instanceof RaiseUnsupportedError && namedGlobal.has(base)) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
throw e;
|
|
240
|
+
}
|
|
241
|
+
name++;
|
|
194
242
|
count++;
|
|
195
243
|
}
|
|
196
244
|
return count;
|
|
197
245
|
}
|
|
198
246
|
|
|
247
|
+
/** The next `Struct<N>` index past every one this function's graph already uses — the shared name
|
|
248
|
+
* allocator for the two passes that synthesize a struct pointee. The scan itself is
|
|
249
|
+
* `ir/struct-names.ts`, which the three struct minters share; this names the prefix. */
|
|
250
|
+
export function firstFreeStructIndex(fn: Fn): number {
|
|
251
|
+
return nextStructIndex(
|
|
252
|
+
[...collectStructs(fn)].map((s) => s.name),
|
|
253
|
+
'Struct',
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
199
257
|
/** The distinct struct types this function's L2 GRAPH mentions (unwrapping struct pointers on every
|
|
200
258
|
* value), deduped by name and sorted, for the backend to declare above the function.
|
|
201
259
|
*
|