Browse Source

Support 'state + c1 < c2' in state-range-analysis.

pull/734/merge
Daniel Grunwald 8 years ago
parent
commit
173c8d353f
  1. 19
      ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs
  2. 98
      ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs
  3. 37
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  4. 12
      ICSharpCode.Decompiler/Util/Interval.cs

19
ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs

@ -134,25 +134,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return new LongSet(exitIntervals); return new LongSet(exitIntervals);
case IfInstruction ifInst: case IfInstruction ifInst:
val = evalContext.Eval(ifInst.Condition).AsBool(); val = evalContext.Eval(ifInst.Condition).AsBool();
LongSet trueRanges; if (val.Type != SymbolicValueType.StateInSet) {
if (val.Type == SymbolicValueType.StateEquals) {
trueRanges = new LongSet(val.Constant);
} else if (val.Type == SymbolicValueType.StateInEquals) {
trueRanges = new LongSet(val.Constant).Invert();
} else if (val.Type == SymbolicValueType.StateLessThan) {
// note: val.Constant is of type int, so it can't be equal to long.MinValue,
// which would cause problems.
trueRanges = new LongSet(new LongInterval(long.MinValue, val.Constant));
} else if (val.Type == SymbolicValueType.StateLessEqual) {
trueRanges = new LongSet(LongInterval.Inclusive(long.MinValue, val.Constant));
} else if (val.Type == SymbolicValueType.StateGreaterThan) {
// note: val.Constant is of type int, so the addition can't overflow.
trueRanges = new LongSet(LongInterval.Inclusive(val.Constant + 1L, long.MaxValue));
} else if (val.Type == SymbolicValueType.StateGreaterEqual) {
trueRanges = new LongSet(LongInterval.Inclusive(val.Constant, long.MaxValue));
} else {
goto default; goto default;
} }
LongSet trueRanges = val.ValueSet;
var afterTrue = AssignStateRanges(ifInst.TrueInst, stateRange.IntersectWith(trueRanges)); var afterTrue = AssignStateRanges(ifInst.TrueInst, stateRange.IntersectWith(trueRanges));
var afterFalse = AssignStateRanges(ifInst.FalseInst, stateRange.ExceptWith(trueRanges)); var afterFalse = AssignStateRanges(ifInst.FalseInst, stateRange.ExceptWith(trueRanges));
return afterTrue.UnionWith(afterFalse); return afterTrue.UnionWith(afterFalse);

98
ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs

