Skip to content

Commit d710379

Browse files
authored
JIT: Avoid duplicate hash table lookups in VN (#104873)
We can switch to recently added `LookupPointerOrAdd` to avoid duplicate hash table lookups for the `VNForFunc` family of functions.
1 parent 2f09bfa commit d710379

File tree

1 file changed

+51
-84
lines changed

1 file changed

+51
-84
lines changed

src/coreclr/jit/valuenum.cpp

Lines changed: 51 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,21 +1793,16 @@ ValueNumStore::Chunk* ValueNumStore::GetAllocChunk(var_types typ, ChunkExtraAttr
17931793
template <typename T, typename NumMap>
17941794
ValueNum ValueNumStore::VnForConst(T cnsVal, NumMap* numMap, var_types varType)
17951795
{
1796-
ValueNum res;
1797-
if (numMap->Lookup(cnsVal, &res))
1798-
{
1799-
return res;
1800-
}
1801-
else
1796+
ValueNum* res = numMap->LookupPointerOrAdd(cnsVal, NoVN);
1797+
if (*res == NoVN)
18021798
{
18031799
Chunk* chunk = GetAllocChunk(varType, CEA_Const);
18041800
unsigned offsetWithinChunk = chunk->AllocVN();
1805-
res = chunk->m_baseVN + offsetWithinChunk;
1801+
*res = chunk->m_baseVN + offsetWithinChunk;
18061802
T* chunkDefs = reinterpret_cast<T*>(chunk->m_defs);
18071803
chunkDefs[offsetWithinChunk] = cnsVal;
1808-
numMap->Set(cnsVal, res);
1809-
return res;
18101804
}
1805+
return *res;
18111806
}
18121807

18131808
ValueNum ValueNumStore::VNForIntCon(INT32 cnsVal)
@@ -2046,12 +2041,12 @@ ValueNum ValueNumStore::VNForHandle(ssize_t cnsVal, GenTreeFlags handleFlags)
20462041
{
20472042
assert((handleFlags & ~GTF_ICON_HDL_MASK) == 0);
20482043

2049-
ValueNum res;
20502044
VNHandle handle;
20512045
VNHandle::Initialize(&handle, cnsVal, handleFlags);
2052-
if (GetHandleMap()->Lookup(handle, &res))
2046+
ValueNum* res = GetHandleMap()->LookupPointerOrAdd(handle, NoVN);
2047+
if (*res != NoVN)
20532048
{
2054-
return res;
2049+
return *res;
20552050
}
20562051

20572052
var_types type = Compiler::gtGetTypeForIconFlags(handleFlags);
@@ -2060,10 +2055,9 @@ ValueNum ValueNumStore::VNForHandle(ssize_t cnsVal, GenTreeFlags handleFlags)
20602055
VNHandle* const chunkSlots = reinterpret_cast<VNHandle*>(c->m_defs);
20612056

20622057
chunkSlots[offsetWithinChunk] = handle;
2063-
res = c->m_baseVN + offsetWithinChunk;
2058+
*res = c->m_baseVN + offsetWithinChunk;
20642059

2065-
GetHandleMap()->Set(handle, res);
2066-
return res;
2060+
return *res;
20672061
}
20682062

20692063
ValueNum ValueNumStore::VNZeroForType(var_types typ)
@@ -2507,22 +2501,20 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func)
25072501
{
25082502
assert(VNFuncArity(func) == 0);
25092503

2510-
ValueNum resultVN;
2511-
25122504
// Have we already assigned a ValueNum for 'func' ?
25132505
//
2514-
if (!GetVNFunc0Map()->Lookup(func, &resultVN))
2506+
ValueNum* resultVN = GetVNFunc0Map()->LookupPointerOrAdd(func, NoVN);
2507+
if (*resultVN == NoVN)
25152508
{
25162509
// Allocate a new ValueNum for 'func'
25172510
Chunk* const c = GetAllocChunk(typ, CEA_Func0);
25182511
unsigned const offsetWithinChunk = c->AllocVN();
25192512
VNFunc* const chunkSlots = reinterpret_cast<VNFunc*>(c->m_defs);
25202513

25212514
chunkSlots[offsetWithinChunk] = func;
2522-
resultVN = c->m_baseVN + offsetWithinChunk;
2523-
GetVNFunc0Map()->Set(func, resultVN);
2515+
*resultVN = c->m_baseVN + offsetWithinChunk;
25242516
}
2525-
return resultVN;
2517+
return *resultVN;
25262518
}
25272519

25282520
//----------------------------------------------------------------------------------------
@@ -2544,16 +2536,11 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN)
25442536
assert(func != VNF_MemOpaque);
25452537
assert(arg0VN == VNNormalValue(arg0VN)); // Arguments don't carry exceptions.
25462538

