Skip to content

Commit 3e8ea8d

Browse files
committed
Polish the implementation
1 parent 13ad9d8 commit 3e8ea8d

File tree

9 files changed

+93
-68
lines changed

9 files changed

+93
-68
lines changed

src/coreclr/inc/corinfo.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,16 +1512,16 @@ struct CORINFO_DEVIRTUALIZATION_INFO
15121512
// - If pResolvedTokenDevirtualizedMethod is not set to NULL and targeting an R2R image
15131513
// use it as the parameter to getCallInfo
15141514
// - isInstantiatingStub is set to TRUE if the devirtualized method is a generic method instantiating stub
1515-
// - wasArrayInterfaceDevirt is set TRUE for array interface method devirtualization
1516-
// (in which case the method handle and context will be a generic method)
1515+
// - hasGenericMethodHandleContext is set TRUE for cases where the method handle and context will be a generic method
1516+
// (for array interface method or generic virtual method devirtualization)
15171517
//
15181518
CORINFO_METHOD_HANDLE devirtualizedMethod;
15191519
CORINFO_CONTEXT_HANDLE exactContext;
15201520
CORINFO_DEVIRTUALIZATION_DETAIL detail;
15211521
CORINFO_RESOLVED_TOKEN resolvedTokenDevirtualizedMethod;
15221522
CORINFO_RESOLVED_TOKEN resolvedTokenDevirtualizedUnboxedMethod;
15231523
bool isInstantiatingStub;
1524-
bool wasArrayInterfaceDevirt;
1524+
bool hasGenericMethodHandleContext;
15251525
};
15261526

15271527
//----------------------------------------------------------------------------

src/coreclr/jit/gentree.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2318,8 +2318,8 @@ int GenTreeCall::GetNonStandardAddedArgCount(Compiler* compiler) const
23182318
bool GenTreeCall::IsDevirtualizationCandidate(Compiler* compiler) const
23192319
{
23202320
return (IsVirtual() && gtCallType == CT_USER_FUNC) ||
2321-
(gtCallType == CT_INDIRECT && (gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_VIRTUAL_FUNC_PTR) ||
2322-
gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_GVMLOOKUP_FOR_SLOT)));
2321+
// TODO: Support CORINFO_HELP_GVMLOOKUP_FOR_SLOT for NativeAOT
2322+
(gtCallType == CT_INDIRECT && gtCallAddr->IsHelperCall(compiler, CORINFO_HELP_VIRTUAL_FUNC_PTR));
23232323
}
23242324

23252325
//-------------------------------------------------------------------------

src/coreclr/jit/importercalls.cpp

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,6 @@ var_types Compiler::impImportCall(OPCODE opcode,
185185
compInlineResult->NoteFatal(InlineObservation::CALLEE_HAS_MANAGED_VARARGS);
186186
return TYP_UNDEF;
187187
}
188-
189-
if ((mflags & CORINFO_FLG_VIRTUAL) && (sig->sigInst.methInstCount != 0) && (opcode == CEE_CALLVIRT))
190-
{
191-
compInlineResult->NoteFatal(InlineObservation::CALLEE_IS_GENERIC_VIRTUAL);
192-
return TYP_UNDEF;
193-
}
194188
}
195189

