Skip to content

Commit 786b288

Browse files
authored
Refactor handling of two immediates in hwintrinsic (#102071)
* Refactor handling of two immediates in hwintrinsic * Add immOp2 assert * Update comments for getHWIntrinsicImmTypes * fix arg name * update function summaries
1 parent 6c3245e commit 786b288

8 files changed

+331
-175
lines changed

src/coreclr/jit/codegen.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1009,7 +1009,7 @@ class CodeGen final : public CodeGenInterface
10091009
class HWIntrinsicImmOpHelper final
10101010
{
10111011
public:
1012-
HWIntrinsicImmOpHelper(CodeGen* codeGen, GenTree* immOp, GenTreeHWIntrinsic* intrin);
1012+
HWIntrinsicImmOpHelper(CodeGen* codeGen, GenTree* immOp, GenTreeHWIntrinsic* intrin, int immNum = 1);
10131013

10141014
void EmitBegin();
10151015
void EmitCaseEnd();

src/coreclr/jit/compiler.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4603,6 +4603,34 @@ class Compiler
46034603
NamedIntrinsic intrinsic, GenTree* immOp, bool mustExpand, int immLowerBound, int immUpperBound);
46044604
GenTree* addRangeCheckForHWIntrinsic(GenTree* immOp, int immLowerBound, int immUpperBound);
46054605

4606+
void getHWIntrinsicImmOps(NamedIntrinsic intrinsic,
4607+
CORINFO_SIG_INFO* sig,
4608+
GenTree** immOp1Ptr,
4609+
GenTree** immOp2Ptr);
4610+
4611+
bool CheckHWIntrinsicImmRange(NamedIntrinsic intrinsic,
4612+
CorInfoType simdBaseJitType,
4613+
GenTree* immOp,
4614+
bool mustExpand,
4615+
int immLowerBound,
4616+
int immUpperBound,
4617+
bool hasFullRangeImm,
4618+
bool *useFallback);
4619+
4620+
#if defined(TARGET_ARM64)
4621+
4622+
void getHWIntrinsicImmTypes(NamedIntrinsic intrinsic,
4623+
CORINFO_SIG_INFO* sig,
4624+
unsigned immNumber,
4625+
var_types simdBaseType,
4626+
CorInfoType simdBaseJitType,
4627+
CORINFO_CLASS_HANDLE op2ClsHnd,
4628+
CORINFO_CLASS_HANDLE op3ClsHnd,
4629+
unsigned* immSimdSize,
4630+
var_types* immSimdBaseType);
4631+
4632+
#endif // TARGET_ARM64
4633+
46064634
#endif // FEATURE_HW_INTRINSICS
46074635
GenTree* impArrayAccessIntrinsic(CORINFO_CLASS_HANDLE clsHnd,
46084636
CORINFO_SIG_INFO* sig,

src/coreclr/jit/hwintrinsic.cpp

Lines changed: 127 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,97 @@ struct HWIntrinsicSignatureReader final
10321032
}
10331033
};
10341034

