@@ -1032,6 +1032,97 @@ struct HWIntrinsicSignatureReader final
1032
1032
}
1033
1033
};
1034
1034
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
+
1035
1126
// ------------------------------------------------------------------------
1036
1127
// impHWIntrinsic: Import a hardware intrinsic as a GT_HWINTRINSIC node if possible
1037
1128
//
@@ -1162,190 +1253,64 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
1162
1253
}
1163
1254
1164
1255
var_types simdBaseType = TYP_UNKNOWN;
1165
- GenTree* immOp = nullptr ;
1166
1256
1167
1257
if (simdBaseJitType != CORINFO_TYPE_UNDEF)
1168
1258
{
1169
1259
simdBaseType = JitType2PreciseVarType (simdBaseJitType);
1170
1260
}
1171
1261
1262
+ const unsigned simdSize = HWIntrinsicInfo::lookupSimdSize (this , intrinsic, sig);
1263
+
1172
1264
HWIntrinsicSignatureReader sigReader;
1173
1265
sigReader.Read (info.compCompHnd , sig);
1174
1266
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 ;
1223
1273
1224
- const int immVal2 = ( int ) immOp2-> AsIntCon ()-> IconValue ( );
1274
+ getHWIntrinsicImmOps (intrinsic, sig, &immOp1, & immOp2);
1225
1275
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))
1227
1288
{
1228
- assert (!mustExpand);
1229
- return nullptr ;
1289
+ return useFallback ? impNonConstFallback (intrinsic, retType, simdBaseJitType) : nullptr ;
1230
1290
}
1231
1291
}
1232
- else
1292
+ #else
1293
+ assert (immOp2 == nullptr );
1233
1294
#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 ;
1246
1295
1247
- if (immOp != nullptr )
1296
+ // Validate the first immediate
1297
+ if (immOp1 != nullptr )
1248
1298
{
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
1250
1306
immUpperBound = HWIntrinsicInfo::lookupImmUpperBound (intrinsic);
1251
1307
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
- }
1290
1308
#endif
1291
1309
1292
- if (!hasFullRangeImm && immOp->IsCnsIntOrI ())
1310
+ if (!CheckHWIntrinsicImmRange (intrinsic, simdBaseJitType, immOp1, mustExpand, immLowerBound, immUpperBound,
1311
+ hasFullRangeImm, &useFallback))
1293
1312
{
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 ;
1349
1314
}
1350
1315
}
1351
1316
0 commit comments