Browse Source

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)'.
pull/870/head
Daniel Grunwald 8 years ago
parent
commit
919219524b
  1. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  2. 3
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs
  3. 5
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 4
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  5. 2
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  6. 10
      ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
  7. 29
      ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs
  8. 2
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  9. 4
      ICSharpCode.Decompiler/IL/ILReader.cs
  10. 53
      ICSharpCode.Decompiler/IL/Instructions.cs
  11. 2
      ICSharpCode.Decompiler/IL/Instructions.tt
  12. 14
      ICSharpCode.Decompiler/IL/Instructions/Comp.cs
  13. 60
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  14. 67
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  15. 2
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -118,7 +118,11 @@ namespace ICSharpCode.Decompiler.Tests
[Test, Ignore("Not implemented")] [Test, Ignore("Not implemented")]
public void LiftedOperators([ValueSource("defaultOptions")] CompilerOptions cscOptions) 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")] [Test, Ignore("Bad variable names; some if-else misdetected")]

3
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs

@ -143,8 +143,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return from c in this.customers return from c in this.customers
join o in this.orders on c.CustomerID equals o.CustomerID into co join o in this.orders on c.CustomerID equals o.CustomerID into co
let n = co.Count() let n = co.Count()
// should be n >= 10 where n >= 10
where !(n < 10)
select new { select new {
Name = c.Name, Name = c.Name,
OrderCount = n OrderCount = n

5
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -342,11 +342,6 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new TypeOfResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeTypeHandle")), inst.Type)); .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) protected internal override TranslatedExpression VisitBitNot(BitNot inst, TranslationContext context)
{ {
var argument = Translate(inst.Argument); var argument = Translate(inst.Argument);

4
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -381,9 +381,9 @@ namespace ICSharpCode.Decompiler.CSharp
ILInstruction condition = null; ILInstruction condition = null;
foreach (var c in conditions) { foreach (var c in conditions) {
if (condition == null) if (condition == null)
condition = new LogicNot(c); condition = Comp.LogicNot(c);
else else
condition = IfInstruction.LogicAnd(new LogicNot(c), condition); condition = IfInstruction.LogicAnd(Comp.LogicNot(c), condition);
} }
return condition; return condition;
} }

2
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -865,7 +865,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
foreach (var tryFinally in function.Descendants.OfType<TryFinally>()) { foreach (var tryFinally in function.Descendants.OfType<TryFinally>()) {
entryPoint = GetBodyEntryPoint(tryFinally.FinallyBlock as BlockContainer); entryPoint = GetBodyEntryPoint(tryFinally.FinallyBlock as BlockContainer);
if (entryPoint?.Instructions[0] is IfInstruction ifInst) { 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); context.Step("Remove if(doFinallyBodies) from try-finally", tryFinally);
// condition will always be false now that we're using 'await' instructions // condition will always be false now that we're using 'await' instructions
entryPoint.Instructions.RemoveAt(0); entryPoint.Instructions.RemoveAt(0);

10
ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs

@ -98,7 +98,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
block.Instructions[block.Instructions.Count - 1] = ifInst.TrueInst; block.Instructions[block.Instructions.Count - 1] = ifInst.TrueInst;
ifInst.TrueInst = exitInst; ifInst.TrueInst = exitInst;
exitInst = block.Instructions.Last(); exitInst = block.Instructions.Last();
ifInst.Condition = new LogicNot(ifInst.Condition); ifInst.Condition = Comp.LogicNot(ifInst.Condition);
} }
ILInstruction trueExitInst; ILInstruction trueExitInst;
@ -119,7 +119,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// "if (...) { if (nestedCondition) goto exitPoint; ... } goto exitPoint;" // "if (...) { if (nestedCondition) goto exitPoint; ... } goto exitPoint;"
// -> "if (... && !nestedCondition) { ... } goto exitPoint;" // -> "if (... && !nestedCondition) { ... } goto exitPoint;"
context.Step("Combine 'if (cond1 && !cond2)' in then-branch", ifInst); 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); targetBlock.Instructions.RemoveAt(0);
// Update targetBlock label now that we've removed the first instruction // Update targetBlock label now that we've removed the first instruction
if (targetBlock.Instructions.FirstOrDefault()?.ILRange.IsEmpty == false) { if (targetBlock.Instructions.FirstOrDefault()?.ILRange.IsEmpty == false) {
@ -137,7 +137,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// (only if end-point of 'falseInst...' is unreachable) // (only if end-point of 'falseInst...' is unreachable)
context.Step("Invert nested condition to reduce number of gotos", ifInst); context.Step("Invert nested condition to reduce number of gotos", ifInst);
var nestedIfInst = (IfInstruction)targetBlock.Instructions[0]; 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; nestedTrueBlock.Instructions.RemoveAt(nestedTrueBlock.Instructions.Count - 1); // remove nested goto exitPoint;
// remove falseInsts from outer block // remove falseInsts from outer block
var falseInsts = targetBlock.Instructions.Skip(1).ToArray(); var falseInsts = targetBlock.Instructions.Skip(1).ToArray();
@ -197,7 +197,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
var oldTrue = ifInst.TrueInst; var oldTrue = ifInst.TrueInst;
ifInst.TrueInst = ifInst.FalseInst; ifInst.TrueInst = ifInst.FalseInst;
ifInst.FalseInst = new Nop { ILRange = oldTrue.ILRange }; 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: // After swapping, it's possible that we can introduce a short-circuit operator:
Block trueBlock = ifInst.TrueInst as Block; Block trueBlock = ifInst.TrueInst as Block;
@ -218,7 +218,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
var oldTrue = ifInst.TrueInst; var oldTrue = ifInst.TrueInst;
ifInst.TrueInst = ifInst.FalseInst; ifInst.TrueInst = ifInst.FalseInst;
ifInst.FalseInst = oldTrue; ifInst.FalseInst = oldTrue;
ifInst.Condition = new LogicNot(ifInst.Condition); ifInst.Condition = Comp.LogicNot(ifInst.Condition);
} }
} }

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

