Gtneg mul divoptimizations (#45604)
authorAlex Covington <68252706+alexcovington@users.noreply.github.com>
Tue, 12 Jan 2021 02:30:28 +0000 (18:30 -0800)
committerGitHub <noreply@github.com>
Tue, 12 Jan 2021 02:30:28 +0000 (18:30 -0800)
* GT_NEG optimization for multiplication and division

* Distribute negation over parenthetical multiplication or division.

* Removing duplicate logic that I had put in accidently.

* Check overflow and other conditions before performing morph

* Resolved merge conflict and cleanup morph.cpp

* Formatting morph.cpp

* Returning tree after performing smpop again to fix flags

* Formatting

* Added check for optimizations, formatting.

* Using gtIsActiveCSE_Candidate instead of fgGlobalMorph

* Update src/coreclr/jit/morph.cpp

Co-authored-by: Sergey Andreenko <seandree@microsoft.com>
* Formatting

* delete formatting changes.

* Add a test.

* Change the conditions a bit.

* Better names for the tests.

Co-authored-by: Sergey Andreenko <seandree@microsoft.com>
src/coreclr/jit/morph.cpp
src/tests/JIT/opt/InstructionCombining/NegMulOrDivToConst.cs [new file with mode: 0644]
src/tests/JIT/opt/InstructionCombining/NegMulOrDivToConst.csproj [new file with mode: 0644]

index b72d32e..3d96acb 100644 (file)
@@ -11909,6 +11909,21 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
             return fgMorphCast(tree);
 
         case GT_MUL:
+            if (opts.OptimizationEnabled() && !optValnumCSE_phase && !tree->gtOverflow())
+            {
+                // MUL(NEG(a), C) => MUL(a, NEG(C))
+                if (op1->OperIs(GT_NEG) && !op1->gtGetOp1()->IsCnsIntOrI() && op2->IsCnsIntOrI() &&
+                    !op2->IsIconHandle())
+                {
+                    GenTree* newOp1   = op1->gtGetOp1();
+                    GenTree* newConst = gtNewIconNode(-op2->AsIntCon()->IconValue(), op2->TypeGet());
+                    DEBUG_DESTROY_NODE(op1);
+                    DEBUG_DESTROY_NODE(op2);
+                    tree->AsOp()->gtOp1 = newOp1;
+                    tree->AsOp()->gtOp2 = newConst;
+                    return fgMorphSmpOp(tree, mac);
+                }
+            }
 
 #ifndef TARGET_64BIT
             if (typ == TYP_LONG)
@@ -12056,6 +12071,24 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
                 return fgMorphSmpOp(tree, mac);
             }
 
+            if (opts.OptimizationEnabled() && !optValnumCSE_phase)
+            {
+                // DIV(NEG(a), C) => DIV(a, NEG(C))
+                if (op1->OperIs(GT_NEG) && !op1->gtGetOp1()->IsCnsIntOrI() && op2->IsCnsIntOrI() &&
+                    !op2->IsIconHandle())
+                {
+                    ssize_t op2Value = op2->AsIntCon()->IconValue();
+                    if (op2Value != 1 && op2Value != -1) // Div must throw exception for int(long).MinValue / -1.
+                    {
+                        tree->AsOp()->gtOp1 = op1->gtGetOp1();
+                        DEBUG_DESTROY_NODE(op1);
+                        tree->AsOp()->gtOp2 = gtNewIconNode(-op2Value, op2->TypeGet());
+                        DEBUG_DESTROY_NODE(op2);
+                        return fgMorphSmpOp(tree, mac);
+                    }
+                }
+            }
+
 #ifndef TARGET_64BIT
             if (typ == TYP_LONG)
             {
@@ -13920,6 +13953,35 @@ DONE_MORPHING_CHILDREN:
                 return child;
             }
 
