Skip to content

Commit 014632b

Browse files
authored
JIT: Encode SIMD base type in VN for all HW intrinsics (#105869)
This changes the VN funcs for all HW intrinsic functions to always have an extra parameter for the SIMD base type. Before this change it was based on the instruction list, however that is not always the right thing (e.g. #105721 is a bug because of that). We also kept it as part of the HW intrinsic table, but that requires manual maintenance and is easy to get wrong. Always encoding the type is much more simple and the diffs do not look too bad. In .NET 10 we can decide if we want to opt some intrinsics into not being differentiated based on the SIMD base type. The easiest thing might be to always map those to have the same base SIMD type (e.g. `CORINFO_TYPE_BYTE`) so that we don't end up with differences in arities for some VN functions that are hard to reason about. Fix #105721
1 parent c116616 commit 014632b

11 files changed

Lines changed: 2553 additions & 2663 deletions

File tree

src/coreclr/jit/compiler.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9531,10 +9531,6 @@ class Compiler
95319531
#endif // FEATURE_SIMD
95329532
}
95339533

9534-
#ifdef FEATURE_HW_INTRINSICS
9535-
static bool vnEncodesResultTypeForHWIntrinsic(NamedIntrinsic hwIntrinsicID);
9536-
#endif // FEATURE_HW_INTRINSICS
9537-
95389534
private:
95399535
// Returns true if the TYP_SIMD locals on stack are aligned at their
95409536
// preferred byte boundary specified by getSIMDTypeAlignment().

src/coreclr/jit/hwintrinsic.cpp

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
static const HWIntrinsicInfo hwIntrinsicInfoArray[] = {
1010
// clang-format off
1111
#if defined(TARGET_XARCH)
12-
#define HARDWARE_INTRINSIC(isa, name, size, numarg, extra, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, category, flag) \
12+
#define HARDWARE_INTRINSIC(isa, name, size, numarg, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, category, flag) \
1313
{ \
1414
/* name */ #name, \
1515
/* flags */ static_cast<HWIntrinsicFlag>(flag), \
@@ -22,7 +22,7 @@ static const HWIntrinsicInfo hwIntrinsicInfoArray[] = {
2222
},
2323
#include "hwintrinsiclistxarch.h"
2424
#elif defined (TARGET_ARM64)
25-
#define HARDWARE_INTRINSIC(isa, name, size, numarg, extra, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, category, flag) \
25+
#define HARDWARE_INTRINSIC(isa, name, size, numarg, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, category, flag) \
2626
{ \
2727
/* name */ #name, \
2828
/* flags */ static_cast<HWIntrinsicFlag>(flag), \
@@ -759,75 +759,6 @@ CorInfoType Compiler::getBaseJitTypeFromArgIfNeeded(NamedIntrinsic intrins
759759
return simdBaseJitType;
760760
}
761761

762-
//------------------------------------------------------------------------
763-
// vnEncodesResultTypeForHWIntrinsic(NamedIntrinsic hwIntrinsicID):
764-
//
765-
// Arguments:
766-
// hwIntrinsicID -- The id for the HW intrinsic
767-
//
768-
// Return Value:
769-
// Returns true if this intrinsic requires value numbering to add an
770-
// extra SimdType argument that encodes the resulting type.
771-
// If we don't do this overloaded versions can return the same VN
772-
// leading to incorrect CSE substitutions.
773-
//
774-
/* static */ bool Compiler::vnEncodesResultTypeForHWIntrinsic(NamedIntrinsic hwIntrinsicID)
775-
{
776-
// No extra type information is needed for scalar/special HW Intrinsic.
777-
//
778-
unsigned simdSize = 0;
779-
if (HWIntrinsicInfo::tryLookupSimdSize(hwIntrinsicID, &simdSize) && (simdSize == 0))
780-
{
781-
return false;
782-
}
783-
784-
int numArgs = HWIntrinsicInfo::lookupNumArgs(hwIntrinsicID);
785-
786-
// HW Intrinsic's with -1 for numArgs have a varying number of args, so we currently
787-
// give them a unique value number, and don't add an extra argument.
788-
//
789-
if (numArgs == -1)
790-
{
791-
return false;
792-
}
793-
794-
// We iterate over all of the different baseType's for this intrinsic in the HWIntrinsicInfo table
795-
// We set diffInsCount to the number of instructions that can execute differently.
796-
//
797-
unsigned diffInsCount = 0;
798-
#ifdef TARGET_XARCH
799-
instruction lastIns = INS_invalid;
800-
#endif
801-
for (var_types baseType = TYP_BYTE; (baseType <= TYP_DOUBLE); baseType = (var_types)(baseType + 1))
802-
{
803-
instruction curIns = HWIntrinsicInfo::lookupIns(hwIntrinsicID, baseType);
804-
if (curIns != INS_invalid)
805-
{
806-
#ifdef TARGET_XARCH
807-
if (curIns != lastIns)
808-
{
809-
diffInsCount++;
810-
// remember the last valid instruction that we saw
811-
lastIns = curIns;
812-
}
813-
#elif defined(TARGET_ARM64)
814-
// On ARM64 we use the same instruction and specify an insOpt arrangement
815-
// so we always consider the instruction operation to be different
816-
//
817-
diffInsCount++;
818-
#endif // TARGET
819-
if (diffInsCount >= 2)
820-
{
821-
// We can early exit the loop now
822-
break;
823-
}
824-
}
825-
}
826-
827-
// If we see two (or more) different instructions we need the extra VNF_SimdType arg
828-
return (diffInsCount >= 2);
829-
}
830-
831762
struct HWIntrinsicIsaRange
832763
{
833764
NamedIntrinsic FirstId;

src/coreclr/jit/hwintrinsiclistarm64.h

Lines changed: 723 additions & 723 deletions
Large diffs are not rendered by default.

src/coreclr/jit/hwintrinsiclistarm64sve.h

Lines changed: 285 additions & 285 deletions
Large diffs are not rendered by default.

src/coreclr/jit/hwintrinsiclistxarch.h

Lines changed: 1310 additions & 1312 deletions
Large diffs are not rendered by default.

src/coreclr/jit/namedintrinsiclist.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,11 @@ enum NamedIntrinsic : unsigned short
152152
#ifdef FEATURE_HW_INTRINSICS
153153
NI_HW_INTRINSIC_START,
154154
#if defined(TARGET_XARCH)
155-
#define HARDWARE_INTRINSIC(isa, name, size, numarg, extra, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, category, flag) \
155+
#define HARDWARE_INTRINSIC(isa, name, size, numarg, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, category, flag) \
156156
NI_##isa##_##name,
157157
#include "hwintrinsiclistxarch.h"
158158
#elif defined(TARGET_ARM64)
159-
#define HARDWARE_INTRINSIC(isa, name, size, numarg, extra, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, category, flag) \
159+
#define HARDWARE_INTRINSIC(isa, name, size, numarg, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, category, flag) \
160160
NI_##isa##_##name,
161161
#include "hwintrinsiclistarm64.h"
162162
#endif // !defined(TARGET_XARCH) && !defined(TARGET_ARM64)

src/coreclr/jit/valuenum.cpp

Lines changed: 36 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -2947,7 +2947,8 @@ ValueNum ValueNumStore::VNForFunc(
29472947
assert(arg1VN == VNNormalValue(arg1VN));
29482948
assert(arg2VN == VNNormalValue(arg2VN));
29492949
assert((func == VNF_MapStore) || (arg3VN == VNNormalValue(arg3VN)));
2950-
assert(VNFuncArity(func) == 4);
2950+
// Some SIMD functions with variable number of arguments are defined with zero arity
2951+
assert((VNFuncArity(func) == 0) || (VNFuncArity(func) == 4));
29512952

29522953
// Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN','arg2VN','arg3VN') ?
29532954
//
@@ -7823,8 +7824,10 @@ ValueNum EvaluateSimdCvtVectorToMask(ValueNumStore* vns, var_types simdType, var
78237824
return vns->VNForSimdMaskCon(result);
78247825
}
78257826

7826-
ValueNum ValueNumStore::EvalHWIntrinsicFunUnary(
7827-
GenTreeHWIntrinsic* tree, VNFunc func, ValueNum arg0VN, bool encodeResultType, ValueNum resultTypeVN)
7827+
ValueNum ValueNumStore::EvalHWIntrinsicFunUnary(GenTreeHWIntrinsic* tree,
7828+
VNFunc func,
7829+
ValueNum arg0VN,
7830+
ValueNum resultTypeVN)
78287831
{
78297832
var_types type = tree->TypeGet();
78307833
var_types baseType = tree->GetSimdBaseType();
@@ -8158,19 +8161,11 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunUnary(
81588161
}
81598162
}
81608163

8161-
if (encodeResultType)
8162-
{
8163-
return VNForFunc(type, func, arg0VN, resultTypeVN);
8164-
}
8165-
return VNForFunc(type, func, arg0VN);
8164+
return VNForFunc(type, func, arg0VN, resultTypeVN);
81668165
}
81678166

8168-
ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(GenTreeHWIntrinsic* tree,
8169-
VNFunc func,
8170-
ValueNum arg0VN,
8171-
ValueNum arg1VN,
8172-
bool encodeResultType,
8173-
ValueNum resultTypeVN)
8167+
ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(
8168+
GenTreeHWIntrinsic* tree, VNFunc func, ValueNum arg0VN, ValueNum arg1VN, ValueNum resultTypeVN)
81748169
{
81758170
var_types type = tree->TypeGet();
81768171
var_types baseType = tree->GetSimdBaseType();
@@ -8953,11 +8948,7 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(GenTreeHWIntrinsic* tree,
89538948
}
89548949
}
89558950

8956-
if (encodeResultType)
8957-
{
8958-
return VNForFunc(type, func, arg0VN, arg1VN, resultTypeVN);
8959-
}
8960-
return VNForFunc(type, func, arg0VN, arg1VN);
8951+
return VNForFunc(type, func, arg0VN, arg1VN, resultTypeVN);
89618952
}
89628953

89638954
ValueNum EvaluateSimdWithElementFloating(
@@ -9068,13 +9059,8 @@ ValueNum EvaluateSimdWithElementIntegral(
90689059
}
90699060
}
90709061

9071-
ValueNum ValueNumStore::EvalHWIntrinsicFunTernary(GenTreeHWIntrinsic* tree,
9072-
VNFunc func,
9073-
ValueNum arg0VN,
9074-
ValueNum arg1VN,
9075-
ValueNum arg2VN,
9076-
bool encodeResultType,
9077-
ValueNum resultTypeVN)
9062+
ValueNum ValueNumStore::EvalHWIntrinsicFunTernary(
9063+
GenTreeHWIntrinsic* tree, VNFunc func, ValueNum arg0VN, ValueNum arg1VN, ValueNum arg2VN, ValueNum resultTypeVN)
90789064
{
90799065
var_types type = tree->TypeGet();
90809066
var_types baseType = tree->GetSimdBaseType();
@@ -9185,14 +9171,7 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunTernary(GenTreeHWIntrinsic* tree,
91859171
}
91869172
}
91879173

9188-
if (encodeResultType)
9189-
{
9190-
return VNForFunc(type, func, arg0VN, arg1VN, arg2VN, resultTypeVN);
9191-
}
9192-
else
9193-
{
9194-
return VNForFunc(type, func, arg0VN, arg1VN, arg2VN);
9195-
}
9174+
return VNForFunc(type, func, arg0VN, arg1VN, arg2VN, resultTypeVN);
91969175
}
91979176

91989177
#endif // FEATURE_HW_INTRINSICS
@@ -10381,25 +10360,6 @@ void ValueNumStore::vnDumpZeroObj(Compiler* comp, VNFuncApp* zeroObj)
1038110360

1038210361
// Static fields, methods.
1038310362

10384-
#define ValueNumFuncDef(vnf, arity, commute, knownNonNull, sharedStatic, extra) \
10385-
static_assert((arity) >= 0 || !(extra), "valuenumfuncs.h has EncodesExtraTypeArg==true and arity<0 for " #vnf);
10386-
#include "valuenumfuncs.h"
10387-
10388-
#ifdef FEATURE_HW_INTRINSICS
10389-
10390-
#define HARDWARE_INTRINSIC(isa, name, size, argCount, extra, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, category, flag) \
10391-
static_assert((size) != 0 || !(extra), \
10392-
"hwintrinsicslist<arch>.h has EncodesExtraTypeArg==true and size==0 for " #isa " " #name);
10393-
#if defined(TARGET_XARCH)
10394-
#include "hwintrinsiclistxarch.h"
10395-
#elif defined(TARGET_ARM64)
10396-
#include "hwintrinsiclistarm64.h"
10397-
#else
10398-
#error Unsupported platform
10399-
#endif
10400-
10401-
#endif // FEATURE_HW_INTRINSICS
10402-
1040310363
/* static */ constexpr uint8_t ValueNumStore::GetOpAttribsForArity(genTreeOps oper, GenTreeOperKind kind)
1040410364
{
1040510365
return ((GenTree::StaticOperIs(oper, GT_SELECT) ? 3 : (((kind & GTK_UNOP) >> 1) | ((kind & GTK_BINOP) >> 1)))
@@ -10434,8 +10394,8 @@ const uint8_t ValueNumStore::s_vnfOpAttribs[VNF_COUNT] = {
1043410394

1043510395
0, // VNF_Boundary
1043610396

10437-
#define ValueNumFuncDef(vnf, arity, commute, knownNonNull, sharedStatic, extra) \
10438-
GetOpAttribsForFunc((arity) + static_cast<int>(extra), commute, knownNonNull, sharedStatic),
10397+
#define ValueNumFuncDef(vnf, arity, commute, knownNonNull, sharedStatic) \
10398+
GetOpAttribsForFunc(arity, commute, knownNonNull, sharedStatic),
1043910399
#include "valuenumfuncs.h"
1044010400
};
1044110401

@@ -10490,7 +10450,7 @@ void ValueNumStore::ValidateValueNumStoreStatics()
1049010450

1049110451
int vnfNum = VNF_Boundary + 1; // The macro definition below will update this after using it.
1049210452

10493-
#define ValueNumFuncDef(vnf, arity, commute, knownNonNull, sharedStatic, extra) \
10453+
#define ValueNumFuncDef(vnf, arity, commute, knownNonNull, sharedStatic) \
1049410454
if (commute) \
1049510455
arr[vnfNum] |= VNFOA_Commutative; \
1049610456
if (knownNonNull) \
@@ -10514,19 +10474,6 @@ void ValueNumStore::ValidateValueNumStoreStatics()
1051410474
for (NamedIntrinsic id = (NamedIntrinsic)(NI_HW_INTRINSIC_START + 1); (id < NI_HW_INTRINSIC_END);
1051510475
id = (NamedIntrinsic)(id + 1))
1051610476
{
10517-
bool encodeResultType = Compiler::vnEncodesResultTypeForHWIntrinsic(id);
10518-
10519-
if (encodeResultType)
10520-
{
10521-
// These HW_Intrinsic's have an extra VNF_SimdType arg.
10522-
//
10523-
VNFunc func = VNFunc(VNF_HWI_FIRST + (id - NI_HW_INTRINSIC_START - 1));
10524-
unsigned oldArity = (arr[func] & VNFOA_ArityMask) >> VNFOA_ArityShift;
10525-
unsigned newArity = oldArity + 1;
10526-
10527-
ValueNumFuncSetArity(func, newArity);
10528-
}
10529-
1053010477
if (HWIntrinsicInfo::IsCommutative(id))
1053110478
{
1053210479
VNFunc func = VNFunc(VNF_HWI_FIRST + (id - NI_HW_INTRINSIC_START - 1));
@@ -10553,7 +10500,7 @@ void ValueNumStore::ValidateValueNumStoreStatics()
1055310500

1055410501
#ifdef DEBUG
1055510502
// Define the name array.
10556-
#define ValueNumFuncDef(vnf, arity, commute, knownNonNull, sharedStatic, extra) #vnf,
10503+
#define ValueNumFuncDef(vnf, arity, commute, knownNonNull, sharedStatic) #vnf,
1055710504

1055810505
const char* ValueNumStore::VNFuncNameArr[] = {
1055910506
#include "valuenumfuncs.h"
@@ -13014,19 +12961,13 @@ void Compiler::fgValueNumberHWIntrinsic(GenTreeHWIntrinsic* tree)
1301412961
}
1301512962
else
1301612963
{
13017-
VNFunc func = GetVNFuncForNode(tree);
13018-
ValueNumPair resultTypeVNPair = ValueNumPair();
13019-
bool encodeResultType = vnEncodesResultTypeForHWIntrinsic(intrinsicId);
12964+
VNFunc func = GetVNFuncForNode(tree);
12965+
ValueNum simdTypeVN = vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetNormalizedSimdBaseJitType());
12966+
ValueNumPair resultTypeVNPair(simdTypeVN, simdTypeVN);
1302012967

13021-
if (encodeResultType)
13022-
{
13023-
ValueNum simdTypeVN = vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetNormalizedSimdBaseJitType());
13024-
resultTypeVNPair.SetBoth(simdTypeVN);
13025-
13026-
JITDUMP(" simdTypeVN is ");
13027-
JITDUMPEXEC(vnPrint(simdTypeVN, 1));
13028-
JITDUMP("\n");
13029-
}
12968+
JITDUMP(" simdTypeVN is ");
12969+
JITDUMPEXEC(vnPrint(simdTypeVN, 1));
12970+
JITDUMP("\n");
1303012971

1303112972
auto getOperandVNs = [this, addr](GenTree* operand, ValueNumPair* pNormVNPair, ValueNumPair* pExcVNPair) {
1303212973
vnStore->VNPUnpackExc(operand->gtVNPair, pNormVNPair, pExcVNPair);
@@ -13049,22 +12990,12 @@ void Compiler::fgValueNumberHWIntrinsic(GenTreeHWIntrinsic* tree)
1304912990
}
1305012991
};
1305112992

13052-
const bool isVariableNumArgs = HWIntrinsicInfo::lookupNumArgs(intrinsicId) == -1;
13053-
1305412993
// There are some HWINTRINSICS operations that have zero args, i.e. NI_Vector128_Zero
1305512994
if (opCount == 0)
1305612995
{
13057-
if (encodeResultType)
13058-
{
13059-
// There are zero arg HWINTRINSICS operations that encode the result type, i.e. Vector128_AllBitSet
13060-
normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, resultTypeVNPair);
13061-
assert(vnStore->VNFuncArity(func) == 1);
13062-
}
13063-
else
13064-
{
13065-
normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func);
13066-
assert(vnStore->VNFuncArity(func) == 0);
13067-
}
12996+
// There are zero arg HWINTRINSICS operations that encode the result type, i.e. Vector128_AllBitSet
12997+
normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, resultTypeVNPair);
12998+
assert(vnStore->VNFuncArity(func) == 1);
1306812999
}
1306913000
else // HWINTRINSIC unary or binary or ternary operator.
1307013001
{
@@ -13074,11 +13005,10 @@ void Compiler::fgValueNumberHWIntrinsic(GenTreeHWIntrinsic* tree)
1307413005

1307513006
if (opCount == 1)
1307613007
{
13077-
ValueNum normalLVN = vnStore->EvalHWIntrinsicFunUnary(tree, func, op1vnp.GetLiberal(), encodeResultType,
13078-
resultTypeVNPair.GetLiberal());
13079-
ValueNum normalCVN =
13080-
vnStore->EvalHWIntrinsicFunUnary(tree, func, op1vnp.GetConservative(), encodeResultType,
13081-
resultTypeVNPair.GetConservative());
13008+
ValueNum normalLVN =
13009+
vnStore->EvalHWIntrinsicFunUnary(tree, func, op1vnp.GetLiberal(), resultTypeVNPair.GetLiberal());
13010+
ValueNum normalCVN = vnStore->EvalHWIntrinsicFunUnary(tree, func, op1vnp.GetConservative(),
13011+
resultTypeVNPair.GetConservative());
1308213012

1308313013
normalPair = ValueNumPair(normalLVN, normalCVN);
1308413014
excSetPair = op1Xvnp;
@@ -13093,10 +13023,10 @@ void Compiler::fgValueNumberHWIntrinsic(GenTreeHWIntrinsic* tree)
1309313023
{
1309413024
ValueNum normalLVN =
1309513025
vnStore->EvalHWIntrinsicFunBinary(tree, func, op1vnp.GetLiberal(), op2vnp.GetLiberal(),
13096-
encodeResultType, resultTypeVNPair.GetLiberal());
13097-
ValueNum normalCVN = vnStore->EvalHWIntrinsicFunBinary(tree, func, op1vnp.GetConservative(),
13098-
op2vnp.GetConservative(), encodeResultType,
13099-
resultTypeVNPair.GetConservative());
13026+
resultTypeVNPair.GetLiberal());
13027+
ValueNum normalCVN =
13028+
vnStore->EvalHWIntrinsicFunBinary(tree, func, op1vnp.GetConservative(),
13029+
op2vnp.GetConservative(), resultTypeVNPair.GetConservative());
1310013030

1310113031
normalPair = ValueNumPair(normalLVN, normalCVN);
1310213032
excSetPair = vnStore->VNPExcSetUnion(op1Xvnp, op2Xvnp);
@@ -13111,12 +13041,11 @@ void Compiler::fgValueNumberHWIntrinsic(GenTreeHWIntrinsic* tree)
1311113041

1311213042
ValueNum normalLVN =
1311313043
vnStore->EvalHWIntrinsicFunTernary(tree, func, op1vnp.GetLiberal(), op2vnp.GetLiberal(),
13114-
op3vnp.GetLiberal(), encodeResultType,
13115-
resultTypeVNPair.GetLiberal());
13044+
op3vnp.GetLiberal(), resultTypeVNPair.GetLiberal());
1311613045
ValueNum normalCVN =
1311713046
vnStore->EvalHWIntrinsicFunTernary(tree, func, op1vnp.GetConservative(),
1311813047
op2vnp.GetConservative(), op3vnp.GetConservative(),
13119-
encodeResultType, resultTypeVNPair.GetConservative());
13048+
resultTypeVNPair.GetConservative());
1312013049

1312113050
normalPair = ValueNumPair(normalLVN, normalCVN);
1312213051

0 commit comments

Comments
 (0)