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)
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)
{
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;
--- /dev/null
+// 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;
+ }
+ }
+}