Skip to content

Commit 34bf05f

Browse files
authored
Late cast expansion: more improvements #2 (dotnet#97480)
1 parent 9bbfa9a commit 34bf05f

File tree

2 files changed

+59
-50
lines changed

2 files changed

+59
-50
lines changed

src/coreclr/jit/helperexpansion.cpp

+57-43
Original file line numberDiff line numberDiff line change
@@ -1828,31 +1828,12 @@ bool Compiler::fgVNBasedIntrinsicExpansionForCall_ReadUtf8(BasicBlock** pBlock,
18281828
//
18291829
PhaseStatus Compiler::fgLateCastExpansion()
18301830
{
1831-
if (!doesMethodHaveExpandableCasts())
1831+
if (!doesMethodHaveExpandableCasts() || opts.OptimizationDisabled())
18321832
{
18331833
// Nothing to expand in the current method
18341834
return PhaseStatus::MODIFIED_NOTHING;
18351835
}
18361836

1837-
if (!opts.IsOptimizedWithProfile())
1838-
{
1839-
// Currently, we're only interested in expanding cast helpers using profile data
1840-
return PhaseStatus::MODIFIED_NOTHING;
1841-
}
1842-
1843-
if (JitConfig.JitConsumeProfileForCasts() == 0)
1844-
{
1845-
return PhaseStatus::MODIFIED_NOTHING;
1846-
}
1847-
1848-
const bool preferSize = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_SIZE_OPT);
1849-
if (preferSize)
1850-
{
1851-
// The optimization comes with a codegen size increase
1852-
JITDUMP("Optimized for size - bail out.\n");
1853-
return PhaseStatus::MODIFIED_NOTHING;
1854-
}
1855-
18561837
// TODO-InlineCast: should we still inline some trivial cases even in cold blocks?
18571838
const bool skipForRarelyRunBlocks = true;
18581839
return fgExpandHelper<&Compiler::fgLateCastExpansionForCall>(skipForRarelyRunBlocks);
@@ -1862,6 +1843,7 @@ enum class TypeCheckFailedAction
18621843
{
18631844
ReturnNull,
18641845
CallHelper,
1846+
CallHelper_Specialized,
18651847
CallHelper_AlwaysThrows
18661848
};
18671849

@@ -1978,14 +1960,25 @@ static CORINFO_CLASS_HANDLE PickCandidateForTypeCheck(Compiler* com
19781960
const bool isCastToExact = comp->info.compCompHnd->isExactType(castToCls);
19791961
if (isCastToExact && ((helper == CORINFO_HELP_CHKCASTCLASS) || (helper == CORINFO_HELP_CHKCASTARRAY)))
19801962
{
1981-
// (string)obj
1982-
// (string[])obj
1963+
result = castToCls;
1964+
1965+
// obj is string
1966+
// obj is string[]
19831967
//
1984-
// Fallbacks for these expansions always throw InvalidCastException
1985-
*typeCheckFailed = TypeCheckFailedAction::CallHelper_AlwaysThrows;
1968+
if ((helper == CORINFO_HELP_CHKCASTCLASS))
1969+
{
1970+
// (string)obj
1971+
//
1972+
// Fallback for this expansion always throws InvalidCastException
1973+
// TODO: can we do the same for string[]? (importer did not)
1974+
*typeCheckFailed = TypeCheckFailedAction::CallHelper_AlwaysThrows;
1975+
1976+
// Assume that exceptions are rare
1977+
*likelihood = 100;
19861978

1987-
// Assume that exceptions are rare
1988-
*likelihood = 100;
1979+
// Update the common denominator class to be more exact
1980+
*commonCls = result;
1981+
}
19891982

19901983
// We're done, there is no need in consulting with PGO data
19911984
}
@@ -1996,39 +1989,55 @@ static CORINFO_CLASS_HANDLE PickCandidateForTypeCheck(Compiler* com
19961989
// obj is string[]
19971990
//
19981991
// Fallbacks for these expansions simply return null
1992+
// TODO: should we keep the helper call for ISINSTANCEOFARRAY like we do for CHKCASTARRAY above?
1993+
// The logic is copied from the importer.
19991994
*typeCheckFailed = TypeCheckFailedAction::ReturnNull;
1995+
1996+
// We're done, there is no need in consulting with PGO data
1997+
result = castToCls;
20001998
}
20011999
else
20022000
{
2001+
CORINFO_CLASS_HANDLE exactClass = NO_CLASS_HANDLE;
20032002
// 2) If VM can tell us the exact class for this "cast to" class - use it.
20042003
// Just make sure the class is truly exact.
2005-
if ((comp->info.compCompHnd->getExactClasses(castToCls, 1, &result) == 1) &&
2006-
comp->info.compCompHnd->isExactType(result))
2004+
if ((comp->info.compCompHnd->getExactClasses(castToCls, 1, &exactClass) == 1) &&
2005+
comp->info.compCompHnd->isExactType(exactClass))
20072006
{
2008-
if (isCastClass)
2007+
result = exactClass;
2008+
2009+
if ((helper == CORINFO_HELP_CHKCASTINTERFACE) || (helper == CORINFO_HELP_CHKCASTCLASS))
20092010
{
20102011
// Fallback call is only needed for castclass and only to throw InvalidCastException
20112012
*typeCheckFailed = TypeCheckFailedAction::CallHelper_AlwaysThrows;
20122013

20132014
// Assume that exceptions are rare
20142015
*likelihood = 100;
2016+
2017+
// Update the common denominator class to be more exact
2018+
*commonCls = result;
20152019
}
2016-
else
2020+
else if ((helper == CORINFO_HELP_ISINSTANCEOFINTERFACE) || (helper == CORINFO_HELP_ISINSTANCEOFCLASS))
20172021
{
20182022
// Fallback for isinst simply returns null here
20192023
*typeCheckFailed = TypeCheckFailedAction::ReturnNull;
2020-
}
20212024

2022-
// Update the common denominator class to be more exact
2023-
*commonCls = result;
2025+
// Update the common denominator class to be more exact
2026+
*commonCls = result;
2027+
}
20242028
}
20252029
else
20262030
{
20272031
// 3) Consult with PGO data
20282032
LikelyClassMethodRecord likelyClasses[MAX_GDV_TYPE_CHECKS];
2029-
unsigned likelyClassCount =
2030-
getLikelyClasses(likelyClasses, MAX_GDV_TYPE_CHECKS, comp->fgPgoSchema, comp->fgPgoSchemaCount,
2031-
comp->fgPgoData, (int)castHelper->gtCastHelperILOffset);
2033+
unsigned likelyClassCount = 0;
2034+
2035+
if (comp->opts.IsOptimizedWithProfile() && (JitConfig.JitConsumeProfileForCasts() != 0))
2036+
{
2037+
const int ilOffset = (int)castHelper->gtCastHelperILOffset;
2038+
likelyClassCount = getLikelyClasses(likelyClasses, MAX_GDV_TYPE_CHECKS, comp->fgPgoSchema,
2039+
comp->fgPgoSchemaCount, comp->fgPgoData, ilOffset);
2040+
}
20322041

20332042
if (likelyClassCount != 0)
20342043
{
@@ -2183,10 +2192,12 @@ static CORINFO_CLASS_HANDLE PickCandidateForTypeCheck(Compiler* com
21832192
unreached();
21842193
}
21852194

2186-
if (isCastClass && (result == castToCls) && (*typeCheckFailed == TypeCheckFailedAction::CallHelper))
2195+
if ((helper == CORINFO_HELP_CHKCASTCLASS) && (result == castToCls) &&
2196+
(*typeCheckFailed == TypeCheckFailedAction::CallHelper))
21872197
{
2188-
// TODO-InlineCast: Change helper to faster CORINFO_HELP_CHKCASTCLASS_SPECIAL
2189-
// it won't check for null and castToCls assuming we've already done it inline.
2198+
// A small optimization - use a slightly faster fallback which assumes that we've already checked
2199+
// for null and for castToCls itself so it won't do it again.
2200+
*typeCheckFailed = TypeCheckFailedAction::CallHelper_Specialized;
21902201
}
21912202

21922203
assert(result != NO_CLASS_HANDLE);
@@ -2299,6 +2310,12 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt,
22992310
}
23002311
else
23012312
{
2313+
if (typeCheckFailedAction == TypeCheckFailedAction::CallHelper_Specialized)
2314+
{
2315+
// A slightly faster fallback which assumes that we've already checked
2316+
// for null and for castToCls itself.
2317+
call->gtCallMethHnd = eeFindHelper(CORINFO_HELP_CHKCASTCLASS_SPECIAL);
2318+
}
23022319
GenTree* fallbackTree = gtNewTempStore(tmpNum, call);
23032320
fallbackBb = fgNewBBFromTreeAfter(BBJ_ALWAYS, typeCheckBb, fallbackTree, debugInfo, lastBb, true);
23042321
}
@@ -2346,10 +2363,7 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt,
23462363
//
23472364
nullcheckBb->inheritWeight(firstBb);
23482365
typeCheckBb->inheritWeightPercentage(nullcheckBb, 50);
2349-
fallbackBb->inheritWeightPercentage(typeCheckBb,
2350-
(typeCheckFailedAction == TypeCheckFailedAction::CallHelper_AlwaysThrows)
2351-
? 0
2352-
: 100 - likelihood);
2366+
fallbackBb->inheritWeightPercentage(typeCheckBb, fallbackBb->KindIs(BBJ_THROW) ? 0 : 100 - likelihood);
23532367
typeCheckSucceedBb->inheritWeightPercentage(typeCheckBb, likelihood);
23542368
lastBb->inheritWeight(firstBb);
23552369

src/coreclr/jit/importer.cpp

+2-7
Original file line numberDiff line numberDiff line change
@@ -5462,7 +5462,6 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
54625462

54635463
// Pessimistically assume the jit cannot expand this as an inline test
54645464
bool canExpandInline = false;
5465-
bool partialExpand = false;
54665465
bool reversedMTCheck = false;
54675466
const CorInfoHelpFunc helper = info.compCompHnd->getCastingHelper(pResolvedToken, isCastClass);
54685467

@@ -5559,7 +5558,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
55595558
//
55605559

55615560
GenTree* op2Var = op2;
5562-
if (isCastClass && !partialExpand && (exactCls == NO_CLASS_HANDLE))
5561+
if (isCastClass && (exactCls == NO_CLASS_HANDLE))
55635562
{
55645563
// if exactCls is not null we won't have to clone op2 (it will be used only for the fallback)
55655564
op2Var = fgInsertCommaFormTemp(&op2);
@@ -5597,11 +5596,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
55975596
// use the special helper that skips the cases checked by our inlined cast
55985597
specialHelper = CORINFO_HELP_CHKCASTCLASS_SPECIAL;
55995598
}
5600-
condTrue = gtNewHelperCallNode(specialHelper, TYP_REF, partialExpand ? op2 : op2Var, gtClone(op1));
5601-
}
5602-
else if (partialExpand)
5603-
{
5604-
condTrue = gtNewHelperCallNode(helper, TYP_REF, op2, gtClone(op1));
5599+
condTrue = gtNewHelperCallNode(specialHelper, TYP_REF, op2Var, gtClone(op1));
56055600
}
56065601
else
56075602
{

0 commit comments

Comments
 (0)