From 8490b4cfd51502f8f7cb0f61c01469df156ea68f Mon Sep 17 00:00:00 2001 From: Pent Ploompuu Date: Thu, 3 Nov 2011 01:22:48 +0200 Subject: [PATCH] Restrict variable splitting in case of nondeterministic ldloca opcodes. --- ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 59 ++++++++++++++----- .../ILAst/LiftedOperators.cs | 2 +- ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 2 +- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index a2d45f396..4219d3e0a 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -30,10 +30,8 @@ namespace ICSharpCode.Decompiler.ILAst { public class ILAstBuilder { - static ByteCode[] EmptyByteCodeArray = new ByteCode[] {}; - /// Immutable - class StackSlot + sealed class StackSlot { public readonly ByteCode[] PushedBy; // One of those public readonly ILVariable LoadFrom; // Where can we get the value from in AST @@ -61,11 +59,14 @@ namespace ICSharpCode.Decompiler.ILAst } /// Immutable - class VariableSlot + sealed class VariableSlot { public readonly ByteCode[] StoredBy; // One of those public readonly bool StoredByAll; // Overestimate which is useful for exceptional control flow. + static readonly VariableSlot EmptyInstance = new VariableSlot(new ByteCode[0], false); + public static readonly VariableSlot StoredByAllInstance = new VariableSlot(EmptyInstance.StoredBy, true); + public VariableSlot(ByteCode[] storedBy, bool storedByAll) { this.StoredBy = storedBy; @@ -81,9 +82,7 @@ namespace ICSharpCode.Decompiler.ILAst public static VariableSlot[] CloneVariableState(VariableSlot[] state) { VariableSlot[] clone = new VariableSlot[state.Length]; - for (int i = 0; i < clone.Length; i++) { - clone[i] = state[i]; - } + Array.Copy(state, 0, clone, 0, state.Length); return clone; } @@ -91,7 +90,7 @@ namespace ICSharpCode.Decompiler.ILAst { VariableSlot[] emptyVariableState = new VariableSlot[varCount]; for (int i = 0; i < emptyVariableState.Length; i++) { - emptyVariableState[i] = new VariableSlot(EmptyByteCodeArray, false); + emptyVariableState[i] = EmptyInstance; } return emptyVariableState; } @@ -100,13 +99,13 @@ namespace ICSharpCode.Decompiler.ILAst { VariableSlot[] unknownVariableState = new VariableSlot[varCount]; for (int i = 0; i < unknownVariableState.Length; i++) { - unknownVariableState[i] = new VariableSlot(EmptyByteCodeArray, true); + unknownVariableState[i] = StoredByAllInstance; } return unknownVariableState; } } - class ByteCode + sealed class ByteCode { public ILLabel Label; // Non-null only if needed public int Offset; @@ -342,7 +341,8 @@ namespace ICSharpCode.Decompiler.ILAst if (byteCode.Code == ILCode.Stloc || byteCode.Code == ILCode.Ldloca) { int varIndex = ((VariableReference)byteCode.Operand).Index; newVariableState[varIndex] = byteCode.Code == ILCode.Stloc || byteCode.Next.Code == ILCode.Initobj ? - new VariableSlot(byteCode) : new VariableSlot(newVariableState[varIndex].StoredBy.Union(byteCode), false); + new VariableSlot(byteCode) : !newVariableState[varIndex].StoredByAll && IsDeterministicLdloca(byteCode) ? + new VariableSlot(newVariableState[varIndex].StoredBy.Union(byteCode), false) : VariableSlot.StoredByAllInstance; } // After the leave, finally block might have touched the variables @@ -505,8 +505,36 @@ namespace ICSharpCode.Decompiler.ILAst return body; } + + static bool IsDeterministicLdloca(ByteCode b) + { + var v = b.Operand; + b = b.Next; + if (b.Code == ILCode.Initobj) return true; + + // instance method calls on value types use the variable ref deterministically + int stack = 1; + while (true) { + if (b.PopCount == null) return false; + stack -= b.PopCount.GetValueOrDefault(); + if (stack == 0) break; + if (stack < 0) return false; + if (b.Code.IsConditionalControlFlow() || b.Code.IsUnconditionalControlFlow()) return false; + switch (b.Code) { + case ILCode.Ldloc: + case ILCode.Ldloca: + case ILCode.Stloc: + if (b.Operand == v) return false; + break; + } + stack += b.PushCount; + b = b.Next; + if (b == null) return false; + } + return (b.Code == ILCode.Call || b.Code == ILCode.Callvirt) && ((MethodReference)b.Operand).HasThis; + } - class VariableInfo + sealed class VariableInfo { public ILVariable Variable; public List Stores; @@ -528,7 +556,7 @@ namespace ICSharpCode.Decompiler.ILAst // ldloca on an uninitialized variable or followed by initobj isn't considered a load var loads = body.Where(b => (b.Code == ILCode.Ldloc || (b.Code == ILCode.Ldloca && b.Next.Code != ILCode.Initobj && - (b.VariablesBefore[variableIndex].StoredBy.Length != 0 || b.VariablesBefore[variableIndex].StoredByAll))) + (b.VariablesBefore[variableIndex].StoredBy.Length != 0 || b.VariablesBefore[variableIndex].StoredByAll || !IsDeterministicLdloca(b)))) && b.Operand is VariableDefinition && b.OperandAsVariable.Index == variableIndex).ToList(); TypeReference varType = methodDef.Body.Variables[variableIndex].VariableType; @@ -537,7 +565,8 @@ namespace ICSharpCode.Decompiler.ILAst bool isPinned = methodDef.Body.Variables[variableIndex].IsPinned; // If the variable is pinned, use single variable. // If any of the loads is from "all", use single variable - if (isPinned || loads.Any(b => b.VariablesBefore[variableIndex].StoredByAll)) { + // If any of the loads is ldloca with a nondeterministic usage pattern, use single variable + if (isPinned || loads.Any(b => b.VariablesBefore[variableIndex].StoredByAll || (b.Code == ILCode.Ldloca && !IsDeterministicLdloca(b)))) { newVars = new List(1) { new VariableInfo() { Variable = new ILVariable() { Name = "var_" + variableIndex, @@ -838,7 +867,7 @@ namespace ICSharpCode.Decompiler.ILAst if (Array.IndexOf(a, b) >= 0) return a; var res = new T[a.Length + 1]; - Array.Copy(a, res, a.Length); + Array.Copy(a, 0, res, 0, a.Length); res[res.Length - 1] = b; return res; } diff --git a/ICSharpCode.Decompiler/ILAst/LiftedOperators.cs b/ICSharpCode.Decompiler/ILAst/LiftedOperators.cs index 7bef7befe..f4d4109bc 100644 --- a/ICSharpCode.Decompiler/ILAst/LiftedOperators.cs +++ b/ICSharpCode.Decompiler/ILAst/LiftedOperators.cs @@ -515,7 +515,7 @@ namespace ICSharpCode.Decompiler.ILAst { // IL ranges from removed nodes are assigned to the new operator expression var removednodes = expr.GetSelfAndChildrenRecursive().Except(n.GetSelfAndChildrenRecursive()); - n.ILRanges = ILRange.OrderAndJoint(n.ILRanges.Concat(removednodes.SelectMany(el => el.ILRanges))); + n.ILRanges.AddRange(removednodes.SelectMany(el => el.ILRanges)); // the new expression is wrapped in a container so that negations aren't pushed through lifted comparison operations expr.Code = ILCode.Wrap; expr.Arguments.Clear(); diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index de7d2be9a..4aa9e1514 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -799,7 +799,7 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.Dup: { var arg = expr.Arguments.Single(); - return arg.ExpectedType = InferTypeForExpression(expr.Arguments.Single(), expectedType); + return arg.ExpectedType = InferTypeForExpression(arg, expectedType); } default: Debug.WriteLine("Type Inference: Can't handle " + expr.Code.GetName());