Browse Source

Adjust labels of simple switch instructions.

pull/734/merge
Daniel Grunwald 8 years ago
parent
commit
a5a98ae9f1
  1. 83
      ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs
  2. 26
      ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs
  3. 30
      ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs
  4. 14
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

83
ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using System;
namespace ICSharpCode.Decompiler.IL.ControlFlow
{
@ -140,15 +141,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -140,15 +141,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
long offset;
if (MatchSwitchVar(inst.Value)) {
offset = 0;
} else if (inst.Value.OpCode == OpCode.BinaryNumericInstruction) {
var bop = (BinaryNumericInstruction)inst.Value;
} else if (inst.Value is BinaryNumericInstruction bop) {
if (bop.CheckForOverflow)
return false;
long val;
if (MatchSwitchVar(bop.Left) && MatchLdcI(bop.Right, out val)) {
if (MatchSwitchVar(bop.Left) && bop.Right.MatchLdcI(out long val)) {
switch (bop.Operator) {
case BinaryNumericOperator.Add:
offset = -val;
offset = unchecked(-val);
break;
case BinaryNumericOperator.Sub:
offset = val;
@ -203,60 +202,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -203,60 +202,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return inst.MatchLdLoc(out switchVar);
}
bool MatchLdcI(ILInstruction inst, out long val)
{
if (inst.MatchLdcI8(out val))
return true;
int intVal;
if (inst.MatchLdcI4(out intVal)) {
val = intVal;
return true;
}
return false;
}
/// <summary>
/// Analyzes the boolean condition, returning the set of values of the interesting
/// variable for which the condition evaluates to true.
/// </summary>
private bool AnalyzeCondition(ILInstruction condition, out LongSet trueValues)
{
ILInstruction arg;
Comp comp = condition as Comp;
long val;
if (comp != null && MatchSwitchVar(comp.Left) && MatchLdcI(comp.Right, out val)) {
if (condition is Comp comp && MatchSwitchVar(comp.Left) && comp.Right.MatchLdcI(out long val)) {
// if (comp(V OP val))
switch (comp.Kind) {
case ComparisonKind.Equality:
trueValues = new LongSet(val);
return true;
case ComparisonKind.Inequality:
trueValues = new LongSet(val).Invert();
return true;
case ComparisonKind.LessThan:
trueValues = MakeGreaterThanOrEqualSet(val, comp.Sign).Invert();
return true;
case ComparisonKind.LessThanOrEqual:
trueValues = MakeLessThanOrEqualSet(val, comp.Sign);
return true;
case ComparisonKind.GreaterThan:
trueValues = MakeLessThanOrEqualSet(val, comp.Sign).Invert();
return true;
case ComparisonKind.GreaterThanOrEqual:
trueValues = MakeGreaterThanOrEqualSet(val, comp.Sign);
return true;
default:
trueValues = LongSet.Empty;
return false;
}
trueValues = MakeSetWhereComparisonIsTrue(comp.Kind, val, comp.Sign);
return true;
} else if (MatchSwitchVar(condition)) {
// if (ldloc V) --> branch for all values except 0
trueValues = new LongSet(0).Invert();
return true;
} else if (condition.MatchLogicNot(out arg)) {
} else if (condition.MatchLogicNot(out ILInstruction arg)) {
// if (logic.not(X)) --> branch for all values where if (X) does not branch
LongSet falseValues;
bool res = AnalyzeCondition(arg, out falseValues);
bool res = AnalyzeCondition(arg, out LongSet falseValues);
trueValues = falseValues.Invert();
return res;
} else {
@ -265,7 +227,30 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -265,7 +227,30 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
private LongSet MakeGreaterThanOrEqualSet(long val, Sign sign)
/// <summary>
/// Create the LongSet that contains a value x iff x compared with value is true.
/// </summary>
internal static LongSet MakeSetWhereComparisonIsTrue(ComparisonKind kind, long val, Sign sign)
{
switch (kind) {
case ComparisonKind.Equality:
return new LongSet(val);
case ComparisonKind.Inequality:
return new LongSet(val).Invert();
case ComparisonKind.LessThan:
return MakeGreaterThanOrEqualSet(val, sign).Invert();
case ComparisonKind.LessThanOrEqual:
return MakeLessThanOrEqualSet(val, sign);
case ComparisonKind.GreaterThan:
return MakeLessThanOrEqualSet(val, sign).Invert();
case ComparisonKind.GreaterThanOrEqual:
return MakeGreaterThanOrEqualSet(val, sign);
default:
throw new ArgumentException("Invalid ComparisonKind");
}
}
private static LongSet MakeGreaterThanOrEqualSet(long val, Sign sign)
{
if (sign == Sign.Signed) {
return new LongSet(LongInterval.Inclusive(val, long.MaxValue));
@ -282,7 +267,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -282,7 +267,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
private LongSet MakeLessThanOrEqualSet(long val, Sign sign)
private static LongSet MakeLessThanOrEqualSet(long val, Sign sign)
{
if (sign == Sign.Signed) {
return new LongSet(LongInterval.Inclusive(long.MinValue, val));

26
ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs

@ -96,8 +96,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -96,8 +96,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Any switch instructions will only have branch instructions in the sections.
// Combine sections with identical branch target:
Block defaultTarget;
block.Instructions.Last().MatchBranch(out defaultTarget);
block.Instructions.Last().MatchBranch(out Block defaultTarget);
var dict = new Dictionary<Block, SwitchSection>(); // branch target -> switch section
sw.Sections.RemoveAll(
section => {
@ -117,6 +116,29 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -117,6 +116,29 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
return false;
});
AdjustLabels(sw);
}
static void AdjustLabels(SwitchInstruction sw)
{
if (sw.Value is BinaryNumericInstruction bop && !bop.CheckForOverflow && bop.Right.MatchLdcI(out long val)) {
// Move offset into labels:
long offset;
switch (bop.Operator) {
case BinaryNumericOperator.Add:
offset = unchecked(-val);
break;
case BinaryNumericOperator.Sub:
offset = val;
break;
default: // unknown bop.Operator
return;
}
sw.Value = bop.Left;
foreach (var section in sw.Sections) {
section.Labels = section.Labels.AddOffset(offset);
}
}
}
const ulong MaxValuesPerSection = 50;

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

@ -143,34 +143,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -143,34 +143,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant)
return Failed;
// bool: (state + left.Constant == right.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 trueSums = SwitchAnalysis.MakeSetWhereComparisonIsTrue(comp.Kind, right.Constant, comp.Sign);
// symbolic value is true iff trueSums.Contains(state + left.Constant)
LongSet trueStates = trueSums.AddOffset(unchecked(-left.Constant));
// evals to true if trueStates.Contains(state)
// symbolic value is true iff trueStates.Contains(state)
return new SymbolicValue(SymbolicValueType.StateInSet, trueStates);
} else if (inst is LogicNot logicNot) {
SymbolicValue val = Eval(logicNot.Argument).AsBool();

14
ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

@ -27,6 +27,20 @@ namespace ICSharpCode.Decompiler.IL @@ -27,6 +27,20 @@ namespace ICSharpCode.Decompiler.IL
return OpCode == OpCode.LdcI4 && ((LdcI4)this).Value == val;
}
/// <summary>
/// Matches either LdcI4 or LdcI8.
/// </summary>
public bool MatchLdcI(out long val)
{
if (MatchLdcI8(out val))
return true;
if (MatchLdcI4(out int intVal)) {
val = intVal;
return true;
}
return false;
}
public bool MatchLdLoc(ILVariable variable)
{
var inst = this as LdLoc;

Loading…
Cancel
Save