Skip to content

Commit 5501afd

Browse files
authored
Late cast expansion: remove the null check if possible (#97234)
1 parent 0fafa50 commit 5501afd

File tree

7 files changed

+63
-31
lines changed

7 files changed

+63
-31
lines changed

src/coreclr/jit/assertionprop.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5013,10 +5013,16 @@ GenTree* Compiler::optAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCal
50135013
return optAssertionProp_Update(arg1, call, stmt);
50145014
}
50155015

5016-
// TODO-InlineCast: check optAssertionIsNonNull for the object argument and replace
5017-
// the helper with its nonnull version, e.g.:
5018-
// CORINFO_HELP_ISINSTANCEOFANY -> CORINFO_HELP_ISINSTANCEOFANY_NONNULL
5019-
// so then fgLateCastExpansion can skip the null check.
5016+
// Leave a hint for fgLateCastExpansion that obj is never null.
5017+
INDEBUG(AssertionIndex nonNullIdx = NO_ASSERTION_INDEX);
5018+
INDEBUG(bool vnBased = false);
5019+
// GTF_CALL_M_CAST_CAN_BE_EXPANDED check is to improve TP
5020+
if (((call->gtCallMoreFlags & GTF_CALL_M_CAST_CAN_BE_EXPANDED) != 0) &&
5021+
optAssertionIsNonNull(arg1, assertions DEBUGARG(&vnBased) DEBUGARG(&nonNullIdx)))
5022+
{
5023+
call->gtCallMoreFlags |= GTF_CALL_M_CAST_OBJ_NONNULL;
5024+
return optAssertionProp_Update(call, call, stmt);
5025+
}
50205026
}
50215027
}
50225028

src/coreclr/jit/compiler.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10028,11 +10028,6 @@ JITDBGAPI void __cdecl cTreeFlags(Compiler* comp, GenTree* tree)
1002810028
chars += printf("[CALL_GUARDED]");
1002910029
}
1003010030

10031-
if (call->IsExpRuntimeLookup())
10032-
{
10033-
chars += printf("[CALL_EXP_RUNTIME_LOOKUP]");
10034-
}
10035-
1003610031
if (call->gtCallDebugFlags & GTF_CALL_MD_STRESS_TAILCALL)
1003710032
{
1003810033
chars += printf("[CALL_MD_STRESS_TAILCALL]");

src/coreclr/jit/gcinfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ GCInfo::WriteBarrierForm GCInfo::gcIsWriteBarrierCandidate(GenTreeStoreInd* stor
240240
}
241241

242242
// Ignore any assignments of NULL.
243-
GenTree* const data = store->Data()->gtSkipReloadOrCopy()->gtEffectiveVal();
243+
GenTree* const data = store->Data()->gtSkipReloadOrCopy();
244244
if (data->IsIntegralConst(0))
245245
{
246246
return WBF_NoBarrier;

src/coreclr/jit/gentree.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2492,6 +2492,34 @@ bool GenTreeCall::IsHelperCall(Compiler* compiler, unsigned helper) const
24922492
return IsHelperCall(compiler->eeFindHelper(helper));
24932493
}
24942494

2495+
//-------------------------------------------------------------------------
2496+
// IsRuntimeLookupHelperCall: Determine if this GT_CALL node represents a runtime lookup helper call.
2497+
//
2498+
// Arguments:
2499+
// compiler - the compiler instance so that we can call eeGetHelperNum
2500+
//
2501+
// Return Value:
2502+
// Returns true if this GT_CALL node represents a runtime lookup helper call.
2503+
//
2504+
bool GenTreeCall::IsRuntimeLookupHelperCall(Compiler* compiler) const
2505+
{
2506+
if (!IsHelperCall())
2507+
{
2508+
return false;
2509+
}
2510+
2511+
switch (compiler->eeGetHelperNum(gtCallMethHnd))
2512+
{
2513+
case CORINFO_HELP_RUNTIMEHANDLE_METHOD:
2514+
case CORINFO_HELP_RUNTIMEHANDLE_CLASS:
2515+
case CORINFO_HELP_RUNTIMEHANDLE_METHOD_LOG:
2516+
case CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG:
2517+
return true;
2518+
default:
2519+
return false;
2520+
}
2521+
}
2522+
24952523
//-------------------------------------------------------------------------
24962524
// IsSpecialIntrinsic: Determine if this GT_CALL node is a specific intrinsic.
24972525
//
@@ -2550,6 +2578,12 @@ bool GenTreeCall::Equals(GenTreeCall* c1, GenTreeCall* c2)
25502578
return false;
25512579
}
25522580

