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 @@ -134,25 +134,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return new LongSet(exitIntervals);
case IfInstruction ifInst:
val = evalContext.Eval(ifInst.Condition).AsBool();
LongSet trueRanges;
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 {
if (val.Type != SymbolicValueType.StateInSet) {
goto default;
}
LongSet trueRanges = val.ValueSet;
var afterTrue = AssignStateRanges(ifInst.TrueInst, stateRange.IntersectWith(trueRanges));
var afterFalse = AssignStateRanges(ifInst.FalseInst, stateRange.ExceptWith(trueRanges));
return afterTrue.UnionWith(afterFalse);

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

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

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

@ -409,16 +409,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -409,16 +409,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
#region Analyze MoveNext() and generate new body
BlockContainer AnalyzeMoveNext()
{
context.Stepper.StartGroup("AnalyzeMoveNext");
MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext");
ILFunction moveNextFunction = CreateILAst(moveNextMethod);
// 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.
context.Stepper.StartGroup("AnalyzeMoveNext");
foreach (var stloc in moveNextFunction.Descendants.OfType<StLoc>().Where(s => s.Variable.IsSingleDefinition && s.Value.MatchLdThis()).ToList()) {
CopyPropagation.Propagate(stloc, context);
}
context.Stepper.EndGroup();
var body = (BlockContainer)moveNextFunction.Body;
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 @@ -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,
// but those cannot contain any yield statements.
// So for reconstructing the control flow, we only consider the blocks directly within body.
@ -448,9 +449,41 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -448,9 +449,41 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
moveNextFunction.Variables.Clear();
// release references from old moveNextFunction to instructions that were moved over to newBody
moveNextFunction.ReleaseRef();
context.Stepper.EndGroup();
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>
/// 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 @@ -259,10 +259,16 @@ namespace ICSharpCode.Decompiler.Util
public override string ToString()
{
if (End == long.MinValue)
return string.Format("[{0}..long.MaxValue]", Start);
else
if (End == long.MinValue) {
if (Start == long.MinValue)
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);
}
}
#region Equals and GetHashCode implementation

Loading…
Cancel
Save