diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 7bb50d17eb7aed..e7ebad5db09c43 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6676,7 +6676,10 @@ class Compiler void fgValueNumberAddExceptionSetForDivision(GenTree* tree); // Compute exceptions for a division operation - ValueNumPair fgValueNumberDivisionExceptions(genTreeOps oper, GenTree* dividend, GenTree* divisor); + ValueNumPair fgValueNumberDivisionExceptions(genTreeOps oper, + GenTree* dividend, + GenTree* divisor, + var_types simdBaseType = TYP_UNDEF); // Adds the exception set for the current tree node which is performing a overflow checking operation void fgValueNumberAddExceptionSetForOverflow(GenTree* tree); diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index ac8164bcc400c1..d5576983590379 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -14403,6 +14403,20 @@ void Compiler::fgValueNumberHWIntrinsic(GenTreeHWIntrinsic* tree) } } +#if defined(TARGET_XARCH) + // Vector division can throw both arithmetic overflow, if signed, and divide by zero exceptions + if (intrinsicId == NI_Vector_op_Division) + { + assert(opCount == 2); + assert(varTypeIsInt(tree->GetSimdBaseType())); + + genTreeOps oper = varTypeIsSigned(tree->GetSimdBaseType()) ? GT_DIV : GT_UDIV; + ValueNumPair divisionExc = + fgValueNumberDivisionExceptions(oper, tree->Op(1), tree->Op(2), tree->GetSimdBaseType()); + excSetPair = vnStore->VNPExcSetUnion(excSetPair, divisionExc); + } +#endif // TARGET_XARCH + // Some intrinsics should always be unique bool makeUnique = false; @@ -15855,19 +15869,57 @@ void Compiler::fgValueNumberAddExceptionSetForDivision(GenTree* tree) tree->gtVNPair = vnStore->VNPWithExc(vnpTreeNorm, newExcSet); } +//-------------------------------------------------------------------------------- +// SimdConstantContains: +// Determine whether an integral SIMD constant contains the specified value. +// +// Arguments: +// vnStore - Value number store +// vn - SIMD constant value number +// simdBaseType - SIMD base type +// value - Value to search for +// +// Return Value: +// True if any element equals value +// +#if defined(FEATURE_SIMD) +static bool SimdConstantContains(ValueNumStore* vnStore, ValueNum vn, var_types simdBaseType, INT64 value) +{ + assert(vnStore->IsVNConstant(vn)); + assert(varTypeIsSIMD(vnStore->TypeOfVN(vn))); + + simd_t simdValue = vnStore->GetConstantSimd(vn); + unsigned elementCount = genTypeSize(vnStore->TypeOfVN(vn)) / genTypeSize(simdBaseType); + + for (unsigned index = 0; index < elementCount; index++) + { + if (EvaluateGetElementIntegral(simdBaseType, simdValue, index) == value) + { + return true; + } + } + + return false; +} +#endif // FEATURE_SIMD + //-------------------------------------------------------------------------------- // fgValueNumberDivisionExceptions // Compute exception set for a division operation // // Arguments: -// oper - Division operation (signed/unsigned division/modulo) -// dividend - Tree representing dividend -// divisor - Tree representing divisor +// oper - Division operation (signed/unsigned division/modulo) +// dividend - Tree representing dividend +// divisor - Tree representing divisor +// simdBaseType - SIMD base type, or TYP_UNDEF for scalar division // // Return Value: // VNP representing exception set // -ValueNumPair Compiler::fgValueNumberDivisionExceptions(genTreeOps oper, GenTree* dividend, GenTree* divisor) +ValueNumPair Compiler::fgValueNumberDivisionExceptions(genTreeOps oper, + GenTree* dividend, + GenTree* divisor, + var_types simdBaseType) { // A Divide By Zero exception may be possible. // @@ -15877,8 +15929,11 @@ ValueNumPair Compiler::fgValueNumberDivisionExceptions(genTreeOps oper, GenTree* bool needArithmeticExcLib = !isUnsignedOper; // Overflow isn't possible for unsigned divide bool needArithmeticExcCon = !isUnsignedOper; + bool isSimdOper = varTypeIsSIMD(dividend); + assert(isSimdOper == (simdBaseType != TYP_UNDEF)); + // Determine if we have a 32-bit or 64-bit divide operation - var_types typ = genActualType(dividend); + var_types typ = isSimdOper ? genActualType(simdBaseType) : genActualType(dividend); assert((typ == TYP_INT) || (typ == TYP_LONG)); // Retrieve the Norm VN for divisor to use it for the DivideByZeroExc @@ -15886,7 +15941,27 @@ ValueNumPair Compiler::fgValueNumberDivisionExceptions(genTreeOps oper, GenTree* ValueNum vnDivisorNormLib = vnpDisivorNorm.GetLiberal(); ValueNum vnDivisorNormCon = vnpDisivorNorm.GetConservative(); - if (typ == TYP_INT) +#if defined(FEATURE_SIMD) + if (isSimdOper) + { + // DivideByZero exception unneeded for simd if constant divisor has all nonzero lanes + // Arithmetic exception unneeded if constant divisor has all lanes not equal to -1 + if (vnStore->IsVNConstant(vnDivisorNormLib)) + { + needDivideByZeroExcLib = SimdConstantContains(vnStore, vnDivisorNormLib, simdBaseType, 0); + needArithmeticExcLib = + needArithmeticExcLib && SimdConstantContains(vnStore, vnDivisorNormLib, simdBaseType, -1); + } + if (vnStore->IsVNConstant(vnDivisorNormCon)) + { + needDivideByZeroExcCon = SimdConstantContains(vnStore, vnDivisorNormCon, simdBaseType, 0); + needArithmeticExcCon = + needArithmeticExcCon && SimdConstantContains(vnStore, vnDivisorNormCon, simdBaseType, -1); + } + } +#endif // FEATURE_SIMD + + if (!isSimdOper && (typ == TYP_INT)) { if (vnStore->IsVNConstant(vnDivisorNormLib)) { @@ -15913,7 +15988,7 @@ ValueNumPair Compiler::fgValueNumberDivisionExceptions(genTreeOps oper, GenTree* } } } - else // (typ == TYP_LONG) + else if (!isSimdOper) // (typ == TYP_LONG) { if (vnStore->IsVNConstant(vnDivisorNormLib)) { @@ -15946,7 +16021,25 @@ ValueNumPair Compiler::fgValueNumberDivisionExceptions(genTreeOps oper, GenTree* ValueNum vnDividendNormLib = vnpDividendNorm.GetLiberal(); ValueNum vnDividendNormCon = vnpDividendNorm.GetConservative(); - if (needArithmeticExcLib || needArithmeticExcCon) +#if defined(FEATURE_SIMD) + if (isSimdOper) + { + INT64 minValue = (typ == TYP_INT) ? INT32_MIN : INT64_MIN; + + if (needArithmeticExcLib && vnStore->IsVNConstant(vnDividendNormLib) && + !SimdConstantContains(vnStore, vnDividendNormLib, simdBaseType, minValue)) + { + needArithmeticExcLib = false; + } + if (needArithmeticExcCon && vnStore->IsVNConstant(vnDividendNormCon) && + !SimdConstantContains(vnStore, vnDividendNormCon, simdBaseType, minValue)) + { + needArithmeticExcCon = false; + } + } +#endif // FEATURE_SIMD + + if (!isSimdOper && (needArithmeticExcLib || needArithmeticExcCon)) { if (typ == TYP_INT) { diff --git a/src/tests/JIT/Regression_o_2/Runtime_133844.cs b/src/tests/JIT/Regression_o_2/Runtime_133844.cs new file mode 100644 index 00000000000000..013ce53e746910 --- /dev/null +++ b/src/tests/JIT/Regression_o_2/Runtime_133844.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Runtime_133844; + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_133844 +{ + private int _negativeOne = -1; + private int _minValue = int.MinValue; + private Vector _value = Vector.Create(1); + private Vector _sink; + private int _zero; + + [Fact] + public static void DivideByZeroIsPreserved() + { + Assert.Throws(new Runtime_133844().DivideByZero); + } + + [Fact] + public static void OverflowIsPreserved() + { + Assert.Throws(new Runtime_133844().Overflow); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private void DivideByZero() + { + _sink = (_value ^ _value) * Vector.CreateHarmonicSequence(_zero, _zero); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private void Overflow() + { + _sink = (_value ^ _value) * (Vector.Create(_minValue) / Vector.Create(_negativeOne)); + } +}