@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -54,35 +55,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// </summary> /// </summary>
This, This,
/// <summary> /// <summary>
/// bool: State == Constant /// bool: ValueSet.Contains(State)
/// </summary> /// </summary>
StateEquals, StateInSet,
/// <summary>
/// bool: State != Constant
/// </summary>
StateInEquals,
/// <summary>
/// bool: State &lt; Constant
/// </summary>
StateLessThan,
/// <summary>
/// bool: State &lt;= Constant
/// </summary>
StateLessEqual,
/// <summary>
/// bool: State &gt; Constant
/// </summary>
StateGreaterThan,
/// <summary>
/// bool: State &gt;= Constant
/// </summary>
StateGreaterEqual
} }
struct SymbolicValue struct SymbolicValue
{ {
public readonly int Constant; public readonly int Constant;
public readonly SymbolicValueType Type; public readonly SymbolicValueType Type;
public readonly LongSet ValueSet;
public SymbolicValue(SymbolicValueType type, int constant = 0) public SymbolicValue(SymbolicValueType type, int constant = 0)
{ {
@ -90,12 +72,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
this.Constant = constant; this.Constant = constant;
} }
public SymbolicValue(SymbolicValueType type, LongSet valueSet)
{
this.Type = type;
this.Constant = 0;
this.ValueSet = valueSet;
}
public SymbolicValue AsBool() public SymbolicValue AsBool()
{ {
if (Type == SymbolicValueType.State) { if (Type == SymbolicValueType.State) {
// convert state integer to bool: // convert state integer to bool:
// if (state + c) = if (state + c != 0) = if (state != -c) // if (state + c) = if (state + c != 0) = if (state != -c)
return new SymbolicValue(SymbolicValueType.StateInEquals, unchecked(-Constant)); return new SymbolicValue(SymbolicValueType.StateInSet, new LongSet(unchecked(-Constant)).Invert());
} }
return this; return this;
} }
@ -154,39 +143,42 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant) if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant)
return Failed; return Failed;
// bool: (state + left.Constant == right.Constant) // bool: (state + left.Constant == right.Constant)
// bool: (state == right.Constant - left.Constant) LongSet trueSums; // evals to true if trueSums.Contains(state + left.Constant)
if (comp.Kind == ComparisonKind.Equality) switch (comp.Kind) {
return new SymbolicValue(SymbolicValueType.StateEquals, unchecked(right.Constant - left.Constant)); case ComparisonKind.Equality:
else if (comp.Kind == ComparisonKind.Inequality) trueSums = new LongSet(right.Constant);
return new SymbolicValue(SymbolicValueType.StateInEquals, unchecked(right.Constant - left.Constant)); break;
else if (comp.Kind == ComparisonKind.LessThan && left.Constant == 0) case ComparisonKind.Inequality:
return new SymbolicValue(SymbolicValueType.StateLessThan, right.Constant); trueSums = new LongSet(right.Constant).Invert();
else if (comp.Kind == ComparisonKind.LessThanOrEqual && left.Constant == 0) break;
return new SymbolicValue(SymbolicValueType.StateLessEqual, right.Constant); case ComparisonKind.LessThan:
else if (comp.Kind == ComparisonKind.GreaterThan && left.Constant == 0) // note: right.Constant is of type int, so it can't be equal to long.MinValue,
return new SymbolicValue(SymbolicValueType.StateGreaterThan, right.Constant); // which would cause problems.
else if (comp.Kind == ComparisonKind.GreaterThanOrEqual && left.Constant == 0) trueSums = new LongSet(new LongInterval(long.MinValue, right.Constant));
return new SymbolicValue(SymbolicValueType.StateGreaterEqual, right.Constant); break;
else case ComparisonKind.LessThanOrEqual:
return Failed; trueSums = new LongSet(LongInterval.Inclusive(long.MinValue, right.Constant));
} else if (inst is LogicNot logicNot) { break;
SymbolicValue val = Eval(logicNot.Argument).AsBool(); case ComparisonKind.GreaterThan:
switch (val.Type) { // note: val.Constant is of type int, so the addition can't overflow.
case SymbolicValueType.StateEquals: trueSums = new LongSet(LongInterval.Inclusive(right.Constant + 1L, long.MaxValue));
return new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant); break;
case SymbolicValueType.StateInEquals: case ComparisonKind.GreaterThanOrEqual:
return new SymbolicValue(SymbolicValueType.StateEquals, val.Constant); trueSums = new LongSet(LongInterval.Inclusive(right.Constant, long.MaxValue));
case SymbolicValueType.StateLessThan: break;
return new SymbolicValue(SymbolicValueType.StateGreaterEqual, val.Constant);
case SymbolicValueType.StateLessEqual:
return new SymbolicValue(SymbolicValueType.StateGreaterThan, val.Constant);
case SymbolicValueType.StateGreaterThan:
return new SymbolicValue(SymbolicValueType.StateLessEqual, val.Constant);
case SymbolicValueType.StateGreaterEqual:
return new SymbolicValue(SymbolicValueType.StateLessThan, val.Constant);
default: default:
return Failed; return Failed;
} }
LongSet trueStates = trueSums.AddOffset(unchecked(-left.Constant));
// evals to true if trueStates.Contains(state)
return new SymbolicValue(SymbolicValueType.StateInSet, trueStates);
} else if (inst is LogicNot logicNot) {
SymbolicValue val = Eval(logicNot.Argument).AsBool();
if (val.Type == SymbolicValueType.StateInSet) {
return new SymbolicValue(SymbolicValueType.StateInSet, val.ValueSet.Invert());
} else {
return Failed;
}
} else { } else {
return Failed; return Failed;
} }