2581+
if (c1->IsHelperCall() && ((c1->gtCallMoreFlags & GTF_CALL_M_CAST_OBJ_NONNULL) !=
2582+
(c2->gtCallMoreFlags & GTF_CALL_M_CAST_OBJ_NONNULL)))
2583+
{
2584+
return false;
2585+
}
2586+
25532587
#ifdef FEATURE_READYTORUN
25542588
if (c1->gtEntryPoint.addr != c2->gtEntryPoint.addr)
25552589
{

src/coreclr/jit/gentree.h

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4101,11 +4101,12 @@ enum GenTreeCallFlags : unsigned int
41014101
GTF_CALL_M_GUARDED_DEVIRT_CHAIN = 0x00080000, // this call is a candidate for chained guarded devirtualization
41024102
GTF_CALL_M_ALLOC_SIDE_EFFECTS = 0x00100000, // this is a call to an allocator with side effects
41034103
GTF_CALL_M_SUPPRESS_GC_TRANSITION = 0x00200000, // suppress the GC transition (i.e. during a pinvoke) but a separate GC safe point is required.
4104-
GTF_CALL_M_EXP_RUNTIME_LOOKUP = 0x00400000, // [DEBUG only] this call needs to be transformed into CFG for the dynamic dictionary expansion feature.
41054104
GTF_CALL_M_EXPANDED_EARLY = 0x00800000, // the Virtual Call target address is expanded and placed in gtControlExpr in Morph rather than in Lower
41064105
GTF_CALL_M_HAS_LATE_DEVIRT_INFO = 0x01000000, // this call has late devirtualzation info
41074106
GTF_CALL_M_LDVIRTFTN_INTERFACE = 0x02000000, // ldvirtftn on an interface type
41084107
GTF_CALL_M_CAST_CAN_BE_EXPANDED = 0x04000000, // this cast (helper call) can be expanded if it's profitable. To be removed.
4108+
GTF_CALL_M_CAST_OBJ_NONNULL = 0x08000000, // if we expand this specific cast we don't need to check the input object for null
4109+
// NOTE: if needed, this flag can be removed, and we can introduce new _NONNUL cast helpers
41094110
};
41104111

41114112
inline constexpr GenTreeCallFlags operator ~(GenTreeCallFlags a)
@@ -4141,6 +4142,7 @@ enum GenTreeCallDebugFlags : unsigned int
41414142
GTF_CALL_MD_DEVIRTUALIZED = 0x00000002, // this call was devirtualized
41424143
GTF_CALL_MD_UNBOXED = 0x00000004, // this call was optimized to use the unboxed entry point
41434144
GTF_CALL_MD_GUARDED = 0x00000008, // this call was transformed by guarded devirtualization
4145+
GTF_CALL_MD_RUNTIME_LOOKUP_EXPANDED = 0x00000010, // this runtime lookup helper is expanded
41444146
};
41454147

41464148
inline constexpr GenTreeCallDebugFlags operator ~(GenTreeCallDebugFlags a)
@@ -5476,21 +5478,6 @@ struct GenTreeCall final : public GenTree
54765478
}
54775479
#endif
54785480

