@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
package/src/frontend/splat.ts
CHANGED
|
@@ -210,13 +210,80 @@ function splitOperands(s: string): string[] {
|
|
|
210
210
|
return out;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
/** A COPROCESSOR-1 REGISTER, in every spelling either MIPS dialect writes. THE ONE COPY: this
|
|
214
|
+
* reader decides which tokens keep the `$` sigil and `frontend/mips.ts` passes the same object as
|
|
215
|
+
* `OpaquePolicy.fpReg`, so the two questions — "does this survive normalisation with its sigil?"
|
|
216
|
+
* and "is this the FPU's file?" — are answered by one predicate. Two copies are sequenced rather
|
|
217
|
+
* than merely duplicated: widening the frontend's alone changes nothing, because this one has
|
|
218
|
+
* already stripped the token, and widening this one alone hands the frontend a sigil its own
|
|
219
|
+
* predicate rejects. Each half is silent on its own and the result is an opaque on a register in
|
|
220
|
+
* a file nothing models.
|
|
221
|
+
*
|
|
222
|
+
* Why the sigil: objdump writes a GPR bare (`v0`) and an FPU register with the sigil (`$f12`), so
|
|
223
|
+
* an FPU register is the one operand shape this reader must NOT strip, or the two dialects hand
|
|
224
|
+
* the frontend two different spellings of the same register. It is REQUIRED rather than optional
|
|
225
|
+
* because the bare form is indistinguishable from an objdump branch target, which is also bare
|
|
226
|
+
* lower-case hex (`f4`, `fa0`).
|
|
227
|
+
*
|
|
228
|
+
* The spellings, and all three are live in the trees: objdump's numbers (`$f12`), this dialect's
|
|
229
|
+
* o32 ABI names (`$ft2`, `$fv0`, `$fa0`, `$fs0`), and the trailing `f` those names take for the
|
|
230
|
+
* ODD HALF of a double-precision pair (`$ft0f`, `$fa1f`). `$fp` is the frame pointer and is
|
|
231
|
+
* stripped like any GPR: the digit is what decides, and it is what excludes it.
|
|
232
|
+
*
|
|
233
|
+
* Across every `$`-token in the three MIPS `asm/` trees this matches 52 tokens and every one is an
|
|
234
|
+
* FPU register:
|
|
235
|
+
* `grep -rhoE '\$[A-Za-z0-9_]+' apps/benchmark/checkouts/{af,marioparty3,snowboardkids2-decomp}/asm
|
|
236
|
+
* | sort -u | grep -icE '^\$f[vats]?[0-9]+f?$'` */
|
|
237
|
+
export const MIPS_FP_REG = /^\$f[vats]?\d+f?$/i;
|
|
238
|
+
|
|
239
|
+
/** The o32 ABI names of the EVEN FPU registers, by register number — the only ones a
|
|
240
|
+
* single-precision arithmetic instruction names under the FR=0 register model these compilers
|
|
241
|
+
* target. Read off the instruction words, not a manual: over 9,981 `add.s`/`sub.s`/`mul.s`/`div.s`
|
|
242
|
+
* lines in the `af` and `marioparty3` trees, decoding each word's `fd`/`fs`/`ft` fields gives every
|
|
243
|
+
* name one number and no name two. */
|
|
244
|
+
const O32_FP_NAMES: ReadonlyMap<string, number> = new Map([
|
|
245
|
+
['fv0', 0],
|
|
246
|
+
['fv1', 2],
|
|
247
|
+
['ft0', 4],
|
|
248
|
+
['ft1', 6],
|
|
249
|
+
['ft2', 8],
|
|
250
|
+
['ft3', 10],
|
|
251
|
+
['fa0', 12],
|
|
252
|
+
['fa1', 14],
|
|
253
|
+
['ft4', 16],
|
|
254
|
+
['ft5', 18],
|
|
255
|
+
['fs0', 20],
|
|
256
|
+
['fs1', 22],
|
|
257
|
+
['fs2', 24],
|
|
258
|
+
['fs3', 26],
|
|
259
|
+
['fs4', 28],
|
|
260
|
+
['fs5', 30],
|
|
261
|
+
]);
|
|
262
|
+
|
|
263
|
+
/** An FPU register token, in either dialect's spelling, as the ONE key both dialects share — the
|
|
264
|
+
* objdump number (`$fa0` and `$f12` are both `$f12`) — or null for anything this does not name: a
|
|
265
|
+
* GPR, or an ODD half (`$f13`, `$fa0f`), which only a double-precision value or a word move uses.
|
|
266
|
+
* A key is what the SSA builder names a register by, so two spellings of one register that did
|
|
267
|
+
* not meet here would be two variables. */
|
|
268
|
+
export function mipsEvenFpKey(tok: string): string | null {
|
|
269
|
+
if (!MIPS_FP_REG.test(tok)) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
const bare = tok.slice(1).toLowerCase();
|
|
273
|
+
const num = /^f\d+$/.test(bare) ? Number(bare.slice(1)) : O32_FP_NAMES.get(bare);
|
|
274
|
+
return num !== undefined && num < 32 && num % 2 === 0 ? `$f${num}` : null;
|
|
275
|
+
}
|
|
276
|
+
|
|
213
277
|
// Rewrite one Splat operand into the canonical objdump spelling the frontend consumes: strip the
|
|
214
|
-
// `$` register sigil
|
|
215
|
-
// expression, split a `%hi`/`%lo` reference into
|
|
278
|
+
// `$` register sigil (except on an FPU register, where objdump keeps it), fold a memory operand's
|
|
279
|
+
// displacement expression, evaluate a bare constant expression, split a `%hi`/`%lo` reference into
|
|
280
|
+
// an immediate plus its record, decline a PIC one.
|
|
216
281
|
function normalizeOperand(name: string, op: string): { op: string; reloc?: DisasmReloc } {
|
|
217
282
|
// `%hi(SYM)` / `%lo(SYM + N)` / `%lo(SYM)(base)` — a global's address. Becomes the relocation
|
|
218
283
|
// record an object file would carry plus the immediate the instruction really encodes, so the
|
|
219
284
|
// frontend folds this dialect through the same path as objdump; NOT declined like the PIC relocs.
|
|
285
|
+
// The addend is a constant expression like any other here, so its RADIX is {@link evalConst}'s
|
|
286
|
+
// question rather than this pattern's — {@link refuseOctal} says why the placement is the rule.
|
|
220
287
|
const hilo = op.match(
|
|
221
288
|
/^%(hi|lo)\(\s*([A-Za-z_.$][\w.$]*)\s*(?:([+-])\s*(0x[0-9a-fA-F]+|\d+))?\s*\)(?:\((\$?[A-Za-z]\w*)\))?$/,
|
|
222
289
|
);
|
|
@@ -235,8 +302,8 @@ function normalizeOperand(name: string, op: string): { op: string; reloc?: Disas
|
|
|
235
302
|
if (HILO_OP.test(op)) {
|
|
236
303
|
throw new FrontendUnsupportedError(
|
|
237
304
|
`cannot lift '${name}': relocation operand '${op}' — this reader resolves a '%hi'/'%lo' half ` +
|
|
238
|
-
`only against a symbol ('SYM' or 'SYM ± <integer>'), and will not treat one it cannot
|
|
239
|
-
`as arithmetic`,
|
|
305
|
+
`only against a symbol ('SYM' or 'SYM ± <integer>'), and will not treat one it cannot ` +
|
|
306
|
+
`resolve as arithmetic`,
|
|
240
307
|
);
|
|
241
308
|
}
|
|
242
309
|
if (RELOC_OP.test(op)) {
|
|
@@ -256,9 +323,42 @@ function normalizeOperand(name: string, op: string): { op: string; reloc?: Disas
|
|
|
256
323
|
if (op.startsWith('(')) {
|
|
257
324
|
return plain(String(evalConst(name, op)));
|
|
258
325
|
}
|
|
259
|
-
|
|
326
|
+
// The one path that hands an operand on UNREAD, to a reader somewhere else: `addiu`/`addi`'s
|
|
327
|
+
// re-sign above is one, the MIPS frontend's `parseImm` another, and neither is told the radix, so
|
|
328
|
+
// a leading-zero immediate stops here — `addiu $v0, $a0, 020` lifted as `a0 + 20` against the
|
|
329
|
+
// assembler's 16.
|
|
330
|
+
if (OCTAL_MAGNITUDE.test(op)) {
|
|
331
|
+
refuseOctal(name, op, op);
|
|
332
|
+
}
|
|
333
|
+
return plain(MIPS_FP_REG.test(op) ? op : op.replace(/^\$/, ''));
|
|
260
334
|
}
|
|
261
335
|
|
|
336
|
+
/** A magnitude with a LEADING ZERO is octal to the assembler and decimal to every `parseInt(…, 10)`
|
|
337
|
+
* in this file. Both readings are measured rather than assumed: under `mips-linux-gnu-as` 2.45,
|
|
338
|
+
* `.word 020` assembles to 0x10 and `.word 010` to 8 read back out of `.data`, and
|
|
339
|
+
* `lw $v0, 020($a0)` encodes the displacement 16 — against the 20 and the 10 a decimal reader
|
|
340
|
+
* answers. The difference reached the emitted C as a different ELEMENT: `%lo(gTab + 020)` lifted
|
|
341
|
+
* as `((s32 *)&gTab)[5]` where the assembler's addend gives `[4]`, and `%lo(gTab + 010)` as the
|
|
342
|
+
* fractional index `2.5`. A wrong address compiles and scores, so this is not something to guess.
|
|
343
|
+
*
|
|
344
|
+
* This reader models hex and decimal, so the third radix REFUSES rather than being resolved:
|
|
345
|
+
* nothing produces the shape — 0 operand occurrences carrying a leading-zero integer over the
|
|
346
|
+
* 4,200,524 operands of the 20,260 `.s` files that carry a `glabel` or a `.set noreorder` in the
|
|
347
|
+
* nine benchmark checkouts (counted with python; a recursive `grep` here skips `build/`) — so a
|
|
348
|
+
* reading for it would be a capability nothing could referee.
|
|
349
|
+
*
|
|
350
|
+
* THE PLACEMENT IS THE POINT. The rule belongs to the readers that turn a digit string into a
|
|
351
|
+
* value, not to the patterns that feed them: a guard on one caller's regex leaves the next caller
|
|
352
|
+
* reading base 10, and the three readers that do — `evalConst`'s literal, {@link signExtend16} and
|
|
353
|
+
* the shared `parseImm` — are not all in this file. */
|
|
354
|
+
const OCTAL_MAGNITUDE = /^[-+]?0\d/;
|
|
355
|
+
const refuseOctal = (name: string, tok: string, operand: string): never => {
|
|
356
|
+
throw new FrontendUnsupportedError(
|
|
357
|
+
`cannot lift '${name}': operand '${operand}' has a leading-zero magnitude ('${tok}'), which is ` +
|
|
358
|
+
`octal to the assembler and a radix this reader does not model`,
|
|
359
|
+
);
|
|
360
|
+
};
|
|
361
|
+
|
|
262
362
|
// Evaluate a constant integer expression (the assembler's hi/lo split: hex/dec literals with
|
|
263
363
|
// `+ - * << >> & | ^ ~` and parentheses). Precedence-climbing; C-like precedence. A shift `>>` is
|
|
264
364
|
// LOGICAL — Splat's operands are unsigned 32-bit constants. Anything unparsable declines LOUD
|
|
@@ -292,6 +392,9 @@ function evalConst(name: string, expr: string): number {
|
|
|
292
392
|
return ~unary();
|
|
293
393
|
}
|
|
294
394
|
if (/^(0x[0-9a-fA-F]+|\d+)$/.test(t)) {
|
|
395
|
+
if (OCTAL_MAGNITUDE.test(t)) {
|
|
396
|
+
refuseOctal(name, t, expr);
|
|
397
|
+
}
|
|
295
398
|
return t.toLowerCase().startsWith('0x') ? parseInt(t, 16) : parseInt(t, 10);
|
|
296
399
|
}
|
|
297
400
|
return fail();
|