1035+
//------------------------------------------------------------------------
1036+
// CheckHWIntrinsicImmRange: Check if an immediate is within the valid range
1037+
//
1038+
// Arguments:
1039+
// intrinsicId -- HW intrinsic id
1040+
// simdBaseJitType -- The base JIT type of SIMD type of the intrinsic
1041+
// immOp -- Immediate to check is within range
1042+
// mustExpand -- true if the intrinsic must expand to a GenTree*; otherwise, false
1043+
// immLowerBound -- the lower valid bound of the immediate
1044+
// immLowerBound -- the upper valid bound of the immediate
1045+
// hasFullRangeImm -- the range has all valid values. The immediate is always within range.
1046+
// useFallback [OUT] -- Only set if false is returned. A fallback can be used instead.
1047+
//
1048+
// Return Value:
1049+
// returns true if immOp is within range. Otherwise false.
1050+
//
1051+
bool Compiler::CheckHWIntrinsicImmRange(NamedIntrinsic intrinsic,
1052+
CorInfoType simdBaseJitType,
1053+
GenTree* immOp,
1054+
bool mustExpand,
1055+
int immLowerBound,
1056+
int immUpperBound,
1057+
bool hasFullRangeImm,
1058+
bool* useFallback)
1059+
{
1060+
*useFallback = false;
1061+
1062+
if (!hasFullRangeImm && immOp->IsCnsIntOrI())
1063+
{
1064+
const int ival = (int)immOp->AsIntCon()->IconValue();
1065+
bool immOutOfRange;
1066+
#ifdef TARGET_XARCH
1067+
if (HWIntrinsicInfo::isAVX2GatherIntrinsic(intrinsic))
1068+
{
1069+
immOutOfRange = (ival != 1) && (ival != 2) && (ival != 4) && (ival != 8);
1070+
}
1071+
else
1072+
#endif
1073+
{
1074+
immOutOfRange = (ival < immLowerBound) || (ival > immUpperBound);
1075+
}
1076+
1077+
if (immOutOfRange)
1078+
{
1079+
assert(!mustExpand);
1080+
// The imm-HWintrinsics that do not accept all imm8 values may throw
1081+
// ArgumentOutOfRangeException when the imm argument is not in the valid range
1082+
return false;
1083+
}
1084+
}
1085+
else if (!immOp->IsCnsIntOrI())
1086+
{
1087+
if (HWIntrinsicInfo::NoJmpTableImm(intrinsic))
1088+
{
1089+
*useFallback = true;
1090+
return false;
1091+
}
1092+
#if defined(TARGET_XARCH)
1093+
else if (HWIntrinsicInfo::MaybeNoJmpTableImm(intrinsic))
1094+
{
1095+
#if defined(TARGET_X86)
1096+
var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
1097+
1098+
if (varTypeIsLong(simdBaseType))
1099+
{
1100+
if (!mustExpand)
1101+
{
1102+
return false;
1103+
}
1104+
}
1105+
else
1106+
#endif // TARGET_XARCH
1107+
{
1108+
*useFallback = true;
1109+
return false;
1110+
}
1111+
}
1112+
#endif // TARGET_XARCH
1113+
else if (!mustExpand)
1114+
{
1115+
// When the imm-argument is not a constant and we are not being forced to expand, we need to
1116+
// return false so a GT_CALL to the intrinsic method is emitted instead. The
1117+
// intrinsic method is recursive and will be forced to expand, at which point
1118+
// we emit some less efficient fallback code.
1119+
return false;
1120+
}
1121+
}
1122+
1123+
return true;
1124+
}
1125+
10351126
//------------------------------------------------------------------------
10361127
// impHWIntrinsic: Import a hardware intrinsic as a GT_HWINTRINSIC node if possible
10371128
//
@@ -1162,190 +1253,64 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
11621253
}
11631254

11641255
var_types simdBaseType = TYP_UNKNOWN;
1165-
GenTree* immOp = nullptr;
11661256

11671257
if (simdBaseJitType != CORINFO_TYPE_UNDEF)
11681258
{
11691259
simdBaseType = JitType2PreciseVarType(simdBaseJitType);
11701260
}
11711261

1262+
const unsigned simdSize = HWIntrinsicInfo::lookupSimdSize(this, intrinsic, sig);
1263+
11721264
HWIntrinsicSignatureReader sigReader;
11731265
sigReader.Read(info.compCompHnd, sig);
11741266

