Skip to content

Commit 9a31a5b

Browse files
JIT: Preserve range check for HW intrinsics with non-const/out-of-range immediates (#106765)
1 parent fb8e078 commit 9a31a5b

File tree

8 files changed

+241
-41
lines changed

8 files changed

+241
-41
lines changed

src/coreclr/jit/compiler.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3446,7 +3446,6 @@ class Compiler
34463446
GenTreeHWIntrinsic* gtNewScalarHWIntrinsicNode(
34473447
var_types type, GenTree* op1, GenTree* op2, GenTree* op3, NamedIntrinsic hwIntrinsicID);
34483448
CorInfoType getBaseJitTypeFromArgIfNeeded(NamedIntrinsic intrinsic,
3449-
CORINFO_CLASS_HANDLE clsHnd,
34503449
CORINFO_SIG_INFO* sig,
34513450
CorInfoType simdBaseJitType);
34523451

@@ -4718,7 +4717,7 @@ class Compiler
47184717
GenTree* getArgForHWIntrinsic(var_types argType, CORINFO_CLASS_HANDLE argClass);
47194718
GenTree* impNonConstFallback(NamedIntrinsic intrinsic, var_types simdType, CorInfoType simdBaseJitType);
47204719
GenTree* addRangeCheckIfNeeded(
4721-
NamedIntrinsic intrinsic, GenTree* immOp, bool mustExpand, int immLowerBound, int immUpperBound);
4720+
NamedIntrinsic intrinsic, GenTree* immOp, int immLowerBound, int immUpperBound);
47224721
GenTree* addRangeCheckForHWIntrinsic(GenTree* immOp, int immLowerBound, int immUpperBound);
47234722

