@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
|
@@ -29,20 +29,38 @@
|
|
|
29
29
|
// source's (`tide`) in one step, `nameLocalStatics`, which is where a clash between that name and
|
|
30
30
|
// anything else the function names can first be seen whole.
|
|
31
31
|
//
|
|
32
|
+
// An element the target RELOCATES holds an address — a table of function pointers, of data
|
|
33
|
+
// pointers, of `&arr[k]` — and is spelled as the address constant it is, `(u32)&sym` or
|
|
34
|
+
// `(u32)((u8 *)&sym + k)`, in the integer element the rest of this typing picks: such an element
|
|
35
|
+
// compiles to the same word and the same relocation as the pointer-typed source (agbcc and mwcc,
|
|
36
|
+
// compiled), so the definition needs no pointer type to put the bytes where the target has them.
|
|
37
|
+
//
|
|
32
38
|
// REFUSES, naming the static, when the target's compiler declares no layout rules, when nothing
|
|
33
39
|
// in the definition settles the width and the accesses disagree on it, when the width is no
|
|
34
40
|
// integer type's or does not divide the size, when the loads disagree on the signedness, when the
|
|
35
41
|
// definition is aligned narrower than its declaration here would be, when a bss static sits where
|
|
36
42
|
// only a scalar initialized to zero is sure to land and it is no scalar (or the input does not
|
|
37
|
-
// show where it sits)
|
|
43
|
+
// show where it sits), when an element holding an address — a static's or the compiler's
|
|
44
|
+
// anonymous image's — is no pointer's width;
|
|
38
45
|
// and, at the rename, when two statics share a source name (one block cannot declare both) or
|
|
39
46
|
// when the function names anything else by it — a global, a callee, a parameter, a local, itself —
|
|
40
|
-
// which the block-scope static would hide
|
|
41
|
-
|
|
47
|
+
// which the block-scope static would hide — and when a global an initializer holds the address of
|
|
48
|
+
// is hidden there by a static, parameter or local of its name.
|
|
49
|
+
import {
|
|
50
|
+
type Fn,
|
|
51
|
+
LOCAL_OBJECT_ADDRESS_BYTES,
|
|
52
|
+
type LocalObject,
|
|
53
|
+
type LocalObjects,
|
|
54
|
+
type Op,
|
|
55
|
+
type Value,
|
|
56
|
+
defOpMap,
|
|
57
|
+
} from '../ir/core';
|
|
42
58
|
import { type IrType, T } from '../ir/types';
|
|
43
59
|
import {
|
|
44
60
|
type Expr,
|
|
61
|
+
type InitAddress,
|
|
45
62
|
type SFn,
|
|
63
|
+
type SImage,
|
|
46
64
|
type SStatic,
|
|
47
65
|
type Stmt,
|
|
48
66
|
mapExprChildren,
|
|
@@ -123,6 +141,8 @@ function zeroInitialized(
|
|
|
123
141
|
export interface LocalStaticShapes {
|
|
124
142
|
infos: Map<string, SymbolInfo>;
|
|
125
143
|
statics: SStatic[];
|
|
144
|
+
/** the compiler's anonymous constant images, which no declaration spells (l3/constinit.ts) */
|
|
145
|
+
images: SImage[];
|
|
126
146
|
}
|
|
127
147
|
|
|
128
148
|
/** One access through a static's address: the width it reads or writes, and a load's extension. */
|
|
@@ -206,8 +226,11 @@ function elements(obj: LocalObject, width: number, signed: boolean): number[] {
|
|
|
206
226
|
}
|
|
207
227
|
|
|
208
228
|
/** The width of `obj`'s element: the one its definition's directives write, else the one every
|
|
209
|
-
* access agrees on; with no access either, the
|
|
210
|
-
*
|
|
229
|
+
* access agrees on; with no access either, a pointer's where the target relocates a word of it (an
|
|
230
|
+
* address table only handed to a callee, on a compiler that writes no directives: mwcc), else the
|
|
231
|
+
* whole object where it is aligned below the compiler's aggregate floor, as only a scalar is, and
|
|
232
|
+
* a byte where it is not. A relocation the pointer width does not seat is still refused, by the
|
|
233
|
+
* caller's check of every relocation's offset. */
|
|
211
234
|
function elementWidth(
|
|
212
235
|
obj: LocalObject,
|
|
213
236
|
accesses: readonly Access[],
|
|
@@ -220,9 +243,15 @@ function elementWidth(
|
|
|
220
243
|
if (widths.length > 1) {
|
|
221
244
|
return { refused: `whose accesses disagree on its element width (${widths.sort().join(' and ')} bytes)` };
|
|
222
245
|
}
|
|
246
|
+
if (widths.length === 1) {
|
|
247
|
+
return widths[0];
|
|
248
|
+
}
|
|
249
|
+
if ((obj.relocs?.length ?? 0) > 0 && obj.size % LOCAL_OBJECT_ADDRESS_BYTES === 0) {
|
|
250
|
+
return LOCAL_OBJECT_ADDRESS_BYTES;
|
|
251
|
+
}
|
|
223
252
|
const shown = obj.directives?.align ?? obj.placement?.align;
|
|
224
253
|
const scalarOnly = shown !== undefined && shown < layout.aggregateAlign;
|
|
225
|
-
return
|
|
254
|
+
return scalarOnly && [1, 2, 4].includes(obj.size) ? obj.size : 1;
|
|
226
255
|
}
|
|
227
256
|
|
|
228
257
|
/** The shape and definition of every static `fn` defines, or the refusal: the static's linker name
|
|
@@ -234,8 +263,9 @@ export function localStaticShapes(
|
|
|
234
263
|
const objs = fn.localObjects;
|
|
235
264
|
const infos = new Map<string, SymbolInfo>();
|
|
236
265
|
const statics: SStatic[] = [];
|
|
266
|
+
const images: SImage[] = [];
|
|
237
267
|
if (objs === undefined) {
|
|
238
|
-
return { infos, statics };
|
|
268
|
+
return { infos, statics, images };
|
|
239
269
|
}
|
|
240
270
|
if (layout === undefined) {
|
|
241
271
|
const [first] = objs.keys();
|
|
@@ -262,6 +292,75 @@ export function localStaticShapes(
|
|
|
262
292
|
// it places in the order it first uses them: the candidate's code decides that, no declaration.
|
|
263
293
|
for (const obj of [...objs.values()].sort((a, b) => a.order - b.order)) {
|
|
264
294
|
const say = (why: string) => ({ symbol: obj.symbol, refused: why });
|
|
295
|
+
if (obj.anonymous) {
|
|
296
|
+
// The compiler's own image of a local aggregate's initializer: typed by its directives alone,
|
|
297
|
+
// since nothing but the copy reads it, and never declared where it sits — l3/constinit.ts
|
|
298
|
+
// folds it into the local the copy fills, which is the declaration that placed it.
|
|
299
|
+
const unit = obj.directives?.unit;
|
|
300
|
+
if (unit === undefined || obj.bytes === undefined) {
|
|
301
|
+
return say('whose image writes no one element width — a struct, or padding alone');
|
|
302
|
+
}
|
|
303
|
+
if (![1, 2, 4].includes(unit) || obj.size % unit !== 0) {
|
|
304
|
+
return say(`whose ${unit}-byte elements are no integer array of its ${obj.size} bytes`);
|
|
305
|
+
}
|
|
306
|
+
const signed = obj.directives!.negative;
|
|
307
|
+
const count = obj.size / unit;
|
|
308
|
+
infos.set(obj.symbol, {
|
|
309
|
+
name: obj.symbol,
|
|
310
|
+
kind: 'data',
|
|
311
|
+
size: obj.size,
|
|
312
|
+
shape: 'array',
|
|
313
|
+
elemSize: unit,
|
|
314
|
+
elemSigned: signed,
|
|
315
|
+
dims: [count],
|
|
316
|
+
const: true,
|
|
317
|
+
});
|
|
318
|
+
// Only the elements the source spelled: an element it left out is a hole, and the holes after
|
|
319
|
+
// the last value are dropped. Spelled out as zeros, the initializer is a different one to the
|
|
320
|
+
// compiler, which clears and stores where most of one is zero rather than copy an image
|
|
321
|
+
// (expr.c `mostly_zeros_p`) — so the printer spells the element after a hole by designator,
|
|
322
|
+
// where the compiler parses one (`SFn.designators`).
|
|
323
|
+
const init: (number | InitAddress | null)[] = elements(obj, unit, signed);
|
|
324
|
+
for (const [from, n] of obj.directives!.gaps ?? []) {
|
|
325
|
+
if (from % unit !== 0 || n % unit !== 0) {
|
|
326
|
+
return say(`whose padding at byte ${from} does not fall on its ${unit}-byte elements`);
|
|
327
|
+
}
|
|
328
|
+
init.fill(null, from / unit, (from + n) / unit);
|
|
329
|
+
}
|
|
330
|
+
// An element the image relocates holds an address, as a static's does — an array of pointers
|
|
331
|
+
// (agbcc copies one with memcpy from 16 words, compiled).
|
|
332
|
+
for (const r of obj.relocs ?? []) {
|
|
333
|
+
if (unit !== LOCAL_OBJECT_ADDRESS_BYTES || r.offset % unit !== 0) {
|
|
334
|
+
return say(
|
|
335
|
+
`whose image holds the address '${r.symbol}', which its ${unit}-byte elements cannot — ` +
|
|
336
|
+
`an address is ${LOCAL_OBJECT_ADDRESS_BYTES}`,
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
init[r.offset / unit] = { symbol: r.symbol, addend: r.addend };
|
|
340
|
+
}
|
|
341
|
+
// A string literal's image is placed at the compiler's string alignment, which a brace list of
|
|
342
|
+
// strings is not (agbcc: `.align 2, 0` ahead of a STRING_CST's label, none ahead of
|
|
343
|
+
// `{"abcde"}`'s) — the same bytes, but spelled as a literal the image moves to a stricter
|
|
344
|
+
// alignment and shifts the unit's `.rodata`. Nor has it a padding run: one shorter than its
|
|
345
|
+
// array is copied at the literal's length and the tail cleared (`char a[16] = "abc…hijklm"` is
|
|
346
|
+
// memcpy 15 + memset 1), so a `.space` after the `.ascii` is a brace list's image copied whole.
|
|
347
|
+
// So the test is asked before the trailing holes are dropped, not after.
|
|
348
|
+
const string =
|
|
349
|
+
obj.directives!.string === true &&
|
|
350
|
+
unit === 1 &&
|
|
351
|
+
obj.directives!.align >= layout.stringAlign &&
|
|
352
|
+
!init.includes(null);
|
|
353
|
+
while (init.length > 0 && init[init.length - 1] === null) {
|
|
354
|
+
init.pop();
|
|
355
|
+
}
|
|
356
|
+
images.push({
|
|
357
|
+
name: obj.symbol,
|
|
358
|
+
type: T.array(T.int(unit * 8, signed), count),
|
|
359
|
+
init,
|
|
360
|
+
...(string ? { string: true as const } : {}),
|
|
361
|
+
});
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
265
364
|
const accesses = access.get(obj.symbol) ?? [];
|
|
266
365
|
const dir = obj.directives;
|
|
267
366
|
// the alignment the target shows
|
|
@@ -284,7 +383,22 @@ export function localStaticShapes(
|
|
|
284
383
|
const signed = signs.size > 0 ? signs.has(true) : dir?.negative === true;
|
|
285
384
|
const count = obj.size / width;
|
|
286
385
|
const elem = T.int(width * 8, signed);
|
|
287
|
-
const init
|
|
386
|
+
const init: (number | InitAddress)[] | undefined = zeroed.has(obj.symbol)
|
|
387
|
+
? [0]
|
|
388
|
+
: obj.bytes === undefined
|
|
389
|
+
? undefined
|
|
390
|
+
: elements(obj, width, signed);
|
|
391
|
+
// An element the target relocates holds an address, which only an element of a pointer's width
|
|
392
|
+
// can: the relocation fills exactly that many bytes.
|
|
393
|
+
for (const r of obj.relocs ?? []) {
|
|
394
|
+
if (width !== LOCAL_OBJECT_ADDRESS_BYTES || r.offset % width !== 0) {
|
|
395
|
+
return say(
|
|
396
|
+
`whose initializer holds the address '${r.symbol}', which its ${width}-byte elements cannot — ` +
|
|
397
|
+
`an address is ${LOCAL_OBJECT_ADDRESS_BYTES}`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
init![r.offset / width] = { symbol: r.symbol, addend: r.addend };
|
|
401
|
+
}
|
|
288
402
|
if (zeroed.has(obj.symbol) && count !== 1) {
|
|
289
403
|
return say(`laid out where only a scalar initialized to zero is, but of ${count} elements`);
|
|
290
404
|
}
|
|
@@ -320,7 +434,7 @@ export function localStaticShapes(
|
|
|
320
434
|
...(string ? { string: true as const } : {}),
|
|
321
435
|
});
|
|
322
436
|
}
|
|
323
|
-
return { infos, statics };
|
|
437
|
+
return { infos, statics, images };
|
|
324
438
|
}
|
|
325
439
|
|
|
326
440
|
/** `sfn` with each static's linker name replaced by its source name — every reference, assignment
|
|
@@ -332,7 +446,26 @@ export function nameLocalStatics(sfn: SFn, objs: LocalObjects): SFn | { symbol:
|
|
|
332
446
|
for (const g of sfn.globals ?? []) {
|
|
333
447
|
taken.add(g.name);
|
|
334
448
|
}
|
|
449
|
+
// The globals an initializer holds the address of are named here as much as the body's: a static
|
|
450
|
+
// of the same name is in scope from its own declarator on, its initializer included.
|
|
451
|
+
const addressed = new Map<string, string>();
|
|
452
|
+
for (const obj of objs.values()) {
|
|
453
|
+
for (const r of obj.relocs ?? []) {
|
|
454
|
+
taken.add(r.symbol);
|
|
455
|
+
addressed.set(r.symbol, obj.symbol);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
335
458
|
const bound = new Set([...sfn.params, ...sfn.locals].map((x) => x.name));
|
|
459
|
+
// …and the function's own parameters hide one too: a static's initializer is read in their scope.
|
|
460
|
+
// A local is declared after every static and hides none, but its name is the emitter's to move.
|
|
461
|
+
for (const [name, holder] of addressed) {
|
|
462
|
+
if (bound.has(name)) {
|
|
463
|
+
return {
|
|
464
|
+
symbol: holder,
|
|
465
|
+
refused: `whose initializer holds the address of '${name}', which a parameter or local here also names — it would hide that global`,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
}
|
|
336
469
|
const callees = new Set<string>([sfn.name]);
|
|
337
470
|
for (const e of walkExprs(sfn.body)) {
|
|
338
471
|
if (e.k === 'call' && typeof e.fn === 'string') {
|
|
@@ -342,6 +475,9 @@ export function nameLocalStatics(sfn: SFn, objs: LocalObjects): SFn | { symbol:
|
|
|
342
475
|
const rename = new Map<string, string>();
|
|
343
476
|
const bySource = new Map<string, string>();
|
|
344
477
|
for (const obj of objs.values()) {
|
|
478
|
+
if (obj.anonymous) {
|
|
479
|
+
continue; // no source name: folded into a local's initializer at emission (l3/constinit.ts)
|
|
480
|
+
}
|
|
345
481
|
const say = (why: string) => ({ symbol: obj.symbol, refused: `whose source name '${obj.name}' ${why}` });
|
|
346
482
|
const other = bySource.get(obj.name);
|
|
347
483
|
if (other !== undefined) {
|
|
@@ -133,7 +133,7 @@ export interface NameCoalesceDeps {
|
|
|
133
133
|
varName: ReadonlyMap<Value, string>;
|
|
134
134
|
/** name → the type it is declared with */
|
|
135
135
|
varType: ReadonlyMap<string, IrType>;
|
|
136
|
-
/** every natural loop, as its header and the blocks its body contains (
|
|
136
|
+
/** every natural loop, as its header and the blocks its body contains (ir/loops.ts) */
|
|
137
137
|
loops: readonly { header: Block; body: ReadonlySet<Block> }[];
|
|
138
138
|
}
|
|
139
139
|
|