1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 // ===========================================================================
5 // File: CodeVersion.cpp
7 // ===========================================================================
10 #include "codeversion.h"
12 #ifdef FEATURE_CODE_VERSIONING
13 #include "threadsuspend.h"
14 #include "methoditer.h"
15 #include "../debug/ee/debugger.h"
16 #include "../debug/ee/walker.h"
17 #include "../debug/ee/controller.h"
18 #endif // FEATURE_CODE_VERSIONING
20 #ifndef FEATURE_CODE_VERSIONING
23 // When not using code versioning we've got a minimal implementation of
24 // NativeCodeVersion that simply wraps a MethodDesc* with no additional
25 // versioning information
28 NativeCodeVersion::NativeCodeVersion(const NativeCodeVersion & rhs) : m_pMethod(rhs.m_pMethod) {}
29 NativeCodeVersion::NativeCodeVersion(PTR_MethodDesc pMethod) : m_pMethod(pMethod) {}
30 BOOL NativeCodeVersion::IsNull() const { return m_pMethod == NULL; }
31 PTR_MethodDesc NativeCodeVersion::GetMethodDesc() const { return m_pMethod; }
32 PCODE NativeCodeVersion::GetNativeCode() const { return m_pMethod->GetNativeCode(); }
33 NativeCodeVersionId NativeCodeVersion::GetVersionId() const { return 0; }
34 ReJITID NativeCodeVersion::GetILCodeVersionId() const; { return 0; }
35 ILCodeVersion NativeCodeVersion::GetILCodeVersion() const { return ILCodeVersion(m_pMethod); }
36 #ifndef DACCESS_COMPILE
37 BOOL NativeCodeVersion::SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected) { return m_pMethod->SetNativeCodeInterlocked(pCode, pExpected); }
39 bool NativeCodeVersion::operator==(const NativeCodeVersion & rhs) const { return m_pMethod == rhs.m_pMethod; }
40 bool NativeCodeVersion::operator!=(const NativeCodeVersion & rhs) const { return !operator==(rhs); }
43 #else // FEATURE_CODE_VERSIONING
46 // This HRESULT is only used as a private implementation detail. If it escapes through public APIS
47 // it is a bug. Corerror.xml has a comment in it reserving this value for our use but it doesn't
48 // appear in the public headers.
50 #define CORPROF_E_RUNTIME_SUSPEND_REQUIRED 0x80131381
52 #ifndef DACCESS_COMPILE
53 NativeCodeVersionNode::NativeCodeVersionNode(NativeCodeVersionId id, MethodDesc* pMethodDesc, ReJITID parentId) :
55 m_pMethodDesc(pMethodDesc),
57 m_pNextMethodDescSibling(NULL),
59 m_optTier(NativeCodeVersion::OptimizationTier0),
65 BOOL NativeCodeVersionNode::LockOwnedByCurrentThread() const
67 LIMITED_METHOD_DAC_CONTRACT;
68 return GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread();
72 PTR_MethodDesc NativeCodeVersionNode::GetMethodDesc() const
74 LIMITED_METHOD_DAC_CONTRACT;
78 PCODE NativeCodeVersionNode::GetNativeCode() const
80 LIMITED_METHOD_DAC_CONTRACT;
84 ReJITID NativeCodeVersionNode::GetILVersionId() const
86 LIMITED_METHOD_DAC_CONTRACT;
90 ILCodeVersion NativeCodeVersionNode::GetILCodeVersion() const
92 LIMITED_METHOD_DAC_CONTRACT;
94 if (GetILVersionId() != 0)
96 _ASSERTE(LockOwnedByCurrentThread());
99 PTR_MethodDesc pMD = GetMethodDesc();
100 return pMD->GetCodeVersionManager()->GetILCodeVersion(pMD, GetILVersionId());
103 NativeCodeVersionId NativeCodeVersionNode::GetVersionId() const
105 LIMITED_METHOD_DAC_CONTRACT;
109 #ifndef DACCESS_COMPILE
110 BOOL NativeCodeVersionNode::SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected)
112 LIMITED_METHOD_CONTRACT;
113 return FastInterlockCompareExchangePointer(&m_pNativeCode,
114 (TADDR&)pCode, (TADDR&)pExpected) == (TADDR&)pExpected;
118 BOOL NativeCodeVersionNode::IsActiveChildVersion() const
120 LIMITED_METHOD_DAC_CONTRACT;
121 _ASSERTE(LockOwnedByCurrentThread());
122 return (m_flags & IsActiveChildFlag) != 0;
125 #ifndef DACCESS_COMPILE
126 void NativeCodeVersionNode::SetActiveChildFlag(BOOL isActive)
128 LIMITED_METHOD_CONTRACT;
129 _ASSERTE(LockOwnedByCurrentThread());
132 m_flags |= IsActiveChildFlag;
136 m_flags &= ~IsActiveChildFlag;
142 #ifdef FEATURE_TIERED_COMPILATION
143 NativeCodeVersion::OptimizationTier NativeCodeVersionNode::GetOptimizationTier() const
145 LIMITED_METHOD_DAC_CONTRACT;
146 return m_optTier.Load();
148 #ifndef DACCESS_COMPILE
149 void NativeCodeVersionNode::SetOptimizationTier(NativeCodeVersion::OptimizationTier tier)
151 LIMITED_METHOD_DAC_CONTRACT;
152 m_optTier.Store(tier);
155 #endif // FEATURE_TIERED_COMPILATION
157 NativeCodeVersion::NativeCodeVersion() :
158 m_storageKind(StorageKind::Unknown)
161 NativeCodeVersion::NativeCodeVersion(const NativeCodeVersion & rhs) :
162 m_storageKind(rhs.m_storageKind)
164 if(m_storageKind == StorageKind::Explicit)
166 m_pVersionNode = rhs.m_pVersionNode;
168 else if(m_storageKind == StorageKind::Synthetic)
170 m_synthetic = rhs.m_synthetic;
174 NativeCodeVersion::NativeCodeVersion(PTR_NativeCodeVersionNode pVersionNode) :
175 m_storageKind(pVersionNode != NULL ? StorageKind::Explicit : StorageKind::Unknown),
176 m_pVersionNode(pVersionNode)
179 NativeCodeVersion::NativeCodeVersion(PTR_MethodDesc pMethod) :
180 m_storageKind(pMethod != NULL ? StorageKind::Synthetic : StorageKind::Unknown)
182 LIMITED_METHOD_DAC_CONTRACT;
183 m_synthetic.m_pMethodDesc = pMethod;
186 BOOL NativeCodeVersion::IsNull() const
188 LIMITED_METHOD_DAC_CONTRACT;
189 return m_storageKind == StorageKind::Unknown;
192 BOOL NativeCodeVersion::IsDefaultVersion() const
194 LIMITED_METHOD_DAC_CONTRACT;
195 return m_storageKind == StorageKind::Synthetic;
198 PTR_MethodDesc NativeCodeVersion::GetMethodDesc() const
200 LIMITED_METHOD_DAC_CONTRACT;
201 if (m_storageKind == StorageKind::Explicit)
203 return AsNode()->GetMethodDesc();
207 return m_synthetic.m_pMethodDesc;
211 PCODE NativeCodeVersion::GetNativeCode() const
213 LIMITED_METHOD_DAC_CONTRACT;
214 if (m_storageKind == StorageKind::Explicit)
216 return AsNode()->GetNativeCode();
220 return GetMethodDesc()->GetNativeCode();
224 ReJITID NativeCodeVersion::GetILCodeVersionId() const
226 LIMITED_METHOD_DAC_CONTRACT;
227 if (m_storageKind == StorageKind::Explicit)
229 return AsNode()->GetILVersionId();
237 ILCodeVersion NativeCodeVersion::GetILCodeVersion() const
239 LIMITED_METHOD_DAC_CONTRACT;
240 if (m_storageKind == StorageKind::Explicit)
242 return AsNode()->GetILCodeVersion();
246 PTR_MethodDesc pMethod = GetMethodDesc();
247 return ILCodeVersion(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef());
251 NativeCodeVersionId NativeCodeVersion::GetVersionId() const
253 LIMITED_METHOD_DAC_CONTRACT;
254 if (m_storageKind == StorageKind::Explicit)
256 return AsNode()->GetVersionId();
264 #ifndef DACCESS_COMPILE
265 BOOL NativeCodeVersion::SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected)
267 LIMITED_METHOD_CONTRACT;
268 if (m_storageKind == StorageKind::Explicit)
270 return AsNode()->SetNativeCodeInterlocked(pCode, pExpected);
274 return GetMethodDesc()->SetNativeCodeInterlocked(pCode, pExpected);
279 BOOL NativeCodeVersion::IsActiveChildVersion() const
281 LIMITED_METHOD_DAC_CONTRACT;
282 if (m_storageKind == StorageKind::Explicit)
284 return AsNode()->IsActiveChildVersion();
288 MethodDescVersioningState* pMethodVersioningState = GetMethodDescVersioningState();
289 if (pMethodVersioningState == NULL)
293 return pMethodVersioningState->IsDefaultVersionActiveChild();
297 PTR_MethodDescVersioningState NativeCodeVersion::GetMethodDescVersioningState() const
299 LIMITED_METHOD_DAC_CONTRACT;
300 PTR_MethodDesc pMethodDesc = GetMethodDesc();
301 CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
302 return pCodeVersionManager->GetMethodDescVersioningState(pMethodDesc);
305 #ifndef DACCESS_COMPILE
306 void NativeCodeVersion::SetActiveChildFlag(BOOL isActive)
308 LIMITED_METHOD_DAC_CONTRACT;
309 if (m_storageKind == StorageKind::Explicit)
311 AsNode()->SetActiveChildFlag(isActive);
315 MethodDescVersioningState* pMethodVersioningState = GetMethodDescVersioningState();
316 pMethodVersioningState->SetDefaultVersionActiveChildFlag(isActive);
320 MethodDescVersioningState* NativeCodeVersion::GetMethodDescVersioningState()
322 LIMITED_METHOD_DAC_CONTRACT;
323 MethodDesc* pMethodDesc = GetMethodDesc();
324 CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
325 return pCodeVersionManager->GetMethodDescVersioningState(pMethodDesc);
329 #ifdef FEATURE_TIERED_COMPILATION
330 NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() const
332 LIMITED_METHOD_DAC_CONTRACT;
333 if (m_storageKind == StorageKind::Explicit)
335 return AsNode()->GetOptimizationTier();
339 return NativeCodeVersion::OptimizationTier0;
343 #ifndef DACCESS_COMPILE
344 void NativeCodeVersion::SetOptimizationTier(NativeCodeVersion::OptimizationTier tier)
346 LIMITED_METHOD_CONTRACT;
347 if (m_storageKind == StorageKind::Explicit)
349 AsNode()->SetOptimizationTier(tier);
353 _ASSERTE(!"Do not call SetOptimizationTier on default code versions - these versions are immutable");
359 PTR_NativeCodeVersionNode NativeCodeVersion::AsNode() const
361 LIMITED_METHOD_DAC_CONTRACT;
362 if (m_storageKind == StorageKind::Explicit)
364 return m_pVersionNode;
372 #ifndef DACCESS_COMPILE
373 PTR_NativeCodeVersionNode NativeCodeVersion::AsNode()
375 LIMITED_METHOD_CONTRACT;
376 if (m_storageKind == StorageKind::Explicit)
378 return m_pVersionNode;
387 bool NativeCodeVersion::operator==(const NativeCodeVersion & rhs) const
389 LIMITED_METHOD_DAC_CONTRACT;
390 if (m_storageKind == StorageKind::Explicit)
392 return (rhs.m_storageKind == StorageKind::Explicit) &&
393 (rhs.AsNode() == AsNode());
395 else if (m_storageKind == StorageKind::Synthetic)
397 return (rhs.m_storageKind == StorageKind::Synthetic) &&
398 (m_synthetic.m_pMethodDesc == rhs.m_synthetic.m_pMethodDesc);
402 return rhs.m_storageKind == StorageKind::Unknown;
405 bool NativeCodeVersion::operator!=(const NativeCodeVersion & rhs) const
407 LIMITED_METHOD_DAC_CONTRACT;
408 return !operator==(rhs);
411 NativeCodeVersionCollection::NativeCodeVersionCollection(PTR_MethodDesc pMethodDescFilter, ILCodeVersion ilCodeFilter) :
412 m_pMethodDescFilter(pMethodDescFilter),
413 m_ilCodeFilter(ilCodeFilter)
417 NativeCodeVersionIterator NativeCodeVersionCollection::Begin()
419 LIMITED_METHOD_DAC_CONTRACT;
420 return NativeCodeVersionIterator(this);
422 NativeCodeVersionIterator NativeCodeVersionCollection::End()
424 LIMITED_METHOD_DAC_CONTRACT;
425 return NativeCodeVersionIterator(NULL);
428 NativeCodeVersionIterator::NativeCodeVersionIterator(NativeCodeVersionCollection* pNativeCodeVersionCollection) :
429 m_stage(IterationStage::Initial),
430 m_pCollection(pNativeCodeVersionCollection),
431 m_pLinkedListCur(dac_cast<PTR_NativeCodeVersionNode>(nullptr))
433 LIMITED_METHOD_DAC_CONTRACT;
436 void NativeCodeVersionIterator::First()
438 LIMITED_METHOD_DAC_CONTRACT;
439 if (m_pCollection == NULL)
441 m_stage = IterationStage::End;
445 void NativeCodeVersionIterator::Next()
447 LIMITED_METHOD_DAC_CONTRACT;
448 if (m_stage == IterationStage::Initial)
450 ILCodeVersion ilCodeFilter = m_pCollection->m_ilCodeFilter;
451 m_stage = IterationStage::ImplicitCodeVersion;
452 if (ilCodeFilter.IsNull() || ilCodeFilter.IsDefaultVersion())
454 m_cur = NativeCodeVersion(m_pCollection->m_pMethodDescFilter);
458 if (m_stage == IterationStage::ImplicitCodeVersion)
460 m_stage = IterationStage::LinkedList;
461 CodeVersionManager* pCodeVersionManager = m_pCollection->m_pMethodDescFilter->GetCodeVersionManager();
462 MethodDescVersioningState* pMethodDescVersioningState = pCodeVersionManager->GetMethodDescVersioningState(m_pCollection->m_pMethodDescFilter);
463 if (pMethodDescVersioningState == NULL)
465 m_pLinkedListCur = NULL;
469 ILCodeVersion ilCodeFilter = m_pCollection->m_ilCodeFilter;
470 m_pLinkedListCur = pMethodDescVersioningState->GetFirstVersionNode();
471 while (m_pLinkedListCur != NULL && !ilCodeFilter.IsNull() && ilCodeFilter.GetVersionId() != m_pLinkedListCur->GetILVersionId())
473 m_pLinkedListCur = m_pLinkedListCur->m_pNextMethodDescSibling;
476 if (m_pLinkedListCur != NULL)
478 m_cur = NativeCodeVersion(m_pLinkedListCur);
482 if (m_stage == IterationStage::LinkedList)
484 if (m_pLinkedListCur != NULL)
486 ILCodeVersion ilCodeFilter = m_pCollection->m_ilCodeFilter;
489 m_pLinkedListCur = m_pLinkedListCur->m_pNextMethodDescSibling;
490 } while (m_pLinkedListCur != NULL && !ilCodeFilter.IsNull() && ilCodeFilter.GetVersionId() != m_pLinkedListCur->GetILVersionId());
492 if (m_pLinkedListCur != NULL)
494 m_cur = NativeCodeVersion(m_pLinkedListCur);
499 m_stage = IterationStage::End;
500 m_cur = NativeCodeVersion();
504 const NativeCodeVersion & NativeCodeVersionIterator::Get() const
506 LIMITED_METHOD_DAC_CONTRACT;
509 bool NativeCodeVersionIterator::Equal(const NativeCodeVersionIterator &i) const
511 LIMITED_METHOD_DAC_CONTRACT;
512 return m_cur == i.m_cur;
515 ILCodeVersionNode::ILCodeVersionNode() :
516 m_pModule(dac_cast<PTR_Module>(nullptr)),
519 m_pNextILVersionNode(dac_cast<PTR_ILCodeVersionNode>(nullptr)),
520 m_rejitState(ILCodeVersion::kStateRequested),
524 m_pIL.Store(dac_cast<PTR_COR_ILMETHOD>(nullptr));
527 #ifndef DACCESS_COMPILE
528 ILCodeVersionNode::ILCodeVersionNode(Module* pModule, mdMethodDef methodDef, ReJITID id) :
530 m_methodDef(methodDef),
532 m_pNextILVersionNode(dac_cast<PTR_ILCodeVersionNode>(nullptr)),
533 m_rejitState(ILCodeVersion::kStateRequested),
540 BOOL ILCodeVersionNode::LockOwnedByCurrentThread() const
542 LIMITED_METHOD_DAC_CONTRACT;
543 return GetModule()->GetCodeVersionManager()->LockOwnedByCurrentThread();
547 PTR_Module ILCodeVersionNode::GetModule() const
549 LIMITED_METHOD_DAC_CONTRACT;
553 mdMethodDef ILCodeVersionNode::GetMethodDef() const
555 LIMITED_METHOD_DAC_CONTRACT;
559 ReJITID ILCodeVersionNode::GetVersionId() const
561 LIMITED_METHOD_DAC_CONTRACT;
565 ILCodeVersion::RejitFlags ILCodeVersionNode::GetRejitState() const
567 LIMITED_METHOD_DAC_CONTRACT;
568 return m_rejitState.Load();
571 PTR_COR_ILMETHOD ILCodeVersionNode::GetIL() const
573 LIMITED_METHOD_DAC_CONTRACT;
574 return dac_cast<PTR_COR_ILMETHOD>(m_pIL.Load());
577 DWORD ILCodeVersionNode::GetJitFlags() const
579 LIMITED_METHOD_DAC_CONTRACT;
580 return m_jitFlags.Load();
583 const InstrumentedILOffsetMapping* ILCodeVersionNode::GetInstrumentedILMap() const
585 LIMITED_METHOD_DAC_CONTRACT;
586 _ASSERTE(LockOwnedByCurrentThread());
587 return &m_instrumentedILMap;
590 PTR_ILCodeVersionNode ILCodeVersionNode::GetNextILVersionNode() const
592 LIMITED_METHOD_DAC_CONTRACT;
593 _ASSERTE(LockOwnedByCurrentThread());
594 return m_pNextILVersionNode;
597 #ifndef DACCESS_COMPILE
598 void ILCodeVersionNode::SetRejitState(ILCodeVersion::RejitFlags newState)
600 LIMITED_METHOD_CONTRACT;
601 m_rejitState.Store(newState);
604 void ILCodeVersionNode::SetIL(COR_ILMETHOD* pIL)
606 LIMITED_METHOD_CONTRACT;
610 void ILCodeVersionNode::SetJitFlags(DWORD flags)
612 LIMITED_METHOD_CONTRACT;
613 m_jitFlags.Store(flags);
616 void ILCodeVersionNode::SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap)
618 LIMITED_METHOD_CONTRACT;
619 _ASSERTE(LockOwnedByCurrentThread());
620 m_instrumentedILMap.SetMappingInfo(cMap, rgMap);
623 void ILCodeVersionNode::SetNextILVersionNode(ILCodeVersionNode* pNextILVersionNode)
625 LIMITED_METHOD_CONTRACT;
626 _ASSERTE(LockOwnedByCurrentThread());
627 m_pNextILVersionNode = pNextILVersionNode;
631 ILCodeVersion::ILCodeVersion() :
632 m_storageKind(StorageKind::Unknown)
635 ILCodeVersion::ILCodeVersion(const ILCodeVersion & ilCodeVersion) :
636 m_storageKind(ilCodeVersion.m_storageKind)
638 if(m_storageKind == StorageKind::Explicit)
640 m_pVersionNode = ilCodeVersion.m_pVersionNode;
642 else if(m_storageKind == StorageKind::Synthetic)
644 m_synthetic = ilCodeVersion.m_synthetic;
648 ILCodeVersion::ILCodeVersion(PTR_ILCodeVersionNode pILCodeVersionNode) :
649 m_storageKind(pILCodeVersionNode != NULL ? StorageKind::Explicit : StorageKind::Unknown),
650 m_pVersionNode(pILCodeVersionNode)
653 ILCodeVersion::ILCodeVersion(PTR_Module pModule, mdMethodDef methodDef) :
654 m_storageKind(pModule != NULL ? StorageKind::Synthetic : StorageKind::Unknown)
656 LIMITED_METHOD_DAC_CONTRACT;
657 m_synthetic.m_pModule = pModule;
658 m_synthetic.m_methodDef = methodDef;
661 bool ILCodeVersion::operator==(const ILCodeVersion & rhs) const
663 LIMITED_METHOD_DAC_CONTRACT;
664 if (m_storageKind == StorageKind::Explicit)
666 return (rhs.m_storageKind == StorageKind::Explicit) &&
667 (AsNode() == rhs.AsNode());
669 else if (m_storageKind == StorageKind::Synthetic)
671 return (rhs.m_storageKind == StorageKind::Synthetic) &&
672 (m_synthetic.m_pModule == rhs.m_synthetic.m_pModule) &&
673 (m_synthetic.m_methodDef == rhs.m_synthetic.m_methodDef);
677 return rhs.m_storageKind == StorageKind::Unknown;
681 BOOL ILCodeVersion::IsNull() const
683 LIMITED_METHOD_DAC_CONTRACT;
684 return m_storageKind == StorageKind::Unknown;
687 BOOL ILCodeVersion::IsDefaultVersion() const
689 LIMITED_METHOD_DAC_CONTRACT;
690 return m_storageKind == StorageKind::Synthetic;
693 PTR_Module ILCodeVersion::GetModule() const
695 LIMITED_METHOD_DAC_CONTRACT;
696 if (m_storageKind == StorageKind::Explicit)
698 return AsNode()->GetModule();
702 return m_synthetic.m_pModule;
706 mdMethodDef ILCodeVersion::GetMethodDef() const
708 LIMITED_METHOD_DAC_CONTRACT;
709 if (m_storageKind == StorageKind::Explicit)
711 return AsNode()->GetMethodDef();
715 return m_synthetic.m_methodDef;
719 ReJITID ILCodeVersion::GetVersionId() const
721 LIMITED_METHOD_DAC_CONTRACT;
722 if (m_storageKind == StorageKind::Explicit)
724 return AsNode()->GetVersionId();
732 NativeCodeVersionCollection ILCodeVersion::GetNativeCodeVersions(PTR_MethodDesc pClosedMethodDesc) const
734 LIMITED_METHOD_DAC_CONTRACT;
735 return NativeCodeVersionCollection(pClosedMethodDesc, *this);
738 NativeCodeVersion ILCodeVersion::GetActiveNativeCodeVersion(PTR_MethodDesc pClosedMethodDesc) const
740 LIMITED_METHOD_DAC_CONTRACT;
741 NativeCodeVersionCollection versions = GetNativeCodeVersions(pClosedMethodDesc);
742 for (NativeCodeVersionIterator cur = versions.Begin(), end = versions.End(); cur != end; cur++)
744 if (cur->IsActiveChildVersion())
749 return NativeCodeVersion();
752 ILCodeVersion::RejitFlags ILCodeVersion::GetRejitState() const
754 LIMITED_METHOD_DAC_CONTRACT;
755 if (m_storageKind == StorageKind::Explicit)
757 return AsNode()->GetRejitState();
761 return ILCodeVersion::kStateActive;
765 PTR_COR_ILMETHOD ILCodeVersion::GetIL() const
769 THROWS; //GetILHeader throws
776 PTR_COR_ILMETHOD pIL = NULL;
777 if (m_storageKind == StorageKind::Explicit)
779 pIL = AsNode()->GetIL();
782 // For the default code version we always fetch the globally stored default IL for a method
784 // In the non-default code version we assume NULL is the equivalent of explicitly requesting to
785 // re-use the default IL. Ideally there would be no reason to create a new version that re-uses
786 // the default IL (just use the default code version for that) but we do it here for compat. We've
787 // got some profilers that use ReJIT to create a new code version and then instead of calling
788 // ICorProfilerFunctionControl::SetILFunctionBody they call ICorProfilerInfo::SetILFunctionBody.
789 // This mutates the default IL so that it is now correct for their new code version. Of course this
790 // also overwrote the previous default IL so now the default code version GetIL() is out of sync
791 // with the jitted code. In the majority of cases we never re-read the IL after the initial
792 // jitting so this issue goes unnoticed.
794 // If changing the default IL after it is in use becomes more problematic in the future we would
795 // need to add enforcement that prevents profilers from using ICorProfilerInfo::SetILFunctionBody
796 // that way + coordinate with them because it is a breaking change for any profiler currently doing it.
799 PTR_Module pModule = GetModule();
800 PTR_MethodDesc pMethodDesc = dac_cast<PTR_MethodDesc>(pModule->LookupMethodDef(GetMethodDef()));
801 if (pMethodDesc != NULL)
803 pIL = dac_cast<PTR_COR_ILMETHOD>(pMethodDesc->GetILHeader(TRUE));
810 PTR_COR_ILMETHOD ILCodeVersion::GetILNoThrow() const
812 LIMITED_METHOD_DAC_CONTRACT;
813 PTR_COR_ILMETHOD ret;
822 EX_END_CATCH(RethrowTerminalExceptions);
826 DWORD ILCodeVersion::GetJitFlags() const
828 LIMITED_METHOD_DAC_CONTRACT;
829 if (m_storageKind == StorageKind::Explicit)
831 return AsNode()->GetJitFlags();
839 const InstrumentedILOffsetMapping* ILCodeVersion::GetInstrumentedILMap() const
841 LIMITED_METHOD_DAC_CONTRACT;
842 if (m_storageKind == StorageKind::Explicit)
844 return AsNode()->GetInstrumentedILMap();
852 #ifndef DACCESS_COMPILE
853 void ILCodeVersion::SetRejitState(RejitFlags newState)
855 LIMITED_METHOD_CONTRACT;
856 AsNode()->SetRejitState(newState);
859 void ILCodeVersion::SetIL(COR_ILMETHOD* pIL)
861 LIMITED_METHOD_CONTRACT;
862 AsNode()->SetIL(pIL);
865 void ILCodeVersion::SetJitFlags(DWORD flags)
867 LIMITED_METHOD_CONTRACT;
868 AsNode()->SetJitFlags(flags);
871 void ILCodeVersion::SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap)
873 LIMITED_METHOD_CONTRACT;
874 AsNode()->SetInstrumentedILMap(cMap, rgMap);
877 HRESULT ILCodeVersion::AddNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion)
879 LIMITED_METHOD_CONTRACT;
880 CodeVersionManager* pManager = GetModule()->GetCodeVersionManager();
881 HRESULT hr = pManager->AddNativeCodeVersion(*this, pClosedMethodDesc, pNativeCodeVersion);
884 _ASSERTE(hr == E_OUTOFMEMORY);
890 HRESULT ILCodeVersion::GetOrCreateActiveNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion* pActiveNativeCodeVersion)
892 LIMITED_METHOD_CONTRACT;
894 NativeCodeVersion activeNativeChild = GetActiveNativeCodeVersion(pClosedMethodDesc);
895 if (activeNativeChild.IsNull())
897 if (FAILED(hr = AddNativeCodeVersion(pClosedMethodDesc, &activeNativeChild)))
899 _ASSERTE(hr == E_OUTOFMEMORY);
903 // The first added child should automatically become active
904 _ASSERTE(GetActiveNativeCodeVersion(pClosedMethodDesc) == activeNativeChild);
905 *pActiveNativeCodeVersion = activeNativeChild;
909 HRESULT ILCodeVersion::SetActiveNativeCodeVersion(NativeCodeVersion activeNativeCodeVersion, BOOL fEESuspended)
911 LIMITED_METHOD_CONTRACT;
913 MethodDesc* pMethodDesc = activeNativeCodeVersion.GetMethodDesc();
914 NativeCodeVersion prevActiveVersion = GetActiveNativeCodeVersion(pMethodDesc);
915 if (prevActiveVersion == activeNativeCodeVersion)
917 //nothing to do, this version is already active
921 if (!prevActiveVersion.IsNull())
923 prevActiveVersion.SetActiveChildFlag(FALSE);
925 activeNativeCodeVersion.SetActiveChildFlag(TRUE);
927 // If needed update the published code body for this method
928 CodeVersionManager* pCodeVersionManager = GetModule()->GetCodeVersionManager();
929 if (pCodeVersionManager->GetActiveILCodeVersion(GetModule(), GetMethodDef()) == *this)
931 if (FAILED(hr = pCodeVersionManager->PublishNativeCodeVersion(pMethodDesc, activeNativeCodeVersion, fEESuspended)))
940 ILCodeVersionNode* ILCodeVersion::AsNode()
942 LIMITED_METHOD_CONTRACT;
943 //This is dangerous - NativeCodeVersion coerces non-explicit versions to NULL but ILCodeVersion assumes the caller
944 //will never invoke AsNode() on a non-explicit node. Asserting for now as a minimal fix, but we should revisit this.
945 _ASSERTE(m_storageKind == StorageKind::Explicit);
946 return m_pVersionNode;
948 #endif //DACCESS_COMPILE
950 PTR_ILCodeVersionNode ILCodeVersion::AsNode() const
952 LIMITED_METHOD_DAC_CONTRACT;
953 //This is dangerous - NativeCodeVersion coerces non-explicit versions to NULL but ILCodeVersion assumes the caller
954 //will never invoke AsNode() on a non-explicit node. Asserting for now as a minimal fix, but we should revisit this.
955 _ASSERTE(m_storageKind == StorageKind::Explicit);
956 return m_pVersionNode;
959 ILCodeVersionCollection::ILCodeVersionCollection(PTR_Module pModule, mdMethodDef methodDef) :
961 m_methodDef(methodDef)
964 ILCodeVersionIterator ILCodeVersionCollection::Begin()
966 LIMITED_METHOD_DAC_CONTRACT;
967 return ILCodeVersionIterator(this);
970 ILCodeVersionIterator ILCodeVersionCollection::End()
972 LIMITED_METHOD_DAC_CONTRACT;
973 return ILCodeVersionIterator(NULL);
976 ILCodeVersionIterator::ILCodeVersionIterator(const ILCodeVersionIterator & iter) :
977 m_stage(iter.m_stage),
979 m_pLinkedListCur(iter.m_pLinkedListCur),
980 m_pCollection(iter.m_pCollection)
983 ILCodeVersionIterator::ILCodeVersionIterator(ILCodeVersionCollection* pCollection) :
984 m_stage(pCollection != NULL ? IterationStage::Initial : IterationStage::End),
985 m_pLinkedListCur(dac_cast<PTR_ILCodeVersionNode>(nullptr)),
986 m_pCollection(pCollection)
988 LIMITED_METHOD_DAC_CONTRACT;
992 const ILCodeVersion & ILCodeVersionIterator::Get() const
994 LIMITED_METHOD_DAC_CONTRACT;
998 void ILCodeVersionIterator::First()
1000 LIMITED_METHOD_DAC_CONTRACT;
1004 void ILCodeVersionIterator::Next()
1006 LIMITED_METHOD_DAC_CONTRACT;
1007 if (m_stage == IterationStage::Initial)
1009 m_stage = IterationStage::ImplicitCodeVersion;
1010 m_cur = ILCodeVersion(m_pCollection->m_pModule, m_pCollection->m_methodDef);
1013 if (m_stage == IterationStage::ImplicitCodeVersion)
1015 CodeVersionManager* pCodeVersionManager = m_pCollection->m_pModule->GetCodeVersionManager();
1016 _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread());
1017 PTR_ILCodeVersioningState pILCodeVersioningState = pCodeVersionManager->GetILCodeVersioningState(m_pCollection->m_pModule, m_pCollection->m_methodDef);
1018 if (pILCodeVersioningState != NULL)
1020 m_pLinkedListCur = pILCodeVersioningState->GetFirstVersionNode();
1022 m_stage = IterationStage::LinkedList;
1023 if (m_pLinkedListCur != NULL)
1025 m_cur = ILCodeVersion(m_pLinkedListCur);
1029 if (m_stage == IterationStage::LinkedList)
1031 if (m_pLinkedListCur != NULL)
1033 m_pLinkedListCur = m_pLinkedListCur->GetNextILVersionNode();
1035 if (m_pLinkedListCur != NULL)
1037 m_cur = ILCodeVersion(m_pLinkedListCur);
1042 m_stage = IterationStage::End;
1043 m_cur = ILCodeVersion();
1049 bool ILCodeVersionIterator::Equal(const ILCodeVersionIterator &i) const
1051 LIMITED_METHOD_DAC_CONTRACT;
1052 return m_cur == i.m_cur;
1055 MethodDescVersioningState::MethodDescVersioningState(PTR_MethodDesc pMethodDesc) :
1056 m_pMethodDesc(pMethodDesc),
1057 m_flags(IsDefaultVersionActiveChildFlag),
1059 m_pFirstVersionNode(dac_cast<PTR_NativeCodeVersionNode>(nullptr))
1061 LIMITED_METHOD_DAC_CONTRACT;
1062 #ifdef FEATURE_JUMPSTAMP
1063 ZeroMemory(m_rgSavedCode, JumpStubSize);
1067 PTR_MethodDesc MethodDescVersioningState::GetMethodDesc() const
1069 LIMITED_METHOD_DAC_CONTRACT;
1070 return m_pMethodDesc;
1073 #ifndef DACCESS_COMPILE
1074 NativeCodeVersionId MethodDescVersioningState::AllocateVersionId()
1076 LIMITED_METHOD_CONTRACT;
1081 PTR_NativeCodeVersionNode MethodDescVersioningState::GetFirstVersionNode() const
1083 LIMITED_METHOD_DAC_CONTRACT;
1084 return m_pFirstVersionNode;
1087 #ifdef FEATURE_JUMPSTAMP
1088 MethodDescVersioningState::JumpStampFlags MethodDescVersioningState::GetJumpStampState()
1090 LIMITED_METHOD_DAC_CONTRACT;
1091 return (JumpStampFlags)(m_flags & JumpStampMask);
1094 #ifndef DACCESS_COMPILE
1095 void MethodDescVersioningState::SetJumpStampState(JumpStampFlags newState)
1097 LIMITED_METHOD_CONTRACT;
1098 m_flags = (m_flags & ~JumpStampMask) | (BYTE)newState;
1100 #endif // DACCESS_COMPILE
1102 #ifndef DACCESS_COMPILE
1103 HRESULT MethodDescVersioningState::SyncJumpStamp(NativeCodeVersion nativeCodeVersion, BOOL fEESuspended)
1105 LIMITED_METHOD_CONTRACT;
1107 PCODE pCode = nativeCodeVersion.IsNull() ? NULL : nativeCodeVersion.GetNativeCode();
1108 MethodDesc* pMethod = GetMethodDesc();
1109 _ASSERTE(pMethod->IsVersionable() && pMethod->IsVersionableWithJumpStamp());
1111 if (!pMethod->HasNativeCode())
1113 //we'll set up the jump-stamp when the default native code is created
1117 if (!nativeCodeVersion.IsNull() && nativeCodeVersion.IsDefaultVersion())
1119 return UndoJumpStampNativeCode(fEESuspended);
1123 // We don't have new code ready yet, jumpstamp back to the prestub to let us generate it the next time
1124 // the method is called
1129 return CORPROF_E_RUNTIME_SUSPEND_REQUIRED;
1131 return JumpStampNativeCode();
1133 // We do know the new code body, install the jump stamp now
1136 return UpdateJumpTarget(fEESuspended, pCode);
1140 #endif // DACCESS_COMPILE
1142 //---------------------------------------------------------------------------------------
1144 // Simple, thin abstraction of debugger breakpoint patching. Given an address and a
1145 // previously procured DebuggerControllerPatch governing the code address, this decides
1146 // whether the code address is patched. If so, it returns a pointer to the debugger's
1147 // buffer (of what's "underneath" the int 3 patch); otherwise, it returns the code
1151 // * pbCode - Code address to return if unpatched
1152 // * dbgpatch - DebuggerControllerPatch to test
1155 // Either pbCode or the debugger's patch buffer, as per description above.
1158 // Caller must manually grab (and hold) the ControllerLockHolder and get the
1159 // DebuggerControllerPatch before calling this helper.
1162 // pbCode need not equal the code address governed by dbgpatch, but is always
1163 // "related" (and sometimes really is equal). For example, this helper may be used
1164 // when writing a code byte to an internal rejit buffer (e.g., in preparation for an
1165 // eventual 64-bit interlocked write into the code stream), and thus pbCode would
1166 // point into the internal rejit buffer whereas dbgpatch governs the corresponding
1167 // code byte in the live code stream. This function would then be used to determine
1168 // whether a byte should be written into the internal rejit buffer OR into the
1169 // debugger controller's breakpoint buffer.
1172 LPBYTE FirstCodeByteAddr(LPBYTE pbCode, DebuggerControllerPatch * dbgpatch)
1174 LIMITED_METHOD_CONTRACT;
1176 if (dbgpatch != NULL && dbgpatch->IsActivated())
1178 // Debugger has patched the code, so return the address of the buffer
1179 return LPBYTE(&(dbgpatch->opcode));
1182 // no active patch, just return the direct code address
1188 #ifndef DACCESS_COMPILE
1189 BOOL MethodDescVersioningState::CodeIsSaved()
1191 LIMITED_METHOD_CONTRACT;
1193 for (size_t i = 0; i < sizeof(m_rgSavedCode); i++)
1195 if (m_rgSavedCode[i] != 0)
1200 #endif //DACCESS_COMPILE
1203 //---------------------------------------------------------------------------------------
1205 // Do the actual work of stamping the top of originally-jitted-code with a jmp that goes
1206 // to the prestub. This can be called in one of three ways:
1207 // * Case 1: By RequestReJIT against an already-jitted function, in which case the
1208 // PCODE may be inferred by the MethodDesc, and our caller will have suspended
1209 // the EE for us, OR
1210 // * Case 2: By the prestub worker after jitting the original code of a function
1211 // (i.e., the "pre-rejit" scenario). In this case, the EE is not suspended. But
1212 // that's ok, because the PCODE has not yet been published to the MethodDesc, and
1213 // no thread can be executing inside the originally JITted function yet.
1214 // * Case 3: At type/method restore time for an NGEN'ed assembly. This is also the pre-rejit
1215 // scenario because we are guaranteed to do this before the code in the module
1216 // is executable. EE suspend is not required.
1219 // * pCode - Case 1 (above): will be NULL, and we can infer the PCODE from the
1220 // MethodDesc; Case 2+3 (above, pre-rejit): will be non-NULL, and we'll need to use
1221 // this to find the code to stamp on top of.
1224 // * S_OK: Either we successfully did the jmp-stamp, or a racing thread took care of
1226 // * Else, HRESULT indicating failure.
1229 // The caller will have suspended the EE if necessary (case 1), before this is
1232 #ifndef DACCESS_COMPILE
1233 HRESULT MethodDescVersioningState::JumpStampNativeCode(PCODE pCode /* = NULL */)
1239 // It may seem dangerous to be stamping jumps over code while a GC is going on,
1240 // but we're actually safe. As we assert below, either we're holding the thread
1241 // store lock (and thus preventing a GC) OR we're stamping code that has not yet
1242 // been published (and will thus not be executed by managed therads or examined
1248 PCODE pCodePublished = GetMethodDesc()->GetNativeCode();
1250 _ASSERTE((pCode != NULL) || (pCodePublished != NULL));
1251 _ASSERTE(GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread());
1255 // We'll jump-stamp over pCode, or if pCode is NULL, jump-stamp over the published
1256 // code for this's MethodDesc.
1257 LPBYTE pbCode = (LPBYTE)pCode;
1260 // If caller didn't specify a pCode, just use the one that was published after
1261 // the original JIT. (A specific pCode would be passed in the pre-rejit case,
1262 // to jump-stamp the original code BEFORE the PCODE gets published.)
1263 pbCode = (LPBYTE)pCodePublished;
1265 _ASSERTE(pbCode != NULL);
1267 // The debugging API may also try to write to the very top of this function (though
1268 // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know
1269 // whether we can safely patch the actual code, or instead write to the debugger's
1271 DebuggerController::ControllerLockHolder lockController;
1273 if (GetJumpStampState() == JumpStampToPrestub)
1275 // The method has already been jump stamped so nothing left to do
1276 _ASSERTE(CodeIsSaved());
1280 // Remember what we're stamping our jump on top of, so we can replace it during a
1282 if (GetJumpStampState() == JumpStampNone)
1284 for (int i = 0; i < sizeof(m_rgSavedCode); i++)
1286 m_rgSavedCode[i] = *FirstCodeByteAddr(pbCode + i, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)(pbCode + i)));
1292 AllocMemTracker amt;
1294 // This guy might throw on out-of-memory, so rely on the tracker to clean-up
1295 Precode * pPrecode = Precode::Allocate(PRECODE_STUB, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator(), &amt);
1296 PCODE target = pPrecode->GetEntryPoint();
1298 #if defined(_X86_) || defined(_AMD64_)
1300 // Normal unpatched code never starts with a jump
1301 _ASSERTE(GetJumpStampState() == JumpStampToActiveVersion ||
1302 *FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) != X86_INSTR_JMP_REL32);
1304 INT64 i64OldCode = *(INT64*)pbCode;
1305 INT64 i64NewCode = i64OldCode;
1306 LPBYTE pbNewValue = (LPBYTE)&i64NewCode;
1307 *pbNewValue = X86_INSTR_JMP_REL32;
1308 INT32 UNALIGNED * pOffset = reinterpret_cast<INT32 UNALIGNED *>(&pbNewValue[1]);
1309 // This will throw for out-of-memory, so don't write anything until
1310 // after he succeeds
1311 // This guy will leak/cache/reuse the jumpstub
1312 *pOffset = rel32UsingJumpStub(reinterpret_cast<INT32 UNALIGNED *>(pbCode + 1), target, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator());
1314 // If we have the EE suspended or the code is unpublished there won't be contention on this code
1315 hr = UpdateJumpStampHelper(pbCode, i64OldCode, i64NewCode, FALSE);
1322 // No failure point after this!
1324 amt.SuppressRelease();
1326 #else // _X86_ || _AMD64_
1327 #error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
1328 #endif // _X86_ || _AMD64_
1330 SetJumpStampState(JumpStampToPrestub);
1332 EX_CATCH_HRESULT(hr);
1333 _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY);
1337 _ASSERTE(GetJumpStampState() == JumpStampToPrestub);
1338 _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0
1345 //---------------------------------------------------------------------------------------
1347 // After code has been rejitted, this is called to update the jump-stamp to go from
1348 // pointing to the prestub, to pointing to the newly rejitted code.
1351 // fEESuspended - TRUE if the caller keeps the EE suspended during this call
1352 // pRejittedCode - jitted code for the updated IL this method should execute
1355 // This rejit manager's table crst should be held by the caller
1357 // Returns - S_OK if the jump target is updated
1358 // CORPROF_E_RUNTIME_SUSPEND_REQUIRED if the ee isn't suspended and it
1359 // will need to be in order to do the update safely
1360 HRESULT MethodDescVersioningState::UpdateJumpTarget(BOOL fEESuspended, PCODE pRejittedCode)
1370 MethodDesc * pMD = GetMethodDesc();
1371 _ASSERTE(pMD->GetCodeVersionManager()->LockOwnedByCurrentThread());
1373 // It isn't safe to overwrite the original method prolog with a jmp because threads might
1374 // be at an IP in the middle of the jump stamp already. However converting between different
1375 // jump stamps is OK (when done atomically) because this only changes the jmp target, not
1376 // instruction boundaries.
1377 if (GetJumpStampState() == JumpStampNone && !fEESuspended)
1379 return CORPROF_E_RUNTIME_SUSPEND_REQUIRED;
1382 // Beginning of originally JITted code containing the jmp that we will redirect.
1383 BYTE * pbCode = (BYTE*)pMD->GetNativeCode();
1385 // Remember what we're stamping our jump on top of, so we can replace it during a
1387 if (GetJumpStampState() == JumpStampNone)
1389 for (int i = 0; i < sizeof(m_rgSavedCode); i++)
1391 m_rgSavedCode[i] = *FirstCodeByteAddr(pbCode + i, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)(pbCode + i)));
1395 #if defined(_X86_) || defined(_AMD64_)
1399 DebuggerController::ControllerLockHolder lockController;
1401 // This will throw for out-of-memory, so don't write anything until
1402 // after he succeeds
1403 // This guy will leak/cache/reuse the jumpstub
1407 offset = rel32UsingJumpStub(
1408 reinterpret_cast<INT32 UNALIGNED *>(&pbCode[1]), // base of offset
1409 pRejittedCode, // target of jump
1411 pMD->GetLoaderAllocator());
1413 EX_CATCH_HRESULT(hr);
1414 _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY);
1419 // For validation later, remember what pbCode is right now
1420 INT64 i64OldValue = *(INT64 *)pbCode;
1422 // Assemble the INT64 of the new code bytes to write. Start with what's there now
1423 INT64 i64NewValue = i64OldValue;
1424 LPBYTE pbNewValue = (LPBYTE)&i64NewValue;
1426 // First byte becomes a rel32 jmp instruction (if it wasn't already)
1427 *pbNewValue = X86_INSTR_JMP_REL32;
1428 // Next 4 bytes are the jmp target (offset to jmp stub)
1429 INT32 UNALIGNED * pnOffset = reinterpret_cast<INT32 UNALIGNED *>(&pbNewValue[1]);
1432 hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended);
1433 _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended));
1440 #else // _X86_ || _AMD64_
1441 #error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
1442 #endif // _X86_ || _AMD64_
1445 SetJumpStampState(JumpStampToActiveVersion);
1450 //---------------------------------------------------------------------------------------
1452 // Poke the JITted code to satsify a revert request (or to perform an implicit revert as
1453 // part of a second, third, etc. rejit request). Reinstates the originally JITted code
1454 // that had been jump-stamped over to perform a prior rejit.
1457 // fEESuspended - TRUE if the caller keeps the EE suspended during this call
1461 // S_OK to indicate the revert succeeded,
1462 // CORPROF_E_RUNTIME_SUSPEND_REQUIRED to indicate the jumpstamp hasn't been reverted
1463 // and EE suspension will be needed for success
1464 // other failure HRESULT indicating what went wrong.
1467 // Caller must be holding the owning ReJitManager's table crst.
1469 HRESULT MethodDescVersioningState::UndoJumpStampNativeCode(BOOL fEESuspended)
1479 _ASSERTE(GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread());
1480 if (GetJumpStampState() == JumpStampNone)
1485 _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0
1487 BYTE * pbCode = (BYTE*)GetMethodDesc()->GetNativeCode();
1488 DebuggerController::ControllerLockHolder lockController;
1490 #if defined(_X86_) || defined(_AMD64_)
1491 _ASSERTE(m_rgSavedCode[0] != X86_INSTR_JMP_REL32);
1492 _ASSERTE(*FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) == X86_INSTR_JMP_REL32);
1494 #error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
1495 #endif // _X86_ || _AMD64_
1497 // For the interlocked compare, remember what pbCode is right now
1498 INT64 i64OldValue = *(INT64 *)pbCode;
1499 // Assemble the INT64 of the new code bytes to write. Start with what's there now
1500 INT64 i64NewValue = i64OldValue;
1501 memcpy(LPBYTE(&i64NewValue), m_rgSavedCode, sizeof(m_rgSavedCode));
1502 HRESULT hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended);
1503 _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended));
1507 // Transition state of this ReJitInfo to indicate the MD no longer has any jump stamp
1508 SetJumpStampState(JumpStampNone);
1513 //---------------------------------------------------------------------------------------
1515 // This is called to modify the jump-stamp area, the first ReJitInfo::JumpStubSize bytes
1516 // in the method's code.
1519 // Callers use this method in a variety of circumstances:
1520 // a) when the code is unpublished (fContentionPossible == FALSE)
1521 // b) when the caller has taken the ThreadStoreLock and suspended the EE
1522 // (fContentionPossible == FALSE)
1523 // c) when the code is published, the EE isn't suspended, and the jumpstamp
1524 // area consists of a single 5 byte long jump instruction
1525 // (fContentionPossible == TRUE)
1526 // This method will attempt to alter the jump-stamp even if the caller has not prevented
1527 // contention, but there is no guarantee it will be succesful. When the caller has prevented
1528 // contention, then success is assured. Callers may oportunistically try without
1529 // EE suspension, and then upgrade to EE suspension if the first attempt fails.
1532 // This rejit manager's table crst should be held by the caller or fContentionPossible==FALSE
1533 // The debugger patch table lock should be held by the caller
1536 // pbCode - pointer to the code where the jump stamp is placed
1537 // i64OldValue - the bytes which should currently be at the start of the method code
1538 // i64NewValue - the new bytes which should be written at the start of the method code
1539 // fContentionPossible - See the Notes section above.
1542 // S_OK => the jumpstamp has been succesfully updated.
1543 // CORPROF_E_RUNTIME_SUSPEND_REQUIRED => the jumpstamp remains unchanged (preventing contention will be necessary)
1544 // other failing HR => VirtualProtect failed, the jumpstamp remains unchanged
1546 #ifndef DACCESS_COMPILE
1547 HRESULT MethodDescVersioningState::UpdateJumpStampHelper(BYTE* pbCode, INT64 i64OldValue, INT64 i64NewValue, BOOL fContentionPossible)
1557 MethodDesc * pMD = GetMethodDesc();
1558 _ASSERTE(pMD->GetCodeVersionManager()->LockOwnedByCurrentThread() || !fContentionPossible);
1560 // When ReJIT is enabled, method entrypoints are always at least 8-byte aligned (see
1561 // code:EEJitManager::allocCode), so we can do a single 64-bit interlocked operation
1562 // to update the jump target. However, some code may have gotten compiled before
1563 // the profiler had a chance to enable ReJIT (e.g., NGENd code, or code JITted
1564 // before a profiler attaches). In such cases, we cannot rely on a simple
1565 // interlocked operation, and instead must suspend the runtime to ensure we can
1566 // safely update the jmp instruction.
1568 // This method doesn't verify that the method is actually safe to rejit, we expect
1569 // callers to do that. At the moment NGEN'ed code is safe to rejit even if
1570 // it is unaligned, but code generated before the profiler attaches is not.
1571 if (fContentionPossible && !(IS_ALIGNED(pbCode, sizeof(INT64))))
1573 return CORPROF_E_RUNTIME_SUSPEND_REQUIRED;
1576 // The debugging API may also try to write to this function (though
1577 // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know
1578 // whether we can safely patch the actual code, or instead write to the debugger's
1580 if (fContentionPossible)
1582 for (CORDB_ADDRESS_TYPE* pbProbeAddr = pbCode; pbProbeAddr < pbCode + MethodDescVersioningState::JumpStubSize; pbProbeAddr++)
1584 if (NULL != DebuggerController::GetPatchTable()->GetPatch(pbProbeAddr))
1586 return CORPROF_E_RUNTIME_SUSPEND_REQUIRED;
1591 #if defined(_X86_) || defined(_AMD64_)
1594 if (!ClrVirtualProtect((LPVOID)pbCode, 8, PAGE_EXECUTE_READWRITE, &oldProt))
1596 return HRESULT_FROM_WIN32(GetLastError());
1599 if (fContentionPossible)
1601 INT64 i64InterlockReportedOldValue = FastInterlockCompareExchangeLong((INT64 *)pbCode, i64NewValue, i64OldValue);
1602 // Since changes to these bytes are protected by this rejitmgr's m_crstTable, we
1603 // shouldn't have two writers conflicting.
1604 _ASSERTE(i64InterlockReportedOldValue == i64OldValue);
1608 // In this path the caller ensures:
1609 // a) no thread will execute through the prologue area we are modifying
1610 // b) no thread is stopped in a prologue such that it resumes in the middle of code we are modifying
1611 // c) no thread is doing a debugger patch skip operation in which an unmodified copy of the method's
1612 // code could be executed from a patch skip buffer.
1614 // PERF: we might still want a faster path through here if we aren't debugging that doesn't do
1615 // all the patch checks
1616 for (int i = 0; i < MethodDescVersioningState::JumpStubSize; i++)
1618 *FirstCodeByteAddr(pbCode + i, DebuggerController::GetPatchTable()->GetPatch(pbCode + i)) = ((BYTE*)&i64NewValue)[i];
1622 if (oldProt != PAGE_EXECUTE_READWRITE)
1624 // The CLR codebase in many locations simply ignores failures to restore the page protections
1625 // Its true that it isn't a problem functionally, but it seems a bit sketchy?
1626 // I am following the convention for now.
1627 ClrVirtualProtect((LPVOID)pbCode, 8, oldProt, &oldProt);
1630 FlushInstructionCache(GetCurrentProcess(), pbCode, MethodDescVersioningState::JumpStubSize);
1633 #else // _X86_ || _AMD64_
1634 #error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
1635 #endif // _X86_ || _AMD64_
1638 #endif // FEATURE_JUMPSTAMP
1640 BOOL MethodDescVersioningState::IsDefaultVersionActiveChild() const
1642 LIMITED_METHOD_DAC_CONTRACT;
1643 return (m_flags & IsDefaultVersionActiveChildFlag) != 0;
1645 #ifndef DACCESS_COMPILE
1646 void MethodDescVersioningState::SetDefaultVersionActiveChildFlag(BOOL isActive)
1648 LIMITED_METHOD_CONTRACT;
1651 m_flags |= IsDefaultVersionActiveChildFlag;
1655 m_flags &= ~IsDefaultVersionActiveChildFlag;
1659 void MethodDescVersioningState::LinkNativeCodeVersionNode(NativeCodeVersionNode* pNativeCodeVersionNode)
1661 LIMITED_METHOD_CONTRACT;
1662 pNativeCodeVersionNode->m_pNextMethodDescSibling = m_pFirstVersionNode;
1663 m_pFirstVersionNode = pNativeCodeVersionNode;
1667 ILCodeVersioningState::ILCodeVersioningState(PTR_Module pModule, mdMethodDef methodDef) :
1668 m_activeVersion(ILCodeVersion(pModule,methodDef)),
1669 m_pFirstVersionNode(dac_cast<PTR_ILCodeVersionNode>(nullptr)),
1671 m_methodDef(methodDef)
1675 ILCodeVersioningState::Key::Key() :
1676 m_pModule(dac_cast<PTR_Module>(nullptr)),
1680 ILCodeVersioningState::Key::Key(PTR_Module pModule, mdMethodDef methodDef) :
1682 m_methodDef(methodDef)
1685 size_t ILCodeVersioningState::Key::Hash() const
1687 LIMITED_METHOD_DAC_CONTRACT;
1688 return (size_t)(dac_cast<TADDR>(m_pModule) ^ m_methodDef);
1691 bool ILCodeVersioningState::Key::operator==(const Key & rhs) const
1693 LIMITED_METHOD_DAC_CONTRACT;
1694 return (m_pModule == rhs.m_pModule) && (m_methodDef == rhs.m_methodDef);
1697 ILCodeVersioningState::Key ILCodeVersioningState::GetKey() const
1699 LIMITED_METHOD_DAC_CONTRACT;
1700 return Key(m_pModule, m_methodDef);
1703 ILCodeVersion ILCodeVersioningState::GetActiveVersion() const
1705 LIMITED_METHOD_DAC_CONTRACT;
1706 return m_activeVersion;
1709 PTR_ILCodeVersionNode ILCodeVersioningState::GetFirstVersionNode() const
1711 LIMITED_METHOD_DAC_CONTRACT;
1712 return m_pFirstVersionNode;
1715 #ifndef DACCESS_COMPILE
1716 void ILCodeVersioningState::SetActiveVersion(ILCodeVersion ilActiveCodeVersion)
1718 LIMITED_METHOD_CONTRACT;
1719 m_activeVersion = ilActiveCodeVersion;
1722 void ILCodeVersioningState::LinkILCodeVersionNode(ILCodeVersionNode* pILCodeVersionNode)
1724 LIMITED_METHOD_CONTRACT;
1725 pILCodeVersionNode->SetNextILVersionNode(m_pFirstVersionNode);
1726 m_pFirstVersionNode = pILCodeVersionNode;
1730 CodeVersionManager::CodeVersionManager()
1733 //---------------------------------------------------------------------------------------
1735 // Called from BaseDomain::BaseDomain to do any constructor-time initialization.
1736 // Presently, this takes care of initializing the Crst, choosing the type based on
1737 // whether this ReJitManager belongs to the SharedDomain.
1740 // * fSharedDomain - nonzero iff this ReJitManager belongs to the SharedDomain.
1743 void CodeVersionManager::PreInit(BOOL fSharedDomain)
1754 #ifndef DACCESS_COMPILE
1756 fSharedDomain ? CrstReJITSharedDomainTable : CrstReJITDomainTable,
1757 CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN));
1758 #endif // DACCESS_COMPILE
1761 CodeVersionManager::TableLockHolder::TableLockHolder(CodeVersionManager* pCodeVersionManager) :
1762 CrstHolder(&pCodeVersionManager->m_crstTable)
1765 #ifndef DACCESS_COMPILE
1766 void CodeVersionManager::EnterLock()
1768 m_crstTable.Enter();
1770 void CodeVersionManager::LeaveLock()
1772 m_crstTable.Leave();
1777 BOOL CodeVersionManager::LockOwnedByCurrentThread() const
1779 LIMITED_METHOD_DAC_CONTRACT;
1780 #ifdef DACCESS_COMPILE
1783 return const_cast<CrstExplicitInit &>(m_crstTable).OwnedByCurrentThread();
1788 PTR_ILCodeVersioningState CodeVersionManager::GetILCodeVersioningState(PTR_Module pModule, mdMethodDef methodDef) const
1790 LIMITED_METHOD_DAC_CONTRACT;
1791 ILCodeVersioningState::Key key = ILCodeVersioningState::Key(pModule, methodDef);
1792 return m_ilCodeVersioningStateMap.Lookup(key);
1795 PTR_MethodDescVersioningState CodeVersionManager::GetMethodDescVersioningState(PTR_MethodDesc pClosedMethodDesc) const
1797 LIMITED_METHOD_DAC_CONTRACT;
1798 return m_methodDescVersioningStateMap.Lookup(pClosedMethodDesc);
1801 #ifndef DACCESS_COMPILE
1802 HRESULT CodeVersionManager::GetOrCreateILCodeVersioningState(Module* pModule, mdMethodDef methodDef, ILCodeVersioningState** ppILCodeVersioningState)
1804 LIMITED_METHOD_CONTRACT;
1806 ILCodeVersioningState* pILCodeVersioningState = GetILCodeVersioningState(pModule, methodDef);
1807 if (pILCodeVersioningState == NULL)
1809 pILCodeVersioningState = new (nothrow) ILCodeVersioningState(pModule, methodDef);
1810 if (pILCodeVersioningState == NULL)
1812 return E_OUTOFMEMORY;
1816 // This throws when out of memory, but remains internally
1817 // consistent (without adding the new element)
1818 m_ilCodeVersioningStateMap.Add(pILCodeVersioningState);
1820 EX_CATCH_HRESULT(hr);
1823 delete pILCodeVersioningState;
1827 *ppILCodeVersioningState = pILCodeVersioningState;
1831 HRESULT CodeVersionManager::GetOrCreateMethodDescVersioningState(MethodDesc* pMethod, MethodDescVersioningState** ppMethodVersioningState)
1833 LIMITED_METHOD_CONTRACT;
1835 MethodDescVersioningState* pMethodVersioningState = m_methodDescVersioningStateMap.Lookup(pMethod);
1836 if (pMethodVersioningState == NULL)
1838 pMethodVersioningState = new (nothrow) MethodDescVersioningState(pMethod);
1839 if (pMethodVersioningState == NULL)
1841 return E_OUTOFMEMORY;
1845 // This throws when out of memory, but remains internally
1846 // consistent (without adding the new element)
1847 m_methodDescVersioningStateMap.Add(pMethodVersioningState);
1849 EX_CATCH_HRESULT(hr);
1852 delete pMethodVersioningState;
1856 *ppMethodVersioningState = pMethodVersioningState;
1859 #endif // DACCESS_COMPILE
1861 DWORD CodeVersionManager::GetNonDefaultILVersionCount()
1863 LIMITED_METHOD_DAC_CONTRACT;
1865 //This function is legal to call WITHOUT taking the lock
1866 //It is used to do a quick check if work might be needed without paying the overhead
1867 //of acquiring the lock and doing dictionary lookups
1868 return m_ilCodeVersioningStateMap.GetCount();
1871 ILCodeVersionCollection CodeVersionManager::GetILCodeVersions(PTR_MethodDesc pMethod)
1873 LIMITED_METHOD_DAC_CONTRACT;
1874 _ASSERTE(LockOwnedByCurrentThread());
1875 return GetILCodeVersions(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef());
1878 ILCodeVersionCollection CodeVersionManager::GetILCodeVersions(PTR_Module pModule, mdMethodDef methodDef)
1880 LIMITED_METHOD_DAC_CONTRACT;
1881 _ASSERTE(LockOwnedByCurrentThread());
1882 return ILCodeVersionCollection(pModule, methodDef);
1885 ILCodeVersion CodeVersionManager::GetActiveILCodeVersion(PTR_MethodDesc pMethod)
1887 LIMITED_METHOD_DAC_CONTRACT;
1888 _ASSERTE(LockOwnedByCurrentThread());
1889 return GetActiveILCodeVersion(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef());
1892 ILCodeVersion CodeVersionManager::GetActiveILCodeVersion(PTR_Module pModule, mdMethodDef methodDef)
1894 LIMITED_METHOD_DAC_CONTRACT;
1895 _ASSERTE(LockOwnedByCurrentThread());
1896 ILCodeVersioningState* pILCodeVersioningState = GetILCodeVersioningState(pModule, methodDef);
1897 if (pILCodeVersioningState == NULL)
1899 return ILCodeVersion(pModule, methodDef);
1903 return pILCodeVersioningState->GetActiveVersion();
1907 ILCodeVersion CodeVersionManager::GetILCodeVersion(PTR_MethodDesc pMethod, ReJITID rejitId)
1909 LIMITED_METHOD_DAC_CONTRACT;
1910 _ASSERTE(LockOwnedByCurrentThread());
1912 #ifdef FEATURE_REJIT
1913 ILCodeVersionCollection collection = GetILCodeVersions(pMethod);
1914 for (ILCodeVersionIterator cur = collection.Begin(), end = collection.End(); cur != end; cur++)
1916 if (cur->GetVersionId() == rejitId)
1921 return ILCodeVersion();
1922 #else // FEATURE_REJIT
1923 _ASSERTE(rejitId == 0);
1924 return ILCodeVersion(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef());
1925 #endif // FEATURE_REJIT
1928 NativeCodeVersionCollection CodeVersionManager::GetNativeCodeVersions(PTR_MethodDesc pMethod) const
1930 LIMITED_METHOD_DAC_CONTRACT;
1931 _ASSERTE(LockOwnedByCurrentThread());
1932 return NativeCodeVersionCollection(pMethod, ILCodeVersion());
1935 NativeCodeVersion CodeVersionManager::GetNativeCodeVersion(PTR_MethodDesc pMethod, PCODE codeStartAddress) const
1937 LIMITED_METHOD_DAC_CONTRACT;
1938 _ASSERTE(LockOwnedByCurrentThread());
1940 NativeCodeVersionCollection nativeCodeVersions = GetNativeCodeVersions(pMethod);
1941 for (NativeCodeVersionIterator cur = nativeCodeVersions.Begin(), end = nativeCodeVersions.End(); cur != end; cur++)
1943 if (cur->GetNativeCode() == codeStartAddress)
1948 return NativeCodeVersion();
1951 #ifndef DACCESS_COMPILE
1952 HRESULT CodeVersionManager::AddILCodeVersion(Module* pModule, mdMethodDef methodDef, ReJITID rejitId, ILCodeVersion* pILCodeVersion)
1954 LIMITED_METHOD_CONTRACT;
1955 _ASSERTE(LockOwnedByCurrentThread());
1957 ILCodeVersioningState* pILCodeVersioningState;
1958 HRESULT hr = GetOrCreateILCodeVersioningState(pModule, methodDef, &pILCodeVersioningState);
1961 _ASSERTE(hr == E_OUTOFMEMORY);
1965 ILCodeVersionNode* pILCodeVersionNode = new (nothrow) ILCodeVersionNode(pModule, methodDef, rejitId);
1966 if (pILCodeVersionNode == NULL)
1968 return E_OUTOFMEMORY;
1970 pILCodeVersioningState->LinkILCodeVersionNode(pILCodeVersionNode);
1971 *pILCodeVersion = ILCodeVersion(pILCodeVersionNode);
1975 HRESULT CodeVersionManager::SetActiveILCodeVersions(ILCodeVersion* pActiveVersions, DWORD cActiveVersions, BOOL fEESuspended, CDynArray<CodePublishError> * pErrors)
1977 // If the IL version is in the shared domain we need to iterate all domains
1978 // looking for instantiations. The domain iterator lock is bigger than
1979 // the code version manager lock so we can't do this atomically. In one atomic
1980 // update the bookkeeping for IL versioning will happen and then in a second
1981 // update the active native code versions will change/code jumpstamps+precodes
1984 // Note: For all domains other than the shared AppDomain we could do this
1985 // atomically, but for now we use the lowest common denominator for all
1993 PRECONDITION(CheckPointer(pActiveVersions));
1994 PRECONDITION(CheckPointer(pErrors, NULL_OK));
1997 _ASSERTE(!LockOwnedByCurrentThread());
2001 for (DWORD i = 0; i < cActiveVersions; i++)
2003 ILCodeVersion activeVersion = pActiveVersions[i];
2004 if (activeVersion.IsNull())
2006 _ASSERTE(!"The active IL version can't be NULL");
2011 // step 1 - mark the IL versions as being active, this ensures that
2012 // any new method instantiations added after this point will bind to
2013 // the correct version
2015 TableLockHolder(this);
2016 for (DWORD i = 0; i < cActiveVersions; i++)
2018 ILCodeVersion activeVersion = pActiveVersions[i];
2019 ILCodeVersioningState* pILCodeVersioningState = NULL;
2020 if (FAILED(hr = GetOrCreateILCodeVersioningState(activeVersion.GetModule(), activeVersion.GetMethodDef(), &pILCodeVersioningState)))
2022 _ASSERTE(hr == E_OUTOFMEMORY);
2025 pILCodeVersioningState->SetActiveVersion(activeVersion);
2029 // step 2 - determine the set of pre-existing method instantiations
2031 // a parallel array to activeVersions
2032 // for each ILCodeVersion in activeVersions, this lists the set
2033 // MethodDescs that will need to be updated
2034 CDynArray<CDynArray<MethodDesc*>> methodDescsToUpdate;
2035 CDynArray<CodePublishError> errorRecords;
2036 for (DWORD i = 0; i < cActiveVersions; i++)
2038 CDynArray<MethodDesc*>* pMethodDescs = methodDescsToUpdate.Append();
2039 if (pMethodDescs == NULL)
2041 return E_OUTOFMEMORY;
2043 *pMethodDescs = CDynArray<MethodDesc*>();
2045 MethodDesc* pLoadedMethodDesc = pActiveVersions[i].GetModule()->LookupMethodDef(pActiveVersions[i].GetMethodDef());
2046 if (FAILED(hr = CodeVersionManager::EnumerateClosedMethodDescs(pLoadedMethodDesc, pMethodDescs, &errorRecords)))
2048 _ASSERTE(hr == E_OUTOFMEMORY);
2053 // step 3 - update each pre-existing method instantiation
2055 TableLockHolder lock(this);
2056 for (DWORD i = 0; i < cActiveVersions; i++)
2058 // Its possible the active IL version has changed if
2059 // another caller made an update while this method wasn't
2060 // holding the lock. We will ensure that we synchronize
2061 // publishing to whatever version is currently active, even
2062 // if that isn't the IL version we set above.
2064 // Note: Although we attempt to handle this case gracefully
2065 // it isn't recommended for callers to do this. Racing two calls
2066 // that set the IL version to different results means it will be
2067 // completely arbitrary which version wins.
2068 ILCodeVersion requestedActiveILVersion = pActiveVersions[i];
2069 ILCodeVersion activeILVersion = GetActiveILCodeVersion(requestedActiveILVersion.GetModule(), requestedActiveILVersion.GetMethodDef());
2071 CDynArray<MethodDesc*> methodDescs = methodDescsToUpdate[i];
2072 for (int j = 0; j < methodDescs.Count(); j++)
2074 // Get an the active child code version for this method instantiation (it might be NULL, that is OK)
2075 NativeCodeVersion activeNativeChild = activeILVersion.GetActiveNativeCodeVersion(methodDescs[j]);
2077 // Publish that child version, because it is the active native child of the active IL version
2078 // Failing to publish is non-fatal, but we do record it so the caller is aware
2079 if (FAILED(hr = PublishNativeCodeVersion(methodDescs[j], activeNativeChild, fEESuspended)))
2081 if (FAILED(hr = AddCodePublishError(activeILVersion.GetModule(), activeILVersion.GetMethodDef(), methodDescs[j], hr, &errorRecords)))
2083 _ASSERTE(hr == E_OUTOFMEMORY);
2094 HRESULT CodeVersionManager::AddNativeCodeVersion(ILCodeVersion ilCodeVersion, MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion)
2096 LIMITED_METHOD_CONTRACT;
2097 _ASSERTE(LockOwnedByCurrentThread());
2099 MethodDescVersioningState* pMethodVersioningState;
2100 HRESULT hr = GetOrCreateMethodDescVersioningState(pClosedMethodDesc, &pMethodVersioningState);
2103 _ASSERTE(hr == E_OUTOFMEMORY);
2107 NativeCodeVersionId newId = pMethodVersioningState->AllocateVersionId();
2108 NativeCodeVersionNode* pNativeCodeVersionNode = new (nothrow) NativeCodeVersionNode(newId, pClosedMethodDesc, ilCodeVersion.GetVersionId());
2109 if (pNativeCodeVersionNode == NULL)
2111 return E_OUTOFMEMORY;
2114 pMethodVersioningState->LinkNativeCodeVersionNode(pNativeCodeVersionNode);
2116 // the first child added is automatically considered the active one.
2117 if (ilCodeVersion.GetActiveNativeCodeVersion(pClosedMethodDesc).IsNull())
2119 pNativeCodeVersionNode->SetActiveChildFlag(TRUE);
2120 _ASSERTE(!ilCodeVersion.GetActiveNativeCodeVersion(pClosedMethodDesc).IsNull());
2122 // the new child shouldn't have any native code. If it did we might need to
2123 // publish that code as part of adding the node which would require callers
2124 // to pay attention to GC suspension and we'd need to report publishing errors
2126 _ASSERTE(pNativeCodeVersionNode->GetNativeCode() == NULL);
2128 *pNativeCodeVersion = NativeCodeVersion(pNativeCodeVersionNode);
2132 PCODE CodeVersionManager::PublishVersionableCodeIfNecessary(MethodDesc* pMethodDesc, BOOL fCanBackpatchPrestub)
2134 STANDARD_VM_CONTRACT;
2135 _ASSERTE(!LockOwnedByCurrentThread());
2136 _ASSERTE(pMethodDesc->IsVersionable());
2137 _ASSERTE(!pMethodDesc->IsPointingToPrestub() || !pMethodDesc->IsVersionableWithJumpStamp());
2141 BOOL fIsJumpStampMethod = pMethodDesc->IsVersionableWithJumpStamp();
2143 NativeCodeVersion activeVersion;
2145 TableLockHolder lock(this);
2146 if (FAILED(hr = GetActiveILCodeVersion(pMethodDesc).GetOrCreateActiveNativeCodeVersion(pMethodDesc, &activeVersion)))
2148 _ASSERTE(hr == E_OUTOFMEMORY);
2149 ReportCodePublishError(pMethodDesc->GetModule(), pMethodDesc->GetMemberDef(), pMethodDesc, hr);
2154 BOOL fEESuspend = FALSE;
2157 // compile the code if needed
2158 pCode = activeVersion.GetNativeCode();
2161 pCode = pMethodDesc->PrepareCode(activeVersion);
2164 // suspend in preparation for publishing if needed
2167 ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT);
2171 TableLockHolder lock(this);
2172 // The common case is that newActiveCode == activeCode, however we did leave the lock so there is
2173 // possibility that the active version has changed. If it has we need to restart the compilation
2174 // and publishing process with the new active version instead.
2176 // In theory it should be legitimate to break out of this loop and run the less recent active version,
2177 // because ultimately this is a race between one thread that is updating the version and another thread
2178 // trying to run the current version. However for back-compat with ReJIT we need to guarantee that
2179 // a versioning update at least as late as the profiler JitCompilationFinished callback wins the race.
2180 NativeCodeVersion newActiveVersion;
2181 if (FAILED(hr = GetActiveILCodeVersion(pMethodDesc).GetOrCreateActiveNativeCodeVersion(pMethodDesc, &newActiveVersion)))
2183 _ASSERTE(hr == E_OUTOFMEMORY);
2184 ReportCodePublishError(pMethodDesc->GetModule(), pMethodDesc->GetMemberDef(), pMethodDesc, hr);
2188 if (newActiveVersion != activeVersion)
2190 activeVersion = newActiveVersion;
2194 // if we aren't allowed to backpatch we are done
2195 if (!fCanBackpatchPrestub)
2200 // attempt to publish the active version still under the lock
2201 if (FAILED(hr = PublishNativeCodeVersion(pMethodDesc, activeVersion, fEESuspend)))
2203 // If we need an EESuspend to publish then start over. We have to leave the lock in order to suspend,
2204 // and when we leave the lock the active version might change again. However now we know that suspend is
2206 if (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED)
2208 _ASSERTE(!fEESuspend);
2210 continue; // skip RestartEE() below since SuspendEE() has not been called yet
2214 ReportCodePublishError(pMethodDesc->GetModule(), pMethodDesc->GetMemberDef(), pMethodDesc, hr);
2229 ThreadSuspend::RestartEE(FALSE, TRUE);
2233 // if the EE is still suspended from breaking in the middle of the loop, resume it
2236 ThreadSuspend::RestartEE(FALSE, TRUE);
2241 HRESULT CodeVersionManager::PublishNativeCodeVersion(MethodDesc* pMethod, NativeCodeVersion nativeCodeVersion, BOOL fEESuspended)
2243 // TODO: This function needs to make sure it does not change the precode's target if call counting is in progress. Track
2244 // whether call counting is currently being done for the method, and use a lock to ensure the expected precode target.
2245 LIMITED_METHOD_CONTRACT;
2246 _ASSERTE(LockOwnedByCurrentThread());
2247 _ASSERTE(pMethod->IsVersionable());
2249 PCODE pCode = nativeCodeVersion.IsNull() ? NULL : nativeCodeVersion.GetNativeCode();
2250 if (pMethod->IsVersionableWithPrecode())
2252 Precode* pPrecode = pMethod->GetOrCreatePrecode();
2259 EX_CATCH_HRESULT(hr);
2266 pPrecode->SetTargetInterlocked(pCode, FALSE);
2268 // SetTargetInterlocked() would return false if it lost the race with another thread. That is fine, this thread
2269 // can continue assuming it was successful, similarly to it successfully updating the target and another thread
2270 // updating the target again shortly afterwards.
2273 EX_CATCH_HRESULT(hr);
2279 #ifndef FEATURE_JUMPSTAMP
2280 _ASSERTE(!"This platform doesn't support JumpStamp but this method doesn't version with Precode,"
2281 " this method can't be updated");
2284 MethodDescVersioningState* pVersioningState;
2285 if (FAILED(hr = GetOrCreateMethodDescVersioningState(pMethod, &pVersioningState)))
2287 _ASSERTE(hr == E_OUTOFMEMORY);
2290 return pVersioningState->SyncJumpStamp(nativeCodeVersion, fEESuspended);
2296 HRESULT CodeVersionManager::EnumerateClosedMethodDescs(
2298 CDynArray<MethodDesc*> * pClosedMethodDescs,
2299 CDynArray<CodePublishError> * pUnsupportedMethodErrors)
2307 PRECONDITION(CheckPointer(pMD, NULL_OK));
2308 PRECONDITION(CheckPointer(pClosedMethodDescs));
2309 PRECONDITION(CheckPointer(pUnsupportedMethodErrors));
2315 // nothing is loaded yet so we're done for this method.
2319 if (!pMD->HasClassOrMethodInstantiation())
2321 // We have a JITted non-generic.
2322 MethodDesc ** ppMD = pClosedMethodDescs->Append();
2325 return E_OUTOFMEMORY;
2330 if (!pMD->HasClassOrMethodInstantiation())
2332 // not generic, we're done for this method
2336 // Ok, now the case of a generic function (or function on generic class), which
2337 // is loaded, and may thus have compiled instantiations.
2338 // It's impossible to get to any other kind of domain from the profiling API
2339 Module* pModule = pMD->GetModule();
2340 mdMethodDef methodDef = pMD->GetMemberDef();
2341 BaseDomain * pBaseDomainFromModule = pModule->GetDomain();
2342 _ASSERTE(pBaseDomainFromModule->IsAppDomain() ||
2343 pBaseDomainFromModule->IsSharedDomain());
2345 if (pBaseDomainFromModule->IsSharedDomain())
2347 // Iterate through all modules loaded into the shared domain, to
2348 // find all instantiations living in the shared domain. This will
2349 // include orphaned code (i.e., shared code used by ADs that have
2350 // all unloaded), which is good, because orphaned code could get
2351 // re-adopted if a new AD is created that can use that shared code
2352 hr = EnumerateDomainClosedMethodDescs(
2353 NULL, // NULL means to search SharedDomain instead of an AD
2357 pUnsupportedMethodErrors);
2361 // Module is unshared, so just use the module's domain to find instantiations.
2362 hr = EnumerateDomainClosedMethodDescs(
2363 pBaseDomainFromModule->AsAppDomain(),
2367 pUnsupportedMethodErrors);
2371 _ASSERTE(hr == E_OUTOFMEMORY);
2375 // We want to iterate through all compilations of existing instantiations to
2376 // ensure they get marked for rejit. Note: There may be zero instantiations,
2377 // but we won't know until we try.
2378 if (pBaseDomainFromModule->IsSharedDomain())
2380 // Iterate through all real domains, to find shared instantiations.
2381 AppDomainIterator appDomainIterator(TRUE);
2382 while (appDomainIterator.Next())
2384 AppDomain * pAppDomain = appDomainIterator.GetDomain();
2385 if (pAppDomain->IsUnloading())
2389 hr = EnumerateDomainClosedMethodDescs(
2394 pUnsupportedMethodErrors);
2397 _ASSERTE(hr == E_OUTOFMEMORY);
2406 HRESULT CodeVersionManager::EnumerateDomainClosedMethodDescs(
2407 AppDomain * pAppDomainToSearch,
2408 Module* pModuleContainingMethodDef,
2409 mdMethodDef methodDef,
2410 CDynArray<MethodDesc*> * pClosedMethodDescs,
2411 CDynArray<CodePublishError> * pUnsupportedMethodErrors)
2419 PRECONDITION(CheckPointer(pAppDomainToSearch, NULL_OK));
2420 PRECONDITION(CheckPointer(pModuleContainingMethodDef));
2421 PRECONDITION(CheckPointer(pClosedMethodDescs));
2422 PRECONDITION(CheckPointer(pUnsupportedMethodErrors));
2426 _ASSERTE(methodDef != mdTokenNil);
2430 BaseDomain * pDomainContainingGenericDefinition = pModuleContainingMethodDef->GetDomain();
2433 // If the generic definition is not loaded domain-neutral, then all its
2434 // instantiations will also be non-domain-neutral and loaded into the same
2435 // domain as the generic definition. So the caller may only pass the
2436 // domain containing the generic definition as pAppDomainToSearch
2437 if (!pDomainContainingGenericDefinition->IsSharedDomain())
2439 _ASSERTE(pDomainContainingGenericDefinition == pAppDomainToSearch);
2443 // If pAppDomainToSearch is NULL, iterate through all existing
2444 // instantiations loaded into the SharedDomain. If pAppDomainToSearch is non-NULL,
2445 // iterate through all existing instantiations in pAppDomainToSearch, and only consider
2446 // instantiations in non-domain-neutral assemblies (as we already covered domain
2447 // neutral assemblies when we searched the SharedDomain).
2448 LoadedMethodDescIterator::AssemblyIterationMode mode = LoadedMethodDescIterator::kModeSharedDomainAssemblies;
2449 // these are the default flags which won't actually be used in shared mode other than
2450 // asserting they were specified with their default values
2451 AssemblyIterationFlags assemFlags = (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution);
2452 ModuleIterationOption moduleFlags = (ModuleIterationOption)kModIterIncludeLoaded;
2453 if (pAppDomainToSearch != NULL)
2455 mode = LoadedMethodDescIterator::kModeUnsharedADAssemblies;
2456 assemFlags = (AssemblyIterationFlags)(kIncludeAvailableToProfilers | kIncludeExecution);
2457 moduleFlags = (ModuleIterationOption)kModIterIncludeAvailableToProfilers;
2459 LoadedMethodDescIterator it(
2461 pModuleContainingMethodDef,
2466 CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
2467 while (it.Next(pDomainAssembly.This()))
2469 MethodDesc * pLoadedMD = it.Current();
2471 if (!pLoadedMD->IsVersionable())
2473 // For compatibility with the rejit APIs we ensure certain errors are detected and reported using their
2474 // original HRESULTS
2475 HRESULT errorHR = GetNonVersionableError(pLoadedMD);
2476 if (FAILED(errorHR))
2478 if (FAILED(hr = CodeVersionManager::AddCodePublishError(pModuleContainingMethodDef, methodDef, pLoadedMD, CORPROF_E_FUNCTION_IS_COLLECTIBLE, pUnsupportedMethodErrors)))
2480 _ASSERTE(hr == E_OUTOFMEMORY);
2488 if (!pDomainContainingGenericDefinition->IsSharedDomain())
2490 // Method is defined outside of the shared domain, so its instantiation must
2491 // be defined in the AD we're iterating over (pAppDomainToSearch, which, as
2492 // asserted above, must be the same domain as the generic's definition)
2493 _ASSERTE(pLoadedMD->GetDomain() == pAppDomainToSearch);
2497 MethodDesc ** ppMD = pClosedMethodDescs->Append();
2500 return E_OUTOFMEMORY;
2506 #endif // DACCESS_COMPILE
2509 //---------------------------------------------------------------------------------------
2511 // Given the default version code for a MethodDesc that is about to published, add
2512 // a jumpstamp pointing back to the prestub if the currently active version isn't
2513 // the default one. This called from the PublishMethodHolder.
2516 // * pMD - MethodDesc to jmp-stamp
2517 // * pCode - Top of the code that was just jitted (using original IL).
2521 // * S_OK: Either we successfully did the jmp-stamp, or we didn't have to
2522 // * Else, HRESULT indicating failure.
2525 // The caller has not yet published pCode to the MethodDesc, so no threads can be
2526 // executing inside pMD's code yet. Thus, we don't need to suspend the runtime while
2527 // applying the jump-stamp like we usually do for rejit requests that are made after
2528 // a function has been JITted.
2530 #ifndef DACCESS_COMPILE
2531 HRESULT CodeVersionManager::DoJumpStampIfNecessary(MethodDesc* pMD, PCODE pCode)
2539 PRECONDITION(CheckPointer(pMD));
2540 PRECONDITION(pCode != NULL);
2544 _ASSERTE(LockOwnedByCurrentThread());
2546 NativeCodeVersion activeCodeVersion = GetActiveILCodeVersion(pMD).GetActiveNativeCodeVersion(pMD);
2547 if (activeCodeVersion.IsDefaultVersion())
2549 //Method not requested to be rejitted, nothing to do
2553 if (!(pMD->IsVersionable() && pMD->IsVersionableWithJumpStamp()))
2555 return GetNonVersionableError(pMD);
2558 #ifndef FEATURE_JUMPSTAMP
2559 _ASSERTE(!"How did we get here? IsVersionableWithJumpStamp() should have been FALSE above");
2563 MethodDescVersioningState* pVersioningState;
2564 if (FAILED(hr = GetOrCreateMethodDescVersioningState(pMD, &pVersioningState)))
2566 _ASSERTE(hr == E_OUTOFMEMORY);
2569 if (pVersioningState->GetJumpStampState() != MethodDescVersioningState::JumpStampNone)
2571 //JumpStamp already in place
2574 return pVersioningState->JumpStampNativeCode(pCode);
2575 #endif // FEATURE_JUMPSTAMP
2578 #endif // DACCESS_COMPILE
2580 #ifndef DACCESS_COMPILE
2582 void CodeVersionManager::OnAppDomainExit(AppDomain * pAppDomain)
2584 LIMITED_METHOD_CONTRACT;
2585 // This would clean up all the allocations we have done and synchronize with any threads that might
2586 // still be using the data
2587 _ASSERTE(!".Net Core shouldn't be doing app domain shutdown - if we start doing so this needs to be implemented");
2591 //---------------------------------------------------------------------------------------
2593 // Small helper to determine whether a given (possibly instantiated generic) MethodDesc
2594 // is safe to rejit.
2597 // pMD - MethodDesc to test
2599 // S_OK iff pMD is safe to rejit
2600 // CORPROF_E_FUNCTION_IS_COLLECTIBLE - function can't be rejitted because it is collectible
2604 #ifndef DACCESS_COMPILE
2605 HRESULT CodeVersionManager::GetNonVersionableError(MethodDesc* pMD)
2616 _ASSERTE(pMD != NULL);
2618 // Weird, non-user functions were already weeded out in RequestReJIT(), and will
2619 // also never be passed to us by the prestub worker (for the pre-rejit case).
2620 _ASSERTE(pMD->IsIL());
2622 // Any MethodDescs that could be collected are not currently supported. Although we
2623 // rule out all Ref.Emit modules in RequestReJIT(), there can still exist types defined
2624 // in a non-reflection module and instantiated into a collectible assembly
2625 // (e.g., List<MyCollectibleStruct>). In the future we may lift this
2626 // restriction by updating the ReJitManager when the collectible assemblies
2627 // owning the instantiations get collected.
2628 if (pMD->GetLoaderAllocator()->IsCollectible())
2630 return CORPROF_E_FUNCTION_IS_COLLECTIBLE;
2637 //---------------------------------------------------------------------------------------
2639 // Helper that inits a new CodePublishError and adds it to the pErrors array
2642 // * pModule - The module in the module/MethodDef identifier pair for the method which
2643 // had an error during rejit
2644 // * methodDef - The MethodDef in the module/MethodDef identifier pair for the method which
2645 // had an error during rejit
2646 // * pMD - If available, the specific method instance which had an error during rejit
2647 // * hrStatus - HRESULT for the rejit error that occurred
2648 // * pErrors - the list of error records that this method will append to
2651 // * S_OK: error was appended
2652 // * E_OUTOFMEMORY: Not enough memory to create the new error item. The array is unchanged.
2656 #ifndef DACCESS_COMPILE
2657 HRESULT CodeVersionManager::AddCodePublishError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus, CDynArray<CodePublishError> * pErrors)
2667 if (pErrors == NULL)
2672 CodePublishError* pError = pErrors->Append();
2675 return E_OUTOFMEMORY;
2677 pError->pModule = pModule;
2678 pError->methodDef = methodDef;
2679 pError->pMethodDesc = pMD;
2680 pError->hrStatus = hrStatus;
2685 #ifndef DACCESS_COMPILE
2686 void CodeVersionManager::ReportCodePublishError(CodePublishError* pErrorRecord)
2697 ReportCodePublishError(pErrorRecord->pModule, pErrorRecord->methodDef, pErrorRecord->pMethodDesc, pErrorRecord->hrStatus);
2700 void CodeVersionManager::ReportCodePublishError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus)
2711 #ifdef FEATURE_REJIT
2712 BOOL isRejitted = FALSE;
2714 TableLockHolder(this);
2715 isRejitted = !GetActiveILCodeVersion(pModule, methodDef).IsDefaultVersion();
2718 // this isn't perfect, we might be activating a tiered jitting variation of a rejitted
2719 // method for example. If it proves to be an issue we can revisit.
2722 ReJitManager::ReportReJITError(pModule, methodDef, pMD, hrStatus);
2726 #endif // DACCESS_COMPILE
2728 //---------------------------------------------------------------------------------------
2730 // PrepareCodeConfig::SetNativeCode() calls this to determine if there's a non-default code
2731 // version requested for a MethodDesc that has just been jitted for the first time.
2732 // This is also called when methods are being restored in NGEN images. The sequence looks like:
2734 // Enter code version manager lock
2735 // DoJumpStampIfNecessary
2736 // *Runtime code publishes/restores method
2738 // Leave code version manager lock
2739 // Send rejit error callbacks if needed
2743 // Note that the runtime needs to publish/restore the PCODE while this holder is
2744 // on the stack, so it can happen under the code version manager's lock.
2745 // This prevents a race with a profiler that calls
2746 // RequestReJIT just as the method finishes compiling. In particular, the locking ensures
2747 // atomicity between this set of steps (performed in DoJumpStampIfNecessary):
2748 // * (1) Checking whether there is a non-default version for this MD
2749 // * (2) If not, skip doing the jmp-stamp
2750 // * (3) Publishing the PCODE
2752 // with respect to these steps performed in RequestReJIT:
2753 // * (a) Is PCODE published yet?
2754 // * (b) Create non-default ILCodeVersion which the prestub will
2755 // consult when it JITs the original IL
2757 // Without this atomicity, we could get the ordering (1), (2), (a), (b), (3), resulting
2758 // in the rejit request getting completely ignored (i.e., we file away the new ILCodeVersion
2759 // AFTER the prestub checks for it).
2761 // A similar race is possible for code being restored. In that case the restoring thread
2763 // * (1) Check if there is a non-default ILCodeVersion for this MD
2764 // * (2) If not, no need to jmp-stamp
2765 // * (3) Restore the MD
2767 // And RequestRejit does:
2768 // * (a) [In LoadedMethodDescIterator] Is a potential MD restored yet?
2769 // * (b) [In EnumerateDomainClosedMethodDescs] If not, don't queue it for jump-stamping
2771 // Same ordering (1), (2), (a), (b), (3) results in missing both opportunities to jump
2774 #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
2775 PublishMethodHolder::PublishMethodHolder(MethodDesc* pMethodDesc, PCODE pCode) :
2776 m_pMD(NULL), m_hr(S_OK)
2778 // This method can't have a contract because entering the table lock
2779 // below increments GCNoTrigger count. Contracts always revert these changes
2780 // at the end of the method but we need the incremented count to flow out of the
2781 // method. The balancing decrement occurs in the destructor.
2782 STATIC_CONTRACT_NOTHROW;
2783 STATIC_CONTRACT_GC_NOTRIGGER;
2784 STATIC_CONTRACT_CAN_TAKE_LOCK;
2785 STATIC_CONTRACT_MODE_ANY;
2787 // We come here from the PreStub and from MethodDesc::CheckRestore
2788 // The method should be effectively restored, but we haven't yet
2789 // cleared the unrestored bit so we can't assert pMethodDesc->IsRestored()
2791 _ASSERTE(pMethodDesc->GetMethodTable()->IsRestored());
2795 m_pMD = pMethodDesc;
2796 CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
2797 pCodeVersionManager->EnterLock();
2798 m_hr = pCodeVersionManager->DoJumpStampIfNecessary(pMethodDesc, pCode);
2803 PublishMethodHolder::~PublishMethodHolder()
2805 // This method can't have a contract because leaving the table lock
2806 // below decrements GCNoTrigger count. Contracts always revert these changes
2807 // at the end of the method but we need the decremented count to flow out of the
2808 // method. The balancing increment occurred in the constructor.
2809 STATIC_CONTRACT_NOTHROW;
2810 STATIC_CONTRACT_GC_TRIGGERS; // NOTRIGGER until we leave the lock
2811 STATIC_CONTRACT_CAN_TAKE_LOCK;
2812 STATIC_CONTRACT_MODE_ANY;
2816 CodeVersionManager* pCodeVersionManager = m_pMD->GetCodeVersionManager();
2817 pCodeVersionManager->LeaveLock();
2820 pCodeVersionManager->ReportCodePublishError(m_pMD->GetModule(), m_pMD->GetMemberDef(), m_pMD, m_hr);
2825 PublishMethodTableHolder::PublishMethodTableHolder(MethodTable* pMethodTable) :
2826 m_pMethodTable(NULL)
2828 // This method can't have a contract because entering the table lock
2829 // below increments GCNoTrigger count. Contracts always revert these changes
2830 // at the end of the method but we need the incremented count to flow out of the
2831 // method. The balancing decrement occurs in the destructor.
2832 STATIC_CONTRACT_NOTHROW;
2833 STATIC_CONTRACT_GC_NOTRIGGER;
2834 STATIC_CONTRACT_CAN_TAKE_LOCK;
2835 STATIC_CONTRACT_MODE_ANY;
2837 // We come here from MethodTable::SetIsRestored
2838 // The method table should be effectively restored, but we haven't yet
2839 // cleared the unrestored bit so we can't assert pMethodTable->IsRestored()
2841 m_pMethodTable = pMethodTable;
2842 CodeVersionManager* pCodeVersionManager = pMethodTable->GetModule()->GetCodeVersionManager();
2843 pCodeVersionManager->EnterLock();
2844 MethodTable::IntroducedMethodIterator itMethods(pMethodTable, FALSE);
2845 for (; itMethods.IsValid(); itMethods.Next())
2847 // Although the MethodTable is restored, the methods might not be.
2848 // We need to be careful to only query portions of the MethodDesc
2849 // that work in a partially restored state. The only methods that need
2850 // further restoration are IL stubs (which aren't rejittable) and
2851 // generic methods. The only generic methods directly accesible from
2852 // the MethodTable are definitions. GetNativeCode() on generic defs
2853 // will run succesfully and return NULL which short circuits the
2854 // rest of the logic.
2855 MethodDesc * pMD = itMethods.GetMethodDesc();
2856 PCODE pCode = pMD->GetNativeCode();
2859 HRESULT hr = pCodeVersionManager->DoJumpStampIfNecessary(pMD, pCode);
2862 CodeVersionManager::AddCodePublishError(pMD->GetModule(), pMD->GetMemberDef(), pMD, hr, &m_errors);
2869 PublishMethodTableHolder::~PublishMethodTableHolder()
2871 // This method can't have a contract because leaving the table lock
2872 // below decrements GCNoTrigger count. Contracts always revert these changes
2873 // at the end of the method but we need the decremented count to flow out of the
2874 // method. The balancing increment occurred in the constructor.
2875 STATIC_CONTRACT_NOTHROW;
2876 STATIC_CONTRACT_GC_TRIGGERS; // NOTRIGGER until we leave the lock
2877 STATIC_CONTRACT_CAN_TAKE_LOCK;
2878 STATIC_CONTRACT_MODE_ANY;
2882 CodeVersionManager* pCodeVersionManager = m_pMethodTable->GetModule()->GetCodeVersionManager();
2883 pCodeVersionManager->LeaveLock();
2884 for (int i = 0; i < m_errors.Count(); i++)
2886 pCodeVersionManager->ReportCodePublishError(&(m_errors[i]));
2890 #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
2892 #endif // FEATURE_CODE_VERSIONING