47244723
void getHWIntrinsicImmOps(NamedIntrinsic intrinsic,

src/coreclr/jit/hwintrinsic.cpp

Lines changed: 85 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -718,18 +718,16 @@ uint8_t TernaryLogicInfo::GetTernaryControlByte(const TernaryLogicInfo& info, ui
718718
//
719719
// Arguments:
720720
// intrinsic -- id of the intrinsic function.
721-
// clsHnd -- class handle containing the intrinsic function.
722721
// method -- method handle of the intrinsic function.
723722
// sig -- signature of the intrinsic call.
724723
// simdBaseJitType -- Predetermined simdBaseJitType, could be CORINFO_TYPE_UNDEF
725724
//
726725
// Return Value:
727726
// The basetype of intrinsic of it can be fetched from 1st or 2nd argument, else return baseType unmodified.
728727
//
729-
CorInfoType Compiler::getBaseJitTypeFromArgIfNeeded(NamedIntrinsic intrinsic,
730-
CORINFO_CLASS_HANDLE clsHnd,
731-
CORINFO_SIG_INFO* sig,
732-
CorInfoType simdBaseJitType)
728+
CorInfoType Compiler::getBaseJitTypeFromArgIfNeeded(NamedIntrinsic intrinsic,
729+
CORINFO_SIG_INFO* sig,
730+
CorInfoType simdBaseJitType)
733731
{
734732
if (HWIntrinsicInfo::BaseTypeFromSecondArg(intrinsic) || HWIntrinsicInfo::BaseTypeFromFirstArg(intrinsic))
735733
{
@@ -1332,29 +1330,26 @@ GenTree* Compiler::getArgForHWIntrinsic(var_types argType, CORINFO_CLASS_HANDLE
13321330
// Arguments:
13331331
// intrinsic -- intrinsic ID
13341332
// immOp -- the immediate operand of the intrinsic
1335-
// mustExpand -- true if the compiler is compiling the fallback(GT_CALL) of this intrinsics
13361333
// immLowerBound -- lower incl. bound for a value of the immediate operand (for a non-full-range imm-intrinsic)
13371334
// immUpperBound -- upper incl. bound for a value of the immediate operand (for a non-full-range imm-intrinsic)
13381335
//
13391336
// Return Value:
13401337
// add a GT_BOUNDS_CHECK node for non-full-range imm-intrinsic, which would throw ArgumentOutOfRangeException
13411338
// when the imm-argument is not in the valid range
13421339
//
1343-
GenTree* Compiler::addRangeCheckIfNeeded(
1344-
NamedIntrinsic intrinsic, GenTree* immOp, bool mustExpand, int immLowerBound, int immUpperBound)
1340+
GenTree* Compiler::addRangeCheckIfNeeded(NamedIntrinsic intrinsic, GenTree* immOp, int immLowerBound, int immUpperBound)
13451341
{
13461342
assert(immOp != nullptr);
13471343
// Full-range imm-intrinsics do not need the range-check
13481344
// because the imm-parameter of the intrinsic method is a byte.
13491345
// AVX2 Gather intrinsics no not need the range-check
13501346
// because their imm-parameter have discrete valid values that are handle by managed code
1351-
if (mustExpand && HWIntrinsicInfo::isImmOp(intrinsic, immOp)
1347+
if (!immOp->IsCnsIntOrI() && HWIntrinsicInfo::isImmOp(intrinsic, immOp)
13521348
#ifdef TARGET_XARCH
13531349
&& !HWIntrinsicInfo::isAVX2GatherIntrinsic(intrinsic) && !HWIntrinsicInfo::HasFullRangeImm(intrinsic)
13541350
#endif
13551351
)
13561352
{
1357-
assert(!immOp->IsCnsIntOrI());
13581353
assert(varTypeIsIntegral(immOp));
13591354

13601355
return addRangeCheckForHWIntrinsic(immOp, immLowerBound, immUpperBound);
@@ -1596,7 +1591,6 @@ bool Compiler::CheckHWIntrinsicImmRange(NamedIntrinsic intrinsic,
15961591

15971592
if (immOutOfRange)
15981593
{
1599-
assert(!mustExpand);
16001594
// The imm-HWintrinsics that do not accept all imm8 values may throw
16011595
// ArgumentOutOfRangeException when the imm argument is not in the valid range,
16021596
// unless the intrinsic can be transformed into one that does accept all imm8 values
@@ -1764,7 +1758,8 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
17641758
}
17651759
}
17661760

1767-
simdBaseJitType = getBaseJitTypeFromArgIfNeeded(intrinsic, clsHnd, sig, simdBaseJitType);
1761+
simdBaseJitType = getBaseJitTypeFromArgIfNeeded(intrinsic, sig, simdBaseJitType);
1762+
unsigned simdSize = 0;
17681763

17691764
if (simdBaseJitType == CORINFO_TYPE_UNDEF)
17701765
{
@@ -1783,7 +1778,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
17831778

17841779
simdBaseJitType = getBaseJitTypeAndSizeOfSIMDType(clsHnd, &sizeBytes);
17851780

1786-
#if defined(TARGET_ARM64)
1781+
#ifdef TARGET_ARM64
17871782
if (simdBaseJitType == CORINFO_TYPE_UNDEF && HWIntrinsicInfo::HasScalarInputVariant(intrinsic))
17881783
{
17891784
// Did not find a valid vector type. The intrinsic has alternate scalar version. Switch to that.
@@ -1799,12 +1794,40 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
17991794
assert(simdBaseJitType != CORINFO_TYPE_VALUECLASS);
18001795
}
18011796
else
1802-
#endif
1797+
#endif // TARGET_ARM64
18031798
{
18041799
assert((category == HW_Category_Special) || (category == HW_Category_Helper) || (sizeBytes != 0));
18051800
}
18061801
}
18071802
}
1803+
#ifdef TARGET_ARM64
1804+
else if ((simdBaseJitType == CORINFO_TYPE_VALUECLASS) && (HWIntrinsicInfo::BaseTypeFromValueTupleArg(intrinsic)))
1805+
{
1806+
// If HW_Flag_BaseTypeFromValueTupleArg is set, one of the base type position flags must be set.
1807+
// There is no point to using this flag if the SIMD size is known at compile-time.
1808+
assert(HWIntrinsicInfo::BaseTypeFromFirstArg(intrinsic) || HWIntrinsicInfo::BaseTypeFromSecondArg(intrinsic));
1809+
assert(!HWIntrinsicInfo::tryLookupSimdSize(intrinsic, &simdSize));
1810+
1811+
CORINFO_ARG_LIST_HANDLE arg = sig->args;
1812+
1813+
if (HWIntrinsicInfo::BaseTypeFromSecondArg(intrinsic))
1814+
{
1815+
arg = info.compCompHnd->getArgNext(arg);
1816+
}
1817+
1818+
CORINFO_CLASS_HANDLE argClass = info.compCompHnd->getArgClass(sig, arg);
1819+
INDEBUG(unsigned fieldCount = info.compCompHnd->getClassNumInstanceFields(argClass));
1820+
assert(fieldCount > 1);
1821+
1822+
CORINFO_CLASS_HANDLE classHnd;
1823+
CORINFO_FIELD_HANDLE fieldHandle = info.compCompHnd->getFieldInClass(argClass, 0);
1824+
CorInfoType fieldType = info.compCompHnd->getFieldType(fieldHandle, &classHnd);
1825+
assert(isIntrinsicType(classHnd));
1826+
1827+
simdBaseJitType = getBaseJitTypeAndSizeOfSIMDType(classHnd, &simdSize);
1828+
assert(simdSize > 0);
1829+
}
1830+
#endif // TARGET_ARM64
18081831

18091832
// Immediately return if the category is other than scalar/special and this is not a supported base type.
18101833
if ((category != HW_Category_Special) && (category != HW_Category_Scalar) && !HWIntrinsicInfo::isScalarIsa(isa) &&
@@ -1827,7 +1850,9 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
18271850
#endif // TARGET_XARCH
18281851
}
18291852

1830-
const unsigned simdSize = HWIntrinsicInfo::lookupSimdSize(this, intrinsic, sig);
1853+
// We may have already determined simdSize for intrinsics that require special handling.
1854+
// If so, skip the lookup.
1855+
simdSize = (simdSize == 0) ? HWIntrinsicInfo::lookupSimdSize(this, intrinsic, sig) : simdSize;
18311856

18321857
HWIntrinsicSignatureReader sigReader;
18331858
sigReader.Read(info.compCompHnd, sig);
@@ -1859,15 +1884,30 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
18591884
{
18601885
return impNonConstFallback(intrinsic, retType, simdBaseJitType);
18611886
}
1862-
else if (!opts.OptimizationEnabled())
1887+
else if (immOp2->IsCnsIntOrI())
18631888
{
1864-
// Only enable late stage rewriting if optimizations are enabled
1865-
// as we won't otherwise encounter a constant at the later point
1866-
return nullptr;
1889+
// If we know the immediate is out-of-range,
1890+
// convert the intrinsic into a user call (or throw if we must expand)
1891+
return impUnsupportedNamedIntrinsic(CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION, method, sig,
1892+
mustExpand);
18671893
}
18681894
else
18691895
{
1870-
setMethodHandle = true;
1896+
// The immediate is unknown, and we aren't using a fallback intrinsic.
1897+
// In this case, CheckHWIntrinsicImmRange should not return false for intrinsics that must expand.
1898+
assert(!mustExpand);
1899+
1900+
if (opts.OptimizationEnabled())
1901+
{
1902+
// Only enable late stage rewriting if optimizations are enabled
1903+
// as we won't otherwise encounter a constant at the later point
1904+
setMethodHandle = true;
1905+
}
1906+
else
1907+
{
1908+
// Just convert to a user call
1909+
return nullptr;
1910+
}
18711911
}
18721912
}
18731913
}
@@ -1896,15 +1936,30 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
18961936
{
18971937
return impNonConstFallback(intrinsic, retType, simdBaseJitType);
18981938
}
1899-
else if (!opts.OptimizationEnabled())
1939+
else if (immOp1->IsCnsIntOrI())
19001940
{
1901-
// Only enable late stage rewriting if optimizations are enabled
1902-
// as we won't otherwise encounter a constant at the later point
1903-
return nullptr;
1941+
// If we know the immediate is out-of-range,
1942+
// convert the intrinsic into a user call (or throw if we must expand)
1943+
return impUnsupportedNamedIntrinsic(CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION, method, sig,
1944+
mustExpand);
19041945
}
19051946
else
19061947
{
1907-
setMethodHandle = true;
1948+
// The immediate is unknown, and we aren't using a fallback intrinsic.
1949+
// In this case, CheckHWIntrinsicImmRange should not return false for intrinsics that must expand.
1950+
assert(!mustExpand);
1951+
1952+
if (opts.OptimizationEnabled())
1953+
{
1954+
// Only enable late stage rewriting if optimizations are enabled
1955+
// as we won't otherwise encounter a constant at the later point
1956+
setMethodHandle = true;
1957+
}
1958+
else
1959+
{
1960+
// Just convert to a user call
1961+
return nullptr;
1962+
}
19081963
}
19091964
}
19101965
}
@@ -1960,7 +2015,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
19602015
{
19612016
case 4:
19622017
op4 = getArgForHWIntrinsic(sigReader.GetOp4Type(), sigReader.op4ClsHnd);
1963-
op4 = addRangeCheckIfNeeded(intrinsic, op4, mustExpand, immLowerBound, immUpperBound);
2018+
op4 = addRangeCheckIfNeeded(intrinsic, op4, immLowerBound, immUpperBound);
19642019
op3 = getArgForHWIntrinsic(sigReader.GetOp3Type(), sigReader.op3ClsHnd);
19652020
op2 = getArgForHWIntrinsic(sigReader.GetOp2Type(), sigReader.op2ClsHnd);
19662021
op1 = getArgForHWIntrinsic(sigReader.GetOp1Type(), sigReader.op1ClsHnd);
@@ -1974,7 +2029,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
19742029

19752030
case 2:
19762031
op2 = getArgForHWIntrinsic(sigReader.GetOp2Type(), sigReader.op2ClsHnd);
1977-
op2 = addRangeCheckIfNeeded(intrinsic, op2, mustExpand, immLowerBound, immUpperBound);
2032+
op2 = addRangeCheckIfNeeded(intrinsic, op2, immLowerBound, immUpperBound);
19782033
op1 = getArgForHWIntrinsic(sigReader.GetOp1Type(), sigReader.op1ClsHnd);
19792034
break;
19802035

@@ -2144,7 +2199,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
21442199
#ifdef TARGET_ARM64
21452200
if (intrinsic == NI_AdvSimd_LoadAndInsertScalar)
21462201
{
2147-
op2 = addRangeCheckIfNeeded(intrinsic, op2, mustExpand, immLowerBound, immUpperBound);
2202+
op2 = addRangeCheckIfNeeded(intrinsic, op2, immLowerBound, immUpperBound);
21482203

21492204
if (op1->OperIs(GT_CAST))
21502205
{
@@ -2158,12 +2213,12 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
21582213
}
21592214
else if ((intrinsic == NI_AdvSimd_Insert) || (intrinsic == NI_AdvSimd_InsertScalar))
21602215
{
2161-
op2 = addRangeCheckIfNeeded(intrinsic, op2, mustExpand, immLowerBound, immUpperBound);
2216+
op2 = addRangeCheckIfNeeded(intrinsic, op2, immLowerBound, immUpperBound);
21622217
}
21632218
else
21642219
#endif
21652220
{
2166-
op3 = addRangeCheckIfNeeded(intrinsic, op3, mustExpand, immLowerBound, immUpperBound);
2221+
op3 = addRangeCheckIfNeeded(intrinsic, op3, immLowerBound, immUpperBound);
21672222
}
21682223

21692224
retNode = isScalar ? gtNewScalarHWIntrinsicNode(nodeRetType, op1, op2, op3, intrinsic)

src/coreclr/jit/hwintrinsic.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ enum HWIntrinsicFlag : unsigned int
225225
// (instead of merging).
226226
HW_Flag_ZeroingMaskedOperation = 0x800000,
227227

228+
// The intrinsic has an overload where the base type is extracted from a ValueTuple of SIMD types
229+
// (HW_Flag_BaseTypeFrom{First, Second}Arg must also be set to denote the position of the ValueTuple)
230+
HW_Flag_BaseTypeFromValueTupleArg = 0x1000000,
231+
228232
#else
229233
#error Unsupported platform
230234
#endif
@@ -988,6 +992,12 @@ struct HWIntrinsicInfo
988992
return (flags & HW_Flag_ZeroingMaskedOperation) != 0;
989993
}
990994

995+
static bool BaseTypeFromValueTupleArg(NamedIntrinsic id)
996+
{
997+
const HWIntrinsicFlag flags = lookupFlags(id);
998+
return (flags & HW_Flag_BaseTypeFromValueTupleArg) != 0;
999+
}
1000+
9911001
static NamedIntrinsic GetScalarInputVariant(NamedIntrinsic id)
9921002
{
9931003
assert(HasScalarInputVariant(id));

src/coreclr/jit/hwintrinsicarm64.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2466,7 +2466,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
24662466

24672467
assert(HWIntrinsicInfo::isImmOp(intrinsic, op3));
24682468
HWIntrinsicInfo::lookupImmBounds(intrinsic, simdSize, simdBaseType, 1, &immLowerBound, &immUpperBound);
2469-
op3 = addRangeCheckIfNeeded(intrinsic, op3, (!op3->IsCnsIntOrI()), immLowerBound, immUpperBound);
2469+
op3 = addRangeCheckIfNeeded(intrinsic, op3, immLowerBound, immUpperBound);
24702470
argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg1, &argClass)));
24712471
op1 = getArgForHWIntrinsic(argType, argClass);
24722472

@@ -2939,11 +2939,11 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
29392939

29402940
assert(HWIntrinsicInfo::isImmOp(intrinsic, op2));
29412941
HWIntrinsicInfo::lookupImmBounds(intrinsic, simdSize, simdBaseType, 1, &immLowerBound, &immUpperBound);
2942-
op2 = addRangeCheckIfNeeded(intrinsic, op2, (!op2->IsCnsIntOrI()), immLowerBound, immUpperBound);
2942+
op2 = addRangeCheckIfNeeded(intrinsic, op2, immLowerBound, immUpperBound);
29432943

29442944
assert(HWIntrinsicInfo::isImmOp(intrinsic, op3));
29452945
HWIntrinsicInfo::lookupImmBounds(intrinsic, simdSize, simdBaseType, 2, &immLowerBound, &immUpperBound);
2946-
op3 = addRangeCheckIfNeeded(intrinsic, op3, (!op3->IsCnsIntOrI()), immLowerBound, immUpperBound);
2946+
op3 = addRangeCheckIfNeeded(intrinsic, op3, immLowerBound, immUpperBound);
29472947

29482948
retNode = isScalar ? gtNewScalarHWIntrinsicNode(retType, op1, op2, op3, intrinsic)
29492949
: gtNewSimdHWIntrinsicNode(retType, op1, op2, op3, intrinsic, simdBaseJitType, simdSize);
@@ -3010,7 +3010,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
30103010
op3 = getArgForHWIntrinsic(argType, argClass);
30113011

30123012
assert(HWIntrinsicInfo::isImmOp(intrinsic, op3));
3013-
op3 = addRangeCheckIfNeeded(intrinsic, op3, mustExpand, immLowerBound, immUpperBound);
3013+
op3 = addRangeCheckIfNeeded(intrinsic, op3, immLowerBound, immUpperBound);
30143014

30153015
argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg2, &argClass)));
30163016
op2 = getArgForHWIntrinsic(argType, argClass);
@@ -3040,7 +3040,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
30403040
op4 = getArgForHWIntrinsic(argType, argClass);
30413041