+            // Distribute negation over simple multiplication/division expressions
+            if (opts.OptimizationEnabled() && !optValnumCSE_phase && tree->OperIs(GT_NEG) &&
+                op1->OperIs(GT_MUL, GT_DIV))
+            {
+                GenTreeOp* mulOrDiv = op1->AsOp();
+                GenTree*   op1op1   = mulOrDiv->gtGetOp1();
+                GenTree*   op1op2   = mulOrDiv->gtGetOp2();
+
+                if (!op1op1->IsCnsIntOrI() && op1op2->IsCnsIntOrI() && !op1op2->IsIconHandle())
+                {
+                    // NEG(MUL(a, C)) => MUL(a, -C)
+                    // NEG(DIV(a, C)) => DIV(a, -C), except when C = {-1, 1}
+                    ssize_t constVal = op1op2->AsIntCon()->IconValue();
+                    if ((mulOrDiv->OperIs(GT_DIV) && (constVal != -1) && (constVal != 1)) ||
+                        (mulOrDiv->OperIs(GT_MUL) && !mulOrDiv->gtOverflow()))
+                    {
+                        GenTree* newOp1 = op1op1;                                      // a
+                        GenTree* newOp2 = gtNewIconNode(-constVal, op1op2->TypeGet()); // -C
+                        mulOrDiv->gtOp1 = newOp1;
+                        mulOrDiv->gtOp2 = newOp2;
+
+                        DEBUG_DESTROY_NODE(tree);
+                        DEBUG_DESTROY_NODE(op1op2);
+
+                        return mulOrDiv;
+                    }
+                }
+            }
+
             /* Any constant cases should have been folded earlier */
             noway_assert(!op1->OperIsConst() || !opts.OptEnabled(CLFLG_CONSTANTFOLD) || optValnumCSE_phase);
             break;