196190
clsHnd = pResolvedToken->hClass;
@@ -384,12 +378,6 @@ var_types Compiler::impImportCall(OPCODE opcode,
384378

385379
case CORINFO_VIRTUALCALL_LDVIRTFTN:
386380
{
387-
if (compIsForInlining())
388-
{
389-
compInlineResult->NoteFatal(InlineObservation::CALLSITE_HAS_CALL_VIA_LDVIRTFTN);
390-
return TYP_UNDEF;
391-
}
392-
393381
assert(!(mflags & CORINFO_FLG_STATIC)); // can't call a static method
394382
assert(!(clsFlags & CORINFO_FLG_VALUECLASS));
395383
// OK, We've been told to call via LDVIRTFTN, so just
@@ -402,11 +390,20 @@ var_types Compiler::impImportCall(OPCODE opcode,
402390
thisPtr = impTransformThis(thisPtr, pConstrainedResolvedToken, callInfo->thisTransform);
403391
assert(thisPtr != nullptr);
404392

393+
GenTree* origThisPtr = thisPtr;
394+
405395
// Clone the (possibly transformed) "this" pointer
406396
GenTree* thisPtrCopy;
407397
thisPtr =
408398
impCloneExpr(thisPtr, &thisPtrCopy, CHECK_SPILL_ALL, nullptr DEBUGARG("LDVIRTFTN this pointer"));
409399

400+
// We cloned the "this" pointer, mark it as a single def and set the class for it
401+
if (thisPtr->TypeIs(TYP_REF) && (origThisPtr != thisPtr))
402+
{
403+
lvaGetDesc(thisPtr->AsLclVar())->lvSingleDef = 1;
404+
lvaSetClass(thisPtr->AsLclVar()->GetLclNum(), origThisPtr);
405+
}
406+
410407
GenTree* fptr = impImportLdvirtftn(thisPtr, pResolvedToken, callInfo);
411408
assert(fptr != nullptr);
412409

@@ -415,10 +412,6 @@ var_types Compiler::impImportCall(OPCODE opcode,
415412

416413
// Now make an indirect call through the function pointer
417414

418-
unsigned lclNum = lvaGrabTemp(true DEBUGARG("VirtualCall through function pointer"));
419-
impStoreToTemp(lclNum, fptr, CHECK_SPILL_ALL);
420-
fptr = gtNewLclvNode(lclNum, TYP_I_IMPL);
421-
422415
call->AsCall()->gtCallAddr = fptr;
423416
call->gtFlags |= GTF_EXCEPT | (fptr->gtFlags & GTF_GLOB_EFFECT);
424417

@@ -439,7 +432,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
439432
// Sine we are jumping over some code, check that its OK to skip that code
440433
assert((sig->callConv & CORINFO_CALLCONV_MASK) != CORINFO_CALLCONV_VARARG &&
441434
(sig->callConv & CORINFO_CALLCONV_MASK) != CORINFO_CALLCONV_NATIVEVARARG);
442-
goto DONE;
435+
goto DEVIRT;
443436
}
444437

445438
case CORINFO_CALL:
@@ -933,6 +926,8 @@ var_types Compiler::impImportCall(OPCODE opcode,
933926
}
934927
}
935928

929+
DEVIRT:
930+
936931
bool probing;
937932
probing = impConsiderCallProbe(call->AsCall(), rawILOffset);
938933

@@ -1232,7 +1227,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
12321227
// If the call is virtual, record the inliner's context for possible use during late devirt inlining.
12331228
// Also record the generics context if there is any.
12341229
//
1235-
if (call->AsCall()->IsVirtual() && (call->AsCall()->gtCallType != CT_INDIRECT))
1230+
if (call->AsCall()->IsDevirtualizationCandidate(this))
12361231
{
12371232
JITDUMP("\nSaving generic context %p and inline context %p for call [%06u]\n", dspPtr(exactContextHnd),
12381233
dspPtr(compInlineContext), dspTreeID(call->AsCall()));
@@ -1449,8 +1444,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
14491444
varDsc->lvType = call->AsCall()->gtReturnType;
14501445
}
14511446

1452-
// TODO-Bug: CHECK_SPILL_NONE here looks wrong.
1453-
impStoreToTemp(calliSlot, call, CHECK_SPILL_NONE);
1447+
impStoreToTemp(calliSlot, call, CHECK_SPILL_ALL);
14541448
// impStoreToTemp can change src arg list and return type for call that returns struct.
14551449
var_types type = genActualType(lvaTable[calliSlot].TypeGet());
14561450
call = gtNewLclvNode(calliSlot, type);
@@ -7136,7 +7130,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
71367130
}
71377131

71387132
addGuardedDevirtualizationCandidate(call, exactMethod, exactCls, exactContext, exactMethodAttrs,
7139-
clsAttrs, likelyHood, dvInfo.wasArrayInterfaceDevirt,
7133+
clsAttrs, likelyHood, dvInfo.hasGenericMethodHandleContext,
71407134
dvInfo.isInstantiatingStub, originalContext);
71417135
}
71427136

@@ -7208,7 +7202,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call,
72087202

72097203
likelyContext = dvInfo.exactContext;
72107204
likelyMethod = dvInfo.devirtualizedMethod;
7211-
arrayInterface = dvInfo.wasArrayInterfaceDevirt;
7205+
arrayInterface = dvInfo.hasGenericMethodHandleContext;
72127206
instantiatingStub = dvInfo.isInstantiatingStub;
72137207
}
72147208
else
@@ -8210,14 +8204,14 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
82108204