@ -142,18 +142,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} else if (inst is Comp comp) { } else if (inst is Comp comp) {
var left = Eval(comp.Left); var left = Eval(comp.Left);
var right = Eval(comp.Right); var right = Eval(comp.Right);
if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant) if (left.Type == SymbolicValueType.State && right.Type == SymbolicValueType.IntegerConstant) {
return Failed; // bool: (state + left.Constant == right.Constant)
// bool: (state + left.Constant == right.Constant) LongSet trueSums = SwitchAnalysis.MakeSetWhereComparisonIsTrue(comp.Kind, right.Constant, comp.Sign);
LongSet trueSums = SwitchAnalysis.MakeSetWhereComparisonIsTrue(comp.Kind, right.Constant, comp.Sign); // symbolic value is true iff trueSums.Contains(state + left.Constant)
// symbolic value is true iff trueSums.Contains(state + left.Constant) LongSet trueStates = trueSums.AddOffset(unchecked(-left.Constant));
LongSet trueStates = trueSums.AddOffset(unchecked(-left.Constant)); // symbolic value is true iff trueStates.Contains(state)
// symbolic value is true iff trueStates.Contains(state) return new SymbolicValue(SymbolicValueType.StateInSet, trueStates);
return new SymbolicValue(SymbolicValueType.StateInSet, trueStates); } else if (left.Type == SymbolicValueType.StateInSet && right.Type == SymbolicValueType.IntegerConstant) {
} else if (inst is LogicNot logicNot) { if (comp.Kind == ComparisonKind.Equality && right.Constant == 0) {
SymbolicValue val = Eval(logicNot.Argument).AsBool(); // comp((x in set) == 0) ==> x not in set
if (val.Type == SymbolicValueType.StateInSet) { return new SymbolicValue(SymbolicValueType.StateInSet, left.ValueSet.Invert());
return new SymbolicValue(SymbolicValueType.StateInSet, val.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 { } else {
return Failed; return Failed;
} }

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

@ -995,7 +995,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
foreach (var tryFinally in function.Descendants.OfType<TryFinally>()) { foreach (var tryFinally in function.Descendants.OfType<TryFinally>()) {
entryPoint = AsyncAwaitDecompiler.GetBodyEntryPoint(tryFinally.FinallyBlock as BlockContainer); entryPoint = AsyncAwaitDecompiler.GetBodyEntryPoint(tryFinally.FinallyBlock as BlockContainer);
if (entryPoint?.Instructions[0] is IfInstruction ifInst) { 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); context.Step("Remove if (skipFinallyBodies) from try-finally", tryFinally);
// condition will always be true now that we're using 'yield' instructions // condition will always be true now that we're using 'yield' instructions
entryPoint.Instructions[0] = ifInst.TrueInst; entryPoint.Instructions[0] = ifInst.TrueInst;

4
ICSharpCode.Decompiler/IL/ILReader.cs

@ -1133,7 +1133,7 @@ namespace ICSharpCode.Decompiler.IL
if (left.ResultType == StackType.F && right.ResultType == StackType.F) { if (left.ResultType == StackType.F && right.ResultType == StackType.F) {
if (un) { if (un) {
// for floats, 'un' means 'unordered' // 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 { } else {
return new Comp(kind, Sign.None, left, right); return new Comp(kind, Sign.None, left, right);
} }
@ -1189,7 +1189,7 @@ namespace ICSharpCode.Decompiler.IL
break; break;
default: default:
if (negate) { if (negate) {
condition = new LogicNot(condition); condition = Comp.LogicNot(condition);
} }
break; break;
} }

53
ICSharpCode.Decompiler/IL/Instructions.cs

@ -43,8 +43,6 @@ namespace ICSharpCode.Decompiler.IL
Block, Block,
/// <summary>A region where a pinned variable is used (initial representation of future fixed statement).</summary> /// <summary>A region where a pinned variable is used (initial representation of future fixed statement).</summary>
PinnedRegion, PinnedRegion,
/// <summary>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).</summary>
LogicNot,
/// <summary>Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr.</summary> /// <summary>Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr.</summary>
BinaryNumericInstruction, BinaryNumericInstruction,
/// <summary>Common instruction for compound assignments.</summary> /// <summary>Common instruction for compound assignments.</summary>
@ -833,34 +831,6 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{
/// <summary>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).</summary>
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<T>(ILVisitor<T> visitor)
{
return visitor.VisitLogicNot(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> 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
{ {
/// <summary>Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr.</summary> /// <summary>Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr.</summary>
public sealed partial class BinaryNumericInstruction : BinaryInstruction public sealed partial class BinaryNumericInstruction : BinaryInstruction
@ -4223,10 +4193,6 @@ namespace ICSharpCode.Decompiler.IL
{ {
Default(inst); Default(inst);
} }
protected internal virtual void VisitLogicNot(LogicNot inst)
{
Default(inst);
}
protected internal virtual void VisitBinaryNumericInstruction(BinaryNumericInstruction inst) protected internal virtual void VisitBinaryNumericInstruction(BinaryNumericInstruction inst)
{ {
Default(inst); Default(inst);
@ -4501,10 +4467,6 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst); return Default(inst);
} }
protected internal virtual T VisitLogicNot(LogicNot inst)
{
return Default(inst);
}
protected internal virtual T VisitBinaryNumericInstruction(BinaryNumericInstruction inst) protected internal virtual T VisitBinaryNumericInstruction(BinaryNumericInstruction inst)
{ {
return Default(inst); return Default(inst);
@ -4779,10 +4741,6 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst, context); 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) protected internal virtual T VisitBinaryNumericInstruction(BinaryNumericInstruction inst, C context)
{ {
return Default(inst, context); return Default(inst, context);
@ -5031,7 +4989,6 @@ namespace ICSharpCode.Decompiler.IL
"BlockContainer", "BlockContainer",
"Block", "Block",
"PinnedRegion", "PinnedRegion",
"logic.not",
"binary", "binary",
"compound", "compound",
"bit.not", "bit.not",
@ -5135,16 +5092,6 @@ namespace ICSharpCode.Decompiler.IL
body = default(ILInstruction); body = default(ILInstruction);
return false; 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) public bool MatchBitNot(out ILInstruction argument)
{ {
var inst = this as BitNot; var inst = this as BitNot;

2
ICSharpCode.Decompiler/IL/Instructions.tt

@ -63,8 +63,6 @@
new ChildInfo("init") { CanInlineInto = true }, new ChildInfo("init") { CanInlineInto = true },
new ChildInfo("body") 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.", 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, CustomClassName("BinaryNumericInstruction"), Binary, CustomWriteTo, CustomConstructor, CustomComputeFlags,
MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")), MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")),

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

@ -20,6 +20,8 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
public enum ComparisonKind : byte public enum ComparisonKind : byte
@ -132,7 +134,7 @@ namespace ICSharpCode.Decompiler.IL
/// For lifted comparisons, this is the underlying input type. /// For lifted comparisons, this is the underlying input type.
/// </summary> /// </summary>
public StackType InputType; public StackType InputType;
/// <summary> /// <summary>
/// If this is an integer comparison, specifies the sign used to interpret the integers. /// If this is an integer comparison, specifies the sign used to interpret the integers.
/// </summary> /// </summary>
@ -198,6 +200,16 @@ namespace ICSharpCode.Decompiler.IL
Right.WriteTo(output); Right.WriteTo(output);
output.Write(')'); 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 };
}
} }
} }

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

@ -257,6 +257,22 @@ namespace ICSharpCode.Decompiler.IL
return false; return false;
} }
/// <summary>
/// Matches an logical negation.
/// </summary>
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) public bool MatchTryCatchHandler(out ILVariable variable)
{ {
var inst = this as TryCatchHandler; var inst = this as TryCatchHandler;
@ -273,23 +289,19 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
public bool MatchCompEquals(out ILInstruction left, out ILInstruction right) public bool MatchCompEquals(out ILInstruction left, out ILInstruction right)
{ {
ComparisonKind op; if (this.MatchLogicNot(out var arg) && arg is Comp nestedComp && nestedComp.Kind == ComparisonKind.Inequality && !nestedComp.IsLifted) {
Comp comp; left = nestedComp.Left;
if (this is LogicNot logicNot) { right = nestedComp.Right;
op = ComparisonKind.Inequality; return true;
comp = logicNot.Argument as Comp; } else if (this is Comp comp && comp.Kind == ComparisonKind.Equality && !comp.IsLifted) {
} else {
op = ComparisonKind.Equality;
comp = this as Comp;
}
if (comp != null && comp.Kind == op) {
left = comp.Left; left = comp.Left;
right = comp.Right; right = comp.Right;
return true; return true;
} else {
left = null;
right = null;
return false;
} }
left = null;
right = null;
return false;
} }
/// <summary> /// <summary>
@ -297,23 +309,19 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
public bool MatchCompNotEquals(out ILInstruction left, out ILInstruction right) public bool MatchCompNotEquals(out ILInstruction left, out ILInstruction right)
{ {
ComparisonKind op; if (this.MatchLogicNot(out var arg) && arg is Comp nestedComp && nestedComp.Kind == ComparisonKind.Equality && !nestedComp.IsLifted) {
Comp comp; left = nestedComp.Left;
if (this is LogicNot logicNot) { right = nestedComp.Right;
op = ComparisonKind.Equality; return true;
comp = logicNot.Argument as Comp; } else if (this is Comp comp && comp.Kind == ComparisonKind.Inequality && !comp.IsLifted) {
} else {
op = ComparisonKind.Inequality;
comp = this as Comp;
}
if (comp != null && comp.Kind == op) {
left = comp.Left; left = comp.Left;
right = comp.Right; right = comp.Right;
return true; return true;
} else {
left = null;
right = null;
return false;
} }
left = null;
right = null;
return false;
} }
public bool MatchLdsFld(IField field) public bool MatchLdsFld(IField field)

67
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -52,8 +52,36 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// we know those were already handled previously. // 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) 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); base.VisitComp(inst);
if (inst.IsLifted) { if (inst.IsLifted) {
return; return;
@ -94,9 +122,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (inst.Kind == ComparisonKind.GreaterThan) { if (inst.Kind == ComparisonKind.GreaterThan) {
context.Step("comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)", inst); context.Step("comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)", inst);
inst.Kind = ComparisonKind.Inequality; inst.Kind = ComparisonKind.Inequality;
VisitComp(inst);
return;
} else if (inst.Kind == ComparisonKind.LessThanOrEqual) { } else if (inst.Kind == ComparisonKind.LessThanOrEqual) {
context.Step("comp.unsigned(left <= ldc.i4 0) => comp(left == ldc.i4 0)", inst); context.Step("comp.unsigned(left <= ldc.i4 0) => comp(left == ldc.i4 0)", inst);
inst.Kind = ComparisonKind.Equality; 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; ILInstruction lhs, rhs;
if (inst.Argument.MatchLogicNot(out arg)) { if (arg is Comp comp) {
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) {
if ((comp.InputType != StackType.F && !comp.IsLifted) || comp.Kind.IsEqualityOrInequality()) { if ((comp.InputType != StackType.F && !comp.IsLifted) || comp.Kind.IsEqualityOrInequality()) {
context.Step("push negation into comparison", inst); context.Step("push negation into comparison", inst);
comp.Kind = comp.Kind.Negate(); comp.Kind = comp.Kind.Negate();
@ -155,32 +180,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms
inst.ReplaceWith(comp); inst.ReplaceWith(comp);
} }
comp.AcceptVisitor(this); 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) // logic.not(if (lhs) rhs else ldc.i4 0)
// ==> if (logic.not(lhs)) ldc.i4 1 else logic.not(rhs) // ==> if (logic.not(lhs)) ldc.i4 1 else logic.not(rhs)
context.Step("push negation into logic.and", inst); context.Step("push negation into logic.and", inst);
IfInstruction ifInst = (IfInstruction)inst.Argument; IfInstruction ifInst = (IfInstruction)arg;
var ldc0 = ifInst.FalseInst; var ldc0 = ifInst.FalseInst;
Debug.Assert(ldc0.MatchLdcI4(0)); 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.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); inst.ReplaceWith(ifInst);
ifInst.AcceptVisitor(this); 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) // logic.not(if (lhs) ldc.i4 1 else rhs)
// ==> if (logic.not(lhs)) logic.not(rhs) else ldc.i4 0) // ==> if (logic.not(lhs)) logic.not(rhs) else ldc.i4 0)
context.Step("push negation into logic.or", inst); context.Step("push negation into logic.or", inst);
IfInstruction ifInst = (IfInstruction)inst.Argument; IfInstruction ifInst = (IfInstruction)arg;
var ldc1 = ifInst.TrueInst; var ldc1 = ifInst.TrueInst;
Debug.Assert(ldc1.MatchLdcI4(1)); Debug.Assert(ldc1.MatchLdcI4(1));
ifInst.Condition = new LogicNot(lhs) { ILRange = inst.ILRange }; ifInst.Condition = Comp.LogicNot(lhs, inst.ILRange);
ifInst.TrueInst = new LogicNot(rhs) { ILRange = inst.ILRange }; ifInst.TrueInst = Comp.LogicNot(rhs, inst.ILRange);
ifInst.FalseInst = new LdcI4(0) { ILRange = ldc1.ILRange }; ifInst.FalseInst = new LdcI4(0) { ILRange = ldc1.ILRange };
inst.ReplaceWith(ifInst); inst.ReplaceWith(ifInst);
ifInst.AcceptVisitor(this); ifInst.AcceptVisitor(this);
} else { } else {
inst.Argument.AcceptVisitor(this); arg.AcceptVisitor(this);
} }
} }
@ -286,7 +311,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var t = inst.TrueInst; var t = inst.TrueInst;
inst.TrueInst = inst.FalseInst; inst.TrueInst = inst.FalseInst;
inst.FalseInst = t; inst.FalseInst = t;
inst.Condition = new LogicNot(inst.Condition); inst.Condition = Comp.LogicNot(inst.Condition);
} }
base.VisitIfInstruction(inst); base.VisitIfInstruction(inst);
@ -309,7 +334,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILInstruction value1, value2; ILInstruction value1, value2;
if (trueInst.Instructions[0].MatchStLoc(out v, out value1) && falseInst.Instructions[0].MatchStLoc(v, out value2)) { if (trueInst.Instructions[0].MatchStLoc(out v, out value1) && falseInst.Instructions[0].MatchStLoc(v, out value2)) {
context.Step("conditional operator", inst); 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; newIf.ILRange = inst.ILRange;
inst.ReplaceWith(new StLoc(v, newIf)); inst.ReplaceWith(new StLoc(v, newIf));
return newIf; return newIf;

2
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -287,7 +287,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case OpCode.Leave: case OpCode.Leave:
return parent == next; return parent == next;
case OpCode.IfInstruction: case OpCode.IfInstruction:
while (parent.OpCode == OpCode.LogicNot) { while (parent.MatchLogicNot(out _)) {
parent = parent.Parent; parent = parent.Parent;
} }
return parent == next; return parent == next;

Loading…
Cancel
Save