5479-
void SetExpRuntimeLookup()
5480-
{
5481-
gtCallMoreFlags |= GTF_CALL_M_EXP_RUNTIME_LOOKUP;
5482-
}
5483-
5484-
void ClearExpRuntimeLookup()
5485-
{
5486-
gtCallMoreFlags &= ~GTF_CALL_M_EXP_RUNTIME_LOOKUP;
5487-
}
5488-
5489-
bool IsExpRuntimeLookup() const
5490-
{
5491-
return (gtCallMoreFlags & GTF_CALL_M_EXP_RUNTIME_LOOKUP) != 0;
5492-
}
5493-
54945481
void SetExpandedEarly()
54955482
{
54965483
gtCallMoreFlags |= GTF_CALL_M_EXPANDED_EARLY;
@@ -5684,6 +5671,8 @@ struct GenTreeCall final : public GenTree
56845671

56855672
bool IsHelperCall(Compiler* compiler, unsigned helper) const;
56865673

5674+
bool IsRuntimeLookupHelperCall(Compiler* compiler) const;
5675+
56875676
bool IsSpecialIntrinsic(Compiler* compiler, NamedIntrinsic ni) const;
56885677

56895678
CorInfoHelpFunc GetHelperNum() const;

src/coreclr/jit/helperexpansion.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ GenTreeCall* Compiler::gtNewRuntimeLookupHelperCallNode(CORINFO_RUNTIME_LOOKUP*
9494
// Leave a note that this method has runtime lookups we might want to expand (nullchecks, size checks) later.
9595
// We can also consider marking current block as a runtime lookup holder to improve TP for Tier0
9696
impInlineRoot()->setMethodHasExpRuntimeLookup();
97-
helperCall->SetExpRuntimeLookup();
9897
if (!impInlineRoot()->GetSignatureToLookupInfoMap()->Lookup(pRuntimeLookup->signature))
9998
{
10099
JITDUMP("Registering %p in SignatureToLookupInfoMap\n", pRuntimeLookup->signature)
@@ -152,13 +151,12 @@ bool Compiler::fgExpandRuntimeLookupsForCall(BasicBlock** pBlock, Statement* stm
152151
{
153152
BasicBlock* block = *pBlock;
154153

155-
if (!call->IsHelperCall() || !call->IsExpRuntimeLookup())
154+
if (!call->IsRuntimeLookupHelperCall(this))
156155
{
157156
return false;
158157
}
159158

160-
// Clear ExpRuntimeLookup flag so we won't miss any runtime lookup that needs partial expansion
161-
call->ClearExpRuntimeLookup();
159+
INDEBUG(call->gtCallDebugFlags |= GTF_CALL_MD_RUNTIME_LOOKUP_EXPANDED);
162160

163161
if (call->IsTailCall())
164162
{
@@ -2405,6 +2403,15 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt,
24052403
assert(BasicBlock::sameEHRegion(firstBb, fallbackBb));
24062404
assert(BasicBlock::sameEHRegion(firstBb, lastTypeCheckBb));
24072405

2406+
// call guarantees that obj is never null, we can drop the nullcheck
2407+
// by converting it to a BBJ_ALWAYS to typeCheckBb.
2408+
if ((call->gtCallMoreFlags & GTF_CALL_M_CAST_OBJ_NONNULL) != 0)
2409+
{
2410+
fgRemoveStmt(nullcheckBb, nullcheckBb->lastStmt());
2411+
nullcheckBb->SetKindAndTarget(BBJ_ALWAYS, typeChecksBbs[0]);
2412+
fgRemoveRefPred(lastBb, nullcheckBb);
2413+
}
2414+
24082415
// Bonus step: merge prevBb with nullcheckBb as they are likely to be mergeable
24092416
if (fgCanCompactBlocks(firstBb, nullcheckBb))
24102417
{

src/coreclr/jit/lower.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2203,7 +2203,8 @@ GenTree* Lowering::LowerCall(GenTree* node)
22032203
JITDUMP("\n");
22042204

22052205
// All runtime lookups are expected to be expanded in fgExpandRuntimeLookups
2206-
assert(!call->IsExpRuntimeLookup());
2206+
assert(!call->IsRuntimeLookupHelperCall(comp) ||
2207+
(call->gtCallDebugFlags & GTF_CALL_MD_RUNTIME_LOOKUP_EXPANDED) != 0);
22072208

22082209
// Also, always expand static cctor helper for NativeAOT, see
22092210
// https://github.com/dotnet/runtime/issues/68278#issuecomment-1543322819

0 commit comments

Comments
 (0)