From a5a98ae9f16b2810bfa361f49e87aa98be014d25 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 23 Aug 2017 00:16:26 +0200 Subject: [PATCH] Adjust labels of simple switch instructions. --- .../IL/ControlFlow/SwitchAnalysis.cs | 83 ++++++++----------- .../IL/ControlFlow/SwitchDetection.cs | 26 +++++- .../IL/ControlFlow/SymbolicExecution.cs | 30 +------ .../IL/Instructions/PatternMatching.cs | 16 +++- 4 files changed, 76 insertions(+), 79 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs index c8c709cf9..70f44a2b6 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs @@ -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 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 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; - } - /// /// Analyzes the boolean condition, returning the set of values of the interesting /// variable for which the condition evaluates to true. /// 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 } } - private LongSet MakeGreaterThanOrEqualSet(long val, Sign sign) + /// + /// Create the LongSet that contains a value x iff x compared with value is true. + /// + 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 } } - 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)); diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index aaecf90a6..897d41e7e 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -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(); // branch target -> switch section sw.Sections.RemoveAll( section => { @@ -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; diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs index cf437b0be..96ca08445 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs @@ -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(); diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs index b3f6b94f1..7e0e884d0 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs @@ -26,7 +26,21 @@ namespace ICSharpCode.Decompiler.IL { return OpCode == OpCode.LdcI4 && ((LdcI4)this).Value == val; } - + + /// + /// Matches either LdcI4 or LdcI8. + /// + 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;