@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/trace.ts
CHANGED
|
@@ -12,8 +12,9 @@ import { print } from './ir/print';
|
|
|
12
12
|
import { verify } from './ir/verify';
|
|
13
13
|
import type { LanguageBackend } from './l3/ast';
|
|
14
14
|
import { DEFAULT_IDIOM_PATTERNS, RewritePattern, applyPattern, dce, patternApplies } from './pattern/engine';
|
|
15
|
-
import { type OnGap, liftStamped, raiseRecovered, structureChecked, stubResult } from './pipeline';
|
|
16
|
-
import { type Prototypes, declaresVoidReturn, prototypesFromSymbols } from './proto';
|
|
15
|
+
import { type OnGap, liftStamped, raiseRecovered, stampBehaviorsFor, structureChecked, stubResult } from './pipeline';
|
|
16
|
+
import { type FnProto, type Prototypes, declaresVoidReturn, prototypesFromSymbols } from './proto';
|
|
17
|
+
import { type ContextGlobals, splitOwnContext } from './proto-context';
|
|
17
18
|
import { assumedShapes, inferGlobalArrays, orderLicensedGlobals } from './raise/globalshape';
|
|
18
19
|
import { OFFSET_NAME_PASS } from './raise/offsetnames';
|
|
19
20
|
import { type SymbolInfo, type SymbolMap, symbolsByName } from './symbols';
|
|
@@ -92,6 +93,8 @@ export interface TraceOptions {
|
|
|
92
93
|
prototypes?: Prototypes; // header facts (callee arities + void-ness), keyed by symbol
|
|
93
94
|
asmData?: AsmData; // data-section side table (Regime-B jump tables), as in decompile()
|
|
94
95
|
symbols?: SymbolMap; // address→symbol map (symbols.ts), as in decompile(); absent ⇒ inert
|
|
96
|
+
contextGlobals?: ContextGlobals; // the declaration context's globals, as in decompile()
|
|
97
|
+
ownContextProto?: FnProto; // the context's own entry, as in decompile()
|
|
95
98
|
onGap?: OnGap; // "strict" (default) | "annotate", as in decompile()
|
|
96
99
|
/** Source probe at pattern boundaries: the source asmlift would emit there, which the cli report
|
|
97
100
|
* scores. One call per boundary: pattern N's after-source is pattern N+1's before-source. Absent ⇒
|
|
@@ -189,13 +192,24 @@ function traceTower(
|
|
|
189
192
|
// Merged exactly as pipeline.ts does: the project's own DWARF signatures fill in what the caller
|
|
190
193
|
// did not state. A trace that lifted from a different table would explain a run that never
|
|
191
194
|
// happened.
|
|
192
|
-
const
|
|
195
|
+
const ownContext = splitOwnContext(opts.prototypes, name, opts.ownContextProto);
|
|
196
|
+
const prototypes = prototypesFromSymbols(opts.symbols, ownContext.prototypes ?? {});
|
|
193
197
|
const returnsVoid = declaresVoidReturn(prototypes[name]);
|
|
194
198
|
const trace: StageTrace[] = [];
|
|
195
199
|
const patternEvents: PatternEvent[] = [];
|
|
196
200
|
|
|
197
201
|
// (1) lift → typed-SSA IR
|
|
198
|
-
const fn = liftStamped(
|
|
202
|
+
const fn = liftStamped(
|
|
203
|
+
name,
|
|
204
|
+
asm,
|
|
205
|
+
target,
|
|
206
|
+
prototypes,
|
|
207
|
+
opts.asmData,
|
|
208
|
+
opts.symbols,
|
|
209
|
+
opts.contextGlobals,
|
|
210
|
+
undefined,
|
|
211
|
+
ownContext.ownContextProto,
|
|
212
|
+
);
|
|
199
213
|
trace.push({ id: 'stage:lift', title: 'Lift (ISA frontend → typed-SSA IR)', irDump: irDump(fn), verified: true });
|
|
200
214
|
// The array shapes the assembly evidences, off the LIFTED fn — same reading, same reason, as
|
|
201
215
|
// pipeline.ts's runTower: the fold and the tower below destroy the order the licence reads.
|
|
@@ -221,7 +235,9 @@ function traceTower(
|
|
|
221
235
|
// The names the map holds for addresses this machine code built by arithmetic off a named one
|
|
222
236
|
// (raise/offsetnames.ts), off the same lifted fn and for the same reason as pipeline.ts's
|
|
223
237
|
// runTower.
|
|
224
|
-
const named = opts.symbols
|
|
238
|
+
const named = opts.symbols
|
|
239
|
+
? OFFSET_NAME_PASS.run(fn, opts.symbols, stampBehaviorsFor(target, opts.contextGlobals))
|
|
240
|
+
: [];
|
|
225
241
|
trace.push({
|
|
226
242
|
id: 'stage:offsetnames',
|
|
227
243
|
title: 'Name a walked-to address the symbol map knows',
|
|
@@ -302,10 +318,10 @@ function traceTower(
|
|
|
302
318
|
irDump: irDump(fn),
|
|
303
319
|
verified: true,
|
|
304
320
|
}),
|
|
305
|
-
|
|
321
|
+
afterFold: (fold) =>
|
|
306
322
|
trace.push({
|
|
307
|
-
id:
|
|
308
|
-
title:
|
|
323
|
+
id: `stage:${fold.id}`,
|
|
324
|
+
title: fold.title,
|
|
309
325
|
irDump: irDump(fn),
|
|
310
326
|
verified: true,
|
|
311
327
|
}),
|
|
@@ -319,8 +335,10 @@ function traceTower(
|
|
|
319
335
|
const sfn = structureChecked(fn, {
|
|
320
336
|
...structureOptionsFor(target, returnsVoid, prototypes),
|
|
321
337
|
spellSwitchFallthrough: (opts.backend ?? cBackend).spellsSwitchFallthrough,
|
|
338
|
+
spellSwitchBreak: (opts.backend ?? cBackend).spellsSwitchBreak,
|
|
322
339
|
onGap: opts.onGap ?? 'strict',
|
|
323
340
|
...(mapSymbols ? { symbols: mapSymbols } : {}),
|
|
341
|
+
...(opts.contextGlobals ? { contextGlobals: opts.contextGlobals } : {}),
|
|
324
342
|
...(inferredSymbols.size ? { inferredSymbols } : {}),
|
|
325
343
|
...(orderLicensed.size ? { orderLicensedGlobals: orderLicensed } : {}),
|
|
326
344
|
});
|
|
@@ -96,7 +96,7 @@ export const VARIATION_KIND_DEFINITIONS: { readonly [K in VariationKind]: Variat
|
|
|
96
96
|
title: 'Structure',
|
|
97
97
|
meaning:
|
|
98
98
|
'How the checked control flow becomes C: which way a test reads, where a loop starts, what reaches a merge (the point where two paths meet), where a value is kept, and how a read or a compare is spelled.',
|
|
99
|
-
examples: ['flip-branch', 'defsite', 'loop-entry', 'reread-globals', '
|
|
99
|
+
examples: ['flip-branch', 'defsite', 'loop-entry', 'reread-globals', 'signed-cmp'],
|
|
100
100
|
},
|
|
101
101
|
respell: {
|
|
102
102
|
title: 'Respell',
|
|
@@ -296,7 +296,7 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
296
296
|
after: 's32 example(u32 a0, u32 a1)',
|
|
297
297
|
},
|
|
298
298
|
implementedIn: RANK,
|
|
299
|
-
seeAlso: ['signed', '
|
|
299
|
+
seeAlso: ['signed', 'signed-cmp'],
|
|
300
300
|
},
|
|
301
301
|
signed: {
|
|
302
302
|
title: 'Signed parameters',
|
|
@@ -371,6 +371,31 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
371
371
|
implementedIn: 'packages/core/src/raise/shortcircuit.ts',
|
|
372
372
|
seeAlso: ['site-sense'],
|
|
373
373
|
},
|
|
374
|
+
'narrow-param': {
|
|
375
|
+
title: 'Parameter declared at the width every use extends it to',
|
|
376
|
+
summary: 'a parameter read only through narrowing casts is declared narrow, though the prologue does not widen it',
|
|
377
|
+
detail:
|
|
378
|
+
'A parameter the function only ever reads through narrowing casts can be declared at 32 bits and cast ' +
|
|
379
|
+
'at each use, or declared at the widest cast and read bare. Every use is the same value either way, ' +
|
|
380
|
+
'and where the compiler widens it at each use rather than on entry, the assembly does not say which ' +
|
|
381
|
+
'one the source declared. This lift declares it narrow, and loses a tie when no prototype declares the width.',
|
|
382
|
+
compilerBehavior:
|
|
383
|
+
'agbcc widens an `s16` parameter at each use when every use extends it, and then the two declarations ' +
|
|
384
|
+
'differ only in how the registers around them are allocated: a sibling `u8` parameter is copied ' +
|
|
385
|
+
'before it is widened under the narrow one.',
|
|
386
|
+
offeredWhen: {
|
|
387
|
+
when: 'Some parameter is read only through 8- or 16-bit casts or masks, and the default lift left it 32 bits wide.',
|
|
388
|
+
decidedBy: { symbol: 'runPreRecovery', file: 'packages/core/src/raise/pre-recovery.ts' },
|
|
389
|
+
gates: ['PARAM_READER_WIDTH_GATES'],
|
|
390
|
+
},
|
|
391
|
+
example: {
|
|
392
|
+
compiler: 'agbcc',
|
|
393
|
+
unit: 'extern u8 *gM; void f3(u8 *, u16, u8); void f2(u8 *, u8);\n@',
|
|
394
|
+
before: 'void example(s32 a, u8 b) { u8 *p = gM; f3(p + (s16)a * 4, (u16)a, b); f2(p + b * 8, b); }',
|
|
395
|
+
after: 'void example(s16 a, u8 b) { u8 *p = gM; f3(p + a * 4, (u16)a, b); f2(p + b * 8, b); }',
|
|
396
|
+
},
|
|
397
|
+
implementedIn: 'packages/core/src/raise/paramwidth.ts',
|
|
398
|
+
},
|
|
374
399
|
'shared-ret': {
|
|
375
400
|
title: 'Shared code after an early return',
|
|
376
401
|
summary: 'code both arms of an `if` reach before returning is written once, after the `if`',
|
|
@@ -420,6 +445,63 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
420
445
|
implementedIn: 'packages/core/src/raise/tailsink.ts',
|
|
421
446
|
seeAlso: ['shared-ret'],
|
|
422
447
|
},
|
|
448
|
+
'kept-slot': {
|
|
449
|
+
title: 'Each arm keeps its copy of a shared tail',
|
|
450
|
+
summary: "a MIPS jump's delay slot that ends a tail both arms spell stays in its own arm",
|
|
451
|
+
detail:
|
|
452
|
+
'IDO, KMC and GCC 2.7.2 fill the delay slot of a `b` or `j` with a copy of the first word at its ' +
|
|
453
|
+
'target, so by default the jump is read as landing one word earlier and the two arms meet there. ' +
|
|
454
|
+
'Where the word before the jump also matches the word before that copy, the two arms may instead ' +
|
|
455
|
+
'have been written out twice, each ending in the same instructions. This lift keeps the slot in its ' +
|
|
456
|
+
'own arm, so each arm keeps the whole tail it spells.',
|
|
457
|
+
compilerBehavior:
|
|
458
|
+
'The two readings are one object: the slot runs before the target on its one edge either way. ' +
|
|
459
|
+
'Which arms the source wrote out is something only the differ can tell. The question arises on IDO ' +
|
|
460
|
+
'alone: GCC 2.7.2 and KMC cross-jump a tail both arms spell into one copy at the join before they ' +
|
|
461
|
+
'fill any slot, so their output has no tail written twice (`mergenarrow` builds with no `b` on either).',
|
|
462
|
+
offeredWhen: {
|
|
463
|
+
when: 'The MIPS lift reported a `b`/`j` whose slot ends a common tail of two or more words, and retargeted it.',
|
|
464
|
+
decidedBy: { symbol: 'enumerateCandidates', file: RANK },
|
|
465
|
+
},
|
|
466
|
+
example: {
|
|
467
|
+
compiler: 'ido',
|
|
468
|
+
unit: 'void example(s32 *out, s32 a, s32 b, s32 c) { @ }',
|
|
469
|
+
before: 's32 v; if (c) { v = (a + b) << 16; } else { v = (a - b) << 16; } out[0] = v >> 16;',
|
|
470
|
+
after: 's16 v; if (c) { v = a + b; } else { v = a - b; } out[0] = v;',
|
|
471
|
+
note: 'the shape of `synthetic:mergenarrow`',
|
|
472
|
+
},
|
|
473
|
+
implementedIn: 'packages/core/src/frontend/mips.ts',
|
|
474
|
+
},
|
|
475
|
+
'far-cast': {
|
|
476
|
+
title: 'Store past the displacement range through a cast',
|
|
477
|
+
summary: 'a store to a member past the instruction offset range is spelled through a cast of the base',
|
|
478
|
+
detail:
|
|
479
|
+
'A member past the offset an instruction can carry is reached through an address the machine adds up ' +
|
|
480
|
+
'first, and by default that access joins its struct and is spelled as the member. A store there could ' +
|
|
481
|
+
'equally have been written through a cast of the base, `*((u8 *)p + 38) = v`, or through a pointer the ' +
|
|
482
|
+
'source held, and on some compilers that is a different object. This lift keeps every such store on ' +
|
|
483
|
+
'the address it was reached through; loads still join the struct.',
|
|
484
|
+
compilerBehavior:
|
|
485
|
+
'agbcc evaluates the right-hand side of a member store before it adds up the address, and the address ' +
|
|
486
|
+
'of a cast store before the right-hand side, so the two spellings allocate and order registers ' +
|
|
487
|
+
"differently; a hoisted `u8 *d = &p->f38; *d = v;` builds the cast spelling's object. Elsewhere the " +
|
|
488
|
+
'member and the cast compile alike one access at a time, but a folded store no longer aliases the ' +
|
|
489
|
+
'loads beside it, so the lift moves those loads past it, and a pointer the source held is spelled as ' +
|
|
490
|
+
'members: KMC and GCC 2.7.2 build two loads written before a store and the same loads written after ' +
|
|
491
|
+
'it into different objects, and two of the three mwcc builds allocate a held pointer and the member ' +
|
|
492
|
+
'spelling different registers.',
|
|
493
|
+
offeredWhen: {
|
|
494
|
+
when: 'The default lift folded a store through a materialised `base + K` address into its struct.',
|
|
495
|
+
decidedBy: { symbol: 'enumerateCandidates', file: RANK },
|
|
496
|
+
},
|
|
497
|
+
example: {
|
|
498
|
+
compiler: 'agbcc',
|
|
499
|
+
unit: 'struct S { s32 a; u8 pad[34]; u8 f38; };\nvoid example(struct S *p, u8 *q) { @ }',
|
|
500
|
+
before: 'p->f38 = *q; p->a = 0;',
|
|
501
|
+
after: '*((u8 *)p + 38) = *q; p->a = 0;',
|
|
502
|
+
},
|
|
503
|
+
implementedIn: 'packages/core/src/raise/structs.ts',
|
|
504
|
+
},
|
|
423
505
|
|
|
424
506
|
// ── structure ───────────────────────────────────────────────────────────────────────────────
|
|
425
507
|
'flip-branch': {
|
|
@@ -772,14 +854,16 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
772
854
|
implementedIn: ANALYSIS,
|
|
773
855
|
seeAlso: ['expr-home', 'derived-home'],
|
|
774
856
|
},
|
|
775
|
-
'
|
|
776
|
-
title: 'Unsigned compare spelled
|
|
777
|
-
summary: 'an unsigned compare
|
|
857
|
+
'signed-cmp': {
|
|
858
|
+
title: 'Unsigned compare spelled signed',
|
|
859
|
+
summary: 'an unsigned compare drops the `(u32)` cast the default gives an operand that does not already say so',
|
|
778
860
|
detail:
|
|
779
|
-
'
|
|
780
|
-
'unsigned, and a declaration claimed by both kinds of use becomes `u32` when nothing under it
|
|
781
|
-
'signed.
|
|
782
|
-
'
|
|
861
|
+
'By default an unsigned comparison takes a `(u32)` cast on an operand the rendering does not already ' +
|
|
862
|
+
'make unsigned, and a declaration claimed by both kinds of use becomes `u32` when nothing under it ' +
|
|
863
|
+
'needs signed. This writes the comparison without them, as a signed one. The source may have done so: ' +
|
|
864
|
+
'a compiler emits an unsigned compare for a signed one where it proved both sides non-negative, and ' +
|
|
865
|
+
'asmlift can prove less than the compiler can. Where the sides can be negative it computes something ' +
|
|
866
|
+
'else, so it is published only when the candidate matches byte for byte; otherwise it is withheld.',
|
|
783
867
|
compilerBehavior:
|
|
784
868
|
'A compiler emits an unsigned branch from a signed compare only where it proved both sides non-negative.',
|
|
785
869
|
offeredWhen: {
|
|
@@ -789,8 +873,8 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
789
873
|
example: {
|
|
790
874
|
compiler: 'agbcc',
|
|
791
875
|
unit: CALLS + 'void example(s32 a, s32 b) { @ A(); }',
|
|
792
|
-
before: 'if (a < b)',
|
|
793
|
-
after: 'if (
|
|
876
|
+
before: 'if ((u32)a < b)',
|
|
877
|
+
after: 'if (a < b)',
|
|
794
878
|
},
|
|
795
879
|
implementedIn: STRUCTURE,
|
|
796
880
|
seeAlso: ['unsigned', 'signed'],
|
|
@@ -835,8 +919,12 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
835
919
|
},
|
|
836
920
|
example: {
|
|
837
921
|
compiler: 'agbcc',
|
|
922
|
+
// The callees are PROTOTYPED here, unlike `CALLS`: `bl f` sees `c` read in r2 and a0 untouched
|
|
923
|
+
// in r0, which a pass-through `f(a0, a1, c)` would leave the same way, so an undeclared `f`
|
|
924
|
+
// declines (frontend/ssa.ts `fallbackArgc` refuses the gap). The lift reads these declarations
|
|
925
|
+
// off the unit as shown.
|
|
838
926
|
unit:
|
|
839
|
-
|
|
927
|
+
's32 f(void); s32 g(void); s32 h(s32, s32);\n' +
|
|
840
928
|
's32 example(s32 a0, s32 a1, s32 c) { s32 v1; s32 v2; if (c) { v1 = f() + 1; v2 = g(); } else { @ } return h(v1, v2); }',
|
|
841
929
|
before: 'v2 = a1; v1 = a0;',
|
|
842
930
|
after: 'v1 = a0; v2 = a1;',
|
|
@@ -1630,7 +1718,31 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
1630
1718
|
after: 'u8 v; v = gA[12] - 1; gB[14] = (v & 1) + f() % (5 - v) + 1;',
|
|
1631
1719
|
},
|
|
1632
1720
|
implementedIn: l3('narrowdecl'),
|
|
1633
|
-
seeAlso: ['narrow-read', 'derived-home', 'escape-home'],
|
|
1721
|
+
seeAlso: ['narrow-load', 'narrow-read', 'derived-home', 'escape-home'],
|
|
1722
|
+
},
|
|
1723
|
+
'narrow-load': {
|
|
1724
|
+
title: 'Narrow load declared at its width',
|
|
1725
|
+
summary: 'a local holding one narrow memory read is declared at that width instead of as an `s32`',
|
|
1726
|
+
detail:
|
|
1727
|
+
'A byte or halfword read from memory into a local can be spelled two ways: an `s32` local assigned the ' +
|
|
1728
|
+
"read, or a local declared at the read's width. Every read of the local is the same value either way; " +
|
|
1729
|
+
"which one the source declared is not in the assembly. Applied on top of every other candidate's source.",
|
|
1730
|
+
compilerBehavior:
|
|
1731
|
+
'agbcc loads a narrow local where it is written and an `s32` one where it is first read, so two byte ' +
|
|
1732
|
+
'reads held across two calls are loaded at different points.',
|
|
1733
|
+
offeredWhen: {
|
|
1734
|
+
when: 'An `s32` local written once, outside a `for` init, by an element or member read of a narrower integer, in no memory.',
|
|
1735
|
+
decidedBy: { symbol: 'narrowLoadDeclarations', file: l3('narrowdecl') },
|
|
1736
|
+
},
|
|
1737
|
+
example: {
|
|
1738
|
+
compiler: 'agbcc',
|
|
1739
|
+
unit: 'void f7(u8 *, u8 *, u8, u8, s32, u8, u8);\nvoid example(u8 *p, s32 x) { @ }',
|
|
1740
|
+
before:
|
|
1741
|
+
's32 a; s32 b; a = p[100]; b = p[101]; f7(p + 4, p + 8, a, b, x, 1, 2); f7(p + 8, p + 12, a, b, x, 3, 4);',
|
|
1742
|
+
after: 'u8 a; u8 b; a = p[100]; b = p[101]; f7(p + 4, p + 8, a, b, x, 1, 2); f7(p + 8, p + 12, a, b, x, 3, 4);',
|
|
1743
|
+
},
|
|
1744
|
+
implementedIn: l3('narrowdecl'),
|
|
1745
|
+
seeAlso: ['narrow-decl', 'narrow-read'],
|
|
1634
1746
|
},
|
|
1635
1747
|
'narrow-read': {
|
|
1636
1748
|
title: 'Narrow value declared at its width, read bare',
|
|
@@ -1653,7 +1765,7 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
1653
1765
|
after: 'u8 y; u8 m; y = cv(p[0]); m = cv(p[1]); return dc(y, m, (u8)cv(p[2]));',
|
|
1654
1766
|
},
|
|
1655
1767
|
implementedIn: l3('narrowdecl'),
|
|
1656
|
-
seeAlso: ['narrow-decl'],
|
|
1768
|
+
seeAlso: ['narrow-decl', 'narrow-load'],
|
|
1657
1769
|
},
|
|
1658
1770
|
'int-cell': {
|
|
1659
1771
|
title: 'Global stored a pointer, spelled as an integer',
|
package/src/variation-gates.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
import { UNREDUCE_GATES } from './l3/unreduce';
|
|
30
30
|
import { VOL_SLOT_GATES } from './l3/volatileval';
|
|
31
31
|
import { VOL_STORE_GATES } from './l3/volstore';
|
|
32
|
+
import { PARAM_READER_WIDTH_GATES } from './raise/paramwidth';
|
|
32
33
|
import { ARM_REREAD_GATES } from './raise/shortcircuit';
|
|
33
34
|
import { NAME_COALESCE_GATES } from './structure/namecoalesce';
|
|
34
35
|
import { FRESH_MERGE_GATES } from './structure/structure';
|
|
@@ -59,6 +60,7 @@ export const VARIATION_GATE_TABLES = {
|
|
|
59
60
|
NAME_COALESCE_GATES,
|
|
60
61
|
OFFMEMBER_GATES,
|
|
61
62
|
ORDERBASE_GATES,
|
|
63
|
+
PARAM_READER_WIDTH_GATES,
|
|
62
64
|
PTR_FIELD_GATES,
|
|
63
65
|
REGIONBASE_GATES,
|
|
64
66
|
SCOPEBASE_ELIGIBILITY,
|
package/src/variation-tokens.ts
CHANGED
|
@@ -67,8 +67,11 @@ const TOKENS = [
|
|
|
67
67
|
// lift: the assembly lifted or raised again
|
|
68
68
|
{ name: 'setup-args', variationKind: 'lift' },
|
|
69
69
|
{ name: 'connective', variationKind: 'lift' },
|
|
70
|
+
{ name: 'narrow-param', variationKind: 'lift' },
|
|
70
71
|
{ name: 'shared-ret', variationKind: 'lift' },
|
|
71
72
|
{ name: 'shared-tail', variationKind: 'lift' },
|
|
73
|
+
{ name: 'kept-slot', variationKind: 'lift' },
|
|
74
|
+
{ name: 'far-cast', variationKind: 'lift' },
|
|
72
75
|
// structure: `structure()` re-run with other options
|
|
73
76
|
{ name: 'flip-branch', variationKind: 'structure' },
|
|
74
77
|
{ name: 'defsite', variationKind: 'structure' },
|
|
@@ -86,7 +89,7 @@ const TOKENS = [
|
|
|
86
89
|
{ name: 'derived-home', variationKind: 'structure' },
|
|
87
90
|
{ name: 'merge-home', variationKind: 'structure' },
|
|
88
91
|
{ name: 'escape-home', variationKind: 'structure' },
|
|
89
|
-
{ name: '
|
|
92
|
+
{ name: 'signed-cmp', variationKind: 'structure' },
|
|
90
93
|
{ name: 'fresh-merge', variationKind: 'structure' },
|
|
91
94
|
{ name: 'copy-defpos', variationKind: 'structure' },
|
|
92
95
|
{ name: 'site-sense', variationKind: 'structure' },
|
|
@@ -131,6 +134,7 @@ const TOKENS = [
|
|
|
131
134
|
{ name: 'pollguard', variationKind: 'respell' },
|
|
132
135
|
{ name: 'pollread', variationKind: 'respell' },
|
|
133
136
|
{ name: 'narrow-decl', variationKind: 'respell' },
|
|
137
|
+
{ name: 'narrow-load', variationKind: 'respell' },
|
|
134
138
|
{ name: 'narrow-read', variationKind: 'respell' },
|
|
135
139
|
{
|
|
136
140
|
name: 'int-cell',
|