Skip to content

Commit 7f11ef4

Browse files
authored
WIP: runtime support for static virtual interface methods (#2)
* Minimalistic JIT change to enable the new constrained. opcodes * Runtime changes up to the point of constraint resolution in getCallInfo
1 parent 6fd0e8c commit 7f11ef4

8 files changed

Lines changed: 135 additions & 78 deletions

File tree

src/coreclr/jit/fgbasic.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,9 +1143,9 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
11431143
{
11441144
OPCODE actualOpcode = impGetNonPrefixOpcode(codeAddr, codeEndp);
11451145

1146-
if (actualOpcode != CEE_CALLVIRT)
1146+
if (actualOpcode != CEE_CALLVIRT && actualOpcode != CEE_CALL && actualOpcode != CEE_LDFTN)
11471147
{
1148-
BADCODE("constrained. has to be followed by callvirt");
1148+
BADCODE("constrained. has to be followed by callvirt, call or ldftn");
11491149
}
11501150
}
11511151
goto OBSERVE_OPCODE;

src/coreclr/jit/importer.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13862,7 +13862,8 @@ void Compiler::impImportBlockCode(BasicBlock* block)
1386213862

1386313863
JITDUMP(" %08X", resolvedToken.token);
1386413864

13865-
eeGetCallInfo(&resolvedToken, nullptr /* constraint typeRef*/,
13865+
eeGetCallInfo(&resolvedToken,
13866+
(prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr,
1386613867
addVerifyFlag(combine(CORINFO_CALLINFO_SECURITYCHECKS, CORINFO_CALLINFO_LDFTN)),
1386713868
&callInfo);
1386813869

@@ -14033,9 +14034,9 @@ void Compiler::impImportBlockCode(BasicBlock* block)
1403314034

1403414035
{
1403514036
OPCODE actualOpcode = impGetNonPrefixOpcode(codeAddr, codeEndp);
14036-
if (actualOpcode != CEE_CALLVIRT)
14037+
if (actualOpcode != CEE_CALLVIRT && actualOpcode != CEE_CALL && actualOpcode != CEE_LDFTN)
1403714038
{
14038-
BADCODE("constrained. has to be followed by callvirt");
14039+
BADCODE("constrained. has to be followed by callvirt, call or ldftn");
1403914040
}
1404014041
}
1404114042

src/coreclr/vm/jitinterface.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5118,7 +5118,7 @@ void CEEInfo::getCallInfo(
51185118
TypeHandle exactType = TypeHandle(pResolvedToken->hClass);
51195119

51205120
TypeHandle constrainedType;
5121-
if ((flags & CORINFO_CALLINFO_CALLVIRT) && (pConstrainedResolvedToken != NULL))
5121+
if (pConstrainedResolvedToken != NULL)
51225122
{
51235123
constrainedType = TypeHandle(pConstrainedResolvedToken->hClass);
51245124
}

src/coreclr/vm/methodtable.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9170,6 +9170,15 @@ MethodDesc *MethodTable::GetDefaultConstructor(BOOL forceBoxedEntryPoint /* = FA
91709170
FALSE /* no allowInstParam */);
91719171
}
91729172

9173+
//==========================================================================================
9174+
// Finds the (non-unboxing) MethodDesc that implements the interface virtual static method pInterfaceMD.
9175+
MethodDesc *
9176+
MethodTable::ResolveVirtualStaticMethod(MethodDesc* pInterfaceMD)
9177+
{
9178+
// TODO
9179+
COMPlusThrow(kTypeLoadException, E_NOTIMPL);
9180+
}
9181+
91739182
//==========================================================================================
91749183
// Finds the (non-unboxing) MethodDesc that implements the interface method pInterfaceMD.
91759184
//
@@ -9189,6 +9198,11 @@ MethodTable::TryResolveConstraintMethodApprox(
91899198
GC_TRIGGERS;
91909199
} CONTRACTL_END;
91919200

9201+
if (pInterfaceMD->IsStatic())
9202+
{
9203+
return ResolveVirtualStaticMethod(pInterfaceMD);
9204+
}
9205+
91929206
// We can't resolve constraint calls effectively for reference types, and there's
91939207
// not a lot of perf. benefit in doing it anyway.
91949208
//

src/coreclr/vm/methodtable.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,9 @@ class MethodTable
15571557

15581558
inline WORD GetNumNonVirtualSlots();
15591559

1560+
inline BOOL HasVirtualStaticMethods() const;
1561+
inline void SetHasVirtualStaticMethods();
1562+
15601563
inline WORD GetNumVirtuals()
15611564
{
15621565
LIMITED_METHOD_DAC_CONTRACT;
@@ -2276,6 +2279,9 @@ class MethodTable
22762279
#endif // FEATURE_COMINTEROP
22772280

22782281

2282+
// Resolve virtual static interface method pInterfaceMD on this type.
2283+
MethodDesc *ResolveVirtualStaticMethod(MethodDesc* pInterfaceMD);
2284+
22792285
// Try a partial resolve of the constraint call, up to generic code sharing.
22802286
//
22812287
// Note that this will not necessarily resolve the call exactly, since we might be compiling
@@ -3655,7 +3661,7 @@ public :
36553661
enum_flag_RequiresDispatchTokenFat = 0x0200,
36563662

36573663
enum_flag_HasCctor = 0x0400,
3658-
// enum_flag_unused = 0x0800,
3664+
enum_flag_HasVirtualStaticMethods = 0x0800,
36593665

36603666
#ifdef FEATURE_64BIT_ALIGNMENT
36613667
enum_flag_RequiresAlign8 = 0x1000, // Type requires 8-byte alignment (only set on platforms that require this and don't get it implicitly)

src/coreclr/vm/methodtable.inl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,20 @@ inline INT32 MethodTable::MethodIterator::GetNumMethods() const
541541
return m_iMethods;
542542
}
543543

544+
//==========================================================================================
545+
inline BOOL MethodTable::HasVirtualStaticMethods() const
546+
{
547+
WRAPPER_NO_CONTRACT;
548+
return GetFlag(enum_flag_HasVirtualStaticMethods);
549+
}
550+
551+
//==========================================================================================
552+
inline void MethodTable::SetHasVirtualStaticMethods()
553+
{
554+
WRAPPER_NO_CONTRACT;
555+
return SetFlag(enum_flag_HasVirtualStaticMethods);
556+
}
557+
544558
//==========================================================================================
545559
// Returns TRUE if it's valid to request data from the current position
546560
inline BOOL MethodTable::MethodIterator::IsValid() const

src/coreclr/vm/methodtablebuilder.cpp

Lines changed: 89 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2945,7 +2945,15 @@ MethodTableBuilder::EnumerateClassMethods()
29452945
}
29462946
if(IsMdStatic(dwMemberAttrs))
29472947
{
2948-
BuildMethodTableThrowException(BFA_VIRTUAL_STATIC_METHOD);
2948+
if (fIsClassInterface)
2949+
{
2950+
bmtProp->fHasVirtualStaticMethods = TRUE;
2951+
}
2952+
else
2953+
{
2954+
// Static virtual methods are only allowed to exist in interfaces
2955+
BuildMethodTableThrowException(BFA_VIRTUAL_STATIC_METHOD);
2956+
}
29492957
}
29502958
if(strMethodName && (0==strcmp(strMethodName, COR_CTOR_METHOD_NAME)))
29512959
{
@@ -5021,14 +5029,16 @@ MethodTableBuilder::ValidateMethods()
50215029

50225030
if (it.IsMethodImpl())
50235031
{
5024-
if (!IsMdVirtual(it.Attrs()))
5025-
{ // Non-virtual methods cannot participate in a methodImpl pair.
5032+
if (!IsMdVirtual(it.Attrs()) && !IsMdStatic(it.Attrs()))
5033+
{
5034+
// Non-virtual methods may only participate in a methodImpl pair when
5035+
// they are static and they implement a virtual static interface method.
50265036
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL, it.Token());
50275037
}
50285038
}
50295039

50305040
// Virtual static methods are not allowed.
5031-
if (IsMdStatic(it.Attrs()) && IsMdVirtual(it.Attrs()))
5041+
if (IsMdStatic(it.Attrs()) && IsMdVirtual(it.Attrs()) && !IsInterface())
50325042
{
50335043
BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL, it.Token());
50345044
}
@@ -5297,8 +5307,8 @@ MethodTableBuilder::PlaceVirtualMethods()
52975307
DeclaredMethodIterator it(*this);
52985308
while (it.Next())
52995309
{
5300-
if (!IsMdVirtual(it.Attrs()))
5301-
{ // Only processing declared virtual methods
5310+
if (!IsMdVirtual(it.Attrs()) || IsMdStatic(it.Attrs()))
5311+
{ // Only processing declared virtual instance methods
53025312
continue;
53035313
}
53045314

@@ -5613,12 +5623,11 @@ MethodTableBuilder::ProcessMethodImpls()
56135623
DeclaredMethodIterator it(*this);
56145624
while (it.Next())
56155625
{
5616-
// Non-virtual methods cannot be classified as methodImpl - we should have thrown an
5617-
// error before reaching this point.
5618-
CONSISTENCY_CHECK(!(!IsMdVirtual(it.Attrs()) && it.IsMethodImpl()));
5619-
5620-
if (!IsMdVirtual(it.Attrs()))
5621-
{ // Only virtual methods can participate in methodImpls
5626+
if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl())
5627+
{
5628+
// Non-virtual methods can only be classified as methodImpl when implementing
5629+
// static virtual methods.
5630+
CONSISTENCY_CHECK(IsMdStatic(it.Attrs()));
56225631
continue;
56235632
}
56245633

@@ -6263,75 +6272,80 @@ MethodTableBuilder::PlaceMethodImpls()
62636272
// Get the declaration part of the method impl. It will either be a token
62646273
// (declaration is on this type) or a method desc.
62656274
bmtMethodHandle hDeclMethod = bmtMethodImpl->GetDeclarationMethod(iEntry);
6266-
if(hDeclMethod.IsMDMethod())
6267-
{
6268-
// The declaration is on the type being built
6269-
bmtMDMethod * pCurDeclMethod = hDeclMethod.AsMDMethod();
6270-
6271-
mdToken mdef = pCurDeclMethod->GetMethodSignature().GetToken();
6272-
if (bmtMethodImpl->IsBody(mdef))
6273-
{ // A method declared on this class cannot be both a decl and an impl
6274-
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MULTIPLEOVERRIDES, mdef);
6275-
}
62766275

6277-
if (IsInterface())
6278-
{
6279-
// Throws
6280-
PlaceInterfaceDeclarationOnInterface(
6281-
hDeclMethod,
6282-
pCurImplMethod,
6283-
slots, // Adds override to the slot and replaced arrays.
6284-
replaced,
6285-
&slotIndex,
6286-
dwMaxSlotSize); // Increments count
6287-
}
6288-
else
6289-
{
6290-
// Throws
6291-
PlaceLocalDeclarationOnClass(
6292-
pCurDeclMethod,
6293-
pCurImplMethod,
6294-
slots, // Adds override to the slot and replaced arrays.
6295-
replaced,
6296-
&slotIndex,
6297-
dwMaxSlotSize); // Increments count
6298-
}
6299-
}
6300-
else
6276+
// Don't place static virtual method overrides in the vtable
6277+
if (!IsMdStatic(hDeclMethod.GetDeclAttrs()))
63016278
{
6302-
bmtRTMethod * pCurDeclMethod = hDeclMethod.AsRTMethod();
6303-
6304-
if (IsInterface())
6305-
{
6306-
// Throws
6307-
PlaceInterfaceDeclarationOnInterface(
6308-
hDeclMethod,
6309-
pCurImplMethod,
6310-
slots, // Adds override to the slot and replaced arrays.
6311-
replaced,
6312-
&slotIndex,
6313-
dwMaxSlotSize); // Increments count
6314-
}
6315-
else
6279+
if(hDeclMethod.IsMDMethod())
63166280
{
6317-
// Do not use pDecl->IsInterface here as that asks the method table and the MT may not yet be set up.
6318-
if (pCurDeclMethod->GetOwningType()->IsInterface())
6281+
// The declaration is on the type being built
6282+
bmtMDMethod * pCurDeclMethod = hDeclMethod.AsMDMethod();
6283+
6284+
mdToken mdef = pCurDeclMethod->GetMethodSignature().GetToken();
6285+
if (bmtMethodImpl->IsBody(mdef))
6286+
{ // A method declared on this class cannot be both a decl and an impl
6287+
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MULTIPLEOVERRIDES, mdef);
6288+
}
6289+
6290+
if (IsInterface())
63196291
{
63206292
// Throws
6321-
PlaceInterfaceDeclarationOnClass(
6322-
pCurDeclMethod,
6323-
pCurImplMethod);
6293+
PlaceInterfaceDeclarationOnInterface(
6294+
hDeclMethod,
6295+
pCurImplMethod,
6296+
slots, // Adds override to the slot and replaced arrays.
6297+
replaced,
6298+
&slotIndex,
6299+
dwMaxSlotSize); // Increments count
63246300
}
63256301
else
63266302
{
63276303
// Throws
6328-
PlaceParentDeclarationOnClass(
6304+
PlaceLocalDeclarationOnClass(
63296305
pCurDeclMethod,
63306306
pCurImplMethod,
6331-
slots,
6307+
slots, // Adds override to the slot and replaced arrays.
6308+
replaced,
6309+
&slotIndex,
6310+
dwMaxSlotSize); // Increments count
6311+
}
6312+
}
6313+
else
6314+
{
6315+
bmtRTMethod * pCurDeclMethod = hDeclMethod.AsRTMethod();
6316+
6317+
if (IsInterface())
6318+
{
6319+
// Throws
6320+
PlaceInterfaceDeclarationOnInterface(
6321+
hDeclMethod,
6322+
pCurImplMethod,
6323+
slots, // Adds override to the slot and replaced arrays.
63326324
replaced,
63336325
&slotIndex,
6334-
dwMaxSlotSize); // Increments count
6326+
dwMaxSlotSize); // Increments count
6327+
}
6328+
else
6329+
{
6330+
// Do not use pDecl->IsInterface here as that asks the method table and the MT may not yet be set up.
6331+
if (pCurDeclMethod->GetOwningType()->IsInterface())
6332+
{
6333+
// Throws
6334+
PlaceInterfaceDeclarationOnClass(
6335+
pCurDeclMethod,
6336+
pCurImplMethod);
6337+
}
6338+
else
6339+
{
6340+
// Throws
6341+
PlaceParentDeclarationOnClass(
6342+
pCurDeclMethod,
6343+
pCurImplMethod,
6344+
slots,
6345+
replaced,
6346+
&slotIndex,
6347+
dwMaxSlotSize); // Increments count
6348+
}
63356349
}
63366350
}
63376351
}
@@ -9783,7 +9797,8 @@ MethodTable * MethodTableBuilder::AllocateNewMT(
97839797
LoaderAllocator *pAllocator,
97849798
BOOL isInterface,
97859799
BOOL fDynamicStatics,
9786-
BOOL fHasGenericsStaticsInfo
9800+
BOOL fHasGenericsStaticsInfo,
9801+
BOOL fHasVirtualStaticMethods
97879802
#ifdef FEATURE_COMINTEROP
97889803
, BOOL fHasDynamicInterfaceMap
97899804
#endif
@@ -9926,6 +9941,10 @@ MethodTable * MethodTableBuilder::AllocateNewMT(
99269941

99279942
// initialize the total number of slots
99289943
pMT->SetNumVirtuals(static_cast<WORD>(dwVirtuals));
9944+
if (fHasVirtualStaticMethods)
9945+
{
9946+
pMT->SetHasVirtualStaticMethods();
9947+
}
99299948

99309949
pMT->SetParentMethodTable(pMTParent);
99319950

@@ -10103,6 +10122,7 @@ MethodTableBuilder::SetupMethodTable2(
1010310122
IsInterface(),
1010410123
bmtProp->fDynamicStatics,
1010510124
bmtProp->fGenericsStatics,
10125+
bmtProp->fHasVirtualStaticMethods,
1010610126
#ifdef FEATURE_COMINTEROP
1010710127
fHasDynamicInterfaceMap,
1010810128
#endif

src/coreclr/vm/methodtablebuilder.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,7 @@ class MethodTableBuilder
13251325
bool fIsEnum;
13261326
bool fNoSanityChecks;
13271327
bool fSparse; // Set to true if a sparse interface is being used.
1328+
bool fHasVirtualStaticMethods; // Set to true if the interface type declares virtual static methods.
13281329

13291330
// Com Interop, ComWrapper classes extend from ComObject
13301331
bool fIsComObjectType; // whether this class is an instance of ComObject class
@@ -1496,7 +1497,7 @@ class MethodTableBuilder
14961497
AddNonVirtualMethod(bmtMDMethod * pMethod)
14971498
{
14981499
INDEBUG(SealVirtualSlotSection());
1499-
CONSISTENCY_CHECK(!IsMdVirtual(pMethod->GetDeclAttrs()));
1500+
CONSISTENCY_CHECK(!IsMdVirtual(pMethod->GetDeclAttrs()) || IsMdStatic(pMethod->GetDeclAttrs()));
15001501
pMethod->SetSlotIndex(pSlotTable->GetSlotCount());
15011502
if (!pSlotTable->AddMethodSlot(bmtMethodSlot(pMethod, pMethod)))
15021503
return false;
@@ -2984,7 +2985,8 @@ class MethodTableBuilder
29842985
LoaderAllocator *pAllocator,
29852986
BOOL isIFace,
29862987
BOOL fDynamicStatics,
2987-
BOOL fHasGenericsStaticsInfo
2988+
BOOL fHasGenericsStaticsInfo,
2989+
BOOL fHasVirtualStaticMethods
29882990
#ifdef FEATURE_COMINTEROP
29892991
, BOOL bHasDynamicInterfaceMap
29902992
#endif

0 commit comments

Comments
 (0)