From 919219524b09670426c8d4185ccd0813a5e6034c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 23 Sep 2017 00:27:17 +0200 Subject: [PATCH] Eliminate the dedicated logic.not instruction, and treat it as syntax sugar similar to logic.and/logic.or. 'logic.not(arg)' is now represented using 'comp(arg == ldc.i4 0)'. --- .../PrettyTestRunner.cs | 6 +- .../TestCases/Pretty/QueryExpressions.cs | 3 +- .../CSharp/ExpressionBuilder.cs | 5 -- .../CSharp/StatementBuilder.cs | 4 +- .../IL/ControlFlow/AsyncAwaitDecompiler.cs | 2 +- .../IL/ControlFlow/ConditionDetection.cs | 10 +-- .../IL/ControlFlow/SymbolicExecution.cs | 29 ++++---- .../IL/ControlFlow/YieldReturnDecompiler.cs | 2 +- ICSharpCode.Decompiler/IL/ILReader.cs | 4 +- ICSharpCode.Decompiler/IL/Instructions.cs | 53 --------------- ICSharpCode.Decompiler/IL/Instructions.tt | 2 - .../IL/Instructions/Comp.cs | 14 +++- .../IL/Instructions/PatternMatching.cs | 60 ++++++++++------- .../IL/Transforms/ExpressionTransforms.cs | 67 +++++++++++++------ .../IL/Transforms/ILInlining.cs | 2 +- 15 files changed, 128 insertions(+), 135 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 279010c1c..2bef4ccf9 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -118,7 +118,11 @@ namespace ICSharpCode.Decompiler.Tests [Test, Ignore("Not implemented")] public void LiftedOperators([ValueSource("defaultOptions")] CompilerOptions cscOptions) { - Run(cscOptions: cscOptions); + try { + Run(cscOptions: cscOptions); + } catch (AssertionException) { + Assert.Ignore("Not implemented"); + } } [Test, Ignore("Bad variable names; some if-else misdetected")] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs index 890af6dc0..d655327b3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs @@ -143,8 +143,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return from c in this.customers join o in this.orders on c.CustomerID equals o.CustomerID into co let n = co.Count() - // should be n >= 10 - where !(n < 10) + where n >= 10 select new { Name = c.Name, OrderCount = n diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index fea4d9ec4..20c41f4a4 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -342,11 +342,6 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new TypeOfResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeTypeHandle")), inst.Type)); } - protected internal override TranslatedExpression VisitLogicNot(LogicNot inst, TranslationContext context) - { - return TranslateCondition(inst.Argument, negate: true).WithILInstruction(inst); - } - protected internal override TranslatedExpression VisitBitNot(BitNot inst, TranslationContext context) { var argument = Translate(inst.Argument); diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index b015d2338..23e2c406a 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -381,9 +381,9 @@ namespace ICSharpCode.Decompiler.CSharp ILInstruction condition = null; foreach (var c in conditions) { if (condition == null) - condition = new LogicNot(c); + condition = Comp.LogicNot(c); else - condition = IfInstruction.LogicAnd(new LogicNot(c), condition); + condition = IfInstruction.LogicAnd(Comp.LogicNot(c), condition); } return condition; } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index a72e20c2a..3220f7c09 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -865,7 +865,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow foreach (var tryFinally in function.Descendants.OfType()) { entryPoint = GetBodyEntryPoint(tryFinally.FinallyBlock as BlockContainer); if (entryPoint?.Instructions[0] is IfInstruction ifInst) { - if (ifInst.Condition is LogicNot logicNot && logicNot.Argument.MatchLdLoc(doFinallyBodies)) { + if (ifInst.Condition.MatchLogicNot(out var logicNotArg) && logicNotArg.MatchLdLoc(doFinallyBodies)) { context.Step("Remove if(doFinallyBodies) from try-finally", tryFinally); // condition will always be false now that we're using 'await' instructions entryPoint.Instructions.RemoveAt(0); diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs index 75241715a..48ecdf218 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -98,7 +98,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow block.Instructions[block.Instructions.Count - 1] = ifInst.TrueInst; ifInst.TrueInst = exitInst; exitInst = block.Instructions.Last(); - ifInst.Condition = new LogicNot(ifInst.Condition); + ifInst.Condition = Comp.LogicNot(ifInst.Condition); } ILInstruction trueExitInst; @@ -119,7 +119,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // "if (...) { if (nestedCondition) goto exitPoint; ... } goto exitPoint;" // -> "if (... && !nestedCondition) { ... } goto exitPoint;" context.Step("Combine 'if (cond1 && !cond2)' in then-branch", ifInst); - ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, new LogicNot(nestedCondition)); + ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, Comp.LogicNot(nestedCondition)); targetBlock.Instructions.RemoveAt(0); // Update targetBlock label now that we've removed the first instruction if (targetBlock.Instructions.FirstOrDefault()?.ILRange.IsEmpty == false) { @@ -137,7 +137,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // (only if end-point of 'falseInst...' is unreachable) context.Step("Invert nested condition to reduce number of gotos", ifInst); var nestedIfInst = (IfInstruction)targetBlock.Instructions[0]; - nestedIfInst.Condition = new LogicNot(nestedCondition); + nestedIfInst.Condition = Comp.LogicNot(nestedCondition); nestedTrueBlock.Instructions.RemoveAt(nestedTrueBlock.Instructions.Count - 1); // remove nested goto exitPoint; // remove falseInsts from outer block var falseInsts = targetBlock.Instructions.Skip(1).ToArray(); @@ -197,7 +197,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow var oldTrue = ifInst.TrueInst; ifInst.TrueInst = ifInst.FalseInst; ifInst.FalseInst = new Nop { ILRange = oldTrue.ILRange }; - ifInst.Condition = new LogicNot(ifInst.Condition); + ifInst.Condition = Comp.LogicNot(ifInst.Condition); // After swapping, it's possible that we can introduce a short-circuit operator: Block trueBlock = ifInst.TrueInst as Block; @@ -218,7 +218,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow var oldTrue = ifInst.TrueInst; ifInst.TrueInst = ifInst.FalseInst; ifInst.FalseInst = oldTrue; - ifInst.Condition = new LogicNot(ifInst.Condition); + ifInst.Condition = Comp.LogicNot(ifInst.Condition); } } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs index dc0ac8db3..9d406f3aa 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs @@ -142,18 +142,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } else if (inst is Comp comp) { var left = Eval(comp.Left); var right = Eval(comp.Right); - if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant) - return Failed; - // bool: (state + left.Constant == right.Constant) - 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)); - // 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(); - if (val.Type == SymbolicValueType.StateInSet) { - return new SymbolicValue(SymbolicValueType.StateInSet, val.ValueSet.Invert()); + if (left.Type == SymbolicValueType.State && right.Type == SymbolicValueType.IntegerConstant) { + // bool: (state + left.Constant == right.Constant) + 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)); + // symbolic value is true iff trueStates.Contains(state) + return new SymbolicValue(SymbolicValueType.StateInSet, trueStates); + } else if (left.Type == SymbolicValueType.StateInSet && right.Type == SymbolicValueType.IntegerConstant) { + if (comp.Kind == ComparisonKind.Equality && right.Constant == 0) { + // comp((x in set) == 0) ==> x not in set + return new SymbolicValue(SymbolicValueType.StateInSet, left.ValueSet.Invert()); + } else if (comp.Kind == ComparisonKind.Inequality && right.Constant != 0) { + // comp((x in set) != 0) => x in set + return new SymbolicValue(SymbolicValueType.StateInSet, left.ValueSet); + } else { + return Failed; + } } else { return Failed; } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index 3ce46d87f..0ba14f826 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -995,7 +995,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow foreach (var tryFinally in function.Descendants.OfType()) { entryPoint = AsyncAwaitDecompiler.GetBodyEntryPoint(tryFinally.FinallyBlock as BlockContainer); if (entryPoint?.Instructions[0] is IfInstruction ifInst) { - if (ifInst.Condition is LogicNot logicNot && logicNot.Argument.MatchLdLoc(skipFinallyBodies)) { + if (ifInst.Condition.MatchLogicNot(out var logicNotArg) && logicNotArg.MatchLdLoc(skipFinallyBodies)) { context.Step("Remove if (skipFinallyBodies) from try-finally", tryFinally); // condition will always be true now that we're using 'yield' instructions entryPoint.Instructions[0] = ifInst.TrueInst; diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 383c4dc5b..cc77ec1eb 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -1133,7 +1133,7 @@ namespace ICSharpCode.Decompiler.IL if (left.ResultType == StackType.F && right.ResultType == StackType.F) { if (un) { // for floats, 'un' means 'unordered' - return new LogicNot(new Comp(kind.Negate(), Sign.None, left, right)); + return Comp.LogicNot(new Comp(kind.Negate(), Sign.None, left, right)); } else { return new Comp(kind, Sign.None, left, right); } @@ -1189,7 +1189,7 @@ namespace ICSharpCode.Decompiler.IL break; default: if (negate) { - condition = new LogicNot(condition); + condition = Comp.LogicNot(condition); } break; } diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 9f5ac3ad9..3f32e182a 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -43,8 +43,6 @@ namespace ICSharpCode.Decompiler.IL Block, /// A region where a pinned variable is used (initial representation of future fixed statement). PinnedRegion, - /// Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4). - LogicNot, /// Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr. BinaryNumericInstruction, /// Common instruction for compound assignments. @@ -833,34 +831,6 @@ namespace ICSharpCode.Decompiler.IL } } namespace ICSharpCode.Decompiler.IL -{ - /// Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4). - public sealed partial class LogicNot : UnaryInstruction - { - public LogicNot(ILInstruction argument) : base(OpCode.LogicNot, argument) - { - } - public override StackType ResultType { get { return StackType.I4; } } - public override void AcceptVisitor(ILVisitor visitor) - { - visitor.VisitLogicNot(this); - } - public override T AcceptVisitor(ILVisitor visitor) - { - return visitor.VisitLogicNot(this); - } - public override T AcceptVisitor(ILVisitor visitor, C context) - { - return visitor.VisitLogicNot(this, context); - } - protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) - { - var o = other as LogicNot; - return o != null && this.Argument.PerformMatch(o.Argument, ref match); - } - } -} -namespace ICSharpCode.Decompiler.IL { /// Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr. public sealed partial class BinaryNumericInstruction : BinaryInstruction @@ -4223,10 +4193,6 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } - protected internal virtual void VisitLogicNot(LogicNot inst) - { - Default(inst); - } protected internal virtual void VisitBinaryNumericInstruction(BinaryNumericInstruction inst) { Default(inst); @@ -4501,10 +4467,6 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } - protected internal virtual T VisitLogicNot(LogicNot inst) - { - return Default(inst); - } protected internal virtual T VisitBinaryNumericInstruction(BinaryNumericInstruction inst) { return Default(inst); @@ -4779,10 +4741,6 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst, context); } - protected internal virtual T VisitLogicNot(LogicNot inst, C context) - { - return Default(inst, context); - } protected internal virtual T VisitBinaryNumericInstruction(BinaryNumericInstruction inst, C context) { return Default(inst, context); @@ -5031,7 +4989,6 @@ namespace ICSharpCode.Decompiler.IL "BlockContainer", "Block", "PinnedRegion", - "logic.not", "binary", "compound", "bit.not", @@ -5135,16 +5092,6 @@ namespace ICSharpCode.Decompiler.IL body = default(ILInstruction); return false; } - public bool MatchLogicNot(out ILInstruction argument) - { - var inst = this as LogicNot; - if (inst != null) { - argument = inst.Argument; - return true; - } - argument = default(ILInstruction); - return false; - } public bool MatchBitNot(out ILInstruction argument) { var inst = this as BitNot; diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 909f0f2ae..3c00ce01e 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -63,8 +63,6 @@ new ChildInfo("init") { CanInlineInto = true }, new ChildInfo("body") })), - new OpCode("logic.not", "Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4).", - ResultType("I4"), Unary), new OpCode("binary", "Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr.", CustomClassName("BinaryNumericInstruction"), Binary, CustomWriteTo, CustomConstructor, CustomComputeFlags, MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")), diff --git a/ICSharpCode.Decompiler/IL/Instructions/Comp.cs b/ICSharpCode.Decompiler/IL/Instructions/Comp.cs index 6910e8300..dc8318cf3 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Comp.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Comp.cs @@ -20,6 +20,8 @@ using System; using System.Diagnostics; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; + namespace ICSharpCode.Decompiler.IL { public enum ComparisonKind : byte @@ -132,7 +134,7 @@ namespace ICSharpCode.Decompiler.IL /// For lifted comparisons, this is the underlying input type. /// public StackType InputType; - + /// /// If this is an integer comparison, specifies the sign used to interpret the integers. /// @@ -198,6 +200,16 @@ namespace ICSharpCode.Decompiler.IL Right.WriteTo(output); output.Write(')'); } + + public static Comp LogicNot(ILInstruction arg) + { + return new Comp(ComparisonKind.Equality, Sign.None, arg, new LdcI4(0)); + } + + public static Comp LogicNot(ILInstruction arg, Interval ilrange) + { + return new Comp(ComparisonKind.Equality, Sign.None, arg, new LdcI4(0)) { ILRange = ilrange }; + } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs index 53b46c358..9e9185f7b 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs @@ -257,6 +257,22 @@ namespace ICSharpCode.Decompiler.IL return false; } + /// + /// Matches an logical negation. + /// + public bool MatchLogicNot(out ILInstruction arg) + { + if (this is Comp comp && comp.Kind == ComparisonKind.Equality + && comp.LiftingKind == ComparisonLiftingKind.None + && comp.Right.MatchLdcI4(0)) + { + arg = comp.Left; + return true; + } + arg = null; + return false; + } + public bool MatchTryCatchHandler(out ILVariable variable) { var inst = this as TryCatchHandler; @@ -273,23 +289,19 @@ namespace ICSharpCode.Decompiler.IL /// public bool MatchCompEquals(out ILInstruction left, out ILInstruction right) { - ComparisonKind op; - Comp comp; - if (this is LogicNot logicNot) { - op = ComparisonKind.Inequality; - comp = logicNot.Argument as Comp; - } else { - op = ComparisonKind.Equality; - comp = this as Comp; - } - if (comp != null && comp.Kind == op) { + if (this.MatchLogicNot(out var arg) && arg is Comp nestedComp && nestedComp.Kind == ComparisonKind.Inequality && !nestedComp.IsLifted) { + left = nestedComp.Left; + right = nestedComp.Right; + return true; + } else if (this is Comp comp && comp.Kind == ComparisonKind.Equality && !comp.IsLifted) { left = comp.Left; right = comp.Right; return true; + } else { + left = null; + right = null; + return false; } - left = null; - right = null; - return false; } /// @@ -297,23 +309,19 @@ namespace ICSharpCode.Decompiler.IL /// public bool MatchCompNotEquals(out ILInstruction left, out ILInstruction right) { - ComparisonKind op; - Comp comp; - if (this is LogicNot logicNot) { - op = ComparisonKind.Equality; - comp = logicNot.Argument as Comp; - } else { - op = ComparisonKind.Inequality; - comp = this as Comp; - } - if (comp != null && comp.Kind == op) { + if (this.MatchLogicNot(out var arg) && arg is Comp nestedComp && nestedComp.Kind == ComparisonKind.Equality && !nestedComp.IsLifted) { + left = nestedComp.Left; + right = nestedComp.Right; + return true; + } else if (this is Comp comp && comp.Kind == ComparisonKind.Inequality && !comp.IsLifted) { left = comp.Left; right = comp.Right; return true; + } else { + left = null; + right = null; + return false; } - left = null; - right = null; - return false; } public bool MatchLdsFld(IField field) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 209311435..297e08dcf 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -52,8 +52,36 @@ namespace ICSharpCode.Decompiler.IL.Transforms // we know those were already handled previously. } + static bool IsInConditionSlot(ILInstruction inst) + { + var slot = inst.SlotInfo; + if (slot == IfInstruction.ConditionSlot) + return true; + if (slot == IfInstruction.TrueInstSlot || slot == IfInstruction.FalseInstSlot || slot == NullCoalescingInstruction.FallbackInstSlot) + return IsInConditionSlot(inst.Parent); + if (inst.Parent.MatchLogicNot(out _)) + return true; + return false; + } + protected internal override void VisitComp(Comp inst) { + // "logic.not(arg)" is sugar for "comp(arg != ldc.i4 0)" + if (inst.MatchLogicNot(out var arg)) { + VisitLogicNot(inst, arg); + return; + } else if (inst.Kind == ComparisonKind.Inequality && inst.LiftingKind == ComparisonLiftingKind.None + && inst.Right.MatchLdcI4(0) && (IsInConditionSlot(inst) || inst.Left is Comp) + ) { + // if (comp(x != 0)) ==> if (x) + // comp(comp(...) != 0) => comp(...) + context.Step("Remove redundant comp(... != 0)", inst); + inst.Left.AddILRange(inst.ILRange); + inst.ReplaceWith(inst.Left); + inst.Left.AcceptVisitor(this); + return; + } + base.VisitComp(inst); if (inst.IsLifted) { return; @@ -94,9 +122,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (inst.Kind == ComparisonKind.GreaterThan) { context.Step("comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)", inst); inst.Kind = ComparisonKind.Inequality; + VisitComp(inst); + return; } else if (inst.Kind == ComparisonKind.LessThanOrEqual) { context.Step("comp.unsigned(left <= ldc.i4 0) => comp(left == ldc.i4 0)", inst); inst.Kind = ComparisonKind.Equality; + VisitComp(inst); + return; } } } @@ -137,17 +169,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - protected internal override void VisitLogicNot(LogicNot inst) + void VisitLogicNot(Comp inst, ILInstruction arg) { - ILInstruction arg, lhs, rhs; - if (inst.Argument.MatchLogicNot(out arg)) { - context.Step("logic.not(logic.not(arg)) => arg", inst); - Debug.Assert(arg.ResultType == StackType.I4); - arg.AddILRange(inst.ILRange); - arg.AddILRange(inst.Argument.ILRange); - inst.ReplaceWith(arg); - arg.AcceptVisitor(this); - } else if (inst.Argument is Comp comp) { + ILInstruction lhs, rhs; + if (arg is Comp comp) { if ((comp.InputType != StackType.F && !comp.IsLifted) || comp.Kind.IsEqualityOrInequality()) { context.Step("push negation into comparison", inst); comp.Kind = comp.Kind.Negate(); @@ -155,32 +180,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms inst.ReplaceWith(comp); } comp.AcceptVisitor(this); - } else if (inst.Argument.MatchLogicAnd(out lhs, out rhs)) { + } else if (arg.MatchLogicAnd(out lhs, out rhs)) { // logic.not(if (lhs) rhs else ldc.i4 0) // ==> if (logic.not(lhs)) ldc.i4 1 else logic.not(rhs) context.Step("push negation into logic.and", inst); - IfInstruction ifInst = (IfInstruction)inst.Argument; + IfInstruction ifInst = (IfInstruction)arg; var ldc0 = ifInst.FalseInst; Debug.Assert(ldc0.MatchLdcI4(0)); - ifInst.Condition = new LogicNot(lhs) { ILRange = inst.ILRange }; + ifInst.Condition = Comp.LogicNot(lhs, inst.ILRange); ifInst.TrueInst = new LdcI4(1) { ILRange = ldc0.ILRange }; - ifInst.FalseInst = new LogicNot(rhs) { ILRange = inst.ILRange }; + ifInst.FalseInst = Comp.LogicNot(rhs, inst.ILRange); inst.ReplaceWith(ifInst); ifInst.AcceptVisitor(this); - } else if (inst.Argument.MatchLogicOr(out lhs, out rhs)) { + } else if (arg.MatchLogicOr(out lhs, out rhs)) { // logic.not(if (lhs) ldc.i4 1 else rhs) // ==> if (logic.not(lhs)) logic.not(rhs) else ldc.i4 0) context.Step("push negation into logic.or", inst); - IfInstruction ifInst = (IfInstruction)inst.Argument; + IfInstruction ifInst = (IfInstruction)arg; var ldc1 = ifInst.TrueInst; Debug.Assert(ldc1.MatchLdcI4(1)); - ifInst.Condition = new LogicNot(lhs) { ILRange = inst.ILRange }; - ifInst.TrueInst = new LogicNot(rhs) { ILRange = inst.ILRange }; + ifInst.Condition = Comp.LogicNot(lhs, inst.ILRange); + ifInst.TrueInst = Comp.LogicNot(rhs, inst.ILRange); ifInst.FalseInst = new LdcI4(0) { ILRange = ldc1.ILRange }; inst.ReplaceWith(ifInst); ifInst.AcceptVisitor(this); } else { - inst.Argument.AcceptVisitor(this); + arg.AcceptVisitor(this); } } @@ -286,7 +311,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var t = inst.TrueInst; inst.TrueInst = inst.FalseInst; inst.FalseInst = t; - inst.Condition = new LogicNot(inst.Condition); + inst.Condition = Comp.LogicNot(inst.Condition); } base.VisitIfInstruction(inst); @@ -309,7 +334,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILInstruction value1, value2; if (trueInst.Instructions[0].MatchStLoc(out v, out value1) && falseInst.Instructions[0].MatchStLoc(v, out value2)) { context.Step("conditional operator", inst); - var newIf = new IfInstruction(new LogicNot(inst.Condition), value2, value1); + var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1); newIf.ILRange = inst.ILRange; inst.ReplaceWith(new StLoc(v, newIf)); return newIf; diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 9dc8ec508..922a5f1b9 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -287,7 +287,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms case OpCode.Leave: return parent == next; case OpCode.IfInstruction: - while (parent.OpCode == OpCode.LogicNot) { + while (parent.MatchLogicNot(out _)) { parent = parent.Parent; } return parent == next;