Browse Source

Implemented control flow merge points (aka Phi)

pull/10/head
David Srbecký 15 years ago
parent
commit
c3885ad7a1
  1. BIN
      Decompiler/tests/Stack/StackTests.exe
  2. 135
      Decompiler/tests/Stack/StackTests.il
  3. 26
      ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs
  4. 15
      ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs
  5. 398
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  6. 4
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  7. 8
      ICSharpCode.Decompiler/Mono.Cecil.Rocks/MyRocks.cs

BIN
Decompiler/tests/Stack/StackTests.exe

Binary file not shown.

135
Decompiler/tests/Stack/StackTests.il

@ -0,0 +1,135 @@ @@ -0,0 +1,135 @@
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 4:0:0:0
}
.assembly StackTests
{
.hash algorithm 0x00008004
.ver 1:0:4059:39717
}
.module StackTests.exe
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000003 // ILONLY 32BITREQUIRED
.class private auto ansi beforefieldinit StackTests.Program extends [mscorlib]System.Object
{
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 8
ldc.i4.0
call string StackTests.Program::Test1(bool cond)
call void [mscorlib]System.Console::WriteLine(string) // false
ldc.i4.1
call string StackTests.Program::Test1(bool cond)
call void [mscorlib]System.Console::WriteLine(string) // true
ldc.i4.0
ldc.i4.0
ldc.i4.0
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 11
ldc.i4.0
ldc.i4.1
ldc.i4.0
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 21
ldc.i4.1
ldc.i4.1
ldc.i4.1
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 32
ldc.i4.2
ldc.i4.1
ldc.i4.0
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 23
ret
}
.method public hidebysig static string Test1(bool cond) cil managed
{
ldarg.0
brtrue TRUE
FALSE:
ldstr "false"
br EXIT
TRUE:
ldstr "true"
EXIT:
ret
}
.method public hidebysig static int32 Test2(int32 switch1, int32 br1, int32 br2) cil managed
{
ldarg.0
switch (ENTRY1, ENTRY2, ENTRY3)
ldc.i4.0
ret
ENTRY1:
ldc.i4.1
br BRANCH1
ENTRY2:
ldc.i4.2
br BRANCH1
ENTRY3:
ldc.i4.3
br BRANCH2
BRANCH1:
ldarg.1
brtrue BRANCH2
EXIT1:
ldc.i4 10
add
ret
BRANCH2:
ldarg.2
brtrue.s EXIT3
EXIT2:
ldc.i4 20
add
ret
EXIT3:
ldc.i4 30
add
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class StackTests.Program
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file D:\git\ILSpy\Decompiler\tests\Stack\StackTests.res

26
ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Ast = ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Ast;
using Cecil = Mono.Cecil;
@ -21,12 +21,16 @@ namespace Decompiler @@ -21,12 +21,16 @@ namespace Decompiler
{
AstMetodBodyBuilder builder = new AstMetodBodyBuilder();
builder.methodDef = methodDef;
try {
if (Debugger.IsAttached) {
return builder.CreateMethodBody();
} catch {
BlockStatement block = new BlockStatement();
block.Children.Add(MakeComment("Exception during decompilation"));
return block;
} else {
try {
return builder.CreateMethodBody();
} catch {
BlockStatement block = new BlockStatement();
block.Children.Add(MakeComment("// Exception during decompilation"));
return block;
}
}
}
@ -53,7 +57,7 @@ namespace Decompiler @@ -53,7 +57,7 @@ namespace Decompiler
if (methodDef.Body == null) return astBlock;
List<ILNode> body = new ILAstBuilder().Build(methodDef);
List<ILNode> body = new ILAstBuilder().Build(methodDef, true);
MethodBodyGraph bodyGraph = new MethodBodyGraph(body);
bodyGraph.Optimize();
@ -649,8 +653,8 @@ namespace Decompiler @@ -649,8 +653,8 @@ namespace Decompiler
case Code.Ldsflda: throw new NotImplementedException();
case Code.Ldftn: throw new NotImplementedException();
case Code.Ldloc:
if (operand is ILStackVariable) {
return new Ast.IdentifierExpression(((ILStackVariable)operand).Name);
if (operand is ILVariable) {
return new Ast.IdentifierExpression(((ILVariable)operand).Name);
} else {
return new Ast.IdentifierExpression(((VariableDefinition)operand).Name);
}
@ -703,8 +707,8 @@ namespace Decompiler @@ -703,8 +707,8 @@ namespace Decompiler
case Code.Sizeof: throw new NotImplementedException();
case Code.Starg: throw new NotImplementedException();
case Code.Stloc: {
if (operand is ILStackVariable) {
Ast.LocalVariableDeclaration astLocalVar = new Ast.LocalVariableDeclaration(new Ast.VariableDeclaration(((ILStackVariable)operand).Name, arg1));
if (operand is ILVariable) {
Ast.LocalVariableDeclaration astLocalVar = new Ast.LocalVariableDeclaration(new Ast.VariableDeclaration(((ILVariable)operand).Name, arg1));
astLocalVar.TypeReference = new Ast.TypeReference("var");
return astLocalVar;
}

15
ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs

@ -142,10 +142,17 @@ namespace Decompiler.Transforms.Ast @@ -142,10 +142,17 @@ namespace Decompiler.Transforms.Ast
{
// Remove redundant goto which goes to a label that imideately follows
INode fallthoughTarget = GetNextStatement(gotoStatement);
if ((fallthoughTarget is LabelStatement) &&
(fallthoughTarget as LabelStatement).Label == gotoStatement.Label) {
RemoveCurrentNode();
return null;
while(true) {
if (fallthoughTarget is LabelStatement) {
if ((fallthoughTarget as LabelStatement).Label == gotoStatement.Label) {
RemoveCurrentNode();
return null;
} else {
fallthoughTarget = GetNextStatement((LabelStatement)fallthoughTarget);
continue;
}
}
break;
}
// Replace goto with 'break'

398
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -12,27 +12,45 @@ namespace Decompiler @@ -12,27 +12,45 @@ namespace Decompiler
{
public class ILAstBuilder
{
class ILStack
class StackSlot
{
public class Slot
public List<ByteCode> PushedBy; // Pushed by one of these; null element means exception pushed by CLR
public ILVariable LoadFrom;
public StackSlot()
{
public Instruction PushedBy;
public TypeReference Type;
public Slot(Instruction inst, TypeReference type)
{
this.PushedBy = inst;
this.Type = type;
}
}
public List<Slot> Items = new List<Slot>();
public StackSlot(ByteCode pushedBy)
{
this.PushedBy = new List<ByteCode>(1);
this.PushedBy.Add(pushedBy);
}
}
class ByteCode
{
public ILLabel Label; // Non-null only if needed
public int Offset;
public OpCode OpCode;
public object Operand;
public int? PopCount; // Null means pop all
public int PushCount;
public string Name { get { return "IL_" + this.Offset.ToString("X2"); } }
public ByteCode Next;
public List<StackSlot> StackBefore;
public List<ILVariable> StoreTo;
public ILStack Clone()
public List<StackSlot> CloneStack(int? popCount)
{
ILStack clone = new ILStack();
foreach(Slot s in this.Items) {
clone.Items.Add(new Slot(s.PushedBy, s.Type));
List<StackSlot> clone = new List<StackSlot>();
if (popCount.HasValue) {
if (popCount.Value > this.StackBefore.Count) {
throw new Exception("Can not pop - the stack is empty");
}
for(int i = 0; i < this.StackBefore.Count - popCount.Value; i++) {
clone.Add(new StackSlot() { PushedBy = new List<ByteCode>(this.StackBefore[i].PushedBy) });
}
}
return clone;
}
@ -40,123 +58,237 @@ namespace Decompiler @@ -40,123 +58,237 @@ namespace Decompiler
public override string ToString()
{
StringBuilder sb = new StringBuilder();
bool first = true;
foreach (Slot s in this.Items) {
if (!first) sb.Append(", ");
sb.Append(s.PushedBy.Offset.ToString("X"));
first = false;
sb.AppendFormat("{0}:{1} {2} {3}", this.Name, this.Label != null ? " *" : "", this.OpCode, this.Operand);
if (this.StackBefore != null) {
sb.Append(" StackBefore = {");
bool first = true;
foreach (StackSlot slot in this.StackBefore) {
if (!first) sb.Append(",");
bool first2 = true;
foreach(ByteCode pushedBy in slot.PushedBy) {
if (!first2) sb.Append("|");
sb.AppendFormat("IL_{0:X2}", pushedBy.Offset);
first2 = false;
}
first = false;
}
sb.Append("}");
}
if (this.StoreTo != null && this.StoreTo.Count > 0) {
sb.Append(" StoreTo = {");
bool first = true;
foreach (ILVariable stackVar in this.StoreTo) {
if (!first) sb.Append(",");
sb.Append(stackVar.Name);
first = false;
}
sb.Append("}");
}
return sb.ToString();
}
}
MethodDefinition methodDef;
Dictionary<Instruction, ILStack> stackBefore = new Dictionary<Instruction, ILAstBuilder.ILStack>();
Dictionary<Instruction, ILLabel> labels = new Dictionary<Instruction, ILLabel>();
bool optimize;
public List<ILNode> Build(MethodDefinition methodDef)
Dictionary<Instruction, ByteCode> instrToByteCode = new Dictionary<Instruction, ByteCode>();
Dictionary<ILVariable, bool> allowInline = new Dictionary<ILVariable, bool>();
public List<ILNode> Build(MethodDefinition methodDef, bool optimize)
{
this.methodDef = methodDef;
this.optimize = optimize;
// Make editable copy
List<Instruction> body = new List<Instruction>(methodDef.Body.Instructions);
if (body.Count == 0) return new List<ILNode>();
if (methodDef.Body.Instructions.Count == 0) return new List<ILNode>();
StackAnalysis(body, methodDef);
// Create branch labels for instructins; use the labels as branch operands
foreach (Instruction inst in body) {
if (inst.Operand is Instruction[]) {
foreach(Instruction target in (Instruction[])inst.Operand) {
if (!labels.ContainsKey(target)) {
labels[target] = new ILLabel() { Name = "IL_" + target.Offset.ToString("X2") };
}
}
} else if (inst.Operand is Instruction) {
Instruction target = (Instruction)inst.Operand;
if (!labels.ContainsKey(target)) {
labels[target] = new ILLabel() { Name = "IL_" + target.Offset.ToString("X2") };
}
}
}
List<ByteCode> body = StackAnalysis(methodDef);
List<ILNode> ast = ConvertToAst(body, methodDef.Body.ExceptionHandlers);
return ast;
}
public void StackAnalysis(List<Instruction> body, MethodDefinition methodDef)
List<ByteCode> StackAnalysis(MethodDefinition methodDef)
{
Queue<Instruction> agenda = new Queue<Instruction>();
// Create temporary structure for the stack analysis
List<ByteCode> body = new List<ByteCode>(methodDef.Body.Instructions.Count);
foreach(Instruction inst in methodDef.Body.Instructions) {
OpCode opCode = inst.OpCode;
object operand = inst.Operand;
MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body);
ByteCode byteCode = new ByteCode() {
Offset = inst.Offset,
OpCode = opCode,
Operand = operand,
PopCount = inst.GetPopCount(),
PushCount = inst.GetPushCount()
};
instrToByteCode[inst] = byteCode;
body.Add(byteCode);
}
for (int i = 0; i < body.Count - 1; i++) {
body[i].Next = body[i + 1];
}
Queue<ByteCode> agenda = new Queue<ByteCode>();
// Add known states
stackBefore[body[0]] = new ILStack();
body[0].StackBefore = new List<StackSlot>();
agenda.Enqueue(body[0]);
if(methodDef.Body.HasExceptionHandlers) {
foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) {
stackBefore[ex.TryStart] = new ILStack();
agenda.Enqueue(ex.TryStart);
ByteCode tryStart = instrToByteCode[ex.TryStart];
tryStart.StackBefore = new List<StackSlot>();
agenda.Enqueue(tryStart);
ILStack stack = new ILStack();
stack.Items.Add(new ILStack.Slot(null, MyRocks.TypeException));
stackBefore[ex.HandlerStart] = stack;
agenda.Enqueue(ex.HandlerStart);
ByteCode handlerStart = instrToByteCode[ex.HandlerType == ExceptionHandlerType.Filter ? ex.FilterStart : ex.HandlerStart];
handlerStart.StackBefore = new List<StackSlot>();
if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) {
handlerStart.StackBefore.Add(new StackSlot(null));
}
agenda.Enqueue(handlerStart);
// Control flow is not required to reach endfilter
if (ex.HandlerType == ExceptionHandlerType.Filter) {
ByteCode endFilter = instrToByteCode[ex.FilterEnd.Previous];
endFilter.StackBefore = new List<StackSlot>();
}
}
}
// Process agenda
while(agenda.Count > 0) {
Instruction inst = agenda.Dequeue();
ByteCode byteCode = agenda.Dequeue();
// What is the effect of the instruction on the stack?
ILStack newStack = stackBefore[inst].Clone();
int popCount = inst.GetPopCount();
if (popCount == int.MaxValue) popCount = stackBefore[inst].Items.Count; // Pop all
List<TypeReference> typeArgs = new List<TypeReference>();
for (int i = newStack.Items.Count - popCount; i < newStack.Items.Count; i++) {
typeArgs.Add(newStack.Items[i].Type);
}
TypeReference type;
try {
type = inst.GetTypeInternal(methodDef, typeArgs);
} catch {
type = MyRocks.TypeObject;
}
if (popCount > 0) {
newStack.Items.RemoveRange(newStack.Items.Count - popCount, popCount);
}
int pushCount = inst.GetPushCount();
for (int i = 0; i < pushCount; i++) {
newStack.Items.Add(new ILStack.Slot(inst, type));
// Calculate new stack
List<StackSlot> newStack = byteCode.CloneStack(byteCode.PopCount);
for (int i = 0; i < byteCode.PushCount; i++) {
newStack.Add(new StackSlot(byteCode));
}
// Apply the state to any successors
List<Instruction> branchTargets = new List<Instruction>();
if (inst.OpCode.CanFallThough()) {
branchTargets.Add(inst.Next);
List<ByteCode> branchTargets = new List<ByteCode>();
if (byteCode.OpCode.CanFallThough()) {
branchTargets.Add(byteCode.Next);
}
if (inst.OpCode.IsBranch()) {
if (inst.Operand is Instruction[]) {
branchTargets.AddRange((Instruction[])inst.Operand);
if (byteCode.OpCode.IsBranch()) {
if (byteCode.Operand is Instruction[]) {
foreach(Instruction inst in (Instruction[])byteCode.Operand) {
ByteCode target = instrToByteCode[inst];
branchTargets.Add(target);
// The target of a branch must have label
if (target.Label == null) {
target.Label = new ILLabel() { Name = target.Name };
}
}
} else {
branchTargets.Add((Instruction)inst.Operand);
ByteCode target = instrToByteCode[(Instruction)byteCode.Operand];
branchTargets.Add(target);
// The target of a branch must have label
if (target.Label == null) {
target.Label = new ILLabel() { Name = target.Name };
}
}
}
foreach (Instruction branchTarget in branchTargets) {
ILStack nextStack;
if (stackBefore.TryGetValue(branchTarget, out nextStack)) {
// TODO: Compare stacks
} else {
stackBefore[branchTarget] = newStack;
foreach (ByteCode branchTarget in branchTargets) {
if (branchTarget.StackBefore == null) {
branchTarget.StackBefore = newStack;
// Do not share one stack for several bytecodes
if (branchTargets.Count > 1) {
branchTarget.StackBefore = branchTarget.CloneStack(0);
}
agenda.Enqueue(branchTarget);
} else {
if (branchTarget.StackBefore.Count != newStack.Count) {
throw new Exception("Inconsistent stack size at " + byteCode.Name);
}
// Merge stacks
bool modified = false;
for (int i = 0; i < newStack.Count; i++) {
List<ByteCode> oldPushedBy = branchTarget.StackBefore[i].PushedBy;
List<ByteCode> newPushedBy = oldPushedBy.Union(newStack[i].PushedBy).ToList();
if (newPushedBy.Count > oldPushedBy.Count) {
branchTarget.StackBefore[i].PushedBy = newPushedBy;
modified = true;
}
}
if (modified) {
agenda.Enqueue(branchTarget);
}
}
}
}
// Genertate temporary variables to replace stack
foreach(ByteCode byteCode in body) {
int argIdx = 0;
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count;
for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) {
StackSlot arg = byteCode.StackBefore[i];
ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true };
arg.LoadFrom = tmpVar;
foreach(ByteCode pushedBy in arg.PushedBy) {
// TODO: Handle exception variables
if (pushedBy != null) {
if (pushedBy.StoreTo == null) {
pushedBy.StoreTo = new List<ILVariable>(1);
}
pushedBy.StoreTo.Add(tmpVar);
}
}
if (arg.PushedBy.Count == 1) {
allowInline[tmpVar] = true;
}
argIdx++;
}
}
// Convert local varibles
List<ILVariable> vars = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ? "var_" + v.Index : v.Name }).ToList();
int[] numReads = new int[vars.Count];
int[] numWrites = new int[vars.Count];
foreach(ByteCode byteCode in body) {
if (byteCode.OpCode == OpCodes.Ldloc) {
int index = ((VariableDefinition)byteCode.Operand).Index;
byteCode.Operand = vars[index];
numReads[index]++;
}
if (byteCode.OpCode == OpCodes.Stloc) {
int index = ((VariableDefinition)byteCode.Operand).Index;
byteCode.Operand = vars[index];
numWrites[index]++;
}
}
// Find which variables we can inline
if (this.optimize) {
for (int i = 0; i < vars.Count; i++) {
if (numReads[i] == 1 && numWrites[i] == 1) {
allowInline[vars[i]] = true;
}
}
}
// Convert branch targets to labels
foreach(ByteCode byteCode in body) {
if (byteCode.Operand is Instruction[]) {
List<ILLabel> newOperand = new List<ILLabel>();
foreach(Instruction target in (Instruction[])byteCode.Operand) {
newOperand.Add(instrToByteCode[target].Label);
}
byteCode.Operand = newOperand.ToArray();
} else if (byteCode.Operand is Instruction) {
byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label;
}
}
return body;
}
public List<ILNode> ConvertToAst(List<Instruction> body, IEnumerable<ExceptionHandler> ehs)
List<ILNode> ConvertToAst(List<ByteCode> body, IEnumerable<ExceptionHandler> ehs)
{
List<ILNode> ast = new List<ILNode>();
@ -187,9 +319,9 @@ namespace Decompiler @@ -187,9 +319,9 @@ namespace Decompiler
tryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>();
foreach(ExceptionHandler eh in handlers) {
int start;
for (start = 0; body[start] != eh.HandlerStart; start++);
for (start = 0; body[start].Offset != eh.HandlerStart.Offset; start++);
int end;
for (end = 0; body[end] != eh.HandlerEnd; end++);
for (end = 0; body[end].Offset != eh.HandlerEnd.Offset; end++);
int count = end - start;
List<ExceptionHandler> nestedEHs = ehs.Where(e => (start <= e.TryStart.Offset && e.TryEnd.Offset < end) || (start < e.TryStart.Offset && e.TryEnd.Offset <= end)).ToList();
List<ILNode> handlerAst = ConvertToAst(body.CutRange(start, count), nestedEHs);
@ -216,73 +348,62 @@ namespace Decompiler @@ -216,73 +348,62 @@ namespace Decompiler
return ast;
}
public List<ILNode> ConvertToAst(List<Instruction> body)
List<ILNode> ConvertToAst(List<ByteCode> body)
{
List<ILNode> ast = new List<ILNode>();
// Convert stack-based IL code to ILAst tree
foreach(Instruction inst in body) {
OpCode opCode = inst.OpCode;
object operand = inst.Operand;
foreach(ByteCode byteCode in body) {
OpCode opCode = byteCode.OpCode;
object operand = byteCode.Operand;
MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body);
ILExpression expr = new ILExpression(opCode, operand);
// Label for this instruction
ILLabel label;
if (labels.TryGetValue(inst, out label)) {
ast.Add(label);
}
// Branch using labels
if (inst.Operand is Instruction[]) {
List<ILLabel> newOperand = new List<ILLabel>();
foreach(Instruction target in (Instruction[])inst.Operand) {
newOperand.Add(labels[target]);
}
expr.Operand = newOperand.ToArray();
} else if (inst.Operand is Instruction) {
expr.Operand = labels[(Instruction)inst.Operand];
if (byteCode.Label != null) {
ast.Add(byteCode.Label);
}
// Reference arguments using temporary variables
ILStack stack = stackBefore[inst];
int popCount = inst.GetPopCount();
if (popCount == int.MaxValue) popCount = stackBefore[inst].Items.Count; // Pop all
for (int i = stack.Items.Count - popCount; i < stack.Items.Count; i++) {
Instruction pushedBy = stack.Items[i].PushedBy;
if (pushedBy != null) {
ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new ILStackVariable() { Name = "expr" + pushedBy.Offset.ToString("X2") });
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count;
for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) {
StackSlot slot = byteCode.StackBefore[i];
if (slot.PushedBy != null) {
ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, slot.LoadFrom);
expr.Arguments.Add(ldExpr);
} else {
ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new ILStackVariable() { Name = "exception" });
ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new ILVariable() { Name = "ex", IsGenerated = true });
expr.Arguments.Add(ldExpr);
}
}
// If the bytecode pushes anything store the result in temporary variable
int pushCount = inst.GetPushCount();
if (pushCount > 0) {
ILExpression stExpr = new ILExpression(OpCodes.Stloc, new ILStackVariable() { Name = "expr" + inst.Offset.ToString("X2"), RefCount = pushCount });
stExpr.Arguments.Add(expr);
expr = stExpr;
// Store the result to temporary variable(s) if needed
if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0) {
ast.Add(expr);
} else if (byteCode.StoreTo.Count == 1) {
ast.Add(new ILExpression(OpCodes.Stloc, byteCode.StoreTo[0], expr));
} else {
ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true };
ast.Add(new ILExpression(OpCodes.Stloc, tmpVar, expr));
foreach(ILVariable storeTo in byteCode.StoreTo) {
ast.Add(new ILExpression(OpCodes.Stloc, storeTo, new ILExpression(OpCodes.Ldloc, tmpVar)));
}
}
ast.Add(expr);
}
// Try to in-line stloc / ldloc pairs
for(int i = 0; i < ast.Count - 1; i++) {
if (i < 0) continue;
ILExpression expr = ast[i] as ILExpression;
ILExpression currExpr = ast[i] as ILExpression;
ILExpression nextExpr = ast[i + 1] as ILExpression;
if (expr != null && nextExpr != null && expr.OpCode.Code == Code.Stloc && expr.Operand is ILStackVariable) {
if (currExpr != null && nextExpr != null && currExpr.OpCode.Code == Code.Stloc) {
// If the next expression is stloc, look inside
if (nextExpr.OpCode.Code == Code.Stloc && nextExpr.Operand is ILStackVariable) {
// If the next expression is generated stloc, look inside
if (nextExpr.OpCode.Code == Code.Stloc && ((ILVariable)nextExpr.Operand).IsGenerated) {
nextExpr = nextExpr.Arguments[0];
}
@ -290,22 +411,19 @@ namespace Decompiler @@ -290,22 +411,19 @@ namespace Decompiler
for(int j = 0; j < nextExpr.Arguments.Count; j++) {
ILExpression arg = nextExpr.Arguments[j];
// TODO: Check if duplicating the dup opcode has side-effects
if (arg.OpCode.Code == Code.Ldloc && arg.Operand is ILStackVariable) {
ILStackVariable stVar = (ILStackVariable)expr.Operand;
ILStackVariable ldVar = (ILStackVariable)arg.Operand;
if (stVar.Name == ldVar.Name) {
stVar.RefCount--;
if (stVar.RefCount <= 0) {
ast.RemoveAt(i);
}
nextExpr.Arguments[j] = expr.Arguments[0]; // Inline the stloc body
// We are moving the expression evaluation past the other aguments.
// It is ok to pass ldloc because the expression can not contain stloc and thus the ldcoc will still return the same value
if (arg.OpCode.Code == Code.Ldloc) {
bool canInline;
allowInline.TryGetValue((ILVariable)arg.Operand, out canInline);
if (arg.Operand == currExpr.Operand && canInline) {
ast.RemoveAt(i);
nextExpr.Arguments[j] = currExpr.Arguments[0]; // Inline the stloc body
i -= 2; // Try the same index again
break; // Found
}
} else {
break; // This argument might have side effects so we can not move the 'expr' after it.
break; // Side-effects
}
}
}

4
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -41,10 +41,10 @@ namespace Decompiler @@ -41,10 +41,10 @@ namespace Decompiler
}
}
public class ILStackVariable
public class ILVariable
{
public string Name;
public int RefCount;
public bool IsGenerated;
public override string ToString()
{

8
ICSharpCode.Decompiler/Mono.Cecil.Rocks/MyRocks.cs

@ -43,7 +43,7 @@ namespace Decompiler.Mono.Cecil.Rocks @@ -43,7 +43,7 @@ namespace Decompiler.Mono.Cecil.Rocks
case FlowControl.Call: return true;
case FlowControl.Return: return false;
case FlowControl.Throw: return false;
case FlowControl.Meta: return false;
case FlowControl.Meta: return true;
default: throw new NotImplementedException();
}
}
@ -53,7 +53,7 @@ namespace Decompiler.Mono.Cecil.Rocks @@ -53,7 +53,7 @@ namespace Decompiler.Mono.Cecil.Rocks
return opCode.FlowControl == FlowControl.Branch || opCode.FlowControl == FlowControl.Cond_Branch;
}
public static int GetPopCount(this Instruction inst)
public static int? GetPopCount(this Instruction inst)
{
switch(inst.OpCode.StackBehaviourPop) {
case StackBehaviour.Pop0: return 0;
@ -74,7 +74,7 @@ namespace Decompiler.Mono.Cecil.Rocks @@ -74,7 +74,7 @@ namespace Decompiler.Mono.Cecil.Rocks
case StackBehaviour.Popref_popi_popr4: return 3;
case StackBehaviour.Popref_popi_popr8: return 3;
case StackBehaviour.Popref_popi_popref: return 3;
case StackBehaviour.PopAll: return int.MaxValue;
case StackBehaviour.PopAll: return null;
case StackBehaviour.Varpop:
switch(inst.OpCode.Code) {
case Code.Call:
@ -86,7 +86,7 @@ namespace Decompiler.Mono.Cecil.Rocks @@ -86,7 +86,7 @@ namespace Decompiler.Mono.Cecil.Rocks
return cecilMethod.Parameters.Count;
}
case Code.Calli: throw new NotImplementedException();
case Code.Ret: return int.MaxValue;
case Code.Ret: return null;
case Code.Newobj:
MethodReference ctorMethod = ((MethodReference)inst.Operand);
return ctorMethod.Parameters.Count;

Loading…
Cancel
Save