Skip to content

Commit

Permalink
Refactor: more analyses implementing the IAnalysis<SsaState> interface.
Browse files Browse the repository at this point in the history
  • Loading branch information
uxmal committed May 24, 2024
1 parent e25ac9f commit 137ae91
Show file tree
Hide file tree
Showing 32 changed files with 611 additions and 331 deletions.
60 changes: 42 additions & 18 deletions src/Decompiler/Analysis/ArgumentGuesser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,34 +24,56 @@
using Reko.Core.Diagnostics;
using Reko.Core.Expressions;
using Reko.Core.Operators;
using Reko.Core.Services;
using Reko.Core.Types;
using Reko.Services;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Reko.Analysis
namespace Reko.Analysis;

/// <summary>
/// Guesses the arguments to otherwise opaque calls by walking backwards
/// from the call site and detect assignments which likely are intended
/// to be the arguments of the call. This is a best effort transformation
/// and may introduce errors, so use with caution.
/// </summary>

public class ArgumentGuesser : IAnalysis<SsaState>
{
/// <summary>
/// Guesses the arguments to otherwise opaque calls by walking backwards
/// from the call site and detect assignments which likely are intended
/// to be the arguments of the call. This is a best effort transformation
/// and may introduce errors, so use with caution.
/// </summary>
public class ArgumentGuesser
private static readonly TraceSwitch trace = new(nameof(ArgumentGuesser), "Trace ArgumentGuesser")
{
private static readonly TraceSwitch trace = new(nameof(ArgumentGuesser), "Trace ArgumentGuesser")
{
Level = TraceLevel.Info,
};
Level = TraceLevel.Info,
};

private readonly AnalysisContext context;

public ArgumentGuesser(AnalysisContext context)
{
this.context = context;
}

public string Id => "argg";

public string Description => "Guess arguments to unknown called procedures";

public (SsaState, bool) Transform(SsaState ssa)
{
var platform = context.Program.Platform;
var worker = new Worker(platform, ssa, context.EventListener);
var changed = worker.Transform();
return (ssa, changed);
}

private class Worker
{
private readonly IPlatform platform;
private readonly SsaState ssa;
private readonly Storage stackPointer;
private readonly Storage framePointer;
private readonly IDecompilerEventListener eventListener;
private readonly IEventListener eventListener;

public ArgumentGuesser(IPlatform platform, SsaState ssa, IDecompilerEventListener eventListener)
public Worker(IPlatform platform, SsaState ssa, IEventListener eventListener)
{
this.platform = platform;
this.ssa = ssa;
Expand All @@ -60,15 +82,15 @@ public ArgumentGuesser(IPlatform platform, SsaState ssa, IDecompilerEventListene
this.framePointer = ssa.Procedure.Frame.FramePointer.Storage;
}

public void Transform()
public bool Transform()
{
bool changed = false;
foreach (var block in ssa.Procedure.ControlGraph.Blocks)
{
for (int i = 0; i < block.Statements.Count; ++i)
{

if (eventListener.IsCanceled())
return;
return changed;
var stm = block.Statements[i];
if (stm.Instruction is CallInstruction call &&
call.Callee is ProcedureConstant pc &&
Expand All @@ -82,10 +104,12 @@ public void Transform()
{
ReplaceCallWithApplication(stm, call, pc, gargs, gret);
trace.Verbose(" rewritten as: {0}", stm);
changed = true;
}
}
}
}
return changed;
}

private GuessedArguments? GuessArguments(Statement stmCall, CallInstruction call, Block block, int i)
Expand Down
65 changes: 42 additions & 23 deletions src/Decompiler/Analysis/Coalescer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,47 @@
using System.Collections.Generic;
using System.Diagnostics;

namespace Reko.Analysis
namespace Reko.Analysis;

/// <summary>
/// Builds expression trees out of identifier assignment statements and
/// moves assignment statements (def's) as close to their
/// uses in this block as possible.
/// </summary>
/// <remarks>
/// This transformation is quite destabilizing and ineffective if long
/// chains of expressions are still in their 3-address format. Before
/// coalescing, call <see cref="ValuePropagator"/> to perform constant
/// propagation and other transformations that make the expression trees
/// smaller.
/// </remarks>
public class Coalescer : IAnalysis<SsaState>
{
/// <summary>
/// Builds expression trees out of identifier assignment statements and
/// moves assignment statements (def's) as close to their
/// uses in this block as possible.
/// </summary>
/// <remarks>
/// This transformation is quite destabilizing and ineffective if long
/// chains of expressions are still in their 3-address format. Before
/// coalescing, call <see cref="ValuePropagator"/> to perform constant
/// propagation and other transformations that make the expression trees
/// smaller.
/// </remarks>
public class Coalescer : InstructionTransformer
private static readonly TraceSwitch trace = new(nameof(Coalescer), "Traces the progress of identifier coalescing");

public Coalescer(AnalysisContext context)
{
}

public string Id => "coa";

public string Description => "Builds expression trees by joining using expressions with definitions";

public (SsaState, bool) Transform(SsaState ssa)
{
var worker = new Worker(ssa);
bool changed = worker.Transform();
return (ssa, changed);
}
private class Worker: InstructionTransformer
{
private readonly SsaState ssa;
private readonly SideEffectFinder sef;
private readonly Dictionary<Statement, List<SsaIdentifier>> defsByStatement;
private bool changed;
private bool coalesced;

private static readonly TraceSwitch trace = new(nameof(Coalescer), "Traces the progress of identifier coalescing");

public Coalescer(SsaState ssa)
public Worker(SsaState ssa)
{
this.ssa = ssa;
this.sef = new SideEffectFinder();
Expand All @@ -59,8 +77,6 @@ public Coalescer(SsaState ssa)
}
}

public bool Coalesced { get; set; }

private void SetDefStatement(Statement stm, SsaIdentifier sid)
{
if (defsByStatement.TryGetValue(sid.DefStatement, out var sids))
Expand Down Expand Up @@ -189,7 +205,7 @@ public void Process(Block block)
{
do
{
Coalesced = false;
coalesced = false;

var visited = new HashSet<Identifier>();
for (int i = 0; i < block.Statements.Count; ++i)
Expand All @@ -205,15 +221,18 @@ public void Process(Block block)
}
}
}
} while (Coalesced);
changed |= coalesced;
} while (coalesced);
}

public void Transform()
public bool Transform()
{
this.changed = false;
foreach (Block b in ssa.Procedure.ControlGraph.Blocks)
{
Process(b);
}
return changed;
}

/// <summary>
Expand All @@ -233,7 +252,7 @@ public bool TryMoveAssignment(Statement stmDef, SsaIdentifier sidDef, Expression
{
if (CanCoalesce(sidDef, stmDef, stm))
{
Coalesced = true;
coalesced = true;
return CoalesceStatements(sidDef, defExpr, stmDef, stm);
}
else
Expand Down
57 changes: 46 additions & 11 deletions src/Decompiler/Analysis/ComplexStackVariableTransformer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,62 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace Reko.Analysis
namespace Reko.Analysis;

/// <summary>
/// Rewrites expressions like <code>fp +/-offset</code> if offset is
/// inside of one of the specified intervals. In particular it rewrites
/// <code>fp - offset</code> to <code>&amp;tLoc_offset1 + offset2</code>
/// where <code>offset1 - offset2 = offset</code>
/// </summary>
public class ComplexStackVariableTransformer : IAnalysis<SsaState>
{
/// <summary>
/// Rewrites expressions like <code>fp +/-offset</code> if offset is
/// inside of one of the specified intervals. In particular it rewrites
/// <code>fp - offset</code> to <code>&amp;tLoc_offset1 + offset2</code>
/// where <code>offset1 - offset2 = offset</code>
/// </summary>
public class ComplexStackVariableTransformer : InstructionTransformer
private readonly AnalysisContext context;
private readonly ProgramDataFlow programFlow;

public ComplexStackVariableTransformer(AnalysisContext context, ProgramDataFlow programFlow)
{
this.context = context;
this.programFlow = programFlow;
}

public string Id => "csvt";

public string Description => "Converts escaped accesses to memory";

public (SsaState, bool) Transform(SsaState ssa)
{
var efif = new EscapedFrameIntervalsFinder(
context.Program, programFlow, ssa, context.EventListener);
var escapedFrameIntervals = efif.Find();
return Transform(ssa, escapedFrameIntervals);
}

public (SsaState, bool) Transform(SsaState ssa, IntervalTree<int, DataType> frameIntervals)
{
bool changed = false;
if (frameIntervals.Count > 0)
{
var csvt = new Worker( ssa, frameIntervals, context.EventListener);
csvt.Transform();
changed = true;
}
return (ssa, changed);
}

public class Worker : InstructionTransformer
{
private readonly SsaState ssa;
private readonly IntervalTree<int, DataType> escapedFrameIntervals;
private readonly IDecompilerEventListener eventListener;
private readonly IEventListener eventListener;
private readonly Dictionary<int, SsaIdentifier> frameIds;
private Statement stmCur;
private readonly ExpressionEmitter m;

public ComplexStackVariableTransformer(
public Worker(
SsaState ssa,
IntervalTree<int, DataType> escapedFrameIntervals,
IDecompilerEventListener eventListener)
IEventListener eventListener)
{
this.ssa = ssa;
this.escapedFrameIntervals = escapedFrameIntervals;
Expand Down
12 changes: 6 additions & 6 deletions src/Decompiler/Analysis/DataFlowAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ public void BuildExpressionTrees(IReadOnlyCollection<SsaTransform> ssts)
// This ends up being very aggressive and doesn't replicate the original
// binary code. See discussion on https://github.com/uxmal/reko/issues/932
DumpWatchedProcedure("urb", "Before unreachable block removal", ssa.Procedure);
var urb = new UnreachableBlockRemover(ssa, eventListener);
urb.Transform();
var urb = new UnreachableBlockRemover(context);
urb.Transform(ssa);
}

DumpWatchedProcedure("precoa", "Before expression coalescing", ssa.Procedure);
Expand All @@ -244,8 +244,8 @@ public void BuildExpressionTrees(IReadOnlyCollection<SsaTransform> ssts)

// Build expressions. A definition with a single use can be subsumed
// into the using expression.
var coa = new Coalescer(ssa);
coa.Transform();
var coa = new Coalescer(context);
coa.Transform(ssa);
DeadCode.Eliminate(ssa);

var vp = new ValuePropagator(context);
Expand All @@ -272,8 +272,8 @@ public void BuildExpressionTrees(IReadOnlyCollection<SsaTransform> ssts)
DumpWatchedProcedure("sr", "After strength reduction", ssa.Procedure);

// Definitions with multiple uses and variables joined by PHI functions become webs.
var web = new WebBuilder(Program, ssa, Program.InductionVariables, eventListener);
web.Transform();
var web = new WebBuilder(context, Program.InductionVariables);
web.Transform(ssa);

var unssa = new UnSsaTransform(false);
unssa.Transform(ssa);
Expand Down
4 changes: 2 additions & 2 deletions src/Decompiler/Analysis/EscapedFrameIntervalsFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class EscapedFrameIntervalsFinder : ExpressionVisitorBase, InstructionVis
private readonly IReadOnlyProgram program;
private readonly ProgramDataFlow flow;
private readonly SsaState ssa;
private readonly IDecompilerEventListener eventListener;
private readonly IEventListener eventListener;
private Context ctx;
private ExpressionSimplifier eval;
private IntervalTree<int, DataType> intervals;
Expand All @@ -55,7 +55,7 @@ public class EscapedFrameIntervalsFinder : ExpressionVisitorBase, InstructionVis
IReadOnlyProgram program,
ProgramDataFlow flow,
SsaState ssa,
IDecompilerEventListener eventListener)
IEventListener eventListener)
{
this.program = program;
this.flow = flow;
Expand Down
Loading

0 comments on commit 137ae91

Please sign in to comment.