Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into Debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
fb8a453f79
  1. 16
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 2
      ICSharpCode.Decompiler/DecompilerSettings.cs
  3. 61
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  4. 133
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  5. 31
      ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
  6. 16
      ICSharpCode.Decompiler/Tests/ValueTypes.cs

16
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -152,6 +152,12 @@ namespace ICSharpCode.Decompiler.Ast @@ -152,6 +152,12 @@ namespace ICSharpCode.Decompiler.Ast
}
if (tryCatchNode.FinallyBlock != null)
tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock);
if (tryCatchNode.FaultBlock != null) {
CatchClause cc = new CatchClause();
cc.Body = TransformBlock(tryCatchNode.FaultBlock);
cc.Body.Add(new ThrowStatement()); // rethrow
tryCatchStmt.CatchClauses.Add(cc);
}
yield return tryCatchStmt;
} else if (node is ILBlock) {
yield return TransformBlock((ILBlock)node);
@ -534,6 +540,16 @@ namespace ICSharpCode.Decompiler.Ast @@ -534,6 +540,16 @@ namespace ICSharpCode.Decompiler.Ast
}
}
if (cecilMethod.Name == ".ctor" && cecilMethod.DeclaringType.IsValueType) {
// On value types, the constructor can be called.
// This is equivalent to 'target = new ValueType(args);'.
ObjectCreateExpression oce = new ObjectCreateExpression();
oce.Type = AstBuilder.ConvertType(cecilMethod.DeclaringType);
AdjustArgumentsForMethodCall(cecilMethod, methodArgs);
oce.Arguments.AddRange(methodArgs);
return new AssignmentExpression(target, oce);
}
if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
return target.Indexer(methodArgs);
} else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) {

2
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -41,8 +41,6 @@ namespace ICSharpCode.Decompiler @@ -41,8 +41,6 @@ namespace ICSharpCode.Decompiler
}
}
public event EventHandler YieldReturnChanged;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)

61
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -208,9 +208,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -208,9 +208,6 @@ namespace ICSharpCode.Decompiler.ILAst
MethodDefinition methodDef;
bool optimize;
Dictionary<Instruction, ByteCode> instrToByteCode = new Dictionary<Instruction, ByteCode>();
Dictionary<ILVariable, bool> allowInline = new Dictionary<ILVariable, bool>();
// Virtual instructions to load exception on stack
Dictionary<ExceptionHandler, ByteCode> ldexceptions = new Dictionary<ExceptionHandler, ILAstBuilder.ByteCode>();
@ -230,6 +227,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -230,6 +227,8 @@ namespace ICSharpCode.Decompiler.ILAst
List<ByteCode> StackAnalysis(MethodDefinition methodDef)
{
Dictionary<Instruction, ByteCode> instrToByteCode = new Dictionary<Instruction, ByteCode>();
// Create temporary structure for the stack analysis
List<ByteCode> body = new List<ByteCode>(methodDef.Body.Instructions.Count);
List<Instruction> prefixes = null;
@ -413,9 +412,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -413,9 +412,6 @@ namespace ICSharpCode.Decompiler.ILAst
}
pushedBy.StoreTo.Add(tmpVar);
}
if (byteCode.StackBefore[i].PushedBy.Length == 1) {
allowInline[tmpVar] = true;
}
argIdx++;
}
}
@ -507,13 +503,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -507,13 +503,6 @@ namespace ICSharpCode.Decompiler.ILAst
newVars.Add(mergedVar);
}
}
// Permit inlining
foreach(VariableInfo newVar in newVars) {
if (newVar.Stores.Count == 1 && newVar.Loads.Count == 1) {
allowInline[newVar.Variable] = true;
}
}
}
// Set bytecode operands
@ -668,56 +657,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -668,56 +657,12 @@ namespace ICSharpCode.Decompiler.ILAst
} else {
ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true };
ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr));
foreach(ILVariable storeTo in byteCode.StoreTo) {
foreach(ILVariable storeTo in byteCode.StoreTo.AsEnumerable().Reverse()) {
ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar)));
}
}
}
// Try to in-line stloc / ldloc pairs
for(int i = 0; i < ast.Count - 1; i++) {
if (i < 0) continue;
ILExpression currExpr = ast[i] as ILExpression;
ILExpression nextExpr = ast[i + 1] as ILExpression;
if (currExpr != null && nextExpr != null && currExpr.Code == ILCode.Stloc) {
// If the next expression is generated stloc, look inside
if (nextExpr.Code == ILCode.Stloc && ((ILVariable)nextExpr.Operand).IsGenerated) {
nextExpr = nextExpr.Arguments[0];
}
// Find the use of the 'expr'
for(int j = 0; j < nextExpr.Arguments.Count; j++) {
ILExpression arg = nextExpr.Arguments[j];
// 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
// Do not inline ldloca
if (arg.Code == ILCode.Ldloc) {
if (arg.Operand == currExpr.Operand) {
bool canInline;
allowInline.TryGetValue((ILVariable)arg.Operand, out canInline);
if (canInline) {
// Assigne the ranges for optimized away instrustions somewhere
currExpr.Arguments[0].ILRanges.AddRange(currExpr.ILRanges);
currExpr.Arguments[0].ILRanges.AddRange(nextExpr.Arguments[j].ILRanges);
ast.RemoveAt(i);
nextExpr.Arguments[j] = currExpr.Arguments[0]; // Inline the stloc body
i -= 2; // Try the same index again
break; // Found
}
}
} else {
break; // Side-effects
}
}
}
}
return ast;
}
}

