Skip to content

Commit

Permalink
Undo last commit, fix order of overlapping loops
Browse files Browse the repository at this point in the history
  • Loading branch information
colinator27 committed Jul 11, 2024
1 parent 5aea601 commit 5f86e7a
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 18 deletions.
6 changes: 2 additions & 4 deletions Underanalyzer/Decompiler/ControlFlow/Branches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ public static Dictionary<Block, Loop> FindSurroundingLoops(
List<Block> blocks, Dictionary<int, Block> blockByAddress, List<Loop> loops)
{
// Assign blocks to loops.
// We assume that loops are sorted so that nested loops come before outer loops.
// We go in reverse so that inner loops override outer loops.
// We iterate over loops so that inner loops are processed after outer loops.
Dictionary<Block, Loop> surroundingLoops = [];
for (int i = loops.Count - 1; i >= 0; i--)
foreach (Loop l in loops)
{
Loop l = loops[i];
Block startBlock = blockByAddress[l.StartAddress];
Block endBlock = blockByAddress[l.EndAddress];
for (int blockIndex = startBlock.BlockIndex; blockIndex < endBlock.BlockIndex; blockIndex++)
Expand Down
4 changes: 2 additions & 2 deletions Underanalyzer/Decompiler/ControlFlow/DoUntilLoop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ internal class DoUntilLoop : Loop
/// </remarks>
public IControlFlowNode After { get => Children[2]!; private set => Children[2] = value; }

public DoUntilLoop(int startAddress, int endAddress, IControlFlowNode head, IControlFlowNode tail, IControlFlowNode after)
: base(startAddress, endAddress)
public DoUntilLoop(int startAddress, int endAddress, int index, IControlFlowNode head, IControlFlowNode tail, IControlFlowNode after)
: base(startAddress, endAddress, index)
{
Head = head;
Tail = tail;
Expand Down
22 changes: 16 additions & 6 deletions Underanalyzer/Decompiler/ControlFlow/Loop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Underanalyzer.Decompiler.ControlFlow;
/// <summary>
/// Represents a loop (jump/branch backwards) node in a control flow graph.
/// </summary>
internal abstract class Loop(int startAddress, int endAddress) : IControlFlowNode
internal abstract class Loop(int startAddress, int endAddress, int index) : IControlFlowNode
{
public int StartAddress { get; private set; } = startAddress;

Expand All @@ -31,6 +31,11 @@ internal abstract class Loop(int startAddress, int endAddress) : IControlFlowNod

public bool Unreachable { get; set; } = false;

/// <summary>
/// The index of this loop, as assigned in order of when the loop was first discovered (from top to bottom of code).
/// </summary>
public int LoopIndex { get; } = index;

/// <summary>
/// Called to insert a given loop's node into the control flow graph.
/// </summary>
Expand All @@ -45,6 +50,7 @@ public static List<Loop> FindLoops(DecompileContext ctx)

List<Loop> loops = [];
HashSet<int> whileLoopsFound = [];
int loopIndex = 0;

// Search for different loop types based on instruction patterns
// Do this in reverse order, because we want to find the ends of loops first
Expand All @@ -70,7 +76,7 @@ public static List<Loop> FindLoops(DecompileContext ctx)
int conditionAddr = instr.Address + instr.BranchOffset;
if (whileLoopsFound.Add(conditionAddr))
{
loops.Add(new WhileLoop(conditionAddr, block.EndAddress,
loops.Add(new WhileLoop(conditionAddr, block.EndAddress, loopIndex++,
block.Successors[0], block, blocks[block.BlockIndex + 1]));
}
}
Expand All @@ -79,15 +85,15 @@ public static List<Loop> FindLoops(DecompileContext ctx)
if (instr.BranchOffset < 0)
{
// Do...until loop detected
loops.Add(new DoUntilLoop(block.Successors[1].StartAddress, block.EndAddress,
loops.Add(new DoUntilLoop(block.Successors[1].StartAddress, block.EndAddress, loopIndex++,
block.Successors[1], block, block.Successors[0]));
}
break;
case IGMInstruction.Opcode.BranchTrue:
if (instr.BranchOffset < 0)
{
// Repeat loop detected
loops.Add(new RepeatLoop(block.Successors[1].StartAddress, block.EndAddress,
loops.Add(new RepeatLoop(block.Successors[1].StartAddress, block.EndAddress, loopIndex++,
block.Successors[1], block, block.Successors[0]));
}
break;
Expand All @@ -106,7 +112,7 @@ potentialBreakBlock.Instructions is
breakBlock = potentialBreakBlock;
}
}
loops.Add(new WithLoop(block.EndAddress, block.Successors[1].StartAddress,
loops.Add(new WithLoop(block.EndAddress, block.Successors[1].StartAddress, loopIndex++,
block, block.Successors[0], block.Successors[1], afterBlock, breakBlock));
}
break;
Expand All @@ -120,7 +126,11 @@ potentialBreakBlock.Instructions is
return -1;
if (a.StartAddress > b.StartAddress)
return 1;
return b.EndAddress - a.EndAddress;
if (b.EndAddress < a.EndAddress)
return -1;
if (b.EndAddress > a.EndAddress)
return 1;
return b.LoopIndex - a.LoopIndex;
});
foreach (var loop in loops)
loop.UpdateFlowGraph();
Expand Down
4 changes: 2 additions & 2 deletions Underanalyzer/Decompiler/ControlFlow/RepeatLoop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ internal class RepeatLoop : Loop
/// </remarks>
public IControlFlowNode After { get => Children[2]!; private set => Children[2] = value; }

public RepeatLoop(int startAddress, int endAddress, IControlFlowNode head, IControlFlowNode tail, IControlFlowNode after)
: base(startAddress, endAddress)
public RepeatLoop(int startAddress, int endAddress, int index, IControlFlowNode head, IControlFlowNode tail, IControlFlowNode after)
: base(startAddress, endAddress, index)
{
Head = head;
Tail = tail;
Expand Down
4 changes: 2 additions & 2 deletions Underanalyzer/Decompiler/ControlFlow/WhileLoop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ internal class WhileLoop : Loop
/// </summary>
public bool MustBeWhileLoop { get; set; } = false;

public WhileLoop(int startAddress, int endAddress, IControlFlowNode head, IControlFlowNode tail, IControlFlowNode after)
: base(startAddress, endAddress)
public WhileLoop(int startAddress, int endAddress, int index, IControlFlowNode head, IControlFlowNode tail, IControlFlowNode after)
: base(startAddress, endAddress, index)
{
Head = head;
Tail = tail;
Expand Down
4 changes: 2 additions & 2 deletions Underanalyzer/Decompiler/ControlFlow/WithLoop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ internal class WithLoop : Loop
/// </remarks>
public IControlFlowNode? BreakBlock { get => Children[4]; private set => Children[4] = value; }

public WithLoop(int startAddress, int endAddress,
public WithLoop(int startAddress, int endAddress, int index,
IControlFlowNode before, IControlFlowNode head, IControlFlowNode tail,
IControlFlowNode after, IControlFlowNode? breakBlock)
: base(startAddress, endAddress)
: base(startAddress, endAddress, index)
{
Before = before;
Head = head;
Expand Down
46 changes: 46 additions & 0 deletions UnderanalyzerTest/DecompileContext.DecompileToString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2269,4 +2269,50 @@ popenv [1]
"""
);
}

[Fact]
public void TestWhileWhileIfBreak()
{
TestUtil.VerifyDecompileResult(
"""
:[0]
push.v builtin.a
conv.v.b
bf [6]
:[1]
push.v builtin.b
conv.v.b
bf [5]
:[2]
push.v builtin.c
conv.v.b
bf [4]
:[3]
b [5]
:[4]
b [1]
:[5]
b [0]
:[6]
""",
"""
while (a)
{
while (b)
{
if (c)
{
break;
}
}
}
"""
);
}
}

0 comments on commit 5f86e7a

Please sign in to comment.