2547-
ValueNum resultVN = NoVN;
2548-
25492539
// Have we already assigned a ValueNum for 'func'('arg0VN') ?
25502540
//
25512541
VNDefFuncApp<1> fstruct(func, arg0VN);
2552-
if (GetVNFunc1Map()->Lookup(fstruct, &resultVN))
2553-
{
2554-
assert(resultVN != NoVN);
2555-
}
2556-
else
2542+
ValueNum* resultVN = GetVNFunc1Map()->LookupPointerOrAdd(fstruct, NoVN);
2543+
if (*resultVN == NoVN)
25572544
{
25582545
// Check if we can fold GT_ARR_LENGTH on top of a known array (immutable)
25592546
if (func == VNFunc(GT_ARR_LENGTH))
@@ -2566,13 +2553,13 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN)
25662553
int len = m_pComp->info.compCompHnd->getArrayOrStringLength((CORINFO_OBJECT_HANDLE)handle);
25672554
if (len >= 0)
25682555
{
2569-
resultVN = VNForIntCon(len);
2556+
*resultVN = VNForIntCon(len);
25702557
}
25712558
}
25722559

25732560
// Case 2: ARR_LENGTH(static-readonly-field)
25742561
VNFuncApp funcApp;
2575-
if ((resultVN == NoVN) && GetVNFunc(addressVN, &funcApp) && (funcApp.m_func == VNF_InvariantNonNullLoad))
2562+
if ((*resultVN == NoVN) && GetVNFunc(addressVN, &funcApp) && (funcApp.m_func == VNF_InvariantNonNullLoad))
25762563
{
25772564
ValueNum fieldSeqVN = VNNormalValue(funcApp.m_args[0]);
25782565
if (IsVNHandle(fieldSeqVN, GTF_ICON_FIELD_SEQ))
@@ -2595,7 +2582,7 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN)
25952582
int len = m_pComp->info.compCompHnd->getArrayOrStringLength(objHandle);
25962583
if (len >= 0)
25972584
{
2598-
resultVN = VNForIntCon(len);
2585+
*resultVN = VNForIntCon(len);
25992586
}
26002587
}
26012588
}
@@ -2606,36 +2593,33 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN)
26062593
// Case 3: ARR_LENGTH(new T[cns])
26072594
// TODO: Add support for MD arrays
26082595
int knownSize;
2609-
if ((resultVN == NoVN) && TryGetNewArrSize(addressVN, &knownSize))
2596+
if ((*resultVN == NoVN) && TryGetNewArrSize(addressVN, &knownSize))
26102597
{
2611-
resultVN = VNForIntCon(knownSize);
2598+
*resultVN = VNForIntCon(knownSize);
26122599
}
26132600
}
26142601

26152602
// Try to perform constant-folding.
26162603
//
2617-
if ((resultVN == NoVN) && VNEvalCanFoldUnaryFunc(typ, func, arg0VN))
2604+
if ((*resultVN == NoVN) && VNEvalCanFoldUnaryFunc(typ, func, arg0VN))
26182605
{
2619-
resultVN = EvalFuncForConstantArgs(typ, func, arg0VN);
2606+
*resultVN = EvalFuncForConstantArgs(typ, func, arg0VN);
26202607
}
26212608

26222609
// Otherwise, Allocate a new ValueNum for 'func'('arg0VN')
26232610
//
2624-
if (resultVN == NoVN)
2611+
if (*resultVN == NoVN)
26252612
{
26262613
Chunk* const c = GetAllocChunk(typ, CEA_Func1);
26272614
unsigned const offsetWithinChunk = c->AllocVN();
26282615
VNDefFuncAppFlexible* fapp = c->PointerToFuncApp(offsetWithinChunk, 1);
26292616
fapp->m_func = func;
26302617
fapp->m_args[0] = arg0VN;
2631-
resultVN = c->m_baseVN + offsetWithinChunk;
2618+
*resultVN = c->m_baseVN + offsetWithinChunk;
26322619
}
2633-
2634-
// Record 'resultVN' in the Func1Map
2635-
//
2636-
GetVNFunc1Map()->Set(fstruct, resultVN);
26372620
}
2638-
return resultVN;
2621+
2622+
return *resultVN;
26392623
}
26402624

