Skip to content

Commit ce61999

Browse files
authored
GDV: don't emit fallback call if classes are "exact" (#87055)
1 parent 825f7c3 commit ce61999

File tree

5 files changed

+49
-7
lines changed

5 files changed

+49
-7
lines changed

src/coreclr/jit/gentree.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2212,6 +2212,7 @@ InlineCandidateInfo* GenTreeCall::GetGDVCandidateInfo(uint8_t index)
22122212
//
22132213
void GenTreeCall::AddGDVCandidateInfo(Compiler* comp, InlineCandidateInfo* candidateInfo)
22142214
{
2215+
assert((gtCallMoreFlags & GTF_CALL_M_GUARDED_DEVIRT_EXACT) == 0);
22152216
assert(gtInlineInfoCount < MAX_GDV_TYPE_CHECKS);
22162217
assert(candidateInfo != nullptr);
22172218

@@ -2248,6 +2249,9 @@ void GenTreeCall::AddGDVCandidateInfo(Compiler* comp, InlineCandidateInfo* candi
22482249
//
22492250
void GenTreeCall::RemoveGDVCandidateInfo(Compiler* comp, uint8_t index)
22502251
{
2252+
// We change the number of candidates so it's no longer "doesn't need a fallback"
2253+
gtCallMoreFlags &= ~GTF_CALL_M_GUARDED_DEVIRT_EXACT;
2254+
22512255
assert(index < gtInlineInfoCount);
22522256

22532257
if (gtInlineInfoCount == 1)

src/coreclr/jit/gentree.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4048,6 +4048,7 @@ enum GenTreeCallFlags : unsigned int
40484048
GTF_CALL_M_DEVIRTUALIZED = 0x00040000, // this call was devirtualized
40494049
GTF_CALL_M_UNBOXED = 0x00080000, // this call was optimized to use the unboxed entry point
40504050
GTF_CALL_M_GUARDED_DEVIRT = 0x00100000, // this call is a candidate for guarded devirtualization
4051+
GTF_CALL_M_GUARDED_DEVIRT_EXACT = 0x80000000, // this call is a candidate for guarded devirtualization without a fallback
40514052
GTF_CALL_M_GUARDED_DEVIRT_CHAIN = 0x00200000, // this call is a candidate for chained guarded devirtualization
40524053
GTF_CALL_M_GUARDED = 0x00400000, // this call was transformed by guarded devirtualization
40534054
GTF_CALL_M_ALLOC_SIDE_EFFECTS = 0x00800000, // this is a call to an allocator with side effects
@@ -5370,7 +5371,7 @@ struct GenTreeCall final : public GenTree
53705371

53715372
void ClearGuardedDevirtualizationCandidate()
53725373
{
5373-
gtCallMoreFlags &= ~GTF_CALL_M_GUARDED_DEVIRT;
5374+
gtCallMoreFlags &= ~(GTF_CALL_M_GUARDED_DEVIRT | GTF_CALL_M_GUARDED_DEVIRT_EXACT);
53745375
}
53755376

53765377
void SetIsGuarded()

src/coreclr/jit/importercalls.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5960,7 +5960,6 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
59605960
assert((numExactClasses > 0) && (numExactClasses <= maxTypeChecks));
59615961
JITDUMP("We have exactly %d classes implementing %s:\n", numExactClasses, eeGetClassName(baseClass));
59625962

5963-
int skipped = 0;
59645963
for (int exactClsIdx = 0; exactClsIdx < numExactClasses; exactClsIdx++)
59655964
{
59665965
CORINFO_CLASS_HANDLE exactCls = exactClasses[exactClsIdx];
@@ -6010,6 +6009,14 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
60106009
addGuardedDevirtualizationCandidate(call, exactMethod, exactCls, exactMethodAttrs, clsAttrs,
60116010
likelyHood);
60126011
}
6012+
6013+
if (call->GetInlineCandidatesCount() == numExactClasses)
6014+
{
6015+
assert(numExactClasses > 0);
6016+
call->gtCallMoreFlags |= GTF_CALL_M_GUARDED_DEVIRT_EXACT;
6017+
// NOTE: we have to drop this flag if we change the number of candidates before we expand.
6018+
}
6019+
60136020
return;
60146021
}
60156022
}

src/coreclr/jit/indirectcalltransformer.cpp

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,8 @@ class IndirectCallTransformer
484484
JITDUMP("Likelihood of correct guess is %u\n", likelihood);
485485

486486
// TODO: implement chaining for multiple GDV candidates
487-
const bool canChainGdv = GetChecksCount() == 1;
487+
const bool canChainGdv =
488+
(GetChecksCount() == 1) && ((origCall->gtCallMoreFlags & GTF_CALL_M_GUARDED_DEVIRT_EXACT) == 0);
488489
if (canChainGdv)
489490
{
490491
const bool isChainedGdv = (origCall->gtCallMoreFlags & GTF_CALL_M_GUARDED_DEVIRT_CHAIN) != 0;
@@ -644,6 +645,17 @@ class IndirectCallTransformer
644645
//
645646
lastStmt = checkBlock->lastStmt();
646647

648+
// In case if GDV candidates are "exact" (e.g. we have the full list of classes implementing
649+
// the given interface in the app - NativeAOT only at this moment) we assume the last
650+
// check will always be true, so we just simplify the block to BBJ_NONE
651+
const bool isLastCheck = (checkIdx == origCall->GetInlineCandidatesCount() - 1);
652+
if (isLastCheck && ((origCall->gtCallMoreFlags & GTF_CALL_M_GUARDED_DEVIRT_EXACT) != 0))
653+
{
654+
checkBlock->bbJumpDest = nullptr;
655+
checkBlock->bbJumpKind = BBJ_NONE;
656+
return;
657+
}
658+
647659
InlineCandidateInfo* guardedInfo = origCall->GetGDVCandidateInfo(checkIdx);
648660

649661
// Create comparison. On success we will jump to do the indirect call.
@@ -986,10 +998,23 @@ class IndirectCallTransformer
986998
elseBlock = CreateAndInsertBasicBlock(BBJ_NONE, thenBlock);
987999
elseBlock->bbFlags |= currBlock->bbFlags & BBF_SPLIT_GAINED;
9881000

1001+
// CheckBlock flows into elseBlock unless we deal with the case
1002+
// where we know the last check is always true (in case of "exact" GDV)
1003+
if (checkBlock->KindIs(BBJ_COND))
1004+
{
1005+
checkBlock->bbJumpDest = elseBlock;
1006+
compiler->fgAddRefPred(elseBlock, checkBlock);
1007+
}
1008+
else
1009+
{
1010+
// In theory, we could simplify the IR here, but since it's a rare case
1011+
// and is NativeAOT-only, we just assume the unreached block will be removed
1012+
// by other phases.
1013+
assert(origCall->gtCallMoreFlags & GTF_CALL_M_GUARDED_DEVIRT_EXACT);
1014+
}
1015+
9891016
// elseBlock always flows into remainderBlock
990-
checkBlock->bbJumpDest = elseBlock;
9911017
compiler->fgAddRefPred(remainderBlock, elseBlock);
992-
compiler->fgAddRefPred(elseBlock, checkBlock);
9931018

9941019
// Calculate the likelihood of the else block as a remainder of the sum
9951020
// of all the other likelihoods.

src/coreclr/jit/likelyclass.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,19 @@ static unsigned getLikelyClassesOrMethods(LikelyClassMethodRecord*
230230
LikelyClassMethodHistogramEntry sortedEntries[HISTOGRAM_MAX_SIZE_COUNT];
231231

232232
// Since this method can be invoked without a jit instance we can't use any existing allocators
233-
unsigned knownHandles = 0;
233+
unsigned knownHandles = 0;
234+
unsigned containsUnknownHandles = false;
234235
for (unsigned m = 0; m < h.countHistogramElements; m++)
235236
{
236237
LikelyClassMethodHistogramEntry const hist = h.HistogramEntryAt(m);
237238
if (!ICorJitInfo::IsUnknownHandle(hist.m_handle))
238239
{
239240
sortedEntries[knownHandles++] = hist;
240241
}
242+
else
243+
{
244+
containsUnknownHandles = true;
245+
}
241246
}
242247

243248
if (knownHandles == 0)
@@ -268,7 +273,7 @@ static unsigned getLikelyClassesOrMethods(LikelyClassMethodRecord*
268273

269274
// Distribute the rounding error and just apply it to the first entry.
270275
// Assume that there is no error If we have unknown handles.
271-
if (numberOfClasses == h.m_totalCount)
276+
if (!containsUnknownHandles)
272277
{
273278
assert(numberOfClasses > 0);
274279
assert(totalLikelihood > 0);

0 commit comments

Comments
 (0)