-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Virtual stub indirect call profiling #116453
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
23f432c
5d20e83
870bc65
7dc72f0
b36537a
a7e6159
7f05b81
984e5db
da1d9d3
ecde0e9
efcf49f
89bda64
7ed3ba9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5977,7 +5977,9 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) | |
| if (call->gtCallType == CT_INDIRECT) | ||
| { | ||
| // pinvoke-calli cookie is a constant, or constant indirection | ||
| assert(call->gtCallCookie == nullptr || call->gtCallCookie->OperIs(GT_CNS_INT, GT_IND)); | ||
| // or a non-tree if this is a managed call. | ||
| assert(call->gtCallCookie == nullptr || call->gtCallCookie->OperIs(GT_CNS_INT, GT_IND) || | ||
| call->IsVirtualStub()); | ||
|
||
|
|
||
| GenTree* indirect = call->gtCallAddr; | ||
|
|
||
|
|
@@ -6725,7 +6727,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) | |
| } | ||
| if (call->gtCallType == CT_INDIRECT) | ||
| { | ||
| if (operand == call->gtCallCookie) | ||
| if ((operand == call->gtCallCookie) && !call->IsVirtualStub()) | ||
| { | ||
| *pUse = &call->gtCallCookie; | ||
| return true; | ||
|
|
@@ -9871,16 +9873,23 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree) | |
| /* Copy the union */ | ||
| if (tree->gtCallType == CT_INDIRECT) | ||
| { | ||
| copy->gtCallCookie = tree->gtCallCookie ? gtCloneExpr(tree->gtCallCookie) : nullptr; | ||
| copy->gtCallAddr = tree->gtCallAddr ? gtCloneExpr(tree->gtCallAddr) : nullptr; | ||
| if (tree->IsVirtualStub()) | ||
| { | ||
| copy->gtCallCookie = tree->gtCallCookie; | ||
| } | ||
| else | ||
| { | ||
| copy->gtCallCookie = tree->gtCallCookie ? gtCloneExpr(tree->gtCallCookie) : nullptr; | ||
| } | ||
| copy->gtCallAddr = tree->gtCallAddr ? gtCloneExpr(tree->gtCallAddr) : nullptr; | ||
| } | ||
| else | ||
| { | ||
| copy->gtCallMethHnd = tree->gtCallMethHnd; | ||
| copy->gtInlineCandidateInfo = tree->gtInlineCandidateInfo; | ||
| copy->gtInlineInfoCount = tree->gtInlineInfoCount; | ||
| } | ||
|
|
||
| copy->gtInlineInfoCount = tree->gtInlineInfoCount; | ||
| copy->gtLateDevirtualizationInfo = tree->gtLateDevirtualizationInfo; | ||
|
|
||
| copy->gtCallType = tree->gtCallType; | ||
|
|
@@ -10613,7 +10622,7 @@ void GenTreeUseEdgeIterator::AdvanceCall() | |
| assert(call->gtCallType == CT_INDIRECT); | ||
|
|
||
| m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_ADDRESS>; | ||
| if (call->gtCallCookie != nullptr) | ||
| if ((call->gtCallCookie != nullptr) && !call->IsVirtualStub()) | ||
| { | ||
| m_edge = &call->gtCallCookie; | ||
| return; | ||
|
|
@@ -10936,6 +10945,11 @@ void Compiler::gtDispNodeName(GenTree* tree) | |
| } | ||
| else if (tree->AsCall()->gtCallType == CT_INDIRECT) | ||
| { | ||
| if (tree->AsCall()->IsVirtual()) | ||
| { | ||
| callType = "CALLV"; | ||
| } | ||
|
|
||
| ctType = " ind"; | ||
| } | ||
| else | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1345,7 +1345,7 @@ InlineContext* InlineStrategy::NewContext(InlineContext* parentContext, Statemen | |
| // ldarg instruction. | ||
| context->m_Location = stmt->GetDebugInfo().GetLocation(); | ||
|
|
||
| assert(call->gtCallType == CT_USER_FUNC); | ||
| // assert(call->gtCallType == CT_USER_FUNC); | ||
|
||
| context->m_Callee = call->gtCallMethHnd; | ||
|
|
||
| #if defined(DEBUG) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1794,7 +1794,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call | |
| // add as a non-standard arg. | ||
| } | ||
| } | ||
| else if (call->gtCallType == CT_INDIRECT && (call->gtCallCookie != nullptr)) | ||
| else if (call->gtCallType == CT_INDIRECT && (call->gtCallCookie != nullptr) && !call->IsVirtualStub()) | ||
| { | ||
| assert(!call->IsUnmanaged()); | ||
|
|
||
|
|
@@ -2102,6 +2102,8 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) | |
| continue; | ||
| } | ||
|
|
||
| assert(!argx->OperIs(GT_NONE)); | ||
|
||
|
|
||
| argx = fgMorphTree(argx); | ||
| *parentArgx = argx; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8629,7 +8629,7 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) | |
| return false; | ||
| } | ||
| } | ||
| else if (!pObjMT->CanCastToInterface(pBaseMT)) | ||
| else if (!pBaseMT->IsSharedByGenericInstantiations() && !pObjMT->CanCastToInterface(pBaseMT)) | ||
| { | ||
| info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CAST; | ||
| return false; | ||
|
|
@@ -8639,32 +8639,57 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) | |
| // safely devirtualize. | ||
| if (info->context != nullptr) | ||
| { | ||
| // If the derived class is a shared class, make sure the | ||
| // owner class is too. | ||
| if (pObjMT->IsSharedByGenericInstantiations()) | ||
| MethodTable* interfaceMT = nullptr; | ||
|
|
||
| if (pObjMT->IsSharedByGenericInstantiations() || pBaseMT->IsSharedByGenericInstantiations()) | ||
| { | ||
| MethodTable* pCanonBaseMT = pBaseMT->GetCanonicalMethodTable(); | ||
|
|
||
| // Check to see if the derived class implements multiple variants of a matching interface. | ||
| // If so, we cannot predict exactly which implementation is in use here. | ||
| MethodTable::InterfaceMapIterator it = pObjMT->IterateInterfaceMap(); | ||
| int canonicallyMatchingInterfacesFound = 0; | ||
| MethodTable* interfaceMT = nullptr; | ||
| while (it.Next()) | ||
| { | ||
| if (it.GetInterface(pObjMT)->GetCanonicalMethodTable() == pCanonBaseMT) | ||
| MethodTable* mt = it.GetInterface(pObjMT); | ||
| if (mt->GetCanonicalMethodTable() == pCanonBaseMT) | ||
| { | ||
| interfaceMT = mt; | ||
| canonicallyMatchingInterfacesFound++; | ||
|
|
||
| if (canonicallyMatchingInterfacesFound > 1) | ||
| { | ||
| // Multiple canonically identical interfaces found when attempting to devirtualize an inexact interface dispatch | ||
| // Multiple canonically identical interfaces found. | ||
| // | ||
| info->detail = CORINFO_DEVIRTUALIZATION_MULTIPLE_IMPL; | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (canonicallyMatchingInterfacesFound == 0) | ||
| { | ||
| // The object doesn't implement the interface... | ||
| // | ||
| info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CAST; | ||
| return false; | ||
| } | ||
|
|
||
| pDevirtMD = pObjMT->GetMethodDescForInterfaceMethod(TypeHandle(pBaseMT), pBaseMD, FALSE /* throwOnConflict */); | ||
| MethodDesc* interfaceMD = interfaceMT->GetParallelMethodDesc(pBaseMD); | ||
|
||
|
|
||
| if (interfaceMD == nullptr) | ||
| { | ||
| info->detail = CORINFO_DEVIRTUALIZATION_FAILED_LOOKUP; | ||
| return false; | ||
| } | ||
|
|
||
| pDevirtMD = pObjMT->GetMethodDescForInterfaceMethod(TypeHandle(interfaceMT), interfaceMD, FALSE /* throwOnConflict */); | ||
| } | ||
| else | ||
| { | ||
| pDevirtMD = pObjMT->GetMethodDescForInterfaceMethod(TypeHandle(pBaseMT), pBaseMD, FALSE /* throwOnConflict */); | ||
| } | ||
| } | ||
| else if (!pBaseMD->HasClassOrMethodInstantiation()) | ||
| { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, the active member of the union is not
gtCallCookiewhencall->IsVirtualStub(), but rather eithergtInlineCandidateInfoorgtHandleHistogramProfileCandidateInfo. If so that makes it undefined behavior to accessgtCallCookiein these cases.I think all the
call->IsVirtualStub()checks need to be first to avoid this UB.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, revised all these.