diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs b/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs
index d86411a14..6936bb485 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs
@@ -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);
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs
index fa4bf58ca..cf437b0be 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs
@@ -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
///
This,
///
- /// bool: State == Constant
+ /// bool: ValueSet.Contains(State)
///
- StateEquals,
- ///
- /// bool: State != Constant
- ///
- StateInEquals,
- ///
- /// bool: State < Constant
- ///
- StateLessThan,
- ///
- /// bool: State <= Constant
- ///
- StateLessEqual,
- ///
- /// bool: State > Constant
- ///
- StateGreaterThan,
- ///
- /// bool: State >= Constant
- ///
- 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
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
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;
}
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
index 2ac6782a5..b29457faf 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
@@ -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().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
}
}
+ 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
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().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();
+ }
+
///
/// Convert the old body (of MoveNext function) to the new body (of decompiled iterator method).
///
diff --git a/ICSharpCode.Decompiler/Util/Interval.cs b/ICSharpCode.Decompiler/Util/Interval.cs
index 7c8abddae..6f92b6fd3 100644
--- a/ICSharpCode.Decompiler/Util/Interval.cs
+++ b/ICSharpCode.Decompiler/Util/Interval.cs
@@ -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