26412625
//----------------------------------------------------------------------------------------
@@ -2744,8 +2728,6 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
27442728
assert((VNFuncArity(func) == 0) || (VNFuncArity(func) == 2));
27452729
assert(func != VNF_MapSelect); // Precondition: use the special function VNForMapSelect defined for that.
27462730

2747-
ValueNum resultVN = NoVN;
2748-
27492731
// Even if the argVNs differ, if both operands runtime types constructed from handles,
27502732
// we can sometimes also fold.
27512733
//
@@ -2755,7 +2737,7 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
27552737
const genTreeOps oper = genTreeOps(func);
27562738
if ((arg0VN != arg1VN) && GenTree::StaticOperIs(oper, GT_EQ, GT_NE))
27572739
{
2758-
resultVN = VNEvalFoldTypeCompare(typ, func, arg0VN, arg1VN);
2740+
ValueNum resultVN = VNEvalFoldTypeCompare(typ, func, arg0VN, arg1VN);
27592741
if (resultVN != NoVN)
27602742
{
27612743
return resultVN;
@@ -2776,15 +2758,13 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
27762758
// Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN') ?
27772759
//
27782760
VNDefFuncApp<2> fstruct(func, arg0VN, arg1VN);
2779-
if (GetVNFunc2Map()->Lookup(fstruct, &resultVN))
2780-
{
2781-
assert(resultVN != NoVN);
2782-
}
2783-
else
2761+
2762+
ValueNum* resultVN = GetVNFunc2Map()->LookupPointerOrAdd(fstruct, NoVN);
2763+
if (*resultVN == NoVN)
27842764
{
27852765
if ((func == VNF_CastClass) || (func == VNF_IsInstanceOf))
27862766
{
2787-
resultVN = VNForCast(func, arg0VN, arg1VN);
2767+
*resultVN = VNForCast(func, arg0VN, arg1VN);
27882768
}
27892769
else
27902770
{
@@ -2795,20 +2775,20 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
27952775
bool folded = false;
27962776
if (VNEvalCanFoldBinaryFunc(typ, func, arg0VN, arg1VN) && VNEvalShouldFold(typ, func, arg0VN, arg1VN))
27972777
{
2798-
resultVN = EvalFuncForConstantArgs(typ, func, arg0VN, arg1VN);
2778+
*resultVN = EvalFuncForConstantArgs(typ, func, arg0VN, arg1VN);
27992779
}
28002780

2801-
if (resultVN != NoVN)
2781+
if (*resultVN != NoVN)
28022782
{
28032783
folded = true;
28042784
}
28052785
else
28062786
{
2807-
resultVN = EvalUsingMathIdentity(typ, func, arg0VN, arg1VN);
2787+
*resultVN = EvalUsingMathIdentity(typ, func, arg0VN, arg1VN);
28082788
}
28092789

28102790
// Do we have a valid resultVN?
2811-
if ((resultVN == NoVN) || (!folded && (genActualType(TypeOfVN(resultVN)) != genActualType(typ))))
2791+
if ((*resultVN == NoVN) || (!folded && (genActualType(TypeOfVN(*resultVN)) != genActualType(typ))))
28122792
{
28132793
// Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN')
28142794
//
@@ -2818,14 +2798,11 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
28182798
fapp->m_func = func;
28192799
fapp->m_args[0] = arg0VN;
28202800
fapp->m_args[1] = arg1VN;
2821-
resultVN = c->m_baseVN + offsetWithinChunk;
2801+
*resultVN = c->m_baseVN + offsetWithinChunk;
28222802
}
28232803
}
2824-
2825-
// Record 'resultVN' in the Func2Map
2826-
GetVNFunc2Map()->Set(fstruct, resultVN);
28272804
}
2828-
return resultVN;
2805+
return *resultVN;
28292806
}
28302807

28312808
//----------------------------------------------------------------------------------------
@@ -2849,12 +2826,11 @@ ValueNum ValueNumStore::VNForFuncNoFolding(var_types typ, VNFunc func, ValueNum
28492826
assert(arg1VN == VNNormalValue(arg1VN));
28502827
assert(VNFuncArity(func) == 2);
28512828

2852-
ValueNum resultVN;
2853-
28542829
// Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN') ?
28552830
//
28562831
VNDefFuncApp<2> fstruct(func, arg0VN, arg1VN);
2857-
if (!GetVNFunc2Map()->Lookup(fstruct, &resultVN))
2832+
ValueNum* resultVN = GetVNFunc2Map()->LookupPointerOrAdd(fstruct, NoVN);
2833+
if (*resultVN == NoVN)
28582834
{
28592835
// Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN')
28602836
//
@@ -2864,13 +2840,10 @@ ValueNum ValueNumStore::VNForFuncNoFolding(var_types typ, VNFunc func, ValueNum
28642840
fapp->m_func = func;
28652841
fapp->m_args[0] = arg0VN;
28662842
fapp->m_args[1] = arg1VN;
2867-
resultVN = c->m_baseVN + offsetWithinChunk;
2868-
2869-
// Record 'resultVN' in the Func2Map
2870-
GetVNFunc2Map()->Set(fstruct, resultVN);
2843+
*resultVN = c->m_baseVN + offsetWithinChunk;
28712844
}
28722845

2873-
return resultVN;
2846+
return *resultVN;
28742847
}
28752848

28762849
//----------------------------------------------------------------------------------------
@@ -2913,12 +2886,12 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
29132886
assert(arg2VN == VNNormalValue(arg2VN));
29142887
#endif
29152888

2916-
ValueNum resultVN;
2917-
29182889
// Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN','arg2VN') ?
29192890
//
29202891
VNDefFuncApp<3> fstruct(func, arg0VN, arg1VN, arg2VN);
2921-
if (!GetVNFunc3Map()->Lookup(fstruct, &resultVN))
2892+
ValueNum* resultVN = GetVNFunc3Map()->LookupPointerOrAdd(fstruct, NoVN);
2893+
2894+
if (*resultVN == NoVN)
29222895
{
29232896
// Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN','arg2VN')
29242897
//
@@ -2929,12 +2902,9 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V
29292902
fapp->m_args[0] = arg0VN;
29302903
fapp->m_args[1] = arg1VN;
29312904
fapp->m_args[2] = arg2VN;
2932-
resultVN = c->m_baseVN + offsetWithinChunk;
2933-
2934-
// Record 'resultVN' in the Func3Map
2935-
GetVNFunc3Map()->Set(fstruct, resultVN);
2905+
*resultVN = c->m_baseVN + offsetWithinChunk;
29362906
}
2937-
return resultVN;
2907+
return *resultVN;
29382908
}
29392909

29402910
// ----------------------------------------------------------------------------------------
@@ -2966,12 +2936,12 @@ ValueNum ValueNumStore::VNForFunc(
29662936
assert((func == VNF_MapStore) || (arg3VN == VNNormalValue(arg3VN)));
29672937
assert(VNFuncArity(func) == 4);
29682938

2969-
ValueNum resultVN;
2970-
29712939
// Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN','arg2VN','arg3VN') ?
29722940
//
29732941
VNDefFuncApp<4> fstruct(func, arg0VN, arg1VN, arg2VN, arg3VN);
2974-
if (!GetVNFunc4Map()->Lookup(fstruct, &resultVN))
2942+
ValueNum* resultVN = GetVNFunc4Map()->LookupPointerOrAdd(fstruct, NoVN);
2943+
2944+
if (*resultVN == NoVN)
29752945
{
29762946
// Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN','arg2VN','arg3VN')
29772947
//
@@ -2983,12 +2953,9 @@ ValueNum ValueNumStore::VNForFunc(
29832953
fapp->m_args[1] = arg1VN;
29842954
fapp->m_args[2] = arg2VN;
29852955
fapp->m_args[3] = arg3VN;
2986-
resultVN = c->m_baseVN + offsetWithinChunk;
2987-
2988-
// Record 'resultVN' in the Func4Map
2989-
GetVNFunc4Map()->Set(fstruct, resultVN);
2956+
*resultVN = c->m_baseVN + offsetWithinChunk;
29902957
}
2991-
return resultVN;
2958+
return *resultVN;
29922959
}
29932960

29942961
//------------------------------------------------------------------------------

0 commit comments

Comments
 (0)