@@ -1797,31 +1797,12 @@ bool Compiler::fgVNBasedIntrinsicExpansionForCall_ReadUtf8(BasicBlock** pBlock,
17971797//
17981798PhaseStatus Compiler::fgLateCastExpansion ()
17991799{
1800- if (!doesMethodHaveExpandableCasts ())
1800+ if (!doesMethodHaveExpandableCasts () || opts. OptimizationDisabled () )
18011801 {
18021802 // Nothing to expand in the current method
18031803 return PhaseStatus::MODIFIED_NOTHING;
18041804 }
18051805
1806- if (!opts.IsOptimizedWithProfile ())
1807- {
1808- // Currently, we're only interested in expanding cast helpers using profile data
1809- return PhaseStatus::MODIFIED_NOTHING;
1810- }
1811-
1812- if (JitConfig.JitConsumeProfileForCasts () == 0 )
1813- {
1814- return PhaseStatus::MODIFIED_NOTHING;
1815- }
1816-
1817- const bool preferSize = opts.jitFlags ->IsSet (JitFlags::JIT_FLAG_SIZE_OPT);
1818- if (preferSize)
1819- {
1820- // The optimization comes with a codegen size increase
1821- JITDUMP (" Optimized for size - bail out.\n " );
1822- return PhaseStatus::MODIFIED_NOTHING;
1823- }
1824-
18251806 // TODO-InlineCast: should we still inline some trivial cases even in cold blocks?
18261807 const bool skipForRarelyRunBlocks = true ;
18271808 return fgExpandHelper<&Compiler::fgLateCastExpansionForCall>(skipForRarelyRunBlocks);
@@ -1831,6 +1812,7 @@ enum class TypeCheckFailedAction
18311812{
18321813 ReturnNull,
18331814 CallHelper,
1815+ CallHelper_Specialized,
18341816 CallHelper_AlwaysThrows
18351817};
18361818
@@ -1947,14 +1929,25 @@ static CORINFO_CLASS_HANDLE PickCandidateForTypeCheck(Compiler* com
19471929 const bool isCastToExact = comp->info .compCompHnd ->isExactType (castToCls);
19481930 if (isCastToExact && ((helper == CORINFO_HELP_CHKCASTCLASS) || (helper == CORINFO_HELP_CHKCASTARRAY)))
19491931 {
1950- // (string)obj
1951- // (string[])obj
1932+ result = castToCls;
1933+
1934+ // obj is string
1935+ // obj is string[]
19521936 //
1953- // Fallbacks for these expansions always throw InvalidCastException
1954- *typeCheckFailed = TypeCheckFailedAction::CallHelper_AlwaysThrows;
1937+ if ((helper == CORINFO_HELP_CHKCASTCLASS))
1938+ {
1939+ // (string)obj
1940+ //
1941+ // Fallback for this expansion always throws InvalidCastException
1942+ // TODO: can we do the same for string[]? (importer did not)
1943+ *typeCheckFailed = TypeCheckFailedAction::CallHelper_AlwaysThrows;
1944+
1945+ // Assume that exceptions are rare
1946+ *likelihood = 100 ;
19551947
1956- // Assume that exceptions are rare
1957- *likelihood = 100 ;
1948+ // Update the common denominator class to be more exact
1949+ *commonCls = result;
1950+ }
19581951
19591952 // We're done, there is no need in consulting with PGO data
19601953 }
@@ -1965,39 +1958,55 @@ static CORINFO_CLASS_HANDLE PickCandidateForTypeCheck(Compiler* com
19651958 // obj is string[]
19661959 //
19671960 // Fallbacks for these expansions simply return null
1961+ // TODO: should we keep the helper call for ISINSTANCEOFARRAY like we do for CHKCASTARRAY above?
1962+ // The logic is copied from the importer.
19681963 *typeCheckFailed = TypeCheckFailedAction::ReturnNull;
1964+
1965+ // We're done, there is no need in consulting with PGO data
1966+ result = castToCls;
19691967 }
19701968 else
19711969 {
1970+ CORINFO_CLASS_HANDLE exactClass = NO_CLASS_HANDLE;
19721971 // 2) If VM can tell us the exact class for this "cast to" class - use it.
19731972 // Just make sure the class is truly exact.
1974- if ((comp->info .compCompHnd ->getExactClasses (castToCls, 1 , &result ) == 1 ) &&
1975- comp->info .compCompHnd ->isExactType (result ))
1973+ if ((comp->info .compCompHnd ->getExactClasses (castToCls, 1 , &exactClass ) == 1 ) &&
1974+ comp->info .compCompHnd ->isExactType (exactClass ))
19761975 {
1977- if (isCastClass)
1976+ result = exactClass;
1977+
1978+ if ((helper == CORINFO_HELP_CHKCASTINTERFACE) || (helper == CORINFO_HELP_CHKCASTCLASS))
19781979 {
19791980 // Fallback call is only needed for castclass and only to throw InvalidCastException
19801981 *typeCheckFailed = TypeCheckFailedAction::CallHelper_AlwaysThrows;
19811982
19821983 // Assume that exceptions are rare
19831984 *likelihood = 100 ;
1985+
1986+ // Update the common denominator class to be more exact
1987+ *commonCls = result;
19841988 }
1985- else
1989+ else if ((helper == CORINFO_HELP_ISINSTANCEOFINTERFACE) || (helper == CORINFO_HELP_ISINSTANCEOFCLASS))
19861990 {
19871991 // Fallback for isinst simply returns null here
19881992 *typeCheckFailed = TypeCheckFailedAction::ReturnNull;
1989- }
19901993
1991- // Update the common denominator class to be more exact
1992- *commonCls = result;
1994+ // Update the common denominator class to be more exact
1995+ *commonCls = result;
1996+ }
19931997 }
19941998 else
19951999 {
19962000 // 3) Consult with PGO data
19972001 LikelyClassMethodRecord likelyClasses[MAX_GDV_TYPE_CHECKS];
1998- unsigned likelyClassCount =
1999- getLikelyClasses (likelyClasses, MAX_GDV_TYPE_CHECKS, comp->fgPgoSchema , comp->fgPgoSchemaCount ,
2000- comp->fgPgoData , (int )castHelper->gtCastHelperILOffset );
2002+ unsigned likelyClassCount = 0 ;
2003+
2004+ if (comp->opts .IsOptimizedWithProfile () && (JitConfig.JitConsumeProfileForCasts () != 0 ))
2005+ {
2006+ const int ilOffset = (int )castHelper->gtCastHelperILOffset ;
2007+ likelyClassCount = getLikelyClasses (likelyClasses, MAX_GDV_TYPE_CHECKS, comp->fgPgoSchema ,
2008+ comp->fgPgoSchemaCount , comp->fgPgoData , ilOffset);
2009+ }
20012010
20022011 if (likelyClassCount != 0 )
20032012 {
@@ -2152,10 +2161,12 @@ static CORINFO_CLASS_HANDLE PickCandidateForTypeCheck(Compiler* com
21522161 unreached ();
21532162 }
21542163
2155- if (isCastClass && (result == castToCls) && (*typeCheckFailed == TypeCheckFailedAction::CallHelper))
2164+ if ((helper == CORINFO_HELP_CHKCASTCLASS) && (result == castToCls) &&
2165+ (*typeCheckFailed == TypeCheckFailedAction::CallHelper))
21562166 {
2157- // TODO-InlineCast: Change helper to faster CORINFO_HELP_CHKCASTCLASS_SPECIAL
2158- // it won't check for null and castToCls assuming we've already done it inline.
2167+ // A small optimization - use a slightly faster fallback which assumes that we've already checked
2168+ // for null and for castToCls itself so it won't do it again.
2169+ *typeCheckFailed = TypeCheckFailedAction::CallHelper_Specialized;
21592170 }
21602171
21612172 assert (result != NO_CLASS_HANDLE);
@@ -2268,7 +2279,18 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt,
22682279 }
22692280 else
22702281 {
2271- GenTree* fallbackTree = gtNewTempStore (tmpNum, call);
2282+ GenTreeCall* helperCall = call;
2283+ if (typeCheckFailedAction == TypeCheckFailedAction::CallHelper_Specialized)
2284+ {
2285+ // Re-create the call with a specialized helper
2286+ // For JIT we probably can just change the gtCallMethHnd in the existing call, but let's unify with AOT
2287+ helperCall = gtNewHelperCallNode (CORINFO_HELP_CHKCASTCLASS_SPECIAL, call->TypeGet (),
2288+ helperCall->gtArgs .GetUserArgByIndex (0 )->GetNode (),
2289+ helperCall->gtArgs .GetUserArgByIndex (1 )->GetNode ());
2290+ fgMorphCall (helperCall);
2291+ gtSetEvalOrder (helperCall);
2292+ }
2293+ GenTree* fallbackTree = gtNewTempStore (tmpNum, helperCall);
22722294 fallbackBb = fgNewBBFromTreeAfter (BBJ_ALWAYS, typeCheckBb, fallbackTree, debugInfo, lastBb, true );
22732295 }
22742296
0 commit comments