Skip to content

Commit db5d5eb

Browse files
ziadziad
authored andcommitted
fix bugs with collapse
1 parent d82add0 commit db5d5eb

4 files changed

Lines changed: 306 additions & 26 deletions

File tree

playground/playground.bundle.js

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23059,6 +23059,11 @@ log(mod.toString());`
2305923059
let currentBlockId = blockId;
2306023060
while (currentBlockId !== null && currentBlockId !== regionEnd) {
2306123061
if (processed.has(currentBlockId)) {
23062+
const skipTarget = dominance.postImmediateDominator.get(currentBlockId);
23063+
if (skipTarget !== void 0 && skipTarget !== currentBlockId && skipTarget !== regionEnd && !processed.has(skipTarget)) {
23064+
currentBlockId = skipTarget;
23065+
continue;
23066+
}
2306223067
break;
2306323068
}
2306423069
const block = blockMap.get(currentBlockId);
@@ -23134,6 +23139,35 @@ log(mod.toString());`
2313423139
}
2313523140
return { kind: "sequence", children };
2313623141
}
23142+
function structureAfterWhileLoop(whileNode, loop, primaryExitId, regionEnd) {
23143+
const afterChildren = [whileNode];
23144+
const postDom = dominance.postImmediateDominator.get(loop.headerId);
23145+
const convergenceId = postDom !== void 0 && !loop.bodyIds.has(postDom) ? postDom : null;
23146+
if (primaryExitId !== regionEnd && !processed.has(primaryExitId)) {
23147+
if (primaryExitId !== convergenceId) {
23148+
const primaryPath = structureRegion(primaryExitId, convergenceId ?? regionEnd);
23149+
afterChildren.push(primaryPath);
23150+
}
23151+
}
23152+
for (const exitId of loop.exitIds) {
23153+
if (exitId === primaryExitId || exitId === regionEnd || processed.has(exitId)) {
23154+
continue;
23155+
}
23156+
if (exitId === convergenceId) {
23157+
continue;
23158+
}
23159+
const exitPath = structureRegion(exitId, convergenceId ?? regionEnd);
23160+
afterChildren.push(exitPath);
23161+
}
23162+
if (convergenceId !== null && convergenceId !== regionEnd && !processed.has(convergenceId)) {
23163+
const afterConvergence = structureRegion(convergenceId, regionEnd);
23164+
afterChildren.push(afterConvergence);
23165+
}
23166+
if (afterChildren.length === 1) {
23167+
return whileNode;
23168+
}
23169+
return { kind: "sequence", children: afterChildren };
23170+
}
2313723171
function structureLoop(headerId, loop, regionEnd) {
2313823172
processed.add(headerId);
2313923173
const headerBlock = blockMap.get(headerId);
@@ -23151,12 +23185,7 @@ log(mod.toString());`
2315123185
condition: terminator.condition,
2315223186
body: prependNode(preBody, bodyNode2)
2315323187
};
23154-
const exitId = falseTarget;
23155-
if (exitId !== regionEnd && !processed.has(exitId)) {
23156-
const afterLoop = structureRegion(exitId, regionEnd);
23157-
return { kind: "sequence", children: [whileNode2, afterLoop] };
23158-
}
23159-
return whileNode2;
23188+
return structureAfterWhileLoop(whileNode2, loop, falseTarget, regionEnd);
2316023189
}
2316123190
if (!trueInLoop && falseInLoop) {
2316223191
const bodyNode2 = structureLoopBody(falseTarget, headerId, loop);
@@ -23167,12 +23196,7 @@ log(mod.toString());`
2316723196
condition: negatedCondition,
2316823197
body: prependNode(preBody, bodyNode2)
2316923198
};
23170-
const exitId = trueTarget;
23171-
if (exitId !== regionEnd && !processed.has(exitId)) {
23172-
const afterLoop = structureRegion(exitId, regionEnd);
23173-
return { kind: "sequence", children: [whileNode2, afterLoop] };
23174-
}
23175-
return whileNode2;
23199+
return structureAfterWhileLoop(whileNode2, loop, trueTarget, regionEnd);
2317623200
}
2317723201
}
2317823202
const bodyNode = structureLoopBody(headerId, headerId, loop);
@@ -23501,6 +23525,19 @@ log(mod.toString());`
2350123525
const nextId = mergePoint !== regionEnd && !processed.has(mergePoint) ? mergePoint : null;
2350223526
return { node: prependNode(preBody, ifNode), mergeBlockId: nextId };
2350323527
}
23528+
if (mergePoint === falseTarget && !processed.has(trueTarget)) {
23529+
const thenBody = structureRegion(trueTarget, falseTarget);
23530+
const ifNode = { kind: "if", condition: terminator.condition, thenBody, elseBody: null };
23531+
const mergeId = falseTarget !== regionEnd && !processed.has(falseTarget) ? falseTarget : null;
23532+
return { node: prependNode(preBody, ifNode), mergeBlockId: mergeId };
23533+
}
23534+
if (mergePoint === trueTarget && !processed.has(falseTarget)) {
23535+
const elseBody = structureRegion(falseTarget, trueTarget);
23536+
const negated = negateCondition(terminator.condition);
23537+
const ifNode = { kind: "if", condition: negated, thenBody: elseBody, elseBody: null };
23538+
const mergeId = trueTarget !== regionEnd && !processed.has(trueTarget) ? trueTarget : null;
23539+
return { node: prependNode(preBody, ifNode), mergeBlockId: mergeId };
23540+
}
2350423541
const labeledBody = blockToNode(block);
2350523542
return { node: labeledBody, mergeBlockId: null };
2350623543
}

playground/playground.bundle.js.map

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/decompiler/StructuralAnalysis.ts

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,15 @@ export function structureFunction(
180180

181181
while (currentBlockId !== null && currentBlockId !== regionEnd) {
182182
if (processed.has(currentBlockId)) {
183+
// If the block was already consumed by a nested call, skip to the
184+
// post-dominator so we can continue structuring downstream blocks
185+
// that haven't been processed yet.
186+
const skipTarget = dominance.postImmediateDominator.get(currentBlockId);
187+
if (skipTarget !== undefined && skipTarget !== currentBlockId
188+
&& skipTarget !== regionEnd && !processed.has(skipTarget)) {
189+
currentBlockId = skipTarget;
190+
continue;
191+
}
183192
break;
184193
}
185194

@@ -277,6 +286,46 @@ export function structureFunction(
277286
return { kind: 'sequence', children };
278287
}
279288

289+
function structureAfterWhileLoop(
290+
whileNode: StructuredNode,
291+
loop: NaturalLoop,
292+
primaryExitId: number,
293+
regionEnd: number | null,
294+
): StructuredNode {
295+
const afterChildren: StructuredNode[] = [whileNode];
296+
297+
// Find convergence point where all loop exits meet
298+
const postDom = dominance.postImmediateDominator.get(loop.headerId);
299+
const convergenceId = (postDom !== undefined && !loop.bodyIds.has(postDom)) ? postDom : null;
300+
301+
// Structure the primary exit path (the while condition's false branch)
302+
if (primaryExitId !== regionEnd && !processed.has(primaryExitId)) {
303+
if (primaryExitId !== convergenceId) {
304+
const primaryPath = structureRegion(primaryExitId, convergenceId ?? regionEnd);
305+
afterChildren.push(primaryPath);
306+
}
307+
}
308+
309+
// Structure any other unprocessed exit paths reached via break statements
310+
for (const exitId of loop.exitIds) {
311+
if (exitId === primaryExitId || exitId === regionEnd || processed.has(exitId)) { continue; }
312+
if (exitId === convergenceId) { continue; }
313+
const exitPath = structureRegion(exitId, convergenceId ?? regionEnd);
314+
afterChildren.push(exitPath);
315+
}
316+
317+
// Structure from convergence point onward
318+
if (convergenceId !== null && convergenceId !== regionEnd && !processed.has(convergenceId)) {
319+
const afterConvergence = structureRegion(convergenceId, regionEnd);
320+
afterChildren.push(afterConvergence);
321+
}
322+
323+
if (afterChildren.length === 1) {
324+
return whileNode;
325+
}
326+
return { kind: 'sequence', children: afterChildren };
327+
}
328+
280329
function structureLoop(
281330
headerId: number,
282331
loop: NaturalLoop,
@@ -301,12 +350,7 @@ export function structureFunction(
301350
condition: terminator.condition,
302351
body: prependNode(preBody, bodyNode),
303352
};
304-
const exitId = falseTarget;
305-
if (exitId !== regionEnd && !processed.has(exitId)) {
306-
const afterLoop = structureRegion(exitId, regionEnd);
307-
return { kind: 'sequence', children: [whileNode, afterLoop] };
308-
}
309-
return whileNode;
353+
return structureAfterWhileLoop(whileNode, loop, falseTarget, regionEnd);
310354
}
311355

312356
if (!trueInLoop && falseInLoop) {
@@ -318,12 +362,7 @@ export function structureFunction(
318362
condition: negatedCondition,
319363
body: prependNode(preBody, bodyNode),
320364
};
321-
const exitId = trueTarget;
322-
if (exitId !== regionEnd && !processed.has(exitId)) {
323-
const afterLoop = structureRegion(exitId, regionEnd);
324-
return { kind: 'sequence', children: [whileNode, afterLoop] };
325-
}
326-
return whileNode;
365+
return structureAfterWhileLoop(whileNode, loop, trueTarget, regionEnd);
327366
}
328367
}
329368

@@ -707,6 +746,21 @@ export function structureFunction(
707746
return { node: prependNode(preBody, ifNode), mergeBlockId: nextId };
708747
}
709748

749+
// Merge point equals one of the targets — structure the other branch, use target as merge
750+
if (mergePoint === falseTarget && !processed.has(trueTarget)) {
751+
const thenBody = structureRegion(trueTarget, falseTarget);
752+
const ifNode: StructuredNode = { kind: 'if', condition: terminator.condition, thenBody, elseBody: null };
753+
const mergeId = falseTarget !== regionEnd && !processed.has(falseTarget) ? falseTarget : null;
754+
return { node: prependNode(preBody, ifNode), mergeBlockId: mergeId };
755+
}
756+
if (mergePoint === trueTarget && !processed.has(falseTarget)) {
757+
const elseBody = structureRegion(falseTarget, trueTarget);
758+
const negated = negateCondition(terminator.condition);
759+
const ifNode: StructuredNode = { kind: 'if', condition: negated, thenBody: elseBody, elseBody: null };
760+
const mergeId = trueTarget !== regionEnd && !processed.has(trueTarget) ? trueTarget : null;
761+
return { node: prependNode(preBody, ifNode), mergeBlockId: mergeId };
762+
}
763+
710764
// Last resort — emit block instructions and continue
711765
const labeledBody = blockToNode(block);
712766
return { node: labeledBody, mergeBlockId: null };

0 commit comments

Comments
 (0)