diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs index 0579d1f58..971dfa3d8 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs @@ -268,5 +268,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty E(); } #endif + + public void PreferLogicalToBitwise(bool a, bool b, int i, float f) + { + B(a && b); + B(a && i == 1); + B(i == 1 && a); + B(i > i - 3 && a); + B(f < 0.1f && a); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.il index 0b241cea2..dc5cd99c2 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.il @@ -1369,6 +1369,80 @@ IL_009b: ret } // end of method ShortCircuit::EmptyIf + .method public hidebysig instance void + PreferLogicalToBitwise(bool a, + bool b, + int32 i, + float32 f) cil managed + { + // Code size 90 (0x5a) + .maxstack 4 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: brfalse.s IL_0008 + + IL_0005: ldarg.2 + IL_0006: br.s IL_0009 + + IL_0008: ldc.i4.0 + IL_0009: nop + IL_000a: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_000f: nop + IL_0010: ldarg.0 + IL_0011: ldarg.1 + IL_0012: brfalse.s IL_001a + + IL_0014: ldarg.3 + IL_0015: ldc.i4.1 + IL_0016: ceq + IL_0018: br.s IL_001b + + IL_001a: ldc.i4.0 + IL_001b: nop + IL_001c: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0021: nop + IL_0022: ldarg.0 + IL_0023: ldarg.3 + IL_0024: ldc.i4.1 + IL_0025: bne.un.s IL_002a + + IL_0027: ldarg.1 + IL_0028: br.s IL_002b + + IL_002a: ldc.i4.0 + IL_002b: nop + IL_002c: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0031: nop + IL_0032: ldarg.0 + IL_0033: ldarg.3 + IL_0034: ldarg.3 + IL_0035: ldc.i4.3 + IL_0036: sub + IL_0037: ble.s IL_003c + + IL_0039: ldarg.1 + IL_003a: br.s IL_003d + + IL_003c: ldc.i4.0 + IL_003d: nop + IL_003e: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0043: nop + IL_0044: ldarg.0 + IL_0045: ldarg.s f + IL_0047: ldc.r4 0.1 + IL_004c: bge.un.s IL_0051 + + IL_004e: ldarg.1 + IL_004f: br.s IL_0052 + + IL_0051: ldc.i4.0 + IL_0052: nop + IL_0053: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0058: nop + IL_0059: ret + } // end of method ShortCircuit::PreferLogicalToBitwise + .method family hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.opt.il index bc052b300..12759fc88 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.opt.il @@ -879,6 +879,69 @@ IL_0024: ret } // end of method ShortCircuit::StmtComplex6 + .method public hidebysig instance void + PreferLogicalToBitwise(bool a, + bool b, + int32 i, + float32 f) cil managed + { + // Code size 79 (0x4f) + .maxstack 4 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: brfalse.s IL_0007 + + IL_0004: ldarg.2 + IL_0005: br.s IL_0008 + + IL_0007: ldc.i4.0 + IL_0008: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_000d: ldarg.0 + IL_000e: ldarg.1 + IL_000f: brfalse.s IL_0017 + + IL_0011: ldarg.3 + IL_0012: ldc.i4.1 + IL_0013: ceq + IL_0015: br.s IL_0018 + + IL_0017: ldc.i4.0 + IL_0018: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_001d: ldarg.0 + IL_001e: ldarg.3 + IL_001f: ldc.i4.1 + IL_0020: bne.un.s IL_0025 + + IL_0022: ldarg.1 + IL_0023: br.s IL_0026 + + IL_0025: ldc.i4.0 + IL_0026: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_002b: ldarg.0 + IL_002c: ldarg.3 + IL_002d: ldarg.3 + IL_002e: ldc.i4.3 + IL_002f: sub + IL_0030: ble.s IL_0035 + + IL_0032: ldarg.1 + IL_0033: br.s IL_0036 + + IL_0035: ldc.i4.0 + IL_0036: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_003b: ldarg.0 + IL_003c: ldarg.s f + IL_003e: ldc.r4 0.1 + IL_0043: bge.un.s IL_0048 + + IL_0045: ldarg.1 + IL_0046: br.s IL_0049 + + IL_0048: ldc.i4.0 + IL_0049: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_004e: ret + } // end of method ShortCircuit::PreferLogicalToBitwise + .method family hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.opt.roslyn.il index e9b3fdd90..ab05a0256 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.opt.roslyn.il @@ -883,6 +883,56 @@ IL_0024: ret } // end of method ShortCircuit::StmtComplex6 + .method public hidebysig instance void + PreferLogicalToBitwise(bool a, + bool b, + int32 i, + float32 f) cil managed + { + // Code size 69 (0x45) + .maxstack 4 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: ldarg.2 + IL_0003: and + IL_0004: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0009: ldarg.0 + IL_000a: ldarg.1 + IL_000b: brfalse.s IL_0013 + + IL_000d: ldarg.3 + IL_000e: ldc.i4.1 + IL_000f: ceq + IL_0011: br.s IL_0014 + + IL_0013: ldc.i4.0 + IL_0014: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0019: ldarg.0 + IL_001a: ldarg.3 + IL_001b: ldc.i4.1 + IL_001c: ceq + IL_001e: ldarg.1 + IL_001f: and + IL_0020: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0025: ldarg.0 + IL_0026: ldarg.3 + IL_0027: ldarg.3 + IL_0028: ldc.i4.3 + IL_0029: sub + IL_002a: cgt + IL_002c: ldarg.1 + IL_002d: and + IL_002e: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0033: ldarg.0 + IL_0034: ldarg.s f + IL_0036: ldc.r4 0.1 + IL_003b: clt + IL_003d: ldarg.1 + IL_003e: and + IL_003f: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0044: ret + } // end of method ShortCircuit::PreferLogicalToBitwise + .method family hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.roslyn.il index 3504fed84..70814ab9e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.roslyn.il @@ -1299,6 +1299,62 @@ IL_0090: ret } // end of method ShortCircuit::EmptyIf + .method public hidebysig instance void + PreferLogicalToBitwise(bool a, + bool b, + int32 i, + float32 f) cil managed + { + // Code size 75 (0x4b) + .maxstack 4 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: ldarg.2 + IL_0004: and + IL_0005: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_000a: nop + IL_000b: ldarg.0 + IL_000c: ldarg.1 + IL_000d: brfalse.s IL_0015 + + IL_000f: ldarg.3 + IL_0010: ldc.i4.1 + IL_0011: ceq + IL_0013: br.s IL_0016 + + IL_0015: ldc.i4.0 + IL_0016: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_001b: nop + IL_001c: ldarg.0 + IL_001d: ldarg.3 + IL_001e: ldc.i4.1 + IL_001f: ceq + IL_0021: ldarg.1 + IL_0022: and + IL_0023: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0028: nop + IL_0029: ldarg.0 + IL_002a: ldarg.3 + IL_002b: ldarg.3 + IL_002c: ldc.i4.3 + IL_002d: sub + IL_002e: cgt + IL_0030: ldarg.1 + IL_0031: and + IL_0032: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0037: nop + IL_0038: ldarg.0 + IL_0039: ldarg.s f + IL_003b: ldc.r4 0.1 + IL_0040: clt + IL_0042: ldarg.1 + IL_0043: and + IL_0044: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::B(bool) + IL_0049: nop + IL_004a: ret + } // end of method ShortCircuit::PreferLogicalToBitwise + .method family hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs index bba216c13..5c394b9da 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs @@ -111,5 +111,49 @@ namespace ICSharpCode.Decompiler.IL.Transforms } return null; } + + protected internal override void VisitBinaryNumericInstruction(BinaryNumericInstruction inst) + { + if (inst.Operator != BinaryNumericOperator.BitAnd) + return; + + if (PotentialSideEffects(inst.Left) || PotentialSideEffects(inst.Right)) + return; + + if (!IsBoolean(inst.Left) || !IsBoolean(inst.Right)) + return; + + context.Step("Replace bit.and with logic.and", inst); + var expr = IfInstruction.LogicAnd(inst.Left, inst.Right); + inst.ReplaceWith(expr); + expr.AcceptVisitor(this); + } + + private bool IsBoolean(ILInstruction inst) => inst is Comp || inst.InferType().IsKnownType(KnownTypeCode.Boolean); + + private bool PotentialSideEffects(ILInstruction inst) + { + switch (inst) { + case LdLoc _: + case LdLoca _: + case LdcF4 _: + case LdcF8 _: + case LdcI4 _: + case LdcI8 _: + case LdcDecimal _: + return false; + case Comp c: + return PotentialSideEffects(c.Left) || PotentialSideEffects(c.Right); + case BinaryNumericInstruction b: + return PotentialSideEffects(b.Left) || PotentialSideEffects(b.Right); + case BitNot b: + return PotentialSideEffects(b.Argument); + case Conv c: + return PotentialSideEffects(c.Argument); + + } + + return true; + } } }