1175-
#ifdef TARGET_ARM64
1176-
if ((intrinsic == NI_AdvSimd_Insert) || (intrinsic == NI_AdvSimd_InsertScalar) ||
1177-
((intrinsic >= NI_AdvSimd_LoadAndInsertScalar) && (intrinsic <= NI_AdvSimd_LoadAndInsertScalarVector64x4)) ||
1178-
((intrinsic >= NI_AdvSimd_Arm64_LoadAndInsertScalar) &&
1179-
(intrinsic <= NI_AdvSimd_Arm64_LoadAndInsertScalarVector128x4)))
1180-
{
1181-
assert(sig->numArgs == 3);
1182-
immOp = impStackTop(1).val;
1183-
assert(HWIntrinsicInfo::isImmOp(intrinsic, immOp));
1184-
}
1185-
else if (intrinsic == NI_AdvSimd_Arm64_InsertSelectedScalar)
1186-
{
1187-
// InsertSelectedScalar intrinsic has two immediate operands.
1188-
// Since all the remaining intrinsics on both platforms have only one immediate
1189-
// operand, in order to not complicate the shared logic even further we ensure here that
1190-
// 1) The second immediate operand immOp2 is constant and
1191-
// 2) its value belongs to [0, sizeof(op3) / sizeof(op3.BaseType)).
1192-
// If either is false, we should fallback to the managed implementation Insert(dst, dstIdx, Extract(src,
1193-
// srcIdx)).
1194-
// The check for the first immediate operand immOp will use the same logic as other intrinsics that have an
1195-
// immediate operand.
1196-
1197-
GenTree* immOp2 = nullptr;
1198-
1199-
assert(sig->numArgs == 4);
1200-
1201-
immOp = impStackTop(2).val;
1202-
immOp2 = impStackTop().val;
1203-
1204-
assert(HWIntrinsicInfo::isImmOp(intrinsic, immOp));
1205-
assert(HWIntrinsicInfo::isImmOp(intrinsic, immOp2));
1206-
1207-
if (!immOp2->IsCnsIntOrI())
1208-
{
1209-
assert(HWIntrinsicInfo::NoJmpTableImm(intrinsic));
1210-
return impNonConstFallback(intrinsic, retType, simdBaseJitType);
1211-
}
1212-
1213-
unsigned int otherSimdSize = 0;
1214-
CorInfoType otherBaseJitType = getBaseJitTypeAndSizeOfSIMDType(sigReader.op3ClsHnd, &otherSimdSize);
1215-
var_types otherBaseType = JitType2PreciseVarType(otherBaseJitType);
1216-
1217-
assert(otherBaseJitType == simdBaseJitType);
1218-
1219-
int immLowerBound2 = 0;
1220-
int immUpperBound2 = 0;
1221-
1222-
HWIntrinsicInfo::lookupImmBounds(intrinsic, otherSimdSize, otherBaseType, &immLowerBound2, &immUpperBound2);
1267+
GenTree* immOp1 = nullptr;
1268+
GenTree* immOp2 = nullptr;
1269+
int immLowerBound = 0;
1270+
int immUpperBound = 0;
1271+
bool hasFullRangeImm = false;
1272+
bool useFallback = false;
12231273

1224-
const int immVal2 = (int)immOp2->AsIntCon()->IconValue();
1274+
getHWIntrinsicImmOps(intrinsic, sig, &immOp1, &immOp2);
12251275

1226-
if ((immVal2 < immLowerBound2) || (immVal2 > immUpperBound2))
1276+
// Validate the second immediate
1277+
#ifdef TARGET_ARM64
1278+
if (immOp2 != nullptr)
1279+
{
1280+
unsigned immSimdSize = simdSize;
1281+
var_types immSimdBaseType = simdBaseType;
1282+
getHWIntrinsicImmTypes(intrinsic, sig, 2, simdBaseType, simdBaseJitType, sigReader.op2ClsHnd,
1283+
sigReader.op3ClsHnd, &immSimdSize, &immSimdBaseType);
1284+
HWIntrinsicInfo::lookupImmBounds(intrinsic, immSimdSize, immSimdBaseType, 2, &immLowerBound, &immUpperBound);
1285+
1286+
if (!CheckHWIntrinsicImmRange(intrinsic, simdBaseJitType, immOp2, mustExpand, immLowerBound, immUpperBound,
1287+
false, &useFallback))
12271288
{
1228-
assert(!mustExpand);
1229-
return nullptr;
1289+
return useFallback ? impNonConstFallback(intrinsic, retType, simdBaseJitType) : nullptr;
12301290
}
12311291
}
1232-
else
1292+
#else
1293+
assert(immOp2 == nullptr);
12331294
#endif
1234-
if ((sig->numArgs > 0) && HWIntrinsicInfo::isImmOp(intrinsic, impStackTop().val))
1235-
{
1236-
// NOTE: The following code assumes that for all intrinsics
1237-
// taking an immediate operand, that operand will be last.
1238-
immOp = impStackTop().val;
1239-
}
1240-
1241-
const unsigned simdSize = HWIntrinsicInfo::lookupSimdSize(this, intrinsic, sig);
1242-
1243-
int immLowerBound = 0;
1244-
int immUpperBound = 0;
1245-
bool hasFullRangeImm = false;
12461295

1247-
if (immOp != nullptr)
1296+
// Validate the first immediate
1297+
if (immOp1 != nullptr)
12481298
{
1249-
#ifdef TARGET_XARCH
1299+
#ifdef TARGET_ARM64
1300+
unsigned immSimdSize = simdSize;
1301+
var_types immSimdBaseType = simdBaseType;
1302+
getHWIntrinsicImmTypes(intrinsic, sig, 1, simdBaseType, simdBaseJitType, sigReader.op2ClsHnd,
1303+
sigReader.op3ClsHnd, &immSimdSize, &immSimdBaseType);
1304+
HWIntrinsicInfo::lookupImmBounds(intrinsic, immSimdSize, immSimdBaseType, 1, &immLowerBound, &immUpperBound);
1305+
#else
12501306
immUpperBound = HWIntrinsicInfo::lookupImmUpperBound(intrinsic);
12511307
hasFullRangeImm = HWIntrinsicInfo::HasFullRangeImm(intrinsic);
1252-
#elif defined(TARGET_ARM64)
1253-
if (category == HW_Category_SIMDByIndexedElement)
1254-
{
1255-
CorInfoType indexedElementBaseJitType;
1256-
var_types indexedElementBaseType;
1257-
unsigned int indexedElementSimdSize = 0;
1258-
1259-
if (numArgs == 3)
1260-
{
1261-
indexedElementBaseJitType =
1262-
getBaseJitTypeAndSizeOfSIMDType(sigReader.op2ClsHnd, &indexedElementSimdSize);
1263-
indexedElementBaseType = JitType2PreciseVarType(indexedElementBaseJitType);
1264-
}
1265-
else
1266-
{
1267-
assert(numArgs == 4);
1268-
indexedElementBaseJitType =
1269-
getBaseJitTypeAndSizeOfSIMDType(sigReader.op3ClsHnd, &indexedElementSimdSize);
1270-
indexedElementBaseType = JitType2PreciseVarType(indexedElementBaseJitType);
1271-
1272-
if (intrinsic == NI_Dp_DotProductBySelectedQuadruplet)
1273-
{
1274-
assert(((simdBaseType == TYP_INT) && (indexedElementBaseType == TYP_BYTE)) ||
1275-
((simdBaseType == TYP_UINT) && (indexedElementBaseType == TYP_UBYTE)));
1276-
// The second source operand of sdot, udot instructions is an indexed 32-bit element.
1277-
indexedElementBaseJitType = simdBaseJitType;
1278-
indexedElementBaseType = simdBaseType;
1279-
}
1280-
}
1281-
1282-
assert(indexedElementBaseType == simdBaseType);
1283-
HWIntrinsicInfo::lookupImmBounds(intrinsic, indexedElementSimdSize, simdBaseType, &immLowerBound,
1284-
&immUpperBound);
1285-
}
1286-
else
1287-
{
1288-
HWIntrinsicInfo::lookupImmBounds(intrinsic, simdSize, simdBaseType, &immLowerBound, &immUpperBound);
1289-
}
12901308
#endif
12911309

1292-
if (!hasFullRangeImm && immOp->IsCnsIntOrI())
1310+
if (!CheckHWIntrinsicImmRange(intrinsic, simdBaseJitType, immOp1, mustExpand, immLowerBound, immUpperBound,
1311+
hasFullRangeImm, &useFallback))
12931312
{
1294-
const int ival = (int)immOp->AsIntCon()->IconValue();
1295-
bool immOutOfRange;
1296-
#ifdef TARGET_XARCH
1297-
if (HWIntrinsicInfo::isAVX2GatherIntrinsic(intrinsic))
1298-
{
1299-
immOutOfRange = (ival != 1) && (ival != 2) && (ival != 4) && (ival != 8);
1300-
}
1301-
else
1302-
#endif
1303-
{
1304-
immOutOfRange = (ival < immLowerBound) || (ival > immUpperBound);
1305-
}
1306-
1307-
if (immOutOfRange)
1308-
{
1309-
assert(!mustExpand);
1310-
// The imm-HWintrinsics that do not accept all imm8 values may throw
1311-
// ArgumentOutOfRangeException when the imm argument is not in the valid range
1312-
return nullptr;
1313-
}
1314-
}
1315-
else if (!immOp->IsCnsIntOrI())
1316-
{
1317-
if (HWIntrinsicInfo::NoJmpTableImm(intrinsic))
1318-
{
1319-
return impNonConstFallback(intrinsic, retType, simdBaseJitType);
1320-
}
1321-
#if defined(TARGET_XARCH)
1322-
else if (HWIntrinsicInfo::MaybeNoJmpTableImm(intrinsic))
1323-
{
1324-
#if defined(TARGET_X86)
1325-
var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
1326-
1327-
if (varTypeIsLong(simdBaseType))
1328-
{
1329-
if (!mustExpand)
1330-
{
1331-
return nullptr;
1332-
}
1333-
}
1334-
else
1335-
#endif // TARGET_XARCH
1336-
{
1337-
return impNonConstFallback(intrinsic, retType, simdBaseJitType);
1338-
}
1339-
}
1340-
#endif // TARGET_XARCH
1341-
else if (!mustExpand)
1342-
{
1343-
// When the imm-argument is not a constant and we are not being forced to expand, we need to
1344-
// return nullptr so a GT_CALL to the intrinsic method is emitted instead. The
1345-
// intrinsic method is recursive and will be forced to expand, at which point
1346-
// we emit some less efficient fallback code.
1347-
return nullptr;
1348-
}
1313+
return useFallback ? impNonConstFallback(intrinsic, retType, simdBaseJitType) : nullptr;
13491314
}
13501315
}
13511316

src/coreclr/jit/hwintrinsic.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ struct HWIntrinsicInfo
514514
static int lookupImmUpperBound(NamedIntrinsic intrinsic);
515515
#elif defined(TARGET_ARM64)
516516
static void lookupImmBounds(
517-
NamedIntrinsic intrinsic, int simdSize, var_types baseType, int* lowerBound, int* upperBound);
517+
NamedIntrinsic intrinsic, int simdSize, var_types baseType, int immNumber, int* lowerBound, int* upperBound);
518518
#else
519519
#error Unsupported platform
520520
#endif

0 commit comments

Comments
 (0)