133
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -11,6 +11,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -11,6 +11,8 @@ namespace ICSharpCode.Decompiler.ILAst
{
public enum ILAstOptimizationStep
{
SimpleGotoAndNopRemoval,
InlineVariables,
ReduceBranchInstructionSet,
YieldReturn,
SplitToMovableBlocks,
@ -22,6 +24,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -22,6 +24,7 @@ namespace ICSharpCode.Decompiler.ILAst
DuplicateReturns,
FlattenIfStatements,
PeepholeTransforms,
InlineVariables2,
TypeInference,
None
}
@ -34,6 +37,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -34,6 +37,13 @@ namespace ICSharpCode.Decompiler.ILAst
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
{
if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoAndNopRemoval) return;
SimpleGotoAndNopRemoval(method);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return;
// Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X
InlineVariables(method);
if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
ReduceBranchInstructionSet(block);
@ -78,6 +88,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -78,6 +88,7 @@ namespace ICSharpCode.Decompiler.ILAst
FlattenBasicBlocks(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return;
SimpleGotoAndNopRemoval(method);
new GotoRemoval().RemoveGotos(method);
if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return;
@ -89,12 +100,123 @@ namespace ICSharpCode.Decompiler.ILAst @@ -89,12 +100,123 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return;
PeepholeTransforms.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
InlineVariables(method);
if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return;
TypeAnalysis.Run(context, method);
GotoRemoval.RemoveRedundantCode(method);
}
void SimpleGotoAndNopRemoval(ILBlock method)
{
Dictionary<ILLabel, int> labelRefCount = new Dictionary<ILLabel, int>();
foreach (ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets())) {
labelRefCount[target] = labelRefCount.GetOrDefault(target) + 1;
}
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
List<ILNode> body = block.Body;
List<ILNode> newBody = new List<ILNode>(body.Count);
for (int i = 0; i < body.Count; i++) {
ILLabel target;
if (body[i].Match(ILCode.Br, out target) && i+1 < body.Count && body[i+1] == target) {
// Ignore the branch TODO: ILRanges
if (labelRefCount[target] == 1)
i++; // Ignore the label as well
} else if (body[i].Match(ILCode.Nop)){
// Ignore nop TODO: ILRanges
} else {
newBody.Add(body[i]);
}
}
block.Body = newBody;
}
}
void InlineVariables(ILBlock method)
{
// Analyse the whole method
Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloca = new Dictionary<ILVariable, int>();
foreach(ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
ILVariable locVar = expr.Operand as ILVariable;
if (locVar != null) {
if (expr.Code == ILCode.Stloc) {
numStloc[locVar] = numStloc.GetOrDefault(locVar) + 1;
} else if (expr.Code == ILCode.Ldloc) {
numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + 1;
} else if (expr.Code == ILCode.Ldloca) {
numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + 1;
} else {
throw new NotSupportedException(expr.Code.ToString());
}
}
}
// Inline all blocks
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
List<ILNode> body = block.Body;
for(int i = 0; i < body.Count - 1;) {
ILExpression nextExpr = body[i + 1] as ILExpression;
ILVariable locVar;
ILExpression expr;
ILExpression ldParent;
int ldPos;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) &&
numStloc.GetOrDefault(locVar) == 1 &&
numLdloc.GetOrDefault(locVar) == 1 &&
numLdloca.GetOrDefault(locVar) == 0 &&
nextExpr != null &&
FindLdloc(nextExpr, locVar, out ldParent, out ldPos) == true &&
ldParent != null)
{
// Assign the ranges of the optimized instrustions
expr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges);
expr.ILRanges.AddRange(ldParent.Arguments[ldPos].ILRanges);
// 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 ldloc will still return the same value
body.RemoveAt(i);
ldParent.Arguments[ldPos] = expr; // Inline the stloc body
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
}
}
}
}
/// <summary>
/// Finds the position to inline to.
/// </summary>
/// <returns>true = found; false = cannot continue search; null = not found</returns>
static bool? FindLdloc(ILExpression expr, ILVariable v, out ILExpression parent, out int pos)
{
parent = null;
pos = 0;
for (int i = 0; i < expr.Arguments.Count; i++) {
// Stop when seeing an opcode that does not guarantee that its operands will be evaluated
// Inlining in that case migth result in the inlined expresion not being evaluted
if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp))
return false;
ILExpression arg = expr.Arguments[i];
if (arg.Code == ILCode.Ldloc && arg.Operand == v) {
parent = expr;
pos = i;
return true;
}
bool? r = FindLdloc(arg, v, out parent, out pos);
if (r != null)
return r;
}
return expr.Code == ILCode.Ldloc ? (bool?)null : false;
}
/// <summary>
/// Reduces the branch codes to just br and brtrue.
/// Moves ILRanges to the branch argument
@ -136,10 +258,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -136,10 +258,6 @@ namespace ICSharpCode.Decompiler.ILAst
/// </summary>
void SplitToBasicBlocks(ILBlock block)
{
// Remve no-ops
// TODO: Assign the no-op range to someting
block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).Code == ILCode.Nop)).ToList();
List<ILNode> basicBlocks = new List<ILNode>();
ILBasicBlock basicBlock = new ILBasicBlock() {
@ -941,6 +1059,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -941,6 +1059,13 @@ namespace ICSharpCode.Decompiler.ILAst
return true;
}
public static V GetOrDefault<K,V>(this Dictionary<K, V> dict, K key)
{
V ret;
dict.TryGetValue(key, out ret);
return ret;
}
public static void RemoveOrThrow<T>(this ICollection<T> collection, T item)
{
if (!collection.Remove(item))

31
ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

@ -118,8 +118,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -118,8 +118,6 @@ namespace ICSharpCode.Decompiler.ILAst
// the compiler might skip the above instruction in release builds; in that case, it directly returns stloc.Operand
var2 = var1;
}
if (!SkipDummyBr(method, ref i))
return false;
ILExpression retArg;
if (i < method.Body.Count && method.Body[i].Match(ILCode.Ret, out retArg)) {
// ret(ldloc(var_2))
@ -146,17 +144,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -146,17 +144,6 @@ namespace ICSharpCode.Decompiler.ILAst
return method as MethodDefinition;
}
bool SkipDummyBr(ILBlock method, ref int i)
{
ILLabel target;
if (i + 1 < method.Body.Count && method.Body[i].Match(ILCode.Br, out target)) {
if (target != method.Body[i + 1])
return false;
i += 2;
}
return true;
}
bool MatchEnumeratorCreationNewObj(ILExpression expr, out MethodDefinition ctor)
{
// newobj(CurrentType/...::.ctor, ldc.i4(-2))
@ -243,7 +230,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -243,7 +230,7 @@ namespace ICSharpCode.Decompiler.ILAst
StoreToVariable v = new StoreToVariable(new ILExpression(ILCode.Ldfld, ILExpression.AnyOperand, LoadFromArgument.This));
if (v.Match(method.Body[0])) {
int i = 1;
if (SkipDummyBr(method, ref i) && i == method.Body.Count - 1) {
if (i == method.Body.Count - 1) {
if (new ILExpression(ILCode.Ret, null, new LoadFromVariable(v)).Match(method.Body[i])) {
currentField = GetFieldDefinition(((ILExpression)method.Body[0]).Arguments[0].Operand as FieldReference);
}
@ -310,16 +297,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -310,16 +297,14 @@ namespace ICSharpCode.Decompiler.ILAst
foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) {
Interval interval = ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval();
var finallyBody = tryFinally.FinallyBlock.Body;
if (!(finallyBody.Count == 2 || finallyBody.Count == 3))
if (finallyBody.Count != 2)
throw new YieldAnalysisFailedException();
ILExpression call = finallyBody[0] as ILExpression;
if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1)
throw new YieldAnalysisFailedException();
if (call.Arguments[0].Code != ILCode.Ldarg || ((ParameterDefinition)call.Arguments[0].Operand).Index >= 0)
throw new YieldAnalysisFailedException();
if (finallyBody.Count == 3 && !finallyBody[1].Match(ILCode.Nop))
throw new YieldAnalysisFailedException();
if (!finallyBody[finallyBody.Count - 1].Match(ILCode.Endfinally))
if (!finallyBody[1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference);
@ -651,8 +636,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -651,8 +636,6 @@ namespace ICSharpCode.Decompiler.ILAst
if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg))
throw new YieldAnalysisFailedException();
ilMethod.Body.RemoveAll(n => n.Match(ILCode.Nop)); // remove nops
// There are two possibilities:
if (lastReturnArg.Code == ILCode.Ldloc) {
// a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks)
@ -682,7 +665,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -682,7 +665,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILBlock faultBlock = tryFaultBlock.FaultBlock;
// Ensure the fault block contains the call to Dispose().
if (!(faultBlock.Body.Count == 2 || faultBlock.Body.Count == 3))
if (faultBlock.Body.Count != 2)
throw new YieldAnalysisFailedException();
MethodReference disposeMethodRef;
ILExpression disposeArg;
@ -690,13 +673,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -690,13 +673,10 @@ namespace ICSharpCode.Decompiler.ILAst
throw new YieldAnalysisFailedException();
if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !LoadFromArgument.This.Match(disposeArg))
throw new YieldAnalysisFailedException();
if (faultBlock.Body.Count == 3 && !faultBlock.Body[1].Match(ILCode.Nop))
throw new YieldAnalysisFailedException();
if (!faultBlock.Body[faultBlock.Body.Count - 1].Match(ILCode.Endfinally))
if (!faultBlock.Body[1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
body = tryFaultBlock.TryBlock.Body;
body.RemoveAll(n => n.Match(ILCode.Nop)); // remove nops
bodyLength = body.Count;
} else {
// no try-finally blocks
@ -872,7 +852,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -872,7 +852,6 @@ namespace ICSharpCode.Decompiler.ILAst
ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod)
{
ILBlock block = CreateILAst(finallyMethod);
block.Body.RemoveAll(n => n.Match(ILCode.Nop));
// Get rid of assignment to state
FieldReference stfld;
List<ILExpression> args;

16
ICSharpCode.Decompiler/Tests/ValueTypes.cs

@ -9,6 +9,11 @@ public static class ValueTypes @@ -9,6 +9,11 @@ public static class ValueTypes
{
public int Field;
public S(int field)
{
this.Field = field;
}
public void SetField()
{
this.Field = 5;
@ -46,6 +51,17 @@ public static class ValueTypes @@ -46,6 +51,17 @@ public static class ValueTypes
p = default(S);
}
public static S CallValueTypeCtor1()
{
return new S(10);
}
public static S CallValueTypeCtor2()
{
S s = new S(10);
return s;
}
public static S Copy1(S p)
{
return p;

Loading…
Cancel
Save