30423042
assert(HWIntrinsicInfo::isImmOp(intrinsic, op4));
3043-
op4 = addRangeCheckIfNeeded(intrinsic, op4, mustExpand, immLowerBound, immUpperBound);
3043+
op4 = addRangeCheckIfNeeded(intrinsic, op4, immLowerBound, immUpperBound);
30443044

30453045
argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg3, &argClass)));
30463046
op3 = getArgForHWIntrinsic(argType, argClass);
@@ -3108,12 +3108,12 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
31083108
argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg5, &argClass)));
31093109
GenTree* op5 = getArgForHWIntrinsic(argType, argClass);
31103110
assert(HWIntrinsicInfo::isImmOp(intrinsic, op5));
3111-
op5 = addRangeCheckIfNeeded(intrinsic, op5, mustExpand, imm1LowerBound, imm1UpperBound);
3111+
op5 = addRangeCheckIfNeeded(intrinsic, op5, imm1LowerBound, imm1UpperBound);
31123112

31133113
argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg4, &argClass)));
31143114
op4 = getArgForHWIntrinsic(argType, argClass);
31153115
assert(HWIntrinsicInfo::isImmOp(intrinsic, op4));
3116-
op4 = addRangeCheckIfNeeded(intrinsic, op4, mustExpand, imm2LowerBound, imm2UpperBound);
3116+
op4 = addRangeCheckIfNeeded(intrinsic, op4, imm2LowerBound, imm2UpperBound);
31173117

31183118
argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg3, &argClass)));
31193119
op3 = getArgForHWIntrinsic(argType, argClass);

0 commit comments

Comments
 (0)