82118205
if (((size_t)exactContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS)
82128206
{
8213-
assert(!dvInfo.wasArrayInterfaceDevirt);
8207+
assert(!dvInfo.hasGenericMethodHandleContext);
82148208
derivedClass = (CORINFO_CLASS_HANDLE)((size_t)exactContext & ~CORINFO_CONTEXTFLAGS_MASK);
82158209
}
82168210
else
82178211
{
82188212
// Array interface devirt can return a nonvirtual generic method of the non-generic SZArrayHelper class.
82198213
//
8220-
assert(dvInfo.wasArrayInterfaceDevirt);
8214+
assert(dvInfo.hasGenericMethodHandleContext);
82218215
assert(((size_t)exactContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD);
82228216
derivedClass = info.compCompHnd->getMethodClass(derivedMethod);
82238217
}
@@ -8238,17 +8232,17 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
82388232

82398233
if (dvInfo.isInstantiatingStub)
82408234
{
8241-
// We should only end up with generic methods for array interface devirt.
8235+
// We should only end up with generic methods for array interface or GVM devirt.
82428236
//
8243-
assert(dvInfo.wasArrayInterfaceDevirt);
8237+
assert(dvInfo.hasGenericMethodHandleContext);
82448238

82458239
// We don't expect NAOT to end up here, since it has Array<T>
8246-
// and normal devirtualization.
8240+
// and normal devirtualization. For GVM devirt we don't support NAOT yet.
82478241
//
82488242
assert(!IsTargetAbi(CORINFO_NATIVEAOT_ABI));
82498243

82508244
// We don't expect R2R to end up here, since it does not (yet) support
8251-
// array interface devirtualization.
8245+
// array interface or GVM devirtualization.
82528246
//
82538247
assert(!opts.IsReadyToRun());
82548248

@@ -8354,26 +8348,43 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
83548348

83558349
JITDUMP(" %s; can devirtualize\n", note);
83568350

8357-
// Make the updates.
8358-
call->gtFlags &= ~GTF_CALL_VIRT_VTABLE;
8359-
call->gtFlags &= ~GTF_CALL_VIRT_STUB;
8360-
call->gtCallMethHnd = derivedMethod;
8361-
call->gtCallType = CT_USER_FUNC;
8362-
call->gtControlExpr = nullptr;
8363-
INDEBUG(call->gtCallDebugFlags |= GTF_CALL_MD_DEVIRTUALIZED);
8364-
83658351
if (dvInfo.isInstantiatingStub)
83668352
{
83678353
// Pass the instantiating stub method desc as the inst param arg.
83688354
//
83698355
// Note different embedding would be needed for NAOT/R2R,
83708356
// but we have ruled those out above.
83718357
//
8372-
GenTree* const instParam =
8373-
gtNewIconEmbHndNode(instantiatingStub, nullptr, GTF_ICON_METHOD_HDL, instantiatingStub);
8358+
GenTree* instParam = nullptr;
8359+
// See if this is a generic virtual method call.
8360+
if (call->gtCallType == CT_INDIRECT && call->gtCallAddr->IsHelperCall(this, CORINFO_HELP_VIRTUAL_FUNC_PTR))
8361+
{
8362+
// We need to pass the RUNTIMELOOKUP helper call as the inst param arg
8363+
// instead of the instantiating stub if there is one.
8364+
//
8365+
GenTree* const methHndNode =
8366+
call->gtCallAddr->AsCall()->gtArgs.FindWellKnownArg(WellKnownArg::RuntimeMethodHandle)->GetEarlyNode();
8367+
if (methHndNode->OperIs(GT_RUNTIMELOOKUP))
8368+
{
8369+
instParam = methHndNode;
8370+
}
8371+
}
8372+
// For cases where we don't have a RUNTIMELOOKUP helper call, we pass the instantiating stub.
8373+
if (instParam == nullptr)
8374+
{
8375+
instParam = gtNewIconEmbHndNode(instantiatingStub, nullptr, GTF_ICON_METHOD_HDL, instantiatingStub);
8376+
}
83748377
call->gtArgs.InsertInstParam(this, instParam);
83758378
}
83768379

8380+
// Make the updates.
8381+
call->gtFlags &= ~GTF_CALL_VIRT_VTABLE;
8382+
call->gtFlags &= ~GTF_CALL_VIRT_STUB;
8383+
call->gtCallMethHnd = derivedMethod;
8384+
call->gtCallType = CT_USER_FUNC;
8385+
call->gtControlExpr = nullptr;
8386+
INDEBUG(call->gtCallDebugFlags |= GTF_CALL_MD_DEVIRTUALIZED);
8387+
83778388
// Virtual calls include an implicit null check, which we may
83788389
// now need to make explicit.
83798390
if (!objIsNonNull)

src/coreclr/jit/inline.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ INLINE_OBSERVATION(HAS_NO_BODY, bool, "has no body",
3939
INLINE_OBSERVATION(HAS_NULL_FOR_LDELEM, bool, "has null pointer for ldelem", FATAL, CALLEE)
4040
INLINE_OBSERVATION(HAS_UNMANAGED_CALLCONV, bool, "has unmanaged calling convention", FATAL, CALLEE)
4141
INLINE_OBSERVATION(IS_ARRAY_METHOD, bool, "is array method", FATAL, CALLEE)
42-
INLINE_OBSERVATION(IS_GENERIC_VIRTUAL, bool, "generic virtual", FATAL, CALLEE)
4342
INLINE_OBSERVATION(IS_JIT_NOINLINE, bool, "noinline per JitNoinline", FATAL, CALLEE)
4443
INLINE_OBSERVATION(IS_NOINLINE, bool, "noinline per IL/cached result", FATAL, CALLEE)
4544
INLINE_OBSERVATION(IS_SYNCHRONIZED, bool, "is synchronized", FATAL, CALLEE)
@@ -134,7 +133,6 @@ INLINE_OBSERVATION(COMPILATION_ERROR, bool, "compilation error",
134133
INLINE_OBSERVATION(COMPILATION_FAILURE, bool, "failed to compile", FATAL, CALLSITE)
135134
INLINE_OBSERVATION(EXPLICIT_TAIL_PREFIX, bool, "explicit tail prefix", FATAL, CALLSITE)
136135
INLINE_OBSERVATION(GENERIC_DICTIONARY_LOOKUP, bool, "runtime dictionary lookup", FATAL, CALLSITE)
137-
INLINE_OBSERVATION(HAS_CALL_VIA_LDVIRTFTN, bool, "call via ldvirtftn", FATAL, CALLSITE)
138136
INLINE_OBSERVATION(HAS_COMPLEX_HANDLE, bool, "complex handle access", FATAL, CALLSITE)
139137
INLINE_OBSERVATION(IMPLICIT_REC_TAIL_CALL, bool, "implicit recursive tail call", FATAL, CALLSITE)
140138
INLINE_OBSERVATION(IS_CALL_TO_HELPER, bool, "target is helper", FATAL, CALLSITE)

src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1303,7 +1303,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info)
13031303
info->exactContext = null;
13041304
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_UNKNOWN;
13051305
info->isInstantiatingStub = false;
1306-
info->wasArrayInterfaceDevirt = false;
1306+
info->hasGenericMethodHandleContext = false;
13071307

13081308
TypeDesc objType = HandleToObject(info->objClass);
13091309

src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,8 +1082,8 @@ public unsafe struct CORINFO_DEVIRTUALIZATION_INFO
10821082
// - exactContext is set to wrapped CORINFO_CLASS_HANDLE of devirt'ed method table.
10831083
// - detail describes the computation done by the jit host
10841084
// - isInstantiatingStub is set to TRUE if the devirtualized method is a method instantiation stub
1085-
// - wasArrayInterfaceDevirt is set TRUE for array interface method devirtualization
1086-
// (in which case the method handle and context will be a generic method)
1085+
// - hasGenericMethodHandleContext is set TRUE for cases where the method handle and context will be a generic method
1086+
// (for array interface method or generic virtual method devirtualization)
10871087
//
10881088
public CORINFO_METHOD_STRUCT_* devirtualizedMethod;
10891089
public CORINFO_CONTEXT_STRUCT* exactContext;
@@ -1092,8 +1092,8 @@ public unsafe struct CORINFO_DEVIRTUALIZATION_INFO
10921092
public CORINFO_RESOLVED_TOKEN resolvedTokenDevirtualizedUnboxedMethod;
10931093
public byte _isInstantiatingStub;
10941094
public bool isInstantiatingStub { get { return _isInstantiatingStub != 0; } set { _isInstantiatingStub = value ? (byte)1 : (byte)0; } }
1095-
public byte _wasArrayInterfaceDevirt;
1096-
public bool wasArrayInterfaceDevirt { get { return _wasArrayInterfaceDevirt != 0; } set { _wasArrayInterfaceDevirt = value ? (byte)1 : (byte)0; } }
1095+
public byte _hasGenericMethodHandleContext;
1096+
public bool hasGenericMethodHandleContext { get { return _hasGenericMethodHandleContext != 0; } set { _hasGenericMethodHandleContext = value ? (byte)1 : (byte)0; } }
10971097
}
10981098

10991099
//----------------------------------------------------------------------------

src/coreclr/tools/superpmi/superpmi-shared/agnostic.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ struct Agnostic_ResolveVirtualMethodResult
653653
bool returnValue;
654654
DWORDLONG devirtualizedMethod;
655655
bool isInstantiatingStub;
656-
bool wasArrayInterfaceDevirt;
656+
bool hasGenericMethodHandleContext;
657657
DWORDLONG exactContext;
658658
DWORD detail;
659659
Agnostic_CORINFO_RESOLVED_TOKEN resolvedTokenDevirtualizedMethod;

src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3293,12 +3293,12 @@ void MethodContext::recResolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info
32933293
key.pResolvedTokenVirtualMethod = SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(info->pResolvedTokenVirtualMethod, ResolveToken);
32943294

32953295
Agnostic_ResolveVirtualMethodResult result;
3296-
result.returnValue = returnValue;
3297-
result.devirtualizedMethod = CastHandle(info->devirtualizedMethod);
3298-
result.isInstantiatingStub = info->isInstantiatingStub;
3299-
result.exactContext = CastHandle(info->exactContext);
3300-
result.detail = (DWORD) info->detail;
3301-
result.wasArrayInterfaceDevirt = info->wasArrayInterfaceDevirt;
3296+
result.returnValue = returnValue;
3297+
result.devirtualizedMethod = CastHandle(info->devirtualizedMethod);
3298+
result.isInstantiatingStub = info->isInstantiatingStub;
3299+
result.exactContext = CastHandle(info->exactContext);
3300+
result.detail = (DWORD)info->detail;
3301+
result.hasGenericMethodHandleContext = info->hasGenericMethodHandleContext;
33023302

33033303
if (returnValue)
33043304
{
@@ -3323,11 +3323,11 @@ void MethodContext::dmpResolveVirtualMethod(const Agnostic_ResolveVirtualMethodK
33233323
key.context,
33243324
key.pResolvedTokenVirtualMethodNonNull,
33253325
key.pResolvedTokenVirtualMethodNonNull ? SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.pResolvedTokenVirtualMethod).c_str() : "???");
3326-
printf(", value returnValue-%s, devirtMethod-%016" PRIX64 ", instantiatingStub-%s, wasArrayInterfaceDevirt-%s, exactContext-%016" PRIX64 ", detail-%d, tokDvMeth{%s}, tokDvUnboxMeth{%s}",
3326+
printf(", value returnValue-%s, devirtMethod-%016" PRIX64 ", instantiatingStub-%s, hasGenericMethodHandleContext-%s, exactContext-%016" PRIX64 ", detail-%d, tokDvMeth{%s}, tokDvUnboxMeth{%s}",
33273327
result.returnValue ? "true" : "false",
33283328
result.devirtualizedMethod,
33293329
result.isInstantiatingStub ? "true" : "false",
3330-
result.wasArrayInterfaceDevirt ? "true" : "false",
3330+
result.hasGenericMethodHandleContext ? "true" : "false",
33313331
result.exactContext,
33323332
result.detail,
33333333
result.returnValue ? SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(result.resolvedTokenDevirtualizedMethod).c_str() : "???",
@@ -3352,7 +3352,7 @@ bool MethodContext::repResolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info
33523352

33533353
info->devirtualizedMethod = (CORINFO_METHOD_HANDLE) result.devirtualizedMethod;
33543354
info->isInstantiatingStub = result.isInstantiatingStub;
3355-
info->wasArrayInterfaceDevirt = result.wasArrayInterfaceDevirt;
3355+
info->hasGenericMethodHandleContext = result.hasGenericMethodHandleContext;
33563356
info->exactContext = (CORINFO_CONTEXT_HANDLE) result.exactContext;
33573357
info->detail = (CORINFO_DEVIRTUALIZATION_DETAIL) result.detail;
33583358
if (result.returnValue)

0 commit comments

Comments
 (0)