diff --git a/Documentation/design-docs/code-versioning-pr-notes.md b/Documentation/design-docs/code-versioning-pr-notes.md new file mode 100644 index 000000000000..6b7846af2c25 --- /dev/null +++ b/Documentation/design-docs/code-versioning-pr-notes.md @@ -0,0 +1,59 @@ +# Code Versioning PR Notes # + +This is information that I hope is useful to reviewers while feature work is in progress, but at the end I plan to delete it. If you think there is information here of useful historical significance let me know and I'll preserve it, perhaps as an appendix to the feature spec. + +## Progress so far ## + +The current PR is incomplete so I wouldn't bother pointing out bug level issues or obvious gaps in functionality as long as you don't forsee significant issues in filling them. Hopefully it gives an idea where I am going, primarily for issue #2 in the design doc "There is no mechanism to record the aggregate configuration settings which generated a code body". Please do point out any issues you anticipate with the broader design. I'm trying to balance enough concrete info so that you can see some usage patterns and relation to other code while not doing so much work that I've wasted major effort if we need to make a course correction. I'm going to continue coding under the optimistic assumption no major course correction will be needed. + +Because the change is going to be large in aggregate I'm attempting to structure as a set of incremental steps that should be reasonably understandable in isolation. I think it will be easier to conclude the result is correct when viewing it as a series of deltas from current state. + +The first commit is a relatively uninteresting minor refactor and the 2nd commit just lays down some boilerplate infrastructure for a code versioning feature. The 3rd commit is where it starts getting interesting... + +You should be able to see a pretty clear parallel between the new data structure in codeversion.h and previous datastructure in rejit.h. Roughly SharedReJitInfo -> ILCodeVersion[Node] and ReJitInfo -> NativeCodeVersion[Node]. However there are more changes than merely the rename. + +1. The ReJit feature was roughly sliced in half. All state that Rejit maintained about the versions has now been shifted into the CodeVersion tree and ReJitManager is now a static class. There is also a partial migration of the JumpStamping code that begins building the solution for issue #3 "There is no unified mechanism for switching runtime dispatch between different versions of the code" + +2. The Rejit feature used ReJitInfo in two different ways, one as a representation of a concrete native code body and the other as a representation of future rejitting work that might be needed for a given IL body. This dual usage doesn't appear to fit well in a more generalized tree (its not clear it was even that great just for ReJit, but it works). That second form corresponded to any ReJitInfos that used IL/Metadata token as their key or that used open generic methodDescs as the key. Typically the usage pattern of those would be to enumerate them and then immediately indirect to the corresponding SharedReJitInfo for each. Rather than do that I converted the code to directly enumerate ILCodeVersion instances. If necessary the ILCodeVersion can then be lowered to a set generic instantiations/native code bodies that all share that IL version. A few examples of this type of change: + + - ReJitManager::BindILVersion instead of previous ReJitManager::MarkForReJitHelper + - Top of ReJitManager::DoJumpStampIfNecessary + - DacDbiInterfaceImpl::GetActiveRejitILCodeVersionNode (you can see the previous lack of canonicalization force two separate searches into the old ReJitInfo based data structure) + + ILCodeVersion is always canonically indexed by Module/metadata token pair. Any search by MethodDesc is merely shorthand for searching by MethodDesc->GetModule()/MethodDesc->GetMemberDef() + +3. NativeCodeVersion/ILCodeVersion aren't allocated on the loader heap as ReJitInfo/SharedReJitInfo were. This is intentional to prepare for their eventual collectibility. The loader heap doesn't appear to have any good support for fine-grained alloc/delete and its not clear there would be any significant benefit to adding that functionality vs. using standard new/delete. +4. The synchronization that ReJit did has largely been shifted/replicated on CodeVersionManager. The general pattern is if you want to read/write/add/remove anything in CodeVersionManager/ILCodeVersion/NativeCodeVersion you need to own the corresponding CodeVersionManager lock. As the code updating/code generation paths are fleshed out more I certainly think refinements are on the table, but if possible my goal is still to handle everything with one of: the big lock, narrow usage of interlocked, immutable write once data + +5. NativeCodeVersion doesn't track the state of the JumpStamp within each instance as ReJitInfo used to do. If you consider the set of ReJitInfo's that all map to the same MethodDesc instantiation, all but one of them is always in the state m\_dwInternalFlags = kJumpNone. Likewise all but one of them has a zero-filled m\_rgSavedCode. Conceptually there is only method entry point that can be patched so only one set of these fields is needed per entrypoint. I created MethodDescVersioningState to directly track that data rather than cloning it in every NativeCodeVersion. The choice of which ReJitInfo had the flag set different than none is interesting data - it represented which version was currently active. However we can efficiently mark it with a single bit per NativeCodeVersion or single pointer in MethodDescVersioningState, not the 12 bytes per ReJitInfo it used before. + +## Work still to come ## + +Assuming no major course correction, this is where I anticipate things will go (not necessarily in this order): + +1. The NativeCodeVersion/ILCodeVersion only support the explicitly backed node case. For the case where these structures represent the default version there will be no node and the code needs to bifurcate and fetch the data from other runtime data structures. So far rejit doesn't really need that because it mostly avoids manipulating a default version, but a consolidated prestub codepath will certainly be referencing default code versions. + +2. The special case jitting paths in ReJitManager::DoReJitIfNecessary will get merged with the general case jitting paths. + +3. The low level interaction ReJit currently has with jump-stamping will get raised to an abstraction level where it just indicates to the code version manager which IL version of the code should be active. + +4. The code version manager will automatically use FixupPrecode updates instead of JumpStamps whenever the method being versioned can support a fixup precode. With tiered compilation on, all methods jitted at runtime will support a fixup precode. + +5. Tiered compilation will get embedded into this scheme with new NativeCodeVersion nodes being generated when the tiered compilation manager wants to shift from tier0 (initial default code) to tier1 (more optimized code). Whenever rejit creates a new IL version the tier will reset back to 0 for that IL versioning branch, then upgrade to tier1 after a given number of invocations. + +6. Using the hash tables to enumerate NativeCodeVersions for the same MethodDesc or ILCodeVersions for the same Module/metadata token doesn't appear space efficient. I'm thinking to add a linked list of all NativeCodeVersions for a MethodDesc hanging off MethodDescVersioningState, and then introduce a similar ILVersioningState with linked list of ILCodeVersions. + +7. A variety of functional and stress testing needs to be done. + +8. Diagnostics interactions with ETW, SOS, and the debugger need some updates with tiered compilation enabled. + + +## Checkin bar ## + +I don't anticipate completing all the above work before checkin. I think the reasonable bar is that: + +1. No regressions on profiler rejit behavior, or any other supported runtime feature (CI passes + additional manual testing where CI has insufficient coverage) assuming that tiered compilation is still in its default OFF configuration. +2. Code reviewers are happy with the chunk of code being submitted + overall direction +3. The changes don't interfere with anyone else doing work around the same area. + +We can discuss exactly how much of the above changes that will be in each checkin, but tentatively I'm thinking next checkin might include a good portion, perhaps (1), (2), (3), (5), and some of (7) \ No newline at end of file diff --git a/Documentation/design-docs/code-versioning.md b/Documentation/design-docs/code-versioning.md new file mode 100644 index 000000000000..87a82c8b9a4b --- /dev/null +++ b/Documentation/design-docs/code-versioning.md @@ -0,0 +1,146 @@ +# Code Versioning # + +The current runtime prototype for tiered compilation doesn't work properly when trying to use profiler +rejit at the same time. Although this may seem initially seem like a corner case intersection, diagnostic monitoring profilers are increasingly becoming standard operating procedure with rejit usage increasing fast in that domain. Likewise the goal is for tiered compilation to be used as an always on (or nearly always on) feature. Rejit can also be viewed as a canonical example of a broader problem that the runtime doesn't have a foundational concept of code versioning and lifetime management. Rejit has one conception of it, the tiered jitting prototype adds another, Edit and Continue adds a 3rd, LCG a 4th, the IL interpreter a 5th, Samsung's in progress code-pitching a 6th, and so on. Using any of those features is mutually exclusive with every other at method level granularity. + +I don't aim to produce in one effort a grand unified implementation, I do want to achieve two things: + +1. A roadmap that allows all those features to interoperate if we made the effort to do so in the future. +2. Implement that design far enough so that Rejit + Tiered Compilation interoperate now. + +For now tiered compilation can reasonably co-exist with the other features by disabling it only on the methods that are eligible for one of the other techniques. + + + +### Code Versions and the Build Pipeline ### + +Code versions are an abstract concept not currently embodied precisely in the runtime implementation. Conceptually they include two things: + +1. A native code implementation with corresponding side tables for EH, GC, debug data, etc. +2. Various identifiers and configuration information that describe how this code version was generated and how it differs from other versions. + +Given a single IL method def token loaded with a .Net process we may have N different 'code versions'. +To create a particular code version there is a code generation pipeline operating inside the runtime which gives several different components an opportunity to modify the final result. Again not defined in a very structured way within our code, the current effective pipeline looks somewhat like this: + +1. IL code loaded from an assembly +2. IL code can be replaced at module load time by a profiler +3. IL code can be replaced by Edit And Continue +4. IL code can be replaced by profiler rejit +5. Jit settings can be modified by debugger +6. Jit settings can be modified by the profiler +7. Jit settings can be modified by the tiered compilation manager +8. Final native code is produced by the Jit + +A complete accounting of a code version includes the configuration choices we made at every stage and then the final result of jitting the code. If any stage changes its configuration, the new combination of settings and the resulting code represents a new code version. + + +### Current barriers to interoperability ### + +Among the runtime features that have notions of code versioning or code lifetime management, no single change is going to make them all work together, but we can do some centralized work to provide a standard for interoperation. The issues to resolve are: + +1. Runtime features don't agree on a common in-process build pipeline. ReJit is probably the biggest offender but the general issue that some feature will duplicate a portion of the build/configuration logic that handles the versioning needs of that one feature as a special case, but excludes the requirements from other features. + +2. There is no mechanism to record the aggregate configuration settings which will generate a code body. This is useful for diagnostic purposes, to memoize the resulting code, and to track the configuration of the active code-body (the one that should be run when the method is next called). When all the configuration is static it is easy enough to infer the configuration used, and when one configuration stage is dynamic we usually add a dictionary somewhere which tracks N configurations of one stage to N corresponding code bodies. However once two stages vary it is no longer sufficient to have feature specific dictionaries that assume all other configuration stages are held constant. + +3. There is no unified mechanism for switching runtime dispatch between different versions of the code. ReJit uses a JumpStamp, EnC and tiered compilation use FixupPrecode, and IL interpreter uses a custom stub. + +4. There is no unified mechanism for lifetime management of different code versions. Most of the runtime assumes code has AppDomain lifetime, LCG has a customized garbage collection scheme, and Samsung's code pitching uses a different garbage collection scheme. + +### Proposed Solutions ### + +**1. Runtime features don't agree on a common in-process build pipeline.** + +We can define that the code path through PreStub, PreStubWorker, DoPrestub, JitMethodWorker, UnsafeJitFunction, etc. is the standard path. This code path isn't quite as flexible as needs to be to accomodate all the configuration stage options so I propose we do a little refactoring. Currently that path uses a collection of flags to control behavior, but the number of ifdefs and inlined special cases makes the code hard to follow and some features deliberately avoided attempting to integrate because the perceived complexity of correctly running in that code path seemed overly risky. I believe refactoring using a configuration object argument that has a few polymorphic callbacks will provide a good balance between performance, code maintainability and extensibility. Given typical jit times on the order of 100k+ cycles, the overhead of a few extra virtual calls during setup seems neglible. I do however want to avoid any significant performance costs to make the callbacks so both the caller and the callee should keep the current EH model, not do GC mode or app domain transitions, use simple shared data types, not add defensive synchronization locks, etc. + ReJIT then needs to be refactored to use this path rather than its own custom re-implementation of MakeJitWorker. + +**2. There is no mechanism to record the aggregate configuration settings which generated a code body.** + +We need to define a common data structure that represents a code version, and then a manager that is capable of storing and enumerating them. A tree of versions appears to be the best conceptual fit with +both pre-existing code and the kinds of operations new potential build pipeline stages want to do. +The leaves of the tree represent distinct code versions, and each level in the tree represents configuration that corresponds to one stage in the build pipeline. + +A simple hypothetical tree that grows left to right (apologies there is probably a better way to format this): + +Level 0 IL assembly + method Def -> Level 1 IL instrumentation from rejit -> Level 2 Generic instantiation + +(List.Add) -> List.Add original IL -> List.Add + +...................................................-> List.Add + +...................... -> List.Add new IL -> List.Add + +...................................................-> List.Add + + +I propose we do build this tree-like API but with three levels of branched configuration: + +1. ReJIT modifications (IL instrumentation, jit flags, IL mapping tables) +2. Generic instantiation +3. Jitted code flags for tiered compilation + +In the future we could add other stages as we want to allow for more dynamically varying configuration options. For example a future candidate is Edit and Continue, which if added would likely become the new level 1 shifting all the other levels right. + +Each level of the tree will be represented with an object model node (excepting some pragmatic considerations discussed below). The object model should support basic tree operations such as navigation to parent, enumeration of children, and adding/removing children. Each node also allows read/write access to relevant configuration data/versioning identifiers/code information, though most of the data should become immutable after it is written initially. + +Other pragmatic considerations that will cause our tree to diverge from an idealized form: + +1. The majority of code will likely only have one version, and this configuration data is already stored in other runtime data structures. Creating additional memory allocations to store the data a 2nd time would not be efficient. To avoid this the main trick up my sleave is to add one layer of indirection. Users of the version tree interact with copy-by-value structures that represent logical tree nodes. Internally those structures either have a pointer to a heap allocated physical tree node, or have some identity data indicating what logical node they are supposed to represent. In the case of physically backed nodes all the method calls on the structure are just proxied to the backing store and implemented as nieve field lookups there. In the case of the unbacked nodes, the method calls on the structure would lookup or compute the configuration information on demand from disparate CLR data structures that store it. For example the API to get the native code from a leaf code version might look like this: + +``` + PCODE NativeCodeVersion::GetNativeCode() const + { + if (m_storageKind == StorageKind::Explicit) + { + return m_pNativeCodeVersionStorage->GetNativeCode(); + } + else + { + return GetMethodDesc()->GetNativeCode(); + } + } +``` + +2. Its not clear we'll have a strong need to create an object model representation of versions at the generic instance level of the versioning tree. If it winds up being useful we can certainly do so, but if the difference to other components is minimal as I suspect it will be then we may get away with simply identifying a generic instance version at level 2 as the pair . This allows for various parts of the API surface to be ellided. For example instead of having: + +``` + GenericInstanceCodeVersionCollection ILCodeVersion::GetGenericVersions(); +``` +``` + NativeCodeVersionCollection GenericInstanceCodeVersion::GetTieredCompilationVersions(); +``` + +We get: + + NativeCodeVersionCollection ILCodeVersion::GetNativeCodeVersions(MethodDesc* pClosedMethodDesc); + +3. Although I'm not planning to have any form of garbage collection in the initial version, I think we should assume that eventually we will collect code versions that aren't in active use. The tree might become temporarily very branchy, but over time it would decay back down to 1 branch per generic instantiation. Ultimately non-generic methods might become a single path from root to a leaf, and generic methods look like palm trees with long skinny trunks and then one level with greater branching. In these cases rather than fret too much about maximal sharing of common configuration data between similar children in a branchy tree we would instead worry about compactly encoding the single trunk and the leaves after the generic instantiation branch. + + The result looks similar to the existing tree in rejit.h. The rejit tree has a first level branching node SharedRejitInfo that represents a unique IL body, and then each of those has child ReJitInfo nodes that are 1:1 with native code. I am renaming the types ILCodeVersion and NativeCodeVersion in an attempt to break the direct associations rejit and instead refer to the kind of configuration present at each stage. However if we add more stages that makes this terminology ambiguous (for example adding Edit and Continue IL modification as a stage) then we might need to adjust naming again to better distinguish. + + One aspect of the rejit tree I don't plan to bring forward is the use of ReJitInfo nodes as placeholders for an unspecified set of yet to be loaded generic instantiations. In many cases I find it actually complicates the the rejit code to use ReJitInfo that way and it appears that SharedReJitInfo on its own (or the ILVersion equivalent in the new data structure) is a perfectly viable representation of that concept. + +For more detailed view of some proposed API, see src\vm\codeversion.h + +**3. There is no unified mechanism for switching runtime dispatch between different versions of the code.** + +I propose we create a general purpose concept of swapping to a new method body implementation inside the implementation of MethodDesc and the to be created Code Version Manager. The resulting API might look something like: + + CodeVersionManager::SetActiveCodeVersion(NativeCodeVersion someCodeVersion); + +Then the next time the method that this version pertains to is called, we guarantee it will run this version of the code. Internally this involves: + +a) If the method is already dispatching to certain code, modify the indirections not to point at that old version + +b) If the new version already has generated code, modify the indirections to point at the new code instead. + +c) If the new version does not already have generated code, record that this is the version we want to run eventually, and modify the various indirections to point to the PreStub. When the PreStub runs later it will generate the code for the version we indicated and fixup the indirections. + +In terms of which indirections we use, I think we need to support two of our pre-existing techniques for now: FixupPrecode and JumpStamp. FixupPrestub has much better perf characteristics in some cases, but JumpStamp is the only one that works on precompiled images. JumpStamp is effectively required to prevent ReJit from regressing, but without a FixupPrestub option I don't think tiered compilation performance will be reasonable. The caller of the API should be ignorant of what technique we use, which means we are free to revisit these choices in the future. + +**4. There is no unified mechanism for lifetime management of different code versions.** + +This one isn't an issue for ReJit+tiered compilation so I have given it the least attention, but it will be concern in the future for being able to do any sort of garbage collection over code versions. I'd like to ensure we have a plausible path forward without investing significant time trying to solve any details now. My thoughts: + +Only versionable code is in scope for lifetime management. Versionable code is code that is represented in the code version manager, and that can swapped in/out using the SetActiveCodeVersion() API proposed above. This greatly simplifies the problem because most of the runtime doesn't ever hold a pointer directly to this code, it holds a pointer to a jmp instruction and the target of that jmp is the actual code. At any time the jmp can be re-directed back to the PreStub, the code collected, and pointers to the jmp remain valid. Only a tiny subset of the runtime will be allowed to manipulate the actual jitted code pointers, and this subset can use a basic reference counting scheme to ensure that code versions aren't collected while they are in use. Code pointers could also exist in registers or on the stack and we probably won't be able to use explicit reference counting to handle those cases. For references on the stack and in registers I'm going to handwave and say the stackwalker will identify them when we suspend and sweep for a code garbage collection. There is certainly more detail to getting that exactly right, but we've already got LCG so I'm assuming the prior art makes it a reasonably tractable problem. + +I'm not making any attempt to define policy about how often we sweep the code versions, what the scope of those sweeps are, or what code/version information gets deleted when we detect it is unreferenced. All of those would be performance policy decisions for another day. Samsung's jit pitching PR in progress now does make one stab at defining such policies. diff --git a/src/debug/daccess/dacdbiimpl.cpp b/src/debug/daccess/dacdbiimpl.cpp index f48ecc0bd04c..75f40fb775f7 100644 --- a/src/debug/daccess/dacdbiimpl.cpp +++ b/src/debug/daccess/dacdbiimpl.cpp @@ -903,14 +903,14 @@ void DacDbiInterfaceImpl::GetNativeVarData(MethodDesc * pMethodDesc, // pEntryCount is the number of valid entries in nativeMap, and it may be adjusted downwards // as part of the composition. //----------------------------------------------------------------------------- -void DacDbiInterfaceImpl::ComposeMapping(InstrumentedILOffsetMapping profilerILMap, ICorDebugInfo::OffsetMapping nativeMap[], ULONG32* pEntryCount) +void DacDbiInterfaceImpl::ComposeMapping(const InstrumentedILOffsetMapping * pProfilerILMap, ICorDebugInfo::OffsetMapping nativeMap[], ULONG32* pEntryCount) { // Translate the IL offset if the profiler has provided us with a mapping. // The ICD public API should always expose the original IL offsets, but GetBoundaries() // directly accesses the debug info, which stores the instrumented IL offsets. ULONG32 entryCount = *pEntryCount; - if (!profilerILMap.IsNull()) + if (!pProfilerILMap->IsNull()) { // If we did instrument, then we can't have any sequence points that // are "in-between" the old-->new map that the profiler gave us. @@ -925,7 +925,7 @@ void DacDbiInterfaceImpl::ComposeMapping(InstrumentedILOffsetMapping profilerILM ULONG32 prevILOffset = (ULONG32)(ICorDebugInfo::MAX_ILNUM); for (ULONG32 i = 0; i < entryCount; i++) { - ULONG32 origILOffset = TranslateInstrumentedILOffsetToOriginal(nativeMap[i].ilOffset, &profilerILMap); + ULONG32 origILOffset = TranslateInstrumentedILOffsetToOriginal(nativeMap[i].ilOffset, pProfilerILMap); if (origILOffset == prevILOffset) { @@ -1003,12 +1003,12 @@ void DacDbiInterfaceImpl::GetSequencePoints(MethodDesc * pMethodDesc, // if there is a rejit IL map for this function, apply that in preference to load-time mapping #ifdef FEATURE_REJIT - ReJitManager * pReJitMgr = pMethodDesc->GetReJitManager(); - ReJitInfo* pReJitInfo = pReJitMgr->FindReJitInfo(dac_cast(pMethodDesc), (PCODE)startAddr, 0); - if (pReJitInfo != NULL) + CodeVersionManager * pCodeVersionManager = pMethodDesc->GetCodeVersionManager(); + NativeCodeVersion nativeCodeVersion = pCodeVersionManager->GetNativeCodeVersion(dac_cast(pMethodDesc), (PCODE)startAddr); + if (!nativeCodeVersion.IsNull()) { - InstrumentedILOffsetMapping rejitMapping = pReJitInfo->m_pShared->m_instrumentedILMap; - ComposeMapping(rejitMapping, mapCopy, &entryCount); + const InstrumentedILOffsetMapping * pRejitMapping = nativeCodeVersion.GetILCodeVersion().GetInstrumentedILMap(); + ComposeMapping(pRejitMapping, mapCopy, &entryCount); } else { @@ -1016,7 +1016,7 @@ void DacDbiInterfaceImpl::GetSequencePoints(MethodDesc * pMethodDesc, // if there is a profiler load-time mapping and not a rejit mapping, apply that instead InstrumentedILOffsetMapping loadTimeMapping = pMethodDesc->GetModule()->GetInstrumentedILOffsetMapping(pMethodDesc->GetMemberDef()); - ComposeMapping(loadTimeMapping, mapCopy, &entryCount); + ComposeMapping(&loadTimeMapping, mapCopy, &entryCount); #ifdef FEATURE_REJIT } #endif @@ -7149,25 +7149,35 @@ HRESULT DacDbiInterfaceImpl::GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TA HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pvmReJitInfo) { DD_ENTER_MAY_THROW; - if (pvmReJitInfo == NULL) + _ASSERTE(!"You shouldn't be calling this - use GetActiveRejitILCodeVersionNode instead"); + return S_OK; +} + +HRESULT DacDbiInterfaceImpl::GetActiveRejitILCodeVersionNode(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ILCodeVersionNode* pVmILCodeVersionNode) +{ + DD_ENTER_MAY_THROW; + if (pVmILCodeVersionNode == NULL) return E_INVALIDARG; #ifdef FEATURE_REJIT PTR_Module pModule = vmModule.GetDacPtr(); - ReJitManager * pReJitMgr = pModule->GetReJitManager(); - PTR_ReJitInfo pReJitInfoCurrent = pReJitMgr->FindNonRevertedReJitInfo(pModule, methodTk); - // if the token lookup failed, we need to search again by method desc - // The rejit manager will index by token if the method isn't loaded when RequestReJIT runs - // and by methoddesc if it was loaded - if (pReJitInfoCurrent == NULL) - { - MethodDesc* pMD = pModule->LookupMethodDef(methodTk); - if (pMD != NULL) - { - pReJitInfoCurrent = pReJitMgr->FindNonRevertedReJitInfo(dac_cast(pMD)); - } + CodeVersionManager * pCodeVersionManager = pModule->GetCodeVersionManager(); + // Be careful, there are two different definitions of 'active' being used here + // For the CodeVersionManager, the active IL version is whatever one should be used in the next invocation of the method + // 'rejit active' narrows that to only include rejit IL bodies where the profiler has already provided the definition + // for the new IL (ilCodeVersion.GetRejitState()==ILCodeVersion::kStateActive). It is possible that the code version + // manager's active IL version hasn't yet asked the profiler for the IL body to use, in which case we want to filter it + // out from the return in this method. + ILCodeVersion activeILVersion = pCodeVersionManager->GetActiveILCodeVersion(pModule, methodTk); + if (activeILVersion.IsNull() || activeILVersion.GetRejitState() != ILCodeVersion::kStateActive) + { + pVmILCodeVersionNode->SetDacTargetPtr(0); + } + else + { + pVmILCodeVersionNode->SetDacTargetPtr(PTR_TO_TADDR(activeILVersion.AsNode())); } - pvmReJitInfo->SetDacTargetPtr(PTR_TO_TADDR(pReJitInfoCurrent)); #else + _ASSERTE(!"You shouldn't be calling this - rejit is not supported in this build"); pvmReJitInfo->SetDacTargetPtr(0); #endif return S_OK; @@ -7176,15 +7186,22 @@ HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_Module vmModule, mdMethodDef met HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pvmReJitInfo) { DD_ENTER_MAY_THROW; - if (pvmReJitInfo == NULL) + _ASSERTE(!"You shouldn't be calling this - use GetNativeCodeVersionNode instead"); + return S_OK; +} + +HRESULT DacDbiInterfaceImpl::GetNativeCodeVersionNode(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_NativeCodeVersionNode* pVmNativeCodeVersionNode) +{ + DD_ENTER_MAY_THROW; + if (pVmNativeCodeVersionNode == NULL) return E_INVALIDARG; #ifdef FEATURE_REJIT PTR_MethodDesc pMD = vmMethod.GetDacPtr(); - ReJitManager * pReJitMgr = pMD->GetReJitManager(); - PTR_ReJitInfo pReJitInfoCurrent = pReJitMgr->FindReJitInfo(pMD, (PCODE)codeStartAddress, 0); - pvmReJitInfo->SetDacTargetPtr(PTR_TO_TADDR(pReJitInfoCurrent)); + CodeVersionManager * pCodeVersionManager = pMD->GetCodeVersionManager(); + NativeCodeVersion codeVersion = pCodeVersionManager->GetNativeCodeVersion(pMD, (PCODE)codeStartAddress); + pVmNativeCodeVersionNode->SetDacTargetPtr(PTR_TO_TADDR(codeVersion.AsNode())); #else - pvmReJitInfo->SetDacTargetPtr(0); + pVmNativeCodeVersionNode->SetDacTargetPtr(0); #endif return S_OK; } @@ -7192,30 +7209,44 @@ HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRE HRESULT DacDbiInterfaceImpl::GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, OUT VMPTR_SharedReJitInfo* pvmSharedReJitInfo) { DD_ENTER_MAY_THROW; - if (pvmSharedReJitInfo == NULL) + _ASSERTE(!"You shouldn't be calling this - use GetLCodeVersionNode instead"); + return S_OK; +} + +HRESULT DacDbiInterfaceImpl::GetILCodeVersionNode(VMPTR_NativeCodeVersionNode vmNativeCodeVersionNode, VMPTR_ILCodeVersionNode* pVmILCodeVersionNode) +{ + DD_ENTER_MAY_THROW; + if (pVmILCodeVersionNode == NULL) return E_INVALIDARG; #ifdef FEATURE_REJIT - ReJitInfo* pReJitInfo = vmReJitInfo.GetDacPtr(); - pvmSharedReJitInfo->SetDacTargetPtr(PTR_TO_TADDR(pReJitInfo->m_pShared)); + NativeCodeVersionNode* pNativeCodeVersionNode = vmNativeCodeVersionNode.GetDacPtr(); + pVmILCodeVersionNode->SetDacTargetPtr(PTR_TO_TADDR(pNativeCodeVersionNode->GetILCodeVersion().AsNode())); #else - _ASSERTE(!"You shouldn't be calling this - how did you get a ReJitInfo?"); - pvmSharedReJitInfo->SetDacTargetPtr(0); + _ASSERTE(!"You shouldn't be calling this - rejit is not supported in this build"); + pVmILCodeVersionNode->SetDacTargetPtr(0); #endif return S_OK; } HRESULT DacDbiInterfaceImpl::GetSharedReJitInfoData(VMPTR_SharedReJitInfo vmSharedReJitInfo, DacSharedReJitInfo* pData) +{ + DD_ENTER_MAY_THROW; + _ASSERTE(!"You shouldn't be calling this - use GetILCodeVersionNodeData instead"); + return S_OK; +} + +HRESULT DacDbiInterfaceImpl::GetILCodeVersionNodeData(VMPTR_ILCodeVersionNode vmILCodeVersionNode, DacSharedReJitInfo* pData) { DD_ENTER_MAY_THROW; #ifdef FEATURE_REJIT - SharedReJitInfo* pSharedReJitInfo = vmSharedReJitInfo.GetDacPtr(); - pData->m_state = pSharedReJitInfo->GetState(); - pData->m_pbIL = PTR_TO_CORDB_ADDRESS(pSharedReJitInfo->m_pbIL); - pData->m_dwCodegenFlags = pSharedReJitInfo->m_dwCodegenFlags; - pData->m_cInstrumentedMapEntries = (ULONG)pSharedReJitInfo->m_instrumentedILMap.GetCount(); - pData->m_rgInstrumentedMapEntries = PTR_TO_CORDB_ADDRESS(dac_cast(pSharedReJitInfo->m_instrumentedILMap.GetOffsets())); + ILCodeVersionNode* pILCodeVersionNode = vmILCodeVersionNode.GetDacPtr(); + pData->m_state = pILCodeVersionNode->GetRejitState(); + pData->m_pbIL = PTR_TO_CORDB_ADDRESS(dac_cast(pILCodeVersionNode->GetIL())); + pData->m_dwCodegenFlags = pILCodeVersionNode->GetJitFlags(); + pData->m_cInstrumentedMapEntries = (ULONG)pILCodeVersionNode->GetInstrumentedILMap()->GetCount(); + pData->m_rgInstrumentedMapEntries = PTR_TO_CORDB_ADDRESS(dac_cast(pILCodeVersionNode->GetInstrumentedILMap()->GetOffsets())); #else - _ASSERTE(!"You shouldn't be calling this - how did you get a SharedReJitInfo?"); + _ASSERTE(!"You shouldn't be calling this - rejit isn't supported in this build"); #endif return S_OK; } diff --git a/src/debug/daccess/dacdbiimpl.h b/src/debug/daccess/dacdbiimpl.h index a86072325cda..b13c75a561bb 100644 --- a/src/debug/daccess/dacdbiimpl.h +++ b/src/debug/daccess/dacdbiimpl.h @@ -147,9 +147,13 @@ class DacDbiInterfaceImpl : void GetGCHeapInformation(COR_HEAPINFO * pHeapInfo); HRESULT GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW); HRESULT GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pReJitInfo); + HRESULT GetActiveRejitILCodeVersionNode(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ILCodeVersionNode* pVmILCodeVersionNode); HRESULT GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pReJitInfo); + HRESULT GetNativeCodeVersionNode(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_NativeCodeVersionNode* pVmNativeCodeVersionNode); HRESULT GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, VMPTR_SharedReJitInfo* pSharedReJitInfo); + HRESULT GetILCodeVersionNode(VMPTR_NativeCodeVersionNode vmNativeCodeVersionNode, VMPTR_ILCodeVersionNode* pVmILCodeVersionNode); HRESULT GetSharedReJitInfoData(VMPTR_SharedReJitInfo sharedReJitInfo, DacSharedReJitInfo* pData); + HRESULT GetILCodeVersionNodeData(VMPTR_ILCodeVersionNode vmILCodeVersionNode, DacSharedReJitInfo* pData); HRESULT GetDefinesBitField(ULONG32 *pDefines); HRESULT GetMDStructuresVersion(ULONG32* pMDStructuresVersion); @@ -174,7 +178,7 @@ class DacDbiInterfaceImpl : SequencePoints * pNativeMap); // Helper to compose a IL->IL and IL->Native mapping - void ComposeMapping(InstrumentedILOffsetMapping profilerILMap, ICorDebugInfo::OffsetMapping nativeMap[], ULONG32* pEntryCount); + void ComposeMapping(const InstrumentedILOffsetMapping * pProfilerILMap, ICorDebugInfo::OffsetMapping nativeMap[], ULONG32* pEntryCount); // Helper function to convert an instrumented IL offset to the corresponding original IL offset. ULONG TranslateInstrumentedILOffsetToOriginal(ULONG ilOffset, diff --git a/src/debug/daccess/request.cpp b/src/debug/daccess/request.cpp index ebaa1f833faa..53c97ba3fdb6 100644 --- a/src/debug/daccess/request.cpp +++ b/src/debug/daccess/request.cpp @@ -817,27 +817,27 @@ ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThreadData * } #ifdef FEATURE_REJIT -void CopyReJitInfoToReJitData(ReJitInfo * pReJitInfo, DacpReJitData * pReJitData) +void CopyNativeCodeVersionToReJitData(NativeCodeVersion nativeCodeVersion, DacpReJitData * pReJitData) { - pReJitData->rejitID = pReJitInfo->m_pShared->GetId(); - pReJitData->NativeCodeAddr = pReJitInfo->m_pCode; + pReJitData->rejitID = nativeCodeVersion.GetILCodeVersion().GetVersionId(); + pReJitData->NativeCodeAddr = nativeCodeVersion.GetNativeCode(); - switch (pReJitInfo->m_pShared->GetState()) + switch (nativeCodeVersion.GetILCodeVersion().GetRejitState()) { default: _ASSERTE(!"Unknown SharedRejitInfo state. DAC should be updated to understand this new state."); pReJitData->flags = DacpReJitData::kUnknown; break; - case SharedReJitInfo::kStateRequested: + case ILCodeVersion::kStateRequested: pReJitData->flags = DacpReJitData::kRequested; break; - case SharedReJitInfo::kStateActive: + case ILCodeVersion::kStateActive: pReJitData->flags = DacpReJitData::kActive; break; - case SharedReJitInfo::kStateReverted: + case ILCodeVersion::kStateReverted: pReJitData->flags = DacpReJitData::kReverted; break; } @@ -944,33 +944,37 @@ HRESULT ClrDataAccess::GetMethodDescData( EX_TRY { - ReJitManager * pReJitMgr = pMD->GetReJitManager(); + CodeVersionManager * pCodeVersionManager = pMD->GetCodeVersionManager(); // Current ReJitInfo - ReJitInfo * pReJitInfoCurrent = pReJitMgr->FindNonRevertedReJitInfo(pMD); - if (pReJitInfoCurrent != NULL) + ILCodeVersion activeILCodeVersion = pCodeVersionManager->GetActiveILCodeVersion(pMD); + NativeCodeVersionCollection nativeCodeVersions = activeILCodeVersion.GetNativeCodeVersions(pMD); + for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++) { - CopyReJitInfoToReJitData(pReJitInfoCurrent, &methodDescData->rejitDataCurrent); + // This arbitrarily captures the first jitted version for the active IL version, but with + // tiered compilation there could be many such method bodies. Before tiered compilation is enabled in a broader set + // of scenarios we need to consider how this change goes all the way up to the UI - probably exposing the + // entire set of methods. + CopyNativeCodeVersionToReJitData(*iter, &methodDescData->rejitDataCurrent); } // Requested ReJitInfo _ASSERTE(methodDescData->rejitDataRequested.rejitID == 0); if (methodDescData->requestedIP != NULL) { - ReJitInfo * pReJitInfoRequested = pReJitMgr->FindReJitInfo( + NativeCodeVersion nativeCodeVersionRequested = pCodeVersionManager->GetNativeCodeVersion( pMD, - CLRDATA_ADDRESS_TO_TADDR(methodDescData->requestedIP), - NULL /* reJitId */); + CLRDATA_ADDRESS_TO_TADDR(methodDescData->requestedIP)); - if (pReJitInfoRequested != NULL) + if (!nativeCodeVersionRequested.IsNull()) { - CopyReJitInfoToReJitData(pReJitInfoRequested, &methodDescData->rejitDataRequested); + CopyNativeCodeVersionToReJitData(nativeCodeVersionRequested, &methodDescData->rejitDataRequested); } } // Total number of jitted rejit versions ULONG cJittedRejitVersions; - if (SUCCEEDED(pReJitMgr->GetReJITIDs(pMD, 0 /* cReJitIds */, &cJittedRejitVersions, NULL /* reJitIds */))) + if (SUCCEEDED(ReJitManager::GetReJITIDs(pMD, 0 /* cReJitIds */, &cJittedRejitVersions, NULL /* reJitIds */))) { methodDescData->cJittedRejitVersions = cJittedRejitVersions; } @@ -997,7 +1001,7 @@ HRESULT ClrDataAccess::GetMethodDescData( ReJITID * rgReJitIds = reJitIds.OpenRawBuffer(cRevertedRejitVersions + 1); if (rgReJitIds != NULL) { - hr = pReJitMgr->GetReJITIDs(pMD, cRevertedRejitVersions + 1, &cReJitIds, rgReJitIds); + hr = ReJitManager::GetReJITIDs(pMD, cRevertedRejitVersions + 1, &cReJitIds, rgReJitIds); if (SUCCEEDED(hr)) { // Go through rejitids. For each reverted one, populate a entry in rgRevertedRejitData @@ -1007,18 +1011,23 @@ HRESULT ClrDataAccess::GetMethodDescData( (i < cReJitIds) && (iRejitDataReverted < cRevertedRejitVersions); i++) { - ReJitInfo * pRejitInfo = pReJitMgr->FindReJitInfo( - pMD, - NULL /* pCodeStart */, - reJitIds[i]); + ILCodeVersion ilCodeVersion = pCodeVersionManager->GetILCodeVersion(pMD, reJitIds[i]); - if ((pRejitInfo == NULL) || - (pRejitInfo->m_pShared->GetState() != SharedReJitInfo::kStateReverted)) + if ((ilCodeVersion.IsNull()) || + (ilCodeVersion.GetRejitState() != ILCodeVersion::kStateReverted)) { continue; } - CopyReJitInfoToReJitData(pRejitInfo, &rgRevertedRejitData[iRejitDataReverted]); + NativeCodeVersionCollection nativeCodeVersions = ilCodeVersion.GetNativeCodeVersions(pMD); + for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++) + { + // This arbitrarily captures the first jitted version for this reverted IL version, but with + // tiered compilation there could be many such method bodies. Before tiered compilation is enabled in a broader set + // of scenarios we need to consider how this change goes all the way up to the UI - probably exposing the + // entire set of methods. + CopyNativeCodeVersionToReJitData(*iter, &rgRevertedRejitData[iRejitDataReverted]); + } iRejitDataReverted++; } // pcNeededRevertedRejitData != NULL as per condition at top of function (cuz rgRevertedRejitData != diff --git a/src/debug/di/module.cpp b/src/debug/di/module.cpp index 36cc6f5f9e96..5d1d3da4271e 100644 --- a/src/debug/di/module.cpp +++ b/src/debug/di/module.cpp @@ -3391,15 +3391,15 @@ mdSignature CordbILCode::GetLocalVarSigToken() return m_localVarSigToken; } -CordbReJitILCode::CordbReJitILCode(CordbFunction *pFunction, SIZE_T encVersion, VMPTR_SharedReJitInfo vmSharedReJitInfo) : -CordbILCode(pFunction, TargetBuffer(), encVersion, mdSignatureNil, VmPtrToCookie(vmSharedReJitInfo)), +CordbReJitILCode::CordbReJitILCode(CordbFunction *pFunction, SIZE_T encVersion, VMPTR_ILCodeVersionNode vmILCodeVersionNode) : +CordbILCode(pFunction, TargetBuffer(), encVersion, mdSignatureNil, VmPtrToCookie(vmILCodeVersionNode)), m_cClauses(0), m_cbLocalIL(0), m_cILMap(0) { - _ASSERTE(!vmSharedReJitInfo.IsNull()); + _ASSERTE(!vmILCodeVersionNode.IsNull()); DacSharedReJitInfo data = { 0 }; - IfFailThrow(GetProcess()->GetDAC()->GetSharedReJitInfoData(vmSharedReJitInfo, &data)); + IfFailThrow(GetProcess()->GetDAC()->GetILCodeVersionNodeData(vmILCodeVersionNode, &data)); IfFailThrow(Init(&data)); } diff --git a/src/debug/di/rsfunction.cpp b/src/debug/di/rsfunction.cpp index 8621edcedce0..bf3c49bb9879 100644 --- a/src/debug/di/rsfunction.cpp +++ b/src/debug/di/rsfunction.cpp @@ -557,14 +557,12 @@ HRESULT CordbFunction::GetActiveReJitRequestILCode(ICorDebugILCode **ppReJitedIL { *ppReJitedILCode = NULL; - VMPTR_ReJitInfo vmReJitInfo = VMPTR_ReJitInfo::NullPtr(); - GetProcess()->GetDAC()->GetReJitInfo(GetModule()->m_vmModule, m_MDToken, &vmReJitInfo); - if (!vmReJitInfo.IsNull()) + VMPTR_ILCodeVersionNode vmILCodeVersionNode = VMPTR_ILCodeVersionNode::NullPtr(); + GetProcess()->GetDAC()->GetActiveRejitILCodeVersionNode(GetModule()->m_vmModule, m_MDToken, &vmILCodeVersionNode); + if (!vmILCodeVersionNode.IsNull()) { - VMPTR_SharedReJitInfo vmSharedReJitInfo = VMPTR_SharedReJitInfo::NullPtr(); - GetProcess()->GetDAC()->GetSharedReJitInfo(vmReJitInfo, &vmSharedReJitInfo); RSSmartPtr pILCode; - IfFailThrow(LookupOrCreateReJitILCode(vmSharedReJitInfo, &pILCode)); + IfFailThrow(LookupOrCreateReJitILCode(vmILCodeVersionNode, &pILCode)); IfFailThrow(pILCode->QueryInterface(IID_ICorDebugILCode, (void**)ppReJitedILCode)); } } @@ -1165,21 +1163,21 @@ VOID CordbFunction::NotifyCodeCreated(CordbNativeCode* nativeCode) // If the CordbReJitILCode doesn't exist, it creates it. // // -HRESULT CordbFunction::LookupOrCreateReJitILCode(VMPTR_SharedReJitInfo vmSharedReJitInfo, CordbReJitILCode** ppILCode) +HRESULT CordbFunction::LookupOrCreateReJitILCode(VMPTR_ILCodeVersionNode vmILCodeVersionNode, CordbReJitILCode** ppILCode) { INTERNAL_API_ENTRY(this); HRESULT hr = S_OK; _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); - CordbReJitILCode * pILCode = m_reJitILCodes.GetBase(VmPtrToCookie(vmSharedReJitInfo)); + CordbReJitILCode * pILCode = m_reJitILCodes.GetBase(VmPtrToCookie(vmILCodeVersionNode)); // special case non-existance as need to add to the hash table too if (pILCode == NULL) { // we don't yet support ENC and ReJIT together, so the version should be 1 _ASSERTE(m_dwEnCVersionNumber == 1); - RSInitHolder pILCodeHolder(new CordbReJitILCode(this, 1, vmSharedReJitInfo)); + RSInitHolder pILCodeHolder(new CordbReJitILCode(this, 1, vmILCodeVersionNode)); IfFailRet(m_reJitILCodes.AddBase(pILCodeHolder)); pILCode = pILCodeHolder; pILCodeHolder.ClearAndMarkDontNeuter(); diff --git a/src/debug/di/rspriv.h b/src/debug/di/rspriv.h index 1abe08769356..e0489c53ad7f 100644 --- a/src/debug/di/rspriv.h +++ b/src/debug/di/rspriv.h @@ -5431,7 +5431,7 @@ class CordbFunction : public CordbBase, public ICorDebugFunction, public ICorDeb HRESULT GetILCode(CordbILCode ** ppCode); // Finds or creates an ILCode for a given rejit request - HRESULT LookupOrCreateReJitILCode(VMPTR_SharedReJitInfo vmSharedRejitInfo, + HRESULT LookupOrCreateReJitILCode(VMPTR_ILCodeVersionNode vmILCodeVersionNode, CordbReJitILCode** ppILCode); @@ -5775,7 +5775,7 @@ class CordbReJitILCode : public CordbILCode, public ICorDebugILCode, public ICor { public: // Initialize a new CordbILCode instance - CordbReJitILCode(CordbFunction *pFunction, SIZE_T encVersion, VMPTR_SharedReJitInfo vmSharedReJitInfo); + CordbReJitILCode(CordbFunction *pFunction, SIZE_T encVersion, VMPTR_ILCodeVersionNode vmILCodeVersionNode); //----------------------------------------------------------- // IUnknown diff --git a/src/debug/di/rsstackwalk.cpp b/src/debug/di/rsstackwalk.cpp index 8ade4c9a749c..466e113d8fa0 100644 --- a/src/debug/di/rsstackwalk.cpp +++ b/src/debug/di/rsstackwalk.cpp @@ -749,13 +749,16 @@ HRESULT CordbStackWalk::GetFrameWorker(ICorDebugFrame ** ppFrame) RSSmartPtr pReJitCode; EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY { - VMPTR_ReJitInfo reJitInfo = VMPTR_ReJitInfo::NullPtr(); - IfFailThrow(GetProcess()->GetDAC()->GetReJitInfo(pJITFuncData->vmNativeCodeMethodDescToken, pJITFuncData->nativeStartAddressPtr, &reJitInfo)); - if (!reJitInfo.IsNull()) + VMPTR_NativeCodeVersionNode vmNativeCodeVersionNode = VMPTR_NativeCodeVersionNode::NullPtr(); + IfFailThrow(GetProcess()->GetDAC()->GetNativeCodeVersionNode(pJITFuncData->vmNativeCodeMethodDescToken, pJITFuncData->nativeStartAddressPtr, &vmNativeCodeVersionNode)); + if (!vmNativeCodeVersionNode.IsNull()) { - VMPTR_SharedReJitInfo sharedReJitInfo = VMPTR_SharedReJitInfo::NullPtr(); - IfFailThrow(GetProcess()->GetDAC()->GetSharedReJitInfo(reJitInfo, &sharedReJitInfo)); - IfFailThrow(pFunction->LookupOrCreateReJitILCode(sharedReJitInfo, &pReJitCode)); + VMPTR_ILCodeVersionNode vmILCodeVersionNode = VMPTR_ILCodeVersionNode::NullPtr(); + IfFailThrow(GetProcess()->GetDAC()->GetILCodeVersionNode(vmNativeCodeVersionNode, &vmILCodeVersionNode)); + if (!vmILCodeVersionNode.IsNull()) + { + IfFailThrow(pFunction->LookupOrCreateReJitILCode(vmILCodeVersionNode, &pReJitCode)); + } } } EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY diff --git a/src/debug/inc/dacdbiinterface.h b/src/debug/inc/dacdbiinterface.h index 4077ad426a64..5e765c94f3a7 100644 --- a/src/debug/inc/dacdbiinterface.h +++ b/src/debug/inc/dacdbiinterface.h @@ -2532,6 +2532,7 @@ class IDacDbiInterface virtual HRESULT GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW) = 0; + // DEPRECATED - use GetActiveRejitILCodeVersionNode // Retrieves the active ReJitInfo for a given module/methodDef, if it exists. // Active is defined as after GetReJitParameters returns from the profiler dll and // no call to Revert has completed yet. @@ -2550,17 +2551,16 @@ class IDacDbiInterface virtual HRESULT GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pReJitInfo) = 0; - // Retrieves the active ReJitInfo for a given MethodDesc/code address, if it exists. - // Active is defined as after GetReJitParameters returns from the profiler dll and - // no call to Revert has completed yet. + // DEPRECATED - use GetNativeCodeVersionNode + // Retrieves the ReJitInfo for a given MethodDesc/code address, if it exists. // // // Arguments: // vmMethod - The method to look for // codeStartAddress - The code start address disambiguates between multiple rejitted instances // of the method. - // pReJitInfo - [out] The RejitInfo request, if any, that is active on this method. If no request - // is active this will be pReJitInfo->IsNull() == TRUE. + // pReJitInfo - [out] The RejitInfo request that corresponds to this MethodDesc/code address, if it exists. + // NULL otherwise. // // Returns: // S_OK regardless of whether a rejit request is active or not, as long as the answer is certain @@ -2569,7 +2569,7 @@ class IDacDbiInterface virtual HRESULT GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pReJitInfo) = 0; - + // DEPRECATED - use GetILCodeVersion // Retrieves the SharedReJitInfo for a given ReJitInfo. // // @@ -2584,6 +2584,7 @@ class IDacDbiInterface virtual HRESULT GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, VMPTR_SharedReJitInfo* pSharedReJitInfo) = 0; + // DEPRECATED - use GetILCodeVersionData // Retrieves useful data from a SharedReJitInfo such as IL code and IL mapping. // // @@ -2630,6 +2631,71 @@ class IDacDbiInterface virtual HRESULT GetMDStructuresVersion(ULONG32* pMDStructuresVersion) = 0; + // Retrieves the active rejit ILCodeVersionNode for a given module/methodDef, if it exists. + // Active is defined as after GetReJitParameters returns from the profiler dll and + // no call to Revert has completed yet. + // + // + // Arguments: + // vmModule - The module to search in + // methodTk - The methodDef token indicates the method within the module to check + // pILCodeVersionNode - [out] The Rejit request, if any, that is active on this method. If no request + // is active this will be pILCodeVersionNode->IsNull() == TRUE. + // + // Returns: + // S_OK regardless of whether a rejit request is active or not, as long as the answer is certain + // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible + // + virtual + HRESULT GetActiveRejitILCodeVersionNode(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ILCodeVersionNode* pVmILCodeVersionNode) = 0; + + // Retrieves the NativeCodeVersionNode for a given MethodDesc/code address, if it exists. + // NOTE: The initial (default) code generated for a MethodDesc is a valid MethodDesc/code address pair but it won't have a corresponding + // NativeCodeVersionNode. + // + // + // Arguments: + // vmMethod - The method to look for + // codeStartAddress - The code start address disambiguates between multiple jitted instances of the method. + // pVmNativeCodeVersionNode - [out] The NativeCodeVersionNode request that corresponds to this MethodDesc/code address, if it exists. + // NULL otherwise. + // + // Returns: + // S_OK regardless of whether a rejit request is active or not, as long as the answer is certain + // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible + // + virtual + HRESULT GetNativeCodeVersionNode(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_NativeCodeVersionNode* pVmNativeCodeVersionNode) = 0; + + // Retrieves the ILCodeVersionNode for a given NativeCodeVersionNode. + // This may return a NULL node if the native code belongs to the default IL version for this this method. + // + // + // Arguments: + // vmNativeCodeVersionNode - The NativeCodeVersionNode to inspect + // pVmILCodeVersionNode - [out] The ILCodeVersionNode that is pointed to by vmNativeCodeVersionNode, if any. + // + // Returns: + // S_OK if no error + // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible + // + virtual + HRESULT GetILCodeVersionNode(VMPTR_NativeCodeVersionNode vmNativeCodeVersionNode, VMPTR_ILCodeVersionNode* pVmILCodeVersionNode) = 0; + + // Retrieves useful data from an ILCodeVersion such as IL code and IL mapping. + // + // + // Arguments: + // ilCodeVersionNode - The ILCodeVersionNode to inspect + // pData - [out] Various properties of the ILCodeVersionNode such as IL code and IL mapping. + // + // Returns: + // S_OK if no error + // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible + // + virtual + HRESULT GetILCodeVersionNodeData(VMPTR_ILCodeVersionNode ilCodeVersionNode, DacSharedReJitInfo* pData) = 0; + // The following tag tells the DD-marshalling tool to stop scanning. // END_MARSHAL diff --git a/src/debug/inc/dbgipcevents.h b/src/debug/inc/dbgipcevents.h index 1b4dec7c1621..dc900660c315 100644 --- a/src/debug/inc/dbgipcevents.h +++ b/src/debug/inc/dbgipcevents.h @@ -879,7 +879,8 @@ DEFINE_VMPTR(class SimpleRWLock, PTR_SimpleRWLock, VMPTR_SimpleRWLock); DEFINE_VMPTR(class SimpleRWLock, PTR_SimpleRWLock, VMPTR_RWLock); DEFINE_VMPTR(struct ReJitInfo, PTR_ReJitInfo, VMPTR_ReJitInfo); DEFINE_VMPTR(struct SharedReJitInfo, PTR_SharedReJitInfo, VMPTR_SharedReJitInfo); - +DEFINE_VMPTR(class NativeCodeVersionNode, PTR_NativeCodeVersionNode, VMPTR_NativeCodeVersionNode); +DEFINE_VMPTR(class ILCodeVersionNode, PTR_ILCodeVersionNode, VMPTR_ILCodeVersionNode); typedef CORDB_ADDRESS GENERICS_TYPE_TOKEN; diff --git a/src/inc/shash.h b/src/inc/shash.h index cece2dd34563..daddc4cfb41b 100644 --- a/src/inc/shash.h +++ b/src/inc/shash.h @@ -327,6 +327,14 @@ class SHash : public TRAITS count_t m_tableSize; count_t m_index; + Index() + : m_table(dac_cast(NULL)), + m_tableSize(0), + m_index(0) + { + LIMITED_METHOD_CONTRACT; + } + Index(const SHash *hash, BOOL begin) : m_table(hash->m_table), m_tableSize(hash->m_tableSize), @@ -386,6 +394,10 @@ class SHash : public TRAITS friend class SHash; public: + Iterator() + { + } + Iterator(const SHash *hash, BOOL begin) : Index(hash, begin) { @@ -410,6 +422,13 @@ class SHash : public TRAITS key_t m_key; count_t m_increment; + KeyIndex() : + Index(), + m_increment(0) + { + LIMITED_METHOD_CONTRACT; + } + KeyIndex(const SHash *hash, BOOL begin) : Index(hash, begin), m_increment(0) @@ -479,6 +498,10 @@ class SHash : public TRAITS return *(const Iterator*)this; } + KeyIterator() + { + } + KeyIterator(const SHash *hash, BOOL begin) : KeyIndex(hash, begin) { diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp index 7468f0b4e2e1..e57a452273a7 100644 --- a/src/vm/appdomain.cpp +++ b/src/vm/appdomain.cpp @@ -758,8 +758,8 @@ BaseDomain::BaseDomain() m_ClassInitLock.PreInit(); m_ILStubGenLock.PreInit(); -#ifdef FEATURE_REJIT - m_reJitMgr.PreInit(this == (BaseDomain *) g_pSharedDomainMemory); +#ifdef FEATURE_CODE_VERSIONING + m_codeVersionManager.PreInit(this == (BaseDomain *)g_pSharedDomainMemory); #endif } //BaseDomain::BaseDomain @@ -8151,7 +8151,7 @@ void AppDomain::Exit(BOOL fRunFinalizers, BOOL fAsyncExit) LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Domain [%d] %#08x %ls is exited.\n", GetId().m_dwId, this, GetFriendlyNameForLogging())); - ReJitManager::OnAppDomainExit(this); + CodeVersionManager::OnAppDomainExit(this); // Send ETW events for this domain's unload and potentially iterate through this // domain's modules & assemblies to send events for their unloads as well. This diff --git a/src/vm/ceeload.h b/src/vm/ceeload.h index f626a46f43b4..27ee590fd1a7 100644 --- a/src/vm/ceeload.h +++ b/src/vm/ceeload.h @@ -80,6 +80,7 @@ struct MethodContextElement; class TypeHandleList; class ProfileEmitter; class ReJitManager; +class CodeVersionManager; class TrackingMap; struct MethodInModule; class PersistentInlineTrackingMapNGen; @@ -1786,6 +1787,9 @@ class Module ClassLoader *GetClassLoader(); PTR_BaseDomain GetDomain(); ReJitManager * GetReJitManager(); +#ifdef FEATURE_CODE_VERSIONING + CodeVersionManager * GetCodeVersionManager(); +#endif mdFile GetModuleRef() { diff --git a/src/vm/ceeload.inl b/src/vm/ceeload.inl index 8226dce7d733..f9e98bcc1adb 100644 --- a/src/vm/ceeload.inl +++ b/src/vm/ceeload.inl @@ -662,4 +662,12 @@ inline ReJitManager * Module::GetReJitManager() return GetDomain()->GetReJitManager(); } +#ifdef FEATURE_CODE_VERSIONING +inline CodeVersionManager * Module::GetCodeVersionManager() +{ + LIMITED_METHOD_CONTRACT; + return GetDomain()->GetCodeVersionManager(); +} +#endif // FEATURE_CODE_VERSIONING + #endif // CEELOAD_INL_ diff --git a/src/vm/codeversion.cpp b/src/vm/codeversion.cpp index e04fb711968d..67c73042524b 100644 --- a/src/vm/codeversion.cpp +++ b/src/vm/codeversion.cpp @@ -6,20 +6,1361 @@ // // =========================================================================== +#include "common.h" +#ifdef FEATURE_CODE_VERSIONING -#include "common.h" #include "codeversion.h" +#include "../debug/ee/debugger.h" +#include "../debug/ee/walker.h" +#include "../debug/ee/controller.h" +// This HRESULT is only used as a private implementation detail. If it escapes functions +// defined in this file it is a bug. Corerror.xml has a comment in it reserving this +// value for our use but it doesn't appear in the public headers. +#define CORPROF_E_RUNTIME_SUSPEND_REQUIRED 0x80131381 -#ifdef FEATURE_CODE_VERSIONING #ifndef DACCESS_COMPILE -// Called at AppDomain construction +NativeCodeVersionNode::NativeCodeVersionNode(NativeCodeVersionId id, MethodDesc* pMethodDesc, ReJITID parentId) : + m_id(id), + m_pMethodDesc(pMethodDesc), + m_parentId(parentId), + m_pNextILVersionSibling(NULL), + m_pNativeCode(NULL) +{} +#endif + +PTR_MethodDesc NativeCodeVersionNode::GetMethodDesc() const +{ + return m_pMethodDesc; +} + +PCODE NativeCodeVersionNode::GetNativeCode() const +{ + return m_pNativeCode; +} + +ReJITID NativeCodeVersionNode::GetILVersionId() const +{ + return m_parentId; +} + +ILCodeVersion NativeCodeVersionNode::GetILCodeVersion() const +{ + PTR_MethodDesc pMD = GetMethodDesc(); + return pMD->GetCodeVersionManager()->GetILCodeVersion(pMD, GetILVersionId()); +} + +#ifndef DACCESS_COMPILE +BOOL NativeCodeVersionNode::SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected) +{ + return FastInterlockCompareExchangePointer(&m_pNativeCode, + (TADDR&)pCode, (TADDR&)pExpected) == (TADDR&)pExpected; +} +#endif + +NativeCodeVersion::NativeCodeVersion() : + m_storageKind(StorageKind::Unknown) +{} + +NativeCodeVersion::NativeCodeVersion(const NativeCodeVersion & rhs) : + m_storageKind(rhs.m_storageKind), + m_pVersionNode(rhs.m_pVersionNode) +{} + +NativeCodeVersion::NativeCodeVersion(PTR_NativeCodeVersionNode pVersionNode) : + m_storageKind(pVersionNode != NULL ? StorageKind::Explicit : StorageKind::Unknown), + m_pVersionNode(pVersionNode) +{} + +BOOL NativeCodeVersion::IsNull() const +{ + return m_storageKind == StorageKind::Unknown; +} + +PTR_MethodDesc NativeCodeVersion::GetMethodDesc() const +{ + return AsNode()->GetMethodDesc(); +} + +PCODE NativeCodeVersion::GetNativeCode() const +{ + return AsNode()->GetNativeCode(); +} + +ILCodeVersion NativeCodeVersion::GetILCodeVersion() const +{ + return AsNode()->GetILCodeVersion(); +} + +#ifndef DACCESS_COMPILE +BOOL NativeCodeVersion::SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected) +{ + return AsNode()->SetNativeCodeInterlocked(pCode, pExpected); +} +#endif + +PTR_NativeCodeVersionNode NativeCodeVersion::AsNode() const +{ + return m_pVersionNode; +} + +#ifndef DACCESS_COMPILE +PTR_NativeCodeVersionNode NativeCodeVersion::AsNode() +{ + return m_pVersionNode; +} +#endif + +bool NativeCodeVersion::operator==(const NativeCodeVersion & rhs) const +{ + if (m_storageKind == StorageKind::Explicit) + { + return (rhs.m_storageKind == StorageKind::Explicit) && + (rhs.AsNode() == AsNode()); + } + else + { + return rhs.m_storageKind == StorageKind::Unknown; + } +} +bool NativeCodeVersion::operator!=(const NativeCodeVersion & rhs) const +{ + return !operator==(rhs); +} + +NativeCodeVersionCollection::NativeCodeVersionCollection() +{ + //TODO +} + +NativeCodeVersionIterator NativeCodeVersionCollection::Begin() +{ + return NativeCodeVersionIterator(this); +} +NativeCodeVersionIterator NativeCodeVersionCollection::End() +{ + return NativeCodeVersionIterator(NULL); +} + +NativeCodeVersionIterator::NativeCodeVersionIterator(NativeCodeVersionCollection* pNativeCodeVersionCollection) +{ + //TODO +} +void NativeCodeVersionIterator::First() +{ + //TODO +} +void NativeCodeVersionIterator::Next() +{ + //TODO +} +const NativeCodeVersion & NativeCodeVersionIterator::Get() const +{ + return m_cur; +} +bool NativeCodeVersionIterator::Equal(const NativeCodeVersionIterator &i) const +{ + return m_cur == i.m_cur; +} + +ILCodeVersionNode::ILCodeVersionNode() : + m_pModule((TADDR)NULL), + m_methodDef(0), + m_rejitId(0), + m_pFirstChild((TADDR)NULL), + m_rejitState(ILCodeVersion::kStateRequested), + m_pIL((TADDR)NULL), + m_jitFlags(0) +{} + +#ifndef DACCESS_COMPILE +ILCodeVersionNode::ILCodeVersionNode(Module* pModule, mdMethodDef methodDef, ReJITID id) : + m_pModule(pModule), + m_methodDef(methodDef), + m_rejitId(id), + m_pFirstChild(NULL), + m_rejitState(ILCodeVersion::kStateRequested), + m_pIL(NULL), + m_jitFlags(0) +{} +#endif + +PTR_Module ILCodeVersionNode::GetModule() +{ + return m_pModule; +} + +mdMethodDef ILCodeVersionNode::GetMethodDef() +{ + return m_methodDef; +} + +ReJITID ILCodeVersionNode::GetVersionId() +{ + return m_rejitId; +} + +PTR_NativeCodeVersionNode ILCodeVersionNode::GetActiveNativeCodeVersion(PTR_MethodDesc pClosedMethodDesc) +{ + //TODO: this doesn't handle generics or tiered compilation multiple child versions yet + return m_pFirstChild; +} + +ILCodeVersion::RejitFlags ILCodeVersionNode::GetRejitState() const +{ + return m_rejitState; +} + +PTR_COR_ILMETHOD ILCodeVersionNode::GetIL() const +{ + return m_pIL; +} + +DWORD ILCodeVersionNode::GetJitFlags() const +{ + return m_jitFlags; +} + +const InstrumentedILOffsetMapping* ILCodeVersionNode::GetInstrumentedILMap() const +{ + return &m_instrumentedILMap; +} + +#ifndef DACCESS_COMPILE +void ILCodeVersionNode::SetRejitState(ILCodeVersion::RejitFlags newState) +{ + m_rejitState = newState; +} + +void ILCodeVersionNode::SetIL(COR_ILMETHOD* pIL) +{ + m_pIL = pIL; +} + +void ILCodeVersionNode::SetJitFlags(DWORD flags) +{ + m_jitFlags = flags; +} + +void ILCodeVersionNode::SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap) +{ + m_instrumentedILMap.SetMappingInfo(cMap, rgMap); +} +#endif + +ILCodeVersionNode::Key::Key() : + m_pModule(dac_cast(NULL)), + m_methodDef(0) +{} + +ILCodeVersionNode::Key::Key(PTR_Module pModule, mdMethodDef methodDef) : + m_pModule(pModule), + m_methodDef(methodDef) +{} + + + +size_t ILCodeVersionNode::Key::Hash() const +{ + return (size_t)(dac_cast(m_pModule) ^ m_methodDef); +} + +bool ILCodeVersionNode::Key::operator==(const Key & rhs) const +{ + return (m_pModule == rhs.m_pModule) && (m_methodDef == rhs.m_methodDef); +} + +ILCodeVersionNode::Key ILCodeVersionNode::GetKey() const +{ + return Key(m_pModule, m_methodDef); +} + +#ifndef DACCESS_COMPILE +void ILCodeVersionNode::LinkNativeCodeNode(NativeCodeVersionNode* pNativeCodeVersionNode) +{ + if (m_pFirstChild == NULL) + { + m_pFirstChild = pNativeCodeVersionNode; + return; + } + NativeCodeVersionNode* pCur = m_pFirstChild; + while (pCur->m_pNextILVersionSibling != NULL) + { + pCur = pCur->m_pNextILVersionSibling; + } + pCur->m_pNextILVersionSibling = pNativeCodeVersionNode; +} +#endif + +ILCodeVersion::ILCodeVersion() : + m_storageKind(StorageKind::Unknown) +{} + +ILCodeVersion::ILCodeVersion(const ILCodeVersion & ilCodeVersion) : + m_storageKind(ilCodeVersion.m_storageKind), + m_pVersionNode(ilCodeVersion.m_pVersionNode) +{} + +ILCodeVersion::ILCodeVersion(PTR_ILCodeVersionNode pILCodeVersionNode) : + m_storageKind(pILCodeVersionNode != NULL ? StorageKind::Explicit : StorageKind::Unknown), + m_pVersionNode(pILCodeVersionNode) +{} + +bool ILCodeVersion::operator==(const ILCodeVersion & rhs) const +{ + if (m_storageKind == StorageKind::Explicit) + { + return rhs.m_storageKind == StorageKind::Explicit && + AsNode() == rhs.AsNode(); + } + else + { + return rhs.m_storageKind == StorageKind::Unknown; + } +} + +BOOL ILCodeVersion::IsNull() const +{ + return m_storageKind == StorageKind::Unknown; +} + +PTR_Module ILCodeVersion::GetModule() +{ + return AsNode()->GetModule(); +} + +mdMethodDef ILCodeVersion::GetMethodDef() +{ + return AsNode()->GetMethodDef(); +} + +ReJITID ILCodeVersion::GetVersionId() +{ + return AsNode()->GetVersionId(); +} + +NativeCodeVersionCollection ILCodeVersion::GetNativeCodeVersions(PTR_MethodDesc pClosedMethodDesc) +{ + //TODO + return NativeCodeVersionCollection(); +} + +NativeCodeVersion ILCodeVersion::GetActiveNativeCodeVersion(PTR_MethodDesc pClosedMethodDesc) +{ + return NativeCodeVersion(AsNode()->GetActiveNativeCodeVersion(pClosedMethodDesc)); +} + +ILCodeVersion::RejitFlags ILCodeVersion::GetRejitState() const +{ + return AsNode()->GetRejitState(); +} + +PTR_COR_ILMETHOD ILCodeVersion::GetIL() const +{ + return AsNode()->GetIL(); +} + +DWORD ILCodeVersion::GetJitFlags() const +{ + return AsNode()->GetJitFlags(); +} + +const InstrumentedILOffsetMapping* ILCodeVersion::GetInstrumentedILMap() const +{ + return AsNode()->GetInstrumentedILMap(); +} + +#ifndef DACCESS_COMPILE +void ILCodeVersion::SetRejitState(RejitFlags newState) +{ + AsNode()->SetRejitState(newState); +} + +void ILCodeVersion::SetIL(COR_ILMETHOD* pIL) +{ + AsNode()->SetIL(pIL); +} + +void ILCodeVersion::SetJitFlags(DWORD flags) +{ + AsNode()->SetJitFlags(flags); +} + +void ILCodeVersion::SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap) +{ + AsNode()->SetInstrumentedILMap(cMap, rgMap); +} + +HRESULT ILCodeVersion::AddNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion) +{ + CodeVersionManager* pManager = GetModule()->GetCodeVersionManager(); + return pManager->AddNativeCodeVersion(*this, pClosedMethodDesc, pNativeCodeVersion); +} +#endif + +#ifndef DACCESS_COMPILE +void ILCodeVersion::LinkNativeCodeNode(NativeCodeVersionNode* pNativeCodeVersionNode) +{ + return AsNode()->LinkNativeCodeNode(pNativeCodeVersionNode); +} +#endif + +#ifndef DACCESS_COMPILE +ILCodeVersionNode* ILCodeVersion::AsNode() +{ + return m_pVersionNode; +} +#endif + +PTR_ILCodeVersionNode ILCodeVersion::AsNode() const +{ + return m_pVersionNode; +} + +ILCodeVersionCollection::ILCodeVersionCollection(PTR_Module pModule, mdMethodDef methodDef) : + m_pModule(pModule), + m_methodDef(methodDef) +{} + +ILCodeVersionIterator ILCodeVersionCollection::Begin() +{ + return m_pModule->GetCodeVersionManager()->GetILCodeVersionIterator(m_pModule, m_methodDef); +} + +ILCodeVersionIterator ILCodeVersionCollection::End() +{ + return ILCodeVersionIterator(); +} + +ILCodeVersionIterator::ILCodeVersionIterator() +{ +} + +ILCodeVersionIterator::ILCodeVersionIterator(const ILCodeVersionIterator & iter) : + m_tableCurIter(iter.m_tableCurIter), + m_tableEndIter(iter.m_tableEndIter), + m_cur(iter.m_cur) +{ +} + +ILCodeVersionIterator::ILCodeVersionIterator(ILCodeVersionNodeHash::KeyIterator tableStartIter, ILCodeVersionNodeHash::KeyIterator tableEndIter) : + m_tableCurIter(tableStartIter), + m_tableEndIter(tableEndIter) +{ + First(); +} + +const ILCodeVersion & ILCodeVersionIterator::Get() const +{ + return m_cur; +} + +void ILCodeVersionIterator::First() +{ + if (m_tableCurIter != m_tableEndIter) + { + m_cur = ILCodeVersion(*m_tableCurIter); + } + else + { + m_cur = ILCodeVersion(); + } +} + +void ILCodeVersionIterator::Next() +{ + m_tableCurIter++; + if (m_tableCurIter != m_tableEndIter) + { + m_cur = ILCodeVersion(*m_tableCurIter); + } + else + { + m_cur = ILCodeVersion(); + } +} + +bool ILCodeVersionIterator::Equal(const ILCodeVersionIterator &i) const +{ + return m_cur == i.m_cur; +} + +MethodDescVersioningState::MethodDescVersioningState(PTR_MethodDesc pMethodDesc) : + m_pMethodDesc(pMethodDesc), + m_nextId(1), + m_flags(0) +{ + ZeroMemory(m_rgSavedCode, JumpStubSize); +} + +PTR_MethodDesc MethodDescVersioningState::GetMethodDesc() const +{ + return m_pMethodDesc; +} + +NativeCodeVersionId MethodDescVersioningState::AllocateVersionId() +{ + return m_nextId++; +} + +MethodDescVersioningState::JumpStampFlags MethodDescVersioningState::GetJumpStampState() +{ + return (JumpStampFlags)m_flags; +} + +void MethodDescVersioningState::SetJumpStampState(JumpStampFlags newState) +{ + m_flags = (BYTE)newState; +} + + +//--------------------------------------------------------------------------------------- +// +// Simple, thin abstraction of debugger breakpoint patching. Given an address and a +// previously procured DebuggerControllerPatch governing the code address, this decides +// whether the code address is patched. If so, it returns a pointer to the debugger's +// buffer (of what's "underneath" the int 3 patch); otherwise, it returns the code +// address itself. +// +// Arguments: +// * pbCode - Code address to return if unpatched +// * dbgpatch - DebuggerControllerPatch to test +// +// Return Value: +// Either pbCode or the debugger's patch buffer, as per description above. +// +// Assumptions: +// Caller must manually grab (and hold) the ControllerLockHolder and get the +// DebuggerControllerPatch before calling this helper. +// +// Notes: +// pbCode need not equal the code address governed by dbgpatch, but is always +// "related" (and sometimes really is equal). For example, this helper may be used +// when writing a code byte to an internal rejit buffer (e.g., in preparation for an +// eventual 64-bit interlocked write into the code stream), and thus pbCode would +// point into the internal rejit buffer whereas dbgpatch governs the corresponding +// code byte in the live code stream. This function would then be used to determine +// whether a byte should be written into the internal rejit buffer OR into the +// debugger controller's breakpoint buffer. +// + +LPBYTE FirstCodeByteAddr(LPBYTE pbCode, DebuggerControllerPatch * dbgpatch) +{ + LIMITED_METHOD_CONTRACT; + + if (dbgpatch != NULL && dbgpatch->IsActivated()) + { + // Debugger has patched the code, so return the address of the buffer + return LPBYTE(&(dbgpatch->opcode)); + } + + // no active patch, just return the direct code address + return pbCode; +} + + +#ifdef _DEBUG +BOOL MethodDescVersioningState::CodeIsSaved() +{ + LIMITED_METHOD_CONTRACT; + + for (size_t i = 0; i < sizeof(m_rgSavedCode); i++) + { + if (m_rgSavedCode[i] != 0) + return TRUE; + } + return FALSE; +} +#endif //_DEBUG + +//--------------------------------------------------------------------------------------- +// +// Do the actual work of stamping the top of originally-jitted-code with a jmp that goes +// to the prestub. This can be called in one of three ways: +// * Case 1: By RequestReJIT against an already-jitted function, in which case the +// PCODE may be inferred by the MethodDesc, and our caller will have suspended +// the EE for us, OR +// * Case 2: By the prestub worker after jitting the original code of a function +// (i.e., the "pre-rejit" scenario). In this case, the EE is not suspended. But +// that's ok, because the PCODE has not yet been published to the MethodDesc, and +// no thread can be executing inside the originally JITted function yet. +// * Case 3: At type/method restore time for an NGEN'ed assembly. This is also the pre-rejit +// scenario because we are guaranteed to do this before the code in the module +// is executable. EE suspend is not required. +// +// Arguments: +// * pCode - Case 1 (above): will be NULL, and we can infer the PCODE from the +// MethodDesc; Case 2+3 (above, pre-rejit): will be non-NULL, and we'll need to use +// this to find the code to stamp on top of. +// +// Return Value: +// * S_OK: Either we successfully did the jmp-stamp, or a racing thread took care of +// it for us. +// * Else, HRESULT indicating failure. +// +// Assumptions: +// The caller will have suspended the EE if necessary (case 1), before this is +// called. +// +#ifndef DACCESS_COMPILE +HRESULT MethodDescVersioningState::JumpStampNativeCode(PCODE pCode /* = NULL */) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + // It may seem dangerous to be stamping jumps over code while a GC is going on, + // but we're actually safe. As we assert below, either we're holding the thread + // store lock (and thus preventing a GC) OR we're stamping code that has not yet + // been published (and will thus not be executed by managed therads or examined + // by the GC). + MODE_ANY; + } + CONTRACTL_END; + + PCODE pCodePublished = GetMethodDesc()->GetNativeCode(); + + _ASSERTE((pCode != NULL) || (pCodePublished != NULL)); + _ASSERTE(GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread()); + + HRESULT hr = S_OK; + + // We'll jump-stamp over pCode, or if pCode is NULL, jump-stamp over the published + // code for this's MethodDesc. + LPBYTE pbCode = (LPBYTE)pCode; + if (pbCode == NULL) + { + // If caller didn't specify a pCode, just use the one that was published after + // the original JIT. (A specific pCode would be passed in the pre-rejit case, + // to jump-stamp the original code BEFORE the PCODE gets published.) + pbCode = (LPBYTE)pCodePublished; + } + _ASSERTE(pbCode != NULL); + + // The debugging API may also try to write to the very top of this function (though + // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know + // whether we can safely patch the actual code, or instead write to the debugger's + // buffer. + DebuggerController::ControllerLockHolder lockController; + + // We could be in a race. Either two threads simultaneously JITting the same + // method for the first time or two threads restoring NGEN'ed code. + // Another thread may (or may not) have jump-stamped its copy of the code already + _ASSERTE((GetJumpStampState() == JumpStampNone) || (GetJumpStampState() == JumpStampToPrestub)); + + if (GetJumpStampState() == JumpStampToPrestub) + { + // The method has already been jump stamped so nothing left to do + _ASSERTE(CodeIsSaved()); + return S_OK; + } + + // Remember what we're stamping our jump on top of, so we can replace it during a + // revert. + for (int i = 0; i < sizeof(m_rgSavedCode); i++) + { + m_rgSavedCode[i] = *FirstCodeByteAddr(pbCode + i, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)(pbCode + i))); + } + + EX_TRY + { + AllocMemTracker amt; + + // This guy might throw on out-of-memory, so rely on the tracker to clean-up + Precode * pPrecode = Precode::Allocate(PRECODE_STUB, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator(), &amt); + PCODE target = pPrecode->GetEntryPoint(); + +#if defined(_X86_) || defined(_AMD64_) + + // Normal unpatched code never starts with a jump + // so make sure this code isn't already patched + _ASSERTE(*FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) != X86_INSTR_JMP_REL32); + + INT64 i64OldCode = *(INT64*)pbCode; + INT64 i64NewCode = i64OldCode; + LPBYTE pbNewValue = (LPBYTE)&i64NewCode; + *pbNewValue = X86_INSTR_JMP_REL32; + INT32 UNALIGNED * pOffset = reinterpret_cast(&pbNewValue[1]); + // This will throw for out-of-memory, so don't write anything until + // after he succeeds + // This guy will leak/cache/reuse the jumpstub + *pOffset = rel32UsingJumpStub(reinterpret_cast(pbCode + 1), target, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator()); + + // If we have the EE suspended or the code is unpublished there won't be contention on this code + hr = UpdateJumpStampHelper(pbCode, i64OldCode, i64NewCode, FALSE); + if (FAILED(hr)) + { + ThrowHR(hr); + } + + // + // No failure point after this! + // + amt.SuppressRelease(); + +#else // _X86_ || _AMD64_ +#error "Need to define a way to jump-stamp the prolog in a safe way for this platform" +#endif // _X86_ || _AMD64_ + + SetJumpStampState(JumpStampToPrestub); + } + EX_CATCH_HRESULT(hr); + _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY); + + if (SUCCEEDED(hr)) + { + _ASSERTE(GetJumpStampState() == JumpStampToPrestub); + _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0 + } + + return hr; +} + + +//--------------------------------------------------------------------------------------- +// +// After code has been rejitted, this is called to update the jump-stamp to go from +// pointing to the prestub, to pointing to the newly rejitted code. +// +// Arguments: +// fEESuspended - TRUE if the caller keeps the EE suspended during this call +// pRejittedCode - jitted code for the updated IL this method should execute +// +// Assumptions: +// This rejit manager's table crst should be held by the caller +// +// Returns - S_OK if the jump target is updated +// CORPROF_E_RUNTIME_SUSPEND_REQUIRED if the ee isn't suspended and it +// will need to be in order to do the update safely +HRESULT MethodDescVersioningState::UpdateJumpTarget(BOOL fEESuspended, PCODE pRejittedCode) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + + MethodDesc * pMD = GetMethodDesc(); + _ASSERTE(pMD->GetCodeVersionManager()->LockOwnedByCurrentThread()); + _ASSERTE(GetJumpStampState() == JumpStampToPrestub); + + // Beginning of originally JITted code containing the jmp that we will redirect. + BYTE * pbCode = (BYTE*)pMD->GetNativeCode(); + +#if defined(_X86_) || defined(_AMD64_) + + HRESULT hr = S_OK; + { + DebuggerController::ControllerLockHolder lockController; + + // This will throw for out-of-memory, so don't write anything until + // after he succeeds + // This guy will leak/cache/reuse the jumpstub + INT32 offset = 0; + EX_TRY + { + offset = rel32UsingJumpStub( + reinterpret_cast(&pbCode[1]), // base of offset + pRejittedCode, // target of jump + pMD, + pMD->GetLoaderAllocator()); + } + EX_CATCH_HRESULT(hr); + _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY); + if (FAILED(hr)) + { + return hr; + } + // For validation later, remember what pbCode is right now + INT64 i64OldValue = *(INT64 *)pbCode; + + // Assemble the INT64 of the new code bytes to write. Start with what's there now + INT64 i64NewValue = i64OldValue; + LPBYTE pbNewValue = (LPBYTE)&i64NewValue; + + // First byte becomes a rel32 jmp instruction (should be a no-op as asserted + // above, but can't hurt) + *pbNewValue = X86_INSTR_JMP_REL32; + // Next 4 bytes are the jmp target (offset to jmp stub) + INT32 UNALIGNED * pnOffset = reinterpret_cast(&pbNewValue[1]); + *pnOffset = offset; + + hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended); + _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended)); + } + if (FAILED(hr)) + { + return hr; + } + +#else // _X86_ || _AMD64_ +#error "Need to define a way to jump-stamp the prolog in a safe way for this platform" +#endif // _X86_ || _AMD64_ + + // State transition + SetJumpStampState(JumpStampToActiveVersion); + return S_OK; +} + + +//--------------------------------------------------------------------------------------- +// +// Poke the JITted code to satsify a revert request (or to perform an implicit revert as +// part of a second, third, etc. rejit request). Reinstates the originally JITted code +// that had been jump-stamped over to perform a prior rejit. +// +// Arguments +// fEESuspended - TRUE if the caller keeps the EE suspended during this call +// +// +// Return Value: +// S_OK to indicate the revert succeeded, +// CORPROF_E_RUNTIME_SUSPEND_REQUIRED to indicate the jumpstamp hasn't been reverted +// and EE suspension will be needed for success +// other failure HRESULT indicating what went wrong. +// +// Assumptions: +// Caller must be holding the owning ReJitManager's table crst. +// +HRESULT MethodDescVersioningState::UndoJumpStampNativeCode(BOOL fEESuspended) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + _ASSERTE(GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread()); + _ASSERTE((GetJumpStampState() == JumpStampToPrestub) || (GetJumpStampState() == JumpStampToActiveVersion)); + _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0 (see above test) + + BYTE * pbCode = (BYTE*)GetMethodDesc()->GetNativeCode(); + DebuggerController::ControllerLockHolder lockController; + +#if defined(_X86_) || defined(_AMD64_) + _ASSERTE(m_rgSavedCode[0] != X86_INSTR_JMP_REL32); + _ASSERTE(*FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) == X86_INSTR_JMP_REL32); +#else +#error "Need to define a way to jump-stamp the prolog in a safe way for this platform" +#endif // _X86_ || _AMD64_ + + // For the interlocked compare, remember what pbCode is right now + INT64 i64OldValue = *(INT64 *)pbCode; + // Assemble the INT64 of the new code bytes to write. Start with what's there now + INT64 i64NewValue = i64OldValue; + memcpy(LPBYTE(&i64NewValue), m_rgSavedCode, sizeof(m_rgSavedCode)); + HRESULT hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended); + _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended)); + if (hr != S_OK) + return hr; + + // Transition state of this ReJitInfo to indicate the MD no longer has any jump stamp + SetJumpStampState(JumpStampNone); + return S_OK; +} +#endif + +//--------------------------------------------------------------------------------------- +// +// This is called to modify the jump-stamp area, the first ReJitInfo::JumpStubSize bytes +// in the method's code. +// +// Notes: +// Callers use this method in a variety of circumstances: +// a) when the code is unpublished (fContentionPossible == FALSE) +// b) when the caller has taken the ThreadStoreLock and suspended the EE +// (fContentionPossible == FALSE) +// c) when the code is published, the EE isn't suspended, and the jumpstamp +// area consists of a single 5 byte long jump instruction +// (fContentionPossible == TRUE) +// This method will attempt to alter the jump-stamp even if the caller has not prevented +// contention, but there is no guarantee it will be succesful. When the caller has prevented +// contention, then success is assured. Callers may oportunistically try without +// EE suspension, and then upgrade to EE suspension if the first attempt fails. +// +// Assumptions: +// This rejit manager's table crst should be held by the caller or fContentionPossible==FALSE +// The debugger patch table lock should be held by the caller +// +// Arguments: +// pbCode - pointer to the code where the jump stamp is placed +// i64OldValue - the bytes which should currently be at the start of the method code +// i64NewValue - the new bytes which should be written at the start of the method code +// fContentionPossible - See the Notes section above. +// +// Returns: +// S_OK => the jumpstamp has been succesfully updated. +// CORPROF_E_RUNTIME_SUSPEND_REQUIRED => the jumpstamp remains unchanged (preventing contention will be necessary) +// other failing HR => VirtualProtect failed, the jumpstamp remains unchanged +// +#ifndef DACCESS_COMPILE +HRESULT MethodDescVersioningState::UpdateJumpStampHelper(BYTE* pbCode, INT64 i64OldValue, INT64 i64NewValue, BOOL fContentionPossible) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + MethodDesc * pMD = GetMethodDesc(); + _ASSERTE(pMD->GetCodeVersionManager()->LockOwnedByCurrentThread() || !fContentionPossible); + + // When ReJIT is enabled, method entrypoints are always at least 8-byte aligned (see + // code:EEJitManager::allocCode), so we can do a single 64-bit interlocked operation + // to update the jump target. However, some code may have gotten compiled before + // the profiler had a chance to enable ReJIT (e.g., NGENd code, or code JITted + // before a profiler attaches). In such cases, we cannot rely on a simple + // interlocked operation, and instead must suspend the runtime to ensure we can + // safely update the jmp instruction. + // + // This method doesn't verify that the method is actually safe to rejit, we expect + // callers to do that. At the moment NGEN'ed code is safe to rejit even if + // it is unaligned, but code generated before the profiler attaches is not. + if (fContentionPossible && !(IS_ALIGNED(pbCode, sizeof(INT64)))) + { + return CORPROF_E_RUNTIME_SUSPEND_REQUIRED; + } + + // The debugging API may also try to write to this function (though + // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know + // whether we can safely patch the actual code, or instead write to the debugger's + // buffer. + if (fContentionPossible) + { + for (CORDB_ADDRESS_TYPE* pbProbeAddr = pbCode; pbProbeAddr < pbCode + MethodDescVersioningState::JumpStubSize; pbProbeAddr++) + { + if (NULL != DebuggerController::GetPatchTable()->GetPatch(pbProbeAddr)) + { + return CORPROF_E_RUNTIME_SUSPEND_REQUIRED; + } + } + } + +#if defined(_X86_) || defined(_AMD64_) + + DWORD oldProt; + if (!ClrVirtualProtect((LPVOID)pbCode, 8, PAGE_EXECUTE_READWRITE, &oldProt)) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if (fContentionPossible) + { + INT64 i64InterlockReportedOldValue = FastInterlockCompareExchangeLong((INT64 *)pbCode, i64NewValue, i64OldValue); + // Since changes to these bytes are protected by this rejitmgr's m_crstTable, we + // shouldn't have two writers conflicting. + _ASSERTE(i64InterlockReportedOldValue == i64OldValue); + } + else + { + // In this path the caller ensures: + // a) no thread will execute through the prologue area we are modifying + // b) no thread is stopped in a prologue such that it resumes in the middle of code we are modifying + // c) no thread is doing a debugger patch skip operation in which an unmodified copy of the method's + // code could be executed from a patch skip buffer. + + // PERF: we might still want a faster path through here if we aren't debugging that doesn't do + // all the patch checks + for (int i = 0; i < MethodDescVersioningState::JumpStubSize; i++) + { + *FirstCodeByteAddr(pbCode + i, DebuggerController::GetPatchTable()->GetPatch(pbCode + i)) = ((BYTE*)&i64NewValue)[i]; + } + } + + if (oldProt != PAGE_EXECUTE_READWRITE) + { + // The CLR codebase in many locations simply ignores failures to restore the page protections + // Its true that it isn't a problem functionally, but it seems a bit sketchy? + // I am following the convention for now. + ClrVirtualProtect((LPVOID)pbCode, 8, oldProt, &oldProt); + } + + FlushInstructionCache(GetCurrentProcess(), pbCode, MethodDescVersioningState::JumpStubSize); + return S_OK; + +#else // _X86_ || _AMD64_ +#error "Need to define a way to jump-stamp the prolog in a safe way for this platform" +#endif // _X86_ || _AMD64_ +} +#endif + CodeVersionManager::CodeVersionManager() { LIMITED_METHOD_CONTRACT; } +//--------------------------------------------------------------------------------------- +// +// Called from BaseDomain::BaseDomain to do any constructor-time initialization. +// Presently, this takes care of initializing the Crst, choosing the type based on +// whether this ReJitManager belongs to the SharedDomain. +// +// Arguments: +// * fSharedDomain - nonzero iff this ReJitManager belongs to the SharedDomain. +// + +void CodeVersionManager::PreInit(BOOL fSharedDomain) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + CAN_TAKE_LOCK; + MODE_ANY; + } + CONTRACTL_END; + +#ifndef DACCESS_COMPILE + m_crstTable.Init( + fSharedDomain ? CrstReJITSharedDomainTable : CrstReJITDomainTable, + CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN)); +#endif // DACCESS_COMPILE +} + +CodeVersionManager::TableLockHolder::TableLockHolder(CodeVersionManager* pCodeVersionManager) : + CrstHolder(&pCodeVersionManager->m_crstTable) +{ +} +#ifndef DACCESS_COMPILE +void CodeVersionManager::EnterLock() +{ + m_crstTable.Enter(); +} +void CodeVersionManager::LeaveLock() +{ + m_crstTable.Leave(); +} +#endif +BOOL CodeVersionManager::LockOwnedByCurrentThread() +{ + return m_crstTable.OwnedByCurrentThread(); +} + +PTR_MethodDescVersioningState CodeVersionManager::GetMethodVersioningState(PTR_MethodDesc pClosedMethodDesc) +{ + return m_methodDescVersioningStateMap.Lookup(pClosedMethodDesc); +} + +#ifndef DACCESS_COMPILE +HRESULT CodeVersionManager::GetOrCreateMethodVersioningState(MethodDesc* pMethod, MethodDescVersioningState** ppMethodVersioningState) +{ + HRESULT hr = S_OK; + MethodDescVersioningState* pMethodVersioningState = m_methodDescVersioningStateMap.Lookup(pMethod); + if (pMethodVersioningState == NULL) + { + pMethodVersioningState = new (nothrow) MethodDescVersioningState(pMethod); + if (pMethodVersioningState == NULL) + { + return E_OUTOFMEMORY; + } + EX_TRY + { + // This throws when out of memory, but remains internally + // consistent (without adding the new element) + m_methodDescVersioningStateMap.Add(pMethodVersioningState); + } + EX_CATCH_HRESULT(hr); + if (FAILED(hr)) + { + delete pMethodVersioningState; + return hr; + } + } + *ppMethodVersioningState = pMethodVersioningState; + return S_OK; +} +#endif + +DWORD CodeVersionManager::GetNonDefaultILVersionCount() +{ + //This function is legal to call WITHOUT taking the lock + //It is used to do a quick check if work might be needed without paying the overhead + //of acquiring the lock and doing dictionary lookups + return m_ilCodeVersionNodeMap.GetCount(); +} + +ILCodeVersionCollection CodeVersionManager::GetILCodeVersions(PTR_MethodDesc pMethod) +{ + return GetILCodeVersions(dac_cast(pMethod->GetModule()), pMethod->GetMemberDef()); +} + +ILCodeVersionCollection CodeVersionManager::GetILCodeVersions(PTR_Module pModule, mdMethodDef methodDef) +{ + return ILCodeVersionCollection(pModule, methodDef); +} + +ILCodeVersionIterator CodeVersionManager::GetILCodeVersionIterator(PTR_Module pModule, mdMethodDef methodDef) +{ + _ASSERTE(LockOwnedByCurrentThread()); + + ILCodeVersionNode::Key key(pModule, methodDef); + ILCodeVersionNodeHash::KeyIterator nodeIter = m_ilCodeVersionNodeMap.Begin(key); + ILCodeVersionNodeHash::KeyIterator nodeEndIter = m_ilCodeVersionNodeMap.End(key); + return ILCodeVersionIterator(nodeIter, nodeEndIter); +} + +ILCodeVersion CodeVersionManager::GetActiveILCodeVersion(PTR_MethodDesc pMethod) +{ + return GetActiveILCodeVersion(dac_cast(pMethod->GetModule()), pMethod->GetMemberDef()); +} +ILCodeVersion CodeVersionManager::GetActiveILCodeVersion(PTR_Module pModule, mdMethodDef methodDef) +{ + //TODO + return ILCodeVersion(); +} +ILCodeVersion CodeVersionManager::GetILCodeVersion(PTR_MethodDesc pMethod, ReJITID rejitId) +{ + //TODO + return ILCodeVersion(); +} + +NativeCodeVersion CodeVersionManager::GetNativeCodeVersion(PTR_MethodDesc pMethod, PCODE codeStartAddress) +{ + //TODO + return NativeCodeVersion(); +} + +#ifndef DACCESS_COMPILE +HRESULT CodeVersionManager::AddILCodeVersion(Module* pModule, mdMethodDef methodDef, ReJITID rejitId, ILCodeVersion* pILCodeVersion) +{ + _ASSERTE(LockOwnedByCurrentThread()); + + HRESULT hr = S_OK; + ILCodeVersionNode* pILCodeVersionNode = new (nothrow) ILCodeVersionNode(pModule, methodDef, rejitId); + if (pILCodeVersion == NULL) + { + return E_OUTOFMEMORY; + } + EX_TRY + { + // This throws when out of memory, but remains internally + // consistent (without adding the new element) + m_ilCodeVersionNodeMap.Add(pILCodeVersionNode); + } + EX_CATCH_HRESULT(hr); + if(FAILED(hr)) + { + delete pILCodeVersionNode; + return hr; + } + + *pILCodeVersion = ILCodeVersion(pILCodeVersionNode); + return S_OK; +} +#endif + +#ifndef DACCESS_COMPILE +HRESULT CodeVersionManager::AddNativeCodeVersion(ILCodeVersion ilCodeVersion, MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion) +{ + _ASSERTE(LockOwnedByCurrentThread()); + + MethodDescVersioningState* pMethodVersioningState; + HRESULT hr = GetOrCreateMethodVersioningState(pClosedMethodDesc, &pMethodVersioningState); + if (FAILED(hr)) + { + return hr; + } + + NativeCodeVersionId newId = pMethodVersioningState->AllocateVersionId(); + NativeCodeVersionNode* pNativeCodeVersionNode = new (nothrow) NativeCodeVersionNode(newId, pClosedMethodDesc, ilCodeVersion.GetVersionId()); + if (pNativeCodeVersionNode == NULL) + { + return E_OUTOFMEMORY; + } + EX_TRY + { + // This throws when out of memory, but remains internally + // consistent (without adding the new element) + m_nativeCodeVersionNodeMap.Add(pNativeCodeVersionNode); + } + EX_CATCH_HRESULT(hr); + if(FAILED(hr)) + { + delete pNativeCodeVersionNode; + return hr; + } + + ilCodeVersion.LinkNativeCodeNode(pNativeCodeVersionNode); + *pNativeCodeVersion = NativeCodeVersion(pNativeCodeVersionNode); + return S_OK; +} +#endif + +//--------------------------------------------------------------------------------------- +// +// Helper used by ReJitManager::RequestReJIT to jump stamp all the methods that were +// specified by the caller. Also used by RejitManager::DoJumpStampForAssemblyIfNecessary +// when rejitting a batch of generic method instantiations in a newly loaded NGEN assembly. +// +// This method is responsible for calling ReJITError on the profiler if anything goes +// wrong. +// +// Arguments: +// * pUndoMethods - array containing the methods that need the jump stamp removed +// * pPreStubMethods - array containing the methods that need to be jump stamped to prestub +// * pErrors - any errors will be appended to this array +// +// Returns: +// S_OK - all methods are updated or added an error to the pErrors array +// E_OUTOFMEMORY - some methods neither updated nor added an error to pErrors array +// ReJitInfo state remains consistent +// +// Assumptions: +// 1) Caller prevents contention by either: +// a) Suspending the runtime +// b) Ensuring all methods being updated haven't been published +// +#ifndef DACCESS_COMPILE +HRESULT CodeVersionManager::BatchUpdateJumpStamps(CDynArray * pUndoMethods, CDynArray * pPreStubMethods, CDynArray * pErrors) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + PRECONDITION(CheckPointer(pUndoMethods)); + PRECONDITION(CheckPointer(pPreStubMethods)); + PRECONDITION(CheckPointer(pErrors)); + } + CONTRACTL_END; + + _ASSERTE(LockOwnedByCurrentThread()); + HRESULT hr = S_OK; + + + NativeCodeVersion * pInfoEnd = pUndoMethods->Ptr() + pUndoMethods->Count(); + for (NativeCodeVersion * pInfoCur = pUndoMethods->Ptr(); pInfoCur < pInfoEnd; pInfoCur++) + { + // If we are undoing jumpstamps they have been published already + // and our caller is holding the EE suspended + _ASSERTE(ThreadStore::HoldingThreadStore()); + MethodDescVersioningState* pMethodVersioningState = m_methodDescVersioningStateMap.Lookup(pInfoCur->GetMethodDesc()); + _ASSERTE(pMethodVersioningState != NULL); + if (FAILED(hr = pMethodVersioningState->UndoJumpStampNativeCode(TRUE))) + { + if (FAILED(hr = AddCodePublishError(*pInfoCur, hr, pErrors))) + { + _ASSERTE(hr == E_OUTOFMEMORY); + return hr; + } + } + } + + pInfoEnd = pPreStubMethods->Ptr() + pPreStubMethods->Count(); + for (NativeCodeVersion * pInfoCur = pPreStubMethods->Ptr(); pInfoCur < pInfoEnd; pInfoCur++) + { + MethodDescVersioningState* pMethodVersioningState = m_methodDescVersioningStateMap.Lookup(pInfoCur->GetMethodDesc()); + _ASSERTE(pMethodVersioningState != NULL); + if (FAILED(hr = pMethodVersioningState->JumpStampNativeCode())) + { + if (FAILED(hr = AddCodePublishError(*pInfoCur, hr, pErrors))) + { + _ASSERTE(hr == E_OUTOFMEMORY); + return hr; + } + } + } + return S_OK; +} +#endif + +#ifndef DACCESS_COMPILE +//static +void CodeVersionManager::OnAppDomainExit(AppDomain * pAppDomain) +{ + // This would clean up all the allocations we have done and synchronize with any threads that might + // still be using the data + _ASSERTE(!".Net Core shouldn't be doing app domain shutdown - if we start doing so this needs to be implemented"); +} +#endif + +//--------------------------------------------------------------------------------------- +// +// Helper that inits a new ReJitReportErrorWorkItem and adds it to the pErrors array +// +// Arguments: +// * pModule - The module in the module/MethodDef identifier pair for the method which +// had an error during rejit +// * methodDef - The MethodDef in the module/MethodDef identifier pair for the method which +// had an error during rejit +// * pMD - If available, the specific method instance which had an error during rejit +// * hrStatus - HRESULT for the rejit error that occurred +// * pErrors - the list of error records that this method will append to +// +// Return Value: +// * S_OK: error was appended +// * E_OUTOFMEMORY: Not enough memory to create the new error item. The array is unchanged. +// + +//static +#ifndef DACCESS_COMPILE +HRESULT CodeVersionManager::AddCodePublishError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus, CDynArray * pErrors) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + CodePublishError* pError = pErrors->Append(); + if (pError == NULL) + { + return E_OUTOFMEMORY; + } + pError->pModule = pModule; + pError->methodDef = methodDef; + pError->pMethodDesc = pMD; + pError->hrStatus = hrStatus; + return S_OK; +} +#endif + +//--------------------------------------------------------------------------------------- +// +// Helper that inits a new ReJitReportErrorWorkItem and adds it to the pErrors array +// +// Arguments: +// * pReJitInfo - The method which had an error during rejit +// * hrStatus - HRESULT for the rejit error that occurred +// * pErrors - the list of error records that this method will append to +// +// Return Value: +// * S_OK: error was appended +// * E_OUTOFMEMORY: Not enough memory to create the new error item. The array is unchanged. +// + +//static +#ifndef DACCESS_COMPILE +HRESULT CodeVersionManager::AddCodePublishError(NativeCodeVersion nativeCodeVersion, HRESULT hrStatus, CDynArray * pErrors) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + return E_NOTIMPL; + //TODO + /* + + Module * pModule = NULL; + mdMethodDef methodDef = mdTokenNil; + pReJitInfo->GetModuleAndTokenRegardlessOfKeyType(&pModule, &methodDef); + return AddReJITError(pModule, methodDef, pReJitInfo->GetMethodDesc(), hrStatus, pErrors); + */ +} #endif // DACCESS_COMPILE + #endif // FEATURE_CODE_VERSIONING diff --git a/src/vm/codeversion.h b/src/vm/codeversion.h index 35bfb58a0dbf..ce4956338f09 100644 --- a/src/vm/codeversion.h +++ b/src/vm/codeversion.h @@ -12,17 +12,602 @@ #ifdef FEATURE_CODE_VERSIONING -class CodeVersionManager +class NativeCodeVersionNode; +typedef DPTR(class NativeCodeVersionNode) PTR_NativeCodeVersionNode; +class NativeCodeVersion; +class NativeCodeVersionCollection; +class NativeCodeVersionIterator; +class ILCodeVersionNode; +typedef DPTR(class ILCodeVersionNode) PTR_ILCodeVersionNode; +class ILCodeVersion; +class ILCodeVersionCollection; +class ILCodeVersionIterator; +class MethodDescVersioningState; +typedef DPTR(class MethodDescVersioningState) PTR_MethodDescVersioningState; +class CodeVersionManager; + + + +typedef DWORD NativeCodeVersionId; + +class NativeCodeVersionNode +{ + friend NativeCodeVersionIterator; + friend MethodDescVersioningState; + friend ILCodeVersionNode; +public: +#ifndef DACCESS_COMPILE + NativeCodeVersionNode(NativeCodeVersionId id, MethodDesc* pMethod, ReJITID parentId); +#endif + PTR_MethodDesc GetMethodDesc() const; + NativeCodeVersionId GetVersionId() const; + PCODE GetNativeCode() const; + BOOL SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected); + ReJITID GetILVersionId() const; + ILCodeVersion GetILCodeVersion() const; +//#ifdef FEATURE_TIERED_COMPILATION +// TieredCompilationCodeConfiguration* GetTieredCompilationConfig(); +// const TieredCompilationCodeConfiguration* GetTieredCompilationConfig() const; +//#endif + + +private: + //union + //{ + PCODE m_pNativeCode; + PTR_MethodDesc m_pMethodDesc; + //}; + + //union + //{ + PTR_NativeCodeVersionNode m_pNextILVersionSibling; + ReJITID m_parentId; + //}; + + NativeCodeVersionId m_id; +//#ifdef FEATURE_TIERED_COMPILATION +// TieredCompilationCodeConfiguration m_tieredCompilationConfig; +//#endif +}; + +class NativeCodeVersion +{ + friend MethodDescVersioningState; + +public: + NativeCodeVersion(); + NativeCodeVersion(const NativeCodeVersion & rhs); + NativeCodeVersion(PTR_NativeCodeVersionNode pVersionNode); + NativeCodeVersion(PTR_MethodDesc pMethod); + BOOL IsNull() const; + PTR_MethodDesc GetMethodDesc() const; + NativeCodeVersionId GetVersionId() const; + BOOL IsDefaultVersion() const; + PCODE GetNativeCode() const; + BOOL SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected = NULL); + ILCodeVersion GetILCodeVersion() const; +//#ifdef FEATURE_TIERED_COMPILATION +// TieredCompilationCodeConfiguration* GetTieredCompilationConfig(); +// const TieredCompilationCodeConfiguration* GetTieredCompilationConfig() const; +//#endif + + bool operator==(const NativeCodeVersion & rhs) const; + bool operator!=(const NativeCodeVersion & rhs) const; + +#ifdef DACCESS_COMPILE + // The DAC is privy to the backing node abstraction + PTR_NativeCodeVersionNode AsNode() const; +#endif + +private: + +#ifndef DACCESS_COMPILE + PTR_NativeCodeVersionNode AsNode() const; + PTR_NativeCodeVersionNode AsNode(); +#endif + + enum StorageKind + { + Unknown, + Explicit, + Synthetic + }; + + StorageKind m_storageKind; + union + { + PTR_NativeCodeVersionNode m_pVersionNode; + struct SyntheticStorage + { + MethodDesc* m_pMethodDesc; + //TieredCompilationCodeConfiguration m_tieredCompilationConfig; + } m_synthetic; + }; +}; + +class NativeCodeVersionNodeHashTraits : public DefaultSHashTraits { public: -#if defined(DACCESS_COMPILE) || defined(CROSSGEN_COMPILE) - CodeVersionManager() {} + typedef typename DefaultSHashTraits::element_t element_t; + typedef typename DefaultSHashTraits::count_t count_t; + + typedef const PTR_MethodDesc key_t; + + static key_t GetKey(element_t e) + { + LIMITED_METHOD_CONTRACT; + return e->GetMethodDesc(); + } + static BOOL Equals(key_t k1, key_t k2) + { + LIMITED_METHOD_CONTRACT; + return k1 == k2; + } + static count_t Hash(key_t k) + { + LIMITED_METHOD_CONTRACT; + return (count_t)(size_t)dac_cast(k); + } + static const element_t Null() { LIMITED_METHOD_CONTRACT; return element_t(); } + static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e == NULL; } +}; + +typedef SHash NativeCodeVersionNodeHash; + +class NativeCodeVersionCollection +{ + friend class NativeCodeVersionIterator; +public: + NativeCodeVersionCollection(); + NativeCodeVersionIterator Begin(); + NativeCodeVersionIterator End(); + +private: +}; + +class NativeCodeVersionIterator : public Enumerator +{ + friend class Enumerator; + +public: + NativeCodeVersionIterator(NativeCodeVersionCollection* pCollection); + CHECK Check() const { CHECK_OK; } + +protected: + const NativeCodeVersion & Get() const; + void First(); + void Next(); + bool Equal(const NativeCodeVersionIterator &i) const; + + CHECK DoCheck() const { CHECK_OK; } + +private: + NativeCodeVersion m_cur; +}; + + + +class ILCodeVersion +{ +public: + ILCodeVersion(); + ILCodeVersion(const ILCodeVersion & ilCodeVersion); + ILCodeVersion(PTR_ILCodeVersionNode pILCodeVersionNode); + bool operator==(const ILCodeVersion & rhs) const; + BOOL IsNull() const; + PTR_Module GetModule(); + mdMethodDef GetMethodDef(); + ReJITID GetVersionId(); + NativeCodeVersionCollection GetNativeCodeVersions(PTR_MethodDesc pClosedMethodDesc); + NativeCodeVersion GetActiveNativeCodeVersion(PTR_MethodDesc pClosedMethodDesc); + + PTR_COR_ILMETHOD GetIL() const; + DWORD GetJitFlags() const; + const InstrumentedILOffsetMapping* GetInstrumentedILMap() const; +#ifndef DACCESS_COMPILE + void SetIL(COR_ILMETHOD* pIL); + void SetJitFlags(DWORD flags); + void SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap); + HRESULT AddNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion); + void LinkNativeCodeNode(NativeCodeVersionNode* pNativeCodeVersionNode); +#endif + + enum RejitFlags + { + // The profiler has requested a ReJit, so we've allocated stuff, but we haven't + // called back to the profiler to get any info or indicate that the ReJit has + // started. (This Info can be 'reused' for a new ReJit if the + // profiler calls RequestRejit again before we transition to the next state.) + kStateRequested = 0x00000000, + + // The CLR has initiated the call to the profiler's GetReJITParameters() callback + // but it hasn't completed yet. At this point we have to assume the profiler has + // commited to a specific IL body, even if the CLR doesn't know what it is yet. + // If the profiler calls RequestRejit we need to allocate a new ILCodeVersion + // and call GetReJITParameters() again. + kStateGettingReJITParameters = 0x00000001, + + // We have asked the profiler about this method via ICorProfilerFunctionControl, + // and have thus stored the IL and codegen flags the profiler specified. Can only + // transition to kStateReverted from this state. + kStateActive = 0x00000002, + + // The methoddef has been reverted, but not freed yet. It (or its instantiations + // for generics) *MAY* still be active on the stack someplace or have outstanding + // memory references. + kStateReverted = 0x00000003, + + + kStateMask = 0x0000000F, + }; + + RejitFlags GetRejitState() const; + void SetRejitState(RejitFlags newState); + +#ifdef DACCESS_COMPILE + // The DAC is privy to the backing node abstraction + PTR_ILCodeVersionNode AsNode() const; +#endif + +private: + +#ifndef DACCESS_COMPILE + PTR_ILCodeVersionNode AsNode(); + PTR_ILCodeVersionNode AsNode() const; +#endif + + enum StorageKind + { + Unknown, + Explicit, + Synthetic + }; + + StorageKind m_storageKind; + //union + //{ + PTR_ILCodeVersionNode m_pVersionNode; + // struct SyntheticStorage + // { + // PTR_Module m_pModule; + // mdMethodDef m_methodDef; + // } m_synthetic; + //}; +}; + +class ILCodeVersionNode +{ +public: + ILCodeVersionNode(); +#ifndef DACCESS_COMPILE + ILCodeVersionNode(Module* pModule, mdMethodDef methodDef, ReJITID id); +#endif + PTR_Module GetModule(); + mdMethodDef GetMethodDef(); + ReJITID GetVersionId(); + PTR_NativeCodeVersionNode GetActiveNativeCodeVersion(PTR_MethodDesc pClosedMethodDesc); + PTR_COR_ILMETHOD GetIL() const; + DWORD GetJitFlags() const; + const InstrumentedILOffsetMapping* GetInstrumentedILMap() const; + ILCodeVersion::RejitFlags GetRejitState() const; +#ifndef DACCESS_COMPILE + void SetIL(COR_ILMETHOD* pIL); + void SetJitFlags(DWORD flags); + void SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap); + void SetRejitState(ILCodeVersion::RejitFlags newState); + void LinkNativeCodeNode(NativeCodeVersionNode* pNativeCodeVersionNode); +#endif + + struct Key + { + public: + Key(); + Key(PTR_Module pModule, mdMethodDef methodDef); + size_t Hash() const; + bool operator==(const Key & rhs) const; + private: + PTR_Module m_pModule; + mdMethodDef m_methodDef; + }; + + Key GetKey() const; + +private: + PTR_Module m_pModule; + mdMethodDef m_methodDef; + ReJITID m_rejitId; + PTR_NativeCodeVersionNode m_pFirstChild; + ILCodeVersion::RejitFlags m_rejitState; + PTR_COR_ILMETHOD m_pIL; + DWORD m_jitFlags; + InstrumentedILOffsetMapping m_instrumentedILMap; +}; + +class ILCodeVersionNodeHashTraits : public DefaultSHashTraits +{ +public: + typedef typename DefaultSHashTraits::element_t element_t; + typedef typename DefaultSHashTraits::count_t count_t; + + typedef ILCodeVersionNode::Key key_t; + + static key_t GetKey(element_t e) + { + LIMITED_METHOD_CONTRACT; + return e->GetKey(); + } + static BOOL Equals(key_t k1, key_t k2) + { + LIMITED_METHOD_CONTRACT; + return k1 == k2; + } + static count_t Hash(key_t k) + { + LIMITED_METHOD_CONTRACT; + return (count_t)k.Hash(); + } + static const element_t Null() { LIMITED_METHOD_CONTRACT; return element_t(); } + static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e == NULL; } +}; + +typedef SHash ILCodeVersionNodeHash; + +class ILCodeVersionCollection +{ + friend class ILCodeVersionIterator; + +public: + ILCodeVersionCollection(PTR_Module pModule, mdMethodDef methodDef); + ILCodeVersionIterator Begin(); + ILCodeVersionIterator End(); + +private: + PTR_Module m_pModule; + mdMethodDef m_methodDef; +}; + +class ILCodeVersionIterator : public Enumerator +{ + friend class Enumerator; + +public: + ILCodeVersionIterator(); + ILCodeVersionIterator(const ILCodeVersionIterator & iter); + ILCodeVersionIterator(ILCodeVersionNodeHash::KeyIterator tableStartIter, ILCodeVersionNodeHash::KeyIterator tableEndIter); + CHECK Check() const { CHECK_OK; } + +protected: + const ILCodeVersion & Get() const; + void First(); + void Next(); + bool Equal(const ILCodeVersionIterator &i) const; + + CHECK DoCheck() const { CHECK_OK; } + +private: + ILCodeVersion m_cur; + + //TODO: storing all the versions in a table doesn't seem that efficient + //We've got a density factor of 0.75 so we only get 3 versions for every 4 + //pointers + //What about one ILVersioningState in a table then a linked list from there? + ILCodeVersionNodeHash::KeyIterator m_tableCurIter; + ILCodeVersionNodeHash::KeyIterator m_tableEndIter; +}; + +class MethodDescVersioningState +{ +public: + // The size of the code used to jump stamp the prolog + static const size_t JumpStubSize = +#if defined(_X86_) || defined(_AMD64_) + 5; #else +#error "Need to define size of rejit jump-stamp for this platform" + 1; +#endif + + MethodDescVersioningState(PTR_MethodDesc pMethodDesc); + PTR_MethodDesc GetMethodDesc() const; + NativeCodeVersionId AllocateVersionId(); + +#ifndef DACCESS_COMPILE + HRESULT UpdateJumpTarget(BOOL fEESuspended, PCODE pRejittedCode); + HRESULT UndoJumpStampNativeCode(BOOL fEESuspended); + HRESULT JumpStampNativeCode(PCODE pCode = NULL); +#endif + + enum JumpStampFlags + { + // There is no jump stamp in place on this method (Either because + // there is no code at all, or there is code that hasn't been + // overwritten with a jump) + JumpStampNone = 0x0, + + // The method code has the jump stamp written in, and it points to the Prestub + JumpStampToPrestub = 0x1, + + // The method code has the jump stamp written in, and it points to the currently + // active code version + JumpStampToActiveVersion = 0x2, + }; + + JumpStampFlags GetJumpStampState(); + void SetJumpStampState(JumpStampFlags newState); + +private: + INDEBUG(BOOL CodeIsSaved();) +#ifndef DACCESS_COMPILE + HRESULT UpdateJumpStampHelper(BYTE* pbCode, INT64 i64OldValue, INT64 i64NewValue, BOOL fContentionPossible); +#endif + PTR_MethodDesc m_pMethodDesc; + BYTE m_flags; + NativeCodeVersionId m_nextId; + + // The originally JITted code that was overwritten with the jmp stamp. + BYTE m_rgSavedCode[JumpStubSize]; +}; + +class MethodDescVersioningStateHashTraits : public NoRemoveSHashTraits> +{ +public: + typedef typename DefaultSHashTraits::element_t element_t; + typedef typename DefaultSHashTraits::count_t count_t; + + typedef const PTR_MethodDesc key_t; + + static key_t GetKey(element_t e) + { + LIMITED_METHOD_CONTRACT; + return e->GetMethodDesc(); + } + static BOOL Equals(key_t k1, key_t k2) + { + LIMITED_METHOD_CONTRACT; + return k1 == k2; + } + static count_t Hash(key_t k) + { + LIMITED_METHOD_CONTRACT; + return (count_t)(size_t)dac_cast(k); + } + + static const element_t Null() { LIMITED_METHOD_CONTRACT; return element_t(); } + static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e == NULL; } +}; + +typedef SHash MethodDescVersioningStateHash; + + +class CodeVersionManager +{ + friend class ILCodeVersion; + +public: CodeVersionManager(); + + void PreInit(BOOL fSharedDomain); + + class TableLockHolder : public CrstHolder + { + public: + TableLockHolder(CodeVersionManager * pCodeVersionManager); + }; + //Using the holder is preferable, but in some cases the holder can't be used +#ifndef DACCESS_COMPILE + void EnterLock(); + void LeaveLock(); +#endif + //only intended for debug assertions + BOOL LockOwnedByCurrentThread(); + +#ifndef DACCESS_COMPILE + HRESULT AddILCodeVersion(Module* pModule, mdMethodDef methodDef, ReJITID rejitId, ILCodeVersion* pILCodeVersion); + HRESULT AddNativeCodeVersion(ILCodeVersion ilCodeVersion, MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion); +#endif + + DWORD GetNonDefaultILVersionCount(); + ILCodeVersionCollection GetILCodeVersions(PTR_MethodDesc pMethod); + ILCodeVersionCollection GetILCodeVersions(PTR_Module pModule, mdMethodDef methodDef); + ILCodeVersionIterator GetILCodeVersionIterator(PTR_Module pModule, mdMethodDef methodDef); + ILCodeVersion GetActiveILCodeVersion(PTR_MethodDesc pMethod); + ILCodeVersion GetActiveILCodeVersion(PTR_Module pModule, mdMethodDef methodDef); + ILCodeVersion GetILCodeVersion(PTR_MethodDesc pMethod, ReJITID rejitId); + + NativeCodeVersion GetNativeCodeVersion(PTR_MethodDesc pMethod, PCODE codeStartAddress); + + struct JumpStampBatch + { + JumpStampBatch(CodeVersionManager * pCodeVersionManager) : undoMethods(), preStubMethods() + { + LIMITED_METHOD_CONTRACT; + this->pCodeVersionManager = pCodeVersionManager; + } + + CodeVersionManager* pCodeVersionManager; + CDynArray undoMethods; + CDynArray preStubMethods; + }; + + class JumpStampBatchTraits : public DefaultSHashTraits + { + public: + + // explicitly declare local typedefs for these traits types, otherwise + // the compiler may get confused + typedef DefaultSHashTraits PARENT; + typedef PARENT::element_t element_t; + typedef PARENT::count_t count_t; + + typedef CodeVersionManager * key_t; + + static key_t GetKey(const element_t &e) + { + return e->pCodeVersionManager; + } + + static BOOL Equals(key_t k1, key_t k2) + { + return (k1 == k2); + } + + static count_t Hash(key_t k) + { + return (count_t)k; + } + + static bool IsNull(const element_t &e) + { + return (e == NULL); + } + }; + + struct CodePublishError + { + Module* pModule; + mdMethodDef methodDef; + MethodDesc* pMethodDesc; + HRESULT hrStatus; + }; + +#ifndef DACCESS_COMPILE + HRESULT BatchUpdateJumpStamps(CDynArray * pUndoMethods, + CDynArray * pPreStubMethods, + CDynArray * pErrors); +#endif + +#ifndef DACCESS_COMPILE + static HRESULT AddCodePublishError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus, CDynArray * pErrors); + static HRESULT AddCodePublishError(NativeCodeVersion nativeCodeVersion, HRESULT hrStatus, CDynArray * pErrors); +#endif + + PTR_MethodDescVersioningState GetMethodVersioningState(PTR_MethodDesc pMethod); +#ifndef DACCESS_COMPILE + HRESULT GetOrCreateMethodVersioningState(MethodDesc* pMethod, MethodDescVersioningState** ppMethodDescVersioningState); +#endif + +#ifndef DACCESS_COMPILE + static void OnAppDomainExit(AppDomain* pAppDomain); #endif private: + BOOL PublishMethodCodeIfNeeded(NativeCodeVersionNode* pNativeCodeVersion); + + //Module,MethodDef -> ILCodeVersions + ILCodeVersionNodeHash m_ilCodeVersionNodeMap; + //closed MethodDesc -> NativeCodeVersions + NativeCodeVersionNodeHash m_nativeCodeVersionNodeMap; + //closed MethodDesc -> MethodDescVersioningState + MethodDescVersioningStateHash m_methodDescVersioningStateMap; + + CrstExplicitInit m_crstTable; +}; + +class CodeVersionManagerLockHolder +{ +public: + CodeVersionManagerLockHolder(CodeVersionManager* pCodeVersionManager) {} }; #endif // FEATURE_CODE_VERSIONING diff --git a/src/vm/crst.h b/src/vm/crst.h index a353c6ea4424..720576efb96a 100644 --- a/src/vm/crst.h +++ b/src/vm/crst.h @@ -121,8 +121,7 @@ friend class ListLockEntry; friend struct SavedExceptionInfo; friend void EEEnterCriticalSection(CRITSEC_COOKIE cookie); friend void EELeaveCriticalSection(CRITSEC_COOKIE cookie); -friend class ReJitPublishMethodHolder; -friend class ReJitPublishMethodTableHolder; +friend class CodeVersionManager; friend class Debugger; friend class Crst; diff --git a/src/vm/eventtrace.cpp b/src/vm/eventtrace.cpp index ebe2f1176a39..c955b66d46ae 100644 --- a/src/vm/eventtrace.cpp +++ b/src/vm/eventtrace.cpp @@ -6962,8 +6962,8 @@ VOID ETW::MethodLog::SendEventsForJitMethods(BaseDomain *pDomainFilter, LoaderAl // We only support getting rejit IDs when filtering by domain. if (pDomainFilter) { - ReJitManager::TableLockHolder lkRejitMgrSharedDomain(SharedDomain::GetDomain()->GetReJitManager()); - ReJitManager::TableLockHolder lkRejitMgrModule(pDomainFilter->GetReJitManager()); + CodeVersionManager::TableLockHolder lkRejitMgrSharedDomain(SharedDomain::GetDomain()->GetCodeVersionManager()); + CodeVersionManager::TableLockHolder lkRejitMgrModule(pDomainFilter->GetCodeVersionManager()); SendEventsForJitMethodsHelper(pDomainFilter, pLoaderAllocatorFilter, dwEventOptions, diff --git a/src/vm/ilinstrumentation.cpp b/src/vm/ilinstrumentation.cpp index 4d03bb27409d..a2bdbf1a6083 100644 --- a/src/vm/ilinstrumentation.cpp +++ b/src/vm/ilinstrumentation.cpp @@ -30,7 +30,7 @@ InstrumentedILOffsetMapping::InstrumentedILOffsetMapping() // the Module containing the instrumented method is destructed. // -BOOL InstrumentedILOffsetMapping::IsNull() +BOOL InstrumentedILOffsetMapping::IsNull() const { LIMITED_METHOD_DAC_CONTRACT; @@ -87,4 +87,4 @@ ARRAY_PTR_COR_IL_MAP InstrumentedILOffsetMapping::GetOffsets() const _ASSERTE((m_cMap == 0) == (m_rgMap == NULL)); return m_rgMap; -} \ No newline at end of file +} diff --git a/src/vm/ilinstrumentation.h b/src/vm/ilinstrumentation.h index dd1c6c816a2b..cc486ede3f7a 100644 --- a/src/vm/ilinstrumentation.h +++ b/src/vm/ilinstrumentation.h @@ -29,7 +29,7 @@ class InstrumentedILOffsetMapping InstrumentedILOffsetMapping(); // Check whether there is any mapping information stored in this object. - BOOL IsNull(); + BOOL IsNull() const; #if !defined(DACCESS_COMPILE) // Release the memory used by the array of COR_IL_MAPs. diff --git a/src/vm/method.hpp b/src/vm/method.hpp index 778ce1cc878b..9df54678357a 100644 --- a/src/vm/method.hpp +++ b/src/vm/method.hpp @@ -42,6 +42,7 @@ class Dictionary; class GCCoverageInfo; class DynamicMethodDesc; class ReJitManager; +class CodeVersionManager; typedef DPTR(FCallMethodDesc) PTR_FCallMethodDesc; typedef DPTR(ArrayMethodDesc) PTR_ArrayMethodDesc; @@ -509,6 +510,10 @@ class MethodDesc ReJitManager * GetReJitManager(); +#ifdef FEATURE_CODE_VERSIONING + CodeVersionManager* GetCodeVersionManager(); +#endif + PTR_LoaderAllocator GetLoaderAllocator(); // GetLoaderAllocatorForCode returns the allocator with the responsibility for allocation. diff --git a/src/vm/method.inl b/src/vm/method.inl index cdd137b84bec..e266133ff74a 100644 --- a/src/vm/method.inl +++ b/src/vm/method.inl @@ -209,5 +209,15 @@ inline ReJitManager * MethodDesc::GetReJitManager() return GetModule()->GetReJitManager(); } +#ifdef FEATURE_CODE_VERSIONING + +inline CodeVersionManager * MethodDesc::GetCodeVersionManager() +{ + LIMITED_METHOD_CONTRACT; + return GetModule()->GetCodeVersionManager(); +} + +#endif + #endif // _METHOD_INL_ diff --git a/src/vm/proftoeeinterfaceimpl.cpp b/src/vm/proftoeeinterfaceimpl.cpp index cfd99adf2770..4ccd1d3f4517 100644 --- a/src/vm/proftoeeinterfaceimpl.cpp +++ b/src/vm/proftoeeinterfaceimpl.cpp @@ -2592,13 +2592,24 @@ HRESULT ProfToEEInterfaceImpl::GetCodeInfo3(FunctionID functionId, hr = ValidateParametersForGetCodeInfo(pMethodDesc, cCodeInfos, codeInfos); if (SUCCEEDED(hr)) { - hr = GetCodeInfoFromCodeStart( - // Note here that we must consult the rejit manager to determine the code - // start address - pMethodDesc->GetReJitManager()->GetCodeStart(pMethodDesc, reJitId), - cCodeInfos, - pcCodeInfos, - codeInfos); + CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager(); + ILCodeVersion ilCodeVersion = pCodeVersionManager->GetILCodeVersion(pMethodDesc, reJitId); + + // Now that tiered compilation can create more than one jitted code version for the same rejit id + // we are arbitrarily choosing the first one to return. To return all of them we'd presumably need + // a new profiler API. + NativeCodeVersionCollection nativeCodeVersions = ilCodeVersion.GetNativeCodeVersions(pMethodDesc); + for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++) + { + PCODE pCodeStart = iter->GetNativeCode(); + hr = GetCodeInfoFromCodeStart( + pCodeStart, + cCodeInfos, + pcCodeInfos, + codeInfos); + break; + } + } } EX_CATCH_HRESULT(hr); diff --git a/src/vm/rejit.cpp b/src/vm/rejit.cpp index 7bbd0e2f711e..031a0d78e599 100644 --- a/src/vm/rejit.cpp +++ b/src/vm/rejit.cpp @@ -156,10 +156,12 @@ #include "threadsuspend.h" #ifdef FEATURE_REJIT +#ifdef FEATURE_CODE_VERSIONING #include "../debug/ee/debugger.h" #include "../debug/ee/walker.h" #include "../debug/ee/controller.h" +#include "codeversion.h" // This HRESULT is only used as a private implementation detail. If it escapes functions // defined in this file it is a bug. Corerror.xml has a comment in it reserving this @@ -169,7 +171,7 @@ // This is just used as a unique id. Overflow is OK. If we happen to have more than 4+Billion rejits // and somehow manage to not run out of memory, we'll just have to redefine ReJITID as size_t. /* static */ -ReJITID SharedReJitInfo::s_GlobalReJitId = 1; +static ReJITID s_GlobalReJitId = 1; /* static */ CrstStatic ReJitManager::s_csGlobalRequest; @@ -198,92 +200,7 @@ inline CORJIT_FLAGS JitFlagsFromProfCodegenFlags(DWORD dwCodegenFlags) return jitFlags; } -//--------------------------------------------------------------------------------------- -// Allocation helpers used by ReJitInfo / SharedReJitInfo to ensure they -// stick stuff on the appropriate loader heap. - -void * LoaderHeapAllocatedRejitStructure::operator new (size_t size, LoaderHeap * pHeap, const NoThrow&) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT(return NULL;); - PRECONDITION(CheckPointer(pHeap)); - } - CONTRACTL_END; - -#ifdef DACCESS_COMPILE - return ::operator new(size, nothrow); -#else - return pHeap->AllocMem_NoThrow(S_SIZE_T(size)); -#endif -} - -void * LoaderHeapAllocatedRejitStructure::operator new (size_t size, LoaderHeap * pHeap) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(CheckPointer(pHeap)); - } - CONTRACTL_END; - -#ifdef DACCESS_COMPILE - return ::operator new(size); -#else - return pHeap->AllocMem(S_SIZE_T(size)); -#endif -} - - -//--------------------------------------------------------------------------------------- -// -// Simple, thin abstraction of debugger breakpoint patching. Given an address and a -// previously procured DebuggerControllerPatch governing the code address, this decides -// whether the code address is patched. If so, it returns a pointer to the debugger's -// buffer (of what's "underneath" the int 3 patch); otherwise, it returns the code -// address itself. -// -// Arguments: -// * pbCode - Code address to return if unpatched -// * dbgpatch - DebuggerControllerPatch to test -// -// Return Value: -// Either pbCode or the debugger's patch buffer, as per description above. -// -// Assumptions: -// Caller must manually grab (and hold) the ControllerLockHolder and get the -// DebuggerControllerPatch before calling this helper. -// -// Notes: -// pbCode need not equal the code address governed by dbgpatch, but is always -// "related" (and sometimes really is equal). For example, this helper may be used -// when writing a code byte to an internal rejit buffer (e.g., in preparation for an -// eventual 64-bit interlocked write into the code stream), and thus pbCode would -// point into the internal rejit buffer whereas dbgpatch governs the corresponding -// code byte in the live code stream. This function would then be used to determine -// whether a byte should be written into the internal rejit buffer OR into the -// debugger controller's breakpoint buffer. -// - -LPBYTE FirstCodeByteAddr(LPBYTE pbCode, DebuggerControllerPatch * dbgpatch) -{ - LIMITED_METHOD_CONTRACT; - if (dbgpatch != NULL && dbgpatch->IsActivated()) - { - // Debugger has patched the code, so return the address of the buffer - return LPBYTE(&(dbgpatch->opcode)); - } - - // no active patch, just return the direct code address - return pbCode; -} //--------------------------------------------------------------------------------------- @@ -611,8 +528,8 @@ HRESULT ReJitManager::RequestReJIT( // JIT events would do it for the current domain I think. Of course RequestRejit // could always be called with ModuleIDs in some other AppDomain. //END BUGBUG - SHash mgrToJumpStampBatch; - CDynArray errorRecords; + SHash mgrToJumpStampBatch; + CDynArray errorRecords; for (ULONG i = 0; i < cFunctions; i++) { Module * pModule = reinterpret_cast< Module * >(rgModuleIDs[i]); @@ -660,12 +577,12 @@ HRESULT ReJitManager::RequestReJIT( } } - ReJitManager * pReJitMgr = pModule->GetReJitManager(); - _ASSERTE(pReJitMgr != NULL); - ReJitManagerJumpStampBatch * pJumpStampBatch = mgrToJumpStampBatch.Lookup(pReJitMgr); + CodeVersionManager * pCodeVersionManager = pModule->GetCodeVersionManager(); + _ASSERTE(pCodeVersionManager != NULL); + CodeVersionManager::JumpStampBatch * pJumpStampBatch = mgrToJumpStampBatch.Lookup(pCodeVersionManager); if (pJumpStampBatch == NULL) { - pJumpStampBatch = new (nothrow)ReJitManagerJumpStampBatch(pReJitMgr); + pJumpStampBatch = new (nothrow)CodeVersionManager::JumpStampBatch(pCodeVersionManager); if (pJumpStampBatch == NULL) { return E_OUTOFMEMORY; @@ -692,9 +609,9 @@ HRESULT ReJitManager::RequestReJIT( // may not be a generic (or a function on a generic class). The operations // below depend on these conditions as follows: // - // (1) If pMD == NULL || PMD has no code || pMD is generic - // Do a "PRE-REJIT" (add a placeholder ReJitInfo that points to module/token; - // there's nothing to jump-stamp) + // (1) In all cases, bind to an ILCodeVersion + // This serves as a pre-rejit request for any code that has yet to be generated + // and will also hold the modified IL + REJITID that tracks the request generally // // (2) IF pMD != NULL, but not generic (or function on generic class) // Do a REAL REJIT (add a real ReJitInfo that points to pMD and jump-stamp) @@ -703,24 +620,16 @@ HRESULT ReJitManager::RequestReJIT( // Do a real rejit (including jump-stamp) for all already-jitted instantiations. BaseDomain * pBaseDomainFromModule = pModule->GetDomain(); - SharedReJitInfo * pSharedInfo = NULL; + ILCodeVersion ilCodeVersion; { - CrstHolder ch(&(pReJitMgr->m_crstTable)); + CodeVersionManager::TableLockHolder lock(pCodeVersionManager); - // Do a PRE-rejit - if (pMD == NULL || !pMD->HasNativeCode() || pMD->HasClassOrMethodInstantiation()) + // Bind the il code version + hr = ReJitManager::BindILVersion(pCodeVersionManager, pJumpStampBatch, pModule, rgMethodDefs[i], &ilCodeVersion); + if (FAILED(hr)) { - hr = pReJitMgr->MarkForReJit( - pModule, - rgMethodDefs[i], - pJumpStampBatch, - &errorRecords, - &pSharedInfo); - if (FAILED(hr)) - { - _ASSERTE(hr == E_OUTOFMEMORY); - return hr; - } + _ASSERTE(hr == E_OUTOFMEMORY); + return hr; } if (pMD == NULL) @@ -733,12 +642,12 @@ HRESULT ReJitManager::RequestReJIT( { // We have a JITted non-generic. Easy case. Just mark the JITted method // desc as needing to be rejitted - hr = pReJitMgr->MarkForReJit( + hr = ReJitManager::MarkForReJit( + pCodeVersionManager, pMD, - pSharedInfo, + ilCodeVersion, pJumpStampBatch, - &errorRecords, - NULL); // Don't need the SharedReJitInfo to be returned + &errorRecords); if (FAILED(hr)) { @@ -766,8 +675,9 @@ HRESULT ReJitManager::RequestReJIT( // include orphaned code (i.e., shared code used by ADs that have // all unloaded), which is good, because orphaned code could get // re-adopted if a new AD is created that can use that shared code - hr = pReJitMgr->MarkAllInstantiationsForReJit( - pSharedInfo, + hr = ReJitManager::MarkAllInstantiationsForReJit( + pCodeVersionManager, + ilCodeVersion, NULL, // NULL means to search SharedDomain instead of an AD pModule, rgMethodDefs[i], @@ -777,8 +687,9 @@ HRESULT ReJitManager::RequestReJIT( else { // Module is unshared, so just use the module's domain to find instantiations. - hr = pReJitMgr->MarkAllInstantiationsForReJit( - pSharedInfo, + hr = ReJitManager::MarkAllInstantiationsForReJit( + pCodeVersionManager, + ilCodeVersion, pBaseDomainFromModule->AsAppDomain(), pModule, rgMethodDefs[i], @@ -806,9 +717,10 @@ HRESULT ReJitManager::RequestReJIT( { continue; } - CrstHolder ch(&(pReJitMgr->m_crstTable)); - hr = pReJitMgr->MarkAllInstantiationsForReJit( - pSharedInfo, + CodeVersionManager::TableLockHolder lock(pCodeVersionManager); + hr = ReJitManager::MarkAllInstantiationsForReJit( + pCodeVersionManager, + ilCodeVersion, pAppDomain, pModule, rgMethodDefs[i], @@ -823,15 +735,15 @@ HRESULT ReJitManager::RequestReJIT( } } // for (ULONG i = 0; i < cFunctions; i++) - // For each rejit mgr, if there's work to do, suspend EE if needed, - // enter the rejit mgr's crst, and do the batched work. + // For each code versioning mgr, if there's work to do, suspend EE if needed, + // enter the code versioning mgr's crst, and do the batched work. BOOL fEESuspended = FALSE; - SHash::Iterator beginIter = mgrToJumpStampBatch.Begin(); - SHash::Iterator endIter = mgrToJumpStampBatch.End(); - for (SHash::Iterator iter = beginIter; iter != endIter; iter++) + SHash::Iterator beginIter = mgrToJumpStampBatch.Begin(); + SHash::Iterator endIter = mgrToJumpStampBatch.End(); + for (SHash::Iterator iter = beginIter; iter != endIter; iter++) { - ReJitManagerJumpStampBatch * pJumpStampBatch = *iter; - ReJitManager * pMgr = pJumpStampBatch->pReJitManager; + CodeVersionManager::JumpStampBatch * pJumpStampBatch = *iter; + CodeVersionManager * pCodeVersionManager = pJumpStampBatch->pCodeVersionManager; int cBatchedPreStubMethods = pJumpStampBatch->preStubMethods.Count(); if (cBatchedPreStubMethods == 0) @@ -847,9 +759,9 @@ HRESULT ReJitManager::RequestReJIT( fEESuspended = TRUE; } - CrstHolder ch(&(pMgr->m_crstTable)); + CodeVersionManager::TableLockHolder lock(pCodeVersionManager); _ASSERTE(ThreadStore::HoldingThreadStore()); - hr = pMgr->BatchUpdateJumpStamps(&(pJumpStampBatch->undoMethods), &(pJumpStampBatch->preStubMethods), &errorRecords); + hr = pCodeVersionManager->BatchUpdateJumpStamps(&(pJumpStampBatch->undoMethods), &(pJumpStampBatch->preStubMethods), &errorRecords); if (FAILED(hr)) break; } @@ -870,85 +782,11 @@ HRESULT ReJitManager::RequestReJIT( ReportReJITError(&(errorRecords[i])); } - INDEBUG(SharedDomain::GetDomain()->GetReJitManager()->Dump( - "Finished RequestReJIT(). Dumping Shared ReJitManager\n")); - // We got through processing everything, but profiler will need to see the individual ReJITError // callbacks to know what, if anything, failed. return S_OK; } -//--------------------------------------------------------------------------------------- -// -// Helper used by ReJitManager::RequestReJIT to jump stamp all the methods that were -// specified by the caller. Also used by RejitManager::DoJumpStampForAssemblyIfNecessary -// when rejitting a batch of generic method instantiations in a newly loaded NGEN assembly. -// -// This method is responsible for calling ReJITError on the profiler if anything goes -// wrong. -// -// Arguments: -// * pUndoMethods - array containing the methods that need the jump stamp removed -// * pPreStubMethods - array containing the methods that need to be jump stamped to prestub -// * pErrors - any errors will be appended to this array -// -// Returns: -// S_OK - all methods are updated or added an error to the pErrors array -// E_OUTOFMEMORY - some methods neither updated nor added an error to pErrors array -// ReJitInfo state remains consistent -// -// Assumptions: -// 1) Caller prevents contention by either: -// a) Suspending the runtime -// b) Ensuring all methods being updated haven't been published -// -HRESULT ReJitManager::BatchUpdateJumpStamps(CDynArray * pUndoMethods, CDynArray * pPreStubMethods, CDynArray * pErrors) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - PRECONDITION(CheckPointer(pUndoMethods)); - PRECONDITION(CheckPointer(pPreStubMethods)); - PRECONDITION(CheckPointer(pErrors)); - } - CONTRACTL_END; - - _ASSERTE(m_crstTable.OwnedByCurrentThread()); - HRESULT hr = S_OK; - - ReJitInfo ** ppInfoEnd = pUndoMethods->Ptr() + pUndoMethods->Count(); - for (ReJitInfo ** ppInfoCur = pUndoMethods->Ptr(); ppInfoCur < ppInfoEnd; ppInfoCur++) - { - // If we are undoing jumpstamps they have been published already - // and our caller is holding the EE suspended - _ASSERTE(ThreadStore::HoldingThreadStore()); - if (FAILED(hr = (*ppInfoCur)->UndoJumpStampNativeCode(TRUE))) - { - if (FAILED(hr = AddReJITError(*ppInfoCur, hr, pErrors))) - { - _ASSERTE(hr == E_OUTOFMEMORY); - return hr; - } - } - } - - ppInfoEnd = pPreStubMethods->Ptr() + pPreStubMethods->Count(); - for (ReJitInfo ** ppInfoCur = pPreStubMethods->Ptr(); ppInfoCur < ppInfoEnd; ppInfoCur++) - { - if (FAILED(hr = (*ppInfoCur)->JumpStampNativeCode())) - { - if (FAILED(hr = AddReJITError(*ppInfoCur, hr, pErrors))) - { - _ASSERTE(hr == E_OUTOFMEMORY); - return hr; - } - } - } - return S_OK; -} - //--------------------------------------------------------------------------------------- // // Helper used by ReJitManager::RequestReJIT to iterate through any generic @@ -996,12 +834,13 @@ HRESULT ReJitManager::BatchUpdateJumpStamps(CDynArray * pUndoMethod // HRESULT ReJitManager::MarkAllInstantiationsForReJit( - SharedReJitInfo * pSharedForAllGenericInstantiations, + CodeVersionManager* pCodeVersionManager, + ILCodeVersion ilCodeVersion, AppDomain * pAppDomainToSearch, PTR_Module pModuleContainingMethodDef, mdMethodDef methodDef, - ReJitManagerJumpStampBatch* pJumpStampBatch, - CDynArray * pRejitErrors) + CodeVersionManager::JumpStampBatch* pJumpStampBatch, + CDynArray * pRejitErrors) { CONTRACTL { @@ -1009,25 +848,23 @@ HRESULT ReJitManager::MarkAllInstantiationsForReJit( GC_NOTRIGGER; MODE_PREEMPTIVE; CAN_TAKE_LOCK; - PRECONDITION(CheckPointer(pSharedForAllGenericInstantiations)); + PRECONDITION(CheckPointer(pCodeVersionManager)); PRECONDITION(CheckPointer(pAppDomainToSearch, NULL_OK)); PRECONDITION(CheckPointer(pModuleContainingMethodDef)); PRECONDITION(CheckPointer(pJumpStampBatch)); } CONTRACTL_END; - _ASSERTE(m_crstTable.OwnedByCurrentThread()); + _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread()); _ASSERTE(methodDef != mdTokenNil); - _ASSERTE(pJumpStampBatch->pReJitManager == this); + _ASSERTE(pJumpStampBatch->pCodeVersionManager == pCodeVersionManager); HRESULT hr; BaseDomain * pDomainContainingGenericDefinition = pModuleContainingMethodDef->GetDomain(); #ifdef _DEBUG - // This function should only be called on the ReJitManager that owns the (generic) - // definition of methodDef - _ASSERTE(this == pDomainContainingGenericDefinition->GetReJitManager()); + _ASSERTE(pCodeVersionManager == pDomainContainingGenericDefinition->GetCodeVersionManager()); // If the generic definition is not loaded domain-neutral, then all its // instantiations will also be non-domain-neutral and loaded into the same @@ -1077,7 +914,7 @@ HRESULT ReJitManager::MarkAllInstantiationsForReJit( if (FAILED(hr = IsMethodSafeForReJit(pLoadedMD))) { - if (FAILED(hr = AddReJITError(pModuleContainingMethodDef, methodDef, pLoadedMD, hr, pRejitErrors))) + if (FAILED(hr = CodeVersionManager::AddCodePublishError(pModuleContainingMethodDef, methodDef, pLoadedMD, hr, pRejitErrors))) { _ASSERTE(hr == E_OUTOFMEMORY); return hr; @@ -1097,13 +934,12 @@ HRESULT ReJitManager::MarkAllInstantiationsForReJit( // This will queue up the MethodDesc for rejitting and create all the // look-aside tables needed. - SharedReJitInfo * pSharedUsed = NULL; hr = MarkForReJit( + pCodeVersionManager, pLoadedMD, - pSharedForAllGenericInstantiations, + ilCodeVersion, pJumpStampBatch, - pRejitErrors, - &pSharedUsed); + pRejitErrors); if (FAILED(hr)) { _ASSERTE(hr == E_OUTOFMEMORY); @@ -1114,52 +950,13 @@ HRESULT ReJitManager::MarkAllInstantiationsForReJit( return S_OK; } - -//--------------------------------------------------------------------------------------- -// -// Helper used by ReJitManager::MarkAllInstantiationsForReJit and -// ReJitManager::RequestReJIT to do the actual ReJitInfo allocation and -// placement inside m_table. Note that callers don't use MarkForReJitHelper -// directly. Instead, callers actually use the inlined overloaded wrappers -// ReJitManager::MarkForReJit (one for placeholder (i.e., methodDef pre-rejit) -// ReJitInfos and one for regular (i.e., MethodDesc) ReJitInfos). When the -// overloaded MarkForReJit wrappers call this, they ensure that either pMD is -// valid XOR (pModule, methodDef) is valid. -// -// Arguments: -// * pMD - MethodDesc for which to find / create ReJitInfo. Only used if -// we're creating a regular ReJitInfo -// * pModule - Module for which to find / create ReJitInfo. Only used if -// we're creating a placeholder ReJitInfo -// * methodDef - methodDef for which to find / create ReJitInfo. Only used -// if we're creating a placeholder ReJitInfo -// * pSharedToReuse - SharedReJitInfo to associate any newly created -// ReJitInfo with. If NULL, we'll create a new one. -// * pJumpStampBatch - a batch of methods that need to have jump stamps added -// or removed. This method will add new ReJitInfos to the batch as needed. -// * pRejitErrors - An array of rejit errors that this call will append to -// if there is an error marking -// * ppSharedUsed - [out]: SharedReJitInfo used for this request. If -// pSharedToReuse is non-NULL, *ppSharedUsed == pSharedToReuse. Else, -// *ppSharedUsed is the SharedReJitInfo newly-created to associate with -// the ReJitInfo used for this request. -// -// Return Value: -// * S_OK: Successfully created a new ReJitInfo to manage this request -// * S_FALSE: An existing ReJitInfo was already available to manage this -// request, so we didn't need to create a new one. -// * E_OUTOFMEMORY -// * Else, a failure HRESULT indicating what went wrong. -// - -HRESULT ReJitManager::MarkForReJitHelper( - PTR_MethodDesc pMD, - PTR_Module pModule, +// static +HRESULT ReJitManager::BindILVersion( + CodeVersionManager* pCodeVersionManager, + CodeVersionManager::JumpStampBatch* pJumpStampBatch, + PTR_Module pModule, mdMethodDef methodDef, - SharedReJitInfo * pSharedToReuse, - ReJitManagerJumpStampBatch* pJumpStampBatch, - CDynArray * pRejitErrors, - /* out */ SharedReJitInfo ** ppSharedUsed) + ILCodeVersion *pILCodeVersion) { CONTRACTL { @@ -1167,103 +964,61 @@ HRESULT ReJitManager::MarkForReJitHelper( GC_NOTRIGGER; MODE_PREEMPTIVE; CAN_TAKE_LOCK; - PRECONDITION(CheckPointer(pMD, NULL_OK)); - PRECONDITION(CheckPointer(pModule, NULL_OK)); + PRECONDITION(CheckPointer(pCodeVersionManager)); PRECONDITION(CheckPointer(pJumpStampBatch)); - PRECONDITION(CheckPointer(pRejitErrors)); - PRECONDITION(CheckPointer(ppSharedUsed, NULL_OK)); + PRECONDITION(CheckPointer(pModule)); + PRECONDITION(CheckPointer(pILCodeVersion)); } CONTRACTL_END; - CrstHolder ch(&m_crstTable); - - // Either pMD is valid, xor (pModule,methodDef) is valid - _ASSERTE( - ((pMD != NULL) && (pModule == NULL) && (methodDef == mdTokenNil)) || - ((pMD == NULL) && (pModule != NULL) && (methodDef != mdTokenNil))); - _ASSERTE(pJumpStampBatch->pReJitManager == this); - - if (ppSharedUsed != NULL) - *ppSharedUsed = NULL; - HRESULT hr = S_OK; - - // Check if there was there a previous rejit request for pMD - - ReJitInfoHash::KeyIterator beginIter(&m_table, TRUE /* begin */); - ReJitInfoHash::KeyIterator endIter(&m_table, FALSE /* begin */); + _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread()); + _ASSERTE((pModule != NULL) && (methodDef != mdTokenNil)); - if (pMD != NULL) - { - beginIter = GetBeginIterator(pMD); - endIter = GetEndIterator(pMD); - } - else - { - beginIter = GetBeginIterator(pModule, methodDef); - endIter = GetEndIterator(pModule, methodDef); - } + // Check if there was there a previous rejit request for this method that hasn't been exposed back + // to the profiler yet + ILCodeVersionCollection existingVersions = pCodeVersionManager->GetILCodeVersions(pModule, methodDef); - for (ReJitInfoHash::KeyIterator iter = beginIter; - iter != endIter; + for (ILCodeVersionIterator iter = existingVersions.Begin(); + iter != existingVersions.End(); iter++) { - ReJitInfo * pInfo = *iter; - _ASSERTE(pInfo->m_pShared != NULL); - -#ifdef _DEBUG - if (pMD != NULL) - { - _ASSERTE(pInfo->GetMethodDesc() == pMD); - } - else - { - Module * pModuleTest = NULL; - mdMethodDef methodDefTest = mdTokenNil; - pInfo->GetModuleAndToken(&pModuleTest, &methodDefTest); - _ASSERTE((pModule == pModuleTest) && (methodDef == methodDefTest)); - } -#endif //_DEBUG - - SharedReJitInfo * pShared = pInfo->m_pShared; + ILCodeVersion ilCodeVersion = *iter; - switch (pShared->GetState()) + switch (ilCodeVersion.GetRejitState()) { - case SharedReJitInfo::kStateRequested: + case ILCodeVersion::kStateRequested: // We can 'reuse' this instance because the profiler doesn't know about // it yet. (This likely happened because a profiler called RequestReJIT // twice in a row, without us having a chance to jmp-stamp the code yet OR // while iterating through instantiations of a generic, the iterator found // duplicate entries for the same instantiation.) - _ASSERTE(pShared->m_pbIL == NULL); - _ASSERTE(pInfo->m_pCode == NULL); + _ASSERTE(ilCodeVersion.GetIL() == NULL); - if (ppSharedUsed != NULL) - *ppSharedUsed = pShared; - - INDEBUG(AssertRestOfEntriesAreReverted(iter, endIter)); + *pILCodeVersion = ilCodeVersion; + INDEBUG(AssertRestOfEntriesAreReverted(iter, existingVersions.End())); return S_FALSE; - case SharedReJitInfo::kStateGettingReJITParameters: - case SharedReJitInfo::kStateActive: + case ILCodeVersion::kStateGettingReJITParameters: + case ILCodeVersion::kStateActive: { // Profiler has already requested to rejit this guy, AND we've already // at least started getting the rejit parameters from the profiler. We need to revert this // instance (this will put back the original code) - INDEBUG(AssertRestOfEntriesAreReverted(iter, endIter)); - hr = Revert(pShared, pJumpStampBatch); + INDEBUG(AssertRestOfEntriesAreReverted(iter, existingVersions.End())); + HRESULT hr = Revert(ilCodeVersion, pJumpStampBatch); if (FAILED(hr)) { _ASSERTE(hr == E_OUTOFMEMORY); return hr; } - _ASSERTE(pShared->GetState() == SharedReJitInfo::kStateReverted); + _ASSERTE(ilCodeVersion.GetRejitState() == ILCodeVersion::kStateReverted); // No need to continue looping. Break out of loop to create a new - // ReJitInfo to service the request. + // ILCodeVersion to service the request. goto EXIT_LOOP; } - case SharedReJitInfo::kStateReverted: + case ILCodeVersion::kStateReverted: // just ignore this guy continue; @@ -1273,159 +1028,75 @@ HRESULT ReJitManager::MarkForReJitHelper( } EXIT_LOOP: - // Either there was no ReJitInfo yet for this MethodDesc OR whatever we've found - // couldn't be reused (and needed to be reverted). Create a new ReJitInfo to return + // Either there was no ILCodeVersion yet for this MethodDesc OR whatever we've found + // couldn't be reused (and needed to be reverted). Create a new ILCodeVersion to return // to the caller. - // - // If the caller gave us a pMD that is a new generic instantiation, then the caller - // may also have provided a pSharedToReuse for the generic. Use that instead of - // creating a new one. - - SharedReJitInfo * pShared = NULL; - - if (pSharedToReuse != NULL) - { - pShared = pSharedToReuse; - } - else - { - PTR_LoaderHeap pHeap = NULL; - if (pModule != NULL) - { - pHeap = pModule->GetLoaderAllocator()->GetLowFrequencyHeap(); - } - else - { - pHeap = pMD->GetLoaderAllocator()->GetLowFrequencyHeap(); - } - pShared = new (pHeap, nothrow) SharedReJitInfo; - if (pShared == NULL) - { - return E_OUTOFMEMORY; - } - } - - _ASSERTE(pShared != NULL); - - // ReJitInfos with MethodDesc's need to be jump-stamped, - // ReJitInfos with Module/MethodDef are placeholders that don't need a stamp - ReJitInfo * pInfo = NULL; - ReJitInfo ** ppInfo = &pInfo; - if (pMD != NULL) - { - ppInfo = pJumpStampBatch->preStubMethods.Append(); - if (ppInfo == NULL) - { - return E_OUTOFMEMORY; - } - } - hr = AddNewReJitInfo(pMD, pModule, methodDef, pShared, ppInfo); - if (FAILED(hr)) - { - // NOTE: We could consider using an AllocMemTracker or AllocMemHolder - // here to back out the allocation of pShared, but it probably - // wouldn't make much of a difference. We'll only get here if we ran - // out of memory allocating the pInfo, so our memory has already been - // blown. We can't cause much leaking due to this error path. - _ASSERTE(hr == E_OUTOFMEMORY); - return hr; - } - - _ASSERTE(*ppInfo != NULL); - - if (ppSharedUsed != NULL) - *ppSharedUsed = pShared; - - return S_OK; + return pCodeVersionManager->AddILCodeVersion(pModule, methodDef, InterlockedIncrement(reinterpret_cast(&s_GlobalReJitId)), pILCodeVersion); } //--------------------------------------------------------------------------------------- // -// Helper used by the above helpers (and also during jump-stamping) to -// allocate and store a new ReJitInfo. +// Helper used by ReJitManager::MarkAllInstantiationsForReJit and +// ReJitManager::RequestReJIT to do the actual ReJitInfo allocation and +// placement inside m_table. // // Arguments: -// * pMD - MethodDesc for which to create ReJitInfo. Only used if we're -// creating a regular ReJitInfo -// * pModule - Module for which create ReJitInfo. Only used if we're -// creating a placeholder ReJitInfo -// * methodDef - methodDef for which to create ReJitInfo. Only used if -// we're creating a placeholder ReJitInfo -// * pShared - SharedReJitInfo to associate the newly created ReJitInfo -// with. -// * ppInfo - [out]: ReJitInfo created +// * pMD - MethodDesc for which to find / create ReJitInfo. Only used if +// we're creating a regular ReJitInfo +// * ilCodeVersion - ILCodeVersion to associate any newly created +// ReJitInfo with. +// * pJumpStampBatch - a batch of methods that need to have jump stamps added +// or removed. This method will add new ReJitInfos to the batch as needed. +// * pRejitErrors - An array of rejit errors that this call will append to +// if there is an error marking // // Return Value: -// * S_OK: ReJitInfo successfully created & stored. -// * Else, failure indicating the problem. Currently only E_OUTOFMEMORY. -// -// Assumptions: -// * Caller should be holding this ReJitManager's table crst. +// * S_OK: Successfully created a new ReJitInfo to manage this request +// * S_FALSE: An existing ReJitInfo was already available to manage this +// request, so we didn't need to create a new one. +// * E_OUTOFMEMORY +// * Else, a failure HRESULT indicating what went wrong. // -HRESULT ReJitManager::AddNewReJitInfo( +HRESULT ReJitManager::MarkForReJit( + CodeVersionManager* pCodeVersionManager, PTR_MethodDesc pMD, - PTR_Module pModule, - mdMethodDef methodDef, - SharedReJitInfo * pShared, - ReJitInfo ** ppInfo) + ILCodeVersion ilCodeVersion, + CodeVersionManager::JumpStampBatch* pJumpStampBatch, + CDynArray * pRejitErrors) { CONTRACTL { NOTHROW; GC_NOTRIGGER; - MODE_ANY; + MODE_PREEMPTIVE; CAN_TAKE_LOCK; - PRECONDITION(CheckPointer(pMD, NULL_OK)); - PRECONDITION(CheckPointer(pModule, NULL_OK)); - PRECONDITION(CheckPointer(pShared)); - PRECONDITION(CheckPointer(ppInfo)); + PRECONDITION(CheckPointer(pCodeVersionManager)); + PRECONDITION(CheckPointer(pMD)); + PRECONDITION(CheckPointer(pJumpStampBatch)); + PRECONDITION(CheckPointer(pRejitErrors)); } CONTRACTL_END; - _ASSERTE(m_crstTable.OwnedByCurrentThread()); - _ASSERTE(pShared->GetState() != SharedReJitInfo::kStateReverted); + _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread()); + _ASSERTE(pJumpStampBatch->pCodeVersionManager == pCodeVersionManager); - // Either pMD is valid, xor (pModule,methodDef) is valid - _ASSERTE( - ((pMD != NULL) && (pModule == NULL) && (methodDef == mdTokenNil)) || - ((pMD == NULL) && (pModule != NULL) && (methodDef != mdTokenNil))); + HRESULT hr = S_OK; - HRESULT hr; - ReJitInfo * pInfo = NULL; - if (pMD != NULL) - { - PTR_LoaderHeap pHeap = pMD->GetLoaderAllocator()->GetLowFrequencyHeap(); - pInfo = new (pHeap, nothrow) ReJitInfo(pMD, pShared); - } - else - { - PTR_LoaderHeap pHeap = pModule->GetLoaderAllocator()->GetLowFrequencyHeap(); - pInfo = new (pHeap, nothrow) ReJitInfo(pModule, methodDef, pShared); - } - if (pInfo == NULL) + // ReJitInfos with MethodDesc's need to be jump-stamped, + NativeCodeVersion * pNativeCodeVersion = pJumpStampBatch->preStubMethods.Append(); + if (pNativeCodeVersion == NULL) { return E_OUTOFMEMORY; } - - hr = S_OK; - EX_TRY - { - // This guy throws when out of memory, but remains internally - // consistent (without adding the new element) - m_table.Add(pInfo); - } - EX_CATCH_HRESULT(hr); - - _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY); + hr = ilCodeVersion.AddNativeCodeVersion(pMD, pNativeCodeVersion); if (FAILED(hr)) { - pInfo = NULL; + _ASSERTE(hr == E_OUTOFMEMORY); return hr; } - *ppInfo = pInfo; return S_OK; } @@ -1472,77 +1143,51 @@ HRESULT ReJitManager::DoJumpStampIfNecessary(MethodDesc* pMD, PCODE pCode) HRESULT hr; - _ASSERTE(IsTableCrstOwnedByCurrentThread()); - - ReJitInfo * pInfoToJumpStamp = NULL; + CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager(); + _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread()); - // First, try looking up ReJitInfo by MethodDesc. A "regular" MethodDesc-based - // ReJitInfo already exists for "case 1" (see comment above - // code:ReJitInfo::JumpStampNativeCode), and could even exist for "case 2" - // (pre-rejit), if either: - // * The pre-rejit was requested after the MD had already been loaded (though - // before it had been jitted) OR - // * there was a race to JIT the original code for the MD, and another thread got - // here before us and already added the ReJitInfo for that MD. + ILCodeVersionCollection ilCodeVersions = pCodeVersionManager->GetILCodeVersions(pMD->GetModule(), pMD->GetMemberDef()); + ILCodeVersion ilCodeVersionToJumpStamp = ILCodeVersion(); - ReJitInfoHash::KeyIterator beginIter = GetBeginIterator(pMD); - ReJitInfoHash::KeyIterator endIter = GetEndIterator(pMD); - - pInfoToJumpStamp = FindPreReJittedReJitInfo(beginIter, endIter); - if (pInfoToJumpStamp != NULL) + for (ILCodeVersionIterator iter = ilCodeVersions.Begin(); + iter != ilCodeVersions.End(); + iter++) { - _ASSERTE(pInfoToJumpStamp->GetMethodDesc() == pMD); - // does it need to be jump-stamped? - if (pInfoToJumpStamp->GetState() != ReJitInfo::kJumpNone) + ILCodeVersion curVersion = *iter; + switch (curVersion.GetRejitState()) { - return S_OK; - } - else - { - return pInfoToJumpStamp->JumpStampNativeCode(pCode); + case ILCodeVersion::kStateRequested: + case ILCodeVersion::kStateGettingReJITParameters: + case ILCodeVersion::kStateActive: + INDEBUG(AssertRestOfEntriesAreReverted(iter, ilCodeVersions.End())); + ilCodeVersionToJumpStamp = curVersion; + break; + case ILCodeVersion::kStateReverted: + // just ignore this guy + continue; + + default: + UNREACHABLE(); } } - // In this case, try looking up by module / metadata token. This is the case where - // the pre-rejit request occurred before the MD was loaded. - - Module * pModule = pMD->GetModule(); - _ASSERTE(pModule != NULL); - mdMethodDef methodDef = pMD->GetMemberDef(); - - beginIter = GetBeginIterator(pModule, methodDef); - endIter = GetEndIterator(pModule, methodDef); - ReJitInfo * pInfoPlaceholder = NULL; - - pInfoPlaceholder = FindPreReJittedReJitInfo(beginIter, endIter); - if (pInfoPlaceholder == NULL) + if (ilCodeVersionToJumpStamp.IsNull()) { - // No jump stamping to do. + //Method not requested to be rejitted, nothing to do return S_OK; } - // The placeholder may already have a rejit info for this MD, in which - // case we don't need to do any additional work - for (ReJitInfo * pInfo = pInfoPlaceholder->m_pShared->GetMethods(); pInfo != NULL; pInfo = pInfo->m_pNext) + MethodDescVersioningState* pVersioningState; + if (FAILED(hr = pCodeVersionManager->GetOrCreateMethodVersioningState(pMD, &pVersioningState))) { - if ((pInfo->GetKey().m_keyType == ReJitInfo::Key::kMethodDesc) && - (pInfo->GetMethodDesc() == pMD)) - { - // Any rejit info we find should already be jumpstamped - _ASSERTE(pInfo->GetState() != ReJitInfo::kJumpNone); - return S_OK; - } + return hr; } - -#ifdef _DEBUG + if (pVersioningState->GetJumpStampState() != MethodDescVersioningState::JumpStampNone) { - Module * pModuleTest = NULL; - mdMethodDef methodDefTest = mdTokenNil; - INDEBUG(pInfoPlaceholder->GetModuleAndToken(&pModuleTest, &methodDefTest)); - _ASSERTE((pModule == pModuleTest) && (methodDef == methodDefTest)); + //JumpStamp already in place + return S_OK; } -#endif //_DEBUG - + // We have finished JITting the original code for a function that had been // "pre-rejitted" (i.e., requested to be rejitted before it was first compiled). So // now is the first time where we know the MethodDesc of the request. @@ -1552,17 +1197,7 @@ HRESULT ReJitManager::DoJumpStampIfNecessary(MethodDesc* pMD, PCODE pCode) return hr; } - // Create the ReJitInfo associated with the MethodDesc now (pInfoToJumpStamp), and - // jump-stamp the original code. - pInfoToJumpStamp = NULL; - hr = AddNewReJitInfo(pMD, NULL /*pModule*/, NULL /*methodDef*/, pInfoPlaceholder->m_pShared, &pInfoToJumpStamp); - if (FAILED(hr)) - { - return hr; - } - - _ASSERTE(pInfoToJumpStamp != NULL); - return pInfoToJumpStamp->JumpStampNativeCode(pCode); + return pVersioningState->JumpStampNativeCode(pCode); } //--------------------------------------------------------------------------------------- @@ -1626,7 +1261,7 @@ HRESULT ReJitManager::RequestRevert( } else { - hr = pModule->GetReJitManager()->RequestRevertByToken(pModule, rgMethodDefs[i]); + hr = ReJitManager::RequestRevertByToken(pModule, rgMethodDefs[i]); } if (rgHrStatuses != NULL) @@ -1640,50 +1275,12 @@ HRESULT ReJitManager::RequestRevert( return S_OK; } + //--------------------------------------------------------------------------------------- // -// Called by AppDomain::Exit() to notify the SharedDomain's ReJitManager that this -// AppDomain is exiting. The SharedDomain's ReJitManager will then remove any -// ReJitInfos relating to MDs owned by AppDomain. This is how we remove -// non-domain-neutral instantiations of domain-neutral generics from the SharedDomain's -// ReJitManager. -// -// Arguments: -// pAppDomain - AppDomain that is exiting. -// - -// static -void ReJitManager::OnAppDomainExit(AppDomain * pAppDomain) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - CAN_TAKE_LOCK; - MODE_ANY; - } - CONTRACTL_END; - - // All ReJitInfos and SharedReJitInfos for this AD's ReJitManager automatically get - // cleaned up as they're allocated on the AD's loader heap. - - // We explicitly clean up the SHash here, as its entries get allocated using regular - // "new" - pAppDomain->GetReJitManager()->m_table.RemoveAll(); - - // We need to ensure that any MethodDescs from pAppDomain that are stored on the - // SharedDomain's ReJitManager get removed from the SharedDomain's ReJitManager's - // hash table, and from the linked lists tied to their owning SharedReJitInfo. (This - // covers the case of non-domain-neutral instantiations of domain-neutral generics.) - SharedDomain::GetDomain()->GetReJitManager()->RemoveReJitInfosFromDomain(pAppDomain); -} - - -//--------------------------------------------------------------------------------------- -// -// Small helper to determine whether a given (possibly instantiated generic) MethodDesc -// is safe to rejit. If not, this function is responsible for calling into the -// profiler's ReJITError() +// Small helper to determine whether a given (possibly instantiated generic) MethodDesc +// is safe to rejit. If not, this function is responsible for calling into the +// profiler's ReJITError() // // Arguments: // pMD - MethodDesc to test @@ -1779,6 +1376,8 @@ HRESULT ReJitManager::RequestRevertByToken(PTR_Module pModule, mdMethodDef metho CONTRACTL_END; _ASSERTE(ThreadStore::HoldingThreadStore()); + return E_NOTIMPL; + /* CrstHolder ch(&m_crstTable); _ASSERTE(pModule != NULL); @@ -1821,7 +1420,7 @@ HRESULT ReJitManager::RequestRevertByToken(PTR_Module pModule, mdMethodDef metho _ASSERTE(FAILED(errorRecords[i].hrStatus)); return errorRecords[i].hrStatus; } - return S_OK; + return S_OK;*/ } #ifdef _MSC_VER #pragma warning(pop) @@ -1878,22 +1477,23 @@ HRESULT ReJitManager::RequestRevertByToken(PTR_Module pModule, mdMethodDef metho // MethodDesc) // -PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD) +PCODE ReJitManager::DoReJitIfNecessaryWorker(MethodDesc* pMD) { STANDARD_VM_CONTRACT; - _ASSERTE(!IsTableCrstOwnedByCurrentThread()); + CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager(); + _ASSERTE(!pCodeVersionManager->LockOwnedByCurrentThread()); // Fast-path: If the rejit map is empty, no need to look up anything. Do this outside // of a lock to impact our caller (the prestub worker) as little as possible. If the // map is nonempty, we'll acquire the lock at that point and do the lookup for real. - if (m_table.GetCount() == 0) + if (pCodeVersionManager->GetNonDefaultILVersionCount() == 0) { return NULL; } HRESULT hr = S_OK; - ReJitInfo * pInfoToRejit = NULL; + ILCodeVersion ilCodeVersion; Module* pModule = NULL; mdMethodDef methodDef = mdTokenNil; BOOL fNeedsParameters = FALSE; @@ -1902,10 +1502,20 @@ PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD) { // Serialize access to the rejit table. Though once we find the ReJitInfo we want, // exit the Crst so we can ReJIT the method without holding a lock. - CrstHolder ch(&m_crstTable); + CodeVersionManager::TableLockHolder lock(pCodeVersionManager); + + MethodDescVersioningState* pVersioningState = pCodeVersionManager->GetMethodVersioningState(pMD); + if (pVersioningState == NULL || pVersioningState->GetJumpStampState() == MethodDescVersioningState::JumpStampNone) + { + // We haven't yet installed a jumpstamp so we shouldn't be rejiting with the new IL yet + return NULL; + } - ReJitInfoHash::KeyIterator iter = GetBeginIterator(pMD); - ReJitInfoHash::KeyIterator end = GetEndIterator(pMD); + pModule = pMD->GetModule(); + methodDef = pMD->GetMemberDef(); + ILCodeVersionCollection versions = pCodeVersionManager->GetILCodeVersions(pModule, methodDef); + ILCodeVersionIterator iter = versions.Begin(); + ILCodeVersionIterator end = versions.End(); if (iter == end) { @@ -1913,68 +1523,41 @@ PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD) return NULL; } - for (; iter != end; iter++) { - ReJitInfo * pInfo = *iter; - _ASSERTE(pInfo->GetMethodDesc() == pMD); - _ASSERTE(pInfo->m_pShared != NULL); - SharedReJitInfo * pShared = pInfo->m_pShared; + ILCodeVersion curVersion = *iter; - switch (pShared->GetState()) + switch (curVersion.GetRejitState()) { - case SharedReJitInfo::kStateRequested: - if (pInfo->GetState() == ReJitInfo::kJumpNone) - { - // We haven't actually suspended threads and jump-stamped the - // method's prolog so just ignore this guy - INDEBUG(AssertRestOfEntriesAreReverted(iter, end)); - return NULL; - } + case ILCodeVersion::kStateRequested: // When the SharedReJitInfo is still in the requested state, we haven't // gathered IL & codegen flags from the profiler yet. So, we can't be // pointing to rejitted code already. So we must be pointing to the prestub - _ASSERTE(pInfo->GetState() == ReJitInfo::kJumpToPrestub); - - pInfo->GetModuleAndTokenRegardlessOfKeyType(&pModule, &methodDef); - pShared->m_dwInternalFlags &= ~SharedReJitInfo::kStateMask; - pShared->m_dwInternalFlags |= SharedReJitInfo::kStateGettingReJITParameters; - pInfoToRejit = pInfo; + _ASSERTE(pVersioningState->GetJumpStampState() == MethodDescVersioningState::JumpStampToPrestub); + curVersion.SetRejitState(ILCodeVersion::kStateGettingReJITParameters); + ilCodeVersion = curVersion; fNeedsParameters = TRUE; break; - case SharedReJitInfo::kStateGettingReJITParameters: - if (pInfo->GetState() == ReJitInfo::kJumpNone) - { - // We haven't actually suspended threads and jump-stamped the - // method's prolog so just ignore this guy - INDEBUG(AssertRestOfEntriesAreReverted(iter, end)); - return NULL; - } - pInfoToRejit = pInfo; + case ILCodeVersion::kStateGettingReJITParameters: + ilCodeVersion = curVersion; fWaitForParameters = TRUE; break; - case SharedReJitInfo::kStateActive: + case ILCodeVersion::kStateActive: INDEBUG(AssertRestOfEntriesAreReverted(iter, end)); - if (pInfo->GetState() == ReJitInfo::kJumpNone) - { - // We haven't actually suspended threads and jump-stamped the - // method's prolog so just ignore this guy - return NULL; - } - if (pInfo->GetState() == ReJitInfo::kJumpToRejittedCode) + if (pVersioningState->GetJumpStampState() == MethodDescVersioningState::JumpStampToActiveVersion) { // Looks like another thread has beat us in a race to rejit, so ignore. return NULL; } // Found a ReJitInfo to actually rejit. - _ASSERTE(pInfo->GetState() == ReJitInfo::kJumpToPrestub); - pInfoToRejit = pInfo; + _ASSERTE(pVersioningState->GetJumpStampState() == MethodDescVersioningState::JumpStampToPrestub); + ilCodeVersion = curVersion; goto ExitLoop; - case SharedReJitInfo::kStateReverted: + case ILCodeVersion::kStateReverted: // just ignore this guy continue; @@ -1986,7 +1569,7 @@ PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD) ; } - if (pInfoToRejit == NULL) + if (ilCodeVersion.IsNull()) { // Didn't find the requested MD to rejit. return NULL; @@ -2021,11 +1604,10 @@ PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD) if (FAILED(hr)) { { - CrstHolder ch(&m_crstTable); - if (pInfoToRejit->m_pShared->m_dwInternalFlags == SharedReJitInfo::kStateGettingReJITParameters) + CodeVersionManager::TableLockHolder lock(pCodeVersionManager); + if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateGettingReJITParameters) { - pInfoToRejit->m_pShared->m_dwInternalFlags &= ~SharedReJitInfo::kStateMask; - pInfoToRejit->m_pShared->m_dwInternalFlags |= SharedReJitInfo::kStateRequested; + ilCodeVersion.SetRejitState(ILCodeVersion::kStateRequested); } } ReportReJITError(pModule, methodDef, pMD, hr); @@ -2033,21 +1615,22 @@ PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD) } { - CrstHolder ch(&m_crstTable); - if (pInfoToRejit->m_pShared->m_dwInternalFlags == SharedReJitInfo::kStateGettingReJITParameters) + CodeVersionManager::TableLockHolder lock(pCodeVersionManager); + if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateGettingReJITParameters) { // Inside the above call to ICorProfilerCallback4::GetReJITParameters, the profiler // will have used the specified pFuncControl to provide its IL and codegen flags. // So now we transfer it out to the SharedReJitInfo. - pInfoToRejit->m_pShared->m_dwCodegenFlags = pFuncControl->GetCodegenFlags(); - pInfoToRejit->m_pShared->m_pbIL = pFuncControl->GetIL(); - // pShared is now the owner of the memory for the IL buffer - pInfoToRejit->m_pShared->m_instrumentedILMap.SetMappingInfo(pFuncControl->GetInstrumentedMapEntryCount(), + ilCodeVersion.SetJitFlags(pFuncControl->GetCodegenFlags()); + ilCodeVersion.SetIL((COR_ILMETHOD*)pFuncControl->GetIL()); + // ilCodeVersion is now the owner of the memory for the IL buffer + ilCodeVersion.SetInstrumentedILMap(pFuncControl->GetInstrumentedMapEntryCount(), pFuncControl->GetInstrumentedMapEntries()); - pInfoToRejit->m_pShared->m_dwInternalFlags &= ~SharedReJitInfo::kStateMask; - pInfoToRejit->m_pShared->m_dwInternalFlags |= SharedReJitInfo::kStateActive; - _ASSERTE(pInfoToRejit->m_pCode == NULL); - _ASSERTE(pInfoToRejit->GetState() == ReJitInfo::kJumpToPrestub); + ilCodeVersion.SetRejitState(ILCodeVersion::kStateActive); +#ifdef DEBUG + MethodDescVersioningState* pVersioningState = pCodeVersionManager->GetMethodVersioningState(pMD); + _ASSERTE(pVersioningState->GetJumpStampState() == MethodDescVersioningState::JumpStampToPrestub); +#endif } } } @@ -2077,17 +1660,17 @@ PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD) while (true) { { - CrstHolder ch(&m_crstTable); - if (pInfoToRejit->m_pShared->GetState() == SharedReJitInfo::kStateActive) + CodeVersionManager::TableLockHolder lock(pCodeVersionManager); + if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateActive) { break; // the other thread got the parameters succesfully, go race to rejit } - else if (pInfoToRejit->m_pShared->GetState() == SharedReJitInfo::kStateRequested) + else if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateRequested) { return NULL; // the other thread had an error getting parameters and went // back to requested } - else if (pInfoToRejit->m_pShared->GetState() == SharedReJitInfo::kStateReverted) + else if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateReverted) { break; // we got reverted, enter DoReJit anyways and it will detect this and // bail out. @@ -2096,14 +1679,14 @@ PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD) ClrSleepEx(1, FALSE); } } - + // We've got the info from the profiler, so JIT the method. This is also // responsible for updating the jump target from the prestub to the newly // rejitted code AND for publishing the top of the newly rejitted code to // pInfoToRejit->m_pCode. If two threads race to rejit, DoReJit handles the // race, and ensures the winner publishes his result to pInfoToRejit->m_pCode. - return DoReJit(pInfoToRejit); - + return DoReJit(ilCodeVersion, pMD); + } @@ -2132,24 +1715,29 @@ PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD) // (i.e., pInfo->GetMethodDesc()->GetNativeCode()) // -PCODE ReJitManager::DoReJit(ReJitInfo * pInfo) +PCODE ReJitManager::DoReJit(ILCodeVersion ilCodeVersion, MethodDesc* pMethod) { STANDARD_VM_CONTRACT; #ifdef PROFILING_SUPPORTED - INDEBUG(Dump("Inside DoRejit(). Dumping this ReJitManager\n")); - - _ASSERTE(!pInfo->GetMethodDesc()->IsNoMetadata()); + _ASSERTE(!pMethod->IsNoMetadata()); { BEGIN_PIN_PROFILER(CORProfilerTrackJITInfo()); - g_profControlBlock.pProfInterface->ReJITCompilationStarted((FunctionID)pInfo->GetMethodDesc(), - pInfo->m_pShared->GetId(), + g_profControlBlock.pProfInterface->ReJITCompilationStarted((FunctionID)pMethod, + ilCodeVersion.GetVersionId(), TRUE); END_PIN_PROFILER(); } - COR_ILMETHOD_DECODER ILHeader(pInfo->GetIL(), pInfo->GetMethodDesc()->GetMDImport(), NULL); + COR_ILMETHOD* pIL = ilCodeVersion.GetIL(); + if (pIL == NULL) + { + // If the user hasn't overriden us, get whatever the original IL had + pIL = pMethod->GetILHeader(TRUE); + } + + COR_ILMETHOD_DECODER ILHeader(pIL, pMethod->GetMDImport(), NULL); PCODE pCodeOfRejittedCode = NULL; // Note that we're intentionally not enclosing UnsafeJitFunction in a try block @@ -2162,9 +1750,9 @@ PCODE ReJitManager::DoReJit(ReJitInfo * pInfo) // encountered on the current thread and not on the competing thread), which is // not worth attempting to cover. pCodeOfRejittedCode = UnsafeJitFunction( - pInfo->GetMethodDesc(), + pMethod, &ILHeader, - JitFlagsFromProfCodegenFlags(pInfo->m_pShared->m_dwCodegenFlags)); + JitFlagsFromProfCodegenFlags(ilCodeVersion.GetJitFlags())); _ASSERTE(pCodeOfRejittedCode != NULL); @@ -2181,16 +1769,29 @@ PCODE ReJitManager::DoReJit(ReJitInfo * pInfo) { ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT); } - CrstHolder ch(&m_crstTable); + + CodeVersionManager* pCodeVersionManager = ilCodeVersion.GetModule()->GetCodeVersionManager(); + CodeVersionManager::TableLockHolder lock(pCodeVersionManager); + + + NativeCodeVersion activeNativeCodeVersion = ilCodeVersion.GetActiveNativeCodeVersion(pMethod); + if (activeNativeCodeVersion.IsNull()) + { + hr = ilCodeVersion.AddNativeCodeVersion(pMethod, &activeNativeCodeVersion); + if (FAILED(hr)) + { + break; + } + } // Now that we're under the lock, recheck whether pInfo->m_pCode has been filled // in... - if (pInfo->m_pCode != NULL) + if (activeNativeCodeVersion.GetNativeCode() != NULL) { // Yup, another thread rejitted this request at the same time as us, and beat // us to publishing the result. Intentionally skip the rest of this, and do // not issue a ReJITCompilationFinished from this thread. - ret = pInfo->m_pCode; + ret = activeNativeCodeVersion.GetNativeCode(); break; } @@ -2232,13 +1833,13 @@ PCODE ReJitManager::DoReJit(ReJitInfo * pInfo) // meantime (this check would also include an attempt to re-rejit the method // (i.e., calling RequestReJIT on the method multiple times), which would revert // this pInfo before creating a new one to track the latest rejit request). - if (pInfo->m_pShared->GetState() == SharedReJitInfo::kStateReverted) + if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateReverted) { // Yes, we've been reverted, so the jmp-to-prestub has already been removed, // and we should certainly not attempt to redirect that nonexistent jmp to // the code we just rejitted - _ASSERTE(pInfo->GetMethodDesc()->GetNativeCode() != NULL); - ret = pInfo->GetMethodDesc()->GetNativeCode(); + _ASSERTE(pMethod->GetNativeCode() != NULL); + ret = pMethod->GetNativeCode(); break; } @@ -2251,17 +1852,18 @@ PCODE ReJitManager::DoReJit(ReJitInfo * pInfo) // now is a good place to notify the debugger. if (g_pDebugInterface != NULL) { - g_pDebugInterface->JITComplete(pInfo->GetMethodDesc(), pCodeOfRejittedCode); + g_pDebugInterface->JITComplete(pMethod, pCodeOfRejittedCode); } #endif // DEBUGGING_SUPPORTED - _ASSERTE(pInfo->m_pShared->GetState() == SharedReJitInfo::kStateActive); - _ASSERTE(pInfo->GetState() == ReJitInfo::kJumpToPrestub); + _ASSERTE(ilCodeVersion.GetRejitState() == ILCodeVersion::kStateActive); + MethodDescVersioningState* pVersioningState = pCodeVersionManager->GetMethodVersioningState(pMethod); + _ASSERTE(pVersioningState->GetJumpStampState() == MethodDescVersioningState::JumpStampToPrestub); // Atomically publish the PCODE and update the jmp stamp (to go to the rejitted // code) under the lock - hr = pInfo->UpdateJumpTarget(fEESuspended, pCodeOfRejittedCode); + hr = pVersioningState->UpdateJumpTarget(fEESuspended, pCodeOfRejittedCode); if (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED) { _ASSERTE(!fEESuspended); @@ -2272,35 +1874,32 @@ PCODE ReJitManager::DoReJit(ReJitInfo * pInfo) { break; } - pInfo->m_pCode = pCodeOfRejittedCode; + activeNativeCodeVersion.SetNativeCodeInterlocked(pCodeOfRejittedCode); fNotify = TRUE; ret = pCodeOfRejittedCode; - _ASSERTE(pInfo->m_pShared->GetState() == SharedReJitInfo::kStateActive); - _ASSERTE(pInfo->GetState() == ReJitInfo::kJumpToRejittedCode); + _ASSERTE(ilCodeVersion.GetRejitState() == ILCodeVersion::kStateActive); + _ASSERTE(pVersioningState->GetJumpStampState() == MethodDescVersioningState::JumpStampToActiveVersion); break; } if (fEESuspended) { - ThreadSuspend::RestartEE(FALSE /* bFinishedGC */, TRUE /* SuspendSucceded */); + ThreadSuspend::RestartEE(FALSE, TRUE); fEESuspended = FALSE; } if (FAILED(hr)) { - Module* pModule = NULL; - mdMethodDef methodDef = mdTokenNil; - pInfo->GetModuleAndTokenRegardlessOfKeyType(&pModule, &methodDef); - ReportReJITError(pModule, methodDef, pInfo->GetMethodDesc(), hr); + ReportReJITError(ilCodeVersion.GetModule(), ilCodeVersion.GetMethodDef(), pMethod, hr); } // Notify the profiler that JIT completed. if (fNotify) { BEGIN_PIN_PROFILER(CORProfilerTrackJITInfo()); - g_profControlBlock.pProfInterface->ReJITCompilationFinished((FunctionID)pInfo->GetMethodDesc(), - pInfo->m_pShared->GetId(), + g_profControlBlock.pProfInterface->ReJITCompilationFinished((FunctionID)pMethod, + ilCodeVersion.GetVersionId(), S_OK, TRUE); END_PIN_PROFILER(); @@ -2311,12 +1910,12 @@ PCODE ReJitManager::DoReJit(ReJitInfo * pInfo) if (fNotify) { ETW::MethodLog::MethodJitted( - pInfo->GetMethodDesc(), + pMethod, NULL, // namespaceOrClassName NULL, // methodName NULL, // methodSignature pCodeOfRejittedCode, - pInfo->m_pShared->GetId()); + ilCodeVersion.GetVersionId()); } return ret; } @@ -2340,7 +1939,7 @@ PCODE ReJitManager::DoReJit(ReJitInfo * pInfo) // Caller must be holding this ReJitManager's table crst. // -HRESULT ReJitManager::Revert(SharedReJitInfo * pShared, ReJitManagerJumpStampBatch* pJumpStampBatch) +HRESULT ReJitManager::Revert(ILCodeVersion ilCodeVersion, CodeVersionManager::JumpStampBatch* pJumpStampBatch) { CONTRACTL { @@ -2350,13 +1949,17 @@ HRESULT ReJitManager::Revert(SharedReJitInfo * pShared, ReJitManagerJumpStampBat } CONTRACTL_END; - _ASSERTE(m_crstTable.OwnedByCurrentThread()); - _ASSERTE((pShared->GetState() == SharedReJitInfo::kStateRequested) || - (pShared->GetState() == SharedReJitInfo::kStateGettingReJITParameters) || - (pShared->GetState() == SharedReJitInfo::kStateActive)); - _ASSERTE(pShared->GetMethods() != NULL); - _ASSERTE(pJumpStampBatch->pReJitManager == this); + CodeVersionManager* pCodeVersionManager = ilCodeVersion.GetModule()->GetCodeVersionManager(); + + _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread()); + _ASSERTE((ilCodeVersion.GetRejitState() == ILCodeVersion::kStateRequested) || + (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateGettingReJITParameters) || + (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateActive)); + _ASSERTE(pJumpStampBatch->pCodeVersionManager == pCodeVersionManager); + //TODO + return E_NOTIMPL; + /* HRESULT hrReturn = S_OK; for (ReJitInfo * pInfo = pShared->GetMethods(); pInfo != NULL; pInfo = pInfo->m_pNext) { @@ -2377,150 +1980,14 @@ HRESULT ReJitManager::Revert(SharedReJitInfo * pShared, ReJitManagerJumpStampBat pShared->m_dwInternalFlags &= ~SharedReJitInfo::kStateMask; pShared->m_dwInternalFlags |= SharedReJitInfo::kStateReverted; return S_OK; + */ } -//--------------------------------------------------------------------------------------- -// -// Removes any ReJitInfos relating to MDs for the specified AppDomain from this -// ReJitManager. This is used to remove non-domain-neutral instantiations of -// domain-neutral generics from the SharedDomain's ReJitManager, when the AppDomain -// containing those non-domain-neutral instantiations is unloaded. -// -// Arguments: -// * pAppDomain - AppDomain that is exiting, and is thus the one for which we should -// find ReJitInfos to remove -// -// - -void ReJitManager::RemoveReJitInfosFromDomain(AppDomain * pAppDomain) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - CAN_TAKE_LOCK; - MODE_ANY; - } - CONTRACTL_END; - - CrstHolder ch(&m_crstTable); - - INDEBUG(Dump("Dumping SharedDomain rejit manager BEFORE AD Unload")); - - for (ReJitInfoHash::Iterator iterCur = m_table.Begin(), iterEnd = m_table.End(); - iterCur != iterEnd; - iterCur++) - { - ReJitInfo * pInfo = *iterCur; - - if (pInfo->m_key.m_keyType != ReJitInfo::Key::kMethodDesc) - { - // Skip all "placeholder" ReJitInfos--they'll always be allocated on a - // loader heap for the shared domain. - _ASSERTE(pInfo->m_key.m_keyType == ReJitInfo::Key::kMetadataToken); - _ASSERTE(PTR_Module(pInfo->m_key.m_pModule)->GetDomain()->IsSharedDomain()); - continue; - } - - if (pInfo->GetMethodDesc()->GetDomain() != pAppDomain) - { - // We only care about non-domain-neutral instantiations that live in - // pAppDomain. - continue; - } - - // Remove this ReJitInfo from the linked-list of ReJitInfos associated with its - // SharedReJitInfo. - pInfo->m_pShared->RemoveMethod(pInfo); - - // Remove this ReJitInfo from the ReJitManager's hash table. - m_table.Remove(iterCur); - - // pInfo is not deallocated yet. That will happen when pAppDomain finishes - // unloading and its loader heaps get freed. - } - INDEBUG(Dump("Dumping SharedDomain rejit manager AFTER AD Unload")); -} - #endif // DACCESS_COMPILE // The rest of the ReJitManager methods are safe to compile for DAC -//--------------------------------------------------------------------------------------- -// -// Helper to iterate through m_table, finding the single matching non-reverted ReJitInfo. -// The caller may search either by MethodDesc * XOR by (Module *, methodDef) pair. -// -// Arguments: -// * pMD - MethodDesc * to search for. (NULL if caller is searching by (Module *, -// methodDef) -// * pModule - Module * to search for. (NULL if caller is searching by MethodDesc *) -// * methodDef - methodDef to search for. (NULL if caller is searching by MethodDesc -// *) -// -// Return Value: -// ReJitInfo * requested, or NULL if none is found -// -// Assumptions: -// Caller should be holding this ReJitManager's table crst. -// - -PTR_ReJitInfo ReJitManager::FindNonRevertedReJitInfoHelper( - PTR_MethodDesc pMD, - PTR_Module pModule, - mdMethodDef methodDef) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - INSTANCE_CHECK; - } - CONTRACTL_END; - - // Either pMD is valid, xor (pModule,methodDef) is valid - _ASSERTE( - ((pMD != NULL) && (pModule == NULL) && (methodDef == mdTokenNil)) || - ((pMD == NULL) && (pModule != NULL) && (methodDef != mdTokenNil))); - - // Caller should hold the Crst around calling this function and using the ReJitInfo. -#ifndef DACCESS_COMPILE - _ASSERTE(m_crstTable.OwnedByCurrentThread()); -#endif - - ReJitInfoHash::KeyIterator beginIter(&m_table, TRUE /* begin */); - ReJitInfoHash::KeyIterator endIter(&m_table, FALSE /* begin */); - - if (pMD != NULL) - { - beginIter = GetBeginIterator(pMD); - endIter = GetEndIterator(pMD); - } - else - { - beginIter = GetBeginIterator(pModule, methodDef); - endIter = GetEndIterator(pModule, methodDef); - } - - for (ReJitInfoHash::KeyIterator iter = beginIter; - iter != endIter; - iter++) - { - PTR_ReJitInfo pInfo = *iter; - _ASSERTE(pInfo->m_pShared != NULL); - - if (pInfo->m_pShared->GetState() == SharedReJitInfo::kStateReverted) - continue; - - INDEBUG(AssertRestOfEntriesAreReverted(iter, endIter)); - return pInfo; - } - - return NULL; -} - - //--------------------------------------------------------------------------------------- // // ReJitManager instance constructor--for now, does nothing @@ -2531,114 +1998,6 @@ ReJitManager::ReJitManager() LIMITED_METHOD_DAC_CONTRACT; } - -//--------------------------------------------------------------------------------------- -// -// Called from BaseDomain::BaseDomain to do any constructor-time initialization. -// Presently, this takes care of initializing the Crst, choosing the type based on -// whether this ReJitManager belongs to the SharedDomain. -// -// Arguments: -// * fSharedDomain - nonzero iff this ReJitManager belongs to the SharedDomain. -// - -void ReJitManager::PreInit(BOOL fSharedDomain) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - CAN_TAKE_LOCK; - MODE_ANY; - } - CONTRACTL_END; - -#ifndef DACCESS_COMPILE - m_crstTable.Init( - fSharedDomain ? CrstReJITSharedDomainTable : CrstReJITDomainTable, - CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN)); -#endif // DACCESS_COMPILE -} - - -//--------------------------------------------------------------------------------------- -// -// Finds the ReJitInfo tracking a pre-rejit request. -// -// Arguments: -// * beginIter - Iterator to start search -// * endIter - Iterator to end search -// -// Return Value: -// NULL if no such ReJitInfo exists. This can occur if two thread race -// to JIT the original code and we're the loser. Else, the ReJitInfo * found. -// -// Assumptions: -// Caller must be holding this ReJitManager's table lock. -// - -ReJitInfo * ReJitManager::FindPreReJittedReJitInfo( - ReJitInfoHash::KeyIterator beginIter, - ReJitInfoHash::KeyIterator endIter) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // Caller shouldn't be handing out iterators unless he's already locking the table. -#ifndef DACCESS_COMPILE - _ASSERTE(m_crstTable.OwnedByCurrentThread()); -#endif - - for (ReJitInfoHash::KeyIterator iter = beginIter; - iter != endIter; - iter++) - { - ReJitInfo * pInfo = *iter; - SharedReJitInfo * pShared = pInfo->m_pShared; - _ASSERTE(pShared != NULL); - - switch (pShared->GetState()) - { - case SharedReJitInfo::kStateRequested: - case SharedReJitInfo::kStateGettingReJITParameters: - case SharedReJitInfo::kStateActive: - if (pInfo->GetState() == ReJitInfo::kJumpToRejittedCode) - { - // There was a race for the original JIT, and we're the loser. (The winner - // has already published the original JIT's pcode, jump-stamped, and begun - // the rejit!) - return NULL; - } - - // Otherwise, either we have a rejit request that has not yet been - // jump-stamped, or there was a race for the original JIT, and another - // thread jump-stamped its copy of the originally JITted code already. In - // that case, we still don't know who the winner or loser will be (PCODE may - // not yet be published), so we'll have to jump-stamp our copy just in case - // we win. - _ASSERTE((pInfo->GetState() == ReJitInfo::kJumpNone) || - (pInfo->GetState() == ReJitInfo::kJumpToPrestub)); - INDEBUG(AssertRestOfEntriesAreReverted(iter, endIter)); - return pInfo; - - - case SharedReJitInfo::kStateReverted: - // just ignore this guy - continue; - - default: - UNREACHABLE(); - } - } - - return NULL; -} - //--------------------------------------------------------------------------------------- // // Used by profiler to get the ReJITID corrseponding to a (MethodDesc *, PCODE) pair. @@ -2654,7 +2013,7 @@ ReJitInfo * ReJitManager::FindPreReJittedReJitInfo( // 0 if no such ReJITID found (e.g., PCODE is from a JIT and not a rejit), else the // ReJITID requested. // - +// static ReJITID ReJitManager::GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart) { CONTRACTL @@ -2662,7 +2021,6 @@ ReJITID ReJitManager::GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart) NOTHROW; CAN_TAKE_LOCK; GC_TRIGGERS; - INSTANCE_CHECK; PRECONDITION(CheckPointer(pMD)); PRECONDITION(pCodeStart != NULL); } @@ -2671,14 +2029,14 @@ ReJITID ReJitManager::GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart) // Fast-path: If the rejit map is empty, no need to look up anything. Do this outside // of a lock to impact our caller (the prestub worker) as little as possible. If the // map is nonempty, we'll acquire the lock at that point and do the lookup for real. - if (m_table.GetCount() == 0) + CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager(); + if (pCodeVersionManager->GetNonDefaultILVersionCount() == 0) { return 0; } - CrstHolder ch(&m_crstTable); - - return GetReJitIdNoLock(pMD, pCodeStart); + CodeVersionManager::TableLockHolder ch(pCodeVersionManager); + return ReJitManager::GetReJitIdNoLock(pMD, pCodeStart); } //--------------------------------------------------------------------------------------- @@ -2699,15 +2057,16 @@ ReJITID ReJitManager::GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart) NOTHROW; CANNOT_TAKE_LOCK; GC_NOTRIGGER; - INSTANCE_CHECK; PRECONDITION(CheckPointer(pMD)); PRECONDITION(pCodeStart != NULL); } CONTRACTL_END; // Caller must ensure this lock is taken! - _ASSERTE(m_crstTable.OwnedByCurrentThread()); + CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager(); + _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread()); + /* TODO ReJitInfo * pInfo = FindReJitInfo(pMD, pCodeStart, 0); if (pInfo == NULL) { @@ -2717,60 +2076,10 @@ ReJITID ReJitManager::GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart) _ASSERTE(pInfo->m_pShared->GetState() == SharedReJitInfo::kStateActive || pInfo->m_pShared->GetState() == SharedReJitInfo::kStateReverted); return pInfo->m_pShared->GetId(); + */ + return 0; } - -//--------------------------------------------------------------------------------------- -// -// Used by profilers to map a (MethodDesc *, ReJITID) pair to the corresponding PCODE for -// that rejit attempt. This can also be used for reverted methods, as the PCODE may still -// be available and in use even after a rejitted function has been reverted. -// -// Arguments: -// * pMD - MethodDesc * of interest -// * reJitId - ReJITID of interest -// -// Return Value: -// Corresponding PCODE of the rejit attempt, or NULL if no such rejit attempt can be -// found. -// - -PCODE ReJitManager::GetCodeStart(PTR_MethodDesc pMD, ReJITID reJitId) -{ - CONTRACTL - { - NOTHROW; - CAN_TAKE_LOCK; - GC_NOTRIGGER; - INSTANCE_CHECK; - PRECONDITION(CheckPointer(pMD)); - PRECONDITION(reJitId != 0); - } - CONTRACTL_END; - - // Fast-path: If the rejit map is empty, no need to look up anything. Do this outside - // of a lock to impact our caller (the prestub worker) as little as possible. If the - // map is nonempty, we'll acquire the lock at that point and do the lookup for real. - if (m_table.GetCount() == 0) - { - return NULL; - } - - CrstHolder ch(&m_crstTable); - - ReJitInfo * pInfo = FindReJitInfo(pMD, NULL, reJitId); - if (pInfo == NULL) - { - return NULL; - } - - _ASSERTE(pInfo->m_pShared->GetState() == SharedReJitInfo::kStateActive || - pInfo->m_pShared->GetState() == SharedReJitInfo::kStateReverted); - - return pInfo->m_pCode; -} - - //--------------------------------------------------------------------------------------- // // If a function has been requested to be rejitted, finds the one current @@ -2787,7 +2096,6 @@ PCODE ReJitManager::GetCodeStart(PTR_MethodDesc pMD, ReJITID reJitId) // Returns the requested codegen flags, or 0 (i.e., no flags set) if no rejit attempt // can be found for the MD. // - DWORD ReJitManager::GetCurrentReJitFlagsWorker(PTR_MethodDesc pMD) { CONTRACTL @@ -2802,24 +2110,22 @@ DWORD ReJitManager::GetCurrentReJitFlagsWorker(PTR_MethodDesc pMD) // Fast-path: If the rejit map is empty, no need to look up anything. Do this outside // of a lock to impact our caller (e.g., the JIT asking if it can inline) as little as possible. If the // map is nonempty, we'll acquire the lock at that point and do the lookup for real. - if (m_table.GetCount() == 0) + CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager(); + if (pCodeVersionManager->GetNonDefaultILVersionCount() == 0) { return 0; } - CrstHolder ch(&m_crstTable); + CodeVersionManager::TableLockHolder lock(pCodeVersionManager); - for (ReJitInfoHash::KeyIterator iter = GetBeginIterator(pMD), end = GetEndIterator(pMD); + ILCodeVersionCollection ilCodeVersions = pCodeVersionManager->GetILCodeVersions(pMD); + for (ILCodeVersionIterator iter = ilCodeVersions.Begin(), end = ilCodeVersions.End(); iter != end; iter++) { - ReJitInfo * pInfo = *iter; - _ASSERTE(pInfo->GetMethodDesc() == pMD); - _ASSERTE(pInfo->m_pShared != NULL); + ILCodeVersion curVersion = *iter; - DWORD dwState = pInfo->m_pShared->GetState(); - - if (dwState != SharedReJitInfo::kStateActive) + if (curVersion.GetRejitState() != ILCodeVersion::kStateActive) { // Not active means we never asked profiler for the codegen flags OR the // rejit request has been reverted. So this one is useless. @@ -2827,31 +2133,7 @@ DWORD ReJitManager::GetCurrentReJitFlagsWorker(PTR_MethodDesc pMD) } // Found it! -#ifdef _DEBUG - // This must be the only such ReJitInfo for this MethodDesc. Check the rest and - // assert otherwise. - { - ReJitInfoHash::KeyIterator iterTest = iter; - iterTest++; - - while(iterTest != end) - { - ReJitInfo * pInfoTest = *iterTest; - _ASSERTE(pInfoTest->GetMethodDesc() == pMD); - _ASSERTE(pInfoTest->m_pShared != NULL); - - DWORD dwStateTest = pInfoTest->m_pShared->GetState(); - - if (dwStateTest == SharedReJitInfo::kStateActive) - { - _ASSERTE(!"Multiple active ReJitInfos for same MethodDesc"); - break; - } - iterTest++; - } - } -#endif //_DEBUG - return pInfo->m_pShared->m_dwCodegenFlags; + return curVersion.GetJitFlags(); } return 0; @@ -2859,66 +2141,7 @@ DWORD ReJitManager::GetCurrentReJitFlagsWorker(PTR_MethodDesc pMD) //--------------------------------------------------------------------------------------- // -// Helper to find the matching ReJitInfo by methoddesc paired with either pCodeStart or -// reJitId (exactly one should be non-zero, and will be used as the key for the lookup) -// -// Arguments: -// * pMD - MethodDesc * to look up -// * pCodeStart - PCODE of the particular rejit attempt to look up. NULL if looking -// up by ReJITID. -// * reJitId - ReJITID of the particular rejit attempt to look up. NULL if looking -// up by PCODE. -// -// Return Value: -// ReJitInfo * matching input parameters, or NULL if no such ReJitInfo could be -// found. -// -// Assumptions: -// Caller must be holding this ReJitManager's table lock. -// - -PTR_ReJitInfo ReJitManager::FindReJitInfo(PTR_MethodDesc pMD, PCODE pCodeStart, ReJITID reJitId) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - INSTANCE_CHECK; - PRECONDITION(CheckPointer(pMD)); - } - CONTRACTL_END; - - // Caller should hold the Crst around calling this function and using the ReJitInfo. -#ifndef DACCESS_COMPILE - _ASSERTE(m_crstTable.OwnedByCurrentThread()); -#endif - - // One of these two keys should be used, but not both! - _ASSERTE( - ((pCodeStart != NULL) || (reJitId != 0)) && - !((pCodeStart != NULL) && (reJitId != 0))); - - for (ReJitInfoHash::KeyIterator iter = GetBeginIterator(pMD), end = GetEndIterator(pMD); - iter != end; - iter++) - { - PTR_ReJitInfo pInfo = *iter; - _ASSERTE(pInfo->GetMethodDesc() == pMD); - _ASSERTE(pInfo->m_pShared != NULL); - - if ((pCodeStart != NULL && pInfo->m_pCode == pCodeStart) || // pCodeStart is key - (reJitId != 0 && pInfo->m_pShared->GetId() == reJitId)) // reJitId is key - { - return pInfo; - } - } - - return NULL; -} - -//--------------------------------------------------------------------------------------- -// -// Called by profiler to retrieve an array of ReJITIDs corresponding to a MethodDesc * +// Called by profiler to retrieve an array of ReJITIDs corresponding to a MethodDesc * // // Arguments: // * pMD - MethodDesc * to look up @@ -2934,7 +2157,7 @@ PTR_ReJitInfo ReJitManager::FindReJitInfo(PTR_MethodDesc pMD, PCODE pCodeStart, // cReJitIds were returned and cReJitIds < *pcReJitId (latter being the total // number of ReJITIDs available). // - +// static HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[]) { CONTRACTL @@ -2942,31 +2165,30 @@ HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * p NOTHROW; CAN_TAKE_LOCK; GC_NOTRIGGER; - INSTANCE_CHECK; PRECONDITION(CheckPointer(pMD)); PRECONDITION(pcReJitIds != NULL); PRECONDITION(reJitIds != NULL); } CONTRACTL_END; - CrstHolder ch(&m_crstTable); + CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager(); + CodeVersionManager::TableLockHolder lock(pCodeVersionManager); ULONG cnt = 0; - for (ReJitInfoHash::KeyIterator iter = GetBeginIterator(pMD), end = GetEndIterator(pMD); + ILCodeVersionCollection ilCodeVersions = pCodeVersionManager->GetILCodeVersions(pMD); + for (ILCodeVersionIterator iter = ilCodeVersions.Begin(), end = ilCodeVersions.End(); iter != end; iter++) { - ReJitInfo * pInfo = *iter; - _ASSERTE(pInfo->GetMethodDesc() == pMD); - _ASSERTE(pInfo->m_pShared != NULL); + ILCodeVersion curILVersion = *iter; - if (pInfo->m_pShared->GetState() == SharedReJitInfo::kStateActive || - pInfo->m_pShared->GetState() == SharedReJitInfo::kStateReverted) + if (curILVersion.GetRejitState() == ILCodeVersion::kStateActive || + curILVersion.GetRejitState() == ILCodeVersion::kStateReverted) { if (cnt < cReJitIds) { - reJitIds[cnt] = pInfo->m_pShared->GetId(); + reJitIds[cnt] = curILVersion.GetVersionId(); } ++cnt; @@ -2979,78 +2201,6 @@ HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * p return (cnt > cReJitIds) ? S_FALSE : S_OK; } -//--------------------------------------------------------------------------------------- -// -// Helper that inits a new ReJitReportErrorWorkItem and adds it to the pErrors array -// -// Arguments: -// * pModule - The module in the module/MethodDef identifier pair for the method which -// had an error during rejit -// * methodDef - The MethodDef in the module/MethodDef identifier pair for the method which -// had an error during rejit -// * pMD - If available, the specific method instance which had an error during rejit -// * hrStatus - HRESULT for the rejit error that occurred -// * pErrors - the list of error records that this method will append to -// -// Return Value: -// * S_OK: error was appended -// * E_OUTOFMEMORY: Not enough memory to create the new error item. The array is unchanged. -// - -//static -HRESULT ReJitManager::AddReJITError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus, CDynArray * pErrors) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - ReJitReportErrorWorkItem* pError = pErrors->Append(); - if (pError == NULL) - { - return E_OUTOFMEMORY; - } - pError->pModule = pModule; - pError->methodDef = methodDef; - pError->pMethodDesc = pMD; - pError->hrStatus = hrStatus; - return S_OK; -} - -//--------------------------------------------------------------------------------------- -// -// Helper that inits a new ReJitReportErrorWorkItem and adds it to the pErrors array -// -// Arguments: -// * pReJitInfo - The method which had an error during rejit -// * hrStatus - HRESULT for the rejit error that occurred -// * pErrors - the list of error records that this method will append to -// -// Return Value: -// * S_OK: error was appended -// * E_OUTOFMEMORY: Not enough memory to create the new error item. The array is unchanged. -// - -//static -HRESULT ReJitManager::AddReJITError(ReJitInfo* pReJitInfo, HRESULT hrStatus, CDynArray * pErrors) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - Module * pModule = NULL; - mdMethodDef methodDef = mdTokenNil; - pReJitInfo->GetModuleAndTokenRegardlessOfKeyType(&pModule, &methodDef); - return AddReJITError(pModule, methodDef, pReJitInfo->GetMethodDesc(), hrStatus, pErrors); -} - #ifdef _DEBUG //--------------------------------------------------------------------------------------- // @@ -3065,720 +2215,20 @@ HRESULT ReJitManager::AddReJITError(ReJitInfo* pReJitInfo, HRESULT hrStatus, CDy // void ReJitManager::AssertRestOfEntriesAreReverted( - ReJitInfoHash::KeyIterator iter, - ReJitInfoHash::KeyIterator end) + ILCodeVersionIterator iter, + ILCodeVersionIterator end) { LIMITED_METHOD_CONTRACT; // All other rejits should be in the reverted state while (++iter != end) { - _ASSERTE((*iter)->m_pShared->GetState() == SharedReJitInfo::kStateReverted); + _ASSERTE(iter->GetRejitState() == ILCodeVersion::kStateReverted); } } - -//--------------------------------------------------------------------------------------- -// -// Debug-only helper to dump ReJitManager contents to stdout. Only used if -// COMPlus_ProfAPI_EnableRejitDiagnostics is set. -// -// Arguments: -// * szIntroText - Intro text passed by caller to be output before this ReJitManager -// is dumped. -// -// - -void ReJitManager::Dump(LPCSTR szIntroText) -{ - if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ProfAPI_EnableRejitDiagnostics) == 0) - return; - - printf(szIntroText); - fflush(stdout); - - CrstHolder ch(&m_crstTable); - - printf("BEGIN ReJitManager::Dump: 0x%p\n", this); - - for (ReJitInfoHash::Iterator iterCur = m_table.Begin(), iterEnd = m_table.End(); - iterCur != iterEnd; - iterCur++) - { - ReJitInfo * pInfo = *iterCur; - printf( - "\tInfo 0x%p: State=0x%x, Next=0x%p, Shared=%p, SharedState=0x%x\n", - pInfo, - pInfo->GetState(), - (void*)pInfo->m_pNext, - (void*)pInfo->m_pShared, - pInfo->m_pShared->GetState()); - - switch(pInfo->m_key.m_keyType) - { - case ReJitInfo::Key::kMethodDesc: - printf( - "\t\tMD=0x%p, %s.%s (%s)\n", - (void*)pInfo->GetMethodDesc(), - pInfo->GetMethodDesc()->m_pszDebugClassName, - pInfo->GetMethodDesc()->m_pszDebugMethodName, - pInfo->GetMethodDesc()->m_pszDebugMethodSignature); - break; - - case ReJitInfo::Key::kMetadataToken: - Module * pModule; - mdMethodDef methodDef; - pInfo->GetModuleAndToken(&pModule, &methodDef); - printf( - "\t\tModule=0x%p, Token=0x%x\n", - pModule, - methodDef); - break; - - case ReJitInfo::Key::kUninitialized: - printf("\t\tUNINITIALIZED\n"); - break; - - default: - _ASSERTE(!"Unrecognized pInfo key type"); - } - fflush(stdout); - } - printf("END ReJitManager::Dump: 0x%p\n", this); - fflush(stdout); -} - #endif // _DEBUG -//--------------------------------------------------------------------------------------- -// ReJitInfo implementation - -// All the state-changey stuff is kept up here in the !DACCESS_COMPILE block. -// The more read-only inspection-y stuff follows the block. - - -#ifndef DACCESS_COMPILE - -//--------------------------------------------------------------------------------------- -// -// Do the actual work of stamping the top of originally-jitted-code with a jmp that goes -// to the prestub. This can be called in one of three ways: -// * Case 1: By RequestReJIT against an already-jitted function, in which case the -// PCODE may be inferred by the MethodDesc, and our caller will have suspended -// the EE for us, OR -// * Case 2: By the prestub worker after jitting the original code of a function -// (i.e., the "pre-rejit" scenario). In this case, the EE is not suspended. But -// that's ok, because the PCODE has not yet been published to the MethodDesc, and -// no thread can be executing inside the originally JITted function yet. -// * Case 3: At type/method restore time for an NGEN'ed assembly. This is also the pre-rejit -// scenario because we are guaranteed to do this before the code in the module -// is executable. EE suspend is not required. -// -// Arguments: -// * pCode - Case 1 (above): will be NULL, and we can infer the PCODE from the -// MethodDesc; Case 2+3 (above, pre-rejit): will be non-NULL, and we'll need to use -// this to find the code to stamp on top of. -// -// Return Value: -// * S_OK: Either we successfully did the jmp-stamp, or a racing thread took care of -// it for us. -// * Else, HRESULT indicating failure. -// -// Assumptions: -// The caller will have suspended the EE if necessary (case 1), before this is -// called. -// -HRESULT ReJitInfo::JumpStampNativeCode(PCODE pCode /* = NULL */) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - - // It may seem dangerous to be stamping jumps over code while a GC is going on, - // but we're actually safe. As we assert below, either we're holding the thread - // store lock (and thus preventing a GC) OR we're stamping code that has not yet - // been published (and will thus not be executed by managed therads or examined - // by the GC). - MODE_ANY; - } - CONTRACTL_END; - - PCODE pCodePublished = GetMethodDesc()->GetNativeCode(); - - _ASSERTE((pCode != NULL) || (pCodePublished != NULL)); - _ASSERTE(GetMethodDesc()->GetReJitManager()->IsTableCrstOwnedByCurrentThread()); - - HRESULT hr = S_OK; - - // We'll jump-stamp over pCode, or if pCode is NULL, jump-stamp over the published - // code for this's MethodDesc. - LPBYTE pbCode = (LPBYTE) pCode; - if (pbCode == NULL) - { - // If caller didn't specify a pCode, just use the one that was published after - // the original JIT. (A specific pCode would be passed in the pre-rejit case, - // to jump-stamp the original code BEFORE the PCODE gets published.) - pbCode = (LPBYTE) pCodePublished; - } - _ASSERTE (pbCode != NULL); - - // The debugging API may also try to write to the very top of this function (though - // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know - // whether we can safely patch the actual code, or instead write to the debugger's - // buffer. - DebuggerController::ControllerLockHolder lockController; - - // We could be in a race. Either two threads simultaneously JITting the same - // method for the first time or two threads restoring NGEN'ed code. - // Another thread may (or may not) have jump-stamped its copy of the code already - _ASSERTE((GetState() == kJumpNone) || (GetState() == kJumpToPrestub)); - - if (GetState() == kJumpToPrestub) - { - // The method has already been jump stamped so nothing left to do - _ASSERTE(CodeIsSaved()); - return S_OK; - } - - // Remember what we're stamping our jump on top of, so we can replace it during a - // revert. - for (int i = 0; i < sizeof(m_rgSavedCode); i++) - { - m_rgSavedCode[i] = *FirstCodeByteAddr(pbCode+i, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)(pbCode+i))); - } - - EX_TRY - { - AllocMemTracker amt; - - // This guy might throw on out-of-memory, so rely on the tracker to clean-up - Precode * pPrecode = Precode::Allocate(PRECODE_STUB, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator(), &amt); - PCODE target = pPrecode->GetEntryPoint(); - -#if defined(_X86_) || defined(_AMD64_) - - // Normal unpatched code never starts with a jump - // so make sure this code isn't already patched - _ASSERTE(*FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) != X86_INSTR_JMP_REL32); - - INT64 i64OldCode = *(INT64*)pbCode; - INT64 i64NewCode = i64OldCode; - LPBYTE pbNewValue = (LPBYTE)&i64NewCode; - *pbNewValue = X86_INSTR_JMP_REL32; - INT32 UNALIGNED * pOffset = reinterpret_cast(&pbNewValue[1]); - // This will throw for out-of-memory, so don't write anything until - // after he succeeds - // This guy will leak/cache/reuse the jumpstub - *pOffset = rel32UsingJumpStub(reinterpret_cast(pbCode + 1), target, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator()); - - // If we have the EE suspended or the code is unpublished there won't be contention on this code - hr = UpdateJumpStampHelper(pbCode, i64OldCode, i64NewCode, FALSE); - if (FAILED(hr)) - { - ThrowHR(hr); - } - - // - // No failure point after this! - // - amt.SuppressRelease(); - -#else // _X86_ || _AMD64_ -#error "Need to define a way to jump-stamp the prolog in a safe way for this platform" - -#endif // _X86_ || _AMD64_ - - m_dwInternalFlags &= ~kStateMask; - m_dwInternalFlags |= kJumpToPrestub; - } - EX_CATCH_HRESULT(hr); - _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY); - - if (SUCCEEDED(hr)) - { - _ASSERTE(GetState() == kJumpToPrestub); - _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0 - } - - return hr; -} - -//--------------------------------------------------------------------------------------- -// -// Poke the JITted code to satsify a revert request (or to perform an implicit revert as -// part of a second, third, etc. rejit request). Reinstates the originally JITted code -// that had been jump-stamped over to perform a prior rejit. -// -// Arguments -// fEESuspended - TRUE if the caller keeps the EE suspended during this call -// -// -// Return Value: -// S_OK to indicate the revert succeeded, -// CORPROF_E_RUNTIME_SUSPEND_REQUIRED to indicate the jumpstamp hasn't been reverted -// and EE suspension will be needed for success -// other failure HRESULT indicating what went wrong. -// -// Assumptions: -// Caller must be holding the owning ReJitManager's table crst. -// - -HRESULT ReJitInfo::UndoJumpStampNativeCode(BOOL fEESuspended) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - _ASSERTE(GetMethodDesc()->GetReJitManager()->IsTableCrstOwnedByCurrentThread()); - _ASSERTE((m_pShared->GetState() == SharedReJitInfo::kStateReverted)); - _ASSERTE((GetState() == kJumpToPrestub) || (GetState() == kJumpToRejittedCode)); - _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0 (see above test) - - BYTE * pbCode = (BYTE*)GetMethodDesc()->GetNativeCode(); - DebuggerController::ControllerLockHolder lockController; - -#if defined(_X86_) || defined(_AMD64_) - _ASSERTE(m_rgSavedCode[0] != X86_INSTR_JMP_REL32); - _ASSERTE(*FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) == X86_INSTR_JMP_REL32); -#else -#error "Need to define a way to jump-stamp the prolog in a safe way for this platform" -#endif // _X86_ || _AMD64_ - - // For the interlocked compare, remember what pbCode is right now - INT64 i64OldValue = *(INT64 *)pbCode; - // Assemble the INT64 of the new code bytes to write. Start with what's there now - INT64 i64NewValue = i64OldValue; - memcpy(LPBYTE(&i64NewValue), m_rgSavedCode, sizeof(m_rgSavedCode)); - HRESULT hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended); - _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended)); - if (hr != S_OK) - return hr; - - // Transition state of this ReJitInfo to indicate the MD no longer has any jump stamp - m_dwInternalFlags &= ~kStateMask; - m_dwInternalFlags |= kJumpNone; - return S_OK; -} - -//--------------------------------------------------------------------------------------- -// -// After code has been rejitted, this is called to update the jump-stamp to go from -// pointing to the prestub, to pointing to the newly rejitted code. -// -// Arguments: -// fEESuspended - TRUE if the caller keeps the EE suspended during this call -// pRejittedCode - jitted code for the updated IL this method should execute -// -// Assumptions: -// This rejit manager's table crst should be held by the caller -// -// Returns - S_OK if the jump target is updated -// CORPROF_E_RUNTIME_SUSPEND_REQUIRED if the ee isn't suspended and it -// will need to be in order to do the update safely -HRESULT ReJitInfo::UpdateJumpTarget(BOOL fEESuspended, PCODE pRejittedCode) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - MethodDesc * pMD = GetMethodDesc(); - _ASSERTE(pMD->GetReJitManager()->IsTableCrstOwnedByCurrentThread()); - _ASSERTE(m_pShared->GetState() == SharedReJitInfo::kStateActive); - _ASSERTE(GetState() == kJumpToPrestub); - _ASSERTE(m_pCode == NULL); - - // Beginning of originally JITted code containing the jmp that we will redirect. - BYTE * pbCode = (BYTE*)pMD->GetNativeCode(); - -#if defined(_X86_) || defined(_AMD64_) - - HRESULT hr = S_OK; - { - DebuggerController::ControllerLockHolder lockController; - - // This will throw for out-of-memory, so don't write anything until - // after he succeeds - // This guy will leak/cache/reuse the jumpstub - INT32 offset = 0; - EX_TRY - { - offset = rel32UsingJumpStub( - reinterpret_cast(&pbCode[1]), // base of offset - pRejittedCode, // target of jump - pMD, - pMD->GetLoaderAllocator()); - } - EX_CATCH_HRESULT(hr); - _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY); - if (FAILED(hr)) - { - return hr; - } - // For validation later, remember what pbCode is right now - INT64 i64OldValue = *(INT64 *)pbCode; - - // Assemble the INT64 of the new code bytes to write. Start with what's there now - INT64 i64NewValue = i64OldValue; - LPBYTE pbNewValue = (LPBYTE)&i64NewValue; - - // First byte becomes a rel32 jmp instruction (should be a no-op as asserted - // above, but can't hurt) - *pbNewValue = X86_INSTR_JMP_REL32; - // Next 4 bytes are the jmp target (offset to jmp stub) - INT32 UNALIGNED * pnOffset = reinterpret_cast(&pbNewValue[1]); - *pnOffset = offset; - - hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended); - _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended)); - } - if (FAILED(hr)) - { - return hr; - } - -#else // _X86_ || _AMD64_ -#error "Need to define a way to jump-stamp the prolog in a safe way for this platform" -#endif // _X86_ || _AMD64_ - - // State transition - m_dwInternalFlags &= ~kStateMask; - m_dwInternalFlags |= kJumpToRejittedCode; - return S_OK; -} - - -//--------------------------------------------------------------------------------------- -// -// This is called to modify the jump-stamp area, the first ReJitInfo::JumpStubSize bytes -// in the method's code. -// -// Notes: -// Callers use this method in a variety of circumstances: -// a) when the code is unpublished (fContentionPossible == FALSE) -// b) when the caller has taken the ThreadStoreLock and suspended the EE -// (fContentionPossible == FALSE) -// c) when the code is published, the EE isn't suspended, and the jumpstamp -// area consists of a single 5 byte long jump instruction -// (fContentionPossible == TRUE) -// This method will attempt to alter the jump-stamp even if the caller has not prevented -// contention, but there is no guarantee it will be succesful. When the caller has prevented -// contention, then success is assured. Callers may oportunistically try without -// EE suspension, and then upgrade to EE suspension if the first attempt fails. -// -// Assumptions: -// This rejit manager's table crst should be held by the caller or fContentionPossible==FALSE -// The debugger patch table lock should be held by the caller -// -// Arguments: -// pbCode - pointer to the code where the jump stamp is placed -// i64OldValue - the bytes which should currently be at the start of the method code -// i64NewValue - the new bytes which should be written at the start of the method code -// fContentionPossible - See the Notes section above. -// -// Returns: -// S_OK => the jumpstamp has been succesfully updated. -// CORPROF_E_RUNTIME_SUSPEND_REQUIRED => the jumpstamp remains unchanged (preventing contention will be necessary) -// other failing HR => VirtualProtect failed, the jumpstamp remains unchanged -// -HRESULT ReJitInfo::UpdateJumpStampHelper(BYTE* pbCode, INT64 i64OldValue, INT64 i64NewValue, BOOL fContentionPossible) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - MethodDesc * pMD = GetMethodDesc(); - _ASSERTE(pMD->GetReJitManager()->IsTableCrstOwnedByCurrentThread() || !fContentionPossible); - - // When ReJIT is enabled, method entrypoints are always at least 8-byte aligned (see - // code:EEJitManager::allocCode), so we can do a single 64-bit interlocked operation - // to update the jump target. However, some code may have gotten compiled before - // the profiler had a chance to enable ReJIT (e.g., NGENd code, or code JITted - // before a profiler attaches). In such cases, we cannot rely on a simple - // interlocked operation, and instead must suspend the runtime to ensure we can - // safely update the jmp instruction. - // - // This method doesn't verify that the method is actually safe to rejit, we expect - // callers to do that. At the moment NGEN'ed code is safe to rejit even if - // it is unaligned, but code generated before the profiler attaches is not. - if (fContentionPossible && !(IS_ALIGNED(pbCode, sizeof(INT64)))) - { - return CORPROF_E_RUNTIME_SUSPEND_REQUIRED; - } - - // The debugging API may also try to write to this function (though - // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know - // whether we can safely patch the actual code, or instead write to the debugger's - // buffer. - if (fContentionPossible) - { - for (CORDB_ADDRESS_TYPE* pbProbeAddr = pbCode; pbProbeAddr < pbCode + ReJitInfo::JumpStubSize; pbProbeAddr++) - { - if (NULL != DebuggerController::GetPatchTable()->GetPatch(pbProbeAddr)) - { - return CORPROF_E_RUNTIME_SUSPEND_REQUIRED; - } - } - } - -#if defined(_X86_) || defined(_AMD64_) - - DWORD oldProt; - if (!ClrVirtualProtect((LPVOID)pbCode, 8, PAGE_EXECUTE_READWRITE, &oldProt)) - { - return HRESULT_FROM_WIN32(GetLastError()); - } - - if (fContentionPossible) - { - INT64 i64InterlockReportedOldValue = FastInterlockCompareExchangeLong((INT64 *)pbCode, i64NewValue, i64OldValue); - // Since changes to these bytes are protected by this rejitmgr's m_crstTable, we - // shouldn't have two writers conflicting. - _ASSERTE(i64InterlockReportedOldValue == i64OldValue); - } - else - { - // In this path the caller ensures: - // a) no thread will execute through the prologue area we are modifying - // b) no thread is stopped in a prologue such that it resumes in the middle of code we are modifying - // c) no thread is doing a debugger patch skip operation in which an unmodified copy of the method's - // code could be executed from a patch skip buffer. - - // PERF: we might still want a faster path through here if we aren't debugging that doesn't do - // all the patch checks - for (int i = 0; i < ReJitInfo::JumpStubSize; i++) - { - *FirstCodeByteAddr(pbCode+i, DebuggerController::GetPatchTable()->GetPatch(pbCode+i)) = ((BYTE*)&i64NewValue)[i]; - } - } - - if (oldProt != PAGE_EXECUTE_READWRITE) - { - // The CLR codebase in many locations simply ignores failures to restore the page protections - // Its true that it isn't a problem functionally, but it seems a bit sketchy? - // I am following the convention for now. - ClrVirtualProtect((LPVOID)pbCode, 8, oldProt, &oldProt); - } - - FlushInstructionCache(GetCurrentProcess(), pbCode, ReJitInfo::JumpStubSize); - return S_OK; - -#else // _X86_ || _AMD64_ -#error "Need to define a way to jump-stamp the prolog in a safe way for this platform" -#endif // _X86_ || _AMD64_ -} - - -#endif // DACCESS_COMPILE -// The rest of the ReJitInfo methods are safe to compile for DAC - - - -//--------------------------------------------------------------------------------------- -// -// ReJitInfos can be constructed in two ways: As a "regular" ReJitInfo indexed by -// MethodDesc *, or as a "placeholder" ReJitInfo (to satisfy pre-rejit requests) indexed -// by (Module *, methodDef). Both constructors call this helper to do all the common -// code for initializing the ReJitInfo. -// - -void ReJitInfo::CommonInit() -{ - LIMITED_METHOD_CONTRACT; - - m_pCode = NULL; - m_pNext = NULL; - m_dwInternalFlags = kJumpNone; - m_pShared->AddMethod(this); - ZeroMemory(m_rgSavedCode, sizeof(m_rgSavedCode)); -} - - -//--------------------------------------------------------------------------------------- -// -// Regardless of which kind of ReJitInfo this is, this will always return its -// corresponding Module * & methodDef -// -// Arguments: -// * ppModule - [out] Module * related to this ReJitInfo (which contains the -// returned methodDef) -// * pMethodDef - [out] methodDef related to this ReJitInfo -// - -void ReJitInfo::GetModuleAndTokenRegardlessOfKeyType(Module ** ppModule, mdMethodDef * pMethodDef) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SO_NOT_MAINLINE; - } - CONTRACTL_END; - - _ASSERTE(ppModule != NULL); - _ASSERTE(pMethodDef != NULL); - - if (m_key.m_keyType == Key::kMetadataToken) - { - GetModuleAndToken(ppModule, pMethodDef); - } - else - { - MethodDesc * pMD = GetMethodDesc(); - _ASSERTE(pMD != NULL); - _ASSERTE(pMD->IsRestored()); - - *ppModule = pMD->GetModule(); - *pMethodDef = pMD->GetMemberDef(); - } - - _ASSERTE(*ppModule != NULL); - _ASSERTE(*pMethodDef != mdTokenNil); -} - - -//--------------------------------------------------------------------------------------- -// -// Used as part of the hash table implementation in the containing ReJitManager, this -// hashes a ReJitInfo by MethodDesc * when available, else by (Module *, methodDef) -// -// Arguments: -// key - Key representing the ReJitInfo to hash -// -// Return Value: -// Hash value of the ReJitInfo represented by the specified key -// - -// static -COUNT_T ReJitInfo::Hash(Key key) -{ - LIMITED_METHOD_CONTRACT; - - if (key.m_keyType == Key::kMethodDesc) - { - return HashPtr(0, PTR_MethodDesc(key.m_pMD)); - } - - _ASSERTE (key.m_keyType == Key::kMetadataToken); - - return HashPtr(key.m_methodDef, PTR_Module(key.m_pModule)); -} - - -//--------------------------------------------------------------------------------------- -// -// Return the IL to compile for a given ReJitInfo -// -// Return Value: -// Pointer to IL buffer to compile. If the profiler has specified IL to rejit, -// this will be our copy of the IL buffer specified by the profiler. Else, this -// points to the original IL for the method from its module's metadata. -// -// Notes: -// IL memory is managed by us, not the caller. Caller must not free the buffer. -// - -COR_ILMETHOD * ReJitInfo::GetIL() -{ - CONTRACTL - { - THROWS; // Getting original IL via PEFile::GetIL can throw - CAN_TAKE_LOCK; // Looking up dynamically overridden IL takes a lock - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - if (m_pShared->m_pbIL != NULL) - { - return reinterpret_cast(m_pShared->m_pbIL); - } - - // If the user hasn't overriden us, get whatever the original IL had - return GetMethodDesc()->GetILHeader(TRUE); -} - - -//--------------------------------------------------------------------------------------- -// SharedReJitInfo implementation - - -SharedReJitInfo::SharedReJitInfo() - : m_dwInternalFlags(kStateRequested), - m_pbIL(NULL), - m_dwCodegenFlags(0), - m_reJitId(InterlockedIncrement(reinterpret_cast(&s_GlobalReJitId))), - m_pInfoList(NULL) -{ - LIMITED_METHOD_CONTRACT; -} - - -//--------------------------------------------------------------------------------------- -// -// Link in the specified ReJitInfo to the list maintained by this SharedReJitInfo -// -// Arguments: -// pInfo - ReJitInfo being added -// - -void SharedReJitInfo::AddMethod(ReJitInfo * pInfo) -{ - LIMITED_METHOD_CONTRACT; - - _ASSERTE(pInfo->m_pShared == this); - - // Push it on the head of our list - _ASSERTE(pInfo->m_pNext == NULL); - pInfo->m_pNext = PTR_ReJitInfo(m_pInfoList); - m_pInfoList = pInfo; -} - - -//--------------------------------------------------------------------------------------- -// -// Unlink the specified ReJitInfo from the list maintained by this SharedReJitInfo. -// Currently this is only used on AD unload to remove ReJitInfos of non-domain-neutral instantiations -// of domain-neutral generics (which are tracked in the SharedDomain's ReJitManager). -// This may be used in the future once we implement memory reclamation on revert(). -// -// Arguments: -// pInfo - ReJitInfo being removed -// - -void SharedReJitInfo::RemoveMethod(ReJitInfo * pInfo) -{ - LIMITED_METHOD_CONTRACT; - -#ifndef DACCESS_COMPILE - - // Find it - ReJitInfo ** ppEntry = &m_pInfoList; - while (*ppEntry != pInfo) - { - ppEntry = &(*ppEntry)->m_pNext; - _ASSERTE(*ppEntry != NULL); - } - - // Remove it - _ASSERTE((*ppEntry)->m_pShared == this); - *ppEntry = (*ppEntry)->m_pNext; - -#endif // DACCESS_COMPILE -} //--------------------------------------------------------------------------------------- // @@ -3849,9 +2299,9 @@ m_pMD(NULL), m_hr(S_OK) if (ReJitManager::IsReJITEnabled() && (pCode != NULL)) { m_pMD = pMethodDesc; - ReJitManager* pReJitManager = pMethodDesc->GetReJitManager(); - pReJitManager->m_crstTable.Enter(); - m_hr = pReJitManager->DoJumpStampIfNecessary(pMethodDesc, pCode); + CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager(); + pCodeVersionManager->EnterLock(); + m_hr = ReJitManager::DoJumpStampIfNecessary(pMethodDesc, pCode); } } @@ -3869,8 +2319,8 @@ ReJitPublishMethodHolder::~ReJitPublishMethodHolder() if (m_pMD) { - ReJitManager* pReJitManager = m_pMD->GetReJitManager(); - pReJitManager->m_crstTable.Leave(); + CodeVersionManager* pCodeVersionManager = m_pMD->GetCodeVersionManager(); + pCodeVersionManager->LeaveLock(); if (FAILED(m_hr)) { ReJitManager::ReportReJITError(m_pMD->GetModule(), m_pMD->GetMemberDef(), m_pMD, m_hr); @@ -3897,8 +2347,8 @@ m_pMethodTable(NULL) if (ReJitManager::IsReJITEnabled()) { m_pMethodTable = pMethodTable; - ReJitManager* pReJitManager = pMethodTable->GetModule()->GetReJitManager(); - pReJitManager->m_crstTable.Enter(); + CodeVersionManager* pCodeVersionManager = pMethodTable->GetModule()->GetCodeVersionManager(); + pCodeVersionManager->EnterLock(); MethodTable::IntroducedMethodIterator itMethods(pMethodTable, FALSE); for (; itMethods.IsValid(); itMethods.Next()) { @@ -3914,10 +2364,10 @@ m_pMethodTable(NULL) PCODE pCode = pMD->GetNativeCode(); if (pCode != NULL) { - HRESULT hr = pReJitManager->DoJumpStampIfNecessary(pMD, pCode); + HRESULT hr = ReJitManager::DoJumpStampIfNecessary(pMD, pCode); if (FAILED(hr)) { - ReJitManager::AddReJITError(pMD->GetModule(), pMD->GetMemberDef(), pMD, hr, &m_errors); + CodeVersionManager::AddCodePublishError(pMD->GetModule(), pMD->GetMemberDef(), pMD, hr, &m_errors); } } } @@ -3938,8 +2388,8 @@ ReJitPublishMethodTableHolder::~ReJitPublishMethodTableHolder() if (m_pMethodTable) { - ReJitManager* pReJitManager = m_pMethodTable->GetModule()->GetReJitManager(); - pReJitManager->m_crstTable.Leave(); + CodeVersionManager* pCodeVersionManager = m_pMethodTable->GetModule()->GetCodeVersionManager(); + pCodeVersionManager->LeaveLock(); for (int i = 0; i < m_errors.Count(); i++) { ReJitManager::ReportReJITError(&(m_errors[i])); @@ -3948,6 +2398,7 @@ ReJitPublishMethodTableHolder::~ReJitPublishMethodTableHolder() } #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) +#endif // FEATURE_CODE_VERSIONING #else // FEATURE_REJIT // On architectures that don't support rejit, just keep around some do-nothing @@ -3972,11 +2423,6 @@ HRESULT ReJitManager::RequestRevert( return E_NOTIMPL; } -// static -void ReJitManager::OnAppDomainExit(AppDomain * pAppDomain) -{ -} - ReJitManager::ReJitManager() { } @@ -3995,11 +2441,6 @@ ReJITID ReJitManager::GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart) return 0; } -PCODE ReJitManager::GetCodeStart(PTR_MethodDesc pMD, ReJITID reJitId) -{ - return NULL; -} - HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[]) { return E_NOTIMPL; diff --git a/src/vm/rejit.h b/src/vm/rejit.h index 3c8bfd66b2c9..f72e8825d1c9 100644 --- a/src/vm/rejit.h +++ b/src/vm/rejit.h @@ -19,9 +19,8 @@ #include "contractimpl.h" #include "shash.h" #include "corprof.h" +#include "codeversion.h" -struct ReJitInfo; -struct SharedReJitInfo; class ReJitManager; class MethodDesc; class ClrDataAccess; @@ -68,306 +67,6 @@ class ProfilerFunctionControl : public ICorProfilerFunctionControl COR_IL_MAP * m_rgInstrumentedMapEntries; }; -//--------------------------------------------------------------------------------------- -// Helper base class used by the structures below to enforce that their -// pieces get allocated on the appropriate loader heaps -// -struct LoaderHeapAllocatedRejitStructure -{ -public: - void * operator new (size_t size, LoaderHeap * pHeap, const NoThrow&); - void * operator new (size_t size, LoaderHeap * pHeap); -}; - -//--------------------------------------------------------------------------------------- -// One instance of this per rejit request for each mdMethodDef. Contains IL and -// compilation flags. This is used primarily as a structure, so most of its -// members are left public. -// -struct SharedReJitInfo : public LoaderHeapAllocatedRejitStructure -{ -private: - // This determines what to use next as the value of the profiling API's ReJITID. - static ReJITID s_GlobalReJitId; - -public: - // These represent the various states a SharedReJitInfo can be in. - enum InternalFlags - { - // The profiler has requested a ReJit, so we've allocated stuff, but we haven't - // called back to the profiler to get any info or indicate that the ReJit has - // started. (This Info can be 'reused' for a new ReJit if the - // profiler calls RequestRejit again before we transition to the next state.) - kStateRequested = 0x00000000, - - // The CLR has initiated the call to the profiler's GetReJITParameters() callback - // but it hasn't completed yet. At this point we have to assume the profiler has - // commited to a specific IL body, even if the CLR doesn't know what it is yet. - // If the profiler calls RequestRejit we need to allocate a new SharedReJitInfo - // and call GetReJITParameters() again. - kStateGettingReJITParameters = 0x00000001, - - // We have asked the profiler about this method via ICorProfilerFunctionControl, - // and have thus stored the IL and codegen flags the profiler specified. Can only - // transition to kStateReverted from this state. - kStateActive = 0x00000002, - - // The methoddef has been reverted, but not freed yet. It (or its instantiations - // for generics) *MAY* still be active on the stack someplace or have outstanding - // memory references. - kStateReverted = 0x00000003, - - - kStateMask = 0x0000000F, - }; - - DWORD m_dwInternalFlags; - - // Data - LPBYTE m_pbIL; - DWORD m_dwCodegenFlags; - InstrumentedILOffsetMapping m_instrumentedILMap; - -private: - // This is the value of the profiling API's ReJITID for this particular - // rejit request. - const ReJITID m_reJitId; - - // Children - ReJitInfo * m_pInfoList; - -public: - // Constructor - SharedReJitInfo(); - - // Intentionally no destructor. SharedReJitInfo and its contents are - // allocated on a loader heap, so SharedReJitInfo and its contents will be - // freed when the AD is unloaded. - - // Read-Only Identifcation - ReJITID GetId() { return m_reJitId; } - - void AddMethod(ReJitInfo * pInfo); - - void RemoveMethod(ReJitInfo * pInfo); - - ReJitInfo * GetMethods() { return m_pInfoList; } - - InternalFlags GetState(); -}; - -//--------------------------------------------------------------------------------------- -// One instance of this per rejit request for each MethodDesc*. One SharedReJitInfo -// corresponds to many ReJitInfos, as the SharedReJitInfo tracks the rejit request for -// the methodDef token whereas the ReJitInfo tracks the rejit request for each correspond -// MethodDesc* (instantiation). Points to actual generated code. -// -// In the case of "pre-rejit" (see comment at top of rejit.cpp), a special "placeholder" -// instance of ReJitInfo is used to "remember" to jmp-stamp a not-yet-jitted-method once -// it finally gets jitted the first time. -// -// Each ReJitManager contains a hash table of ReJitInfo instances, keyed by -// ReJitManager::m_key. -// -// This is used primarily as a structure, so most of its members are left public. -// -struct ReJitInfo : public LoaderHeapAllocatedRejitStructure -{ -public: - // The size of the code used to jump stamp the prolog - static const size_t JumpStubSize = -#if defined(_X86_) || defined(_AMD64_) - 5; -#else -#error "Need to define size of rejit jump-stamp for this platform" - 1; -#endif - - // Used by PtrSHash template as the key for this ReJitInfo. For regular - // ReJitInfos, the key is the MethodDesc*. For placeholder ReJitInfos - // (to facilitate pre-rejit), the key is (Module*, mdMethodDef). - struct Key - { - public: - enum - { - // The key has not yet had its values initialized - kUninitialized = 0x0, - - // The key represents a loaded MethodDesc, and is identified by the m_pMD - // field - kMethodDesc = 0x1, - - // The key represents a "placeholder" ReJitInfo identified not by loaded - // MethodDesc, but by the module and metadata token (m_pModule, - // m_methodDef). - kMetadataToken = 0x2, - }; - - // Storage consists of a discriminated union between MethodDesc* or - // (Module*, mdMethodDef), with the key type as the discriminator. - union - { - TADDR m_pMD; - TADDR m_pModule; - }; - ULONG32 m_methodDef : 28; - ULONG32 m_keyType : 2; - - Key(); - Key(PTR_MethodDesc pMD); - Key(PTR_Module pModule, mdMethodDef methodDef); - }; - - static COUNT_T Hash(Key key); - - enum InternalFlags - { - // This ReJitInfo is either a placeholder (identified by module and - // metadata token, rather than loaded MethodDesc) OR this ReJitInfo is - // identified by a loaded MethodDesc that has been reverted OR not yet - // been jump-stamped. In the last case, the time window where this - // ReJitInfo would stay in kJumpNone is rather small, as - // RequestReJIT() will immediately cause the originally JITted code to - // be jump-stamped. - kJumpNone = 0x00000000, - - // This ReJitInfo is identified by a loaded MethodDesc that has been compiled and - // jump-stamped, with the target being the prestub. The MethodDesc has not yet - // been rejitted - kJumpToPrestub = 0x00000001, - - // This ReJitInfo is identified by a loaded MethodDesc that has been compiled AND - // rejitted. The top of the originally JITted code has been jump-stamped, with - // the target being the latest version of the rejitted code. - kJumpToRejittedCode = 0x00000002, - - kStateMask = 0x0000000F, - }; - - Key m_key; - DWORD m_dwInternalFlags; - - // The beginning of the rejitted code - PCODE m_pCode; - - // The parent SharedReJitInfo, which manages the rejit request for all - // instantiations. - PTR_SharedReJitInfo const m_pShared; - - // My next sibling ReJitInfo for this rejit request (e.g., another - // generic instantiation of the same method) - PTR_ReJitInfo m_pNext; - - // The originally JITted code that was overwritten with the jmp stamp. - BYTE m_rgSavedCode[JumpStubSize]; - - - ReJitInfo(PTR_MethodDesc pMD, SharedReJitInfo * pShared); - ReJitInfo(PTR_Module pModule, mdMethodDef methodDef, SharedReJitInfo * pShared); - - // Intentionally no destructor. ReJitInfo is allocated on a loader heap, - // and will be freed (along with its associated SharedReJitInfo) when the - // AD is unloaded. - - Key GetKey(); - PTR_MethodDesc GetMethodDesc(); - void GetModuleAndToken(Module ** ppModule, mdMethodDef * pMethodDef); - void GetModuleAndTokenRegardlessOfKeyType(Module ** ppModule, mdMethodDef * pMethodDef); - InternalFlags GetState(); - - COR_ILMETHOD * GetIL(); - - HRESULT JumpStampNativeCode(PCODE pCode = NULL); - HRESULT UndoJumpStampNativeCode(BOOL fEESuspended); - HRESULT UpdateJumpTarget(BOOL fEESuspended, PCODE pRejittedCode); - HRESULT UpdateJumpStampHelper(BYTE* pbCode, INT64 i64OldValue, INT64 i64newValue, BOOL fContentionPossible); - - -protected: - void CommonInit(); - INDEBUG(BOOL CodeIsSaved();) -}; - -//--------------------------------------------------------------------------------------- -// Used by the SHash inside ReJitManager which maintains the set of ReJitInfo instances. -// -class ReJitInfoTraits : public DefaultSHashTraits -{ -public: - - // explicitly declare local typedefs for these traits types, otherwise - // the compiler may get confused - typedef DefaultSHashTraits PARENT; - typedef PARENT::element_t element_t; - typedef PARENT::count_t count_t; - - typedef ReJitInfo::Key key_t; - - static key_t GetKey(const element_t &e); - static BOOL Equals(key_t k1, key_t k2); - static count_t Hash(key_t k); - static bool IsNull(const element_t &e); -}; - -// RequestRejit and RequestRevert use these batches to accumulate ReJitInfos that need their -// jump stamps updated -class ReJitManager; -struct ReJitManagerJumpStampBatch -{ - ReJitManagerJumpStampBatch(ReJitManager * pReJitManager) : undoMethods(), preStubMethods() - { - LIMITED_METHOD_CONTRACT; - this->pReJitManager = pReJitManager; - } - - ReJitManager* pReJitManager; - CDynArray undoMethods; - CDynArray preStubMethods; -}; - -class ReJitManagerJumpStampBatchTraits : public DefaultSHashTraits -{ -public: - - // explicitly declare local typedefs for these traits types, otherwise - // the compiler may get confused - typedef DefaultSHashTraits PARENT; - typedef PARENT::element_t element_t; - typedef PARENT::count_t count_t; - - typedef ReJitManager * key_t; - - static key_t GetKey(const element_t &e) - { - return e->pReJitManager; - } - - static BOOL Equals(key_t k1, key_t k2) - { - return (k1 == k2); - } - - static count_t Hash(key_t k) - { - return (count_t)k; - } - - static bool IsNull(const element_t &e) - { - return (e == NULL); - } -}; - -struct ReJitReportErrorWorkItem -{ - Module* pModule; - mdMethodDef methodDef; - MethodDesc* pMethodDesc; - HRESULT hrStatus; -}; - - #endif // FEATURE_REJIT // @@ -406,7 +105,7 @@ class ReJitPublishMethodTableHolder private: #if defined(FEATURE_REJIT) MethodTable* m_pMethodTable; - CDynArray m_errors; + CDynArray m_errors; #endif }; @@ -429,46 +128,19 @@ class ReJitManager #ifdef FEATURE_REJIT - // Hash table mapping MethodDesc* (or (ModuleID, mdMethodDef)) to its - // ReJitInfos. One key may map to multiple ReJitInfos if there have been - // multiple rejit requests made for the same MD. See - // code:ReJitManager::ReJitManager#Invariants for more information. - typedef SHash ReJitInfoHash; - // One global crst (for the entire CLR instance) to synchronize // cross-ReJitManager operations, such as batch calls to RequestRejit and // RequestRevert (which modify multiple ReJitManager instances). static CrstStatic s_csGlobalRequest; - // All The ReJitInfos (and their linked SharedReJitInfos) for this domain. - ReJitInfoHash m_table; - - // The crst that synchronizes the data in m_table, including - // adding/removing to m_table, as well as state changes made to - // individual ReJitInfos & SharedReJitInfos in m_table. - CrstExplicitInit m_crstTable; - #endif //FEATURE_REJIT public: - // The ReJITManager takes care of grabbing its m_crstTable when necessary. However, - // for clients who need to do this explicitly (like ETW rundown), this holder may be - // used. - class TableLockHolder -#ifdef FEATURE_REJIT - : public CrstHolder -#endif - { - public: - TableLockHolder(ReJitManager * pReJitManager); - }; static void InitStatic(); static BOOL IsReJITEnabled(); - static void OnAppDomainExit(AppDomain * pAppDomain); - static HRESULT RequestReJIT( ULONG cFunctions, ModuleID rgModuleIDs[], @@ -488,77 +160,53 @@ class ReJitManager ReJitManager(); - void PreInit(BOOL fSharedDomain); - - ReJITID GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart); + static ReJITID GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart); - ReJITID GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart); + static ReJITID GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart); - PCODE GetCodeStart(PTR_MethodDesc pMD, ReJITID reJitId); - - HRESULT GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[]); + static HRESULT GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[]); #ifdef FEATURE_REJIT - - INDEBUG(BOOL IsTableCrstOwnedByCurrentThread()); - private: static HRESULT IsMethodSafeForReJit(PTR_MethodDesc pMD); - static void ReportReJITError(ReJitReportErrorWorkItem* pErrorRecord); + static void ReportReJITError(CodeVersionManager::CodePublishError* pErrorRecord); static void ReportReJITError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus); - static HRESULT AddReJITError(ReJitInfo* pReJitInfo, HRESULT hrStatus, CDynArray * pErrors); - static HRESULT AddReJITError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus, CDynArray * pErrors); - HRESULT BatchUpdateJumpStamps(CDynArray * pUndoMethods, CDynArray * pPreStubMethods, CDynArray * pErrors); - PCODE DoReJitIfNecessaryWorker(PTR_MethodDesc pMD); // Invokes the jit, or returns previously rejitted code - DWORD GetCurrentReJitFlagsWorker(PTR_MethodDesc pMD); + static PCODE DoReJitIfNecessaryWorker(MethodDesc* pMD); // Invokes the jit, or returns previously rejitted code + static DWORD GetCurrentReJitFlagsWorker(PTR_MethodDesc pMD); + + static HRESULT BindILVersion( + CodeVersionManager* pCodeVersionManager, + CodeVersionManager::JumpStampBatch* pJumpStampBatch, + PTR_Module pModule, + mdMethodDef methodDef, + ILCodeVersion *pILCodeVersion); - HRESULT MarkAllInstantiationsForReJit( - SharedReJitInfo * pSharedForAllGenericInstantiations, + static HRESULT MarkAllInstantiationsForReJit( + CodeVersionManager* pCodeVersionManager, + ILCodeVersion ilCodeVersion, AppDomain * pAppDomainToSearch, PTR_Module pModuleContainingGenericDefinition, mdMethodDef methodDef, - ReJitManagerJumpStampBatch* pJumpStampBatch, - CDynArray * pRejitErrors); - - INDEBUG(BaseDomain * m_pDomain;) - INDEBUG(void Dump(LPCSTR szIntroText);) - INDEBUG(void AssertRestOfEntriesAreReverted( - ReJitInfoHash::KeyIterator iter, - ReJitInfoHash::KeyIterator end);) - - - HRESULT DoJumpStampIfNecessary(MethodDesc* pMD, PCODE pCode); - HRESULT MarkForReJit(PTR_MethodDesc pMD, SharedReJitInfo * pSharedToReuse, ReJitManagerJumpStampBatch* pJumpStampBatch, CDynArray * pRejitErrors, SharedReJitInfo ** ppSharedUsed); - HRESULT MarkForReJit(PTR_Module pModule, mdMethodDef methodDef, ReJitManagerJumpStampBatch* pJumpStampBatch, CDynArray * pRejitErrors, SharedReJitInfo ** ppSharedUsed); - HRESULT MarkForReJitHelper( - PTR_MethodDesc pMD, - PTR_Module pModule, - mdMethodDef methodDef, - SharedReJitInfo * pSharedToReuse, - ReJitManagerJumpStampBatch* pJumpStampBatch, - CDynArray * pRejitErrors, - /* out */ SharedReJitInfo ** ppSharedUsed); - HRESULT AddNewReJitInfo( - PTR_MethodDesc pMD, - PTR_Module pModule, - mdMethodDef methodDef, - SharedReJitInfo * pShared, - ReJitInfo ** ppInfo); - HRESULT RequestRevertByToken(PTR_Module pModule, mdMethodDef methodDef); - PTR_ReJitInfo FindReJitInfo(PTR_MethodDesc pMD, PCODE pCodeStart, ReJITID reJitId); - PTR_ReJitInfo FindNonRevertedReJitInfo(PTR_Module pModule, mdMethodDef methodDef); - PTR_ReJitInfo FindNonRevertedReJitInfo(PTR_MethodDesc pMD); - PTR_ReJitInfo FindNonRevertedReJitInfoHelper(PTR_MethodDesc pMD, PTR_Module pModule, mdMethodDef methodDef); - ReJitInfo* FindPreReJittedReJitInfo(ReJitInfoHash::KeyIterator beginIter, ReJitInfoHash::KeyIterator endIter); - HRESULT Revert(SharedReJitInfo * pShared, ReJitManagerJumpStampBatch* pJumpStampBatch); - PCODE DoReJit(ReJitInfo * pInfo); - ReJitInfoHash::KeyIterator GetBeginIterator(PTR_MethodDesc pMD); - ReJitInfoHash::KeyIterator GetEndIterator(PTR_MethodDesc pMD); - ReJitInfoHash::KeyIterator GetBeginIterator(PTR_Module pModule, mdMethodDef methodDef); - ReJitInfoHash::KeyIterator GetEndIterator(PTR_Module pModule, mdMethodDef methodDef); - void RemoveReJitInfosFromDomain(AppDomain * pAppDomain); + CodeVersionManager::JumpStampBatch* pJumpStampBatch, + CDynArray * pRejitErrors); + + INDEBUG(static void AssertRestOfEntriesAreReverted( + ILCodeVersionIterator iter, + ILCodeVersionIterator end);) + + + static HRESULT DoJumpStampIfNecessary(MethodDesc* pMD, PCODE pCode); + static HRESULT MarkForReJit(CodeVersionManager* pCodeVersionManager, + PTR_MethodDesc pMD, + ILCodeVersion ilCodeVersion, + CodeVersionManager::JumpStampBatch* pJumpStampBatch, + CDynArray * pRejitErrors); + + static HRESULT RequestRevertByToken(PTR_Module pModule, mdMethodDef methodDef); + static HRESULT Revert(ILCodeVersion ilCodeVersion, CodeVersionManager::JumpStampBatch* pJumpStampBatch); + static PCODE DoReJit(ILCodeVersion ilCodeVersion, MethodDesc* pMethod); #endif // FEATURE_REJIT diff --git a/src/vm/rejit.inl b/src/vm/rejit.inl index 8662eeaedf85..76e8405ad7dc 100644 --- a/src/vm/rejit.inl +++ b/src/vm/rejit.inl @@ -13,149 +13,6 @@ #ifdef FEATURE_REJIT -inline SharedReJitInfo::InternalFlags SharedReJitInfo::GetState() -{ - LIMITED_METHOD_CONTRACT; - - return (InternalFlags)(m_dwInternalFlags & kStateMask); -} - -inline ReJitInfo::ReJitInfo(PTR_MethodDesc pMD, SharedReJitInfo * pShared) : - m_key(pMD), - m_pShared(pShared) -{ - LIMITED_METHOD_CONTRACT; - - CommonInit(); -} - -inline ReJitInfo::ReJitInfo(PTR_Module pModule, mdMethodDef methodDef, SharedReJitInfo * pShared) : - m_key(pModule, methodDef), - m_pShared(pShared) -{ - LIMITED_METHOD_CONTRACT; - - CommonInit(); -} - -inline ReJitInfo::Key::Key() : - m_pMD(NULL), - m_methodDef(mdTokenNil), - m_keyType(kUninitialized) -{ - LIMITED_METHOD_CONTRACT; -} - -inline ReJitInfo::Key::Key(PTR_MethodDesc pMD) : - m_pMD(dac_cast(pMD)), - m_methodDef(mdTokenNil), - m_keyType(kMethodDesc) -{ - LIMITED_METHOD_CONTRACT; -} - -inline ReJitInfo::Key::Key(PTR_Module pModule, mdMethodDef methodDef) : - m_pModule(dac_cast(pModule)), - m_methodDef(methodDef), - m_keyType(kMetadataToken) -{ - LIMITED_METHOD_CONTRACT; -} - -inline ReJitInfo::Key ReJitInfo::GetKey() -{ - LIMITED_METHOD_CONTRACT; - - return m_key; -} - -inline ReJitInfo::InternalFlags ReJitInfo::GetState() -{ - LIMITED_METHOD_CONTRACT; - - return (InternalFlags)(m_dwInternalFlags & kStateMask); -} - -inline PTR_MethodDesc ReJitInfo::GetMethodDesc() -{ - LIMITED_METHOD_CONTRACT; - - _ASSERTE(m_key.m_keyType == Key::kMethodDesc); - return PTR_MethodDesc(m_key.m_pMD); -} - -inline void ReJitInfo::GetModuleAndToken(Module ** ppModule, mdMethodDef * pMethodDef) -{ - LIMITED_METHOD_CONTRACT; - - _ASSERTE(ppModule != NULL); - _ASSERTE(pMethodDef != NULL); - _ASSERTE(m_key.m_keyType == Key::kMetadataToken); - - *ppModule = PTR_Module(m_key.m_pModule); - *pMethodDef = (mdMethodDef) m_key.m_methodDef; -} - -#ifdef _DEBUG -inline BOOL ReJitInfo::CodeIsSaved() -{ - LIMITED_METHOD_CONTRACT; - - for (size_t i=0; i < sizeof(m_rgSavedCode); i++) - { - if (m_rgSavedCode[i] != 0) - return TRUE; - } - return FALSE; -} -#endif //_DEBUG - -// static -inline ReJitInfoTraits::key_t ReJitInfoTraits::GetKey(const element_t &e) -{ - LIMITED_METHOD_CONTRACT; - - return e->GetKey(); -} - -// static -inline BOOL ReJitInfoTraits::Equals(key_t k1, key_t k2) -{ - LIMITED_METHOD_CONTRACT; - - // Always use the values of the TADDRs of the MethodDesc * and Module * when treating - // them as lookup keys into the SHash. - - if (k1.m_keyType == ReJitInfo::Key::kMethodDesc) - { - return ((k2.m_keyType == ReJitInfo::Key::kMethodDesc) && - (dac_cast(PTR_MethodDesc(k1.m_pMD)) == - dac_cast(PTR_MethodDesc(k2.m_pMD)))); - } - - _ASSERTE(k1.m_keyType == ReJitInfo::Key::kMetadataToken); - return ((k2.m_keyType == ReJitInfo::Key::kMetadataToken) && - (dac_cast(PTR_Module(k1.m_pModule)) == - dac_cast(PTR_Module(k2.m_pModule))) && - (k1.m_methodDef == k2.m_methodDef)); -} - -// static -inline ReJitInfoTraits::count_t ReJitInfoTraits::Hash(key_t k) -{ - LIMITED_METHOD_CONTRACT; - - return ReJitInfo::Hash(k); -} - -// static -inline bool ReJitInfoTraits::IsNull(const element_t &e) -{ - LIMITED_METHOD_CONTRACT; - - return e == NULL; -} - // static inline void ReJitManager::InitStatic() { @@ -172,92 +29,8 @@ inline BOOL ReJitManager::IsReJITEnabled() return CORProfilerEnableRejit(); } -inline ReJitManager::ReJitInfoHash::KeyIterator ReJitManager::GetBeginIterator(PTR_MethodDesc pMD) -{ - LIMITED_METHOD_CONTRACT; -#ifndef DACCESS_COMPILE - _ASSERTE(m_crstTable.OwnedByCurrentThread()); -#endif - return m_table.Begin(ReJitInfo::Key(pMD)); -} - -inline ReJitManager::ReJitInfoHash::KeyIterator ReJitManager::GetEndIterator(PTR_MethodDesc pMD) -{ - LIMITED_METHOD_CONTRACT; -#ifndef DACCESS_COMPILE - _ASSERTE(m_crstTable.OwnedByCurrentThread()); -#endif - return m_table.End(ReJitInfo::Key(pMD)); -} - -inline ReJitManager::ReJitInfoHash::KeyIterator ReJitManager::GetBeginIterator(PTR_Module pModule, mdMethodDef methodDef) -{ - LIMITED_METHOD_CONTRACT; -#ifndef DACCESS_COMPILE - _ASSERTE(m_crstTable.OwnedByCurrentThread()); -#endif - return m_table.Begin(ReJitInfo::Key(pModule, methodDef)); -} - -inline ReJitManager::ReJitInfoHash::KeyIterator ReJitManager::GetEndIterator(PTR_Module pModule, mdMethodDef methodDef) -{ - LIMITED_METHOD_CONTRACT; -#ifndef DACCESS_COMPILE - _ASSERTE(m_crstTable.OwnedByCurrentThread()); -#endif - return m_table.End(ReJitInfo::Key(pModule, methodDef)); -} - -#ifdef _DEBUG -inline BOOL ReJitManager::IsTableCrstOwnedByCurrentThread() -{ - LIMITED_METHOD_CONTRACT; - - return m_crstTable.OwnedByCurrentThread(); -} -#endif //_DEBUG - - -inline HRESULT ReJitManager::MarkForReJit( - PTR_MethodDesc pMD, - SharedReJitInfo * pSharedToReuse, - ReJitManagerJumpStampBatch* pJumpStampBatch, - CDynArray * pRejitErrors, - /* out */ SharedReJitInfo ** ppSharedUsed) -{ - WRAPPER_NO_CONTRACT; - - return MarkForReJitHelper(pMD, NULL, mdTokenNil, pSharedToReuse, pJumpStampBatch, pRejitErrors, ppSharedUsed); -} - -inline HRESULT ReJitManager::MarkForReJit( - PTR_Module pModule, - mdMethodDef methodDef, - ReJitManagerJumpStampBatch* pJumpStampBatch, - CDynArray * pRejitErrors, - /* out */ SharedReJitInfo ** ppSharedUsed) -{ - WRAPPER_NO_CONTRACT; - - return MarkForReJitHelper(NULL, pModule, methodDef, NULL, pJumpStampBatch, pRejitErrors, ppSharedUsed); -} - -inline PTR_ReJitInfo ReJitManager::FindNonRevertedReJitInfo(PTR_Module pModule, mdMethodDef methodDef) -{ - WRAPPER_NO_CONTRACT; - - return FindNonRevertedReJitInfoHelper(NULL, pModule, methodDef); -} - -inline PTR_ReJitInfo ReJitManager::FindNonRevertedReJitInfo(PTR_MethodDesc pMD) -{ - WRAPPER_NO_CONTRACT; - - return FindNonRevertedReJitInfoHelper(pMD, NULL, NULL); -} - //static -inline void ReJitManager::ReportReJITError(ReJitReportErrorWorkItem* pErrorRecord) +inline void ReJitManager::ReportReJITError(CodeVersionManager::CodePublishError* pErrorRecord) { CONTRACTL { @@ -299,14 +72,6 @@ inline void ReJitManager::ReportReJITError(Module* pModule, mdMethodDef methodDe #endif // PROFILING_SUPPORTED } -inline ReJitManager::TableLockHolder::TableLockHolder(ReJitManager * pReJitManager) -#ifdef FEATURE_REJIT - : CrstHolder(&pReJitManager->m_crstTable) -#endif // FEATURE_REJIT -{ - WRAPPER_NO_CONTRACT; -} - #else // FEATURE_REJIT // On architectures that don't support rejit, just keep around some do-nothing @@ -335,10 +100,6 @@ inline void ReJitManager::InitStatic() { } -inline ReJitManager::TableLockHolder::TableLockHolder(ReJitManager *) -{ -} - #endif // FEATURE_REJIT