@asmlift/core 0.1.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/LICENSE +21 -0
- package/README.md +148 -0
- package/package.json +14 -0
- package/src/backend/c.ts +20 -0
- package/src/backend/cfamily.ts +352 -0
- package/src/backend/cpp.ts +145 -0
- package/src/backend/pascal.ts +279 -0
- package/src/contracts.ts +131 -0
- package/src/detect.ts +12 -0
- package/src/frontend/asmdata.ts +170 -0
- package/src/frontend/disasm.ts +102 -0
- package/src/frontend/emit.ts +57 -0
- package/src/frontend/errors.ts +14 -0
- package/src/frontend/format.ts +47 -0
- package/src/frontend/frontend.ts +22 -0
- package/src/frontend/mips.ts +875 -0
- package/src/frontend/opaque.ts +82 -0
- package/src/frontend/ppc.ts +990 -0
- package/src/frontend/registry.ts +34 -0
- package/src/frontend/ssa.ts +214 -0
- package/src/frontend/thumb.ts +1419 -0
- package/src/ir/core.ts +104 -0
- package/src/ir/opcodes.ts +143 -0
- package/src/ir/parse.ts +221 -0
- package/src/ir/print.ts +77 -0
- package/src/ir/types.ts +106 -0
- package/src/ir/verify.ts +221 -0
- package/src/l3/ast.ts +301 -0
- package/src/l3/basecse.ts +218 -0
- package/src/l3/dce.ts +256 -0
- package/src/l3/regspell.ts +331 -0
- package/src/l3/reindex.ts +447 -0
- package/src/l3/typing.ts +145 -0
- package/src/mangle.ts +135 -0
- package/src/pattern/engine.ts +392 -0
- package/src/pipeline.ts +272 -0
- package/src/proto.ts +42 -0
- package/src/raise/arrays.ts +84 -0
- package/src/raise/const.ts +52 -0
- package/src/raise/errors.ts +10 -0
- package/src/raise/magicdiv.ts +386 -0
- package/src/raise/pre-recovery.ts +71 -0
- package/src/raise/recover.ts +215 -0
- package/src/raise/retsink.ts +72 -0
- package/src/raise/shortcircuit.ts +207 -0
- package/src/raise/softdiv.ts +62 -0
- package/src/raise/struct-arrays.ts +257 -0
- package/src/raise/structs.ts +223 -0
- package/src/rank.ts +208 -0
- package/src/structure/analysis.ts +410 -0
- package/src/structure/hazards.ts +142 -0
- package/src/structure/loops.ts +169 -0
- package/src/structure/structure.ts +1726 -0
- package/src/structure/switch-recover.ts +410 -0
- package/src/target.ts +140 -0
- package/src/trace.ts +233 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// asmlift — natural-loop discovery: the pure CFG analysis the structurer consumes.
|
|
2
|
+
//
|
|
3
|
+
// Produces a DEFINITE header set computed structurally from back-edges — a block is a loop header
|
|
4
|
+
// iff it is a real back-edge target (`h ∈ dom(t)` for an edge `t→h`), never inferred from `cond_br`
|
|
5
|
+
// shape. That structural definiteness keeps loop detection from stealing a block switch/if recovery
|
|
6
|
+
// owns. It has NOTHING to do with AST emission and is unit-testable on synthetic CFGs with no
|
|
7
|
+
// emitter and no toolchain (test/loops.test.ts).
|
|
8
|
+
//
|
|
9
|
+
// Dominators live here too: loop discovery is their primary consumer, and keeping the analysis
|
|
10
|
+
// self-contained keeps the module independently testable.
|
|
11
|
+
import { Block, Fn, predecessors, successorsOf } from '../ir/core';
|
|
12
|
+
|
|
13
|
+
/** One natural loop, keyed by its header. */
|
|
14
|
+
export interface NaturalLoop {
|
|
15
|
+
/** the back-edge target — dominates every block in `body`. */
|
|
16
|
+
header: Block;
|
|
17
|
+
/** blocks with a back-edge to `header` (a loop has ≥1). */
|
|
18
|
+
latches: Block[];
|
|
19
|
+
/** the natural-loop node set: `header` + everything reaching a latch without passing through
|
|
20
|
+
* `header`. Ret-terminated blocks reached from the body (early returns) are NOT included — they are
|
|
21
|
+
* classified by the structurer, which distinguishes the header's own exit edge (the single real
|
|
22
|
+
* exit, whether or not it returns) from early-return edges out of other body blocks. */
|
|
23
|
+
body: Set<Block>;
|
|
24
|
+
/** every edge from a body block to a non-body block, scanned over ALL body blocks (so a second
|
|
25
|
+
* exit deep in the body is visible, not just the header's). The structurer decides which is
|
|
26
|
+
* the loop exit vs an early return. */
|
|
27
|
+
exitEdges: { from: Block; to: Block }[];
|
|
28
|
+
/** predecessors of `header` that are NOT in the body — the entry (init) side. A unique one is the
|
|
29
|
+
* preheader. */
|
|
30
|
+
forwardPreds: Block[];
|
|
31
|
+
/** true when `header` is its own successor (the single-block do-while shape emitWhile handles). */
|
|
32
|
+
selfLoop: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface LoopForest {
|
|
36
|
+
byHeader: Map<Block, NaturalLoop>;
|
|
37
|
+
/** nesting parent: the header of the smallest loop strictly containing this loop (or null). */
|
|
38
|
+
parent: Map<Block, Block | null>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Forward dominators (iterative data-flow). dom(b) = {b} ∪ ⋂ dom(preds). */
|
|
42
|
+
export function dominators(fn: Fn): Map<Block, Set<Block>> {
|
|
43
|
+
const preds = predecessors(fn);
|
|
44
|
+
const all = new Set(fn.blocks);
|
|
45
|
+
const dom = new Map<Block, Set<Block>>();
|
|
46
|
+
fn.blocks.forEach((b, i) => dom.set(b, i === 0 ? new Set([b]) : new Set(all)));
|
|
47
|
+
let changed = true;
|
|
48
|
+
while (changed) {
|
|
49
|
+
changed = false;
|
|
50
|
+
for (const b of fn.blocks.slice(1)) {
|
|
51
|
+
const ps = preds.get(b)!;
|
|
52
|
+
let inter: Set<Block> | null = null;
|
|
53
|
+
for (const p of ps) {
|
|
54
|
+
const dp = dom.get(p)!;
|
|
55
|
+
if (inter === null) {
|
|
56
|
+
inter = new Set(dp);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
for (const x of inter) {
|
|
60
|
+
if (!dp.has(x)) {
|
|
61
|
+
inter.delete(x);
|
|
62
|
+
}
|
|
63
|
+
} // intersect in place (spec-safe delete-in-iter)
|
|
64
|
+
}
|
|
65
|
+
const next = new Set<Block>(inter ?? []);
|
|
66
|
+
next.add(b);
|
|
67
|
+
if (!setEq(next, dom.get(b)!)) {
|
|
68
|
+
dom.set(b, next);
|
|
69
|
+
changed = true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return dom;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Discover every natural loop and the nesting forest. Pure over the CFG + dominators. */
|
|
77
|
+
export function analyzeLoops(fn: Fn, dom: Map<Block, Set<Block>>): LoopForest {
|
|
78
|
+
const preds = predecessors(fn);
|
|
79
|
+
const byHeader = new Map<Block, NaturalLoop>();
|
|
80
|
+
|
|
81
|
+
// Back-edges: an edge t→h where h dominates t. h is a header, t a latch. Merge multiple back-edges
|
|
82
|
+
// into one loop (a header can have several latches).
|
|
83
|
+
for (const t of fn.blocks) {
|
|
84
|
+
for (const h of successorsOf(t)) {
|
|
85
|
+
if (!dom.get(t)!.has(h)) {
|
|
86
|
+
continue;
|
|
87
|
+
} // not a back-edge
|
|
88
|
+
let nl = byHeader.get(h);
|
|
89
|
+
if (!nl) {
|
|
90
|
+
nl = { header: h, latches: [], body: new Set([h]), exitEdges: [], forwardPreds: [], selfLoop: false };
|
|
91
|
+
byHeader.set(h, nl);
|
|
92
|
+
}
|
|
93
|
+
nl.latches.push(t);
|
|
94
|
+
if (t === h) {
|
|
95
|
+
nl.selfLoop = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const nl of byHeader.values()) {
|
|
101
|
+
const body = nl.body;
|
|
102
|
+
// Natural body: header + every node reaching a latch without passing through the header.
|
|
103
|
+
const stack: Block[] = [];
|
|
104
|
+
for (const l of nl.latches) {
|
|
105
|
+
if (!body.has(l)) {
|
|
106
|
+
body.add(l);
|
|
107
|
+
}
|
|
108
|
+
stack.push(l);
|
|
109
|
+
}
|
|
110
|
+
while (stack.length) {
|
|
111
|
+
const b = stack.pop()!;
|
|
112
|
+
if (b === nl.header) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
for (const p of preds.get(b)!) {
|
|
116
|
+
if (p === nl.header) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (!body.has(p)) {
|
|
120
|
+
body.add(p);
|
|
121
|
+
stack.push(p);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Exits: every edge from a body block to a non-body block, over every body block. The
|
|
126
|
+
// structurer separates the header's own exit edge (the real exit) from early-return edges.
|
|
127
|
+
for (const b of body) {
|
|
128
|
+
for (const s of successorsOf(b)) {
|
|
129
|
+
if (!body.has(s)) {
|
|
130
|
+
nl.exitEdges.push({ from: b, to: s });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
nl.forwardPreds = (preds.get(nl.header) ?? []).filter((p) => !body.has(p));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Nesting: parent(header) = the header of the smallest OTHER loop whose body contains it.
|
|
138
|
+
const headers = [...byHeader.keys()];
|
|
139
|
+
const parent = new Map<Block, Block | null>();
|
|
140
|
+
for (const h of headers) {
|
|
141
|
+
let best: Block | null = null;
|
|
142
|
+
let bestSize = Infinity;
|
|
143
|
+
for (const h2 of headers) {
|
|
144
|
+
if (h2 === h) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const b2 = byHeader.get(h2)!;
|
|
148
|
+
if (b2.body.has(h) && b2.body.size < bestSize) {
|
|
149
|
+
best = h2;
|
|
150
|
+
bestSize = b2.body.size;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
parent.set(h, best);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return { byHeader, parent };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function setEq<X>(a: Set<X>, b: Set<X>): boolean {
|
|
160
|
+
if (a.size !== b.size) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
for (const x of a) {
|
|
164
|
+
if (!b.has(x)) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|