Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
109 changes: 101 additions & 8 deletions src/coreclr/jit/valuenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
//
Expand All @@ -15877,16 +15929,39 @@ 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
ValueNumPair vnpDisivorNorm = vnStore->VNPNormalPair(divisor->gtVNPair);
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))
{
Expand All @@ -15913,7 +15988,7 @@ ValueNumPair Compiler::fgValueNumberDivisionExceptions(genTreeOps oper, GenTree*
}
}
}
else // (typ == TYP_LONG)
else if (!isSimdOper) // (typ == TYP_LONG)
{
if (vnStore->IsVNConstant(vnDivisorNormLib))
{
Expand Down Expand Up @@ -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)
{
Expand Down
42 changes: 42 additions & 0 deletions src/tests/JIT/Regression_o_2/Runtime_133844.cs
Original file line number Diff line number Diff line change
@@ -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<int> _value = Vector.Create(1);
private Vector<int> _sink;
private int _zero;

[Fact]
public static void DivideByZeroIsPreserved()
{
Assert.Throws<DivideByZeroException>(new Runtime_133844().DivideByZero);
}

[Fact]
public static void OverflowIsPreserved()
{
Assert.Throws<OverflowException>(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));
}
}
Loading