37
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -409,16 +409,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
#region Analyze MoveNext() and generate new body #region Analyze MoveNext() and generate new body
BlockContainer AnalyzeMoveNext() BlockContainer AnalyzeMoveNext()
{ {
context.Stepper.StartGroup("AnalyzeMoveNext");
MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext"); MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext");
ILFunction moveNextFunction = CreateILAst(moveNextMethod); ILFunction moveNextFunction = CreateILAst(moveNextMethod);
// Copy-propagate temporaries holding a copy of 'this'. // Copy-propagate temporaries holding a copy of 'this'.
// This is necessary because the old (pre-Roslyn) C# compiler likes to store 'this' in temporary variables. // This is necessary because the old (pre-Roslyn) C# compiler likes to store 'this' in temporary variables.
context.Stepper.StartGroup("AnalyzeMoveNext");
foreach (var stloc in moveNextFunction.Descendants.OfType<StLoc>().Where(s => s.Variable.IsSingleDefinition && s.Value.MatchLdThis()).ToList()) { foreach (var stloc in moveNextFunction.Descendants.OfType<StLoc>().Where(s => s.Variable.IsSingleDefinition && s.Value.MatchLdThis()).ToList()) {
CopyPropagation.Propagate(stloc, context); CopyPropagation.Propagate(stloc, context);
} }
context.Stepper.EndGroup();
var body = (BlockContainer)moveNextFunction.Body; var body = (BlockContainer)moveNextFunction.Body;
if (body.Blocks.Count == 1 && body.Blocks[0].Instructions.Count == 1 && body.Blocks[0].Instructions[0] is TryFault tryFault) { if (body.Blocks.Count == 1 && body.Blocks[0].Instructions.Count == 1 && body.Blocks[0].Instructions[0] is TryFault tryFault) {
@ -437,6 +436,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
} }
PropagateCopiesOfFields(body);
// Note: body may contain try-catch or try-finally statements that have nested block containers, // Note: body may contain try-catch or try-finally statements that have nested block containers,
// but those cannot contain any yield statements. // but those cannot contain any yield statements.
// So for reconstructing the control flow, we only consider the blocks directly within body. // So for reconstructing the control flow, we only consider the blocks directly within body.
@ -448,9 +449,41 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
moveNextFunction.Variables.Clear(); moveNextFunction.Variables.Clear();
// release references from old moveNextFunction to instructions that were moved over to newBody // release references from old moveNextFunction to instructions that were moved over to newBody
moveNextFunction.ReleaseRef(); moveNextFunction.ReleaseRef();
context.Stepper.EndGroup();
return newBody; return newBody;
} }
private void PropagateCopiesOfFields(BlockContainer body)
{
// Roslyn may optimize MoveNext() by copying fields from the iterator class into local variables
// at the beginning of MoveNext(). Undo this optimization.
context.Stepper.StartGroup("PropagateCopiesOfFields");
var mutableFields = body.Descendants.OfType<LdFlda>().Where(ldflda => ldflda.Parent.OpCode != OpCode.LdObj).Select(ldflda => ldflda.Field).ToHashSet();
for (int i = 0; i < body.EntryPoint.Instructions.Count; i++) {
if (body.EntryPoint.Instructions[i] is StLoc store
&& store.Variable.IsSingleDefinition
&& store.Value is LdObj ldobj
&& ldobj.Target is LdFlda ldflda
&& ldflda.Target.MatchLdThis())
{
if (!mutableFields.Contains(ldflda.Field)) {
// perform copy propagation: (unlike CopyPropagation.Propagate(), copy the ldobj arguments as well)
foreach (var expr in store.Variable.LoadInstructions.ToArray()) {
expr.ReplaceWith(store.Value.Clone());
}
body.EntryPoint.Instructions.RemoveAt(i--);
} else if (ldflda.Field.MemberDefinition == stateField.MemberDefinition) {
continue;
} else {
break; // unsupported: load of mutable field (other than state field)
}
} else {
break; // unknown instruction
}
}
context.Stepper.EndGroup();
}
/// <summary> /// <summary>
/// Convert the old body (of MoveNext function) to the new body (of decompiled iterator method). /// Convert the old body (of MoveNext function) to the new body (of decompiled iterator method).
/// ///

12
ICSharpCode.Decompiler/Util/Interval.cs

@ -259,10 +259,16 @@ namespace ICSharpCode.Decompiler.Util
public override string ToString() public override string ToString()
{ {
if (End == long.MinValue) if (End == long.MinValue) {
return string.Format("[{0}..long.MaxValue]", Start); if (Start == long.MinValue)
else return string.Format("[long.MinValue..long.MaxValue]", End);
else
return string.Format("[{0}..long.MaxValue]", Start);
} else if (Start == long.MinValue) {
return string.Format("[long.MinValue..{0})", End);
} else {
return string.Format("[{0}..{1})", Start, End); return string.Format("[{0}..{1})", Start, End);
}
} }
#region Equals and GetHashCode implementation #region Equals and GetHashCode implementation

Loading…
Cancel
Save