diff --git a/src/tests/JIT/opt/InstructionCombining/NegMulOrDivToConst.cs b/src/tests/JIT/opt/InstructionCombining/NegMulOrDivToConst.cs
new file mode 100644 (file)
index 0000000..ddb9736
--- /dev/null
@@ -0,0 +1,337 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Test the following optimizations:
+//  -(v * const) => v * -const;
+//  -v * const => v * -const;
+//  -(v / const) => v / -const;
+//  -v / const => v / -const;
+//
+// Note that C# spec tells that `int.MinValue / -1` result is implementation specific, but
+// ecma-335 requires it to throw `System.ArithmeticException` in such case, so we should not
+// change 1 and -1 sign.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace TestIntLimits
+{
+    class Program
+    {
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int CheckMulNeg()
+        {
+            bool fail = false;
+            if (MulNeg7(3) != -7 * 3)
+            {
+                fail = true;
+            }
+
+            if (MulNeg0(100) != 0)
+            {
+                fail = true;
+            }
+
+            try
+            {
+                MulNegIntMin(2);
+            }
+            catch
+            {
+                fail = true;
+            }
+
+
+            try
+            {
+                CheckedMulNenIntMin(1);
+                fail = true;
+            }
+            catch
+            { }
+
+            try
+            {
+                CheckedMulNenIntMin(0);
+
+            }
+            catch
+            {
+                fail = true;
+            }
+
+            if (NegMulIntMaxValue(1) != -int.MaxValue)
+            {
+                fail = true;
+            }
+            if (NegMulIntMaxValue(0) != 0)
+            {
+                fail = true;
+            }
+            if (NegMulIntMaxValue(-1) != int.MaxValue)
+            {
+                fail = true;
+            }
+
+            try
+            {
+                CheckedMulNeg0(int.MinValue);
+                fail = true;
+            }
+            catch
+            { }
+
+
+            if (fail)
+            {
+                Console.WriteLine("CheckMulNeg failed");
+                return 101;
+            }
+            return 100;
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int MulNeg7(int a) => -a * 7;
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int MulNeg0(int a) => -a * 0;
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int MulNegIntMin(int a) => -a * int.MinValue;
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int CheckedMulNenIntMin(int a)
+        {
+            checked
+            {
+                return -a * int.MinValue;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int NegMulIntMaxValue(int a) => -(a * int.MaxValue);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int CheckedMulNeg0(int a)
+        {
+            checked
+            {
+                return -a * 0;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int CheckNegMul()
+        {
+            bool fail = false;
+            if (NegMul7(3) != -7 * 3)
+            {
+                fail = true;
+            }
+
+            try
+            {
+                NegMulIntMinValue(100);
+            }
+            catch
+            {
+                fail = true;
+            }
+
+
+            try
+            {
+                CheckedNegMulIntMinValue(1);
+                fail = true;
+            }
+            catch
+            { }
+
+            try
+            {
+                CheckedNegMulIntMinValue(0);
+
+            }
+            catch
+            {
+                fail = true;
+            }
+
+            if (fail)
+            {
+                Console.WriteLine("CheckNegMul failed");
+                return 101;
+            }
+            return 100;
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int NegMul7(int a) => -(a * 7);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int NegMulIntMinValue(int a) => -(a * int.MinValue);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int CheckedNegMulIntMinValue(int a)
+        {
+            checked
+            {
+                return -(a * int.MinValue);
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int CheckDivNeg()
+        {
+            bool fail = false;
+
+            if (DivNeg11(110) != -10)
+            {
+                fail = true;
+            }
+
+            if (LongDivNeg1000(100000000000) != -100000000)
+            {
+                fail = true;
+            }
+
+            try
+            {
+                DivNegIntMinValue(1);
+                DivNegIntMinValue(int.MinValue);
+                LongDivNegLongMinValue(1);
+                LongDivNegLongMinValue(long.MinValue);
+                DivNeg1(int.MinValue);
+                LongDivNeg1(long.MinValue);
+            }
+            catch
+            {
+                fail = true;
+            }
+
+            if (fail)
+            {
+                Console.WriteLine("CheckDivNeg failed");
+                return 101;
+            }
+            return 100;
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int DivNeg11(int a) => -a / 11;
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static long LongDivNeg1000(long a) => -a / 1000;
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int DivNegIntMinValue(int a) => -a / int.MinValue;
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static long LongDivNegLongMinValue(long a) => -a / long.MinValue;
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int DivNeg1(int a) => -a / 1;
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static long LongDivNeg1(long a) => -a / 1;
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int CheckNegDiv()
+        {
+            bool fail = false;
+
+            if (NegDiv11(110) != -10)
+            {
+                fail = true;
+            }
+
+            if (LongNegDiv1000(100000000000) != -100000000)
+            {
+                fail = true;
+            }
+
+            try
+            {
+                NegDivIntMinValue(1);
+                NegDivIntMinValue(int.MinValue);
+                LongNegDivLongMinValue(1);
+                LongNegDivLongMinValue(long.MinValue);
+                NegDiv1(int.MinValue);
+                LongNegDiv1(long.MinValue);
+            }
+            catch
+            {
+                fail = true;
+            }
+
+            try
+            {
+                NegDivMinus1(int.MinValue);
+                fail = true;
+            }
+            catch
+            { }
+
+            try
+            {
+                LongNegDivMinus1(long.MinValue);
+                fail = true;
+            }
+            catch
+            { }
+
+            if (fail)
+            {
+                Console.WriteLine("CheckNegDiv failed");
+                return 101;
+            }
+            return 100;
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int NegDiv11(int a) => -(a / 11);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static long LongNegDiv1000(long a) => -(a / 1000);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int NegDivIntMinValue(int a) => -(a / int.MinValue);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static long LongNegDivLongMinValue(long a) => -(a / long.MinValue);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int NegDiv1(int a) => -(a / 1);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static long LongNegDiv1(long a) => -(a / 1);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static int NegDivMinus1(int a) => -(a / -1);
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        static long LongNegDivMinus1(long a) => -(a / -1);
+
+        static int Main()
+        {
+            if (CheckMulNeg() != 100)
+            {
+                return 101;
+            }
+            if (CheckNegMul() != 100)
+            {
+                return 101;
+            }
+            if (CheckDivNeg() != 100)
+            {
+                return 101;
+            }
+            if (CheckNegDiv() != 100)
+            {
+                return 101;
+            }
+
+            return 100;
+        }
+    }
+}
diff --git a/src/tests/JIT/opt/InstructionCombining/NegMulOrDivToConst.csproj b/src/tests/JIT/opt/InstructionCombining/NegMulOrDivToConst.csproj
new file mode 100644 (file)
index 0000000..c6f2eea
--- /dev/null
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+  </PropertyGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="NegMulOrDivToConst.cs" />
+  </ItemGroup>
+</Project>