From 33704a55be63d87a2048ba6fcd047d4296a39e8e Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 8 Sep 2022 17:28:14 +0200 Subject: [PATCH] JIT: Skip unnecessary binary VN folding checks (#73919) For a lot of extremely common binary VN functions we do a series of unnecessary folding checks, in particular VNF_ValWithExc and VNF_ExcSetCons. Introduce a VNForFuncNoFolding and VNPairForFuncNoFolding that bypasses folding and use it in some cases. --- src/coreclr/jit/valuenum.cpp | 81 ++++++++++++++++++++++++++++-------- src/coreclr/jit/valuenum.h | 19 +++++++++ 2 files changed, 82 insertions(+), 18 deletions(-) diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index b18ebd746e27c..c1bc35435591d 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -992,7 +992,7 @@ int ValueNumStore::EvalComparison(VNFunc vnf, T v0, T v1) // ValueNum ValueNumStore::VNExcSetSingleton(ValueNum x) { - return VNForFunc(TYP_REF, VNF_ExcSetCons, x, VNForEmptyExcSet()); + return VNForFuncNoFolding(TYP_REF, VNF_ExcSetCons, x, VNForEmptyExcSet()); } // Create a ValueNumPair for an exception set singleton for 'xp' // @@ -1071,7 +1071,7 @@ ValueNum ValueNumStore::VNExcSetUnion(ValueNum xs0, ValueNum xs1) assert(VNCheckAscending(funcXs0.m_args[0], funcXs0.m_args[1])); // add the lower one (from xs0) to the result, advance xs0 - res = VNForFunc(TYP_REF, VNF_ExcSetCons, funcXs0.m_args[0], VNExcSetUnion(funcXs0.m_args[1], xs1)); + res = VNForFuncNoFolding(TYP_REF, VNF_ExcSetCons, funcXs0.m_args[0], VNExcSetUnion(funcXs0.m_args[1], xs1)); } else if (funcXs0.m_args[0] == funcXs1.m_args[0]) { @@ -1079,8 +1079,8 @@ ValueNum ValueNumStore::VNExcSetUnion(ValueNum xs0, ValueNum xs1) assert(VNCheckAscending(funcXs1.m_args[0], funcXs1.m_args[1])); // Equal elements; add one (from xs0) to the result, advance both sets - res = VNForFunc(TYP_REF, VNF_ExcSetCons, funcXs0.m_args[0], - VNExcSetUnion(funcXs0.m_args[1], funcXs1.m_args[1])); + res = VNForFuncNoFolding(TYP_REF, VNF_ExcSetCons, funcXs0.m_args[0], + VNExcSetUnion(funcXs0.m_args[1], funcXs1.m_args[1])); } else { @@ -1088,7 +1088,7 @@ ValueNum ValueNumStore::VNExcSetUnion(ValueNum xs0, ValueNum xs1) assert(VNCheckAscending(funcXs1.m_args[0], funcXs1.m_args[1])); // add the lower one (from xs1) to the result, advance xs1 - res = VNForFunc(TYP_REF, VNF_ExcSetCons, funcXs1.m_args[0], VNExcSetUnion(xs0, funcXs1.m_args[1])); + res = VNForFuncNoFolding(TYP_REF, VNF_ExcSetCons, funcXs1.m_args[0], VNExcSetUnion(xs0, funcXs1.m_args[1])); } return res; @@ -1610,7 +1610,7 @@ ValueNum ValueNumStore::VNWithExc(ValueNum vn, ValueNum excSet) ValueNum vnNorm; ValueNum vnX; VNUnpackExc(vn, &vnNorm, &vnX); - return VNForFunc(TypeOfVN(vnNorm), VNF_ValWithExc, vnNorm, VNExcSetUnion(vnX, excSet)); + return VNForFuncNoFolding(TypeOfVN(vnNorm), VNF_ValWithExc, vnNorm, VNExcSetUnion(vnX, excSet)); } } @@ -2176,7 +2176,7 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V { // In terms of values, a castclass always returns its second argument, the object being cast. // The operation may also throw an exception - ValueNum vnExcSet = VNExcSetSingleton(VNForFunc(TYP_REF, VNF_InvalidCastExc, arg1VN, arg0VN)); + ValueNum vnExcSet = VNExcSetSingleton(VNForFuncNoFolding(TYP_REF, VNF_InvalidCastExc, arg1VN, arg0VN)); resultVN = VNWithExc(arg1VN, vnExcSet); } else @@ -2221,6 +2221,51 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V return resultVN; } +//---------------------------------------------------------------------------------------- +// VNForFuncNoFolding - Returns the ValueNum associated with +// 'func'('arg0VN','arg1VN') without doing any folding. +// +// Arguments: +// typ - The type of the resulting ValueNum produced by 'func' +// func - Any binary VNFunc +// arg0VN - The ValueNum of the first argument to 'func' +// arg1VN - The ValueNum of the second argument to 'func' +// +// Return Value: - Returns the ValueNum associated with 'func'('arg0VN','arg1VN') +// +ValueNum ValueNumStore::VNForFuncNoFolding(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN) +{ + assert(arg0VN != NoVN && arg1VN != NoVN); + + // Function arguments carry no exceptions. + assert(arg0VN == VNNormalValue(arg0VN)); + assert(arg1VN == VNNormalValue(arg1VN)); + assert(VNFuncArity(func) == 2); + + ValueNum resultVN; + + // Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN') ? + // + VNDefFuncApp<2> fstruct(func, arg0VN, arg1VN); + if (!GetVNFunc2Map()->Lookup(fstruct, &resultVN)) + { + // Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN') + // + Chunk* const c = GetAllocChunk(typ, CEA_Func2); + unsigned const offsetWithinChunk = c->AllocVN(); + VNDefFuncAppFlexible* fapp = c->PointerToFuncApp(offsetWithinChunk, 2); + fapp->m_func = func; + fapp->m_args[0] = arg0VN; + fapp->m_args[1] = arg1VN; + resultVN = c->m_baseVN + offsetWithinChunk; + + // Record 'resultVN' in the Func2Map + GetVNFunc2Map()->Set(fstruct, resultVN); + } + + return resultVN; +} + //---------------------------------------------------------------------------------------- // VNForFunc - Returns the ValueNum associated with 'func'('arg0VN','arg1VN','arg2VN') // There is a one-to-one relationship between the ValueNum @@ -7571,8 +7616,8 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk) } else { - phiVNP = vnStore->VNPairForFunc(newSsaDef->TypeGet(), VNF_Phi, - ValueNumPair(phiArgSsaNumVN, phiArgSsaNumVN), phiVNP); + phiVNP = vnStore->VNPairForFuncNoFolding(newSsaDef->TypeGet(), VNF_Phi, + ValueNumPair(phiArgSsaNumVN, phiArgSsaNumVN), phiVNP); if ((sameVNP.GetLiberal() != phiArgVNP.GetLiberal()) || (sameVNP.GetConservative() != phiArgVNP.GetConservative())) @@ -7681,7 +7726,7 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk) unsigned phiArgSSANum = phiArgs->GetSsaNum(); ValueNum phiArgSSANumVN = vnStore->VNForIntCon(phiArgSSANum); JITDUMP(" Building phi application: $%x = SSA# %d.\n", phiArgSSANumVN, phiArgSSANum); - phiAppVN = vnStore->VNForFunc(TYP_HEAP, VNF_Phi, phiArgSSANumVN, phiAppVN); + phiAppVN = vnStore->VNForFuncNoFolding(TYP_HEAP, VNF_Phi, phiArgSSANumVN, phiAppVN); JITDUMP(" Building phi application: $%x = phi($%x, $%x).\n", phiAppVN, phiArgSSANumVN, oldPhiAppVN); phiArgs = phiArgs->m_nextArg; @@ -7692,8 +7737,8 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk) } else { - newMemoryVN = vnStore->VNForFunc(TYP_HEAP, VNF_PhiMemoryDef, - vnStore->VNForHandle(ssize_t(blk), GTF_EMPTY), phiAppVN); + newMemoryVN = vnStore->VNForFuncNoFolding(TYP_HEAP, VNF_PhiMemoryDef, + vnStore->VNForHandle(ssize_t(blk), GTF_EMPTY), phiAppVN); } } GetMemoryPerSsaData(blk->bbMemorySsaNumIn[memoryKind])->m_vnPair.SetLiberal(newMemoryVN); @@ -10659,13 +10704,13 @@ void Compiler::fgValueNumberAddExceptionSetForDivision(GenTree* tree) } if (needArithmeticExcLib) { - vnpArithmExc.SetLiberal( - vnStore->VNExcSetSingleton(vnStore->VNForFunc(TYP_REF, VNF_ArithmeticExc, vnOp1NormLib, vnOp2NormLib))); + vnpArithmExc.SetLiberal(vnStore->VNExcSetSingleton( + vnStore->VNForFuncNoFolding(TYP_REF, VNF_ArithmeticExc, vnOp1NormLib, vnOp2NormLib))); } if (needArithmeticExcCon) { - vnpArithmExc.SetConservative( - vnStore->VNExcSetSingleton(vnStore->VNForFunc(TYP_REF, VNF_ArithmeticExc, vnOp1NormLib, vnOp2NormCon))); + vnpArithmExc.SetConservative(vnStore->VNExcSetSingleton( + vnStore->VNForFuncNoFolding(TYP_REF, VNF_ArithmeticExc, vnOp1NormLib, vnOp2NormCon))); } // Combine vnpDivZeroExc with the exception set of tree @@ -10776,8 +10821,8 @@ void Compiler::fgValueNumberAddExceptionSetForBoundsCheck(GenTree* tree) // Construct the exception set for bounds check ValueNumPair boundsChkExcSet = vnStore->VNPExcSetSingleton( - vnStore->VNPairForFunc(TYP_REF, VNF_IndexOutOfRangeExc, vnStore->VNPNormalPair(vnpIndex), - vnStore->VNPNormalPair(vnpArrLen))); + vnStore->VNPairForFuncNoFolding(TYP_REF, VNF_IndexOutOfRangeExc, vnStore->VNPNormalPair(vnpIndex), + vnStore->VNPNormalPair(vnpArrLen))); // Combine the new Overflow exception with the original exception set of tree ValueNumPair newExcSet = vnStore->VNPExcSetUnion(vnpTreeExc, boundsChkExcSet); diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h index 6733d9d64da36..9d86867da51ff 100644 --- a/src/coreclr/jit/valuenum.h +++ b/src/coreclr/jit/valuenum.h @@ -633,6 +633,9 @@ class ValueNumStore ValueNum VNForFunc( var_types typ, VNFunc func, ValueNum op1VNwx, ValueNum op2VNwx, ValueNum op3VNwx, ValueNum op4VNwx); + // Skip all folding checks. + ValueNum VNForFuncNoFolding(var_types typ, VNFunc func, ValueNum op1VNwx, ValueNum op2VNwx); + ValueNum VNForMapSelect(ValueNumKind vnk, var_types type, ValueNum map, ValueNum index); ValueNum VNForMapPhysicalSelect(ValueNumKind vnk, var_types type, ValueNum map, unsigned offset, unsigned size); @@ -702,6 +705,22 @@ class ValueNumStore return ValueNumPair(liberalFuncVN, conservativeFuncVN); } + ValueNumPair VNPairForFuncNoFolding(var_types typ, VNFunc func, ValueNumPair op1VN, ValueNumPair op2VN) + { + ValueNum liberalFuncVN = VNForFuncNoFolding(typ, func, op1VN.GetLiberal(), op2VN.GetLiberal()); + ValueNum conservativeFuncVN; + + if (op1VN.BothEqual() && op2VN.BothEqual()) + { + conservativeFuncVN = liberalFuncVN; + } + else + { + conservativeFuncVN = VNForFuncNoFolding(typ, func, op1VN.GetConservative(), op2VN.GetConservative()); + } + + return ValueNumPair(liberalFuncVN, conservativeFuncVN); + } ValueNumPair VNPairForFunc(var_types typ, VNFunc func, ValueNumPair op1VN, ValueNumPair op2VN, ValueNumPair op3VN) { ValueNum liberalFuncVN = VNForFunc(typ, func, op1VN.GetLiberal(), op2VN.GetLiberal(), op3VN.GetLiberal());