@asmlift/core 0.9.0 → 0.11.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 +2 -0
- package/src/backend/cfamily.ts +197 -61
- package/src/backend/cpp.ts +3 -1
- package/src/backend/lines.ts +10 -0
- package/src/backend/pascal.ts +47 -16
- package/src/cdecl/parse.ts +21 -0
- package/src/contracts.ts +246 -36
- package/src/declare.ts +69 -20
- package/src/frontend/arm-body.ts +155 -0
- package/src/frontend/asmdata.ts +9 -2
- package/src/frontend/call-plan.ts +1117 -61
- package/src/frontend/callee-body.ts +370 -0
- package/src/frontend/dead-reads.ts +628 -0
- package/src/frontend/device-pins.ts +33 -4
- package/src/frontend/emit.ts +4 -3
- package/src/frontend/fpu.ts +224 -17
- package/src/frontend/frame-objects.ts +1689 -206
- package/src/frontend/frontend.ts +84 -3
- package/src/frontend/high-half.ts +53 -4
- package/src/frontend/jump-table.ts +545 -0
- package/src/frontend/local-object.ts +158 -32
- package/src/frontend/mips.ts +2296 -217
- package/src/frontend/own-struct-return.ts +500 -0
- package/src/frontend/ppc.ts +2748 -245
- package/src/frontend/register-struct-call.ts +399 -0
- package/src/frontend/reloc-symbol.ts +22 -7
- package/src/frontend/ssa.ts +1721 -43
- package/src/frontend/stackargs.ts +199 -72
- package/src/frontend/thumb-entry.ts +340 -0
- package/src/frontend/thumb.ts +2400 -325
- package/src/ir/alias.ts +65 -2
- package/src/ir/bits.ts +1699 -4
- package/src/ir/core.ts +254 -9
- package/src/ir/discipline.ts +86 -21
- package/src/ir/entry.ts +157 -0
- package/src/{structure → ir}/loops.ts +6 -2
- package/src/ir/opcodes.ts +137 -9
- package/src/ir/parse.ts +1 -0
- package/src/ir/simplify.ts +61 -10
- package/src/ir/types.ts +86 -2
- package/src/ir/verify.ts +96 -11
- package/src/l3/argbase.ts +2 -0
- package/src/l3/ast.ts +582 -18
- package/src/l3/basecse.ts +68 -15
- package/src/l3/caseendgoto.ts +223 -0
- package/src/l3/coalesce.ts +42 -14
- package/src/l3/constinit.ts +410 -0
- package/src/l3/dce.ts +53 -9
- 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 +33 -34
- package/src/l3/ptrfield.ts +123 -8
- package/src/l3/regspell.ts +11 -4
- package/src/l3/reindex.ts +295 -99
- package/src/l3/scopebase.ts +2 -0
- package/src/l3/storage.ts +98 -1
- package/src/l3/symbol-refs.ts +84 -6
- package/src/l3/tailmerge.ts +6 -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 +1039 -38
- package/src/proto-context.ts +1039 -45
- package/src/proto.ts +555 -40
- package/src/raise/aggmembers.ts +290 -0
- package/src/raise/arrays.ts +5 -1
- package/src/raise/const.ts +25 -0
- package/src/raise/declared-volatile.ts +207 -31
- package/src/raise/divpow2.ts +192 -0
- 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 +422 -36
- package/src/raise/pairparams.ts +33 -0
- package/src/raise/paramwidth.ts +196 -6
- package/src/raise/pre-recovery.ts +107 -21
- package/src/raise/pre-structure.ts +73 -0
- package/src/raise/recover.ts +12 -5
- package/src/raise/reroll.ts +674 -0
- package/src/raise/shortcircuit.ts +217 -34
- 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/unpre.ts +594 -0
- package/src/raise/unthread.ts +400 -0
- package/src/rank-declare.ts +118 -12
- package/src/rank-variations.ts +186 -35
- package/src/rank.ts +536 -68
- package/src/runtime-helpers.ts +140 -22
- package/src/structure/analysis.ts +217 -8
- package/src/structure/arith-ops.ts +23 -0
- package/src/structure/cross-jumped-tail.ts +216 -0
- package/src/structure/globalaccess.ts +157 -10
- 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 +6192 -1213
- package/src/structure/switch-recover.ts +583 -120
- package/src/structure/unfolded-branch-constant.ts +90 -0
- package/src/symbols.ts +58 -15
- package/src/target.ts +1028 -54
- package/src/trace.ts +49 -17
- package/src/variation-definitions.ts +333 -20
- package/src/variation-gates.ts +4 -0
- package/src/variation-tokens.ts +68 -3
package/src/trace.ts
CHANGED
|
@@ -7,13 +7,22 @@
|
|
|
7
7
|
import { cBackend } from './backend/c';
|
|
8
8
|
import type { CodegenProfile } from './codegen-flags';
|
|
9
9
|
import type { AsmData } from './frontend/asmdata';
|
|
10
|
+
import type { CalleeBodies } from './frontend/callee-body';
|
|
10
11
|
import type { Fn } from './ir/core';
|
|
11
12
|
import { print } from './ir/print';
|
|
12
13
|
import { verify } from './ir/verify';
|
|
13
14
|
import type { LanguageBackend } from './l3/ast';
|
|
14
15
|
import { DEFAULT_IDIOM_PATTERNS, RewritePattern, applyPattern, dce, patternApplies } from './pattern/engine';
|
|
15
|
-
import {
|
|
16
|
-
|
|
16
|
+
import {
|
|
17
|
+
type OnGap,
|
|
18
|
+
liftStamped,
|
|
19
|
+
offsetNameBehaviorsFor,
|
|
20
|
+
raiseRecovered,
|
|
21
|
+
structureReturnTailReading,
|
|
22
|
+
stubResult,
|
|
23
|
+
} from './pipeline';
|
|
24
|
+
import { type FnProto, type Prototypes, declaresVoidReturn, prototypesFromSymbols } from './proto';
|
|
25
|
+
import { type ContextGlobals, splitOwnContext } from './proto-context';
|
|
17
26
|
import { assumedShapes, inferGlobalArrays, orderLicensedGlobals } from './raise/globalshape';
|
|
18
27
|
import { OFFSET_NAME_PASS } from './raise/offsetnames';
|
|
19
28
|
import { type SymbolInfo, type SymbolMap, symbolsByName } from './symbols';
|
|
@@ -92,6 +101,9 @@ export interface TraceOptions {
|
|
|
92
101
|
prototypes?: Prototypes; // header facts (callee arities + void-ness), keyed by symbol
|
|
93
102
|
asmData?: AsmData; // data-section side table (Regime-B jump tables), as in decompile()
|
|
94
103
|
symbols?: SymbolMap; // address→symbol map (symbols.ts), as in decompile(); absent ⇒ inert
|
|
104
|
+
contextGlobals?: ContextGlobals; // the declaration context's globals, as in decompile()
|
|
105
|
+
ownContextProto?: FnProto; // the context's own entry, as in decompile()
|
|
106
|
+
calleeBodies?: CalleeBodies; // bodies of callees outside the input, as in decompile()
|
|
95
107
|
onGap?: OnGap; // "strict" (default) | "annotate", as in decompile()
|
|
96
108
|
/** Source probe at pattern boundaries: the source asmlift would emit there, which the cli report
|
|
97
109
|
* scores. One call per boundary: pattern N's after-source is pattern N+1's before-source. Absent ⇒
|
|
@@ -189,13 +201,24 @@ function traceTower(
|
|
|
189
201
|
// Merged exactly as pipeline.ts does: the project's own DWARF signatures fill in what the caller
|
|
190
202
|
// did not state. A trace that lifted from a different table would explain a run that never
|
|
191
203
|
// happened.
|
|
192
|
-
const
|
|
204
|
+
const ownContext = splitOwnContext(opts.prototypes, name, opts.ownContextProto);
|
|
205
|
+
const prototypes = prototypesFromSymbols(opts.symbols, ownContext.prototypes ?? {});
|
|
193
206
|
const returnsVoid = declaresVoidReturn(prototypes[name]);
|
|
194
207
|
const trace: StageTrace[] = [];
|
|
195
208
|
const patternEvents: PatternEvent[] = [];
|
|
196
209
|
|
|
197
210
|
// (1) lift → typed-SSA IR
|
|
198
|
-
const fn = liftStamped(
|
|
211
|
+
const fn = liftStamped(
|
|
212
|
+
name,
|
|
213
|
+
asm,
|
|
214
|
+
target,
|
|
215
|
+
prototypes,
|
|
216
|
+
opts.asmData,
|
|
217
|
+
opts.symbols,
|
|
218
|
+
opts.contextGlobals,
|
|
219
|
+
{ calleeBodies: opts.calleeBodies },
|
|
220
|
+
ownContext.ownContextProto,
|
|
221
|
+
);
|
|
199
222
|
trace.push({ id: 'stage:lift', title: 'Lift (ISA frontend → typed-SSA IR)', irDump: irDump(fn), verified: true });
|
|
200
223
|
// The array shapes the assembly evidences, off the LIFTED fn — same reading, same reason, as
|
|
201
224
|
// pipeline.ts's runTower: the fold and the tower below destroy the order the licence reads.
|
|
@@ -220,8 +243,10 @@ function traceTower(
|
|
|
220
243
|
|
|
221
244
|
// The names the map holds for addresses this machine code built by arithmetic off a named one
|
|
222
245
|
// (raise/offsetnames.ts), off the same lifted fn and for the same reason as pipeline.ts's
|
|
223
|
-
// runTower.
|
|
224
|
-
const named = opts.symbols
|
|
246
|
+
// runTower — and its one-spelling-per-cell decline, which throws from here (pipeline.ts's 1.6).
|
|
247
|
+
const named = opts.symbols
|
|
248
|
+
? OFFSET_NAME_PASS.run(fn, opts.symbols, offsetNameBehaviorsFor(target, opts.contextGlobals))
|
|
249
|
+
: [];
|
|
225
250
|
trace.push({
|
|
226
251
|
id: 'stage:offsetnames',
|
|
227
252
|
title: 'Name a walked-to address the symbol map knows',
|
|
@@ -302,10 +327,10 @@ function traceTower(
|
|
|
302
327
|
irDump: irDump(fn),
|
|
303
328
|
verified: true,
|
|
304
329
|
}),
|
|
305
|
-
|
|
330
|
+
afterFold: (fold) =>
|
|
306
331
|
trace.push({
|
|
307
|
-
id:
|
|
308
|
-
title:
|
|
332
|
+
id: `stage:${fold.id}`,
|
|
333
|
+
title: fold.title,
|
|
309
334
|
irDump: irDump(fn),
|
|
310
335
|
verified: true,
|
|
311
336
|
}),
|
|
@@ -316,14 +341,21 @@ function traceTower(
|
|
|
316
341
|
// (4) structure → neutral AST; boundary contract: no unresolved value leaked (strict) or
|
|
317
342
|
// spelled as a loud ASMLIFT_ERROR marker (annotate) — same onGap option as decompile()
|
|
318
343
|
const mapSymbols = opts.symbols ? symbolsByName(opts.symbols) : undefined;
|
|
319
|
-
const sfn =
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
344
|
+
const { sfn } = structureReturnTailReading(
|
|
345
|
+
fn,
|
|
346
|
+
{
|
|
347
|
+
...structureOptionsFor(target, returnsVoid, prototypes),
|
|
348
|
+
spellSwitchFallthrough: (opts.backend ?? cBackend).spellsSwitchFallthrough,
|
|
349
|
+
spellSwitchBreak: (opts.backend ?? cBackend).spellsSwitchBreak,
|
|
350
|
+
spellEarlyReturns: (opts.backend ?? cBackend).spellsEarlyReturn,
|
|
351
|
+
onGap: opts.onGap ?? 'strict',
|
|
352
|
+
...(mapSymbols ? { symbols: mapSymbols } : {}),
|
|
353
|
+
...(opts.contextGlobals ? { contextGlobals: opts.contextGlobals } : {}),
|
|
354
|
+
...(inferredSymbols.size ? { inferredSymbols } : {}),
|
|
355
|
+
...(orderLicensed.size ? { orderLicensedGlobals: orderLicensed } : {}),
|
|
356
|
+
},
|
|
357
|
+
(t) => backend.emit(t),
|
|
358
|
+
);
|
|
327
359
|
trace.push({
|
|
328
360
|
id: 'stage:structure',
|
|
329
361
|
title: 'Structuring (IR → neutral AST)',
|
|
@@ -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',
|
|
@@ -264,6 +264,7 @@ export interface VariationDefinition {
|
|
|
264
264
|
const CALLS =
|
|
265
265
|
'void A(); void B(); void C(); void D(); void X(); void Y(); void P(); void Q(); s32 f(); s32 g(); s32 h(); void use();\n';
|
|
266
266
|
|
|
267
|
+
const PIPELINE = 'packages/core/src/pipeline.ts';
|
|
267
268
|
const RANK = 'packages/core/src/rank.ts';
|
|
268
269
|
const RANK_VARIATIONS = 'packages/core/src/rank-variations.ts';
|
|
269
270
|
const SYMBOLS = 'packages/core/src/symbols.ts';
|
|
@@ -296,7 +297,7 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
296
297
|
after: 's32 example(u32 a0, u32 a1)',
|
|
297
298
|
},
|
|
298
299
|
implementedIn: RANK,
|
|
299
|
-
seeAlso: ['signed', '
|
|
300
|
+
seeAlso: ['signed', 'signed-cmp'],
|
|
300
301
|
},
|
|
301
302
|
signed: {
|
|
302
303
|
title: 'Signed parameters',
|
|
@@ -371,14 +372,43 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
371
372
|
implementedIn: 'packages/core/src/raise/shortcircuit.ts',
|
|
372
373
|
seeAlso: ['site-sense'],
|
|
373
374
|
},
|
|
375
|
+
'narrow-param': {
|
|
376
|
+
title: 'Parameter declared at the width every use extends it to',
|
|
377
|
+
summary: 'a parameter read only through narrowing casts is declared narrow, though the prologue does not widen it',
|
|
378
|
+
detail:
|
|
379
|
+
'A parameter the function only ever reads through narrowing casts can be declared at 32 bits and cast ' +
|
|
380
|
+
'at each use, or declared at the widest cast and read bare. Every use is the same value either way, ' +
|
|
381
|
+
'and where the compiler widens it at each use rather than on entry, the assembly does not say which ' +
|
|
382
|
+
'one the source declared. This lift declares it narrow, and loses a tie when no prototype declares the width.',
|
|
383
|
+
compilerBehavior:
|
|
384
|
+
'agbcc widens an `s16` parameter at each use when every use extends it, and then the two declarations ' +
|
|
385
|
+
'differ only in how the registers around them are allocated: a sibling `u8` parameter is copied ' +
|
|
386
|
+
'before it is widened under the narrow one.',
|
|
387
|
+
offeredWhen: {
|
|
388
|
+
when: 'Some parameter is read only through 8- or 16-bit casts or masks, and the default lift left it 32 bits wide.',
|
|
389
|
+
decidedBy: { symbol: 'runPreRecovery', file: 'packages/core/src/raise/pre-recovery.ts' },
|
|
390
|
+
gates: ['PARAM_READER_WIDTH_GATES'],
|
|
391
|
+
},
|
|
392
|
+
example: {
|
|
393
|
+
compiler: 'agbcc',
|
|
394
|
+
unit: 'extern u8 *gM; void f3(u8 *, u16, u8); void f2(u8 *, u8);\n@',
|
|
395
|
+
before: 'void example(s32 a, u8 b) { u8 *p = gM; f3(p + (s16)a * 4, (u16)a, b); f2(p + b * 8, b); }',
|
|
396
|
+
after: 'void example(s16 a, u8 b) { u8 *p = gM; f3(p + a * 4, (u16)a, b); f2(p + b * 8, b); }',
|
|
397
|
+
},
|
|
398
|
+
implementedIn: 'packages/core/src/raise/paramwidth.ts',
|
|
399
|
+
},
|
|
374
400
|
'shared-ret': {
|
|
375
401
|
title: 'Shared code after an early return',
|
|
376
402
|
summary: 'code both arms of an `if` reach before returning is written once, after the `if`',
|
|
377
403
|
detail:
|
|
378
|
-
'An `if` whose arms never meet again before the function returns has no follow, so
|
|
379
|
-
'region both arms reach is written in each of them. This structures
|
|
380
|
-
"with that shared region as the `if`'s follow: it is
|
|
381
|
-
'path leaves through an early `return`.'
|
|
404
|
+
'An `if` whose arms never meet again before the function returns has no follow, so under the ' +
|
|
405
|
+
'return-tail copies (`ret-copy`) a region both arms reach is written in each of them. This structures ' +
|
|
406
|
+
"the same raised function again with the copies and that shared region as the `if`'s follow: it is " +
|
|
407
|
+
'written once after the `if`, and every other path leaves through an early `return`. On agbcc, ' +
|
|
408
|
+
"this example's compiler, the copies are the default, so this is the step from them to the region " +
|
|
409
|
+
'written once. Where the join past early returns is the default instead (CodeWarrior ' +
|
|
410
|
+
"2.4.2 and 2.3.3), it already writes this example's region once, since the paths that do not " +
|
|
411
|
+
'return meet at it: the variation is a reading of the copies, ranked beside them.',
|
|
382
412
|
offeredWhen: {
|
|
383
413
|
when: 'Some `if` has arms that reach a common `return` block and no common block before it.',
|
|
384
414
|
decidedBy: { symbol: 'hasDivergentSharedRet', file: STRUCTURE },
|
|
@@ -390,7 +420,68 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
390
420
|
after: 'if (c) { A(); } else { if (x) { C(); return; } } B();',
|
|
391
421
|
},
|
|
392
422
|
implementedIn: STRUCTURE,
|
|
393
|
-
seeAlso: ['shared-tail'],
|
|
423
|
+
seeAlso: ['shared-tail', 'ret-copy'],
|
|
424
|
+
},
|
|
425
|
+
'ret-copy': {
|
|
426
|
+
title: 'Return tails copied',
|
|
427
|
+
summary: 'code below an `if` whose arms only meet at the `return` is written into each arm',
|
|
428
|
+
detail:
|
|
429
|
+
'An `if` with an early `return` in one arm has no block both arms reach before the function ' +
|
|
430
|
+
'returns. Where the join past early returns is the default (CodeWarrior 2.3.3, and 2.4.2, ' +
|
|
431
|
+
"this example's compiler), asmlift gives it the block where the paths that do not return meet as its " +
|
|
432
|
+
'join, so the code from there is written once and every other path leaves through its early ' +
|
|
433
|
+
'`return`; that also reads early returns the compiler merged into the epilogue or cross-jumped ' +
|
|
434
|
+
'into one return tail, and a `switch` with a `case` that returns early. This structures the same ' +
|
|
435
|
+
'raised function again with no such join: everything below the `if` is written into each arm, ' +
|
|
436
|
+
'once more per such `if` of a chain, which is the source some functions were written as. Where ' +
|
|
437
|
+
'the copies spell the whole function in fewer lines than the join, where the compiler is one that ' +
|
|
438
|
+
'cross-jumps return tails and the layout shows it did, or where an early return keeps a constant ' +
|
|
439
|
+
'its branch proved unfolded (gcc 2.x below -O2), they are the default instead, and the join is ' +
|
|
440
|
+
'offered as `ret-join`. On agbcc and IDO 7.1, measured against held-out functions where the join ' +
|
|
441
|
+
'loses matches, on GCC 2.7.2 and KMC, measured against generated functions where it does, and on CodeWarrior 2.4.7, where no held-out function was measured, the copies are ' +
|
|
442
|
+
'the default outright, and this variation is never offered there. Past the copy budget the copies ' +
|
|
443
|
+
'are not offered.',
|
|
444
|
+
offeredWhen: {
|
|
445
|
+
when: 'Some `if` or `switch` outside every loop took a join past early returns that the default kept.',
|
|
446
|
+
decidedBy: { symbol: 'structureReturnTailReading', file: PIPELINE },
|
|
447
|
+
},
|
|
448
|
+
example: {
|
|
449
|
+
compiler: 'mwcc',
|
|
450
|
+
unit: CALLS + 'extern s32 gT; void example(s32 a, s32 b) { @ }',
|
|
451
|
+
before: 'if (a > 3) { if (b == 1) { A(); gT = 0; return; } B(); } else { C(); } X(); Y();',
|
|
452
|
+
after: 'if (a > 3) { if (b == 1) { A(); gT = 0; return; } B(); X(); Y(); } else { C(); X(); Y(); }',
|
|
453
|
+
note: 'the shape of `synthetic:retchain_one`',
|
|
454
|
+
},
|
|
455
|
+
implementedIn: STRUCTURE,
|
|
456
|
+
seeAlso: ['shared-ret', 'ret-join'],
|
|
457
|
+
},
|
|
458
|
+
'ret-join': {
|
|
459
|
+
title: 'Join past early returns, where the copies are the default',
|
|
460
|
+
summary: 'the join past early returns, where it writes a return tail into early returns',
|
|
461
|
+
detail:
|
|
462
|
+
'The join past early returns writes the code below a divergent `if` once, but where the paths that ' +
|
|
463
|
+
'do not return meet at a block only some of them reach, every other path becomes an early `return` ' +
|
|
464
|
+
'that takes its own copy of the tail below. Where that spells the whole function in more lines than ' +
|
|
465
|
+
'the return-tail copies, or where the layout shows the compiler cross-jumped a return tail the ' +
|
|
466
|
+
'source wrote into every arm, or where an early return keeps a constant its branch proved unfolded ' +
|
|
467
|
+
'(gcc 2.x below -O2), the copies are the default and this is the join, offered beside them, which is ' +
|
|
468
|
+
"the source some functions were written as. On agbcc, this example's compiler, and on IDO 7.1, GCC 2.7.2, KMC and " +
|
|
469
|
+
'CodeWarrior 2.4.7, the copies are the default outright: the join is this variation ' +
|
|
470
|
+
'wherever it is taken.',
|
|
471
|
+
offeredWhen: {
|
|
472
|
+
when: 'Some `if` or `switch` outside every loop took a join past early returns that the default gave up for the copies.',
|
|
473
|
+
decidedBy: { symbol: 'structureReturnTailReading', file: PIPELINE },
|
|
474
|
+
},
|
|
475
|
+
example: {
|
|
476
|
+
compiler: 'agbcc',
|
|
477
|
+
unit: CALLS + 'void example(s32 a) { @ }',
|
|
478
|
+
before: 'if (a == 1) { A(); B(); } else if (a == 2 || a == 3) { A(); } else { A(); B(); } X(); Y(); C();',
|
|
479
|
+
after:
|
|
480
|
+
'if (a == 1) { A(); } else { if (a == 2 || a == 3) { A(); X(); Y(); C(); return; } A(); } B(); X(); Y(); C();',
|
|
481
|
+
note: 'the shape of sa3 `sub_809A808`',
|
|
482
|
+
},
|
|
483
|
+
implementedIn: PIPELINE,
|
|
484
|
+
seeAlso: ['ret-copy'],
|
|
394
485
|
},
|
|
395
486
|
'shared-tail': {
|
|
396
487
|
title: 'Store tail copied back',
|
|
@@ -398,7 +489,7 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
398
489
|
detail:
|
|
399
490
|
"agbcc's global common-subexpression pass can move a trailing store into every predecessor of a join, " +
|
|
400
491
|
'and the paths then jump into one `store; return` tail. This lift copies that tail back into each ' +
|
|
401
|
-
'path that branches to it, and then structures with the shared follow `shared-ret` uses, so the ' +
|
|
492
|
+
'path that branches to it, and then structures with the copies and the shared follow `shared-ret` uses, so the ' +
|
|
402
493
|
'store the source wrote once is written after the `if` and the others before an early `return`. It is ' +
|
|
403
494
|
'enumerated apart from `shared-ret` because the sink can delete the shared `return` that variation needs.',
|
|
404
495
|
compilerBehavior:
|
|
@@ -420,6 +511,63 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
420
511
|
implementedIn: 'packages/core/src/raise/tailsink.ts',
|
|
421
512
|
seeAlso: ['shared-ret'],
|
|
422
513
|
},
|
|
514
|
+
'kept-slot': {
|
|
515
|
+
title: 'Each arm keeps its copy of a shared tail',
|
|
516
|
+
summary: "a MIPS jump's delay slot that ends a tail both arms spell stays in its own arm",
|
|
517
|
+
detail:
|
|
518
|
+
'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 ' +
|
|
519
|
+
'target, so by default the jump is read as landing one word earlier and the two arms meet there. ' +
|
|
520
|
+
'Where the word before the jump also matches the word before that copy, the two arms may instead ' +
|
|
521
|
+
'have been written out twice, each ending in the same instructions. This lift keeps the slot in its ' +
|
|
522
|
+
'own arm, so each arm keeps the whole tail it spells.',
|
|
523
|
+
compilerBehavior:
|
|
524
|
+
'The two readings are one object: the slot runs before the target on its one edge either way. ' +
|
|
525
|
+
'Which arms the source wrote out is something only the differ can tell. The question arises on IDO ' +
|
|
526
|
+
'alone: GCC 2.7.2 and KMC cross-jump a tail both arms spell into one copy at the join before they ' +
|
|
527
|
+
'fill any slot, so their output has no tail written twice (`mergenarrow` builds with no `b` on either).',
|
|
528
|
+
offeredWhen: {
|
|
529
|
+
when: 'The MIPS lift reported a `b`/`j` whose slot ends a common tail of two or more words, and retargeted it.',
|
|
530
|
+
decidedBy: { symbol: 'enumerateCandidates', file: RANK },
|
|
531
|
+
},
|
|
532
|
+
example: {
|
|
533
|
+
compiler: 'ido',
|
|
534
|
+
unit: 'void example(s32 *out, s32 a, s32 b, s32 c) { @ }',
|
|
535
|
+
before: 's32 v; if (c) { v = (a + b) << 16; } else { v = (a - b) << 16; } out[0] = v >> 16;',
|
|
536
|
+
after: 's16 v; if (c) { v = a + b; } else { v = a - b; } out[0] = v;',
|
|
537
|
+
note: 'the shape of `synthetic:mergenarrow`',
|
|
538
|
+
},
|
|
539
|
+
implementedIn: 'packages/core/src/frontend/mips.ts',
|
|
540
|
+
},
|
|
541
|
+
'far-cast': {
|
|
542
|
+
title: 'Store past the displacement range through a cast',
|
|
543
|
+
summary: 'a store to a member past the instruction offset range is spelled through a cast of the base',
|
|
544
|
+
detail:
|
|
545
|
+
'A member past the offset an instruction can carry is reached through an address the machine adds up ' +
|
|
546
|
+
'first, and by default that access joins its struct and is spelled as the member. A store there could ' +
|
|
547
|
+
'equally have been written through a cast of the base, `*((u8 *)p + 38) = v`, or through a pointer the ' +
|
|
548
|
+
'source held, and on some compilers that is a different object. This lift keeps every such store on ' +
|
|
549
|
+
'the address it was reached through; loads still join the struct.',
|
|
550
|
+
compilerBehavior:
|
|
551
|
+
'agbcc evaluates the right-hand side of a member store before it adds up the address, and the address ' +
|
|
552
|
+
'of a cast store before the right-hand side, so the two spellings allocate and order registers ' +
|
|
553
|
+
"differently; a hoisted `u8 *d = &p->f38; *d = v;` builds the cast spelling's object. Elsewhere the " +
|
|
554
|
+
'member and the cast compile alike one access at a time, but a folded store no longer aliases the ' +
|
|
555
|
+
'loads beside it, so the lift moves those loads past it, and a pointer the source held is spelled as ' +
|
|
556
|
+
'members: KMC and GCC 2.7.2 build two loads written before a store and the same loads written after ' +
|
|
557
|
+
'it into different objects, and two of the three mwcc builds allocate a held pointer and the member ' +
|
|
558
|
+
'spelling different registers.',
|
|
559
|
+
offeredWhen: {
|
|
560
|
+
when: 'The default lift folded a store through a materialised `base + K` address into its struct.',
|
|
561
|
+
decidedBy: { symbol: 'enumerateCandidates', file: RANK },
|
|
562
|
+
},
|
|
563
|
+
example: {
|
|
564
|
+
compiler: 'agbcc',
|
|
565
|
+
unit: 'struct S { s32 a; u8 pad[34]; u8 f38; };\nvoid example(struct S *p, u8 *q) { @ }',
|
|
566
|
+
before: 'p->f38 = *q; p->a = 0;',
|
|
567
|
+
after: '*((u8 *)p + 38) = *q; p->a = 0;',
|
|
568
|
+
},
|
|
569
|
+
implementedIn: 'packages/core/src/raise/structs.ts',
|
|
570
|
+
},
|
|
423
571
|
|
|
424
572
|
// ── structure ───────────────────────────────────────────────────────────────────────────────
|
|
425
573
|
'flip-branch': {
|
|
@@ -772,14 +920,16 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
772
920
|
implementedIn: ANALYSIS,
|
|
773
921
|
seeAlso: ['expr-home', 'derived-home'],
|
|
774
922
|
},
|
|
775
|
-
'
|
|
776
|
-
title: 'Unsigned compare spelled
|
|
777
|
-
summary: 'an unsigned compare
|
|
923
|
+
'signed-cmp': {
|
|
924
|
+
title: 'Unsigned compare spelled signed',
|
|
925
|
+
summary: 'an unsigned compare drops the `(u32)` cast the default gives an operand that does not already say so',
|
|
778
926
|
detail:
|
|
779
|
-
'
|
|
780
|
-
'unsigned, and a declaration claimed by both kinds of use becomes `u32` when nothing under it
|
|
781
|
-
'signed.
|
|
782
|
-
'
|
|
927
|
+
'By default an unsigned comparison takes a `(u32)` cast on an operand the rendering does not already ' +
|
|
928
|
+
'make unsigned, and a declaration claimed by both kinds of use becomes `u32` when nothing under it ' +
|
|
929
|
+
'needs signed. This writes the comparison without them, as a signed one. The source may have done so: ' +
|
|
930
|
+
'a compiler emits an unsigned compare for a signed one where it proved both sides non-negative, and ' +
|
|
931
|
+
'asmlift can prove less than the compiler can. Where the sides can be negative it computes something ' +
|
|
932
|
+
'else, so it is published only when the candidate matches byte for byte; otherwise it is withheld.',
|
|
783
933
|
compilerBehavior:
|
|
784
934
|
'A compiler emits an unsigned branch from a signed compare only where it proved both sides non-negative.',
|
|
785
935
|
offeredWhen: {
|
|
@@ -789,8 +939,8 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
789
939
|
example: {
|
|
790
940
|
compiler: 'agbcc',
|
|
791
941
|
unit: CALLS + 'void example(s32 a, s32 b) { @ A(); }',
|
|
792
|
-
before: 'if (a < b)',
|
|
793
|
-
after: 'if (
|
|
942
|
+
before: 'if ((u32)a < b)',
|
|
943
|
+
after: 'if (a < b)',
|
|
794
944
|
},
|
|
795
945
|
implementedIn: STRUCTURE,
|
|
796
946
|
seeAlso: ['unsigned', 'signed'],
|
|
@@ -835,8 +985,12 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
835
985
|
},
|
|
836
986
|
example: {
|
|
837
987
|
compiler: 'agbcc',
|
|
988
|
+
// The callees are PROTOTYPED here, unlike `CALLS`: `bl f` sees `c` read in r2 and a0 untouched
|
|
989
|
+
// in r0, which a pass-through `f(a0, a1, c)` would leave the same way, so an undeclared `f`
|
|
990
|
+
// declines (frontend/ssa.ts `fallbackArgc` refuses the gap). The lift reads these declarations
|
|
991
|
+
// off the unit as shown.
|
|
838
992
|
unit:
|
|
839
|
-
|
|
993
|
+
's32 f(void); s32 g(void); s32 h(s32, s32);\n' +
|
|
840
994
|
'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
995
|
before: 'v2 = a1; v1 = a0;',
|
|
842
996
|
after: 'v1 = a0; v2 = a1;',
|
|
@@ -869,6 +1023,107 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
869
1023
|
implementedIn: STRUCTURE,
|
|
870
1024
|
seeAlso: ['flip-join', 'flip-branch', 'sense', 'connective'],
|
|
871
1025
|
},
|
|
1026
|
+
'tail-copies': {
|
|
1027
|
+
title: 'Shared switch tail copied into every arm',
|
|
1028
|
+
summary: 'every `case` that runs into the tail a cross-jump kept gets its own copy of it',
|
|
1029
|
+
detail:
|
|
1030
|
+
'When several `case` arms end in the same code, the compiler keeps one copy and the other arms jump ' +
|
|
1031
|
+
"into it, so they all enter one arm's body. By default the arm placed directly above that body keeps " +
|
|
1032
|
+
'falling into it and the others get a copy. This gives every one of them its own copy.',
|
|
1033
|
+
compilerBehavior:
|
|
1034
|
+
'Which arm the source wrote the shared code under is not in the assembly. On KMC and gcc 2.7.2, an ' +
|
|
1035
|
+
'arm written above its target in case order wants the fall-through, and arms written out of case ' +
|
|
1036
|
+
'order want the copies.',
|
|
1037
|
+
offeredWhen: {
|
|
1038
|
+
when: "Structuring this lift without it left a jump-table arm falling into a sibling arm's body that another arm also enters.",
|
|
1039
|
+
decidedBy: { symbol: 'STRUCTURE_VARIATIONS', file: RANK_VARIATIONS },
|
|
1040
|
+
},
|
|
1041
|
+
example: {
|
|
1042
|
+
compiler: 'gcc',
|
|
1043
|
+
unit:
|
|
1044
|
+
'extern s32 gOut; void f(s32); void g(s32); void h(s32); void m(s32); void r(s32);\n' +
|
|
1045
|
+
'void example(s32 id, s32 v) { switch (id) { @ } r(0); }',
|
|
1046
|
+
before:
|
|
1047
|
+
'case 0: f(1); break; case 1: g(v); case 2: gOut = v; break; case 3: h(v); gOut = v; break; case 4: m(5); break;',
|
|
1048
|
+
after:
|
|
1049
|
+
'case 0: f(1); break; case 1: g(v); gOut = v; break; case 2: gOut = v; break; case 3: h(v); gOut = v; break; case 4: m(5); break;',
|
|
1050
|
+
note: 'cases 1 and 3 both enter case 2',
|
|
1051
|
+
},
|
|
1052
|
+
implementedIn: STRUCTURE,
|
|
1053
|
+
seeAlso: ['unmerge', 'shared-tail'],
|
|
1054
|
+
},
|
|
1055
|
+
'for-continue': {
|
|
1056
|
+
title: 'Counted loop spelled as a `for`',
|
|
1057
|
+
summary: 'a `while (1)` whose last block steps and tests a counter is written `for (init; test; step)`',
|
|
1058
|
+
detail:
|
|
1059
|
+
'A loop whose last block adds to a counter and tests it is written by default as `while (1)` with ' +
|
|
1060
|
+
'`i++; if (!(test)) break;` at the foot of its body, and a jump-table arm inside it that leaves the ' +
|
|
1061
|
+
'loop copies the code after the loop and returns. This writes it as `for (i = init; test; i++)` instead, ' +
|
|
1062
|
+
'so an arm that reaches the step is `continue;`, an arm that leaves the loop ends the switch, and a ' +
|
|
1063
|
+
'`break;` after the switch leaves the loop. Only a loop whose entry test is the same test (or folds to ' +
|
|
1064
|
+
'true) and whose last block does nothing but step one counter and test it is written this way.',
|
|
1065
|
+
compilerBehavior:
|
|
1066
|
+
'agbcc and gcc 2.7.2 lay a `for` out as a guard, the body and a latch that steps and tests, which is ' +
|
|
1067
|
+
'also how they lay out the `while (1)` spelling of the same loop. Which one the source wrote decides ' +
|
|
1068
|
+
'how the arms that leave the loop are laid out, and that is not in the assembly before it is compiled.',
|
|
1069
|
+
offeredWhen: {
|
|
1070
|
+
when: 'Structuring this lift without it met a `while (1)` loop whose last block steps one counter and tests it.',
|
|
1071
|
+
decidedBy: { symbol: 'STRUCTURE_VARIATIONS', file: RANK_VARIATIONS },
|
|
1072
|
+
},
|
|
1073
|
+
example: {
|
|
1074
|
+
compiler: 'agbcc',
|
|
1075
|
+
unit:
|
|
1076
|
+
'struct E { s32 x; u8 id; u8 u; }; extern struct E gE[]; void k(s32);\n' +
|
|
1077
|
+
'void example(s32 lo, s32 hi) { s32 i; @ k(99); }',
|
|
1078
|
+
before:
|
|
1079
|
+
'if (lo <= hi) { i = lo; while (1) { switch (gE[i].id) { case 1: if (gE[i].u != 1) { k(4); k(99); return; } break; ' +
|
|
1080
|
+
'case 2: if (gE[i].x <= 20) { k(5); k(99); return; } break; case 3: if (gE[i].x >= 0) { k(6); k(99); return; } break; ' +
|
|
1081
|
+
'case 4: if (gE[i].x > 2) { k(7); k(99); return; } break; case 5: if (gE[i].x != 9) { k(8); k(99); return; } break; } ' +
|
|
1082
|
+
'i++; if (i > hi) break; } }',
|
|
1083
|
+
after:
|
|
1084
|
+
'for (i = lo; i <= hi; i++) { switch (gE[i].id) { case 1: if (gE[i].u == 1) continue; k(4); break; ' +
|
|
1085
|
+
'case 2: if (gE[i].x > 20) continue; k(5); break; case 3: if (gE[i].x < 0) continue; k(6); break; ' +
|
|
1086
|
+
'case 4: if (gE[i].x <= 2) continue; k(7); break; case 5: if (gE[i].x == 9) continue; k(8); break; ' +
|
|
1087
|
+
'default: continue; } break; }',
|
|
1088
|
+
note: 'every arm either goes round the loop or calls `k` and leaves it; five arms, so agbcc dispatches through a jump table',
|
|
1089
|
+
},
|
|
1090
|
+
implementedIn: STRUCTURE,
|
|
1091
|
+
seeAlso: ['tail-copies'],
|
|
1092
|
+
},
|
|
1093
|
+
'case-goto': {
|
|
1094
|
+
title: 'Shared case body entered by `goto`',
|
|
1095
|
+
summary:
|
|
1096
|
+
"every `case` that runs into another case's body the default copies it into ends `goto` to that case instead",
|
|
1097
|
+
detail:
|
|
1098
|
+
"When several `case` arms run on into one other arm's body, by default the arm placed directly above " +
|
|
1099
|
+
'that body keeps falling into it and the others get a copy of it. This leaves the copies out: those ' +
|
|
1100
|
+
'arms end in `goto` to a label on that arm, after its `case` labels. It is one of the two kinds of `goto` asmlift writes.',
|
|
1101
|
+
compilerBehavior:
|
|
1102
|
+
'A compiler that cross-jumps keeps one copy of code that several arms end in, so a copy and a `goto` can ' +
|
|
1103
|
+
'build the same object. agbcc stops that merge short where an arm already formed an address the copy ' +
|
|
1104
|
+
'forms again, and the copy then compiles to more code than the `goto`.',
|
|
1105
|
+
offeredWhen: {
|
|
1106
|
+
when: "Structuring this lift without it copied a case arm's body into another jump-table arm that runs on into it.",
|
|
1107
|
+
decidedBy: { symbol: 'STRUCTURE_VARIATIONS', file: RANK_VARIATIONS },
|
|
1108
|
+
},
|
|
1109
|
+
example: {
|
|
1110
|
+
compiler: 'agbcc',
|
|
1111
|
+
unit:
|
|
1112
|
+
'struct E { s32 x; u8 id; u8 f; u8 u; u8 v; }; extern struct E gE[]; extern s32 gOut; void k(s32);\n' +
|
|
1113
|
+
'void example(s32 lo, s32 hi) { s32 i; for (i = lo; i <= hi; i++) { switch (gE[i].id) { @ default: continue; } break; } }',
|
|
1114
|
+
before:
|
|
1115
|
+
'case 1: if (gE[i].u == 1) continue; if (gE[i].x > 20) continue; gOut = 1; k(i); break; ' +
|
|
1116
|
+
'case 2: if (gE[i + 1].v != 0) continue; case 3: case 4: if (gE[i].x > 20) continue; gOut = 1; k(i); break; ' +
|
|
1117
|
+
'case 7: k(7); break;',
|
|
1118
|
+
after:
|
|
1119
|
+
'case 1: if (gE[i].u == 1) continue; goto case_3; ' +
|
|
1120
|
+
'case 2: if (gE[i + 1].v != 0) continue; case 3: case 4: case_3: if (gE[i].x > 20) continue; gOut = 1; k(i); ' +
|
|
1121
|
+
'break; case 7: k(7); break;',
|
|
1122
|
+
note: 'cases 1 and 2 both run on into the body of cases 3 and 4',
|
|
1123
|
+
},
|
|
1124
|
+
implementedIn: STRUCTURE,
|
|
1125
|
+
seeAlso: ['tail-copies'],
|
|
1126
|
+
},
|
|
872
1127
|
|
|
873
1128
|
// ── respell ─────────────────────────────────────────────────────────────────────────────────
|
|
874
1129
|
unmerge: {
|
|
@@ -902,6 +1157,40 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
902
1157
|
implementedIn: l3('unmerge'),
|
|
903
1158
|
seeAlso: ['initfirst', 'regionbase'],
|
|
904
1159
|
},
|
|
1160
|
+
'case-end-goto': {
|
|
1161
|
+
title: 'Search loop leaving its case by `goto`',
|
|
1162
|
+
summary: "a search loop's hit that skips the rest of its `case` jumps to a label on the case's closing `break;`",
|
|
1163
|
+
detail:
|
|
1164
|
+
'When a loop inside a `case` has a second way out that skips the code after the loop and ends the ' +
|
|
1165
|
+
'`case`, the default sets a flag before it leaves the loop and runs that code under `if (flag == 0)` ' +
|
|
1166
|
+
'after it. This leaves the flag out: the second way out is `goto` to a label on the `break;` that ' +
|
|
1167
|
+
'ends the case, and the code after the loop runs unguarded before that label. It is one of the two ' +
|
|
1168
|
+
'kinds of `goto` asmlift writes.',
|
|
1169
|
+
compilerBehavior:
|
|
1170
|
+
'agbcc sends a `goto` to a `break;` straight to the end of the `switch`, and past a loop the switch ' +
|
|
1171
|
+
'ends, so only the `goto` builds the bytes the source did; the flag adds a variable and its tests.',
|
|
1172
|
+
offeredWhen: {
|
|
1173
|
+
judges: "each flag set at a loop's own `break` and tested right after that loop",
|
|
1174
|
+
gates: ['CASE_END_GOTO_GATES'],
|
|
1175
|
+
},
|
|
1176
|
+
example: {
|
|
1177
|
+
compiler: 'agbcc',
|
|
1178
|
+
unit:
|
|
1179
|
+
'struct E { s32 x; u8 id; u8 f; u8 u; u8 v; }; extern struct E gE[]; extern s32 gLo, gHi; void k(s32);\n' +
|
|
1180
|
+
'void example(s32 lo, s32 hi) { s32 i, j, h, a = 0; for (i = lo; i <= hi; i++) { if (gE[i].f <= 26) { ' +
|
|
1181
|
+
'switch (gE[i].id) { @ default: continue; } break; } } k(a); }',
|
|
1182
|
+
before:
|
|
1183
|
+
'case 1: case 2: if (gE[i].x > 5) continue; h = 0; for (j = gLo; j <= gHi; j++) { if (i != j && gE[j].f == 28) ' +
|
|
1184
|
+
'{ a = 1; k(j); h = 1; break; } } if (h == 0) { gE[i].f = 19; k(i); } break; case 4: k(i + 4); break;',
|
|
1185
|
+
after:
|
|
1186
|
+
'case 1: case 2: if (gE[i].x > 5) continue; for (j = gLo; j <= gHi; j++) { if (i != j && gE[j].f == 28) ' +
|
|
1187
|
+
'{ a = 1; k(j); goto hit; } } gE[i].f = 19; k(i); hit: break; case 4: k(i + 4); break;',
|
|
1188
|
+
note: 'the search loop in cases 1 and 2 either finds a hit, which skips the rest of the case, or runs out into it',
|
|
1189
|
+
},
|
|
1190
|
+
implementedIn: l3('caseendgoto'),
|
|
1191
|
+
seeAlso: ['case-goto'],
|
|
1192
|
+
},
|
|
1193
|
+
|
|
905
1194
|
argbase: {
|
|
906
1195
|
title: 'Argument bases named before the call',
|
|
907
1196
|
summary: "the fixed addresses a call's arguments read are loaded into pointer locals first",
|
|
@@ -1630,7 +1919,31 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
1630
1919
|
after: 'u8 v; v = gA[12] - 1; gB[14] = (v & 1) + f() % (5 - v) + 1;',
|
|
1631
1920
|
},
|
|
1632
1921
|
implementedIn: l3('narrowdecl'),
|
|
1633
|
-
seeAlso: ['narrow-read', 'derived-home', 'escape-home'],
|
|
1922
|
+
seeAlso: ['narrow-load', 'narrow-read', 'derived-home', 'escape-home'],
|
|
1923
|
+
},
|
|
1924
|
+
'narrow-load': {
|
|
1925
|
+
title: 'Narrow load declared at its width',
|
|
1926
|
+
summary: 'a local holding one narrow memory read is declared at that width instead of as an `s32`',
|
|
1927
|
+
detail:
|
|
1928
|
+
'A byte or halfword read from memory into a local can be spelled two ways: an `s32` local assigned the ' +
|
|
1929
|
+
"read, or a local declared at the read's width. Every read of the local is the same value either way; " +
|
|
1930
|
+
"which one the source declared is not in the assembly. Applied on top of every other candidate's source.",
|
|
1931
|
+
compilerBehavior:
|
|
1932
|
+
'agbcc loads a narrow local where it is written and an `s32` one where it is first read, so two byte ' +
|
|
1933
|
+
'reads held across two calls are loaded at different points.',
|
|
1934
|
+
offeredWhen: {
|
|
1935
|
+
when: 'An `s32` local written once, outside a `for` init, by an element or member read of a narrower integer, in no memory.',
|
|
1936
|
+
decidedBy: { symbol: 'narrowLoadDeclarations', file: l3('narrowdecl') },
|
|
1937
|
+
},
|
|
1938
|
+
example: {
|
|
1939
|
+
compiler: 'agbcc',
|
|
1940
|
+
unit: 'void f7(u8 *, u8 *, u8, u8, s32, u8, u8);\nvoid example(u8 *p, s32 x) { @ }',
|
|
1941
|
+
before:
|
|
1942
|
+
'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);',
|
|
1943
|
+
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);',
|
|
1944
|
+
},
|
|
1945
|
+
implementedIn: l3('narrowdecl'),
|
|
1946
|
+
seeAlso: ['narrow-decl', 'narrow-read'],
|
|
1634
1947
|
},
|
|
1635
1948
|
'narrow-read': {
|
|
1636
1949
|
title: 'Narrow value declared at its width, read bare',
|
|
@@ -1653,7 +1966,7 @@ export const VARIATION_DEFINITIONS: { readonly [N in VariationName]: VariationDe
|
|
|
1653
1966
|
after: 'u8 y; u8 m; y = cv(p[0]); m = cv(p[1]); return dc(y, m, (u8)cv(p[2]));',
|
|
1654
1967
|
},
|
|
1655
1968
|
implementedIn: l3('narrowdecl'),
|
|
1656
|
-
seeAlso: ['narrow-decl'],
|
|
1969
|
+
seeAlso: ['narrow-decl', 'narrow-load'],
|
|
1657
1970
|
},
|
|
1658
1971
|
'int-cell': {
|
|
1659
1972
|
title: 'Global stored a pointer, spelled as an integer',
|
package/src/variation-gates.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { ADVANCE_HEAD_GATES, ADVANCE_MEMBER_GATES } from './l3/advance';
|
|
12
12
|
import { ARGCOPY_GATES, ARGCOPY_REGION_GATES } from './l3/argcopy';
|
|
13
13
|
import { BASEFOLD_GATES, LIVEBASE_BLOCK_GATES, LIVEBASE_GATES, ORDERBASE_GATES, UNFOLDED_GATES } from './l3/basecse';
|
|
14
|
+
import { CASE_END_GOTO_GATES } from './l3/caseendgoto';
|
|
14
15
|
import { ARM_DISJOINT_GATES, COALESCE_GATES } from './l3/coalesce';
|
|
15
16
|
import type { Gate } from './l3/gates';
|
|
16
17
|
import { HOMESPLIT_FAN_GATES, HOMESPLIT_GATES } from './l3/homesplit';
|
|
@@ -29,6 +30,7 @@ import {
|
|
|
29
30
|
import { UNREDUCE_GATES } from './l3/unreduce';
|
|
30
31
|
import { VOL_SLOT_GATES } from './l3/volatileval';
|
|
31
32
|
import { VOL_STORE_GATES } from './l3/volstore';
|
|
33
|
+
import { PARAM_READER_WIDTH_GATES } from './raise/paramwidth';
|
|
32
34
|
import { ARM_REREAD_GATES } from './raise/shortcircuit';
|
|
33
35
|
import { NAME_COALESCE_GATES } from './structure/namecoalesce';
|
|
34
36
|
import { FRESH_MERGE_GATES } from './structure/structure';
|
|
@@ -48,6 +50,7 @@ export const VARIATION_GATE_TABLES = {
|
|
|
48
50
|
ARM_DISJOINT_GATES,
|
|
49
51
|
ARM_REREAD_GATES,
|
|
50
52
|
BASEFOLD_GATES,
|
|
53
|
+
CASE_END_GOTO_GATES,
|
|
51
54
|
COALESCE_GATES,
|
|
52
55
|
COUNTDOWN_GATES,
|
|
53
56
|
FRESH_MERGE_GATES,
|
|
@@ -59,6 +62,7 @@ export const VARIATION_GATE_TABLES = {
|
|
|
59
62
|
NAME_COALESCE_GATES,
|
|
60
63
|
OFFMEMBER_GATES,
|
|
61
64
|
ORDERBASE_GATES,
|
|
65
|
+
PARAM_READER_WIDTH_GATES,
|
|
62
66
|
PTR_FIELD_GATES,
|
|
63
67
|
REGIONBASE_GATES,
|
|
64
68
|
SCOPEBASE_ELIGIBILITY,
|