From ff64ccdc1512bec0ce9ce77b773e74f51380872c Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 8 Jan 2020 10:58:19 -0800 Subject: [PATCH 01/31] Implement simple version of On Stack Replacement (OSR) Add support to runtime and jit to allow switching from unoptimized to optimized code for a method while the method has active stack frames. Details in the included document. --- docs/design/features/OnStackReplacement.md | 1095 +++++++++++++++++ src/coreclr/clrdefinitions.cmake | 3 + .../src/ToolBox/superpmi/mcs/verbdumpmap.cpp | 12 +- .../superpmi-shared/compileresult.cpp | 32 + .../superpmi/superpmi-shared/compileresult.h | 8 + .../superpmi/superpmi-shared/crlwmlist.h | 1 + .../superpmi-shared/icorjitinfoimpl.h | 5 + .../superpmi-shared/methodcontext.cpp | 49 + .../superpmi/superpmi-shared/methodcontext.h | 9 +- .../superpmi-shim-collector/icorjitinfo.cpp | 8 + .../superpmi-shim-counter/icorjitinfo.cpp | 7 + .../superpmi-shim-simple/icorjitinfo.cpp | 6 + .../ToolBox/superpmi/superpmi/icorjitinfo.cpp | 8 + src/coreclr/src/inc/CrstTypes.def | 3 + src/coreclr/src/inc/clrconfigvalues.h | 11 + src/coreclr/src/inc/corinfo.h | 157 ++- src/coreclr/src/inc/corjitflags.h | 2 +- src/coreclr/src/inc/crsttypes.h | 167 +-- src/coreclr/src/inc/jithelpers.h | 2 + src/coreclr/src/jit/CMakeLists.txt | 1 + src/coreclr/src/jit/block.cpp | 8 + src/coreclr/src/jit/block.h | 11 +- src/coreclr/src/jit/codegencommon.cpp | 236 +++- src/coreclr/src/jit/codegenxarch.cpp | 6 + src/coreclr/src/jit/compiler.cpp | 136 +- src/coreclr/src/jit/compiler.h | 50 +- src/coreclr/src/jit/compphases.h | 1 + src/coreclr/src/jit/emitxarch.cpp | 6 +- src/coreclr/src/jit/flowgraph.cpp | 297 +++-- src/coreclr/src/jit/gcencode.cpp | 60 +- src/coreclr/src/jit/gschecks.cpp | 2 +- src/coreclr/src/jit/importer.cpp | 150 ++- src/coreclr/src/jit/jitconfigvalues.h | 5 + src/coreclr/src/jit/jitee.h | 2 +- src/coreclr/src/jit/jiteh.cpp | 19 + src/coreclr/src/jit/lclvars.cpp | 167 ++- src/coreclr/src/jit/lsrabuild.cpp | 21 +- src/coreclr/src/jit/morph.cpp | 26 +- src/coreclr/src/jit/patchpoint.cpp | 238 ++++ src/coreclr/src/jit/rangecheck.cpp | 4 +- src/coreclr/src/jit/valuenum.cpp | 17 +- .../tools/Common/JitInterface/CorInfoBase.cs | 168 +-- .../tools/Common/JitInterface/CorInfoImpl.cs | 3 + .../tools/Common/JitInterface/CorInfoTypes.cs | 10 + .../ThunkGenerator/ThunkInput.txt | 2 + .../ReadyToRun/DebugInfoTableNode.cs | 7 + .../JitInterface/CorInfoImpl.ReadyToRun.cs | 9 + .../crossgen2/jitinterface/jitinterface.h | 9 + .../crossgen2/jitinterface/jitwrapper.cpp | 10 +- src/coreclr/src/vm/codeman.h | 1 + src/coreclr/src/vm/codeversion.cpp | 64 +- src/coreclr/src/vm/codeversion.h | 17 + src/coreclr/src/vm/compile.cpp | 2 +- src/coreclr/src/vm/debuginfostore.cpp | 67 +- src/coreclr/src/vm/debuginfostore.h | 5 + src/coreclr/src/vm/eeconfig.cpp | 14 + src/coreclr/src/vm/eeconfig.h | 17 + src/coreclr/src/vm/jithelpers.cpp | 433 ++++++- src/coreclr/src/vm/jitinterface.cpp | 48 +- src/coreclr/src/vm/jitinterface.h | 24 + src/coreclr/src/vm/method.hpp | 6 + src/coreclr/src/vm/prestub.cpp | 18 + src/coreclr/src/zap/zapinfo.cpp | 8 +- src/coreclr/src/zap/zapinfo.h | 1 + .../src/JIT/opt/OSR/addressexposedlocal.cs | 37 + .../JIT/opt/OSR/addressexposedlocal.csproj | 11 + .../tests/src/JIT/opt/OSR/doublestackalloc.cs | 32 + .../src/JIT/opt/OSR/doublestackalloc.csproj | 11 + src/coreclr/tests/src/JIT/opt/OSR/example.cs | 43 + .../tests/src/JIT/opt/OSR/example.csproj | 10 + .../JIT/opt/OSR/genericmethodpatchpoint.cs | 31 + .../opt/OSR/genericmethodpatchpoint.csproj | 10 + .../tests/src/JIT/opt/OSR/innerloop.cs | 26 + .../tests/src/JIT/opt/OSR/innerloop.csproj | 10 + .../tests/src/JIT/opt/OSR/integersumloop.cs | 36 + .../src/JIT/opt/OSR/integersumloop.csproj | 10 + .../tests/src/JIT/opt/OSR/livelocaladdress.cs | 26 + .../src/JIT/opt/OSR/livelocaladdress.csproj | 11 + .../src/JIT/opt/OSR/livelocalstackalloc.cs | 30 + .../JIT/opt/OSR/livelocalstackalloc.csproj | 11 + src/coreclr/tests/src/JIT/opt/OSR/mainloop.cs | 20 + .../tests/src/JIT/opt/OSR/mainloop.csproj | 10 + .../tests/src/JIT/opt/OSR/mainloop2.cs | 24 + .../tests/src/JIT/opt/OSR/mainloop2.csproj | 10 + .../tests/src/JIT/opt/OSR/mainlooptry.cs | 28 + .../tests/src/JIT/opt/OSR/mainlooptry.csproj | 10 + .../tests/src/JIT/opt/OSR/mainlooptry2.cs | 39 + .../tests/src/JIT/opt/OSR/mainlooptry2.csproj | 10 + .../tests/src/JIT/opt/OSR/mainlooptry3.cs | 45 + .../tests/src/JIT/opt/OSR/mainlooptry3.csproj | 10 + .../tests/src/JIT/opt/OSR/mainlooptry4.cs | 43 + .../tests/src/JIT/opt/OSR/mainlooptry4.csproj | 10 + .../tests/src/JIT/opt/OSR/memoryargument.cs | 29 + .../src/JIT/opt/OSR/memoryargument.csproj | 10 + .../tests/src/JIT/opt/OSR/nesteddoloops.cs | 32 + .../src/JIT/opt/OSR/nesteddoloops.csproj | 10 + .../src/JIT/opt/OSR/originalstackalloc.cs | 31 + .../src/JIT/opt/OSR/originalstackalloc.csproj | 11 + .../tests/src/JIT/opt/OSR/osrcontainstry.cs | 39 + .../src/JIT/opt/OSR/osrcontainstry.csproj | 11 + src/coreclr/tests/src/JIT/opt/OSR/promoted.cs | 53 + .../tests/src/JIT/opt/OSR/promoted.csproj | 10 + .../tests/src/JIT/opt/OSR/tailrecurse.cs | 35 + .../tests/src/JIT/opt/OSR/tailrecurse.csproj | 10 + .../tests/src/JIT/opt/OSR/tailrecursetry.cs | 42 + .../src/JIT/opt/OSR/tailrecursetry.csproj | 10 + .../tests/src/JIT/opt/OSR/tailrecursetry2.cs | 42 + .../src/JIT/opt/OSR/tailrecursetry2.csproj | 11 + .../tests/src/JIT/opt/OSR/twoosrmethods.cs | 45 + .../src/JIT/opt/OSR/twoosrmethods.csproj | 10 + .../tests/src/JIT/opt/OSR/twoosrmethods1.cs | 42 + .../src/JIT/opt/OSR/twoosrmethods1.csproj | 10 + 112 files changed, 4721 insertions(+), 367 deletions(-) create mode 100644 docs/design/features/OnStackReplacement.md create mode 100644 src/coreclr/src/jit/patchpoint.cpp create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/example.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/example.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/innerloop.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/innerloop.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/integersumloop.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/integersumloop.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainloop.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainloop.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainloop2.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/memoryargument.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/memoryargument.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/promoted.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/promoted.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.csproj create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.cs create mode 100644 src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.csproj diff --git a/docs/design/features/OnStackReplacement.md b/docs/design/features/OnStackReplacement.md new file mode 100644 index 00000000000000..88cdf87bad9aa9 --- /dev/null +++ b/docs/design/features/OnStackReplacement.md @@ -0,0 +1,1095 @@ +# On Stack Replacement in the CLR + +Design Sketch and Prototype Assessment + +Andy Ayers + +Initial: 7 July 2019 — +Revised: 25 February 2020 + +## Overview + +On Stack Replacement allows the code executed by currently running methods to be +changed in the middle of method execution, while those methods are active "on +stack." This document describes design considerations and challenges involved in +implementing basic On Stack Replacement for the CLR, presents the results of +some prototype investigations, and describes how OSR might be used to re-host +Edit and Continue and support more general transitions like deoptimization. + +* [Background](#1-Background) +* [Design Prinicples](#2-Design-Principles) +* [An Overview of OSR](#3-An-Overview-of-OSR) +* [Complications](#4-Complications) +* [The Prototype](#5-The-Prototype) +* [Edit and Continue](#6-Edit-and-Continue) +* [Deoptimization](#7-Deoptimization) +* [References](#8-References) + +## 1. Background + +On Stack Replacement (hereafter _OSR_) refers to a set of techniques for +migrating active stack frames from one version of code to another. + +The two versions of the code involved in OSR may arise from different program +sources (as in Edit and Continue) or different approaches to compiling or +executing a single program (say, unoptimized code versus optimized code). The +goal of OSR is to transparently redirect execution from an old version of code +into a new version, even when in the middle of executing the old version. + +Initial work on OSR was pioneered in Self [[1](#1)] as an approach for debugging +optimized code. But in the years since, OSR has mainly seen adoption on +platforms like Java [[2](#2), [3](#3)] and JavaScript that rely heavily on +adaptive recompilation of code. + +The ability to adaptively recompile and switch code versions while methods are +running provides some key advantages: + +* Platforms can offer both quick start up and excellent steady-state + performance, interpreting or quickly jitting to enable initial method + execution, and using OSR to update the methods with better performing or more + completely compiled versions as needed. + +* Platforms can take advantage of transient program facts and recover when those + facts no longer become true. For example, a platform may compile virtual or + interface calls as direct calls initially and use OSR to update to more + general versions of code when overriding methods or other interface + implementations arrive on scene. + +The CLR already supports various mechanisms for changing the code for a method +in a runtime instance. Edit and Continue implements true OSR but is supported +only on some architectures, works only when code is running under a managed +debugger, and is supported only for unoptimized to unoptimized code. Profiler +rejit and tiered compilation can update code used in future invocations of +methods, but not code running in currently active methods. + +In this document we will vary a bit from the literature and use OSR to refer +strictly to the case where we are transitioning execution **from** an +unoptimized code instance (either to another unoptimized instance or an +optimized instance). We will use _deoptimization_ (_deopt_) to describe the +transition from an optimized code instance to some other code instance +(typically to an unoptimized instance). + +We envision OSR as a technology that will allow us to enable tiered compilation +by default: performance-critical applications will no longer risk seeing key +methods trapped in unoptimized tier0 code, and straightforwardly written +microbenchmarks (e.g. all code in main) will perform as expected, as no matter +how they are coded, they will be able to transition to optimized code. + +OSR also provides key building blocks for an eventual implementation of deopt +and the ability of our platforms to make strong speculative bets in code +generation. + +In addition, OSR will also allow us to experiment with so-called _deferred +compilation_, where the jit initially only compiles parts of methods that it +believes likely to execute (say, based on heuristics or prior runs). If an +uncompiled part of a method is reached at runtime, OSR can trigger recompilation +of the missing part or recompilation of the entire method. + +The remainder of this document describes OSR in more detail, providing a design +sketch and some key design choice points, the results and insights gained from +creating a fully functional prototype, and a list of open issues and areas +requiring further investigation. We will also mention _deopt_ in passing and +describe why it presents a different and larger set of challenges. + +## 2. Design Principles + +As we consider proposals for implementing OSR, we will try and satisfy the +following design principles: + +* Pay as you go. The costs of OSR should be limited to methods that can benefit + from OSR, and where possible, paid largely when OSR actually happens. + +* Impose few restrictions on optimized codegen. We should not have to restrict + or dumb down optimized codegen to allow transitions to it via OSR + +* Anticipate likely changes in jit codegen strategy. We should support enabling + some optimizations (devirtualization, early branch pruning, some expression + opts) at Tier0 without having to radically alter our approach. + +* Design for testability. We should be able to force OSR transitions wherever + possible and with alternative stress strategies. + +* Full diagnostic experience. OSR should not inhibit user ability to debug or + reason about logical behavior of their programs. OSR activities should be + tracked via suitable eventing mechanisms. + +## 3 An overview of OSR + +OSR enables transitioning from older unoptimized code to new code +while the old code is active in some stack frames. An implementation +must come up with solutions to several related sub problems, which we +describe briefly here, and in more detail below. + +* **Patchpoints** : Identify where in the original method OSR is possible. +We will use the term _patchpoint_ to describe a particular location in a +method's code that supports OSR transitions. +* **Triggers** : Determine what will trigger an OSR transition +* **Alternatives** : Have means to prepare a suitable alternative code +version covering all or part of the method (loops, for instance), and +having one or possibly many entry points. +* **Transitions**: Remap the stack frame(s) as needed to carry out the +OSR transition + +### 3.1 Patchpoints + +A _patchpoint_ is a point in a version of code where OSR is possible. +Patchpoints are similar in many ways to GC safepoints. At a patchpoint, the live +state of the ongoing computation must be identifiable (for a GC safepoint, only +the live GC references need be so identified). All live registers and stack +slots must be enumerable, and logically described in terms of concepts visible +in the IL. Additional state like the return address, implicit arguments, and so +on must also be accounted for. + +As with GC safepoints, patchpoints can be handled in a _fully interruptible_ +manner here most any instruction boundary is a patchpoint, or a _partially +interruptible_ manner, where only some instruction boundaries are patchpoints. +Also, as with GC, it is acceptable (if suboptimal) to over-identify the live +state at a patch point. For instance, the live set can include values that never +end up being consumed by the new method. + +Also, as with GC safepoints, it is desirable to keep the volume of information +that must be retained to describe patchpoints to a minimum. Most methods +executions will never undergo OSR transition and so the information generated +will never be consulted. To try and keep OSR a _pay as you go_ technique, it is +important that this information be cheap to generate and store. + +#### 3.1.1 Choosing Patchpoints + +Most commonly, patchpoints are chosen to be the places in the code that are +targets of loop back edges. This is a partially interruptible scheme. This +ensures that no loop in the method can iterate without hitting a patchpoint, and +so that the method itself cannot execute indefinitely between patchpoints. Note +by this rule, methods that do not contain any loops will not have any +patchpoints. + +From a compilation standpoint, it would be ideal if patchpoints were also stack +empty points, as this tends to minimize the live state. However, there is no +guarantee that execution of a method will reach stack empty points with any +frequency. So, a fully general patchpoint mechanism must handle the case where +the evaluation stack is not empty. However, it may be acceptable to only allow +patchpoints at stack empty points, as loops that execute with non-empty +evaluation stacks are likely rare. + +It is also beneficial if patchpoint selection works via a fairly simple set of +rules, and here we propose that using the set of _lexical back edges_ or +backwards branches in IL is a reasonable choice. These can be identified by a +single scan over a method's IL. + +When generating unoptimized code, it is thus sufficient to note the target of +any backwards branch in IL, the set of those locations (filtered to just the +subset where the IL stack is empty) are the candidate patchpoints in the method. + +With the above restrictions, the live state at every patchpoint in the method +can be reported in the same way. And because the patchpoints are necessarily at +basic block boundaries, and the jit will not allocate user state to registers +across such boundaries, patchpoint reporting in these cases is _untracked_ +(corresponds to untracked GC lifetime): that is to say, a single patchpoint +descriptor suffices for the entire method. Further, this information is a +superset of the current GC info, so the additional data needed to describe a +patchpoint is simply the set of live non-GC slots on the stack. + +[Note: more general schemes like _deopt_ will require something more +sophisticated.] + + +#### 3.1.2 Option I: non-stack empty patchpoints + +If it turns out we must also allow patchpoints at non-stack empty points, then +some per-patchpoint state will be needed to map the logical state of the +evaluation stack into actual stack slots on the methods frame. This state will +vary from patchpoint to patchpoint. + +#### 3.1.3 Option II: fully interruptible patchpoints + +Patchpoints can be much more fine-grained, at any block boundary or even within +blocks, so long as the correspondence of the generated code to the inspiring IL +is well understood. However fine-grained patchpoints in our proposed version of +OSR do not seem to offer much in the way of advantages, given that we are also +proposing synchronous triggers and transitions, and transitioning from +unoptimized code. A fine-grained patchpoint mechanism would require more +metadata to describe each transition point. + +#### 3.1.4 The Prototype + +In the prototype, patchpoints are the set of IL boundaries in a method that are +stack-empty and the targets of lexical back edges. The live state of the +original method is just the IL-visible locals and arguments, plus a few special +values found in certain frames (GS Cookie, etc). + +### 3.2 Triggers + +When OSR is used to enable transfer control from an unoptimized method into +optimized code, the most natural trigger is a count of the number of times a +patchpoint in the method is reached. Once a threshold is reached at a +patchpoint, the system can begin preparation of the alternative code version +that will work for that patchpoint. + +This counting can be done fairly efficiently, at least in comparison to the +ambient unoptimized code in the method, by using counters on the local frame. +When the threshold is reached, control can transfer to a local policy block; +this can check whether an alternative version needs to be prepared, is already +being prepared, or is ready for transfer. Since this policy logic is common to +all patchpoints it most likely should be encapsulated as a helper. In +pseudocode: + +``` +Patchpoint: // each assigned a dense set of IDs + + if (++counter[ppID] > threshold) call PatchpointHelper(ppID) +``` +The helper can use the return address to determine which patchpoint is making +the request. To keep overheads manageable, we might instead want to down-count +and pass the counter address to the helper. +``` +Patchpoint: // each assigned a dense set of IDs + + if (--counter[ppID] <= 0) call PatchpointHelper(ppID, &counter[ppID]) +``` +The helper logic would be similar to the following: +``` +PatchpointHelper(int ppID, int* counter) +{ + void* patchpointSite = _ReturnAddress(); + PPState s = GetState(patchpointSite); + + switch (s) + { + case Unknown: + *counter = initialThreshold; + SetState(s, Active); + return; + + case Active: + *counter = checkThreshold; + SetState(s, Pending); + RequestAlternative(ppID); + return; + + case Pending: + *counter = checkThreshold; + return; + + case Ready: + Transition(...); // does not return + } +} +``` +Here `RequestAlternative` would queue up a request to produce the alternative +code version; when that request completes the patchpoint state would be set to +Ready. So the cost for a patchpoint would be an initial helper call (to set the +Active threshold), then counting, then a second helper call (to request and set +the pending threshold), then counting, and, depending on how long the request +took, more callbacks in pending state. + +Note that just because a patchpoint is hit often enough to reach Active state, +there is no guarantee that the patchpoint will be reached again in the future. +So, it is possible to trigger alternative version compilations that end up never +getting used, if those alternative versions are patchpoint specific. In a +pathological case a method might have an entire sequence of patchpoints that +reach Active state and trigger alternative versions, none of which ever get +used. + +In this scheme, the local frame of the method would have one local counter per +patchpoint. + +#### 3.2.1 Option I: one global counter per patchpoint + +Instead of keeping the counters on the local frame, they could be kept in global +storage associated with the method, to give an absolute count of patchpoint +frequency over all invocations of the method. This would help trigger +transitions in methods in use across multiple threads or methods that are a weak +mixture of iteration and recursion. Because there would now be shared counter +state, we'd have to think though how to handle the concurrent access. Likely +we'd implement something like we do for IBC and have a method fetch and locally +cache the address of its counter vector locally in the prolog. + +#### 3.2.2 Option II: shared counters + +Alternatively, all patchpoints in a method could share one counter slot (either +local or global), this would save space but would lead to somewhat more frequent +callbacks into the runtime and slightly higher likelihood that useless +alternatives would be created. + +#### 3.2.3 Option III: synchronous OSR + +Independent of the counter scheme, the runtime could also block and +synchronously produce and then transition to the alternative version. This would +eliminate the potential for wasted alternates (though depending on other +choices, we still might produce multiple alternates for a method). It would also +hold up progress of the app, as the thread could just as well continue executing +the unoptimized code past the patchpoint. We might consider transitioning to +synchronous OSR selectively for methods that have a track record of generating +useless versions. This is entirely a runtime policy and would not impact jitted +codegen. + +Note: If OSR is used for EnC or for _deopt_ when an invariant changes, then +synchronous transitions are required as in general, the old method cannot safely +execute past a patchpoint. If the delay from jitting code is a concern it may be +possible to fall back to an interpreter for a time while the new version of the +method is jitted, though this would require that the system also support +OSR-style transitions from interpreted methods to compiled methods... + +#### 3.2.4 Option IV: share counter space with Tiered Compilation + +A final option here is to use global counters and also add a counter at method +entry. The entry counter could be used for two purposes: first to trigger tiered +jitting of the entire method, and second, to help normalize the per-patchpoint +counters so as to provide relative profile weights for the blocks in the method +when it is rejitted (either via tiering or OSR). We note that the set of +observation points from patchpoint counters is fairly sparse (not as detailed as +what we get from IBC, say) but it may be sufficient to build a reasonable +profile. + +#### 3.2.5 The Prototype + +In the prototype OSR transitions are synchronous; there is one local patchpoint +counter per frame shared by all patchpoints; patchpoint IDs are IL offsets. + +### 3.3 Alternative Versions + +When a patchpoint is hit often enough, the runtime should produce an alternative +version of the code that can be transitioned to at that patchpoint. + +There are several choice points for alternatives: + +* Whether to tailor the alternative code specifically to that patchpoint or have + the alternative handle multiple (or perhaps all) the patchpoints in a method. + We'll call the former a single-entry alternative, and the latter + multi-entry alternatives (and, in the limit, whole-method alternatives). + +* Whether the alternative version encompasses the remainder of the method, or + just some part of the method. We'll call these whole and partial + alternatives. + +* If a partial method alternative, whether the part of the method compiled + includes the entire remainder of the method, or just some fragment that + includes the patchpoint (say the enclosing loop nest). + +* Whether or not the alternative entry points include the code to build up the + alternative stack frames, or setup of the new frame happens via some runtime + logic. + +* Whether or not the alternate version is tailored to the actual runtime state + at the point of the trigger. For instance, specific argument or local values, + or actual types. + +The partial alternatives obviously are special versions that can only be used by +OSR. The whole method alternative could also be conceivably used as the +optimized version of the method, but the additional entry points may result some +loss of optimizations. So, in general, the OSR alternatives are likely distinct +from the Tier-1 versions of methods and are used only for active frame +transitions. New calls to methods can be handled via the existing tiering +mechanisms. + +[Note there are some interesting dynamics here that may warrant further +consideration. A method that is seldomly called with a hot loop will eventually +trigger both OSR (from the loop) and Tier1 recompilation (from the calls). We +might consider deferring tiered recompilation for such methods, as the +unoptimized versions can readily transition to OSR alternates in code that +matters for performance.] + +Taken together there are various combinations of these alternatives that make +sense, and various tradeoffs to consider. We explore a few of these below. + +#### 3.3.1 Option 1: Partial Method with Transition Prolog + +In this option, the runtime invokes the jit with a method, IL offset, and the +original method mapping of stack frame state to IL state at that offset. The jit +uses the logical PC (IL offset) to determine the scope of the alternative +fragment. Here the scope is the IL in the method reachable from the patchpoint. + +For the entry point it creates a specialized transition prolog that sets up a +normal frame, and takes the values of the locals from the old stack frame and +copies them to the new stack slots, and pushes& any live evaluation stack +arguments. Arguments passed in registers are restored to the right registers. +Control then transfers to the IL offset of the patchpoint. Any IL in the method +not reachable from the patchpoint is dead code and can be removed (including the +original method entry point). This new partial method is then jitted more or +less normally (modulo the generation of the special prolog). + +It might be possible to express this new prolog in IL or something similar. At +any rate it seems likely the impact on the jit overall can be mostly localized +to the &importer and prolog generation stages and the rest of the jit would +operate more or less as it does today. + +This alternative version can be used any time the original method reaches the +inspiring patchpoint. + +#### 3.3.2 Option 2: Partial Tailored Method with Transition Prolog + +If the runtime also passes the triggering stack frame to the jit, the jit can +incorporate the values in that frame (or information derived from the frame +values) into the alternative method codegen. This creates a tailored alternative +that can only be used at this patchpoint from this specific original method +invocation. The potential benefit here is that the code in the method may be +more optimizable with the additional context, and since OSR alternatives are +likely to be lightly used there may not be much downside to specializing exactly +for this trigger instance. This alternative likely implies synchronous OSR. + +#### 3.3.3 Option 3: Full Method with Multiple Entry Points + +Instead of generating an alternative that can only be used to transition from +one specific patchpoint, the alternative method can offer multiple entry points +to allow transition from some or all of the patchpoints in the original method. + +Such a method would have many prologs and would require additional jit work. It +might also require reworking or suppressing some optimizations where we assume +that the jit method entry dominates all the code in the method � this would no +longer be true. + +#### 3.3.4 Option 4: Method Fragment + +If the alternative method is just a fragment of the entire method, then in +addition to a specialized entry point, the jit will have to create specialized +exit points that either transition back to the unoptimized method, or else use +synchronous OSR to invoke jitting of the method code that comes after the +fragment. + +#### 3.3.5 Prototype + +The prototype generates partial methods with transition prolog. Per 4.1 below, +the OSR method frame incorporates the (live portion of the) original method +frame instead of supplanting it.** + +### 3.4 Transitions + +A transition can happen once an OSR capable method reaches a patchpoint where a +suitable alternative version is ready. Because transitions will likely require +changes in stack frame size it is much simpler to consider transitions only for +methods at the top of the stack. This means that methods that are invoked +recursively may be transitioned by OSR gradually as the stack unwinds. + +Abstractly, the actual transition could work something like the following: the +runtime would copy the top stack frame into temporary storage, then carefully +unwind the current frame. Then the alternative method would be put in place and +invoked, being passed the copy of the original frame as an argument. + +However, the presence of original frame addresses and values derived from those +addresses in the original frame's live state complicates matters (more on this +in [Section 4.1](#Addresses-of-Locals)). So the OSR method needs to ensure that +any "address-exposed" local ends up at the exact same stack location in the OSR +frame as it did in the original method frame. The simplest way to accomplish +this is to just leave the original frame in place, and have the OSR frame +"incorporate" it as part of its frame. + +#### 3.4.1 The Prototype + +The original method conditionally calls to the patchpoint helper at +patchpoints. The helper will return if there is no transition. + +For a transition, the helper will capture context and virtually unwind itself +and the original method from the stack to recover callee-save register values +live into the original method and then restore the callee FP and SP values into +the context (preserving the original method frame); then set the context IP to +the OSR method entry and restore context. OSR method will incorporate the +original method frame as part of its frame. + +## 4 Complications + +### 4.1 Addresses of Locals + +If the live state at the patchpoint includes addresses of locals (or addresses +of arguments, if the OSR transition pushes a new frame), either these addresses +must be updated to properly reflect the new locations or the address-taken +locals must end up in the same relative location in the frame. The jit might +require some hardening to ensure that address of local is always properly +described at patchpoints. + +Detection of address-taken locals (especially in a non-optimizing jit) may +require some attention. We frequently see `ldloca` in IL that is consumed in a +dereference before a stack empty point; such locals are transiently exposed but +their addresses would not be live at our proposed set of patchpoints (note +`ldflda` can cause similar issues if it exposes addresses if local struct +fields). + +Arithmetic done on addresses of locals might not be stable across an OSR +transition (that is, different values could be obtained for a given piece of +code before and after the transition). While in general there is no guarantee +about the values produced by this kind of code it is not unreasonable to expect +that the value would not change over the lifetime of a given method's +execution. It is not clear how much code might depend on this. + +This problem could be partially solved by requiring any address-taken local to +appear at the same stack location in the alternative method frame and by +requiring that the OSR frame supplant the original frame (this is how EnC +works). In that case all address-taken locals would be at the same address. +Ensuring that this is possible likely entails other restrictions like reserving +a maximally large register save area for the original method. + +However, it seems simplest to just preserve the original method frame, or at +least the portion of it that contains the live state, and allow the OSR method +to access the original frame values, either as initial values or as the actual +homes for that state. + +### 4.2 Localloc + +Methods with localloc pose similar challenges to those posed by methods with +address taken locals. Room is made on the original method stack for the localloc +storage, and a native pointer to that storage is part of the live state of the +method. The live state may also include pointers and other values derived from +that address. So, the alternative version must use that same location; a +copy/fixup procedure to allow this storage to be relocated in some manner seems +impractical. + +In addition, localloc makes describing the local frame more complex, as the size +of the frame and the location of particular bits of live state can vary. +Typically, the jit will use multiple frame pointers in a localloc frame to allow +for relative addressing. + +In the most complex case, the original method will have executed one or more +locallocs before hitting the patchpoint, and the OSR variant will then execute +more locallocs. Such cases might require the OSR method to maintain 3 or more +frame pointers. + +### 4.3 Funclets + +When control is executing in a funclet there are effectively two activation +records on the stack that share a single frame: the parent frame and the +funclet frame. The funclet frame is largely a stub frame and most of the frame +state is kept in the parent frame. + +These two frames are not adjacent; they are separated by some number of runtime +frames. This means it is going to be difficult for our system to handle +patchpoints within funclets; even if we could update the code the funclet is +running we would not be able to update the parent frame. + +The proposal here is to disallow patchpoints within funclets so that we do not +attempt OSR transitions when the top of stack frame is a funclet frame. One +hopes that performance critical loops rarely appear in catch or finally clauses. + +EnC has similar restrictions. + +### 4.4 GC + +There is a brief window of time during the transition where there are GC live +values on both the original and alternative frames (and the original frame may +have been copied off-stack). Since the transition is done via a runtime helper, +it seems prudent to forbid GC during this part of the transition, which should +be relatively brief. + +### 4.5 Diagnostics + +Alternative methods will never be called — they are only transitioned to +by active original methods, so likely no special work is needed to make them +compatible with the current profiler guarantees for IL modifications ("new +invocations" of the method invoke the new version). + +We may need to update the mechanisms that the runtime uses to notify profilers +of new native code versions of a method. + +The jit will generate the same debug info mappings as it does today, and so the +debugging experience when debugging an alternative should be similar to the +experience debugging a Tier1 method. Likewise, the code publishing aspects +should be common, so for instance active breakpoints should get applied. + +[Note: I have verified this on simple examples using the VS debugger; a source +breakpoint set in the original method is applied to the OSR method too.] + +We need to decide what happens if the debugger tries to use SetIP on an OSR +method for an IL offset that is not within the range of IL compiled; likely +we'll just have to fail the request. + +Breakpoints set at native code addresses won't transfer to the corresponding +points in OSR methods. We have the same issue with Tiered compilation already. + +OSR (exclusive of EnC) will be disabled for debuggable code. + +Debugging through an OSR transition (say a single-step that triggers OSR) may +require special consideration. This is something that needs further +investigation. + +**Prototype: The OSR methods have somewhat unusual unwind records that may be +confusing the debugger stack trace.** + +### 4.6 Proposed Tier-0 Optimizations + +We have been assuming up until this point that the original method was not +optimized in any way, and so its live state is safely over-approximated by the +values of all locals, arguments, evaluation stack entries. This means that any +value truly live at a reachable patchpoint (capable of influencing future +computation) is included in the live set. The reported live set might well be +larger, of course. The alternative method will likely run liveness and pick from +this set only the values it sees as truly live. + +This means that we can run optimizations in the original method so long as they +do not alter the computation of the over-approximated live set at any +patchpoint. + +The proposed Tier0 optimizations fit into this category, so long as we restrict +patchpoints to stack-empty points: we may prune away unreachable code paths (say +from HW intrinsic checks or provably true or false predicate evaluations &mdash +;patchpoints in pruned sections would be unreachable) and simplify computations. +Optimizing expressions may reduce the truly live set but so long as all stores +to locals and args are kept live the base values needed for any alternate +version of the code will be available. + +### 4.7 Alternative Method Optimizations + +In options where the alternative method has multiple entry points, one must be +wary of early aggressive optimizations done when optimizing the alternative. The +original version of the method may hit a patchpoint while executing code that +can be optimized away by the more aggressive alternative method compiler (e.g. +it may be executing a series of type equality tests in a generic method that the +optimizing jit can evaluate at jit time). But with our simple patchpoint +recognition algorithm the alternate compiler can quickly verify that the +patchpoint IL offset is a viable entry point and ensure that the code at that +offset is not optimized away. If it turns out that the entry point code is +optimizable then we may choose to peel one iteration from the entry point loop +(because with our patchpoint strategy, execution in the alternate method will +immediately hit a loop top once it is out of the prolog) and allow the in-loop +versions to be optimized. + +### 4.8 Prologs and Unwind + +The alternative version of the method will, in all likelihood, need to save and +restore a different set of callee-saves registers than the original version. But +since the original stack frame has already saved some registers, the alternative +version prolog will either need to save a superset of those registers or else +restore the value of some registers in its prolog. So, the alternative version +needs to know which registers the original saved and where in the stack they are +stored. + +If we want to preserve frame offsets for address-taken locals then we may face a +conflict as altering the number of callee save slots may alter frame offsets for +locals. One thought here is that we could perhaps implement a chained unwind +scheme, where there is an initial prolog that emulates the original version +prolog and duplicates its saves, and then a subsequent "shrink wrapped" prolog +& epilog that saves any additional registers in a disjoint area. + +**Prototype: runtime recovers most callee-saves from original method via virtual +unwind; new method just saves what is needed in its own save area. The only +register save live on the old frame is the saved FP.** + +### 4.9 Synchronous Methods + +OSR methods only need add the code to release the synchronous method monitor. +This must still be done in a try-finally to ensure release even on exceptional +exit. + +### 4.10 Profile Enter/Leave Hooks + +OSR methods only need to support the method exit hook. + +## 5 The Prototype + +Based on the above, we developed a prototype implementation of OSR to gain +experience, gather data, and test out assumptions. + +The prototype chose the following options: +* Patchpoints: lexical back edge targets that are stack empty and not in try + regions; live state is all locals and args + specials (thus no liveness needed + at Tier0) +* Trigger: one shared counter per frame. Initial value configurable at runtime. + Patchpoints decrement the counter and conditionally call the runtime helper if + the value is zero or negative. +* Alternatives: partial method tailored to each patchpoint. OSR method + incorporates the original method frame. +* Transition: synchronous—once the patchpoint has been hit often enough a + new alternative is jitted. + +The prototype works for x64 on Windows and Linux, and can pass the basic (pri0) +tests suites with an aggressive transition policy (produce the OSR method and +transition the first time each patchpoint is hit). + +### 5.1 Example Codegen + +Consider the following simple method: +```C# + public static int F(int from, int to) + { + int result = 0; + for (int i = from; i < to; i++) + { + result += i; + } + return result; + } + +``` +Normal (Tier0, x64 windows) codegen for the method is: +```asm +; Tier-0 compilation + +G_M6138_IG01: + 55 push rbp + 4883EC10 sub rsp, 16 + 488D6C2410 lea rbp, [rsp+10H] + 33C0 xor rax, rax + 8945FC mov dword ptr [rbp-04H], eax // result + 8945F8 mov dword ptr [rbp-08H], eax // i + 894D10 mov dword ptr [rbp+10H], ecx // from + 895518 mov dword ptr [rbp+18H], edx // to + +G_M6138_IG02: + 33C0 xor eax, eax + 8945FC mov dword ptr [rbp-04H], eax + 8B4510 mov eax, dword ptr [rbp+10H] + 8945F8 mov dword ptr [rbp-08H], eax + EB11 jmp SHORT G_M6138_IG04 + +G_M6138_IG03: + 8B45FC mov eax, dword ptr [rbp-04H] + 0345F8 add eax, dword ptr [rbp-08H] // result += i + 8945FC mov dword ptr [rbp-04H], eax + 8B45F8 mov eax, dword ptr [rbp-08H] + FFC0 inc eax + 8945F8 mov dword ptr [rbp-08H], eax + +G_M6138_IG04: + 8B45F8 mov eax, dword ptr [rbp-08H] + 3B4518 cmp eax, dword ptr [rbp+18H] + 7CE7 jl SHORT G_M6138_IG03 // i < to ? + 8B45FC mov eax, dword ptr [rbp-04H] + +G_M6138_IG05: + 488D6500 lea rsp, [rbp] + 5D pop rbp + C3 ret +``` +with OSR enabled (and patchpoint counter initial value = 2), this becomes: +```asm +; Tier-0 compilation + Patchpoints + +G_M6138_IG01: + 55 push rbp + 4883EC30 sub rsp, 48 + 488D6C2430 lea rbp, [rsp+30H] + 33C0 xor rax, rax + 8945FC mov dword ptr [rbp-04H], eax // result + 8945F8 mov dword ptr [rbp-08H], eax // i + 894D10 mov dword ptr [rbp+10H], ecx // from + 895518 mov dword ptr [rbp+18H], edx // to + +G_M6138_IG02: + 33C9 xor ecx, ecx + 894DFC mov dword ptr [rbp-04H], ecx // result = 0 + 8B4D10 mov ecx, dword ptr [rbp+10H] + 894DF8 mov dword ptr [rbp-08H], ecx // i = from + C745F002000000 mov dword ptr [rbp-10H], 2 // patchpointCounter = 2 + EB2D jmp SHORT G_M6138_IG06 + +G_M6138_IG03: + 8B4DF0 mov ecx, dword ptr [rbp-10H] // patchpointCounter-- + FFC9 dec ecx + 894DF0 mov dword ptr [rbp-10H], ecx + 837DF000 cmp dword ptr [rbp-10H], 0 // ... > 0 ? + 7F0E jg SHORT G_M6138_IG05 + +G_M6138_IG04: ;; bbWeight=0.01 + 488D4DF0 lea rcx, bword ptr [rbp-10H] // &patchpointCounter + BA06000000 mov edx, 6 // ilOffset + E808CA465F call CORINFO_HELP_PATCHPOINT + +G_M6138_IG05: + 8B45FC mov eax, dword ptr [rbp-04H] + 0345F8 add eax, dword ptr [rbp-08H] + 8945FC mov dword ptr [rbp-04H], eax + 8B45F8 mov eax, dword ptr [rbp-08H] + FFC0 inc eax + 8945F8 mov dword ptr [rbp-08H], eax + +G_M6138_IG06: + 8B4DF8 mov ecx, dword ptr [rbp-08H] + 3B4D18 cmp ecx, dword ptr [rbp+18H] + 7CCB jl SHORT G_M6138_IG03 + 8B45FC mov eax, dword ptr [rbp-04H] + +G_M6138_IG07: + 488D6500 lea rsp, [rbp] + 5D pop rbp + C3 ret +``` +Because Tier0 is unoptimized code, the patchpoint sequence is currently +unoptimized. This leads to a moderate amount of code bloat in methods with +patchpoints. The overall code size impact of patchpoints (as measured by +`jit-diff`) is around 2%, but this is a bit misleading, as many Tier0 methods +won't require patchpoints. This is something that can be improved. + +The OSR method for this patchpoint is: +```asm +; Tier-1 compilation +; OSR variant for entry point 0x6 + +G_M6138_IG01: + 8B542450 mov edx, dword ptr [rsp+50H] // to + 8B4C2434 mov ecx, dword ptr [rsp+34H] // result + 8B442430 mov eax, dword ptr [rsp+30H] // i + +G_M6138_IG02: ;; bbWeight=8 + 03C8 add ecx, eax + FFC0 inc eax + 3BC2 cmp eax, edx + 7CF8 jl SHORT G_M6138_IG02 + +G_M6138_IG03: + 8BC1 mov eax, ecx + +G_M6138_IG04: + 4883C438 add rsp, 56 + 5D pop rbp + C3 ret +``` +Here the live state is `result`, `i`, and `to`. These are kept in registers and +initialized in the prolog to the values they had in the original frame. The jit +request for the OSR method includes 'OSR_INFO" metadata describing the original +method frame, so the jit can compute the correct addresses for original frame +slots in the OSR method. + +Because the OSR method is entered with the original method frame still active, +the OSR method has asymmetric prolog and epilog sequences. This is reflected in +the unwind data for the OSR method by recording a "phantom prolog" to account +for actions taken by the original method. These are at code offset 0 so happen +"instantaneously" when the method is entered. +``` + UnwindCodes: + CodeOffset: 0x00 UnwindOp: UWOP_ALLOC_SMALL (2) OpInfo: 6 * 8 + 8 = 56 = 0x38 + CodeOffset: 0x00 UnwindOp: UWOP_PUSH_NONVOL (0) OpInfo: rbp (5) +``` +By way of comparison, here is the full Tier-1 version of the method. +```asm +G_M6138_IG01: + +G_M6138_IG02: + 33C0 xor eax, eax + 3BCA cmp ecx, edx + 7D08 jge SHORT G_M6138_IG04 + +G_M6138_IG03: ;; bbWeight=4 + 03C1 add eax, ecx + FFC1 inc ecx + 3BCA cmp ecx, edx + 7CF8 jl SHORT G_M6138_IG03 + +G_M6138_IG04: + C3 ret +``` +Note the inner loop codegen is very similar to the OSR variant. This is typical. +It is often possible to diff the Tier1 and OSR codegen and see that the latter +is just a partial version of the former, with different register usage and +different stack offsets. + +### 5.2 More Complex Examples + +If the OSR method needs to save and restore registers, then the epilog will have +two stack pointer adjustments: the first to reach the register save area on the OSR +frame, the second to reach the saved RBP and return address on the original frame. + +For example: +```asm + 4883C440 add rsp, 64 + 5B pop rbx + 5E pop rsi + 5F pop rdi + 4883C448 add rsp, 72 + 5D pop rbp + C3 ret +``` +with unwind info: +``` + UnwindCodes: + CodeOffset: 0x07 UnwindOp: UWOP_ALLOC_SMALL (2) OpInfo: 7 * 8 + 8 = 64 = 0x40 + CodeOffset: 0x03 UnwindOp: UWOP_PUSH_NONVOL (0) OpInfo: rbx (3) + CodeOffset: 0x02 UnwindOp: UWOP_PUSH_NONVOL (0) OpInfo: rsi (6) + CodeOffset: 0x01 UnwindOp: UWOP_PUSH_NONVOL (0) OpInfo: rdi (7) + CodeOffset: 0x00 UnwindOp: UWOP_ALLOC_SMALL (2) OpInfo: 8 * 8 + 8 = 72 = 0x48 + CodeOffset: 0x00 UnwindOp: UWOP_PUSH_NONVOL (0) OpInfo: rbp (5) +``` + +If the OSR method needs to save RBP, we may see two RBP restores in the epilog; this +does not appear to cause problems during execution, as the "last one wins" when unwinding. + +However, the debugger (at least windbg) may end up being confused; any tool +simply following the RBP chain will see the original frame is still "linked" +into the active stack. + +### 5.3 CORINFO_OSR_INFO and CORINFO_PATCHPOINT_INFO + +As noted above, when the jit is invoked to create the OSR method, it is passed +some extra data: +* The IL offset of the OSR entry point +* A description of the original method frame + +These are bundled by the runtime into a new runtime data structure: +```C++ +struct CORINFO_OSR_INFO +{ + unsigned ilOffset; // IL offset of the patchpoint + CORINFO_PATCHPOINT_INFO patchpointInfo; // original frame info +}; +``` +which is part of the method info: +```C++ +struct CORINFO_METHOD_INFO +{ + CORINFO_METHOD_HANDLE ftn; + CORINFO_MODULE_HANDLE scope; + BYTE * ILCode; + unsigned ILCodeSize; + unsigned maxStack; + unsigned EHcount; + CorInfoOptions options; + CorInfoRegionKind regionKind; + CORINFO_SIG_INFO args; + CORINFO_SIG_INFO locals; + CORINFO_OSR_INFO osrInfo; +}; +``` +The `patchpointInfo` is produced by the jit when jitting the Tier0 method. It is +allocated by the runtime similarly to other codegen metadata like GC info and +unwind info and is likewise associated with the original method. When the +runtime helper decides to kick off an OSR jit, it bundles the IL offset (passed +to the helper as an argument and the patchpointInfo (retrieved from the method) +and attaches those to the method info. + +Since the `patchpointInfo` is produced and consumed by the jit its format is +currently opaque to the runtime. It has the following general layout: +```C++ +struct CORINFO_PATCHPOINT_INFO +{ + unsigned m_patchpointInfoSize; + unsigned m_ilSize; + unsigned m_numberOfLocals; + int m_fpToSpDelta; + int m_genericContextArgOffset; + int m_keptAliveThisOffset; + int m_securityCookieOffset; + int m_offsetAndExposureData[]; +}; +``` +The key values are the `fpToSpDelta` which describes the extent of the original +frame, and the `offsetAndExposureData` which describe the offset of each local +on the original frame. + +### 5.4 Performance Impact + +The prototype is mainly intended to show that OSR can be used to improve startup +without compromising steady-state performance: with OSR, we can safely use the +quick jit for almost all methods. + +We are currently evaluating the performance impact of OSR on some realistic +scenarios. + +Initial data shows a general improvement in startup time, in particular for +applications where startup was impacted by disabling quick-jitting of methods +with loops (see dotnet/coreclr#24252). + +## 6 Edit and Continue + +As mentioned in the introduction, OSR is similar to Edit and Continue (EnC). EnC +transitions from an original unoptimized version of a method to a new +unoptimized version with slightly different IL. The CLR already supports EnC on +some platforms, and we briefly review the current implementation here. Our main +interest here is in edits to method IL for an active method, so we focus on that +aspect. + +### 6.1 Current EnC Support + +Method modification in EnC works roughly as follows. A process being debugged is +stopped at a breakpoint. The user makes a source edit and hits apply. The source +edit is vetted by the language compiler as suitable for EnC and metadata edits +are sent to the runtime via the debugger. For method modifications these edits +create a new version of method IL. Any subsequent invocations of that method +will use that new IL. + +To update currently active versions, the debugger also adds special breakpoints +to the plausible patchpoints of the original method's native code. Execution +then resumes. When one of those special breakpoints is hit, an original +method's active frame is at the top of the stack, and the frame can be +transitioned over to the new version. The remapping is done by using the debug +information generated by the jit for both the old and new versions of the +method. As part of this the runtime verifies that locals remain at the same +addresses in the old and new stack frames (thus avoiding the complication noted +earlier in [Section 4.1](#Addresses-of-Locals)). + +The jit is notified if a method is going to potentially be eligible for EnC and +takes some precautions to ensure the EnC transition can be handled by the +runtime: for instance, the jit will always save the same set of registers and +use a frame pointer. + +So, for EnC, we see that: + +- The viable patchpoints are determined by the debugger (via + EnCSequencePointHelper). These are restricted to be stack empty points (since + debug info will not describe contents of the evaluation stack) that are not in + filter or handlers. They are a broader set than we envision needing for OSR. +- The necessary mapping information (stack frame layout, native to IL mapping, + and native offsets of stack empty points) is present in the debug stream + generated by the jit for the original method. +- The trigger is a set of special breakpoints placed into the original method + native code by the debugger when an edit is applied to the method. +- When an EnC breakpoint is hit, the debugger can chose whether or not to + initiate a transition. +- If the debugger initiates a transition, it is done synchronously: the new + version of the method is jitted if necessary and the currently active frame is + transitioned over to the new version, via ResumeInUpdatedFunction. Of interest + here are the lower-level methods used here to update the frame: + FixContextAndResume and FixContextForEnC. +- The alternative version is a full method and can be used to transition from + any patchpoint in the original method. +- The jit modifies its codegen somewhat to facilitate the transition. It does + not, however, explicitly model the alternate entry points. + +## 7 Deoptimization + +Up until this point we have been assuming the original method was not optimized +or was optimized in a manner that did not alter its reported live state. + +More general optimizations break this property and so additional bookkeeping and +some restrictions on optimizations may be necessary to allow OSR transitions +from optimized code. We touch on this briefly below. + +Optimizations can either increase or decrease live state. + +For instance, unused computations can be removed, and unused local updates +("dead stores") can be skipped. Registers holding no longer live locals can be +reused for other values (as can stack slots, though the current jit does not do +this). + +Other optimizations can increase the live state. The classic example is inlining +— a call to a method is expanded inline, and so at patchpoints within the +inline body, there are now arguments and locals to the original method, plus +arguments and locals to the inline method. If we wish to make an OSR transition +from such a patchpoint to say unoptimized code, we need to effectively undo the +inlining, creating two frames (or more generally N frames) in place of the +original frame, and two alternate methods (or N alternate methods). + +The general solution is to first ensure that the live state never decreases. The +patchpoint locations are determined early, and any values truly live at a +patchpoint at that initial stage of compilation are forced to remain live at +that patchpoint always. So, some dead stores are inhibited, and some forms of +code motion are inhibited (e.g. one cannot sink a store to a local out of a +loop, as the patchpoint at loop top would not observe the updated value). + +With all the "naive" state guaranteed live at a patchpoint, and any additions to +live state via inlining carefully tracked, one can transition from optimized +code via OSR. + +Given the need to preserve address artifacts, this transition must be done +gradually -- first creating a frame for the innermost inlined method that +extends the original frame, then, when this innermost method returns, creating a +frame for the next innermost inlined method, and so on, until finally the +root method frame returns and can clean up the optimized method frame as well. + +Each of these (presumably, unoptimized) deopt target methods will need to be +custom-crafted to access the optimized method frame. + +This same consideration makes it challenging to implement deopt fallbacks to +an interpreter; the interpreter will likewise need to keep some of its state +in the original method frame. + +We currently don't have any need to transfer control out of jitted optimized +code (Tier1), though one could potentially imagine supporting this to better +debug optimized code. The really strong motivations for deoptimization may come +about when the system is optimizing based on "currently true" information that +has now become invalid. + +## 8 References + +1. U. Holzle, C. Chambers and D. Ungar, "Debugging Optimized + Code with Dynamic Deoptimization," in _ACM PLDI_, 1992. +2. M. Paleczny, C. Vick and C. Click, "The Java Hotspot(tm) + Server Compiler," in _USENIX Java Virtual Machine Research and + Technology Symposium_, 2001. +3. S. Fink and F. Qian, "Design, Implementation and + Evaluation of Adaptive Recompilation with On-Stack Replacement," in _In + International Symposium on Code Generation and Optimization (CGO)_, 2003. diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 474788010d779a..2734a1ce6b7e4e 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -196,6 +196,9 @@ endif(FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION) add_definitions(-DFEATURE_SVR_GC) add_definitions(-DFEATURE_SYMDIFF) add_compile_definitions($<$>>:FEATURE_TIERED_COMPILATION>) +if (CLR_CMAKE_TARGET_ARCH_AMD64) + add_compile_definitions($<$>>:FEATURE_ON_STACK_REPLACEMENT>) +endif (CLR_CMAKE_TARGET_ARCH_AMD64) if (CLR_CMAKE_TARGET_WIN32) add_definitions(-DFEATURE_TYPEEQUIVALENCE) endif(CLR_CMAKE_TARGET_WIN32) diff --git a/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp b/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp index 2acd2b636de3cf..7c3f2cc44a6a72 100644 --- a/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp +++ b/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp @@ -40,7 +40,17 @@ void DumpMap(int index, MethodContext* mc) DumpPrimToConsoleBare(mc, cmi.args.retType, (DWORDLONG)cmi.args.retTypeClass); printf(" %s(", methodName); DumpSigToConsoleBare(mc, &cmi.args); - printf(")\"\n"); + printf(")\""); + + // Todo: dump tier0/tier1/fullopt request + + // Dump if this is an OSR request + if (cmi.osrInfo.patchpointInfo != nullptr) + { + printf(",Osr-%u", cmi.osrInfo.ilOffset); + } + + printf("\n"); } int verbDumpMap::DoWork(const char* nameOfInput) diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp index b8c006040c4f27..9056dec217df1f 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp @@ -326,6 +326,38 @@ bool CompileResult::repSetVars(CORINFO_METHOD_HANDLE* ftn, ULONG32* cVars, ICorD return true; } +// Note - Ownership of vars is transfered with this call. In replay icorjitinfo we should free it. +void CompileResult::recSetPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +{ + if (SetPatchpointInfo == nullptr) + SetPatchpointInfo = new LightWeightMap(); + + Agnostic_SetPatchpointInfo value; + value.index = (DWORD)SetPatchpointInfo->AddBuffer((const unsigned char*) patchpointInfo, patchpointInfo->PatchpointInfoSize()); + SetPatchpointInfo->Add(0, value); +} +void CompileResult::dmpSetPatchpointInfo(DWORD key, const Agnostic_SetPatchpointInfo& value) +{ + CORINFO_PATCHPOINT_INFO* patchpointInfo = (CORINFO_PATCHPOINT_INFO*)SetPatchpointInfo->GetBuffer(value.index); + printf("SetPatchpointInfo key %u, index %u{", key, value.index); + // todo -- dump contents + printf("}"); + SetPatchpointInfo->Unlock(); +} +bool CompileResult::repSetPatchpointInfo(CORINFO_PATCHPOINT_INFO** patchpointInfo) +{ + if ((SetPatchpointInfo == nullptr) || (SetPatchpointInfo->GetCount() == 0)) + { + *patchpointInfo = nullptr; + return false; + } + + Agnostic_SetPatchpointInfo value; + value = SetPatchpointInfo->Get(0); + *patchpointInfo = (CORINFO_PATCHPOINT_INFO*)SetPatchpointInfo->GetBuffer(value.index); + return true; +} + void CompileResult::recAllocGCInfo(size_t size, void* retval) { allocGCInfoDets.size = size; diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.h b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.h index 9fa9e454462b65..9be342074a0dad 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.h +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.h @@ -112,6 +112,10 @@ class CompileResult DWORD cVars; DWORD vars_offset; }; + struct Agnostic_SetPatchpointInfo + { + DWORD index; + }; struct Agnostic_CORINFO_EH_CLAUSE2 { DWORD Flags; @@ -200,6 +204,10 @@ class CompileResult void dmpSetVars(DWORD key, const Agnostic_SetVars& value); bool repSetVars(CORINFO_METHOD_HANDLE* ftn, ULONG32* cVars, ICorDebugInfo::NativeVarInfo** vars); + void recSetPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo); + void dmpSetPatchpointInfo(DWORD key, const Agnostic_SetPatchpointInfo& value); + bool repSetPatchpointInfo(CORINFO_PATCHPOINT_INFO** patchpointInfo); + void recAllocGCInfo(size_t size, void* retval); void recAllocGCInfoCapture(); void dmpAllocGCInfo(DWORD key, const Agnostic_AllocGCInfo& value); diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/crlwmlist.h b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/crlwmlist.h index 72fa54043a5484..b42077de7abe4f 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/crlwmlist.h +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/crlwmlist.h @@ -41,6 +41,7 @@ LWM(SetEHcount, DWORD, DWORD) LWM(SetEHinfo, DWORD, CompileResult::Agnostic_CORINFO_EH_CLAUSE2) LWM(SetMethodAttribs, DWORDLONG, DWORD) LWM(SetVars, DWORD, CompileResult::Agnostic_SetVars) +LWM(SetPatchpointInfo, DWORD, CompileResult::Agnostic_SetPatchpointInfo) #undef LWM #undef DENSELWM diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h index 349cec9752999d..98036d5bf16ed7 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h @@ -561,6 +561,11 @@ void setVars(CORINFO_METHOD_HANDLE ftn, // [IN] method of interest // jit allocated with allocateArray, EE frees ); +// Provide patchpoint info for the method currently being jitted. +void setPatchpointInfo( + CORINFO_PATCHPOINT_INFO* patchpointInfo + ); + /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index 9ceb35a0af1332..58982be107235e 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -430,6 +430,8 @@ bool MethodContext::Equal(MethodContext* other) return false; if (otherFlags != ourFlags) return false; + if (otherInfo.osrInfo.ilOffset != ourInfo.osrInfo.ilOffset) + return false; // Now compare the other maps to "estimate" equality. @@ -653,6 +655,20 @@ void MethodContext::recCompileMethod(CORINFO_METHOD_INFO* info, unsigned flags) value.info.locals.token = (DWORD)info->locals.token; value.flags = (DWORD)flags; + value.info.osrInfo.ilOffset = (DWORD)info->osrInfo.ilOffset; + + DWORD* patchpointInfo = (DWORD*)info->osrInfo.patchpointInfo; + + if (patchpointInfo == nullptr) + { + value.info.osrInfo.patchpointInfo_Index = -1; + } + else + { + DWORD patchpointInfoSize = patchpointInfo[0]; + value.info.osrInfo.patchpointInfo_Index = CompileMethod->AddBuffer((unsigned char*)patchpointInfo, patchpointInfoSize); + } + CompileMethod->Add(0, value); DEBUG_REC(dmpCompileMethod(0, value)); } @@ -677,6 +693,8 @@ void MethodContext::dmpCompileMethod(DWORD key, const Agnostic_CompileMethod& va value.info.locals.sigInst_classInst_Index, value.info.locals.sigInst_methInstCount, value.info.locals.sigInst_methInst_Index, value.info.locals.args, value.info.locals.cbSig, value.info.locals.pSig_Index, value.info.locals.scope, value.info.locals.token, value.flags); + + printf(" osr{iloffs-%u ppInfo_index-%d}", value.info.osrInfo.ilOffset, value.info.osrInfo.patchpointInfo_Index); } void MethodContext::repCompileMethod(CORINFO_METHOD_INFO* info, unsigned* flags) { @@ -727,6 +745,10 @@ void MethodContext::repCompileMethod(CORINFO_METHOD_INFO* info, unsigned* flags) info->locals.pSig = (PCCOR_SIGNATURE)CompileMethod->GetBuffer(value.info.locals.pSig_Index); info->locals.scope = (CORINFO_MODULE_HANDLE)value.info.locals.scope; info->locals.token = (mdToken)value.info.locals.token; + + info->osrInfo.ilOffset = (unsigned)value.info.osrInfo.ilOffset; + info->osrInfo.patchpointInfo = (CORINFO_PATCHPOINT_INFO*)CompileMethod->GetBuffer(value.info.osrInfo.patchpointInfo_Index); + *flags = (unsigned)value.flags; DEBUG_REP(dmpCompileMethod(0, value)); } @@ -2777,6 +2799,20 @@ void MethodContext::recGetMethodInfo(CORINFO_METHOD_HANDLE ftn, (DWORD)GetMethodInfo->AddBuffer((unsigned char*)info->locals.pSig, info->locals.cbSig); value.info.locals.scope = (DWORDLONG)info->locals.scope; value.info.locals.token = (DWORD)info->locals.token; + + value.info.osrInfo.ilOffset = info->osrInfo.ilOffset; + + DWORD* patchpointInfo = (DWORD*)info->osrInfo.patchpointInfo; + + if (patchpointInfo == nullptr) + { + value.info.osrInfo.patchpointInfo_Index = -1; + } + else + { + DWORD patchpointInfoSize = patchpointInfo[0]; + value.info.osrInfo.patchpointInfo_Index = GetMethodInfo->AddBuffer((unsigned char*)patchpointInfo, patchpointInfoSize); + } } value.result = result; value.exceptionCode = (DWORD)exceptionCode; @@ -2806,6 +2842,8 @@ void MethodContext::dmpGetMethodInfo(DWORDLONG key, const Agnostic_GetMethodInfo value.info.locals.sigInst_classInst_Index, value.info.locals.sigInst_methInstCount, value.info.locals.sigInst_methInst_Index, value.info.locals.args, value.info.locals.cbSig, value.info.locals.pSig_Index, value.info.locals.scope, value.info.locals.token, value.exceptionCode); + + printf(" osr{iloffs-%u ppInfo_index-%d}", value.info.osrInfo.ilOffset, value.info.osrInfo.patchpointInfo_Index); } bool MethodContext::repGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO* info, DWORD* exceptionCode) { @@ -2860,6 +2898,9 @@ bool MethodContext::repGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_I info->locals.pSig = (PCCOR_SIGNATURE)GetMethodInfo->GetBuffer(value.info.locals.pSig_Index); info->locals.scope = (CORINFO_MODULE_HANDLE)value.info.locals.scope; info->locals.token = (mdToken)value.info.locals.token; + + info->osrInfo.ilOffset = (unsigned)value.info.osrInfo.ilOffset; + info->osrInfo.patchpointInfo = (CORINFO_PATCHPOINT_INFO*)GetMethodInfo->GetBuffer(value.info.osrInfo.patchpointInfo_Index); } bool result = value.result; *exceptionCode = (DWORD)value.exceptionCode; @@ -6192,6 +6233,14 @@ int MethodContext::dumpMethodIdentityInfoToBuffer(char* buff, int len, bool igno buff += t; len -= t; + // Add OSR info, if present. + if (info.osrInfo.patchpointInfo != nullptr) + { + t = sprintf_s(buff, len, "OSR IL Offset: %u", info.osrInfo.ilOffset); + buff += t; + len -= t; + } + return (int)(buff - obuff); } int MethodContext::dumpMethodMD5HashToBuffer(char* buff, int len, bool ignoreMethodName /* = false */) diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.h index d0de0fd177331b..b666e2857c33cb 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -42,6 +42,11 @@ class MethodContext DWORDLONG scope; DWORD token; }; + struct Agnostic_CORINFO_OSR_INFO + { + DWORD ilOffset; + DWORD patchpointInfo_Index; + }; struct Agnostic_CORINFO_METHOD_INFO { DWORDLONG ftn; @@ -54,6 +59,7 @@ class MethodContext DWORD regionKind; Agnostic_CORINFO_SIG_INFO args; Agnostic_CORINFO_SIG_INFO locals; + Agnostic_CORINFO_OSR_INFO osrInfo; }; struct Agnostic_CompileMethod { @@ -1309,7 +1315,7 @@ class MethodContext }; // ********************* Please keep this up-to-date to ease adding more *************** -// Highest packet number: 175 +// Highest packet number: 176 // ************************************************************************************* enum mcPackets { @@ -1487,6 +1493,7 @@ enum mcPackets PacketCR_SetEHinfo = 128, PacketCR_SetMethodAttribs = 129, PacketCR_SetVars = 130, + PacketCR_SetPatchpointInfo = 176, // added 8/5/2019 PacketCR_RecordCallSite = 146, // Added 10/28/2013 - to support indirect calls }; diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 2768739712afbd..2dff5c64cbf926 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1165,6 +1165,14 @@ void interceptor_ICJI::setVars(CORINFO_METHOD_HANDLE ftn, // [IN] meth original_ICorJitInfo->setVars(ftn, cVars, vars); } +// Provide patchpoint info for the method currently being jitted. +void interceptor_ICJI::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +{ + mc->cr->AddCall("setPatchpointInfo"); + mc->cr->recSetPatchpointInfo(patchpointInfo); // Since the EE frees, we've gotta record before its sent to the EE. + original_ICorJitInfo->setPatchpointInfo(patchpointInfo); +} + /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. // For eg, use this to allocated memory for reporting debug info, diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp index 93d71b71bd6610..be350234081de3 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -931,6 +931,13 @@ void interceptor_ICJI::setVars(CORINFO_METHOD_HANDLE ftn, // [IN] meth original_ICorJitInfo->setVars(ftn, cVars, vars); } +// Provide patchpoint info for the method currently being jitted. +void interceptor_ICJI::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +{ + mcs->AddCall("setPatchpointInfo"); + original_ICorJitInfo->setPatchpointInfo(patchpointInfo); +} + /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp index 5fee245bce605a..da8af1193dc237 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -830,6 +830,12 @@ void interceptor_ICJI::setVars(CORINFO_METHOD_HANDLE ftn, // [IN] meth original_ICorJitInfo->setVars(ftn, cVars, vars); } +// Provide patchpoint info for the method currently being jitted. +void interceptor_ICJI::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +{ + original_ICorJitInfo->setPatchpointInfo(patchpointInfo); +} + /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp index 084bf2703ffc92..f90293a78fa419 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp @@ -1003,6 +1003,14 @@ void MyICJI::setVars(CORINFO_METHOD_HANDLE ftn, // [IN] method of inte freeArray(vars); // See note in recSetVars... we own destroying this array } +// Provide patchpoint info for the method currently being jitted. +void MyICJI::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +{ + jitInstance->mc->cr->AddCall("setPatchpointInfo"); + jitInstance->mc->cr->recSetPatchpointInfo(patchpointInfo); + freeArray(patchpointInfo); // See note in recSetPatchpointInfo... we own destroying this array +} + /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. diff --git a/src/coreclr/src/inc/CrstTypes.def b/src/coreclr/src/inc/CrstTypes.def index 42ba9fe5922596..6cfb711448922c 100644 --- a/src/coreclr/src/inc/CrstTypes.def +++ b/src/coreclr/src/inc/CrstTypes.def @@ -351,6 +351,9 @@ End Crst JitGenericHandleCache End +Crst JitPatchpoint +End + Crst JitPerf Unordered End diff --git a/src/coreclr/src/inc/clrconfigvalues.h b/src/coreclr/src/inc/clrconfigvalues.h index 1527ffe87008ad..12b381b65bd0cd 100644 --- a/src/coreclr/src/inc/clrconfigvalues.h +++ b/src/coreclr/src/inc/clrconfigvalues.h @@ -639,6 +639,17 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DelaySingleProcMultiplier, W("TC_DelaySingl RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_CallCounting, W("TC_CallCounting"), 1, "Enabled by default (only activates when TieredCompilation is also enabled). If disabled immediately backpatches prestub, and likely prevents any promotion to higher tiers") #endif +/// +/// On-Stack Replacement +/// +#ifdef FEATURE_ON_STACK_REPLACEMENT +RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_CounterBump, W("OSR_CounterBump"), 1000, "Counter reload value when a patchpoint is hit") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_HitLimit, W("OSR_HitLimit"), 10, "Number of times a patchpoint must call back to trigger an OSR transition") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_Verbose, W("OSR_Verbose"), 0, "Nonzero enables OSR diganostic output") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_LowId, W("OSR_LowId"), -1, "Low end of enabled patchpoint range (inclusive)"); +RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_HighId, W("OSR_HighId"), 10000000, "High end of enabled patchpoint range (inclusive)"); +#endif + /// /// Entry point slot backpatch /// diff --git a/src/coreclr/src/inc/corinfo.h b/src/coreclr/src/inc/corinfo.h index 3d209089237df6..a09f4cbb3a14c2 100644 --- a/src/coreclr/src/inc/corinfo.h +++ b/src/coreclr/src/inc/corinfo.h @@ -217,11 +217,11 @@ TODO: Talk about initializing strutures before use #endif #endif -SELECTANY const GUID JITEEVersionIdentifier = { /* b2e40020-6125-41e4-a0fc-821127ec192a */ - 0xb2e40020, - 0x6125, - 0x41e4, - {0xa0, 0xfc, 0x82, 0x11, 0x27, 0xec, 0x19, 0x2a} +SELECTANY const GUID JITEEVersionIdentifier = { /* c231d2d7-4764-4097-a9ef-5961041540df */ + 0xc231d2d7, + 0x4764, + 0x4097, + {0xa9, 0xef, 0x59, 0x61, 0x04, 0x15, 0x40, 0xdf} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -338,6 +338,139 @@ struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR } }; +// CORINFO_PATCHPOINT_INFO +// +// Communicates layout of the original method stack to the jit +// for an On-Stack Replacement (OSR) jit request. +struct CORINFO_PATCHPOINT_INFO +{ + // Determine how much storage is needed to hold this info + static unsigned ComputeSize(unsigned localCount) + { + unsigned baseSize = sizeof(CORINFO_PATCHPOINT_INFO); + unsigned variableSize = localCount * sizeof(int); + unsigned totalSize = baseSize + variableSize; + return totalSize; + } + + // Initialize + void Initialize(unsigned localCount, unsigned ilSize, int fpToSpDelta) + { + m_patchpointInfoSize = ComputeSize(localCount); + m_ilSize = ilSize; + m_fpToSpDelta = fpToSpDelta; + m_numberOfLocals = localCount; + m_genericContextArgOffset = -1; + m_keptAliveThisOffset = -1; + m_securityCookieOffset = -1; + } + + // Total size of this patchpoint info record, in bytes + unsigned PatchpointInfoSize() const + { + return m_patchpointInfoSize; + } + + // IL Size of the original method + unsigned ILSize() const + { + return m_ilSize; + } + + // FP to SP delta of the original method + int FpToSpDelta() const + { + return m_fpToSpDelta; + } + + // Number of locals in the original method (including special locals) + unsigned NumberOfLocals() const + { + return m_numberOfLocals; + } + + // Original method caller SP offset for generic context arg + int GenericContextArgOffset() const + { + return m_genericContextArgOffset; + } + + void SetGenericContextArgOffset(int offset) + { + m_genericContextArgOffset = offset; + } + + // Original method FP relative offset for kept-alive this + int KeptAliveThisOffset() const + { + return m_keptAliveThisOffset; + } + + bool HasKeptAliveThis() const + { + return m_keptAliveThisOffset != -1; + } + + void SetKeptAliveThisOffset(int offset) + { + m_keptAliveThisOffset = offset; + } + + // Original method FP relative offset for security cookie + int SecurityCookieOffset() const + { + return m_securityCookieOffset; + } + + bool HasSecurityCookie() const + { + return m_securityCookieOffset != -1; + } + + void SetSecurityCookieOffset(int offset) + { + m_securityCookieOffset = offset; + } + + // True if this local was address exposed in the original method + bool IsExposed(unsigned localNum) const + { + return ((m_offsetAndExposureData[localNum] & EXPOSURE_MASK) != 0); + } + + void SetIsExposed(unsigned localNum) + { + m_offsetAndExposureData[localNum] |= EXPOSURE_MASK; + } + + // FP relative offset of this local in the original method + int Offset(unsigned localNum) const + { + return (m_offsetAndExposureData[localNum] & ~EXPOSURE_MASK); + } + + void SetOffset(unsigned localNum, int offset) + { + m_offsetAndExposureData[localNum] = offset; + } + +private: + enum + { + EXPOSURE_MASK = 0x1 + }; + + unsigned m_patchpointInfoSize; + unsigned m_ilSize; + unsigned m_numberOfLocals; + int m_fpToSpDelta; + int m_genericContextArgOffset; + int m_keptAliveThisOffset; + int m_securityCookieOffset; + int m_offsetAndExposureData[]; +}; + + // CorInfoHelpFunc defines the set of helpers (accessed via the ICorDynamicInfo::getHelperFtn()) // These helpers can be called by native code which executes in the runtime. // Compilers can emit calls to these helpers. @@ -631,6 +764,8 @@ enum CorInfoHelpFunc CORINFO_HELP_STACK_PROBE, // Probes each page of the allocated stack frame + CORINFO_HELP_PATCHPOINT, // Notify runtime that code has reached a patchpoint + CORINFO_HELP_COUNT, }; @@ -1164,6 +1299,12 @@ struct CORINFO_SIG_INFO bool hasTypeArg() { return ((callConv & CORINFO_CALLCONV_PARAMTYPE) != 0); } }; +struct CORINFO_OSR_INFO +{ + unsigned ilOffset; // IL offset of the patchpoint + CORINFO_PATCHPOINT_INFO* patchpointInfo; // patchpoint info blob for the original method +}; + struct CORINFO_METHOD_INFO { CORINFO_METHOD_HANDLE ftn; @@ -1176,6 +1317,7 @@ struct CORINFO_METHOD_INFO CorInfoRegionKind regionKind; CORINFO_SIG_INFO args; CORINFO_SIG_INFO locals; + CORINFO_OSR_INFO osrInfo; }; //---------------------------------------------------------------------------- @@ -2658,6 +2800,11 @@ class ICorStaticInfo /*-------------------------- Misc ---------------------------------------*/ + // Provide patchpoint info for the method currently being jitted. + virtual void setPatchpointInfo( + CORINFO_PATCHPOINT_INFO* patchpointInfo + ) = 0; + // Used to allocate memory that needs to handed to the EE. // For eg, use this to allocated memory for reporting debug info, // which will be handed to the EE by setVars() and setBoundaries() diff --git a/src/coreclr/src/inc/corjitflags.h b/src/coreclr/src/inc/corjitflags.h index 1982e0c7b93f64..18f24b52acdfbd 100644 --- a/src/coreclr/src/inc/corjitflags.h +++ b/src/coreclr/src/inc/corjitflags.h @@ -52,7 +52,7 @@ class CORJIT_FLAGS #endif // !defined(TARGET_X86) - CORJIT_FLAG_UNUSED6 = 13, + CORJIT_FLAG_OSR = 13, // Generate alternate method for On Stack Replacement #if defined(TARGET_X86) || defined(TARGET_AMD64) diff --git a/src/coreclr/src/inc/crsttypes.h b/src/coreclr/src/inc/crsttypes.h index f497a540f66f9b..109e3632a0d86c 100644 --- a/src/coreclr/src/inc/crsttypes.h +++ b/src/coreclr/src/inc/crsttypes.h @@ -87,88 +87,89 @@ enum CrstType CrstJit = 68, CrstJitGenericHandleCache = 69, CrstJitInlineTrackingMap = 70, - CrstJitPerf = 71, - CrstJumpStubCache = 72, - CrstLeafLock = 73, - CrstListLock = 74, - CrstLoaderAllocator = 75, - CrstLoaderAllocatorReferences = 76, - CrstLoaderHeap = 77, - CrstMda = 78, - CrstMetadataTracker = 79, - CrstMethodDescBackpatchInfoTracker = 80, - CrstModIntPairList = 81, - CrstModule = 82, - CrstModuleFixup = 83, - CrstModuleLookupTable = 84, - CrstMulticoreJitHash = 85, - CrstMulticoreJitManager = 86, - CrstMUThunkHash = 87, - CrstNativeBinderInit = 88, - CrstNativeImageCache = 89, - CrstNls = 90, - CrstNotifyGdb = 91, - CrstObjectList = 92, - CrstOnEventManager = 93, - CrstPatchEntryPoint = 94, - CrstPEImage = 95, - CrstPEImagePDBStream = 96, - CrstPendingTypeLoadEntry = 97, - CrstPinHandle = 98, - CrstPinnedByrefValidation = 99, - CrstProfilerGCRefDataFreeList = 100, - CrstProfilingAPIStatus = 101, - CrstPublisherCertificate = 102, - CrstRCWCache = 103, - CrstRCWCleanupList = 104, - CrstRCWRefCache = 105, - CrstReadyToRunEntryPointToMethodDescMap = 106, - CrstReDacl = 107, - CrstReflection = 108, - CrstReJITDomainTable = 109, - CrstReJITGlobalRequest = 110, - CrstRemoting = 111, - CrstRetThunkCache = 112, - CrstRWLock = 113, - CrstSavedExceptionInfo = 114, - CrstSaveModuleProfileData = 115, - CrstSecurityStackwalkCache = 116, - CrstSharedAssemblyCreate = 117, - CrstSigConvert = 118, - CrstSingleUseLock = 119, - CrstSpecialStatics = 120, - CrstSqmManager = 121, - CrstStackSampler = 122, - CrstStressLog = 123, - CrstStrongName = 124, - CrstStubCache = 125, - CrstStubDispatchCache = 126, - CrstStubUnwindInfoHeapSegments = 127, - CrstSyncBlockCache = 128, - CrstSyncHashLock = 129, - CrstSystemBaseDomain = 130, - CrstSystemDomain = 131, - CrstSystemDomainDelayedUnloadList = 132, - CrstThreadIdDispenser = 133, - CrstThreadpoolEventCache = 134, - CrstThreadpoolTimerQueue = 135, - CrstThreadpoolWaitThreads = 136, - CrstThreadpoolWorker = 137, - CrstThreadStaticDataHashTable = 138, - CrstThreadStore = 139, - CrstTieredCompilation = 140, - CrstTPMethodTable = 141, - CrstTypeEquivalenceMap = 142, - CrstTypeIDMap = 143, - CrstUMEntryThunkCache = 144, - CrstUMThunkHash = 145, - CrstUniqueStack = 146, - CrstUnresolvedClassLock = 147, - CrstUnwindInfoTableLock = 148, - CrstVSDIndirectionCellLock = 149, - CrstWinRTFactoryCache = 150, - CrstWrapperTemplate = 151, - kNumberOfCrstTypes = 152 + CrstJitPatchpoint = 71, + CrstJitPerf = 72, + CrstJumpStubCache = 73, + CrstLeafLock = 74, + CrstListLock = 75, + CrstLoaderAllocator = 76, + CrstLoaderAllocatorReferences = 77, + CrstLoaderHeap = 78, + CrstMda = 79, + CrstMetadataTracker = 80, + CrstMethodDescBackpatchInfoTracker = 81, + CrstModIntPairList = 82, + CrstModule = 83, + CrstModuleFixup = 84, + CrstModuleLookupTable = 85, + CrstMulticoreJitHash = 86, + CrstMulticoreJitManager = 87, + CrstMUThunkHash = 88, + CrstNativeBinderInit = 89, + CrstNativeImageCache = 90, + CrstNls = 91, + CrstNotifyGdb = 92, + CrstObjectList = 93, + CrstOnEventManager = 94, + CrstPatchEntryPoint = 95, + CrstPEImage = 96, + CrstPEImagePDBStream = 97, + CrstPendingTypeLoadEntry = 98, + CrstPinHandle = 99, + CrstPinnedByrefValidation = 100, + CrstProfilerGCRefDataFreeList = 101, + CrstProfilingAPIStatus = 102, + CrstPublisherCertificate = 103, + CrstRCWCache = 104, + CrstRCWCleanupList = 105, + CrstRCWRefCache = 106, + CrstReadyToRunEntryPointToMethodDescMap = 107, + CrstReDacl = 108, + CrstReflection = 109, + CrstReJITDomainTable = 110, + CrstReJITGlobalRequest = 111, + CrstRemoting = 112, + CrstRetThunkCache = 113, + CrstRWLock = 114, + CrstSavedExceptionInfo = 115, + CrstSaveModuleProfileData = 116, + CrstSecurityStackwalkCache = 117, + CrstSharedAssemblyCreate = 118, + CrstSigConvert = 119, + CrstSingleUseLock = 120, + CrstSpecialStatics = 121, + CrstSqmManager = 122, + CrstStackSampler = 123, + CrstStressLog = 124, + CrstStrongName = 125, + CrstStubCache = 126, + CrstStubDispatchCache = 127, + CrstStubUnwindInfoHeapSegments = 128, + CrstSyncBlockCache = 129, + CrstSyncHashLock = 130, + CrstSystemBaseDomain = 131, + CrstSystemDomain = 132, + CrstSystemDomainDelayedUnloadList = 133, + CrstThreadIdDispenser = 134, + CrstThreadpoolEventCache = 135, + CrstThreadpoolTimerQueue = 136, + CrstThreadpoolWaitThreads = 137, + CrstThreadpoolWorker = 138, + CrstThreadStaticDataHashTable = 139, + CrstThreadStore = 140, + CrstTieredCompilation = 141, + CrstTPMethodTable = 142, + CrstTypeEquivalenceMap = 143, + CrstTypeIDMap = 144, + CrstUMEntryThunkCache = 145, + CrstUMThunkHash = 146, + CrstUniqueStack = 147, + CrstUnresolvedClassLock = 148, + CrstUnwindInfoTableLock = 149, + CrstVSDIndirectionCellLock = 150, + CrstWinRTFactoryCache = 151, + CrstWrapperTemplate = 152, + kNumberOfCrstTypes = 153 }; #endif // __CRST_TYPES_INCLUDED @@ -250,6 +251,7 @@ int g_rgCrstLevelMap[] = 8, // CrstJit 0, // CrstJitGenericHandleCache 15, // CrstJitInlineTrackingMap + 0, // CrstJitPatchpoint -1, // CrstJitPerf 6, // CrstJumpStubCache 0, // CrstLeafLock @@ -407,6 +409,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstJit", "CrstJitGenericHandleCache", "CrstJitInlineTrackingMap", + "CrstJitPatchpoint", "CrstJitPerf", "CrstJumpStubCache", "CrstLeafLock", diff --git a/src/coreclr/src/inc/jithelpers.h b/src/coreclr/src/inc/jithelpers.h index b1ce9b13c8c031..cb5a92c39ac5b4 100644 --- a/src/coreclr/src/inc/jithelpers.h +++ b/src/coreclr/src/inc/jithelpers.h @@ -361,6 +361,8 @@ JITHELPER(CORINFO_HELP_STACK_PROBE, NULL, CORINFO_HELP_SIG_UNDEF) #endif + JITHELPER(CORINFO_HELP_PATCHPOINT, JIT_Patchpoint, CORINFO_HELP_SIG_REG_ONLY) + #undef JITHELPER #undef DYNAMICJITHELPER #undef JITHELPER diff --git a/src/coreclr/src/jit/CMakeLists.txt b/src/coreclr/src/jit/CMakeLists.txt index b63e08ae2436da..d4038c7e2a8d22 100644 --- a/src/coreclr/src/jit/CMakeLists.txt +++ b/src/coreclr/src/jit/CMakeLists.txt @@ -67,6 +67,7 @@ set( JIT_SOURCES objectalloc.cpp optcse.cpp optimizer.cpp + patchpoint.cpp phase.cpp rangecheck.cpp rationalize.cpp diff --git a/src/coreclr/src/jit/block.cpp b/src/coreclr/src/jit/block.cpp index 01058ba2ab9f7b..fe186a3544102e 100644 --- a/src/coreclr/src/jit/block.cpp +++ b/src/coreclr/src/jit/block.cpp @@ -344,6 +344,14 @@ void BasicBlock::dspFlags() { printf("bwd "); } + if (bbFlags & BBF_BACKWARD_JUMP_TARGET) + { + printf("bwd-target "); + } + if (bbFlags & BBF_PATCHPOINT) + { + printf("ppoint "); + } if (bbFlags & BBF_RETLESS_CALL) { printf("retless "); diff --git a/src/coreclr/src/jit/block.h b/src/coreclr/src/jit/block.h index 15897374c565d8..94adae39c50345 100644 --- a/src/coreclr/src/jit/block.h +++ b/src/coreclr/src/jit/block.h @@ -440,14 +440,15 @@ struct BasicBlock : private LIR::Range // BBJ_CALLFINALLY block, as well as, on x86, the final step block out of a // finally. -#define BBF_CLONED_FINALLY_BEGIN 0x100000000 // First block of a cloned finally region -#define BBF_CLONED_FINALLY_END 0x200000000 // Last block of a cloned finally region -#define BBF_HAS_CALL 0x400000000 // BB contains a call +#define BBF_CLONED_FINALLY_BEGIN 0x100000000 // First block of a cloned finally region +#define BBF_CLONED_FINALLY_END 0x200000000 // Last block of a cloned finally region +#define BBF_HAS_CALL 0x400000000 // BB contains a call +#define BBF_DOMINATED_BY_EXCEPTIONAL_ENTRY 0x800000000 // Block is dominated by exceptional entry. +#define BBF_BACKWARD_JUMP_TARGET 0x1000000000 // Block is a target of a backward jump +#define BBF_PATCHPOINT 0x2000000000 // Block is a patchpoint // clang-format on -#define BBF_DOMINATED_BY_EXCEPTIONAL_ENTRY 0x800000000 // Block is dominated by exceptional entry. - // Flags that relate blocks to loop structure. #define BBF_LOOP_FLAGS (BBF_LOOP_PREHEADER | BBF_LOOP_HEAD | BBF_LOOP_CALL0 | BBF_LOOP_CALL1) diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index 20e17efe95523b..f18bb96df5e512 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -2142,6 +2142,11 @@ void CodeGen::genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode) printf("; ReadyToRun compilation\n"); } + if (compiler->opts.IsOSR()) + { + printf("; OSR variant for entry point 0x%x\n", compiler->info.compILEntry); + } + if ((compiler->opts.compFlags & CLFLG_MAXOPT) == CLFLG_MAXOPT) { printf("; optimized code\n"); @@ -4474,6 +4479,12 @@ void CodeGen::genEnregisterIncomingStackArgs() } #endif + // OSR handles this specially + if (compiler->opts.IsOSR()) + { + return; + } + assert(compiler->compGeneratingProlog); unsigned varNum = 0; @@ -4574,6 +4585,20 @@ void CodeGen::genCheckUseBlockInit() continue; } + // Initialization of OSR locals must be handled specially + if (compiler->lvaIsOSRLocal(varNum)) + { + varDsc->lvMustInit = 0; + continue; + } + + // Likewise, initialization of the GS cookie is handled specially for OSR. + // Could do this for non-OSR too.. (likewise for the dummy) + if (compiler->opts.IsOSR() && varNum == compiler->lvaGSSecurityCookie) + { + continue; + } + if (!varDsc->lvIsInReg() && !varDsc->lvOnFrame) { noway_assert(varDsc->lvRefCnt() == 0); @@ -6478,6 +6503,87 @@ void CodeGen::genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg, inst_ST_RV(ins_Store(TYP_I_IMPL), tempThis, 0, genGetZeroReg(initReg, pInitRegZeroed), TYP_I_IMPL); } } + + // Initialize args and locals for OSR. Note this may include promoted fields. + if (compiler->opts.IsOSR()) + { + CORINFO_PATCHPOINT_INFO* patchpointInfo = compiler->info.compPatchpointInfo; + + // basic sanity checks (make sure we're OSRing the right method) + assert(patchpointInfo->NumberOfLocals() == compiler->info.compLocalsCount); + assert(patchpointInfo->ILSize() == compiler->info.compILCodeSize); + + const int originalFrameSize = patchpointInfo->FpToSpDelta(); + const unsigned patchpointInfoLen = patchpointInfo->NumberOfLocals(); + + for (unsigned varNum = 0; varNum < compiler->lvaCount; varNum++) + { + if (!compiler->lvaIsOSRLocal(varNum)) + { + continue; + } + + LclVarDsc* const varDsc = compiler->lvaGetDesc(varNum); + + if (!varDsc->lvIsInReg()) + { + JITDUMP("---OSR--- V%02u in memory\n", varNum); + continue; + } + + if (!VarSetOps::IsMember(compiler, compiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex)) + { + JITDUMP("---OSR--- V%02u (reg) not live at entry\n", varNum); + continue; + } + + int fieldOffset = 0; + unsigned lclNum = varNum; + + if (varDsc->lvIsStructField) + { + lclNum = varDsc->lvParentLcl; + assert(lclNum < patchpointInfoLen); + + fieldOffset = varDsc->lvFldOffset; + JITDUMP("---OSR--- V%02u is promoted field of V%02u at offset %d\n", varNum, lclNum, fieldOffset); + } + + // Note we are always reading from the original frame here + const var_types lclTyp = genActualType(varDsc->lvType); + const emitAttr size = emitTypeSize(lclTyp); + const int stkOffs = patchpointInfo->Offset(lclNum) + fieldOffset; + + // stkOffs is the original frame RBP-relative offset + // to the var. We want either an RSP or RBP relative + // offset for the current frame. + // + // If using RSP, we need to add the SP-to-FP delta of + // this frame and the SP-to-FP delta of the original + // frame... that translates from this frame's RSP to + // the old frame RBP. We then add the original frame's + // RBP relative offset. + // + // If using RBP, we need to add the SP-to-FP delta of + // the original frame and then add the original + // frame's RBP relative offset. + int offset = originalFrameSize + stkOffs; + + if (isFramePointerUsed()) + { + offset += TARGET_POINTER_SIZE; + } + else + { + offset += genSPtoFPdelta(); + } + + JITDUMP("---OSR--- V%02u (reg) old rbp offset %d old frame %d this frame sp-fp %d new offset %d (%02xH)\n", + varNum, stkOffs, originalFrameSize, genSPtoFPdelta(), offset, offset); + + GetEmitter()->emitIns_R_AR(ins_Load(lclTyp), size, varDsc->GetRegNum(), genFramePointerReg(), offset); + } + } } /*----------------------------------------------------------------------------- @@ -6491,6 +6597,12 @@ void CodeGen::genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg, void CodeGen::genReportGenericContextArg(regNumber initReg, bool* pInitRegZeroed) { + // For OSR the original method has set this up for us. + if (compiler->opts.IsOSR()) + { + return; + } + assert(compiler->compGeneratingProlog); bool reportArg = compiler->lvaReportParamTypeArg(); @@ -7145,6 +7257,19 @@ void CodeGen::genFnProlog() psiBegProlog(); } +#if defined(TARGET_XARCH) + // For OSR there is a "phantom prolog" to account for the actions taken + // in the original frame that impact RBP and RSP on entry to the OSR method. + if (compiler->opts.IsOSR()) + { + CORINFO_PATCHPOINT_INFO* patchpointInfo = compiler->info.compPatchpointInfo; + const int originalFrameSize = patchpointInfo->FpToSpDelta(); + + compiler->unwindPush(REG_FPBASE); + compiler->unwindAllocStack(originalFrameSize); + } +#endif + #ifdef DEBUG if (compiler->compJitHaltMethod()) @@ -7336,7 +7461,18 @@ void CodeGen::genFnProlog() } } - assert((genInitStkLclCnt > 0) == hasUntrLcl); + // With OSR we may have untracked locals that were initialized by the original method. + if (hasUntrLcl) + { + if (compiler->opts.IsOSR()) + { + // todo: suitable assertion here + } + else + { + assert(genInitStkLclCnt > 0); + } + } #ifdef DEBUG if (verbose) @@ -7442,7 +7578,9 @@ void CodeGen::genFnProlog() // This way, the varargs iterator will be able to retrieve the // call arguments properly since both the arg regs and the stack allocated // args will be contiguous. - if (compiler->info.compIsVarArgs) + // + // OSR methods can skip this, as the setup is done by the orignal method. + if (compiler->info.compIsVarArgs && !compiler->opts.IsOSR()) { GetEmitter()->spillIntArgRegsToShadowSlots(); } @@ -7650,7 +7788,11 @@ void CodeGen::genFnProlog() #ifdef PROFILING_SUPPORTED // Insert a function entry callback for profiling, if requested. - genProfilingEnterCallback(initReg, &initRegZeroed); + // OSR methods aren't called, so don't have enter hooks. + if (!compiler->opts.IsOSR()) + { + genProfilingEnterCallback(initReg, &initRegZeroed); + } #endif // PROFILING_SUPPORTED @@ -7689,37 +7831,41 @@ void CodeGen::genFnProlog() // Update the arg initial register locations. compiler->lvaUpdateArgsWithInitialReg(); - FOREACH_REGISTER_FILE(regState) + if (!compiler->opts.IsOSR()) { - if (regState->rsCalleeRegArgMaskLiveIn) + // OSR handles this by moving the values from the original frame. + FOREACH_REGISTER_FILE(regState) { - // If we need an extra register to shuffle around the incoming registers - // we will use xtraReg (initReg) and set the xtraRegClobbered flag, - // if we don't need to use the xtraReg then this flag will stay false - // - regNumber xtraReg; - bool xtraRegClobbered = false; - - if (genRegMask(initReg) & RBM_ARG_REGS) - { - xtraReg = initReg; - } - else + if (regState->rsCalleeRegArgMaskLiveIn) { - xtraReg = REG_SCRATCH; - initRegZeroed = false; - } + // If we need an extra register to shuffle around the incoming registers + // we will use xtraReg (initReg) and set the xtraRegClobbered flag, + // if we don't need to use the xtraReg then this flag will stay false + // + regNumber xtraReg; + bool xtraRegClobbered = false; - genFnPrologCalleeRegArgs(xtraReg, &xtraRegClobbered, regState); + if (genRegMask(initReg) & RBM_ARG_REGS) + { + xtraReg = initReg; + } + else + { + xtraReg = REG_SCRATCH; + initRegZeroed = false; + } - if (xtraRegClobbered) - { - initRegZeroed = false; + genFnPrologCalleeRegArgs(xtraReg, &xtraRegClobbered, regState); + + if (xtraRegClobbered) + { + initRegZeroed = false; + } } } } - // Home the incoming arguments + // Home the incoming arguments. genEnregisterIncomingStackArgs(); /* Initialize any must-init registers variables now */ @@ -8289,6 +8435,24 @@ void CodeGen::genFnEpilog(BasicBlock* block) } genPopCalleeSavedRegisters(); + + // Extra OSR adjust to get to where RBP was saved by the original frame, and + // restore RBP. + // + // Note the other callee saves made in that frame are dead, the current method + // will save and restore what it needs. + if (compiler->opts.IsOSR()) + { + CORINFO_PATCHPOINT_INFO* patchpointInfo = compiler->info.compPatchpointInfo; + const int originalFrameSize = patchpointInfo->FpToSpDelta(); + + // Use add since we know the SP-to-FP delta of the original method. + // + // If we ever allow the original method to have localloc this will + // need to change. + inst_RV_IV(INS_add, REG_SPBASE, originalFrameSize, EA_PTRSIZE); + inst_RV(INS_pop, REG_EBP, TYP_I_IMPL); + } } else { @@ -8320,9 +8484,11 @@ void CodeGen::genFnEpilog(BasicBlock* block) if (compiler->compLocallocUsed) { + // OSR not yet ready for localloc + assert(!compiler->opts.IsOSR()); + // ESP may be variable if a localloc was actually executed. Reset it. // lea esp, [ebp - compiler->compCalleeRegsPushed * REGSIZE_BYTES] - needLea = true; } else if (!regSet.rsRegsModified(RBM_CALLEE_SAVED)) @@ -8392,10 +8558,26 @@ void CodeGen::genFnEpilog(BasicBlock* block) // // Pop the callee-saved registers (if any) // - genPopCalleeSavedRegisters(); #ifdef TARGET_AMD64 + // Extra OSR adjust to get to where RBP was saved by the original frame. + // + // Note the other callee saves made in that frame are dead, the current method + // will save and restore what it needs. + if (compiler->opts.IsOSR()) + { + CORINFO_PATCHPOINT_INFO* patchpointInfo = compiler->info.compPatchpointInfo; + const int originalFrameSize = patchpointInfo->FpToSpDelta(); + + // Use add since we know the SP-to-FP delta of the original method. + // We also need to skip over the slot where we pushed RBP. + // + // If we ever allow the original method to have localloc this will + // need to change. + inst_RV_IV(INS_add, REG_SPBASE, originalFrameSize + TARGET_POINTER_SIZE, EA_PTRSIZE); + } + assert(!needMovEspEbp); // "mov esp, ebp" is not allowed in AMD64 epilogs #else // !TARGET_AMD64 if (needMovEspEbp) diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index d94d043a528f99..283a58b2e6a688 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -69,6 +69,12 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed) return; } + if (compiler->opts.IsOSR() && compiler->info.compPatchpointInfo->HasSecurityCookie()) + { + // Security cookie is on original frame and was initialized there. + return; + } + if (compiler->gsGlobalSecurityCookieAddr == nullptr) { noway_assert(compiler->gsGlobalSecurityCookieVal != 0); diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 60050c6c493b8d..4570aed2763150 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -3228,6 +3228,11 @@ void Compiler::compInitOptions(JitFlags* jitFlags) printf("OPTIONS: Tier-1/FullOpts compilation, switched to MinOpts\n"); } + if (jitFlags->IsSet(JitFlags::JIT_FLAG_OSR)) + { + printf("OPTIONS: OSR variant with entry point 0x%x\n", info.compILEntry); + } + printf("OPTIONS: compCodeOpt = %s\n", (opts.compCodeOpt == BLENDED_CODE) ? "BLENDED_CODE" @@ -4277,6 +4282,10 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags // DoPhase(this, PHASE_INDXCALL, &Compiler::fgTransformIndirectCalls); + // Expand any patchpoints + // + DoPhase(this, PHASE_PATCHPOINTS, &Compiler::fgTransformPatchpoints); + // PostImportPhase: cleanup inlinees // auto postImportPhase = [this]() { @@ -4920,6 +4929,77 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags } #endif + // Generate PatchpointInfo + if (doesMethodHavePatchpoints()) + { + // Currently we always transition from Tier0 unoptimized code, and + // so we expect the method to have a frame pointer. + assert(codeGen->isFramePointerUsed()); + + // Allocate patchpoint info storage from runtime, and fill in initial bits of data. + const unsigned patchpointInfoSize = CORINFO_PATCHPOINT_INFO::ComputeSize(info.compLocalsCount); + CORINFO_PATCHPOINT_INFO* const patchpointInfo = + (CORINFO_PATCHPOINT_INFO*)info.compCompHnd->allocateArray(patchpointInfoSize); + patchpointInfo->Initialize(info.compLocalsCount, info.compILCodeSize, + codeGen->genSPtoFPdelta() + TARGET_POINTER_SIZE); + + JITDUMP("--OSR--- FP-SP delta is %d\n", patchpointInfo->FpToSpDelta()); + + // We record offsets for all the "locals" here. Could restrict + // this to just the IL locals with some extra logic, and save a bit of space, + // but would need to adjust all consumers, too. + for (unsigned lclNum = 0; lclNum < info.compLocalsCount; lclNum++) + { + LclVarDsc* const varDsc = lvaGetDesc(lclNum); + + // We expect all these to have stack homes, and be FP relative + assert(varDsc->lvOnFrame); + assert(varDsc->lvFramePointerBased); + + // Record FramePtr relative offset (no localloc yet) + patchpointInfo->SetOffset(lclNum, varDsc->lvStkOffs); + + // Note if IL stream contained an address-of that potentially leads to exposure. + // This bit of IL may be skipped by OSR partial importation. + if (varDsc->lvHasLdAddrOp) + { + patchpointInfo->SetIsExposed(lclNum); + } + + JITDUMP("--OSR-- V%02u is at offset %d%s\n", lclNum, patchpointInfo->Offset(lclNum), + patchpointInfo->IsExposed(lclNum) ? " (exposed)" : ""); + } + + // Special offsets + + if (lvaReportParamTypeArg() || lvaKeepAliveAndReportThis()) + { + const int offset = lvaToCallerSPRelativeOffset(lvaCachedGenericContextArgOffset(), true); + patchpointInfo->SetGenericContextArgOffset(offset); + JITDUMP("--OSR-- cached generic context offset is CallerSP %d\n", + patchpointInfo->GenericContextArgOffset()); + } + + if (lvaKeepAliveAndReportThis()) + { + const int offset = lvaCachedGenericContextArgOffset(); + patchpointInfo->SetKeptAliveThisOffset(offset); + JITDUMP("--OSR-- kept-alive this offset is FP %d\n", patchpointInfo->KeptAliveThisOffset()); + } + + if (compGSReorderStackLayout) + { + assert(lvaGSSecurityCookie != BAD_VAR_NUM); + LclVarDsc* const varDsc = lvaGetDesc(lvaGSSecurityCookie); + patchpointInfo->SetSecurityCookieOffset(varDsc->lvStkOffs); + JITDUMP("--OSR-- security cookie V%02u offset is FP %d\n", lvaGSSecurityCookie, + patchpointInfo->SecurityCookieOffset()); + } + + // Register this with the runtime. + info.compCompHnd->setPatchpointInfo(patchpointInfo); + } + RecordStateAtEndOfCompilation(); #ifdef FEATURE_TRACELOGGING @@ -5203,6 +5283,25 @@ int Compiler::compCompile(CORINFO_METHOD_HANDLE methodHnd, info.compMethodHnd = methodHnd; info.compMethodInfo = methodInfo; + if (compIsForInlining()) + { + compileFlags->Clear(JitFlags::JIT_FLAG_OSR); + info.compILEntry = 0; + info.compPatchpointInfo = nullptr; + } + else if (compileFlags->IsSet(JitFlags::JIT_FLAG_OSR)) + { + info.compILEntry = methodInfo->osrInfo.ilOffset; + info.compPatchpointInfo = methodInfo->osrInfo.patchpointInfo; + + assert(info.compPatchpointInfo != nullptr); + } + else + { + // If JIT_FLAG_OSR is not set, we should not be passed valid OSR info + assert(methodInfo->osrInfo.patchpointInfo == nullptr); + } + virtualStubParamInfo = new (this, CMK_Unknown) VirtualStubParamInfo(IsTargetAbi(CORINFO_CORERT_ABI)); // compMatchedVM is set to true if both CPU/ABI and OS are matching the execution engine requirements @@ -6014,9 +6113,9 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, #ifdef DEBUG if ((JitConfig.DumpJittedMethods() == 1) && !compIsForInlining()) { - printf("Compiling %4d %s::%s, IL size = %u, hash=0x%08x %s%s\n", Compiler::jitTotalMethodCompiled, + printf("Compiling %4d %s::%s, IL size = %u, hash=0x%08x %s%s%s\n", Compiler::jitTotalMethodCompiled, info.compClassName, info.compMethodName, info.compILCodeSize, info.compMethodHash(), - compGetTieringName(), compGetStressMessage()); + compGetTieringName(), opts.IsOSR() ? " OSR" : "", compGetStressMessage()); } if (compIsForInlining()) { @@ -9182,3 +9281,36 @@ bool Compiler::killGCRefs(GenTree* tree) return false; } + +//------------------------------------------------------------------------ +// lvaIsOSRLocal: check if this local var is one that requires special +// treatment for OSR compilations. +// +// Arguments: +// varNum - variable of interest +// +// Return Value: +// true - this is an OSR compile and this local requires special treatment +// false - not an OSR compile, or not an interesting local for OSR + +bool Compiler::lvaIsOSRLocal(unsigned varNum) +{ + if (!opts.IsOSR()) + { + return false; + } + + if (varNum < info.compLocalsCount) + { + return true; + } + + LclVarDsc* varDsc = lvaGetDesc(varNum); + + if (varDsc->lvIsStructField) + { + return (varDsc->lvParentLcl < info.compLocalsCount); + } + + return false; +} diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 3bac3113ecdc86..82ced69c91593c 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -2406,6 +2406,8 @@ class Compiler EHblkDsc* ehInitTryBlockRange(BasicBlock* blk, BasicBlock** tryBeg, BasicBlock** tryLast); + void fgSetTryBeg(EHblkDsc* handlerTab, BasicBlock* newTryBeg); + void fgSetTryEnd(EHblkDsc* handlerTab, BasicBlock* newTryLast); void fgSetHndEnd(EHblkDsc* handlerTab, BasicBlock* newHndLast); @@ -3199,6 +3201,10 @@ class Compiler int lvaToInitialSPRelativeOffset(unsigned offset, bool isFpBased); int lvaGetInitialSPRelativeOffset(unsigned varNum); + // True if this is an OSR compilation and this local is potentially + // located on the original method stack frame. + bool lvaIsOSRLocal(unsigned varNum); + //------------------------ For splitting types ---------------------------- void lvaInitTypeRef(); @@ -3521,8 +3527,7 @@ class Compiler public: void impInit(); - - void impImport(BasicBlock* method); + void impImport(); CORINFO_CLASS_HANDLE impGetRefAnyClass(); CORINFO_CLASS_HANDLE impGetRuntimeArgumentHandle(); @@ -4175,6 +4180,7 @@ class Compiler BasicBlock* fgFirstBB; // Beginning of the basic block list BasicBlock* fgLastBB; // End of the basic block list BasicBlock* fgFirstColdBlock; // First block to be placed in the cold section + BasicBlock* fgEntryBB; // For OSR, the original method's entry point #if defined(FEATURE_EH_FUNCLETS) BasicBlock* fgFirstFuncletBB; // First block of outlined funclets (to allow block insertion before the funclets) #endif @@ -4388,6 +4394,8 @@ class Compiler void fgTransformIndirectCalls(); + void fgTransformPatchpoints(); + void fgInline(); void fgRemoveEmptyTry(); @@ -5284,11 +5292,10 @@ class Compiler public: void fgInsertStmtAtEnd(BasicBlock* block, Statement* stmt); Statement* fgNewStmtAtEnd(BasicBlock* block, GenTree* tree); + Statement* fgNewStmtNearEnd(BasicBlock* block, GenTree* tree); private: void fgInsertStmtNearEnd(BasicBlock* block, Statement* stmt); - Statement* fgNewStmtNearEnd(BasicBlock* block, GenTree* tree); - void fgInsertStmtAtBeg(BasicBlock* block, Statement* stmt); Statement* fgNewStmtAtBeg(BasicBlock* block, GenTree* tree); @@ -6362,6 +6369,7 @@ class Compiler #define OMF_HAS_OBJSTACKALLOC 0x00000040 // Method contains an object allocated on the stack. #define OMF_HAS_GUARDEDDEVIRT 0x00000080 // Method contains guarded devirtualization candidate #define OMF_HAS_EXPRUNTIMELOOKUP 0x00000100 // Method contains a runtime lookup to an expandable dictionary. +#define OMF_HAS_PATCHPOINT 0x00000200 // Method contains patchpoints bool doesMethodHaveFatPointer() { @@ -6418,6 +6426,16 @@ class Compiler void addExpRuntimeLookupCandidate(GenTreeCall* call); + bool doesMethodHavePatchpoints() + { + return (optMethodFlags & OMF_HAS_PATCHPOINT) != 0; + } + + void setMethodHasPatchpoint() + { + optMethodFlags |= OMF_HAS_PATCHPOINT; + } + unsigned optMethodFlags; bool doesMethodHaveNoReturnCalls() @@ -8454,6 +8472,18 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX } #endif +#ifdef FEATURE_ON_STACK_REPLACEMENT + bool IsOSR() const + { + return jitFlags->IsSet(JitFlags::JIT_FLAG_OSR); + } +#else + bool IsOSR() const + { + return false; + } +#endif + // true if we should use the PINVOKE_{BEGIN,END} helpers instead of generating // PInvoke transitions inline (e.g. when targeting CoreRT). bool ShouldUsePInvokeHelpers() @@ -8790,11 +8820,13 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // The following holds the class attributes for the method we're compiling. unsigned compClassAttr; - const BYTE* compCode; - IL_OFFSET compILCodeSize; // The IL code size - IL_OFFSET compILImportSize; // Estimated amount of IL actually imported - UNATIVE_OFFSET compNativeCodeSize; // The native code size, after instructions are issued. This - // is less than (compTotalHotCodeSize + compTotalColdCodeSize) only if: + const BYTE* compCode; + IL_OFFSET compILCodeSize; // The IL code size + IL_OFFSET compILImportSize; // Estimated amount of IL actually imported + IL_OFFSET compILEntry; // The IL entry point (normally 0) + CORINFO_PATCHPOINT_INFO* compPatchpointInfo; // Patchpoint data for OSR (normally nullptr) + UNATIVE_OFFSET compNativeCodeSize; // The native code size, after instructions are issued. This + // is less than (compTotalHotCodeSize + compTotalColdCodeSize) only if: // (1) the code is not hot/cold split, and we issued less code than we expected, or // (2) the code is hot/cold split, and we issued less code than we expected // in the cold section (the hot section will always be padded out to compTotalHotCodeSize). diff --git a/src/coreclr/src/jit/compphases.h b/src/coreclr/src/jit/compphases.h index 12ef4313928237..06712d344247ef 100644 --- a/src/coreclr/src/jit/compphases.h +++ b/src/coreclr/src/jit/compphases.h @@ -27,6 +27,7 @@ CompPhaseNameMacro(PHASE_PRE_IMPORT, "Pre-import", "PRE-IMP", false, -1, false) CompPhaseNameMacro(PHASE_IMPORTATION, "Importation", "IMPORT", false, -1, true) CompPhaseNameMacro(PHASE_INDXCALL, "Indirect call transform", "INDXCALL", false, -1, true) +CompPhaseNameMacro(PHASE_PATCHPOINTS, "Expand patchpoints", "PPOINT", false, -1, true) CompPhaseNameMacro(PHASE_POST_IMPORT, "Post-import", "POST-IMP", false, -1, false) CompPhaseNameMacro(PHASE_IBCINSTR, "IBC instrumentation", "IBCINSTR", false, -1, false) CompPhaseNameMacro(PHASE_MORPH_INIT, "Morph - Init", "MOR-INIT" ,false, -1, false) diff --git a/src/coreclr/src/jit/emitxarch.cpp b/src/coreclr/src/jit/emitxarch.cpp index 38a3fff063a927..754ae4e32e8d8a 100644 --- a/src/coreclr/src/jit/emitxarch.cpp +++ b/src/coreclr/src/jit/emitxarch.cpp @@ -1981,9 +1981,11 @@ inline UNATIVE_OFFSET emitter::emitInsSizeSV(code_t code, int var, int dsp) LclVarDsc* varDsc = emitComp->lvaTable + var; bool isRegPassedArg = varDsc->lvIsParam && varDsc->lvIsRegArg; // Register passed args could have a stack offset of 0. - noway_assert((int)offs < 0 || isRegPassedArg); + noway_assert((int)offs < 0 || isRegPassedArg || emitComp->opts.IsOSR()); #else // !UNIX_AMD64_ABI - noway_assert((int)offs < 0); + + // OSR transitioning to RBP frame currently can have mid-frame FP + noway_assert(((int)offs < 0) || emitComp->opts.IsOSR()); #endif // !UNIX_AMD64_ABI } diff --git a/src/coreclr/src/jit/flowgraph.cpp b/src/coreclr/src/jit/flowgraph.cpp index 63d80906cc6a8b..c4769ad9168886 100644 --- a/src/coreclr/src/jit/flowgraph.cpp +++ b/src/coreclr/src/jit/flowgraph.cpp @@ -65,6 +65,7 @@ void Compiler::fgInit() fgFirstBB = nullptr; fgLastBB = nullptr; fgFirstColdBlock = nullptr; + fgEntryBB = nullptr; #if defined(FEATURE_EH_FUNCLETS) fgFirstFuncletBB = nullptr; @@ -5177,16 +5178,19 @@ void Compiler::fgObserveInlineConstants(OPCODE opcode, const FgStack& stack, boo } } -/***************************************************************************** - * - * Finally link up the bbJumpDest of the blocks together - */ +//------------------------------------------------------------------------ +// fgMarkBackwardJump: mark blocks indicating there is a jump backwards in +// IL, from a higher to lower IL offset. +// +// Arguments: +// targetBlock -- target of the jump +// sourceBlock -- source of the jump -void Compiler::fgMarkBackwardJump(BasicBlock* startBlock, BasicBlock* endBlock) +void Compiler::fgMarkBackwardJump(BasicBlock* targetBlock, BasicBlock* sourceBlock) { - noway_assert(startBlock->bbNum <= endBlock->bbNum); + noway_assert(targetBlock->bbNum <= sourceBlock->bbNum); - for (BasicBlock* block = startBlock; block != endBlock->bbNext; block = block->bbNext) + for (BasicBlock* block = targetBlock; block != sourceBlock->bbNext; block = block->bbNext) { if ((block->bbFlags & BBF_BACKWARD_JUMP) == 0) { @@ -5194,6 +5198,8 @@ void Compiler::fgMarkBackwardJump(BasicBlock* startBlock, BasicBlock* endBlock) compHasBackwardJump = true; } } + + targetBlock->bbFlags |= BBF_BACKWARD_JUMP_TARGET; } /***************************************************************************** @@ -5983,6 +5989,29 @@ void Compiler::fgFindBasicBlocks() return; } + // If we are doing OSR, add an entry block that simply branches to the right IL offset. + // Might need to do something special if we're entering try regions? + if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_OSR)) + { + // Remember the original entry block in case this method is tail recursive. + fgEntryBB = fgLookupBB(0); + + // Find the OSR entry block. + assert(info.compILEntry >= 0); + BasicBlock* bbTarget = fgLookupBB(info.compILEntry); + + fgEnsureFirstBBisScratch(); + fgFirstBB->bbJumpKind = BBJ_ALWAYS; + fgFirstBB->bbJumpDest = bbTarget; + fgAddRefPred(bbTarget, fgFirstBB); + + JITDUMP("OSR: redirecting flow at entry via " FMT_BB " to " FMT_BB " (il offset 0x%x)\n", fgFirstBB->bbNum, + bbTarget->bbNum, info.compILEntry); + + // rebuild lookup table... should be able to avoid this by leaving room up front. + fgInitBBLookup(); + } + /* Mark all blocks within 'try' blocks as such */ if (info.compXcptnsCount == 0) @@ -6859,7 +6888,7 @@ unsigned Compiler::fgGetNestingLevel(BasicBlock* block, unsigned* pFinallyNestin void Compiler::fgImport() { - impImport(fgFirstBB); + impImport(); // Estimate how much of method IL was actually imported. // @@ -9693,118 +9722,238 @@ void Compiler::fgSimpleLowering() #endif } -/***************************************************************************** - * - * Find and remove any basic blocks that are useless (e.g. they have not been - * imported because they are not reachable, or they have been optimized away). - */ - +//------------------------------------------------------------------------ +// fgRemoveEmptyBlocks: clean up flow graph after importation +// +// Notes: +// +// Find and remove any basic blocks that are useless (e.g. they have not been +// imported because they are not reachable, or they have been optimized away). +// +// Remove try regions where no blocks in the try were imported. +// Update the end of try and handler regions where trailing blocks were not imported. +// Update the start of try regions that were partially imported (OSR) +// void Compiler::fgRemoveEmptyBlocks() { + JITDUMP("\n*************** In fgRemoveEmptyBlocks\n"); + BasicBlock* cur; BasicBlock* nxt; - /* If we remove any blocks, we'll have to do additional work */ - + // If we remove any blocks, we'll have to do additional work unsigned removedBlks = 0; for (cur = fgFirstBB; cur != nullptr; cur = nxt) { - /* Get hold of the next block (in case we delete 'cur') */ - + // Get hold of the next block (in case we delete 'cur') nxt = cur->bbNext; - /* Should this block be removed? */ - + // Should this block be removed? if (!(cur->bbFlags & BBF_IMPORTED)) { noway_assert(cur->isEmpty()); if (ehCanDeleteEmptyBlock(cur)) { - /* Mark the block as removed */ + JITDUMP(FMT_BB " was not imported, marking as removed (%d)\n", cur->bbNum, removedBlks); cur->bbFlags |= BBF_REMOVED; - - /* Remember that we've removed a block from the list */ - removedBlks++; -#ifdef DEBUG - if (verbose) - { - printf(FMT_BB " was not imported, marked as removed (%d)\n", cur->bbNum, removedBlks); - } -#endif // DEBUG - - /* Drop the block from the list */ - + // Drop the block from the list. + // + // We rely on the fact that this does not clear out + // cur->bbNext or cur->bbPrev in the code that + // follows. fgUnlinkBlock(cur); } else { - // We were prevented from deleting this block by EH normalization. Mark the block as imported. + // We were prevented from deleting this block by EH + // normalization. Mark the block as imported. cur->bbFlags |= BBF_IMPORTED; } } } - /* If no blocks were removed, we're done */ - + // If no blocks were removed, we're done if (removedBlks == 0) { return; } - /* Update all references in the exception handler table. - * Mark the new blocks as non-removable. - * - * We may have made the entire try block unreachable. - * Check for this case and remove the entry from the EH table. - */ - + // Update all references in the exception handler table. + // + // We may have made the entire try block unreachable. + // Check for this case and remove the entry from the EH table. + // + // For OSR, just the initial part of a try range may become + // unreachable; if so we need to shrink the try range down + // to the portion that was imported. unsigned XTnum; EHblkDsc* HBtab; - INDEBUG(unsigned delCnt = 0;) + unsigned delCnt = 0; + // Walk the EH regions from inner to outer for (XTnum = 0, HBtab = compHndBBtab; XTnum < compHndBBtabCount; XTnum++, HBtab++) { AGAIN: - /* If the beginning of the try block was not imported, we - * need to remove the entry from the EH table. */ + // If start of a try region was not imported, then we either + // need to trim the region extent, or remove the region + // entirely. + // + // In normal importation, it is not valid to jump into the + // middle of a try, so if the try entry was not imported, the + // entire try can be removed. + // + // In OSR importation the entry patchpoint may be in the + // middle of a try, and we need to determine how much of the + // try ended up getting imported. Because of backwards + // branches we may end up importing the entire try even though + // execution starts in the middle. + // + // Note it is common in both cases for the ends of trys (and + // associated handlers) to end up not getting imported, so if + // the try region is not removed, we always check if we need + // to trim the ends. + // if (HBtab->ebdTryBeg->bbFlags & BBF_REMOVED) { - noway_assert(!(HBtab->ebdTryBeg->bbFlags & BBF_IMPORTED)); -#ifdef DEBUG - if (verbose) + // Usual case is that the entire try can be removed. + bool removeTryRegion = true; + + if (opts.IsOSR()) { - printf("Beginning of try block (" FMT_BB ") not imported " - "- remove index #%u from the EH table\n", - HBtab->ebdTryBeg->bbNum, XTnum + delCnt); - } - delCnt++; -#endif // DEBUG + // For OSR we may need to trim the try region start. + // + // We rely on the fact that removed blocks have been snipped from + // the main block list, but that those removed blocks have kept + // their bbprev (and bbnext) links. + // + // Find the first unremoved block before the try entry block. + // + BasicBlock* const oldTryEntry = HBtab->ebdTryBeg; + BasicBlock* tryEntryPrev = oldTryEntry->bbPrev; + while ((tryEntryPrev != nullptr) && ((tryEntryPrev->bbFlags & BBF_REMOVED) != 0)) + { + tryEntryPrev = tryEntryPrev->bbPrev; + } - fgRemoveEHTableEntry(XTnum); + // Because we've added an unremovable scratch block as + // fgFirstBB, this backwards walk should always find + // some block. + assert(tryEntryPrev != nullptr); - if (XTnum < compHndBBtabCount) - { - // There are more entries left to process, so do more. Note that - // HBtab now points to the next entry, that we copied down to the - // current slot. XTnum also stays the same. - goto AGAIN; + // If there is a next block of this prev block, and that block is + // contained in the current try, we'd like to make that block + // the new start of the try, and keep the region. + BasicBlock* newTryEntry = tryEntryPrev->bbNext; + bool updateTryEntry = false; + + if ((newTryEntry != nullptr) && bbInTryRegions(XTnum, newTryEntry)) + { + // We want to trim the begin extent of the current try region to newTryEntry. + // + // This method is invoked after EH normalization, so we may need to ensure all + // try regions begin at blocks that are not the start or end of some other try. + // + // So, see if this block is already the start or end of some other EH region. + if (bbIsTryBeg(newTryEntry)) + { + // We've already end-trimmed the inner try. Do the same now for the + // current try, so it is easier to detect when they mutually protect. + // (we will call this again later, which is harmless). + fgSkipRmvdBlocks(HBtab); + + // If this try and the inner try form a "mutually protected try region" + // then we must continue to share the try entry block. + EHblkDsc* const HBinner = ehGetBlockTryDsc(newTryEntry); + assert(HBinner->ebdTryBeg == newTryEntry); + + if (HBtab->ebdTryLast != HBinner->ebdTryLast) + { + updateTryEntry = true; + } + } + // Also, a try and handler cannot start at the same block + else if (bbIsHandlerBeg(newTryEntry)) + { + updateTryEntry = true; + } + + if (updateTryEntry) + { + // We need to trim the current try to begin at a different block. Normally + // this would be problematic as we don't have enough context to redirect + // all the incoming edges, but we know oldTryEntry is unreachable. + // So there are no incoming edges to worry about. + // + assert(!tryEntryPrev->bbFallsThrough()); + + // What follows is similar to fgNewBBInRegion, but we can't call that + // here as the oldTryEntry is no longer in the main bb list. + newTryEntry = bbNewBasicBlock(BBJ_NONE); + newTryEntry->bbFlags |= (BBF_IMPORTED | BBF_INTERNAL); + + // Set the right EH region indices on this new block. + // + // Patchpoints currently cannot be inside handler regions, + // and so likewise the old and new try region entries. + assert(!oldTryEntry->hasHndIndex()); + newTryEntry->setTryIndex(XTnum); + newTryEntry->clearHndIndex(); + fgInsertBBafter(tryEntryPrev, newTryEntry); + + JITDUMP("OSR: changing start of try region #%u from " FMT_BB " to new " FMT_BB "\n", + XTnum + delCnt, oldTryEntry->bbNum, newTryEntry->bbNum); + } + else + { + // We can just trim the try to newTryEntry as it is not part of some inner try or handler. + JITDUMP("OSR: changing start of try region #%u from " FMT_BB " to " FMT_BB "\n", XTnum + delCnt, + oldTryEntry->bbNum, newTryEntry->bbNum); + } + + // Update the handler table + fgSetTryBeg(HBtab, newTryEntry); + + // Try entry blocks get specially marked and have special protection. + HBtab->ebdTryBeg->bbFlags |= BBF_DONT_REMOVE | BBF_TRY_BEG | BBF_HAS_LABEL; + + // We are keeping this try region + removeTryRegion = false; + } } - break; // no more entries (we deleted the last one), so exit the loop - } + if (removeTryRegion) + { + // In the dump, refer to the region by its original index. + JITDUMP("Try region #%u (" FMT_BB " -- " FMT_BB ") not imported, removing try from the EH table\n", + XTnum + delCnt, HBtab->ebdTryBeg->bbNum, HBtab->ebdTryLast->bbNum); -/* At this point we know we have a valid try block */ + delCnt++; -#ifdef DEBUG + fgRemoveEHTableEntry(XTnum); + + if (XTnum < compHndBBtabCount) + { + // There are more entries left to process, so do more. Note that + // HBtab now points to the next entry, that we copied down to the + // current slot. XTnum also stays the same. + goto AGAIN; + } + + // no more entries (we deleted the last one), so exit the loop + break; + } + } + + // If we get here, the try entry block was not removed. + // Check some invariants. assert(HBtab->ebdTryBeg->bbFlags & BBF_IMPORTED); assert(HBtab->ebdTryBeg->bbFlags & BBF_DONT_REMOVE); - assert(HBtab->ebdHndBeg->bbFlags & BBF_IMPORTED); assert(HBtab->ebdHndBeg->bbFlags & BBF_DONT_REMOVE); @@ -9813,10 +9962,10 @@ void Compiler::fgRemoveEmptyBlocks() assert(HBtab->ebdFilter->bbFlags & BBF_IMPORTED); assert(HBtab->ebdFilter->bbFlags & BBF_DONT_REMOVE); } -#endif // DEBUG + // Finally, do region end trimming -- update try and handler ends to reflect removed blocks. fgSkipRmvdBlocks(HBtab); - } /* end of the for loop over XTnum */ + } // Renumber the basic blocks JITDUMP("\nRenumbering the basic blocks for fgRemoveEmptyBlocks\n"); @@ -13222,6 +13371,10 @@ void Compiler::fgComputeCalledCount(BasicBlock::weight_t returnWeight) { fgFirstBB->bbFlags |= BBF_RUN_RARELY; } + else + { + fgFirstBB->bbFlags &= ~BBF_RUN_RARELY; + } } #if DEBUG @@ -20570,6 +20723,12 @@ bool BBPredsChecker::CheckEhTryDsc(BasicBlock* block, BasicBlock* blockPred, EHb return true; } + // For OSR, we allow the firstBB to branch to the middle of a try. + if (comp->opts.IsOSR() && (blockPred == comp->fgFirstBB)) + { + return true; + } + printf("Jump into the middle of try region: " FMT_BB " branches to " FMT_BB "\n", blockPred->bbNum, block->bbNum); assert(!"Jump into middle of try region"); return false; diff --git a/src/coreclr/src/jit/gcencode.cpp b/src/coreclr/src/jit/gcencode.cpp index 1f4d4be185603e..75cf7c22edf7ac 100644 --- a/src/coreclr/src/jit/gcencode.cpp +++ b/src/coreclr/src/jit/gcencode.cpp @@ -3891,20 +3891,43 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz assert(false); } - gcInfoEncoderWithLog->SetGenericsInstContextStackSlot( - compiler->lvaToCallerSPRelativeOffset(compiler->lvaCachedGenericContextArgOffset(), - compiler->isFramePointerUsed()), - ctxtParamType); + int offset = 0; + + if (compiler->opts.IsOSR()) + { + CORINFO_PATCHPOINT_INFO* ppInfo = compiler->info.compPatchpointInfo; + offset = ppInfo->GenericContextArgOffset(); + assert(offset != -1); + } + else + { + offset = compiler->lvaToCallerSPRelativeOffset(compiler->lvaCachedGenericContextArgOffset(), + compiler->isFramePointerUsed()); + } + + gcInfoEncoderWithLog->SetGenericsInstContextStackSlot(offset, ctxtParamType); } // As discussed above, handle the case where the generics context is obtained via // the method table of "this". else if (compiler->lvaKeepAliveAndReportThis()) { assert(compiler->info.compThisArg != BAD_VAR_NUM); - gcInfoEncoderWithLog->SetGenericsInstContextStackSlot( - compiler->lvaToCallerSPRelativeOffset(compiler->lvaCachedGenericContextArgOffset(), - compiler->isFramePointerUsed()), - GENERIC_CONTEXTPARAM_THIS); + + int offset = 0; + + if (compiler->opts.IsOSR()) + { + CORINFO_PATCHPOINT_INFO* ppInfo = compiler->info.compPatchpointInfo; + offset = ppInfo->GenericContextArgOffset(); + assert(offset != -1); + } + else + { + offset = compiler->lvaToCallerSPRelativeOffset(compiler->lvaCachedGenericContextArgOffset(), + compiler->isFramePointerUsed()); + } + + gcInfoEncoderWithLog->SetGenericsInstContextStackSlot(offset, GENERIC_CONTEXTPARAM_THIS); } if (compiler->getNeedsGSSecurityCookie()) @@ -3912,12 +3935,27 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz assert(compiler->lvaGSSecurityCookie != BAD_VAR_NUM); // The lv offset is FP-relative, and the using code expects caller-sp relative, so translate. + int offset = compiler->lvaGetCallerSPRelativeOffset(compiler->lvaGSSecurityCookie); + + if (compiler->opts.IsOSR()) + { + // The offset computed above already includes the OSR frame adjustment, plus the + // pop of the "pseudo0RA" from the OSR frame. + // + // To get to caller-SP, we need to subtract off the original frame size and the + // pushed RA and RBP for that frame. But ppInfo's FpToSpDelta also accounts for the + // pseudo-RA between the original method frame and the OSR frame. So the net adjustment + // is simply FpToSpDelta plus one register. + CORINFO_PATCHPOINT_INFO* ppInfo = compiler->info.compPatchpointInfo; + int adjustment = ppInfo->FpToSpDelta() + REGSIZE_BYTES; + offset -= adjustment; + JITDUMP("OSR cookie adjustment %d, final caller-SP offset %d\n", adjustment, offset); + } + // The code offset ranges assume that the GS Cookie slot is initialized in the prolog, and is valid // through the remainder of the method. We will not query for the GS Cookie while we're in an epilog, // so the question of where in the epilog it becomes invalid is moot. - gcInfoEncoderWithLog->SetGSCookieStackSlot(compiler->lvaGetCallerSPRelativeOffset( - compiler->lvaGSSecurityCookie), - prologSize, methodSize); + gcInfoEncoderWithLog->SetGSCookieStackSlot(offset, prologSize, methodSize); } else if (compiler->lvaReportParamTypeArg() || compiler->lvaKeepAliveAndReportThis()) { diff --git a/src/coreclr/src/jit/gschecks.cpp b/src/coreclr/src/jit/gschecks.cpp index cc1bb6fc58021e..e31998d3b5a429 100644 --- a/src/coreclr/src/jit/gschecks.cpp +++ b/src/coreclr/src/jit/gschecks.cpp @@ -24,7 +24,7 @@ void Compiler::gsGSChecksInitCookie() { var_types type = TYP_I_IMPL; - lvaGSSecurityCookie = lvaGrabTemp(false DEBUGARG("GSSecurityCookie")); + lvaGSSecurityCookie = lvaGrabTempWithImplicitUse(false DEBUGARG("GSSecurityCookie")); // Prevent cookie init/check from being optimized lvaSetVarAddrExposed(lvaGSSecurityCookie); diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 6d1079e2275c82..f1cf7ed1164449 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -8500,12 +8500,52 @@ var_types Compiler::impImportCall(OPCODE opcode, } // A tail recursive call is a potential loop from the current block to the start of the method. + // + // TODO: seems like we should not get here for non-stack empty sites. + // In which case the stack swizzling below for OSR is not needed. if (canTailCall && gtIsRecursiveCall(methHnd)) { + BasicBlock* loopHead = nullptr; + if (opts.IsOSR()) + { + // We might not have been planning on importing the method + // entry block, but now we must. + + // We should have remembered the real method entry block. + assert(fgEntryBB != nullptr); + + // We must make sure this block gets imported as stack empty; + // we sometimes allow tail calls from non-stack empty points. + // + // So save off the current state; we'll restore it in a bit. + SavedStack blockState; + impSaveStackState(&blockState, false); + verCurrentState.esStackDepth = 0; + + JITDUMP("\nOSR: found tail recursive call in the method, scheduling " FMT_BB " for importation\n", + fgEntryBB->bbNum); + impImportBlockPending(fgEntryBB); + loopHead = fgEntryBB; + + // Note there is no explicit flow to this block yet, + // make sure it stays around until we actually try + // the optimization. + fgEntryBB->bbFlags |= BBF_DONT_REMOVE; + + // Now put the stack back the way it was. + impRestoreStackState(&blockState); + } + else + { + // For normal jitting we'll branch back to the firstBB; this + // should already be imported. + loopHead = fgFirstBB; + } + JITDUMP("\nFound tail recursive call in the method. Mark " FMT_BB " to " FMT_BB " as having a backward branch.\n", - fgFirstBB->bbNum, compCurBB->bbNum); - fgMarkBackwardJump(fgFirstBB, compCurBB); + loopHead->bbNum, compCurBB->bbNum); + fgMarkBackwardJump(loopHead, compCurBB); } // Note: we assume that small return types are already normalized by the managed callee @@ -10528,6 +10568,31 @@ void Compiler::impImportBlockCode(BasicBlock* block) impBeginTreeList(); + // Are there any plausible patchpoint locations in this method? + if (compHasBackwardJump) + { + // Are patchpoints enabled? + if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && (JitConfig.JitPatchpoint() > 0)) + { + // We don't inline at Tier0, if we do, we may need rethink our approach. + // Could probably support inlines that don't introduce flow. + assert(!compIsForInlining()); + + // Is this block a patchpoint? + // Current strategy is stack-empty backwards branch targets + if (block->bbFlags & BBF_BACKWARD_JUMP_TARGET && (verCurrentState.esStackDepth == 0)) + { + block->bbFlags |= BBF_PATCHPOINT; + setMethodHasPatchpoint(); + } + } + } + else + { + // Should not see backward branch targets w/o backwards branches + assert((block->bbFlags & BBF_BACKWARD_JUMP_TARGET) == 0); + } + /* Walk the opcodes that comprise the basic block */ const BYTE* codeAddr = info.compCode + block->bbCodeOffs; @@ -16752,10 +16817,13 @@ void Compiler::impVerifyEHBlock(BasicBlock* block, bool isTryStart) assert(HBtab->HasFaultHandler()); } } + } - /* Recursively process the handler block */ - BasicBlock* hndBegBB = HBtab->ebdHndBeg; + // Recursively process the handler block, if we haven't already done so. + BasicBlock* hndBegBB = HBtab->ebdHndBeg; + if (((hndBegBB->bbFlags & BBF_IMPORTED) == 0) && (impGetPendingBlockMember(hndBegBB) == 0)) + { // Construct the proper verification stack state // either empty or one that contains just // the Exception Object that we are dealing with @@ -16791,18 +16859,22 @@ void Compiler::impVerifyEHBlock(BasicBlock* block, bool isTryStart) // Queue up the handler for importing // impImportBlockPending(hndBegBB); + } - if (HBtab->HasFilter()) - { - /* @VERIFICATION : Ideally the end of filter state should get - propagated to the catch handler, this is an incompleteness, - but is not a security/compliance issue, since the only - interesting state is the 'thisInit' state. - */ + // Process the filter block, if we haven't already done so. + if (HBtab->HasFilter()) + { + /* @VERIFICATION : Ideally the end of filter state should get + propagated to the catch handler, this is an incompleteness, + but is not a security/compliance issue, since the only + interesting state is the 'thisInit' state. + */ - verCurrentState.esStackDepth = 0; + BasicBlock* filterBB = HBtab->ebdFilter; - BasicBlock* filterBB = HBtab->ebdFilter; + if (((filterBB->bbFlags & BBF_IMPORTED) == 0) && (impGetPendingBlockMember(filterBB) == 0)) + { + verCurrentState.esStackDepth = 0; // push catch arg the stack, spill to a temp if necessary // Note: can update HBtab->ebdFilter! @@ -16812,7 +16884,9 @@ void Compiler::impVerifyEHBlock(BasicBlock* block, bool isTryStart) impImportBlockPending(filterBB); } } - else if (verTrackObjCtorInitState && HBtab->HasFaultHandler()) + + // This seems redundant ....?? + if (verTrackObjCtorInitState && HBtab->HasFaultHandler()) { /* Recursively process the handler block */ @@ -17879,7 +17953,7 @@ void Compiler::impSpillCliqueSetMember(SpillCliqueDir predOrSucc, BasicBlock* bl * basic flowgraph has already been constructed and is passed in. */ -void Compiler::impImport(BasicBlock* method) +void Compiler::impImport() { #ifdef DEBUG if (verbose) @@ -17941,21 +18015,45 @@ void Compiler::impImport(BasicBlock* method) impPendingList = impPendingFree = nullptr; - /* Add the entry-point to the worker-list */ + // Skip leading internal blocks. + // These can arise from needing a leading scratch BB, from EH normalization, and from OSR entry redirects. + // + // We expect a linear flow to the first non-internal block. But not necessarily straght-line flow. + BasicBlock* entryBlock = fgFirstBB; - // Skip leading internal blocks. There can be one as a leading scratch BB, and more - // from EH normalization. - // NOTE: It might be possible to always just put fgFirstBB on the pending list, and let everything else just fall - // out. - for (; method->bbFlags & BBF_INTERNAL; method = method->bbNext) + while (entryBlock->bbFlags & BBF_INTERNAL) { - // Treat these as imported. - assert(method->bbJumpKind == BBJ_NONE); // We assume all the leading ones are fallthrough. - JITDUMP("Marking leading BBF_INTERNAL block " FMT_BB " as BBF_IMPORTED\n", method->bbNum); - method->bbFlags |= BBF_IMPORTED; + JITDUMP("Marking leading BBF_INTERNAL block " FMT_BB " as BBF_IMPORTED\n", entryBlock->bbNum); + entryBlock->bbFlags |= BBF_IMPORTED; + + if (entryBlock->bbJumpKind == BBJ_NONE) + { + entryBlock = entryBlock->bbNext; + } + else if (entryBlock->bbJumpKind == BBJ_ALWAYS) + { + // Only expected for OSR + assert(opts.IsOSR()); + entryBlock = entryBlock->bbJumpDest; + } + else + { + assert(!"unexpected bbJumpKind in entry sequence"); + } } - impImportBlockPending(method); + // Note for OSR we'd like to be able to verify this block must be + // stack empty, but won't know that until we've imported...so instead + // we'll BADCODE out if we mess up. + // + // (the concern here is that the runtime asks us to OSR a + // different IL version than the one that matched the method that + // triggered OSR). This should not happen but I might have the + // IL versioning stuff wrong. + // + // TODO: we also currently expect this block to be a join point, + // which we should verify over when we find jump targets. + impImportBlockPending(entryBlock); /* Import blocks in the worker-list until there are no more */ diff --git a/src/coreclr/src/jit/jitconfigvalues.h b/src/coreclr/src/jit/jitconfigvalues.h index f852fe2d3d53b2..95bd01cd43261c 100644 --- a/src/coreclr/src/jit/jitconfigvalues.h +++ b/src/coreclr/src/jit/jitconfigvalues.h @@ -385,6 +385,11 @@ CONFIG_INTEGER(JitGuardedDevirtualizationGuessUniqueInterface, W("JitGuardedDevi CONFIG_INTEGER(JitGuardedDevirtualizationGuessBestClass, W("JitGuardedDevirtualizationGuessBestClass"), 1) #endif // DEBUG +// Enable insertion of patchpoints into Tier0 methods with loops. +CONFIG_INTEGER(JitPatchpoint, W("JitPatchpoint"), 0) +// Initial patchpoint counter value +CONFIG_INTEGER(JitPatchpointInitialCounter, W("JitPatchpointInitialCounter"), 1000) + #if defined(DEBUG) // JitFunctionFile: Name of a file that contains a list of functions. If the currently compiled function is in the // file, certain other JIT config variables will be active. If the currently compiled function is not in the file, diff --git a/src/coreclr/src/jit/jitee.h b/src/coreclr/src/jit/jitee.h index 4f9df37e92de8c..473bfc58ff123c 100644 --- a/src/coreclr/src/jit/jitee.h +++ b/src/coreclr/src/jit/jitee.h @@ -39,7 +39,7 @@ class JitFlags #endif // !defined(TARGET_X86) - JIT_FLAG_UNUSED6 = 13, + JIT_FLAG_OSR = 13, // Generate alternate version for On Stack Replacement #if defined(TARGET_X86) || defined(TARGET_AMD64) diff --git a/src/coreclr/src/jit/jiteh.cpp b/src/coreclr/src/jit/jiteh.cpp index b17785c50b800f..4c9f39ebd6e68e 100644 --- a/src/coreclr/src/jit/jiteh.cpp +++ b/src/coreclr/src/jit/jiteh.cpp @@ -1212,6 +1212,25 @@ EHblkDsc* Compiler::ehInitTryBlockRange(BasicBlock* blk, BasicBlock** tryBeg, Ba return tryTab; } +/***************************************************************************** + * This method updates the value of ebdTryBeg + */ + +void Compiler::fgSetTryBeg(EHblkDsc* handlerTab, BasicBlock* newTryBeg) +{ + assert(newTryBeg != nullptr); + + // Check if we are going to change the existing value of endTryLast + // + if (handlerTab->ebdTryBeg != newTryBeg) + { + // Update the EH table with the newTryLast block + handlerTab->ebdTryBeg = newTryBeg; + + JITDUMP("EH#%u: New first block of try: " FMT_BB "\n", ehGetIndex(handlerTab), handlerTab->ebdTryBeg->bbNum); + } +} + /***************************************************************************** * This method updates the value of ebdTryLast. */ diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index d0d00b7331192e..4b5b0f77babd38 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -278,6 +278,17 @@ void Compiler::lvaInitTypeRef() CORINFO_CLASS_HANDLE clsHnd = info.compCompHnd->getArgClass(&info.compMethodInfo->locals, localsSig); lvaSetClass(varNum, clsHnd); } + + if (opts.IsOSR() && info.compPatchpointInfo->IsExposed(varNum)) + { + JITDUMP("-- V%02u is osr exposed\n", varNum); + varDsc->lvHasLdAddrOp = 1; + + if (varDsc->lvType != TYP_STRUCT) + { + lvaSetVarAddrExposed(varNum); + } + } } if ( // If there already exist unsafe buffers, don't mark more structs as unsafe @@ -1027,6 +1038,14 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo) lvaSetVarAddrExposed(varDscInfo->varNum); #endif // !TARGET_X86 } + + if (opts.IsOSR() && info.compPatchpointInfo->IsExposed(varDscInfo->varNum)) + { + JITDUMP("-- V%02u is osr exposed\n", varDscInfo->varNum); + varDsc->lvHasLdAddrOp = 1; + lvaSetVarAddrExposed(varDscInfo->varNum); + } + } // for each user arg #ifdef TARGET_ARM @@ -1829,6 +1848,12 @@ bool Compiler::StructPromotionHelper::CanPromoteStructVar(unsigned lclNum) return false; } + if (compiler->lvaIsOSRLocal(lclNum)) + { + JITDUMP(" struct promotion of V%02u is disabled because it is an OSR local\n", lclNum); + return false; + } + CORINFO_CLASS_HANDLE typeHnd = varDsc->lvVerTypeInfo.GetClassHandle(); return CanPromoteStructType(typeHnd); } @@ -4780,6 +4805,13 @@ void Compiler::lvaFixVirtualFrameOffsets() assert(varDsc->lvFramePointerBased); // We always access it RBP-relative. assert(!varDsc->lvMustInit); // It is never "must init". varDsc->lvStkOffs = codeGen->genCallerSPtoInitialSPdelta() + lvaLclSize(lvaOutgoingArgSpaceVar); + + // With OSR the new frame RBP points at the base of the new frame, but the virtual offsets + // are from the base of the old frame. Adjust. + if (opts.IsOSR()) + { + varDsc->lvStkOffs -= info.compPatchpointInfo->FpToSpDelta(); + } } #endif @@ -4788,9 +4820,11 @@ void Compiler::lvaFixVirtualFrameOffsets() #ifdef TARGET_XARCH delta += REGSIZE_BYTES; // pushed PC (return address) for x86/x64 + JITDUMP("--- delta bump %d for RA\n", REGSIZE_BYTES); if (codeGen->doubleAlignOrFramePointerUsed()) { + JITDUMP("--- delta bump %d for FP\n", REGSIZE_BYTES); delta += REGSIZE_BYTES; // pushed EBP (frame pointer) } #endif @@ -4798,6 +4832,7 @@ void Compiler::lvaFixVirtualFrameOffsets() if (!codeGen->isFramePointerUsed()) { // pushed registers, return address, and padding + JITDUMP("--- delta bump %d for RSP frame\n", codeGen->genTotalFrameSize()); delta += codeGen->genTotalFrameSize(); } #if defined(TARGET_ARM) @@ -4810,10 +4845,21 @@ void Compiler::lvaFixVirtualFrameOffsets() else { // FP is used. + JITDUMP("--- delta bump %d for RBP frame\n", codeGen->genTotalFrameSize() - codeGen->genSPtoFPdelta()); delta += codeGen->genTotalFrameSize() - codeGen->genSPtoFPdelta(); } #endif // TARGET_AMD64 + // For OSR, update the delta to reflect the current policy that + // RBP points at the base of the new frame, and RSP is relative to that RBP. + if (opts.IsOSR()) + { + JITDUMP("--- delta bump %d for OSR\n", info.compPatchpointInfo->FpToSpDelta()); + delta += info.compPatchpointInfo->FpToSpDelta(); + } + + JITDUMP("--- virtual stack offset to actual stack offset delta is %d\n", delta); + unsigned lclNum; for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) { @@ -4856,6 +4902,7 @@ void Compiler::lvaFixVirtualFrameOffsets() if (doAssignStkOffs) { + JITDUMP("-- V%02u was %d, now %d\n", lclNum, varDsc->lvStkOffs, varDsc->lvStkOffs + delta); varDsc->lvStkOffs += delta; #if DOUBLE_ALIGN @@ -4875,8 +4922,9 @@ void Compiler::lvaFixVirtualFrameOffsets() } #endif // On System V environments the stkOffs could be 0 for params passed in registers. - assert(codeGen->isFramePointerUsed() || - varDsc->lvStkOffs >= 0); // Only EBP relative references can have negative offsets + // + // For normal methods only EBP relative references can have negative offsets. + assert(codeGen->isFramePointerUsed() || varDsc->lvStkOffs >= 0); } } @@ -5578,7 +5626,9 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, */ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() { - int stkOffs = 0; + int stkOffs = 0; + int originalFrameStkOffs = 0; + int originalFrameSize = 0; // codeGen->isFramePointerUsed is set in regalloc phase. Initialize it to a guess for pre-regalloc layout. if (lvaDoneFrameLayout <= PRE_REGALLOC_FRAME_LAYOUT) { @@ -5613,6 +5663,15 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() // On x86/amd64, the return address has already been pushed by the call instruction in the caller. stkOffs -= TARGET_POINTER_SIZE; // return address; + // If we are an OSR method, we "inherit" the frame of the original method, + // and the stack is already double aligned on entry (since the return adddress push + // and any special alignment push happend "before"). + if (opts.IsOSR()) + { + originalFrameSize = info.compPatchpointInfo->FpToSpDelta(); + originalFrameStkOffs = stkOffs; + stkOffs -= originalFrameSize; + } // TODO-AMD64-CQ: for X64 eventually this should be pushed with all the other // calleeregs. When you fix this, you'll also need to fix // the assert at the bottom of this method @@ -5686,10 +5745,16 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() // boundary we would have to use movups when offset turns out unaligned. Movaps is more // performant than movups. unsigned calleeFPRegsSavedSize = genCountBits(compCalleeFPRegsSavedMask) * XMM_REGSIZE_BYTES; - if (calleeFPRegsSavedSize > 0 && ((stkOffs % XMM_REGSIZE_BYTES) != 0)) + + // For OSR the alignment pad computation should not take the original frame into account. + // Original frame size includes the pseudo-saved RA and so is always = 8 mod 16. + const int offsetForAlign = -(stkOffs + originalFrameSize); + + if ((calleeFPRegsSavedSize > 0) && ((offsetForAlign % XMM_REGSIZE_BYTES) != 0)) { // Take care of alignment - int alignPad = (int)AlignmentPad((unsigned)-stkOffs, XMM_REGSIZE_BYTES); + int alignPad = (int)AlignmentPad((unsigned)offsetForAlign, XMM_REGSIZE_BYTES); + assert(alignPad != 0); stkOffs -= alignPad; lvaIncrementFrameSize(alignPad); } @@ -5772,7 +5837,19 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() } #endif // JIT32_GCENCODER - if (lvaReportParamTypeArg()) + // OSR methods use the original method slot for the cached kept alive this, + // so don't need to allocate a slot on the new frame. + if (opts.IsOSR()) + { + if (lvaKeepAliveAndReportThis()) + { + CORINFO_PATCHPOINT_INFO* ppInfo = info.compPatchpointInfo; + assert(ppInfo->HasKeptAliveThis()); + int originalOffset = ppInfo->KeptAliveThisOffset(); + lvaCachedGenericContextArgOffs = originalFrameStkOffs + originalOffset; + } + } + else if (lvaReportParamTypeArg()) { #ifdef JIT32_GCENCODER noway_assert(codeGen->isFramePointerUsed()); @@ -5816,7 +5893,11 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() if (compGSReorderStackLayout) { assert(getNeedsGSSecurityCookie()); - stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaGSSecurityCookie, lvaLclSize(lvaGSSecurityCookie), stkOffs); + + if (!opts.IsOSR() || !info.compPatchpointInfo->HasSecurityCookie()) + { + stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaGSSecurityCookie, lvaLclSize(lvaGSSecurityCookie), stkOffs); + } } /* @@ -5909,7 +5990,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() In other words, we will not calculate the "base" address of the struct local if the promotion type is PROMOTION_TYPE_FIELD_DEPENDENT. */ - if (lvaIsFieldOfDependentlyPromotedStruct(varDsc)) + if (!opts.IsOSR() && lvaIsFieldOfDependentlyPromotedStruct(varDsc)) { continue; } @@ -5930,6 +6011,48 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() allocateOnFrame = false; } + // For OSR args and locals, we use the slots on the original frame. + // + // Note we must do this even for "non frame" locals, as we sometimes + // will refer to their memory homes. + if (lvaIsOSRLocal(lclNum)) + { + // Todo: verify it's ok to handle independent fields that are live on entry + // to the OSR method and in memory at entry this way... + if (varDsc->lvIsStructField) + { + const unsigned parentLclNum = varDsc->lvParentLcl; + assert(parentLclNum < info.compLocalsCount); + + LclVarDsc* parentVarDsc = &lvaTable[parentLclNum]; + int originalParentOffset = info.compPatchpointInfo->Offset(parentLclNum); + int parentOffset = originalFrameStkOffs + originalParentOffset; + int fieldOffset = parentOffset + varDsc->lvFldOffset; + + JITDUMP("---OSR--- V%02u (on old frame) (promoted field of V%02u) new virt offset %d\n", lclNum, + parentLclNum, fieldOffset); + + lvaTable[lclNum].lvStkOffs = fieldOffset; + } + else + { + assert(lclNum < info.compLocalsCount); + + // Add frampointer-relative offset of this OSR live local in the original frame + // to the offset of original frame in our new frame. + int originalOffset = info.compPatchpointInfo->Offset(lclNum); + int offset = originalFrameStkOffs + originalOffset; + + JITDUMP("---OSR--- V%02u (on old frame) old rbp offset %d old frame offset %d new virt offset %d\n", + lclNum, originalOffset, originalFrameStkOffs, offset); + + lvaTable[lclNum].lvStkOffs = offset; + continue; + } + + continue; + } + /* Ignore variables that are not on the stack frame */ if (!allocateOnFrame) @@ -5951,7 +6074,21 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() } else if (lvaGSSecurityCookie == lclNum && getNeedsGSSecurityCookie()) { - continue; // This is allocated outside of this loop. + // Special case for OSR. If the original method had a cookie, + // we use its slot on the original frame. + if (opts.IsOSR() && info.compPatchpointInfo->HasSecurityCookie()) + { + int originalOffset = info.compPatchpointInfo->SecurityCookieOffset(); + int offset = originalFrameStkOffs + originalOffset; + + JITDUMP("---OSR--- V%02u (on old frame, security cookie) old rbp offset %d old frame offset %d new " + "virt offset %d\n", + lclNum, originalOffset, originalFrameStkOffs, offset); + + lvaTable[lclNum].lvStkOffs = offset; + } + + continue; } // These need to be located as the very first variables (highest memory address) @@ -6144,8 +6281,11 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() if (getNeedsGSSecurityCookie() && !compGSReorderStackLayout) { - // LOCALLOC used, but we have no unsafe buffer. Allocated cookie last, close to localloc buffer. - stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaGSSecurityCookie, lvaLclSize(lvaGSSecurityCookie), stkOffs); + if (!opts.IsOSR() || !info.compPatchpointInfo->HasSecurityCookie()) + { + // LOCALLOC used, but we have no unsafe buffer. Allocated cookie last, close to localloc buffer. + stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaGSSecurityCookie, lvaLclSize(lvaGSSecurityCookie), stkOffs); + } } if (tempsAllocated == false) @@ -6275,7 +6415,8 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() pushedCount += 1; // pushed PC (return address) #endif - noway_assert(compLclFrameSize == (unsigned)-(stkOffs + (pushedCount * (int)TARGET_POINTER_SIZE))); + noway_assert(compLclFrameSize + originalFrameSize == + (unsigned)-(stkOffs + (pushedCount * (int)TARGET_POINTER_SIZE))); } int Compiler::lvaAllocLocalAndSetVirtualOffset(unsigned lclNum, unsigned size, int stkOffs) @@ -7216,6 +7357,8 @@ int Compiler::lvaGetCallerSPRelativeOffset(unsigned varNum) int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased) const { + // Todo: this should not be called for OSR + // methods -- instead we shoud fetch the offsets from the ppInfo assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT); if (isFpBased) diff --git a/src/coreclr/src/jit/lsrabuild.cpp b/src/coreclr/src/jit/lsrabuild.cpp index c45aad77aec9d1..91ac9fdf32ba75 100644 --- a/src/coreclr/src/jit/lsrabuild.cpp +++ b/src/coreclr/src/jit/lsrabuild.cpp @@ -1732,15 +1732,7 @@ BasicBlock* getNonEmptyBlock(BasicBlock* block) { while (block != nullptr && block->GetFirstLIRNode() == nullptr) { - BasicBlock* nextBlock = block->bbNext; - // Note that here we use the version of NumSucc that does not take a compiler. - // That way this doesn't have to take a compiler, or be an instance method, e.g. of LinearScan. - // If we have an empty block, it must have jump type BBJ_NONE or BBJ_ALWAYS, in which - // case we don't need the version that takes a compiler. - assert(block->NumSucc() == 1 && ((block->bbJumpKind == BBJ_ALWAYS) || (block->bbJumpKind == BBJ_NONE))); - // sometimes the first block is empty and ends with an uncond branch - // assert( block->GetSucc(0) == nextBlock); - block = nextBlock; + block = block->GetUniqueSucc(); } assert(block != nullptr && block->GetFirstLIRNode() != nullptr); return block; @@ -1785,12 +1777,21 @@ void LinearScan::insertZeroInitRefPositions() Interval* interval = getIntervalForLocalVar(varIndex); if (compiler->info.compInitMem || varTypeIsGC(varDsc->TypeGet())) { + varDsc->lvMustInit = true; + + // OSR will handle init of locals and promoted fields thereof + if (compiler->lvaIsOSRLocal(compiler->lvaTrackedIndexToLclNum(varIndex))) + { + JITDUMP(" will be initialized by OSR\n"); + // setIntervalAsSpilled(interval); + varDsc->lvMustInit = false; + } + JITDUMP(" creating ZeroInit\n"); GenTree* firstNode = getNonEmptyBlock(compiler->fgFirstBB)->firstNode(); RefPosition* pos = newRefPosition(interval, MinLocation, RefTypeZeroInit, firstNode, allRegs(interval->registerType)); pos->setRegOptional(true); - varDsc->lvMustInit = true; } else { diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 2b56413f955dec..36775528d3ee03 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -7943,14 +7943,26 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa // Remove the call fgRemoveStmt(block, lastStmt); - // Set the loop edge. Ensure we have a scratch block and then target the - // next block. Loop detection needs to see a pred out of the loop, so - // mark the scratch block BBF_DONT_REMOVE to prevent empty block removal - // on it. - fgEnsureFirstBBisScratch(); - fgFirstBB->bbFlags |= BBF_DONT_REMOVE; + // Set the loop edge. + if (opts.IsOSR()) + { + // Todo: this may not look like a viable loop header. + // Might need the moral equivalent of a scratch BB. + block->bbJumpDest = fgEntryBB; + } + else + { + // Ensure we have a scratch block and then target the next + // block. Loop detection needs to see a pred out of the loop, + // so mark the scratch block BBF_DONT_REMOVE to prevent empty + // block removal on it. + fgEnsureFirstBBisScratch(); + fgFirstBB->bbFlags |= BBF_DONT_REMOVE; + block->bbJumpDest = fgFirstBB->bbNext; + } + + // Finish hooking things up. block->bbJumpKind = BBJ_ALWAYS; - block->bbJumpDest = fgFirstBB->bbNext; block->bbJumpDest->bbFlags |= BBF_JMP_TARGET; fgAddRefPred(block->bbJumpDest, block); block->bbFlags &= ~BBF_HAS_JMP; diff --git a/src/coreclr/src/jit/patchpoint.cpp b/src/coreclr/src/jit/patchpoint.cpp new file mode 100644 index 00000000000000..681bb774ae0269 --- /dev/null +++ b/src/coreclr/src/jit/patchpoint.cpp @@ -0,0 +1,238 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "jitpch.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif + +//------------------------------------------------------------------------ +// PatchpointTransformer +// +// Insert patchpoint checks into Tier0 methods, based on locations identified +// during importation (see impImportBlockCode). +// +// Policy decisions implemented here: +// +// * One counter per stack frame, regardless of the number of patchpoints. +// * Shared counter value initialized to zero in prolog. +// * Patchpoint trees fully expanded into jit IR. Deferring expansion could +// lead to more compact code and lessen size overhead for Tier0. +// +// Workarounds and limitations: +// +// * no patchpoints in handler regions +// * no patchpoints for localloc methods +// * no patchpoints in try regions (workaround) +// * no patchpoints for synchronized methods (workaround) +// +class PatchpointTransformer +{ + unsigned ppCounterLclNum; + const int HIGH_PROBABILITY = 99; + Compiler* compiler; + +public: + PatchpointTransformer(Compiler* compiler) : compiler(compiler) + { + ppCounterLclNum = compiler->lvaGrabTemp(true DEBUGARG("patchpoint counter")); + compiler->lvaTable[ppCounterLclNum].lvType = TYP_INT; + } + + //------------------------------------------------------------------------ + // Run: run transformation for each block. + // + // Returns: + // Number of patchpoints transformed. + int Run() + { + // If the first block is a patchpoint, insert a scratch block. + if (compiler->fgFirstBB->bbFlags & BBF_PATCHPOINT) + { + compiler->fgEnsureFirstBBisScratch(); + } + + BasicBlock* block = compiler->fgFirstBB; + TransformEntry(block); + + int count = 0; + for (block = block->bbNext; block != nullptr; block = block->bbNext) + { + if (block->bbFlags & BBF_PATCHPOINT) + { + // If block is in a handler region, don't insert a patchpoint. + // We can't OSR from funclets. + // + // TODO: check this earlier, somehow, and fall back to fully + // optimizing the method (ala QJFL=0). + if (compiler->ehGetBlockHndDsc(block) != nullptr) + { + JITDUMP("Patchpoint: skipping patchpoint for " FMT_BB " as it is in a handler\n", block->bbNum); + continue; + } + + JITDUMP("Patchpoint: instrumenting " FMT_BB "\n", block->bbNum); + assert(block != compiler->fgFirstBB); + TransformBlock(block); + count++; + } + } + + return count; + } + +private: + //------------------------------------------------------------------------ + // CreateAndInsertBasicBlock: ask compiler to create new basic block. + // and insert in into the basic block list. + // + // Arguments: + // jumpKind - jump kind for the new basic block + // insertAfter - basic block, after which compiler has to insert the new one. + // + // Return Value: + // new basic block. + BasicBlock* CreateAndInsertBasicBlock(BBjumpKinds jumpKind, BasicBlock* insertAfter) + { + BasicBlock* block = compiler->fgNewBBafter(jumpKind, insertAfter, true); + if ((insertAfter->bbFlags & BBF_INTERNAL) == 0) + { + block->bbFlags &= ~BBF_INTERNAL; + block->bbFlags |= BBF_IMPORTED; + } + return block; + } + + //------------------------------------------------------------------------ + // TransformBlock: expand current block to include patchpoint logic. + // + // S; + // + // ==> + // + // if (--ppCounter <= 0) + // { + // ppHelper(&ppCounter, ilOffset); + // } + // S; + // + void TransformBlock(BasicBlock* block) + { + // Capture the IL offset + IL_OFFSET ilOffset = block->bbCodeOffs; + assert(ilOffset != BAD_IL_OFFSET); + + // Current block now becomes the test block + BasicBlock* remainderBlock = compiler->fgSplitBlockAtBeginning(block); + BasicBlock* helperBlock = CreateAndInsertBasicBlock(BBJ_NONE, block); + + // Update flow and flags + block->bbJumpKind = BBJ_COND; + block->bbJumpDest = remainderBlock; + helperBlock->bbFlags |= BBF_BACKWARD_JUMP; + + // Update weights + remainderBlock->inheritWeight(block); + helperBlock->inheritWeightPercentage(block, 100 - HIGH_PROBABILITY); + + // Fill in test block + // + // --ppCounter; + GenTree* ppCounterBefore = compiler->gtNewLclvNode(ppCounterLclNum, TYP_INT); + GenTree* ppCounterAfter = compiler->gtNewLclvNode(ppCounterLclNum, TYP_INT); + GenTree* one = compiler->gtNewIconNode(1, TYP_INT); + GenTree* ppCounterSub = compiler->gtNewOperNode(GT_SUB, TYP_INT, ppCounterBefore, one); + GenTree* ppCounterAsg = compiler->gtNewOperNode(GT_ASG, TYP_INT, ppCounterAfter, ppCounterSub); + + compiler->fgNewStmtAtEnd(block, ppCounterAsg); + + // if (ppCounter > 0), bypass helper call + GenTree* ppCounterUpdated = compiler->gtNewLclvNode(ppCounterLclNum, TYP_INT); + GenTree* zero = compiler->gtNewIconNode(0, TYP_INT); + GenTree* compare = compiler->gtNewOperNode(GT_GT, TYP_INT, ppCounterUpdated, zero); + GenTree* jmp = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, compare); + + compiler->fgNewStmtAtEnd(block, jmp); + + // Fill in helper block + // + // call PPHelper(&ppCounter, ilOffset) + GenTree* ilOffsetNode = compiler->gtNewIconNode(ilOffset, TYP_INT); + GenTree* ppCounterRef = compiler->gtNewLclvNode(ppCounterLclNum, TYP_INT); + GenTree* ppCounterAddr = compiler->gtNewOperNode(GT_ADDR, TYP_I_IMPL, ppCounterRef); + GenTreeCall::Use* helperArgs = compiler->gtNewCallArgs(ppCounterAddr, ilOffsetNode); + GenTreeCall* helperCall = compiler->gtNewHelperCallNode(CORINFO_HELP_PATCHPOINT, TYP_VOID, helperArgs); + + compiler->fgNewStmtAtEnd(helperBlock, helperCall); + } + + // ppCounter = + void TransformEntry(BasicBlock* block) + { + assert((block->bbFlags & BBF_PATCHPOINT) == 0); + + int initialCounterValue = JitConfig.JitPatchpointInitialCounter(); + + if (initialCounterValue < 0) + { + initialCounterValue = 0; + } + + GenTree* initialCounterNode = compiler->gtNewIconNode(initialCounterValue, TYP_INT); + GenTree* ppCounterRef = compiler->gtNewLclvNode(ppCounterLclNum, TYP_INT); + GenTree* ppCounterAsg = compiler->gtNewOperNode(GT_ASG, TYP_INT, ppCounterRef, initialCounterNode); + + compiler->fgNewStmtNearEnd(block, ppCounterAsg); + } +}; + +//------------------------------------------------------------------------ +// fgTransformPatchpoints: expansion of patchpoints into control flow. +// +// Notes: +// +// Patchpoints are placed in the JIT IR during importation, and get expanded +// here into normal JIT IR. +// +void Compiler::fgTransformPatchpoints() +{ + JITDUMP("\n*************** in fgTransformPatchpoints\n"); + + if (!doesMethodHavePatchpoints()) + { + JITDUMP(" -- no patchpoints to transform\n"); + return; + } + + // We should only be adding patchpoints at Tier0, so should not be in an inlinee + assert(!compIsForInlining()); + + // We currently can't do OSR in methods with localloc. + // Such methods don't have a fixed relationship between frame and stack pointers. + // + // This is true whether or not the localloc was executed in the original method. + // + // TODO: handle this case, or else check this earlier and fall back to fully + // optimizing the method (ala QJFL=0). + if (compLocallocUsed) + { + JITDUMP(" -- unable to handle methods with localloc\n"); + return; + } + + // We currently can't do OSR in synchronized methods. We need to alter + // the logic in fgAddSyncMethodEnterExit for OSR to not try and obtain the + // monitor (since the original method will have done so) and set the monitor + // obtained flag to true (or reuse the original method slot value). + if ((info.compFlags & CORINFO_FLG_SYNCH) != 0) + { + JITDUMP(" -- unable to handle synchronized methods\n"); + return; + } + + PatchpointTransformer ppTransformer(this); + int count = ppTransformer.Run(); + JITDUMP("\n*************** After fgTransformPatchpoints() [%d patchpoints transformed]\n", count); + INDEBUG(if (verbose) { fgDispBasicBlocks(true); }); +} diff --git a/src/coreclr/src/jit/rangecheck.cpp b/src/coreclr/src/jit/rangecheck.cpp index 651c5e2c2f4288..52530441888ac2 100644 --- a/src/coreclr/src/jit/rangecheck.cpp +++ b/src/coreclr/src/jit/rangecheck.cpp @@ -763,7 +763,7 @@ void RangeCheck::MergeAssertion(BasicBlock* block, GenTree* op, Range* pRange DE assertions = block->bbAssertionIn; } - if (!BitVecOps::MayBeUninit(assertions)) + if (!BitVecOps::MayBeUninit(assertions) && (m_pCompiler->GetAssertionCount() > 0)) { // Perform the merge step to fine tune the range value. MergeEdgeAssertions(op->AsLclVarCommon(), assertions, pRange); @@ -847,7 +847,7 @@ Range RangeCheck::ComputeRangeForLocalDef(BasicBlock* block, } #endif Range range = GetRange(ssaDef->GetBlock(), ssaDef->GetAssignment()->gtGetOp2(), monIncreasing DEBUGARG(indent)); - if (!BitVecOps::MayBeUninit(block->bbAssertionIn)) + if (!BitVecOps::MayBeUninit(block->bbAssertionIn) && (m_pCompiler->GetAssertionCount() > 0)) { JITDUMP("Merge assertions from " FMT_BB ":%s for assignment about [%06d]\n", block->bbNum, BitVecOps::ToString(m_pCompiler->apTraits, block->bbAssertionIn), diff --git a/src/coreclr/src/jit/valuenum.cpp b/src/coreclr/src/jit/valuenum.cpp index c1d799f603bc63..5edd8c4481a9fa 100644 --- a/src/coreclr/src/jit/valuenum.cpp +++ b/src/coreclr/src/jit/valuenum.cpp @@ -5694,9 +5694,17 @@ void Compiler::fgValueNumber() // these are variables that are read before being initialized (at least on some control flow paths) // if they are not must-init, then they get VNF_InitVal(i), as with the param case.) - bool isZeroed = (info.compInitMem || varDsc->lvMustInit); - ValueNum initVal = ValueNumStore::NoVN; // We must assign a new value to initVal - var_types typ = varDsc->TypeGet(); + bool isZeroed = (info.compInitMem || varDsc->lvMustInit); + + // For OSR, locals or promoted fields of locals may be missing the initial def + // because of partial importation. We can't assume they are zero. + if (lvaIsOSRLocal(lclNum)) + { + isZeroed = false; + } + + ValueNum initVal = ValueNumStore::NoVN; // We must assign a new value to initVal + var_types typ = varDsc->TypeGet(); switch (typ) { @@ -5910,7 +5918,8 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk) BasicBlock::MemoryPhiArg* phiArgs = blk->bbMemorySsaPhiFunc[memoryKind]; assert(phiArgs != BasicBlock::EmptyMemoryPhiDef); // There should be > 1 args to a phi. - assert(phiArgs->m_nextArg != nullptr); + // But OSR might leave around "dead" try entry blocks... + assert((phiArgs->m_nextArg != nullptr) || opts.IsOSR()); ValueNum phiAppVN = vnStore->VNForIntCon(phiArgs->GetSsaNum()); JITDUMP(" Building phi application: $%x = SSA# %d.\n", phiAppVN, phiArgs->GetSsaNum()); bool allSame = true; diff --git a/src/coreclr/src/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/src/tools/Common/JitInterface/CorInfoBase.cs index 850cb2450f3a5c..17cef6d7f457da 100644 --- a/src/coreclr/src/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/src/tools/Common/JitInterface/CorInfoBase.cs @@ -200,6 +200,8 @@ unsafe partial class CorInfoImpl [UnmanagedFunctionPointerAttribute(default(CallingConvention))] delegate void __setVars(IntPtr _this, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftn, uint cVars, NativeVarInfo* vars); [UnmanagedFunctionPointerAttribute(default(CallingConvention))] + delegate void __setPatchpointInfo(IntPtr _this, IntPtr* ppException, CORINFO_PATCHPOINT_INFO* patchpointInfo); + [UnmanagedFunctionPointerAttribute(default(CallingConvention))] delegate void* __allocateArray(IntPtr _this, IntPtr* ppException, UIntPtr cBytes); [UnmanagedFunctionPointerAttribute(default(CallingConvention))] delegate void __freeArray(IntPtr _this, IntPtr* ppException, void* array); @@ -1641,6 +1643,19 @@ static void _setVars(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRU } } + static void _setPatchpointInfo(IntPtr thisHandle, IntPtr* ppException, CORINFO_PATCHPOINT_INFO* patchpointInfo) + { + var _this = GetThis(thisHandle); + try + { + _this.setPatchpointInfo(patchpointInfo); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + } + } + static void* _allocateArray(IntPtr thisHandle, IntPtr* ppException, UIntPtr cBytes) { var _this = GetThis(thisHandle); @@ -2643,8 +2658,8 @@ static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, ref CORJIT_FLAG static IntPtr GetUnmanagedCallbacks(out Object keepAlive) { - IntPtr * callbacks = (IntPtr *)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 167); - Object[] delegates = new Object[167]; + IntPtr * callbacks = (IntPtr *)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 168); + Object[] delegates = new Object[168]; var d0 = new __getMethodAttribs(_getMethodAttribs); callbacks[0] = Marshal.GetFunctionPointerForDelegate(d0); @@ -2928,225 +2943,228 @@ static IntPtr GetUnmanagedCallbacks(out Object keepAlive) var d93 = new __setVars(_setVars); callbacks[93] = Marshal.GetFunctionPointerForDelegate(d93); delegates[93] = d93; - var d94 = new __allocateArray(_allocateArray); + var d94 = new __setPatchpointInfo(_setPatchpointInfo); callbacks[94] = Marshal.GetFunctionPointerForDelegate(d94); delegates[94] = d94; - var d95 = new __freeArray(_freeArray); + var d95 = new __allocateArray(_allocateArray); callbacks[95] = Marshal.GetFunctionPointerForDelegate(d95); delegates[95] = d95; - var d96 = new __getArgNext(_getArgNext); + var d96 = new __freeArray(_freeArray); callbacks[96] = Marshal.GetFunctionPointerForDelegate(d96); delegates[96] = d96; - var d97 = new __getArgType(_getArgType); + var d97 = new __getArgNext(_getArgNext); callbacks[97] = Marshal.GetFunctionPointerForDelegate(d97); delegates[97] = d97; - var d98 = new __getArgClass(_getArgClass); + var d98 = new __getArgType(_getArgType); callbacks[98] = Marshal.GetFunctionPointerForDelegate(d98); delegates[98] = d98; - var d99 = new __getHFAType(_getHFAType); + var d99 = new __getArgClass(_getArgClass); callbacks[99] = Marshal.GetFunctionPointerForDelegate(d99); delegates[99] = d99; - var d100 = new __GetErrorHRESULT(_GetErrorHRESULT); + var d100 = new __getHFAType(_getHFAType); callbacks[100] = Marshal.GetFunctionPointerForDelegate(d100); delegates[100] = d100; - var d101 = new __GetErrorMessage(_GetErrorMessage); + var d101 = new __GetErrorHRESULT(_GetErrorHRESULT); callbacks[101] = Marshal.GetFunctionPointerForDelegate(d101); delegates[101] = d101; - var d102 = new __FilterException(_FilterException); + var d102 = new __GetErrorMessage(_GetErrorMessage); callbacks[102] = Marshal.GetFunctionPointerForDelegate(d102); delegates[102] = d102; - var d103 = new __HandleException(_HandleException); + var d103 = new __FilterException(_FilterException); callbacks[103] = Marshal.GetFunctionPointerForDelegate(d103); delegates[103] = d103; - var d104 = new __ThrowExceptionForJitResult(_ThrowExceptionForJitResult); + var d104 = new __HandleException(_HandleException); callbacks[104] = Marshal.GetFunctionPointerForDelegate(d104); delegates[104] = d104; - var d105 = new __ThrowExceptionForHelper(_ThrowExceptionForHelper); + var d105 = new __ThrowExceptionForJitResult(_ThrowExceptionForJitResult); callbacks[105] = Marshal.GetFunctionPointerForDelegate(d105); delegates[105] = d105; - var d106 = new __runWithErrorTrap(_runWithErrorTrap); + var d106 = new __ThrowExceptionForHelper(_ThrowExceptionForHelper); callbacks[106] = Marshal.GetFunctionPointerForDelegate(d106); delegates[106] = d106; - var d107 = new __getEEInfo(_getEEInfo); + var d107 = new __runWithErrorTrap(_runWithErrorTrap); callbacks[107] = Marshal.GetFunctionPointerForDelegate(d107); delegates[107] = d107; - var d108 = new __getJitTimeLogFilename(_getJitTimeLogFilename); + var d108 = new __getEEInfo(_getEEInfo); callbacks[108] = Marshal.GetFunctionPointerForDelegate(d108); delegates[108] = d108; - var d109 = new __getMethodDefFromMethod(_getMethodDefFromMethod); + var d109 = new __getJitTimeLogFilename(_getJitTimeLogFilename); callbacks[109] = Marshal.GetFunctionPointerForDelegate(d109); delegates[109] = d109; - var d110 = new __getMethodName(_getMethodName); + var d110 = new __getMethodDefFromMethod(_getMethodDefFromMethod); callbacks[110] = Marshal.GetFunctionPointerForDelegate(d110); delegates[110] = d110; - var d111 = new __getMethodNameFromMetadata(_getMethodNameFromMetadata); + var d111 = new __getMethodName(_getMethodName); callbacks[111] = Marshal.GetFunctionPointerForDelegate(d111); delegates[111] = d111; - var d112 = new __getMethodHash(_getMethodHash); + var d112 = new __getMethodNameFromMetadata(_getMethodNameFromMetadata); callbacks[112] = Marshal.GetFunctionPointerForDelegate(d112); delegates[112] = d112; - var d113 = new __findNameOfToken(_findNameOfToken); + var d113 = new __getMethodHash(_getMethodHash); callbacks[113] = Marshal.GetFunctionPointerForDelegate(d113); delegates[113] = d113; - var d114 = new __getSystemVAmd64PassStructInRegisterDescriptor(_getSystemVAmd64PassStructInRegisterDescriptor); + var d114 = new __findNameOfToken(_findNameOfToken); callbacks[114] = Marshal.GetFunctionPointerForDelegate(d114); delegates[114] = d114; - var d115 = new __getThreadTLSIndex(_getThreadTLSIndex); + var d115 = new __getSystemVAmd64PassStructInRegisterDescriptor(_getSystemVAmd64PassStructInRegisterDescriptor); callbacks[115] = Marshal.GetFunctionPointerForDelegate(d115); delegates[115] = d115; - var d116 = new __getInlinedCallFrameVptr(_getInlinedCallFrameVptr); + var d116 = new __getThreadTLSIndex(_getThreadTLSIndex); callbacks[116] = Marshal.GetFunctionPointerForDelegate(d116); delegates[116] = d116; - var d117 = new __getAddrOfCaptureThreadGlobal(_getAddrOfCaptureThreadGlobal); + var d117 = new __getInlinedCallFrameVptr(_getInlinedCallFrameVptr); callbacks[117] = Marshal.GetFunctionPointerForDelegate(d117); delegates[117] = d117; - var d118 = new __getHelperFtn(_getHelperFtn); + var d118 = new __getAddrOfCaptureThreadGlobal(_getAddrOfCaptureThreadGlobal); callbacks[118] = Marshal.GetFunctionPointerForDelegate(d118); delegates[118] = d118; - var d119 = new __getFunctionEntryPoint(_getFunctionEntryPoint); + var d119 = new __getHelperFtn(_getHelperFtn); callbacks[119] = Marshal.GetFunctionPointerForDelegate(d119); delegates[119] = d119; - var d120 = new __getFunctionFixedEntryPoint(_getFunctionFixedEntryPoint); + var d120 = new __getFunctionEntryPoint(_getFunctionEntryPoint); callbacks[120] = Marshal.GetFunctionPointerForDelegate(d120); delegates[120] = d120; - var d121 = new __getMethodSync(_getMethodSync); + var d121 = new __getFunctionFixedEntryPoint(_getFunctionFixedEntryPoint); callbacks[121] = Marshal.GetFunctionPointerForDelegate(d121); delegates[121] = d121; - var d122 = new __getLazyStringLiteralHelper(_getLazyStringLiteralHelper); + var d122 = new __getMethodSync(_getMethodSync); callbacks[122] = Marshal.GetFunctionPointerForDelegate(d122); delegates[122] = d122; - var d123 = new __embedModuleHandle(_embedModuleHandle); + var d123 = new __getLazyStringLiteralHelper(_getLazyStringLiteralHelper); callbacks[123] = Marshal.GetFunctionPointerForDelegate(d123); delegates[123] = d123; - var d124 = new __embedClassHandle(_embedClassHandle); + var d124 = new __embedModuleHandle(_embedModuleHandle); callbacks[124] = Marshal.GetFunctionPointerForDelegate(d124); delegates[124] = d124; - var d125 = new __embedMethodHandle(_embedMethodHandle); + var d125 = new __embedClassHandle(_embedClassHandle); callbacks[125] = Marshal.GetFunctionPointerForDelegate(d125); delegates[125] = d125; - var d126 = new __embedFieldHandle(_embedFieldHandle); + var d126 = new __embedMethodHandle(_embedMethodHandle); callbacks[126] = Marshal.GetFunctionPointerForDelegate(d126); delegates[126] = d126; - var d127 = new __embedGenericHandle(_embedGenericHandle); + var d127 = new __embedFieldHandle(_embedFieldHandle); callbacks[127] = Marshal.GetFunctionPointerForDelegate(d127); delegates[127] = d127; - var d128 = new __getLocationOfThisType(_getLocationOfThisType); + var d128 = new __embedGenericHandle(_embedGenericHandle); callbacks[128] = Marshal.GetFunctionPointerForDelegate(d128); delegates[128] = d128; - var d129 = new __getAddressOfPInvokeTarget(_getAddressOfPInvokeTarget); + var d129 = new __getLocationOfThisType(_getLocationOfThisType); callbacks[129] = Marshal.GetFunctionPointerForDelegate(d129); delegates[129] = d129; - var d130 = new __GetCookieForPInvokeCalliSig(_GetCookieForPInvokeCalliSig); + var d130 = new __getAddressOfPInvokeTarget(_getAddressOfPInvokeTarget); callbacks[130] = Marshal.GetFunctionPointerForDelegate(d130); delegates[130] = d130; - var d131 = new __canGetCookieForPInvokeCalliSig(_canGetCookieForPInvokeCalliSig); + var d131 = new __GetCookieForPInvokeCalliSig(_GetCookieForPInvokeCalliSig); callbacks[131] = Marshal.GetFunctionPointerForDelegate(d131); delegates[131] = d131; - var d132 = new __getJustMyCodeHandle(_getJustMyCodeHandle); + var d132 = new __canGetCookieForPInvokeCalliSig(_canGetCookieForPInvokeCalliSig); callbacks[132] = Marshal.GetFunctionPointerForDelegate(d132); delegates[132] = d132; - var d133 = new __GetProfilingHandle(_GetProfilingHandle); + var d133 = new __getJustMyCodeHandle(_getJustMyCodeHandle); callbacks[133] = Marshal.GetFunctionPointerForDelegate(d133); delegates[133] = d133; - var d134 = new __getCallInfo(_getCallInfo); + var d134 = new __GetProfilingHandle(_GetProfilingHandle); callbacks[134] = Marshal.GetFunctionPointerForDelegate(d134); delegates[134] = d134; - var d135 = new __canAccessFamily(_canAccessFamily); + var d135 = new __getCallInfo(_getCallInfo); callbacks[135] = Marshal.GetFunctionPointerForDelegate(d135); delegates[135] = d135; - var d136 = new __isRIDClassDomainID(_isRIDClassDomainID); + var d136 = new __canAccessFamily(_canAccessFamily); callbacks[136] = Marshal.GetFunctionPointerForDelegate(d136); delegates[136] = d136; - var d137 = new __getClassDomainID(_getClassDomainID); + var d137 = new __isRIDClassDomainID(_isRIDClassDomainID); callbacks[137] = Marshal.GetFunctionPointerForDelegate(d137); delegates[137] = d137; - var d138 = new __getFieldAddress(_getFieldAddress); + var d138 = new __getClassDomainID(_getClassDomainID); callbacks[138] = Marshal.GetFunctionPointerForDelegate(d138); delegates[138] = d138; - var d139 = new __getStaticFieldCurrentClass(_getStaticFieldCurrentClass); + var d139 = new __getFieldAddress(_getFieldAddress); callbacks[139] = Marshal.GetFunctionPointerForDelegate(d139); delegates[139] = d139; - var d140 = new __getVarArgsHandle(_getVarArgsHandle); + var d140 = new __getStaticFieldCurrentClass(_getStaticFieldCurrentClass); callbacks[140] = Marshal.GetFunctionPointerForDelegate(d140); delegates[140] = d140; - var d141 = new __canGetVarArgsHandle(_canGetVarArgsHandle); + var d141 = new __getVarArgsHandle(_getVarArgsHandle); callbacks[141] = Marshal.GetFunctionPointerForDelegate(d141); delegates[141] = d141; - var d142 = new __constructStringLiteral(_constructStringLiteral); + var d142 = new __canGetVarArgsHandle(_canGetVarArgsHandle); callbacks[142] = Marshal.GetFunctionPointerForDelegate(d142); delegates[142] = d142; - var d143 = new __emptyStringLiteral(_emptyStringLiteral); + var d143 = new __constructStringLiteral(_constructStringLiteral); callbacks[143] = Marshal.GetFunctionPointerForDelegate(d143); delegates[143] = d143; - var d144 = new __getFieldThreadLocalStoreID(_getFieldThreadLocalStoreID); + var d144 = new __emptyStringLiteral(_emptyStringLiteral); callbacks[144] = Marshal.GetFunctionPointerForDelegate(d144); delegates[144] = d144; - var d145 = new __setOverride(_setOverride); + var d145 = new __getFieldThreadLocalStoreID(_getFieldThreadLocalStoreID); callbacks[145] = Marshal.GetFunctionPointerForDelegate(d145); delegates[145] = d145; - var d146 = new __addActiveDependency(_addActiveDependency); + var d146 = new __setOverride(_setOverride); callbacks[146] = Marshal.GetFunctionPointerForDelegate(d146); delegates[146] = d146; - var d147 = new __GetDelegateCtor(_GetDelegateCtor); + var d147 = new __addActiveDependency(_addActiveDependency); callbacks[147] = Marshal.GetFunctionPointerForDelegate(d147); delegates[147] = d147; - var d148 = new __MethodCompileComplete(_MethodCompileComplete); + var d148 = new __GetDelegateCtor(_GetDelegateCtor); callbacks[148] = Marshal.GetFunctionPointerForDelegate(d148); delegates[148] = d148; - var d149 = new __getTailCallCopyArgsThunk(_getTailCallCopyArgsThunk); + var d149 = new __MethodCompileComplete(_MethodCompileComplete); callbacks[149] = Marshal.GetFunctionPointerForDelegate(d149); delegates[149] = d149; - var d150 = new __convertPInvokeCalliToCall(_convertPInvokeCalliToCall); + var d150 = new __getTailCallCopyArgsThunk(_getTailCallCopyArgsThunk); callbacks[150] = Marshal.GetFunctionPointerForDelegate(d150); delegates[150] = d150; - var d151 = new __allocMem(_allocMem); + var d151 = new __convertPInvokeCalliToCall(_convertPInvokeCalliToCall); callbacks[151] = Marshal.GetFunctionPointerForDelegate(d151); delegates[151] = d151; - var d152 = new __reserveUnwindInfo(_reserveUnwindInfo); + var d152 = new __allocMem(_allocMem); callbacks[152] = Marshal.GetFunctionPointerForDelegate(d152); delegates[152] = d152; - var d153 = new __allocUnwindInfo(_allocUnwindInfo); + var d153 = new __reserveUnwindInfo(_reserveUnwindInfo); callbacks[153] = Marshal.GetFunctionPointerForDelegate(d153); delegates[153] = d153; - var d154 = new __allocGCInfo(_allocGCInfo); + var d154 = new __allocUnwindInfo(_allocUnwindInfo); callbacks[154] = Marshal.GetFunctionPointerForDelegate(d154); delegates[154] = d154; - var d155 = new __setEHcount(_setEHcount); + var d155 = new __allocGCInfo(_allocGCInfo); callbacks[155] = Marshal.GetFunctionPointerForDelegate(d155); delegates[155] = d155; - var d156 = new __setEHinfo(_setEHinfo); + var d156 = new __setEHcount(_setEHcount); callbacks[156] = Marshal.GetFunctionPointerForDelegate(d156); delegates[156] = d156; - var d157 = new __logMsg(_logMsg); + var d157 = new __setEHinfo(_setEHinfo); callbacks[157] = Marshal.GetFunctionPointerForDelegate(d157); delegates[157] = d157; - var d158 = new __doAssert(_doAssert); + var d158 = new __logMsg(_logMsg); callbacks[158] = Marshal.GetFunctionPointerForDelegate(d158); delegates[158] = d158; - var d159 = new __reportFatalError(_reportFatalError); + var d159 = new __doAssert(_doAssert); callbacks[159] = Marshal.GetFunctionPointerForDelegate(d159); delegates[159] = d159; - var d160 = new __allocMethodBlockCounts(_allocMethodBlockCounts); + var d160 = new __reportFatalError(_reportFatalError); callbacks[160] = Marshal.GetFunctionPointerForDelegate(d160); delegates[160] = d160; - var d161 = new __getMethodBlockCounts(_getMethodBlockCounts); + var d161 = new __allocMethodBlockCounts(_allocMethodBlockCounts); callbacks[161] = Marshal.GetFunctionPointerForDelegate(d161); delegates[161] = d161; - var d162 = new __recordCallSite(_recordCallSite); + var d162 = new __getMethodBlockCounts(_getMethodBlockCounts); callbacks[162] = Marshal.GetFunctionPointerForDelegate(d162); delegates[162] = d162; - var d163 = new __recordRelocation(_recordRelocation); + var d163 = new __recordCallSite(_recordCallSite); callbacks[163] = Marshal.GetFunctionPointerForDelegate(d163); delegates[163] = d163; - var d164 = new __getRelocTypeHint(_getRelocTypeHint); + var d164 = new __recordRelocation(_recordRelocation); callbacks[164] = Marshal.GetFunctionPointerForDelegate(d164); delegates[164] = d164; - var d165 = new __getExpectedTargetArchitecture(_getExpectedTargetArchitecture); + var d165 = new __getRelocTypeHint(_getRelocTypeHint); callbacks[165] = Marshal.GetFunctionPointerForDelegate(d165); delegates[165] = d165; - var d166 = new __getJitFlags(_getJitFlags); + var d166 = new __getExpectedTargetArchitecture(_getExpectedTargetArchitecture); callbacks[166] = Marshal.GetFunctionPointerForDelegate(d166); delegates[166] = d166; + var d167 = new __getJitFlags(_getJitFlags); + callbacks[167] = Marshal.GetFunctionPointerForDelegate(d167); + delegates[167] = d167; keepAlive = delegates; return (IntPtr)callbacks; diff --git a/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs index be2a2618ac9c2c..6a15a04a897e29 100644 --- a/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs @@ -417,6 +417,9 @@ private MethodIL Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, C Get_CORINFO_SIG_INFO(method, &methodInfo->args); Get_CORINFO_SIG_INFO(methodIL.GetLocals(), &methodInfo->locals); + methodInfo->osrInfo.ILOffset = 0; + methodInfo->osrInfo.PatchpointInfo = default(CORINFO_PATCHPOINT_INFO*); + return methodIL; } diff --git a/src/coreclr/src/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/src/tools/Common/JitInterface/CorInfoTypes.cs index e5a47be05cc782..247448648f75a4 100644 --- a/src/coreclr/src/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/src/tools/Common/JitInterface/CorInfoTypes.cs @@ -91,6 +91,10 @@ public struct CORINFO_JUST_MY_CODE_HANDLE_ public struct CORINFO_VarArgInfo { } + + public struct CORINFO_PATCHPOINT_INFO + { + } public enum _EXCEPTION_POINTERS { } @@ -777,6 +781,11 @@ public enum CorJitFuncKind CORJIT_FUNC_FILTER // a funclet associated with an EH filter } + public unsafe struct CORINFO_OSR_INFO + { + public uint ILOffset; + public void* PatchpointInfo; + } public unsafe struct CORINFO_METHOD_INFO { @@ -790,6 +799,7 @@ public unsafe struct CORINFO_METHOD_INFO public CorInfoRegionKind regionKind; public CORINFO_SIG_INFO args; public CORINFO_SIG_INFO locals; + public CORINFO_OSR_INFO osrInfo; } // // what type of code region we are in diff --git a/src/coreclr/src/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/src/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index b6083523bfee25..5f473260357d8c 100644 --- a/src/coreclr/src/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/src/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -95,6 +95,7 @@ CORINFO_GENERICHANDLE_RESULT*,ref CORINFO_GENERICHANDLE_RESULT,void* CORINFO_METHOD_INFO*,CORINFO_METHOD_INFO*,void* CORINFO_FIELD_INFO*,CORINFO_FIELD_INFO*,void* CORINFO_CALL_INFO*,CORINFO_CALL_INFO*,void* +CORINFO_PATCHPOINT_INFO*,CORINFO_PATCHPOINT_INFO*,void* DelegateCtorArgs*,ref DelegateCtorArgs,void* ICorDynamicInfo*,IntPtr,void* va_list,IntPtr @@ -253,6 +254,7 @@ FUNCTIONS void setBoundaries(CORINFO_METHOD_HANDLE ftn, ULONG32 cMap, ICorDebugInfo::OffsetMapping* pMap) void getVars(CORINFO_METHOD_HANDLE ftn, ULONG32* cVars, ICorDebugInfo::ILVarInfo** vars, bool* extendOthers) void setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo* vars) + void setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo); void*allocateArray(size_t cBytes); void freeArray(void*array); CORINFO_ARG_LIST_HANDLE getArgNext(CORINFO_ARG_LIST_HANDLE args); diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs index 68f04436441446..33f1dd83452ca4 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs @@ -84,6 +84,13 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) foreach (MethodWithGCInfo method in factory.EnumerateCompiledMethods()) { MemoryStream methodDebugBlob = new MemoryStream(); + +// #if FEATURE_ON_STACK_REPLACEMENT + // First byte of the debug info blob is an OSR flag byte. + // Currently always zero when prejitting; there is + // no OSR data to follow. + methodDebugBlob.WriteByte(0); +// #endif byte[] bounds = CreateBoundsBlobForMethod(method); byte[] vars = CreateVarBlobForMethod(method); diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 42f853aecf2508..5fbf3c1a4e0284 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -810,6 +810,15 @@ private void setBoundaries(CORINFO_METHOD_STRUCT_* ftn, uint cMap, OffsetMapping } } + /// + /// Record patchpoint info for the method + /// + private void setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) + { + // No patchpoint info when prejitting + throw new NotImplementedException(); + } + private void PublishEmptyCode() { _methodCodeNode.SetCode(new ObjectNode.ObjectData(Array.Empty(), null, 1, Array.Empty())); diff --git a/src/coreclr/src/tools/crossgen2/jitinterface/jitinterface.h b/src/coreclr/src/tools/crossgen2/jitinterface/jitinterface.h index d7622f054b9f95..9ad0d2546b8b46 100644 --- a/src/coreclr/src/tools/crossgen2/jitinterface/jitinterface.h +++ b/src/coreclr/src/tools/crossgen2/jitinterface/jitinterface.h @@ -104,6 +104,7 @@ struct JitInterfaceCallbacks void (* setBoundaries)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int cMap, void* pMap); void (* getVars)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int* cVars, void* vars, bool* extendOthers); void (* setVars)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int cVars, void* vars); + void (* setPatchpointInfo)(void * thisHandle, CorInfoException** ppException, void* patchpointInfo); void* (* allocateArray)(void * thisHandle, CorInfoException** ppException, size_t cBytes); void (* freeArray)(void * thisHandle, CorInfoException** ppException, void* array); void* (* getArgNext)(void * thisHandle, CorInfoException** ppException, void* args); @@ -1016,6 +1017,14 @@ class JitInterfaceWrapper throw pException; } + virtual void setPatchpointInfo(void* patchpointInfo) + { + CorInfoException* pException = nullptr; + _callbacks->setPatchpointInfo(_thisHandle, &pException, patchpointInfo); + if (pException != nullptr) + throw pException; + } + virtual void* allocateArray(size_t cBytes) { CorInfoException* pException = nullptr; diff --git a/src/coreclr/src/tools/crossgen2/jitinterface/jitwrapper.cpp b/src/coreclr/src/tools/crossgen2/jitinterface/jitwrapper.cpp index 8a4b1f91c10e7a..fbc01702767309 100644 --- a/src/coreclr/src/tools/crossgen2/jitinterface/jitwrapper.cpp +++ b/src/coreclr/src/tools/crossgen2/jitinterface/jitwrapper.cpp @@ -27,11 +27,11 @@ class CORJIT_FLAGS uint64_t corJitFlags; }; -static const GUID JITEEVersionIdentifier = { /* b2e40020-6125-41e4-a0fc-821127ec192a */ - 0xb2e40020, - 0x6125, - 0x41e4, - {0xa0, 0xfc, 0x82, 0x11, 0x27, 0xec, 0x19, 0x2a} +static const GUID JITEEVersionIdentifier = { /* c231d2d7-4764-4097-a9ef-5961041540df */ + 0xc231d2d7, + 0x4764, + 0x4097, + {0xa9, 0xef, 0x59, 0x61, 0x04, 0x15, 0x40, 0xdf} }; class Jit diff --git a/src/coreclr/src/vm/codeman.h b/src/coreclr/src/vm/codeman.h index 433ae895bc63ce..38a475cbd9a596 100644 --- a/src/coreclr/src/vm/codeman.h +++ b/src/coreclr/src/vm/codeman.h @@ -262,6 +262,7 @@ typedef struct _hpCodeHdr { SUPPORTS_DAC; return pRealCodeHeader->phdrJitGCInfo; + } PTR_MethodDesc GetMethodDesc() { diff --git a/src/coreclr/src/vm/codeversion.cpp b/src/coreclr/src/vm/codeversion.cpp index fc3c22e23eba8a..3f6df7e37525af 100644 --- a/src/coreclr/src/vm/codeversion.cpp +++ b/src/coreclr/src/vm/codeversion.cpp @@ -50,8 +50,6 @@ void NativeCodeVersion::SetGCCoverageInfo(PTR_GCCoverageInfo gcCover) bool NativeCodeVersion::operator==(const NativeCodeVersion & rhs) const { return m_pMethodDesc == rhs.m_pMethodDesc; } bool NativeCodeVersion::operator!=(const NativeCodeVersion & rhs) const { return !operator==(rhs); } - - #else // FEATURE_CODE_VERSIONING @@ -80,7 +78,14 @@ NativeCodeVersionNode::NativeCodeVersionNode( m_gcCover(PTR_NULL), #endif m_flags(0) -{} +{ + +#ifdef FEATURE_ON_STACK_REPLACEMENT + m_osrInfo.ilOffset = 0; + m_osrInfo.patchpointInfo = NULL; +#endif + +} #endif #ifdef DEBUG @@ -181,6 +186,26 @@ void NativeCodeVersionNode::SetOptimizationTier(NativeCodeVersion::OptimizationT #endif // FEATURE_TIERED_COMPILATION +#ifdef FEATURE_ON_STACK_REPLACEMENT + +CORINFO_OSR_INFO* NativeCodeVersionNode::GetOSRInfo() +{ + LIMITED_METHOD_DAC_CONTRACT; + return &m_osrInfo; +} + +#ifndef DACCESS_COMPILE + +void NativeCodeVersionNode::SetOSRInfo(CORINFO_OSR_INFO * info) +{ + LIMITED_METHOD_CONTRACT; + m_osrInfo = *info; +} + +#endif + +#endif // FEATURE_ON_STACK_REPLACEMENT + #ifdef HAVE_GCCOVER PTR_GCCoverageInfo NativeCodeVersionNode::GetGCCoverageInfo() const @@ -411,6 +436,39 @@ void NativeCodeVersion::SetOptimizationTier(OptimizationTier tier) #endif +#ifdef FEATURE_ON_STACK_REPLACEMENT + +CORINFO_OSR_INFO * NativeCodeVersion::GetOSRInfo() +{ + LIMITED_METHOD_DAC_CONTRACT; + if (m_storageKind == StorageKind::Explicit) + { + return AsNode()->GetOSRInfo(); + } + else + { + return NULL; + } +} + +#ifndef DACCESS_COMPILE +void NativeCodeVersion::SetOSRInfo(CORINFO_OSR_INFO * info) +{ + WRAPPER_NO_CONTRACT; + if (m_storageKind == StorageKind::Explicit) + { + AsNode()->SetOSRInfo(info); + } + else + { + _ASSERTE(!"Cannot set OSR info here"); + } +} +#endif + +#endif + + #ifdef HAVE_GCCOVER PTR_GCCoverageInfo NativeCodeVersion::GetGCCoverageInfo() const diff --git a/src/coreclr/src/vm/codeversion.h b/src/coreclr/src/vm/codeversion.h index daf0b7611d21b7..f55d9faa5ca4ad 100644 --- a/src/coreclr/src/vm/codeversion.h +++ b/src/coreclr/src/vm/codeversion.h @@ -85,6 +85,13 @@ class NativeCodeVersion #endif #endif // FEATURE_TIERED_COMPILATION +#ifdef FEATURE_ON_STACK_REPLACEMENT + CORINFO_OSR_INFO * GetOSRInfo(); +#ifndef DACCESS_COMPILE + void SetOSRInfo(CORINFO_OSR_INFO* info); +#endif +#endif // FEATURE_ON_STACK_REPLACEMENT + #ifdef HAVE_GCCOVER PTR_GCCoverageInfo GetGCCoverageInfo() const; void SetGCCoverageInfo(PTR_GCCoverageInfo gcCover); @@ -277,6 +284,13 @@ class NativeCodeVersionNode void SetGCCoverageInfo(PTR_GCCoverageInfo gcCover); #endif +#ifdef FEATURE_ON_STACK_REPLACEMENT + CORINFO_OSR_INFO * GetOSRInfo(); +#ifndef DACCESS_COMPILE + void SetOSRInfo(CORINFO_OSR_INFO * info); +#endif +#endif + private: //union - could save a little memory? //{ @@ -293,6 +307,9 @@ class NativeCodeVersionNode #ifdef HAVE_GCCOVER PTR_GCCoverageInfo m_gcCover; #endif +#ifdef FEATURE_ON_STACK_REPLACEMENT + CORINFO_OSR_INFO m_osrInfo; +#endif enum NativeCodeVersionNodeFlags { diff --git a/src/coreclr/src/vm/compile.cpp b/src/coreclr/src/vm/compile.cpp index 84212341c27fa8..56a2219979d6fe 100644 --- a/src/coreclr/src/vm/compile.cpp +++ b/src/coreclr/src/vm/compile.cpp @@ -1018,7 +1018,7 @@ void CEECompileInfo::CompressDebugInfo( { STANDARD_VM_CONTRACT; - CompressDebugInfo::CompressBoundariesAndVars(pOffsetMapping, iOffsetMapping, pNativeVarInfo, iNativeVarInfo, pDebugInfoBuffer, NULL); + CompressDebugInfo::CompressBoundariesAndVars(pOffsetMapping, iOffsetMapping, pNativeVarInfo, iNativeVarInfo, NULL, pDebugInfoBuffer, NULL); } ICorJitHost* CEECompileInfo::GetJitHost() diff --git a/src/coreclr/src/vm/debuginfostore.cpp b/src/coreclr/src/vm/debuginfostore.cpp index 295dbe5f201bc5..8af8a1769bc3b3 100644 --- a/src/coreclr/src/vm/debuginfostore.cpp +++ b/src/coreclr/src/vm/debuginfostore.cpp @@ -440,6 +440,7 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( IN ULONG iOffsetMapping, IN ICorDebugInfo::NativeVarInfo * pNativeVarInfo, IN ULONG iNativeVarInfo, + IN CORINFO_PATCHPOINT_INFO * patchpointInfo, IN OUT SBuffer * pDebugInfoBuffer, IN LoaderHeap * pLoaderHeap ) @@ -451,6 +452,13 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( PRECONDITION((pDebugInfoBuffer != NULL) ^ (pLoaderHeap != NULL)); } CONTRACTL_END; + // Patchpoint info is currently uncompressed. + DWORD cbPatchpointInfo = 0; + if (patchpointInfo != NULL) + { + cbPatchpointInfo = patchpointInfo->PatchpointInfoSize(); + } + // Actually do the compression. These will throw on oom. NibbleWriter boundsBuffer; DWORD cbBounds = 0; @@ -479,7 +487,7 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( DWORD cbHeader; PVOID pHeader = w.GetBlob(&cbHeader); - S_UINT32 cbFinalSize = S_UINT32(cbHeader) + S_UINT32(cbBounds) + S_UINT32(cbVars); + S_UINT32 cbFinalSize = S_UINT32(1) + S_UINT32(cbPatchpointInfo) + S_UINT32(cbHeader) + S_UINT32(cbBounds) + S_UINT32(cbVars); if (cbFinalSize.IsOverflow()) ThrowHR(COR_E_OVERFLOW); @@ -497,6 +505,18 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( BYTE *ptr = ptrStart; + // First byte is a flag byte: + // 0 - no patchpoint info + // 1 - patchpoint info + + *ptr++ = (cbPatchpointInfo > 0) ? 1 : 0; + + if (cbPatchpointInfo > 0) + { + memcpy(ptr, (BYTE*) patchpointInfo, cbPatchpointInfo); + ptr += cbPatchpointInfo; + } + memcpy(ptr, pHeader, cbHeader); ptr += cbHeader; @@ -524,6 +544,8 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( // DAC only needs to run the uncompression routines. //----------------------------------------------------------------------------- +typedef DPTR(CORINFO_PATCHPOINT_INFO) PTR_CORINFO_PATCHPOINT_INFO; + //----------------------------------------------------------------------------- // Uncompression (restore) routines //----------------------------------------------------------------------------- @@ -552,6 +574,20 @@ void CompressDebugInfo::RestoreBoundariesAndVars( if (pcVars != NULL) *pcVars = 0; if (ppVars != NULL) *ppVars = NULL; + // Check flag byte and skip over any patchpoint info + BYTE flagByte = *pDebugInfo; + pDebugInfo++; + + if (flagByte == 1) + { + PTR_CORINFO_PATCHPOINT_INFO patchpointInfo = dac_cast(pDebugInfo); + pDebugInfo += patchpointInfo->PatchpointInfoSize(); + } + else + { + _ASSERTE(flagByte == 0); + } + NibbleReader r(pDebugInfo, 12 /* maximum size of compressed 2 UINT32s */); ULONG cbBounds = r.ReadEncodedU32(); @@ -615,6 +651,35 @@ void CompressDebugInfo::RestoreBoundariesAndVars( } } +CORINFO_PATCHPOINT_INFO * CompressDebugInfo::RestorePatchpointInfo(IN PTR_BYTE pDebugInfo) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + PTR_CORINFO_PATCHPOINT_INFO patchpointInfo = NULL; + + // Check flag byte. + BYTE flagByte = *pDebugInfo; + pDebugInfo++; + + if (flagByte == 1) + { + patchpointInfo = dac_cast(pDebugInfo); + } + else + { + _ASSERTE(flagByte == 0); + } + + return patchpointInfo; +} + #ifdef DACCESS_COMPILE void CompressDebugInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo) { diff --git a/src/coreclr/src/vm/debuginfostore.h b/src/coreclr/src/vm/debuginfostore.h index 453e41fa907c10..c9195ad423bf77 100644 --- a/src/coreclr/src/vm/debuginfostore.h +++ b/src/coreclr/src/vm/debuginfostore.h @@ -83,6 +83,7 @@ class CompressDebugInfo IN ULONG iOffsetMapping, IN ICorDebugInfo::NativeVarInfo * pNativeVarInfo, IN ULONG iNativeVarInfo, + IN CORINFO_PATCHPOINT_INFO * patchpointInfo, IN OUT SBuffer * pDebugInfoBuffer, IN LoaderHeap * pLoaderHeap ); @@ -98,6 +99,10 @@ class CompressDebugInfo OUT ICorDebugInfo::NativeVarInfo **ppVars ); + static CORINFO_PATCHPOINT_INFO * RestorePatchpointInfo( + IN PTR_BYTE pDebugInfo + ); + #ifdef DACCESS_COMPILE static void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo); #endif diff --git a/src/coreclr/src/vm/eeconfig.cpp b/src/coreclr/src/vm/eeconfig.cpp index 4c7344a4654171..c031bcabe414e8 100644 --- a/src/coreclr/src/vm/eeconfig.cpp +++ b/src/coreclr/src/vm/eeconfig.cpp @@ -339,6 +339,12 @@ HRESULT EEConfig::Init() tieredCompilation_CallCountingDelayMs = 0; #endif +#if defined(FEATURE_ON_STACK_REPLACEMENT) + dwOSR_Verbose = 0; + dwOSR_HitLimit = 10; + dwOSR_CounterBump = 5000; +#endif + #ifndef CROSSGEN_COMPILE backpatchEntryPointSlots = false; #endif @@ -1240,6 +1246,14 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ } #endif +#if defined(FEATURE_ON_STACK_REPLACEMENT) + dwOSR_Verbose = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_Verbose); + dwOSR_HitLimit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_HitLimit); + dwOSR_CounterBump = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_CounterBump); + dwOSR_LowId = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_LowId); + dwOSR_HighId = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_HighId); +#endif + #ifndef CROSSGEN_COMPILE backpatchEntryPointSlots = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BackpatchEntryPointSlots) != 0; #endif diff --git a/src/coreclr/src/vm/eeconfig.h b/src/coreclr/src/vm/eeconfig.h index c4bc593703b786..766af2c66e472d 100644 --- a/src/coreclr/src/vm/eeconfig.h +++ b/src/coreclr/src/vm/eeconfig.h @@ -288,6 +288,15 @@ class EEConfig DWORD TieredCompilation_CallCountingDelayMs() const { LIMITED_METHOD_CONTRACT; return tieredCompilation_CallCountingDelayMs; } #endif +#if defined(FEATURE_ON_STACK_REPLACEMENT) + // OSR Config + DWORD OSR_Verbose() const { LIMITED_METHOD_CONTRACT; return dwOSR_Verbose; } + DWORD OSR_CounterBump() const { LIMITED_METHOD_CONTRACT; return dwOSR_CounterBump; } + DWORD OSR_HitLimit() const { LIMITED_METHOD_CONTRACT; return dwOSR_HitLimit; } + DWORD OSR_LowId() const { LIMITED_METHOD_CONTRACT; return dwOSR_LowId; } + DWORD OSR_HighId() const { LIMITED_METHOD_CONTRACT; return dwOSR_HighId; } +#endif + #ifndef CROSSGEN_COMPILE bool BackpatchEntryPointSlots() const { LIMITED_METHOD_CONTRACT; return backpatchEntryPointSlots; } #endif @@ -1023,6 +1032,14 @@ class EEConfig DWORD tieredCompilation_CallCountingDelayMs; #endif +#if defined(FEATURE_ON_STACK_REPLACEMENT) + DWORD dwOSR_Verbose; + DWORD dwOSR_HitLimit; + DWORD dwOSR_CounterBump; + DWORD dwOSR_LowId; + DWORD dwOSR_HighId; +#endif + #ifndef CROSSGEN_COMPILE bool backpatchEntryPointSlots; #endif diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp index 263a29d00832d6..76e6020d150489 100644 --- a/src/coreclr/src/vm/jithelpers.cpp +++ b/src/coreclr/src/vm/jithelpers.cpp @@ -5096,6 +5096,426 @@ HCIMPL0(void, JIT_DebugLogLoopCloning) } HCIMPLEND +#ifdef FEATURE_ON_STACK_REPLACEMENT + +// Runtime state for a patchpoint. +// +// Patchpoint identity is currently the return address +// of the helper call in the jitted code. +// +// Todo: handle cases where IP addresses get recycled +struct PerPatchpointInfo +{ + PerPatchpointInfo() : + m_osrMethodCode(0), + m_patchpointCount(0), + m_flags(0), + m_patchpointId(0) + { + } + + enum + { + patchpoint_triggered = 0x1, + patchpoint_invalid = 0x2 + }; + + PCODE m_osrMethodCode; + LONG m_patchpointCount; + LONG m_flags; + + int m_patchpointId; +}; + +typedef EEPtrHashTable JitPatchpointTable; +JitPatchpointTable *g_pJitPatchpointTable = NULL; +CrstStatic g_pJitPatchpointCrst; +static int s_patchpointId = 0; +static bool s_warnedLimitedRange = false; + +// Helper method to jit the OSR version of a method. +// +// Returns the address of the jitted code. +// Returns NULL if osr method can't be created. +static PCODE JitPatchpointWorker(void* ip, int ilOffset) +{ + PCODE osrVariant = NULL; + + GCX_PREEMP(); + + // Find the method desc for this bit of code + EECodeInfo codeInfo((PCODE)ip); + MethodDesc* pMD = codeInfo.GetMethodDesc(); + + // Find the il method version corresponding to this version of the method + // and set up a new native code version for it. + ReJITID rejitId = ReJitManager::GetReJitId(pMD, codeInfo.GetStartAddress()); + CodeVersionManager* codeVersionManager = pMD->GetCodeVersionManager(); + NativeCodeVersion osrNativeCodeVersion; + { + // Request a new native version that is optimized. + CodeVersionManager::TableLockHolder lock(codeVersionManager); + ILCodeVersion ilCodeVersion = codeVersionManager->GetILCodeVersion(pMD, rejitId); + HRESULT hr = ilCodeVersion.AddNativeCodeVersion(pMD, NativeCodeVersion::OptimizationTier1, &osrNativeCodeVersion); + if (FAILED(hr)) + { + // log ... + return NULL; + } + } + + // Prepare info for the jit + CORINFO_OSR_INFO osrInfo; + osrInfo.ilOffset = ilOffset; + + // Fetch the patchpoint info for the current method + EEJitManager* jitMgr = ExecutionManager::GetEEJitManager(); + CodeHeader* codeHdr = jitMgr->GetCodeHeaderFromStartAddress(codeInfo.GetStartAddress()); + PTR_BYTE debugInfo = codeHdr->GetDebugInfo(); + CORINFO_PATCHPOINT_INFO* patchpointInfo = CompressDebugInfo::RestorePatchpointInfo(debugInfo); + osrInfo.patchpointInfo = patchpointInfo; + + if (osrInfo.patchpointInfo == NULL) + { + // log ... + return NULL; + } + osrNativeCodeVersion.SetOSRInfo(&osrInfo); + + // Invoke the jit to compile the OSR version + PrepareCodeConfigBuffer configBuffer(osrNativeCodeVersion); + PrepareCodeConfig *config = configBuffer.GetConfig(); + osrVariant = pMD->PrepareCode(config); + + return osrVariant; +} + +// Helper method wrapper to set up a frame so we can invoke methods that might GC +HCIMPL2(PCODE, JIT_Patchpoint_Framed, void* ip, int ilOffset) +{ + PCODE result = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_0(); + + result = JitPatchpointWorker(ip, ilOffset); + + HELPER_METHOD_FRAME_END(); + + return result; +} +HCIMPLEND + +// Jit helper invoked at a patchpoint. +// +// Checks to see if this is a known patchpoint, if not, +// an entry is added to the patchpoint table. +// +// When the patchpoint has been hit often enough to trigger +// a transition, create an OSR method. +// +// Currently, counter is a pointer into the Tier0 method stack +// frame so we have exclusive access. + +void JIT_Patchpoint(int* counter, int ilOffset) +{ + // This method may not return normally + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_MODE_COOPERATIVE; + + // Patchpoint identity is the helper return address + void* const ip = _ReturnAddress(); + + // Fetch or setup patchpoint info for this patchpoint. + PerPatchpointInfo * ppInfo = NULL; + BOOL hasData = g_pJitPatchpointTable->GetValueSpeculative(ip, (HashDatum*)&ppInfo); + + if (!hasData) + { + CrstHolder lock(&g_pJitPatchpointCrst); + hasData = g_pJitPatchpointTable->GetValue(ip, (HashDatum*)&ppInfo); + + if (!hasData) + { + // Todo: find right heap for this data + ppInfo = new (nothrow) PerPatchpointInfo(); + g_pJitPatchpointTable->InsertValue(ip, (HashDatum)ppInfo); + ppInfo->m_patchpointId = ++s_patchpointId; + } + } + + const int verbose = g_pConfig->OSR_Verbose(); + const int ppId = ppInfo->m_patchpointId; + const int counterBump = g_pConfig->OSR_CounterBump(); + + // In the current prototype, counter is shared by all patchpoints + // in a method, so no matter what happens below, we don't want to + // impair those other patchpoints. + // + // One might be tempted, for instance, to set the counter for + // invalid or ignored patchpoints to some high value to reduce + // the amount of back and forth with the runtime, but this would + // lock out other patchpoints in the method. + // + // So we always reset the counter to the bump value. + // + // In the prototype, counter is a location in a stack frame, + // so we can update it without worrying about other threads. + *counter = counterBump; + + // OSR is only supported on x64, currently. +#if !defined(TARGET_AMD64) + if (verbose > 1) + { + printf("### Runtime: no OSR for this isa/abi yet [%d] 0x%p\n", ppId, ip); + } + return; +#endif + + // Is this an invalid patchpoint? If so, just return to the Tier0 method. + if ((ppInfo->m_flags & PerPatchpointInfo::patchpoint_invalid) == PerPatchpointInfo::patchpoint_invalid) + { + if (verbose > 1) + { + printf("### Runtime: INVALID patchpoint [%d] 0x%p\n", ppId, ip); + } + return; + } + + // See if we have an OSR method for this patchpoint. + PCODE osrMethodCode = ppInfo->m_osrMethodCode; + bool isNewMethod = false; + + if (osrMethodCode == NULL) + { + // No OSR method yet, let's see if we should create one. + // + // First, optionally ignore some patchpoints to increase + // coverage (stress mode). + // + // Because there are multiple patchpoints in a method, and + // each OSR method covers the remainder of the method from + // that point until the method returns, if we trigger on an + // early patchpoint in a method, we may never see triggers on + // a later one. + const int lowId = g_pConfig->OSR_LowId(); + const int highId = g_pConfig->OSR_HighId(); + + if (!s_warnedLimitedRange && ((lowId != -1) || (highId != 10000000))) + { + printf("\n### Runtime: PATCHPOINTS LIMITED to range [%d,%d]\n\n", lowId, highId); + s_warnedLimitedRange = true; + } + + if ((ppId < lowId) || (ppId > highId)) + { + if (verbose > 0) + { + printf("### Runtime: IGNORING patchpoint [%d] 0x%p\n", ppId, ip); + } + return; + } + + // Second, only request the OSR method if this patchpoint has + // been hit often enough. + // + // Note the initial invocation of the helper depends on the + // initial counter value baked into jitted code (call this J); + // subsequent invocations depend on the counter bump (call + // this B). + // + // J and B may differ, so the total number of loop iterations + // before an OSR method is created is: + // + // J, if hitLimit <= 1; + // J + (hitLimit-1)* B, if hitLimit > 1; + // + // Current thinking is: + // + // J should be in the range of tens to hundreds, so that newly + // called Tier0 methods that already have OSR methods + // available can transition to OSR methods quickly, but + // methods called only a few times do not invoke this + // helper and so create PerPatchpoint runtime state. + // + // B should be in the range of hundreds to thousands, so that + // we're not too eager to create OSR methods (since there is + // some jit cost), but are eager enough to transition before + // we run too much Tier0 code. + + const int hitLimit = g_pConfig->OSR_HitLimit(); + const int hitCount = InterlockedIncrement(&ppInfo->m_patchpointCount); + + if ((verbose > 1) || (verbose > 0) && (hitCount == 1)) + { + printf("### Runtime: patchpoint [%d] 0x%p hit:%d (limit %d)\n", ppId, ip, hitCount, hitLimit); + } + + // Defer, if we haven't yet reached the limit + if (hitCount < hitLimit) + { + return; + } + + // Third, make sure no other thread is trying to create the OSR method. + LONG oldFlags = ppInfo->m_flags; + if ((oldFlags & PerPatchpointInfo::patchpoint_triggered) == PerPatchpointInfo::patchpoint_triggered) + { + // Some other thread is already working on the OSR method, just come back later. + if (verbose > 1) + { + printf("### Runtime: AWAITING patchpoint [%d] 0x%p\n", ppId, ip); + } + return; + } + + LONG newFlags = ppInfo->m_flags | PerPatchpointInfo::patchpoint_triggered; + BOOL triggerTransition = InterlockedCompareExchange(&ppInfo->m_flags, newFlags, oldFlags) == oldFlags; + + if (!triggerTransition) + { + // Some other thread won the race, just come back later. + if (verbose > 1) + { + printf("### Runtime: (lost race) AWAITING patchpoint [%d] 0x%p\n", ppId, ip); + } + return; + } + + // Time to create the OSR method. + // + // We currently do this synchronously. We could instead queue + // up a request on some worker thread, like we do for + // rejitting, and return control to the Tier0 method. It may + // eventually return here, if the patchpoint is hit often + // enough. + // + // There is a chance the async version will create methods + // that are never used (just like there is a chance that Tier1 + // methods are ever called). + // + // In this prototype we want to expose bugs in the jitted code + // for OSR methods, so we stick with synchronous creation. + +#if _DEBUG + if (verbose > 0) + { + EECodeInfo codeInfo((PCODE)ip); + MethodDesc* pMD = codeInfo.GetMethodDesc(); + + printf("### Runtime: patchpoint [%d] 0x%p TRIGGER hit:%d bump:%d native:0x%x il:0x%x in 0x%p %s::%s %s\n", + ppId, + ip, + hitCount, counterBump, + codeInfo.GetRelOffset(), ilOffset, + pMD, + pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, pMD->m_pszDebugMethodSignature); + } +#endif + + // Invoke the helper to build the OSR method + osrMethodCode = HCCALL2(JIT_Patchpoint_Framed, ip, ilOffset); + + // If that failed, mark the patchpoint as invaild. + if (osrMethodCode == NULL) + { + if (verbose > 0) + { + printf("### Runtime: unable to create OSR method, marking patchpoint [%d] 0x%p INVALID\n", ppId, ip); + } + + InterlockedOr(&ppInfo->m_flags, (LONG)PerPatchpointInfo::patchpoint_invalid); + return; + } + + // We've successfully created the osr method; make it available. + _ASSERTE(ppInfo->m_osrMethodCode == NULL); + ppInfo->m_osrMethodCode = osrMethodCode; + isNewMethod = true; + } + + // If we get here, we have code to transition to... + _ASSERTE(osrMethodCode != NULL); + + Thread *pThread = GetThread(); + +#ifdef FEATURE_HIJACK + // We can't crawl the stack of a thread that currently has a hijack pending + // (since the hijack routine won't be recognized by any code manager). So we + // Undo any hijack, the EE will re-attempt it later. + pThread->UnhijackThread(); +#endif + + // Find context for the original method + CONTEXT frameContext; + frameContext.ContextFlags = CONTEXT_FULL; + RtlCaptureContext(&frameContext); + + // Walk back to the original method frame + pThread->VirtualUnwindToFirstManagedCallFrame(&frameContext); + + // Remember original method FP and SP because new method will inherit them. + UINT_PTR currentSP = GetSP(&frameContext); + UINT_PTR currentFP = GetFP(&frameContext); + + // We expect to be back at the right IP + if ((UINT_PTR)ip != GetIP(&frameContext)) + { + printf("### Runtime: patchpoint [%d] 0x%p TRANSITION -- odd that context has ip %p\n", + ppId, ip, GetIP(&frameContext)); + } + + // Now unwind back to the original method caller frame. + EECodeInfo codeInfo(GetIP(&frameContext)); + frameContext.ContextFlags = CONTEXT_FULL; + ULONG_PTR establisherFrame = 0; + PVOID handlerData = NULL; + RtlVirtualUnwind(UNW_FLAG_NHANDLER, codeInfo.GetModuleBase(), GetIP(&frameContext), codeInfo.GetFunctionEntry(), + &frameContext, &handlerData, &establisherFrame, NULL); + + // Now, set FP and SP back to the values they had just before this helper was called, + // since the new method must have access to the original method frame. + // + // TODO: if we access the patchpointInfo here, we can read out the FP-SP delta from there and + // use that to adjust the stack, likely saving some stack space. + +#if defined(TARGET_AMD64) + // If calls push the return address, we need to simulate that here, so the OSR + // method sees the "expected" SP misalgnment on entry. + _ASSERTE(currentSP % 16 == 0); + currentSP -= 8; +#endif + + SetSP(&frameContext, currentSP); + frameContext.Rbp = currentFP; + + // Note we can get here w/o triggering, if there is an existing OSR method and + // we hit the patchpoint. + if (((verbose > 0) && isNewMethod) || (verbose > 1)) + { + printf("### Runtime: patchpoint [%d] 0x%p TRANSITION%s RSP %p RBP %p RIP %p (prev RBP %p)\n", + ppId, ip, isNewMethod ? "" : " (existing)", currentSP, currentFP, osrMethodCode , *(char**)currentFP); + } + + // Install new entry point as IP + SetIP(&frameContext, osrMethodCode); + + // Transition! + RtlRestoreContext(&frameContext, NULL); +} + +#else + +void JIT_Patchpoint(int* counter, int ilOffset) +{ + // Stub version if OSR feature is disabled + // + // Should not be called. + + UNREACHABLE(); +} + +#endif // FEATURE_ON_STACK_REPLACEMENT + //======================================================================== // // INTEROP HELPERS @@ -5216,12 +5636,23 @@ void InitJITHelpers2() g_pJitGenericHandleCacheCrst.Init(CrstJitGenericHandleCache, CRST_UNSAFE_COOPGC); - // Allocate and initialize the table + // Allocate and initialize the generic handle cache NewHolder tempGenericHandleCache (new JitGenericHandleCache()); LockOwner sLock = {&g_pJitGenericHandleCacheCrst, IsOwnerOfCrst}; if (!tempGenericHandleCache->Init(59, &sLock)) COMPlusThrowOM(); g_pJitGenericHandleCache = tempGenericHandleCache.Extract(); + +#ifdef FEATURE_ON_STACK_REPLACEMENT + g_pJitPatchpointCrst.Init(CrstJitPatchpoint, CRST_UNSAFE_COOPGC); + + // Allocate and initialize the patchpoint table + NewHolder tempJitPatchpointTable (new JitPatchpointTable()); + LockOwner sLock2 = {&g_pJitPatchpointCrst, IsOwnerOfCrst}; + if (!tempJitPatchpointTable->Init(59, &sLock2)) + COMPlusThrowOM(); + g_pJitPatchpointTable = tempJitPatchpointTable.Extract(); +#endif // FEATURE_ON_STACK_REPLACEMENT } #if defined(TARGET_AMD64) || defined(TARGET_ARM) diff --git a/src/coreclr/src/vm/jitinterface.cpp b/src/coreclr/src/vm/jitinterface.cpp index dded642a903813..490d0f214f3487 100644 --- a/src/coreclr/src/vm/jitinterface.cpp +++ b/src/coreclr/src/vm/jitinterface.cpp @@ -7730,6 +7730,11 @@ getMethodInfoHelper( &methInfo->locals, ftn, true); + + // Set these to default values. Will overwrite later if actually doing an OSR jit request. + methInfo->osrInfo.ilOffset = 0; + methInfo->osrInfo.patchpointInfo = NULL; + } // getMethodInfoHelper //--------------------------------------------------------------------------------------- @@ -10943,6 +10948,27 @@ void CEEJitInfo::setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo EE_TO_JIT_TRANSITION(); } +void CEEJitInfo::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + JIT_TO_EE_TRANSITION(); + +#ifdef FEATURE_ON_STACK_REPLACEMENT + // We receive ownership of the array + _ASSERTE(m_pPatchpointInfo == NULL); + m_pPatchpointInfo = patchpointInfo; +#else + UNREACHABLE(); +#endif + + EE_TO_JIT_TRANSITION(); +} + void CEEJitInfo::CompressDebugInfo() { CONTRACTL { @@ -10953,9 +10979,12 @@ void CEEJitInfo::CompressDebugInfo() // Don't track JIT info for DynamicMethods. if (m_pMethodBeingCompiled->IsDynamicMethod() && !g_pConfig->GetTrackDynamicMethodDebugInfo()) + { + _ASSERTE(m_pPatchpointInfo == NULL); return; + } - if (m_iOffsetMapping == 0 && m_iNativeVarInfo == 0) + if ((m_iOffsetMapping == 0) && (m_iNativeVarInfo == 0) && (m_pPatchpointInfo == NULL)) return; JIT_TO_EE_TRANSITION(); @@ -10965,6 +10994,7 @@ void CEEJitInfo::CompressDebugInfo() PTR_BYTE pDebugInfo = CompressDebugInfo::CompressBoundariesAndVars( m_pOffsetMapping, m_iOffsetMapping, m_pNativeVarInfo, m_iNativeVarInfo, + m_pPatchpointInfo, NULL, m_pMethodBeingCompiled->GetLoaderAllocator()->GetLowFrequencyHeap()); @@ -12102,7 +12132,7 @@ CorJitResult invokeCompileMethodHelper(EEJitManager *jitMgr, info, CORJIT_FLAGS::CORJIT_FLAG_CALL_GETJITFLAGS, nativeEntry, - nativeSizeOfCode ); + nativeSizeOfCode); #ifdef FEATURE_STACK_SAMPLING if (jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_SAMPLING_JIT_BACKGROUND)) @@ -12646,6 +12676,14 @@ PCODE UnsafeJitFunction(NativeCodeVersion nativeCodeVersion, COR_ILMETHOD_DECODE getMethodInfoHelper(ftn, ftnHnd, ILHeader, &methodInfo); +#ifdef FEATURE_ON_STACK_REPLACEMENT + if (flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_OSR)) + { + CORINFO_OSR_INFO *osrInfo = nativeCodeVersion.GetOSRInfo(); + methodInfo.osrInfo = *osrInfo; + } +#endif + // If it's generic then we can only enter through an instantiated md (unless we're just verifying it) _ASSERTE(flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) || !ftn->IsGenericMethodDefinition()); @@ -13882,6 +13920,12 @@ void CEEInfo::setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::N UNREACHABLE(); // only called on derived class. } +void CEEInfo::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +{ + LIMITED_METHOD_CONTRACT; + UNREACHABLE(); // only called on derived class. +} + void* CEEInfo::getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ void ** ppIndirection) /* OUT */ { diff --git a/src/coreclr/src/vm/jitinterface.h b/src/coreclr/src/vm/jitinterface.h index c1e0bb8e49d48f..3bd189fcca939c 100644 --- a/src/coreclr/src/vm/jitinterface.h +++ b/src/coreclr/src/vm/jitinterface.h @@ -823,6 +823,7 @@ class CEEInfo : public ICorJitInfo ICorDebugInfo::ILVarInfo **vars, bool *extendOthers); void setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo *vars); + void setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo); // ICorArgInfo stuff @@ -1216,6 +1217,8 @@ class CEEJitInfo : public CEEInfo void * allocGCInfo (size_t size); + void * allocPatchpointInfo (size_t size); + void setEHcount (unsigned cEH); void setEHinfo ( @@ -1290,6 +1293,13 @@ class CEEJitInfo : public CEEInfo m_iNativeVarInfo = 0; m_pNativeVarInfo = NULL; +#ifdef FEATURE_ON_STACK_REPLACEMENT + if (m_pPatchpointInfo != NULL) + delete [] ((BYTE*) m_pPatchpointInfo); + + m_pPatchpointInfo = NULL; +#endif + #ifdef FEATURE_EH_FUNCLETS m_moduleBase = NULL; m_totalUnwindSize = 0; @@ -1375,6 +1385,9 @@ class CEEJitInfo : public CEEInfo #endif m_GCinfo_len(0), m_EHinfo_len(0), +#ifdef FEATURE_ON_STACK_REPLACEMENT + m_pPatchpointInfo(NULL), +#endif m_iOffsetMapping(0), m_pOffsetMapping(NULL), m_iNativeVarInfo(0), @@ -1405,6 +1418,12 @@ class CEEJitInfo : public CEEInfo if (m_pNativeVarInfo != NULL) delete [] ((BYTE*) m_pNativeVarInfo); + +#ifdef FEATURE_ON_STACK_REPLACEMENT + if (m_pPatchpointInfo != NULL) + delete [] ((BYTE*) m_pPatchpointInfo); +#endif + } // ICorDebugInfo stuff. @@ -1412,6 +1431,7 @@ class CEEJitInfo : public CEEInfo ULONG32 cMap, ICorDebugInfo::OffsetMapping *pMap); void setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo *vars); + void setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo); void CompressDebugInfo(); void* getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ @@ -1475,6 +1495,10 @@ protected : ULONG32 m_iNativeVarInfo; ICorDebugInfo::NativeVarInfo * m_pNativeVarInfo; +#ifdef FEATURE_ON_STACK_REPLACEMENT + CORINFO_PATCHPOINT_INFO * m_pPatchpointInfo; +#endif + // The first time a call is made to CEEJitInfo::GetProfilingHandle() from this thread // for this method, these values are filled in. Thereafter, these values are used // in lieu of calling into the base CEEInfo::GetProfilingHandle() again. This protects the diff --git a/src/coreclr/src/vm/method.hpp b/src/coreclr/src/vm/method.hpp index 53d581ade05ed7..d80d06942e304a 100644 --- a/src/coreclr/src/vm/method.hpp +++ b/src/coreclr/src/vm/method.hpp @@ -2044,6 +2044,9 @@ class PrepareCodeConfig virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse); virtual COR_ILMETHOD* GetILHeader(); virtual CORJIT_FLAGS GetJitCompilationFlags(); +#ifdef FEATURE_ON_STACK_REPLACEMENT + virtual unsigned GetILOffset() const { return 0; } +#endif BOOL ProfilerRejectedPrecompiledCode(); BOOL ReadyToRunRejectedPrecompiledCode(); void SetProfilerRejectedPrecompiledCode(); @@ -2202,6 +2205,9 @@ class VersionedPrepareCodeConfig : public PrepareCodeConfig virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse); virtual COR_ILMETHOD* GetILHeader(); virtual CORJIT_FLAGS GetJitCompilationFlags(); +#ifdef FEATURE_ON_STACK_REPLACEMENT + virtual CORINFO_OSR_INFO * GetOSRInfo(); +#endif private: ILCodeVersion m_ilCodeVersion; }; diff --git a/src/coreclr/src/vm/prestub.cpp b/src/coreclr/src/vm/prestub.cpp index 5a4c86c7390e7f..3d36033ce9a7bd 100644 --- a/src/coreclr/src/vm/prestub.cpp +++ b/src/coreclr/src/vm/prestub.cpp @@ -973,10 +973,13 @@ PCODE MethodDesc::JitCompileCodeLocked(PrepareCodeConfig* pConfig, JitListLockEn // The profiler may have changed the code on the callback. Need to // pick up the new code. + // + // (don't want this for OSR, need to see how it works) COR_ILMETHOD_DECODER ilDecoderTemp; COR_ILMETHOD_DECODER *pilHeader = GetAndVerifyILHeader(pConfig, &ilDecoderTemp); *pFlags = pConfig->GetJitCompilationFlags(); PCODE pOtherCode = NULL; + EX_TRY { #ifndef CROSSGEN_COMPILE @@ -1345,6 +1348,14 @@ CORJIT_FLAGS VersionedPrepareCodeConfig::GetJitCompilationFlags() flags.Add(TieredCompilationManager::GetJitFlags(m_nativeCodeVersion)); #endif +#ifdef FEATURE_ON_STACK_REPLACEMENT + CORINFO_OSR_INFO * osrInfo = GetOSRInfo(); + if (osrInfo->patchpointInfo != NULL) + { + flags.Add(CORJIT_FLAGS::CORJIT_FLAG_OSR); + } +#endif + return flags; } @@ -1368,6 +1379,13 @@ PrepareCodeConfigBuffer::PrepareCodeConfigBuffer(NativeCodeVersion codeVersion) config->FinishConfiguration(); } +#ifdef FEATURE_ON_STACK_REPLACEMENT +CORINFO_OSR_INFO * VersionedPrepareCodeConfig::GetOSRInfo() +{ + return m_nativeCodeVersion.GetOSRInfo(); +} +#endif + #endif //FEATURE_CODE_VERSIONING #ifdef FEATURE_INSTANTIATINGSTUB_AS_IL diff --git a/src/coreclr/src/zap/zapinfo.cpp b/src/coreclr/src/zap/zapinfo.cpp index f0bf42024051a6..cd762988e0a740 100644 --- a/src/coreclr/src/zap/zapinfo.cpp +++ b/src/coreclr/src/zap/zapinfo.cpp @@ -500,7 +500,7 @@ void ZapInfo::CompileMethod() &m_currentMethodInfo, CORJIT_FLAGS::CORJIT_FLAG_CALL_GETJITFLAGS, &pCode, - &cCode ); + &cCode); if (FAILED(res)) { // We will fall back to the "main" JIT on failure. @@ -2943,6 +2943,12 @@ void ZapInfo::setVars(CORINFO_METHOD_HANDLE ftn, return; } +void ZapInfo::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +{ + // No patchpoint info when prejitting + UNREACHABLE(); +} + void * ZapInfo::allocateArray(size_t cBytes) { return new BYTE[cBytes]; diff --git a/src/coreclr/src/zap/zapinfo.h b/src/coreclr/src/zap/zapinfo.h index 22fb7182627105..e9595a9b5ee64b 100644 --- a/src/coreclr/src/zap/zapinfo.h +++ b/src/coreclr/src/zap/zapinfo.h @@ -475,6 +475,7 @@ class ZapInfo ICorDebugInfo::ILVarInfo **vars, bool *extendOthers); void setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo*vars); + void setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo); void * allocateArray(size_t cBytes); void freeArray(void *array); diff --git a/src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.cs b/src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.cs new file mode 100644 index 00000000000000..897c56b7fec291 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +// OSR method has address exposed local + +class AddressExposedLocal +{ + // [MethodImpl(MethodImplOptions.NoInlining)] + public static unsafe int I(ref int p) => p; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static unsafe void J(ref int p) {} + + [MethodImpl(MethodImplOptions.NoInlining)] + public static unsafe int F(int from, int to) + { + int result = 0; + J(ref result); + for (int i = from; i < to; i++) + { + result = I(ref result) + i; + } + return result; + } + + public static int Main() + { + Console.WriteLine($"starting sum"); + int result = F(0, 1_000_000); + Console.WriteLine($"done, sum is {result}"); + return (result == 1783293664) ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.csproj b/src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.csproj new file mode 100644 index 00000000000000..e8168a04617420 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.csproj @@ -0,0 +1,11 @@ + + + Exe + + True + true + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.cs b/src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.cs new file mode 100644 index 00000000000000..7265d18699cf3d --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// OSR test case with two stackallocs. +// +// Original method frame is variable sized when we reach patchpoint +// OSR method frame is also variable sized. + +using System; + +class DoubleStackAlloc +{ + static int outerSize = 1000; + static int innerSize = 1; + public static unsafe int Main() + { + long* result = stackalloc long[outerSize]; + *result = 0; + for (int i = 0; i < 1_000_000; i++) + { + if ((i % 8192) == 0) + { + long *nresult = stackalloc long[innerSize]; + *nresult = *result; + result = nresult; + } + *result += i; + } + return *result == 499999500000 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.csproj b/src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.csproj new file mode 100644 index 00000000000000..e8168a04617420 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.csproj @@ -0,0 +1,11 @@ + + + Exe + + True + true + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/example.cs b/src/coreclr/tests/src/JIT/opt/OSR/example.cs new file mode 100644 index 00000000000000..1f360eea9562e2 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/example.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +// Example from the OSR doc + +class OSR_Example +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static double F(int from, int to) + { + double result = 0; + for (int i = from; i < to; i++) + { + result += (double)i; + } + return result; + } + + public static int Main(string[] args) + { + int final = args.Length <= 0 ? 1_000_000 : Int32.Parse(args[0]); + long frequency = Stopwatch.Frequency; + long nanosecPerTick = (1000L*1000L*1000L) / frequency; + // Console.WriteLine($"computing sum over {final} ints"); + // Get some of the initial jit cost out of the way + Stopwatch s = new Stopwatch(); + s.Start(); + s.Stop(); + + s = new Stopwatch(); + s.Start(); + double result = F(0, final); + s.Stop(); + double elapsedTime = 1000.0 * (double) s.ElapsedTicks / (double) frequency; + Console.WriteLine($"{final} iterations took {elapsedTime:F2}ms"); + return result == 499999500000 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/example.csproj b/src/coreclr/tests/src/JIT/opt/OSR/example.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/example.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.cs b/src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.cs new file mode 100644 index 00000000000000..a3f9dea686ceaa --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +// Patchpoint in generic method + +class GenericMethodPatchpoint +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static int F(T[] data, int from, int to) where T : class + { + int result = 0; + for (int i = from; i < to; i++) + { + if (data[i] == null) result++; + } + return result; + } + + public static int Main() + { + string[] a = new string[1000]; + a[111] = "hello, world"; + int result = F(a, 0, a.Length); + Console.WriteLine($"done, result is {result}"); + return result == 999 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.csproj b/src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/innerloop.cs b/src/coreclr/tests/src/JIT/opt/OSR/innerloop.cs new file mode 100644 index 00000000000000..7dadb6debff9b8 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/innerloop.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +class InnerLoop +{ + public static int Main() + { + int[] a = new int[1000]; + a[555] = 1; + + int result = 0; + + for (int i = 0; i < 1000; i++) + { + for (int j = i; j < 1000; j++) + { + result += a[j]; + } + } + + return result - 456; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/innerloop.csproj b/src/coreclr/tests/src/JIT/opt/OSR/innerloop.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/innerloop.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/integersumloop.cs b/src/coreclr/tests/src/JIT/opt/OSR/integersumloop.cs new file mode 100644 index 00000000000000..3742958f768c28 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/integersumloop.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +class IntegerSumLoop +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static int F(int from, int to) + { + int result = 0; + for (int i = from; i < to; i++) + { + result += i; + } + return result; + } + + public static int Main(string[] args) + { + int final = 1_000_000; + long frequency = Stopwatch.Frequency; + long nanosecPerTick = (1000L*1000L*1000L) / frequency; + F(0, 10); + Stopwatch s = new Stopwatch(); + s.Start(); + int result = F(0, final); + s.Stop(); + double elapsedTime = 1000.0 * (double) s.ElapsedTicks / (double) frequency; + Console.WriteLine($"{final} iterations took {elapsedTime:F2}ms"); + return result == 1783293664 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/integersumloop.csproj b/src/coreclr/tests/src/JIT/opt/OSR/integersumloop.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/integersumloop.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.cs b/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.cs new file mode 100644 index 00000000000000..5552cf91179a1a --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +// An example where OSR must preserve original method addreses for locals + +class LiveLocalAddress +{ + public static unsafe int Main() + { + long result = 0; + int a = 0; + int *c = &a; + int b = 0; + long distance = c - &b; + + for (int i = 0; i < 100_000; i++) + { + result += &a - &b; + } + + return (int)(result / 1000 * distance); + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.csproj b/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.csproj new file mode 100644 index 00000000000000..995a53defbf6b4 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.csproj @@ -0,0 +1,11 @@ + + + Exe + + True + true + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.cs b/src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.cs new file mode 100644 index 00000000000000..b6f1ec1950b096 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +// Example where local address is live in a stackalloc region + +class LiveLocalStackalloc +{ + static int n = 100; + static int j = 30; + + public static unsafe int Main() + { + int nn = n; + int** ptrs = stackalloc int*[nn]; + int a = 100; + *(ptrs + j) = &a; + int result = 0; + + for (int i = 0; i < nn; i++) + { + int* p = *(ptrs + i); + if (p != null) result += *p; + } + + return result; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.csproj b/src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.csproj new file mode 100644 index 00000000000000..e8168a04617420 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.csproj @@ -0,0 +1,11 @@ + + + Exe + + True + true + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainloop.cs b/src/coreclr/tests/src/JIT/opt/OSR/mainloop.cs new file mode 100644 index 00000000000000..e63bdca2b1bb25 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainloop.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +// Simple OSR test case -- long running loop in Main + +class MainLoop +{ + public static int Main() + { + long result = 0; + for (int i = 0; i < 1_000_000; i++) + { + result += (long)i; + } + return result == 499999500000 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainloop.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainloop.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainloop.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.cs b/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.cs new file mode 100644 index 00000000000000..2654ea3fd0beff --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +// Simple OSR test case -- nested loop in Main + +class MainNestedLoop +{ + public static int Main() + { + long result = 0; + for (int i = 0; i < 1_000; i++) + { + for (int j = 0; j < 1_000; j++) + { + result += (long)(i * 1_000 + j); + } + } + + return result == 499999500000 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.cs b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.cs new file mode 100644 index 00000000000000..0fb0b6907ede56 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +// OSR entry in a try region + +class MainLoopTry +{ + public static int Main() + { + Console.WriteLine($"starting sum"); + int result = 0; + try + { + for (int i = 0; i < 1_000_000; i++) + { + result += i; + } + } + finally + { + Console.WriteLine($"done, sum is {result}"); + } + return result == 1783293664 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.cs b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.cs new file mode 100644 index 00000000000000..2eef5c91c3178d --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +// Multiple patchpoints each in a try + +class MainLoopTry2 +{ + public static int Main() + { + Console.WriteLine($"starting sum"); + int result = 0; + try + { + for (int i = 0; i < 1_000; i++) + { + int temp = result; + try + { + for (int j = 0; j < 1_000; j++) + { + temp += 1000 * i + j; + } + } + finally + { + result = temp; + } + } + } + finally + { + Console.WriteLine($"done, sum is {result}"); + } + return result == 1783293664 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.cs b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.cs new file mode 100644 index 00000000000000..0c1469206f18dd --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// Tricky case for OSR with patchpoint in try region. +// +// If we need to OSR at inner loop head, then both try +// regions need trimming, but they can't trim down to the +// same block, and the branch to the logical trimmed +// entry point is not from the OSR entry. + +using System; + +class MainLoopCloselyNestedTry +{ + public static int Main() + { + Console.WriteLine($"starting sum"); + int result = 0; + try + { + try + { + int temp = 0; + for (int i = 0; i < 1_000; i++) + { + for (int j = 0; j < 1_000; j++) + { + temp += 1000 * i + j; + } + } + result = temp; + } + catch (Exception) + { + + } + } + finally + { + Console.WriteLine($"done, sum is {result}"); + } + return result == 1783293664 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.cs b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.cs new file mode 100644 index 00000000000000..8b7afbfa4e9a8a --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// Tricky case for OSR with patchpoint in try region. +// +// If we need to OSR at inner loop head, then both try +// regions need trimming. +// +// Mutually protective try example. + +using System; + +class E : Exception {} + +class MainLoopMutuallyProtectiveTry +{ + public static int Main() + { + Console.WriteLine($"starting sum"); + int result = 0; + try + { + int temp = 0; + for (int i = 0; i < 1_000; i++) + { + for (int j = 0; j < 1_000; j++) + { + temp += 1000 * i + j; + } + } + result = temp; + } + catch (E) + { + } + catch (Exception) + { + } + Console.WriteLine($"done, sum is {result}"); + return result == 1783293664 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/memoryargument.cs b/src/coreclr/tests/src/JIT/opt/OSR/memoryargument.cs new file mode 100644 index 00000000000000..7b8045c0146bfa --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/memoryargument.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +// OSR method must access memory argument + +class MemoryArgument +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static int F(int a, int b, int c, int d, int from, int to) + { + int result = 0; + for (int i = from; i < to; i++) + { + result += i; + } + return result; + } + + public static int Main(string[] args) + { + int final = 1_000_000; + int result = F(0, 0, 0, 0, 0, final); + return result == 1783293664 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/memoryargument.csproj b/src/coreclr/tests/src/JIT/opt/OSR/memoryargument.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/memoryargument.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.cs b/src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.cs new file mode 100644 index 00000000000000..160aeb5406ad30 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +// Nested do lops + +class NestedDoLoops +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static int F(int inner, int outer, int innerTo, int outerTo) + { + do { + do {} while (inner++ < innerTo); + inner = 0; + } + while (outer++ < outerTo); + + return outer; + } + + public static int Main() + { + Console.WriteLine($"starting sum"); + int result1 = F(0, 10, 0, 100_000); + int result2 = F(0, 100_000, 0, 10); + Console.WriteLine($"done, sum is {result1} and {result2}"); + return (result1 == result2) && (result1 == 100_001) ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.csproj b/src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.cs b/src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.cs new file mode 100644 index 00000000000000..cdb6cbf1baf330 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +// Orignal method has stackalloc, osr does not + +class OriginalStackalloc +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static unsafe int F(int from, int to, int s) + { + int* result = stackalloc int[s]; + *result = 0; + for (int i = from; i < to; i++) + { + *result += i; + } + return *result; + } + + public static int Main() + { + Console.WriteLine($"starting sum"); + int result = F(0, 1_000_000, 1); + Console.WriteLine($"done, sum is {result}"); + return (result == 1783293664) ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.csproj b/src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.csproj new file mode 100644 index 00000000000000..e8168a04617420 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.csproj @@ -0,0 +1,11 @@ + + + Exe + + True + true + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.cs b/src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.cs new file mode 100644 index 00000000000000..ce89aefe69eb26 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +// OSR method contains try + +class OSRContainsTry +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static unsafe int I(ref int p) => p; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static unsafe int F(int from, int to) + { + int result = 0; + for (int i = from; i < to; i++) + { + try + { + result = I(ref result) + i; + } + catch (Exception e) + { + } + } + return result; + } + + public static int Main() + { + Console.WriteLine($"starting sum"); + int result = F(0, 1_000_000); + Console.WriteLine($"done, sum is {result}"); + return result == 1783293664 ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.csproj b/src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.csproj new file mode 100644 index 00000000000000..e8168a04617420 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.csproj @@ -0,0 +1,11 @@ + + + Exe + + True + true + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/promoted.cs b/src/coreclr/tests/src/JIT/opt/OSR/promoted.cs new file mode 100644 index 00000000000000..0fde778e7cdd7c --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/promoted.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +// OSR complications with promoted structs + +struct Y +{ + public Y(int _a, int _b) + { + a = _a; + b = _b; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Init(int _a, int _b) + { + s_y = new Y(_a, _b); + } + + public static Y s_y; + public int a; + public int b; +} + +class OSRMethodStructPromotion +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static int F(int from, int to) + { + Y.Init(from, to); + Y y = Y.s_y; + int result = 0; + for (int i = y.a; i < y.b; i++) + { + result += i; + } + return result; + } + + public static int Main(string[] args) + { + int final = 1_000_000; + F(0, 10); + int result = F(0, final); + int expected = 1783293664; + return result == expected ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/promoted.csproj b/src/coreclr/tests/src/JIT/opt/OSR/promoted.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/promoted.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.cs b/src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.cs new file mode 100644 index 00000000000000..61a82945be469b --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +// OSR and tail recursion + +class OSRTailRecursion +{ + public static int F(int from, int to, int n, int a) + { + int result = a; + for (int i = from; i < to; i++) + { + result += i; + } + + if (n <= 0) return result; + + int delta = to - from; + + return F(to, to + to - from, n-1, result); + } + + public static int Main() + { + Console.WriteLine($"starting sum"); + int result = F(0, 100_000, 9, 0); + bool ok = (result == 1783293664); + string msg = ok ? "Pass" : "Fail"; + Console.WriteLine($"done, sum is {result}, {msg}"); + return ok ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.csproj b/src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.cs b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.cs new file mode 100644 index 00000000000000..c6481a6fb654d5 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +// Tail recursion, OSR entry in try region + +class TailRecursionWithOsrEntryInTry +{ + public static int F(int from, int to, int n, int a) + { + int result = a; + + try + { + for (int i = from; i < to; i++) + { + result += i; + } + } + catch(Exception) + { + } + + if (n <= 0) return result; + + int delta = to - from; + + return F(to, to + to - from, n-1, result); + } + + public static int Main() + { + Console.WriteLine($"starting sum"); + int result = F(0, 100_000, 9, 0); + bool ok = (result == 1783293664); + string msg = ok ? "Pass" : "Fail"; + Console.WriteLine($"done, sum is {result}, {msg}"); + return ok ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.csproj b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.cs b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.cs new file mode 100644 index 00000000000000..dfe13efd797584 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +// Tail recursion candidate with OSR entry in a try region + +class TailRecursionCandidateOSREntryInTry +{ + public unsafe static int F(int from, int to, int n, int result, int *x) + { + try + { + for (int i = from; i < to; i++) + { + result += i; + } + } + catch(Exception) + { + } + + if (n <= 0) return result; + + int delta = to - from; + + // Tail recursive site, but can't tail call + return F(to, to + delta, n-1, result, &result); + } + + public static unsafe int Main() + { + int x = 0; + int result = F(0, 100_000, 9, 0, &x); + bool ok = (result == 1783293664); + string msg = ok ? "Pass" : "Fail"; + Console.WriteLine($"done, sum is {result}, {msg}"); + return ok ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.csproj b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.csproj new file mode 100644 index 00000000000000..e8168a04617420 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.csproj @@ -0,0 +1,11 @@ + + + Exe + + True + true + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.cs b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.cs new file mode 100644 index 00000000000000..8128e005c0fbad --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +// Method creates has two OSR methods + +class TwoOSRMethods +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static void I(ref int p, int i) => p = p + i; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int F(int from, int to, bool b) + { + int result = 0; + + if (b) + { + for (int i = from; i < to; i++) + { + I(ref result, i); + } + } + else + { + for (int i = from; i < to; i++) + { + result += i; + } + + } + return result; + } + + public static int Main(string[] args) + { + int final = 1_000_000; + int result1 = F(0, final, true); + int result2 = F(0, final, false); + return (result1 == result2) && (result1 == 1783293664) ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.csproj b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.cs b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.cs new file mode 100644 index 00000000000000..42eb9dc848dbe6 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +// Two OSR methods from one orignal method + +class X +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static int F(int from, int to, bool sumUp) + { + int result = 0; + + if (sumUp) + { + for (int i = from; i < to; i++) + { + result += i; + } + } + else + { + for (int i = to; i > from; i--) + { + result += (i-1); + } + + } + return result; + } + + public static int Main(string[] args) + { + int final = 1_000_000; + int result1 = F(0, final, true); + int result2 = F(0, final, false); + return (result1 == result2) && (result1 == 1783293664) ? 100 : -1; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.csproj b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.csproj new file mode 100644 index 00000000000000..19781e26c20d81 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.csproj @@ -0,0 +1,10 @@ + + + Exe + + True + + + + + From 9628962ccbab74281c7daa1f4860ef07711a7cb9 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 28 Feb 2020 11:24:34 -0800 Subject: [PATCH 02/31] fix initializer order --- src/coreclr/src/vm/jitinterface.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/vm/jitinterface.h b/src/coreclr/src/vm/jitinterface.h index 3bd189fcca939c..33e963402d6d93 100644 --- a/src/coreclr/src/vm/jitinterface.h +++ b/src/coreclr/src/vm/jitinterface.h @@ -1385,13 +1385,13 @@ class CEEJitInfo : public CEEInfo #endif m_GCinfo_len(0), m_EHinfo_len(0), -#ifdef FEATURE_ON_STACK_REPLACEMENT - m_pPatchpointInfo(NULL), -#endif m_iOffsetMapping(0), m_pOffsetMapping(NULL), m_iNativeVarInfo(0), m_pNativeVarInfo(NULL), +#ifdef FEATURE_ON_STACK_REPLACEMENT + m_pPatchpointInfo(NULL), +#endif m_gphCache() { CONTRACTL From a168fe4cf2c884ecf0d9156728ab85c9ed47cc08 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 28 Feb 2020 12:24:30 -0800 Subject: [PATCH 03/31] fix build issue when feature not defined --- src/coreclr/src/vm/jitinterface.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/vm/jitinterface.cpp b/src/coreclr/src/vm/jitinterface.cpp index 490d0f214f3487..2568d3f81563cd 100644 --- a/src/coreclr/src/vm/jitinterface.cpp +++ b/src/coreclr/src/vm/jitinterface.cpp @@ -10977,14 +10977,20 @@ void CEEJitInfo::CompressDebugInfo() MODE_PREEMPTIVE; } CONTRACTL_END; +#ifdef FEATURE_ON_STACK_REPLACEMENT + CORINFO_PATCHPOINT_INFO* ppInfo = m_pPatchpointInfo; +#else + CORINFO_PATCHPOINT_INFO* ppInfo = NULL; +#endif + // Don't track JIT info for DynamicMethods. if (m_pMethodBeingCompiled->IsDynamicMethod() && !g_pConfig->GetTrackDynamicMethodDebugInfo()) { - _ASSERTE(m_pPatchpointInfo == NULL); + _ASSERTE(ppInfo == NULL); return; } - if ((m_iOffsetMapping == 0) && (m_iNativeVarInfo == 0) && (m_pPatchpointInfo == NULL)) + if ((m_iOffsetMapping == 0) && (m_iNativeVarInfo == 0) && (ppInfo == NULL)) return; JIT_TO_EE_TRANSITION(); @@ -10994,7 +11000,7 @@ void CEEJitInfo::CompressDebugInfo() PTR_BYTE pDebugInfo = CompressDebugInfo::CompressBoundariesAndVars( m_pOffsetMapping, m_iOffsetMapping, m_pNativeVarInfo, m_iNativeVarInfo, - m_pPatchpointInfo, + ppInfo, NULL, m_pMethodBeingCompiled->GetLoaderAllocator()->GetLowFrequencyHeap()); From 96338f9a4048d667e1cea952bd5afc4f10e7618c Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 28 Feb 2020 13:17:29 -0800 Subject: [PATCH 04/31] fix another build issue --- src/coreclr/src/vm/jithelpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp index 76e6020d150489..20be815b83a0d8 100644 --- a/src/coreclr/src/vm/jithelpers.cpp +++ b/src/coreclr/src/vm/jithelpers.cpp @@ -5345,7 +5345,7 @@ void JIT_Patchpoint(int* counter, int ilOffset) const int hitLimit = g_pConfig->OSR_HitLimit(); const int hitCount = InterlockedIncrement(&ppInfo->m_patchpointCount); - if ((verbose > 1) || (verbose > 0) && (hitCount == 1)) + if ((verbose > 1) || ((verbose > 0) && (hitCount == 1))) { printf("### Runtime: patchpoint [%d] 0x%p hit:%d (limit %d)\n", ppId, ip, hitCount, hitLimit); } From 6e8e97f564f77d164685164a96fdaaf9259e88d9 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 4 Mar 2020 11:37:22 -0800 Subject: [PATCH 05/31] WIP --- src/coreclr/src/inc/corinfo.h | 134 ++-------------------------- src/coreclr/src/vm/jitinterface.cpp | 46 ++++++++-- src/coreclr/src/vm/jitinterface.h | 24 ++++- 3 files changed, 64 insertions(+), 140 deletions(-) diff --git a/src/coreclr/src/inc/corinfo.h b/src/coreclr/src/inc/corinfo.h index a09f4cbb3a14c2..6c9036fc294a9a 100644 --- a/src/coreclr/src/inc/corinfo.h +++ b/src/coreclr/src/inc/corinfo.h @@ -342,134 +342,8 @@ struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR // // Communicates layout of the original method stack to the jit // for an On-Stack Replacement (OSR) jit request. -struct CORINFO_PATCHPOINT_INFO -{ - // Determine how much storage is needed to hold this info - static unsigned ComputeSize(unsigned localCount) - { - unsigned baseSize = sizeof(CORINFO_PATCHPOINT_INFO); - unsigned variableSize = localCount * sizeof(int); - unsigned totalSize = baseSize + variableSize; - return totalSize; - } - - // Initialize - void Initialize(unsigned localCount, unsigned ilSize, int fpToSpDelta) - { - m_patchpointInfoSize = ComputeSize(localCount); - m_ilSize = ilSize; - m_fpToSpDelta = fpToSpDelta; - m_numberOfLocals = localCount; - m_genericContextArgOffset = -1; - m_keptAliveThisOffset = -1; - m_securityCookieOffset = -1; - } - - // Total size of this patchpoint info record, in bytes - unsigned PatchpointInfoSize() const - { - return m_patchpointInfoSize; - } - - // IL Size of the original method - unsigned ILSize() const - { - return m_ilSize; - } - - // FP to SP delta of the original method - int FpToSpDelta() const - { - return m_fpToSpDelta; - } - - // Number of locals in the original method (including special locals) - unsigned NumberOfLocals() const - { - return m_numberOfLocals; - } - - // Original method caller SP offset for generic context arg - int GenericContextArgOffset() const - { - return m_genericContextArgOffset; - } - - void SetGenericContextArgOffset(int offset) - { - m_genericContextArgOffset = offset; - } - - // Original method FP relative offset for kept-alive this - int KeptAliveThisOffset() const - { - return m_keptAliveThisOffset; - } - - bool HasKeptAliveThis() const - { - return m_keptAliveThisOffset != -1; - } - - void SetKeptAliveThisOffset(int offset) - { - m_keptAliveThisOffset = offset; - } - - // Original method FP relative offset for security cookie - int SecurityCookieOffset() const - { - return m_securityCookieOffset; - } - - bool HasSecurityCookie() const - { - return m_securityCookieOffset != -1; - } - - void SetSecurityCookieOffset(int offset) - { - m_securityCookieOffset = offset; - } - - // True if this local was address exposed in the original method - bool IsExposed(unsigned localNum) const - { - return ((m_offsetAndExposureData[localNum] & EXPOSURE_MASK) != 0); - } - - void SetIsExposed(unsigned localNum) - { - m_offsetAndExposureData[localNum] |= EXPOSURE_MASK; - } - - // FP relative offset of this local in the original method - int Offset(unsigned localNum) const - { - return (m_offsetAndExposureData[localNum] & ~EXPOSURE_MASK); - } - - void SetOffset(unsigned localNum, int offset) - { - m_offsetAndExposureData[localNum] = offset; - } - -private: - enum - { - EXPOSURE_MASK = 0x1 - }; - - unsigned m_patchpointInfoSize; - unsigned m_ilSize; - unsigned m_numberOfLocals; - int m_fpToSpDelta; - int m_genericContextArgOffset; - int m_keptAliveThisOffset; - int m_securityCookieOffset; - int m_offsetAndExposureData[]; -}; +struct CORINFO_PATCHPOINT_INFO; // CorInfoHelpFunc defines the set of helpers (accessed via the ICorDynamicInfo::getHelperFtn()) // These helpers can be called by native code which executes in the runtime. @@ -1317,7 +1191,6 @@ struct CORINFO_METHOD_INFO CorInfoRegionKind regionKind; CORINFO_SIG_INFO args; CORINFO_SIG_INFO locals; - CORINFO_OSR_INFO osrInfo; }; //---------------------------------------------------------------------------- @@ -2805,6 +2678,11 @@ class ICorStaticInfo CORINFO_PATCHPOINT_INFO* patchpointInfo ) = 0; + // Retrieve OSR info for the method currently being jitted. + virtual CORINFO_PATCHPOINT_INFO* getOSRInfo( + unsigned *ilOffset, // [OUT] il offset of OSR entry point + ) = 0; + // Used to allocate memory that needs to handed to the EE. // For eg, use this to allocated memory for reporting debug info, // which will be handed to the EE by setVars() and setBoundaries() diff --git a/src/coreclr/src/vm/jitinterface.cpp b/src/coreclr/src/vm/jitinterface.cpp index 2568d3f81563cd..90b068c67e7678 100644 --- a/src/coreclr/src/vm/jitinterface.cpp +++ b/src/coreclr/src/vm/jitinterface.cpp @@ -10961,6 +10961,7 @@ void CEEJitInfo::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) #ifdef FEATURE_ON_STACK_REPLACEMENT // We receive ownership of the array _ASSERTE(m_pPatchpointInfo == NULL); + m_fOwnsPatchpointInfo = true; m_pPatchpointInfo = patchpointInfo; #else UNREACHABLE(); @@ -10969,6 +10970,29 @@ void CEEJitInfo::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) EE_TO_JIT_TRANSITION(); } +CORINFO_PATCHPOINT_INFO* CEEJitInfo::getPatchpointInfo(unsigned* ilOffset) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + CORINFO_PATCHPONT_INFO* result = NULL; + *ilOffset = = 0; + + JIT_TO_EE_TRANSITION(); + +#ifdef FEATURE_ON_STACK_REPLACEMENT + result = m_pPatchpointInfo; + *ilOffset = m_ilOffset; +#endif + + EE_TO_JIT_TRANSITION(); + + return result; +} + void CEEJitInfo::CompressDebugInfo() { CONTRACTL { @@ -12682,14 +12706,6 @@ PCODE UnsafeJitFunction(NativeCodeVersion nativeCodeVersion, COR_ILMETHOD_DECODE getMethodInfoHelper(ftn, ftnHnd, ILHeader, &methodInfo); -#ifdef FEATURE_ON_STACK_REPLACEMENT - if (flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_OSR)) - { - CORINFO_OSR_INFO *osrInfo = nativeCodeVersion.GetOSRInfo(); - methodInfo.osrInfo = *osrInfo; - } -#endif - // If it's generic then we can only enter through an instantiated md (unless we're just verifying it) _ASSERTE(flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) || !ftn->IsGenericMethodDefinition()); @@ -12754,6 +12770,14 @@ PCODE UnsafeJitFunction(NativeCodeVersion nativeCodeVersion, COR_ILMETHOD_DECODE jitInfo.SetReserveForJumpStubs(reserveForJumpStubs); #endif +#ifdef FEATURE_ON_STACK_REPLACEMENT + if (flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_OSR)) + { + CORINFO_OSR_INFO *osrInfo = nativeCodeVersion.GetOSRInfo(); + jitInfo.SetOSRInfo(osrInfo); + } +#endif + MethodDesc * pMethodForSecurity = jitInfo.GetMethodForSecurity(ftnHnd); //Since the check could trigger a demand, we have to do this every time. @@ -13932,6 +13956,12 @@ void CEEInfo::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) UNREACHABLE(); // only called on derived class. } +CORINFO_PATCHPOINT_INFO* CEEInfo::getPatchpointInfo(unsigned* ilOffset) +{ + LIMITED_METHOD_CONTRACT; + UNREACHABLE(); // only called on derived class. +} + void* CEEInfo::getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ void ** ppIndirection) /* OUT */ { diff --git a/src/coreclr/src/vm/jitinterface.h b/src/coreclr/src/vm/jitinterface.h index 33e963402d6d93..3cea1393c4216c 100644 --- a/src/coreclr/src/vm/jitinterface.h +++ b/src/coreclr/src/vm/jitinterface.h @@ -824,6 +824,7 @@ class CEEInfo : public ICorJitInfo void setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo *vars); void setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo); + CORINFO_PATCHPOINT_INFO* getOSRInfo(ULONG32* ilOffset); // ICorArgInfo stuff @@ -1217,8 +1218,6 @@ class CEEJitInfo : public CEEInfo void * allocGCInfo (size_t size); - void * allocPatchpointInfo (size_t size); - void setEHcount (unsigned cEH); void setEHinfo ( @@ -1294,10 +1293,12 @@ class CEEJitInfo : public CEEInfo m_pNativeVarInfo = NULL; #ifdef FEATURE_ON_STACK_REPLACEMENT - if (m_pPatchpointInfo != NULL) + if (m_fOwnsPatchpointInfo && (m_pPatchpointInfo != NULL)) delete [] ((BYTE*) m_pPatchpointInfo); m_pPatchpointInfo = NULL; + m_fOwnsPatchpointInfo = false; + m_ilOffset = 0; #endif #ifdef FEATURE_EH_FUNCLETS @@ -1362,6 +1363,17 @@ class CEEJitInfo : public CEEInfo } #endif +#ifdef FEATURE_ON_STACK_REPLACEMENT + void SetOSRInfo(CORINFO_OSR_INFO* info) + { + _ASSERTE(m_pPatchpointInfo == NULL); + _ASSERTE(info->patchpointInfo != NULL); + m_pPatchpointInfo = info->patchpointInfo; + m_ilOffset = info->ilOffset; + m_fOwnsPatchpointInfo = false; + } +#endif + CEEJitInfo(MethodDesc* fd, COR_ILMETHOD_DECODER* header, EEJitManager* jm, bool fVerifyOnly, bool allowInlining = true) : CEEInfo(fd, fVerifyOnly, allowInlining), @@ -1390,7 +1402,9 @@ class CEEJitInfo : public CEEInfo m_iNativeVarInfo(0), m_pNativeVarInfo(NULL), #ifdef FEATURE_ON_STACK_REPLACEMENT + m_fOwnsPatchpointInfo(false), m_pPatchpointInfo(NULL), + m_ilOffset(0) #endif m_gphCache() { @@ -1420,7 +1434,7 @@ class CEEJitInfo : public CEEInfo delete [] ((BYTE*) m_pNativeVarInfo); #ifdef FEATURE_ON_STACK_REPLACEMENT - if (m_pPatchpointInfo != NULL) + if (m_fOwnsPatchpointInfo && (m_pPatchpointInfo != NULL)) delete [] ((BYTE*) m_pPatchpointInfo); #endif @@ -1496,7 +1510,9 @@ protected : ICorDebugInfo::NativeVarInfo * m_pNativeVarInfo; #ifdef FEATURE_ON_STACK_REPLACEMENT + bool m_fOwnsPatchpointInfo; CORINFO_PATCHPOINT_INFO * m_pPatchpointInfo; + unsigned m_ilOffset; #endif // The first time a call is made to CEEJitInfo::GetProfilingHandle() from this thread From 270f3c7bd8dc8094cfb79da0d4cf21dff5b19588 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 5 Mar 2020 17:36:01 -0800 Subject: [PATCH 06/31] more WIP; builds and seems to work --- .../src/ToolBox/superpmi/mcs/verbdumpmap.cpp | 8 - .../superpmi-shared/compileresult.cpp | 10 +- .../superpmi/superpmi-shared/compileresult.h | 4 +- .../superpmi-shared/icorjitinfoimpl.h | 14 +- .../superpmi/superpmi-shared/lwmlist.h | 2 + .../superpmi-shared/methodcontext.cpp | 81 ++++----- .../superpmi/superpmi-shared/methodcontext.h | 18 +- .../superpmi/superpmi-shared/runtimedetails.h | 1 + .../superpmi-shim-collector/icorjitinfo.cpp | 25 ++- .../superpmi-shim-counter/icorjitinfo.cpp | 21 ++- .../superpmi-shim-simple/icorjitinfo.cpp | 19 +- .../ToolBox/superpmi/superpmi/icorjitinfo.cpp | 23 ++- src/coreclr/src/inc/corinfo.h | 38 ++-- src/coreclr/src/inc/patchpointinfo.h | 163 ++++++++++++++++++ src/coreclr/src/jit/codegencommon.cpp | 16 +- src/coreclr/src/jit/codegenxarch.cpp | 1 + src/coreclr/src/jit/compiler.cpp | 16 +- src/coreclr/src/jit/compiler.h | 12 +- src/coreclr/src/jit/gcencode.cpp | 13 +- src/coreclr/src/jit/lclvars.cpp | 3 +- src/coreclr/src/vm/codeversion.cpp | 61 +++---- src/coreclr/src/vm/codeversion.h | 27 +-- src/coreclr/src/vm/debuginfostore.cpp | 35 ++-- src/coreclr/src/vm/debuginfostore.h | 6 +- src/coreclr/src/vm/jithelpers.cpp | 32 ++-- src/coreclr/src/vm/jitinterface.cpp | 32 ++-- src/coreclr/src/vm/jitinterface.h | 25 +-- src/coreclr/src/vm/method.hpp | 3 - src/coreclr/src/vm/prestub.cpp | 12 +- src/coreclr/src/zap/zapinfo.cpp | 8 +- src/coreclr/src/zap/zapinfo.h | 5 +- 31 files changed, 450 insertions(+), 284 deletions(-) create mode 100644 src/coreclr/src/inc/patchpointinfo.h diff --git a/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp b/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp index 7c3f2cc44a6a72..ac3955d6a08e32 100644 --- a/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp +++ b/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp @@ -42,14 +42,6 @@ void DumpMap(int index, MethodContext* mc) DumpSigToConsoleBare(mc, &cmi.args); printf(")\""); - // Todo: dump tier0/tier1/fullopt request - - // Dump if this is an OSR request - if (cmi.osrInfo.patchpointInfo != nullptr) - { - printf(",Osr-%u", cmi.osrInfo.ilOffset); - } - printf("\n"); } diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp index 9056dec217df1f..5a6f82f5a0ec15 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.cpp @@ -326,8 +326,8 @@ bool CompileResult::repSetVars(CORINFO_METHOD_HANDLE* ftn, ULONG32* cVars, ICorD return true; } -// Note - Ownership of vars is transfered with this call. In replay icorjitinfo we should free it. -void CompileResult::recSetPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +// Note - Ownership of patchpointInfo is transfered with this call. In replay icorjitinfo we should free it. +void CompileResult::recSetPatchpointInfo(PatchpointInfo* patchpointInfo) { if (SetPatchpointInfo == nullptr) SetPatchpointInfo = new LightWeightMap(); @@ -338,13 +338,13 @@ void CompileResult::recSetPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo } void CompileResult::dmpSetPatchpointInfo(DWORD key, const Agnostic_SetPatchpointInfo& value) { - CORINFO_PATCHPOINT_INFO* patchpointInfo = (CORINFO_PATCHPOINT_INFO*)SetPatchpointInfo->GetBuffer(value.index); + PatchpointInfo* patchpointInfo = (PatchpointInfo*)SetPatchpointInfo->GetBuffer(value.index); printf("SetPatchpointInfo key %u, index %u{", key, value.index); // todo -- dump contents printf("}"); SetPatchpointInfo->Unlock(); } -bool CompileResult::repSetPatchpointInfo(CORINFO_PATCHPOINT_INFO** patchpointInfo) +bool CompileResult::repSetPatchpointInfo(PatchpointInfo** patchpointInfo) { if ((SetPatchpointInfo == nullptr) || (SetPatchpointInfo->GetCount() == 0)) { @@ -354,7 +354,7 @@ bool CompileResult::repSetPatchpointInfo(CORINFO_PATCHPOINT_INFO** patchpointInf Agnostic_SetPatchpointInfo value; value = SetPatchpointInfo->Get(0); - *patchpointInfo = (CORINFO_PATCHPOINT_INFO*)SetPatchpointInfo->GetBuffer(value.index); + *patchpointInfo = (PatchpointInfo*)SetPatchpointInfo->GetBuffer(value.index); return true; } diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.h b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.h index 9be342074a0dad..8fce7ab8e9f168 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.h +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/compileresult.h @@ -204,9 +204,9 @@ class CompileResult void dmpSetVars(DWORD key, const Agnostic_SetVars& value); bool repSetVars(CORINFO_METHOD_HANDLE* ftn, ULONG32* cVars, ICorDebugInfo::NativeVarInfo** vars); - void recSetPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo); + void recSetPatchpointInfo(PatchpointInfo* patchpointInfo); void dmpSetPatchpointInfo(DWORD key, const Agnostic_SetPatchpointInfo& value); - bool repSetPatchpointInfo(CORINFO_PATCHPOINT_INFO** patchpointInfo); + bool repSetPatchpointInfo(PatchpointInfo** patchpointInfo); void recAllocGCInfo(size_t size, void* retval); void recAllocGCInfoCapture(); diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h index 98036d5bf16ed7..9183ad7a301942 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h @@ -180,6 +180,15 @@ void getGSCookie(GSCookie* pCookieVal, // OUT GSCookie** ppCookieVal // OUT ); +// Provide patchpoint info for the method currently being jitted. +void setPatchpointInfo( + PatchpointInfo* patchpointInfo + ); + +PatchpointInfo* getOSRInfo( + unsigned * ilOffset // OUT + ); + /**********************************************************************************/ // // ICorModuleInfo @@ -561,11 +570,6 @@ void setVars(CORINFO_METHOD_HANDLE ftn, // [IN] method of interest // jit allocated with allocateArray, EE frees ); -// Provide patchpoint info for the method currently being jitted. -void setPatchpointInfo( - CORINFO_PATCHPOINT_INFO* patchpointInfo - ); - /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/lwmlist.h index c24e0e0b3c7bdd..c8aa01e3a6a03e 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/lwmlist.h @@ -112,6 +112,7 @@ LWM(GetMethodSync, DWORDLONG, DLDL) LWM(GetMethodVTableOffset, DWORDLONG, DDD) LWM(GetNewArrHelper, DWORDLONG, DWORD) LWM(GetNewHelper, Agnostic_GetNewHelper, DD) +LWM(GetOSRInfo, DWORD, Agnostic_GetOSRInfo) LWM(GetParentType, DWORDLONG, DWORDLONG) LWM(GetProfilingHandle, DWORD, Agnostic_GetProfilingHandle) LWM(GetReadyToRunHelper, GetReadyToRunHelper_TOKENin, GetReadyToRunHelper_TOKENout) @@ -152,5 +153,6 @@ LWM(TryResolveToken, Agnostic_CORINFO_RESOLVED_TOKENin, TryResolveTokenValue) LWM(SatisfiesClassConstraints, DWORDLONG, DWORD) LWM(SatisfiesMethodConstraints, DLDL, DWORD) + #undef LWM #undef DENSELWM diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index 58982be107235e..c4a9f4745cd13c 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -430,8 +430,6 @@ bool MethodContext::Equal(MethodContext* other) return false; if (otherFlags != ourFlags) return false; - if (otherInfo.osrInfo.ilOffset != ourInfo.osrInfo.ilOffset) - return false; // Now compare the other maps to "estimate" equality. @@ -655,20 +653,6 @@ void MethodContext::recCompileMethod(CORINFO_METHOD_INFO* info, unsigned flags) value.info.locals.token = (DWORD)info->locals.token; value.flags = (DWORD)flags; - value.info.osrInfo.ilOffset = (DWORD)info->osrInfo.ilOffset; - - DWORD* patchpointInfo = (DWORD*)info->osrInfo.patchpointInfo; - - if (patchpointInfo == nullptr) - { - value.info.osrInfo.patchpointInfo_Index = -1; - } - else - { - DWORD patchpointInfoSize = patchpointInfo[0]; - value.info.osrInfo.patchpointInfo_Index = CompileMethod->AddBuffer((unsigned char*)patchpointInfo, patchpointInfoSize); - } - CompileMethod->Add(0, value); DEBUG_REC(dmpCompileMethod(0, value)); } @@ -693,8 +677,6 @@ void MethodContext::dmpCompileMethod(DWORD key, const Agnostic_CompileMethod& va value.info.locals.sigInst_classInst_Index, value.info.locals.sigInst_methInstCount, value.info.locals.sigInst_methInst_Index, value.info.locals.args, value.info.locals.cbSig, value.info.locals.pSig_Index, value.info.locals.scope, value.info.locals.token, value.flags); - - printf(" osr{iloffs-%u ppInfo_index-%d}", value.info.osrInfo.ilOffset, value.info.osrInfo.patchpointInfo_Index); } void MethodContext::repCompileMethod(CORINFO_METHOD_INFO* info, unsigned* flags) { @@ -746,9 +728,6 @@ void MethodContext::repCompileMethod(CORINFO_METHOD_INFO* info, unsigned* flags) info->locals.scope = (CORINFO_MODULE_HANDLE)value.info.locals.scope; info->locals.token = (mdToken)value.info.locals.token; - info->osrInfo.ilOffset = (unsigned)value.info.osrInfo.ilOffset; - info->osrInfo.patchpointInfo = (CORINFO_PATCHPOINT_INFO*)CompileMethod->GetBuffer(value.info.osrInfo.patchpointInfo_Index); - *flags = (unsigned)value.flags; DEBUG_REP(dmpCompileMethod(0, value)); } @@ -2799,20 +2778,6 @@ void MethodContext::recGetMethodInfo(CORINFO_METHOD_HANDLE ftn, (DWORD)GetMethodInfo->AddBuffer((unsigned char*)info->locals.pSig, info->locals.cbSig); value.info.locals.scope = (DWORDLONG)info->locals.scope; value.info.locals.token = (DWORD)info->locals.token; - - value.info.osrInfo.ilOffset = info->osrInfo.ilOffset; - - DWORD* patchpointInfo = (DWORD*)info->osrInfo.patchpointInfo; - - if (patchpointInfo == nullptr) - { - value.info.osrInfo.patchpointInfo_Index = -1; - } - else - { - DWORD patchpointInfoSize = patchpointInfo[0]; - value.info.osrInfo.patchpointInfo_Index = GetMethodInfo->AddBuffer((unsigned char*)patchpointInfo, patchpointInfoSize); - } } value.result = result; value.exceptionCode = (DWORD)exceptionCode; @@ -2842,8 +2807,6 @@ void MethodContext::dmpGetMethodInfo(DWORDLONG key, const Agnostic_GetMethodInfo value.info.locals.sigInst_classInst_Index, value.info.locals.sigInst_methInstCount, value.info.locals.sigInst_methInst_Index, value.info.locals.args, value.info.locals.cbSig, value.info.locals.pSig_Index, value.info.locals.scope, value.info.locals.token, value.exceptionCode); - - printf(" osr{iloffs-%u ppInfo_index-%d}", value.info.osrInfo.ilOffset, value.info.osrInfo.patchpointInfo_Index); } bool MethodContext::repGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_INFO* info, DWORD* exceptionCode) { @@ -2898,9 +2861,6 @@ bool MethodContext::repGetMethodInfo(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_I info->locals.pSig = (PCCOR_SIGNATURE)GetMethodInfo->GetBuffer(value.info.locals.pSig_Index); info->locals.scope = (CORINFO_MODULE_HANDLE)value.info.locals.scope; info->locals.token = (mdToken)value.info.locals.token; - - info->osrInfo.ilOffset = (unsigned)value.info.osrInfo.ilOffset; - info->osrInfo.patchpointInfo = (CORINFO_PATCHPOINT_INFO*)GetMethodInfo->GetBuffer(value.info.osrInfo.patchpointInfo_Index); } bool result = value.result; *exceptionCode = (DWORD)value.exceptionCode; @@ -4006,6 +3966,39 @@ void MethodContext::repGetGSCookie(GSCookie* pCookieVal, GSCookie** ppCookieVal) *ppCookieVal = (GSCookie*)value.B; } +void MethodContext::recGetOSRInfo(PatchpointInfo* patchpointInfo, unsigned* ilOffset) +{ + if (GetOSRInfo == nullptr) + { + GetOSRInfo = new LightWeightMap(); + } + + Agnostic_GetOSRInfo value; + + value.index = (DWORD)GetOSRInfo->AddBuffer((const unsigned char*) patchpointInfo, patchpointInfo->PatchpointInfoSize()); + value.ilOffset = *ilOffset; + + // use 0 for key + DWORD key = 0; + GetOSRInfo->Add(key, value); + DEBUG_REC(dmpGetOSRInfo(key, value)); +} + +void MethodContext::dmpGetOSRInfo(DWORD key, const Agnostic_GetOSRInfo& value) +{ + // todo - dump patchpoint info? + printf("GetOSRInfo key %u, value patchpointInfo-%u {...} iloffset-%u\n", + key, value.index, value.ilOffset); +} + +PatchpointInfo* MethodContext::repGetOSRInfo(unsigned* ilOffset) +{ + DWORD key = 0; + Agnostic_GetOSRInfo value = GetOSRInfo->Get(key); + *ilOffset = value.ilOffset; + return (PatchpointInfo*)GetOSRInfo->GetBuffer(value.index); +} + void MethodContext::recGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE* pModule, void** ppIndirection, @@ -6233,14 +6226,6 @@ int MethodContext::dumpMethodIdentityInfoToBuffer(char* buff, int len, bool igno buff += t; len -= t; - // Add OSR info, if present. - if (info.osrInfo.patchpointInfo != nullptr) - { - t = sprintf_s(buff, len, "OSR IL Offset: %u", info.osrInfo.ilOffset); - buff += t; - len -= t; - } - return (int)(buff - obuff); } int MethodContext::dumpMethodMD5HashToBuffer(char* buff, int len, bool ignoreMethodName /* = false */) diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.h index b666e2857c33cb..5e2454d60affee 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -42,11 +42,6 @@ class MethodContext DWORDLONG scope; DWORD token; }; - struct Agnostic_CORINFO_OSR_INFO - { - DWORD ilOffset; - DWORD patchpointInfo_Index; - }; struct Agnostic_CORINFO_METHOD_INFO { DWORDLONG ftn; @@ -59,7 +54,6 @@ class MethodContext DWORD regionKind; Agnostic_CORINFO_SIG_INFO args; Agnostic_CORINFO_SIG_INFO locals; - Agnostic_CORINFO_OSR_INFO osrInfo; }; struct Agnostic_CompileMethod { @@ -182,6 +176,11 @@ class MethodContext DWORD targetAbi; DWORD osType; }; + struct Agnostic_GetOSRInfo + { + DWORD index; + unsigned ilOffset; + }; struct Agnostic_GetFieldAddress { DWORDLONG ppIndirection; @@ -992,6 +991,10 @@ class MethodContext void dmpGetGSCookie(DWORD key, DLDL value); void repGetGSCookie(GSCookie* pCookieVal, GSCookie** ppCookieVal); + void recGetOSRInfo(PatchpointInfo* patchpointInfo, unsigned* ilOffset); + void dmpGetOSRInfo(DWORD key, const Agnostic_GetOSRInfo& value); + PatchpointInfo* repGetOSRInfo(unsigned* ilOffset); + void recGetClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE* pModule, void** ppIndirection, @@ -1315,7 +1318,7 @@ class MethodContext }; // ********************* Please keep this up-to-date to ease adding more *************** -// Highest packet number: 176 +// Highest packet number: 177 // ************************************************************************************* enum mcPackets { @@ -1426,6 +1429,7 @@ enum mcPackets Packet_GetMethodVTableOffset = 78, Packet_GetNewArrHelper = 79, Packet_GetNewHelper = 80, + Packet_GetOSRInfo = 177, // Added 3/5/2020 Packet_GetParentType = 81, Packet_GetPInvokeUnmanagedTarget = 82, // Retired 2/18/2020 Packet_GetProfilingHandle = 83, diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/runtimedetails.h b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/runtimedetails.h index 3d6c7e23512d5b..a8723ce16b9f8d 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shared/runtimedetails.h +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shared/runtimedetails.h @@ -25,6 +25,7 @@ #include #include #include +#include /// Turn back on direct access to a few OS level things... #undef HeapCreate diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 2dff5c64cbf926..29dc318e6dd2e3 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -365,6 +365,23 @@ void interceptor_ICJI::getGSCookie(GSCookie* pCookieVal, // OUT mc->recGetGSCookie(pCookieVal, ppCookieVal); } +// Provide patchpoint info for the method currently being jitted. +void interceptor_ICJI::setPatchpointInfo(PatchpointInfo* patchpointInfo) +{ + mc->cr->AddCall("setPatchpointInfo"); + mc->cr->recSetPatchpointInfo(patchpointInfo); // Since the EE frees, we've gotta record before its sent to the EE. + original_ICorJitInfo->setPatchpointInfo(patchpointInfo); +} + +// Get OSR info for the method currently being jitted +PatchpointInfo* interceptor_ICJI::getOSRInfo(unsigned* ilOffset) +{ + mc->cr->AddCall("getOSRInfo"); + PatchpointInfo* patchpointInfo = original_ICorJitInfo->getOSRInfo(ilOffset); + mc->recGetOSRInfo(patchpointInfo, ilOffset); + return patchpointInfo; +} + /**********************************************************************************/ // // ICorModuleInfo @@ -1165,14 +1182,6 @@ void interceptor_ICJI::setVars(CORINFO_METHOD_HANDLE ftn, // [IN] meth original_ICorJitInfo->setVars(ftn, cVars, vars); } -// Provide patchpoint info for the method currently being jitted. -void interceptor_ICJI::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) -{ - mc->cr->AddCall("setPatchpointInfo"); - mc->cr->recSetPatchpointInfo(patchpointInfo); // Since the EE frees, we've gotta record before its sent to the EE. - original_ICorJitInfo->setPatchpointInfo(patchpointInfo); -} - /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. // For eg, use this to allocated memory for reporting debug info, diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp index be350234081de3..6a2dff8e1e3403 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -262,6 +262,20 @@ void interceptor_ICJI::getGSCookie(GSCookie* pCookieVal, // OUT original_ICorJitInfo->getGSCookie(pCookieVal, ppCookieVal); } +// Provide patchpoint info for the method currently being jitted. +void interceptor_ICJI::setPatchpointInfo(PatchpointInfo* patchpointInfo) +{ + mcs->AddCall("setPatchpointInfo"); + original_ICorJitInfo->setPatchpointInfo(patchpointInfo); +} + +// Get OSR info for the method currently being jitted +PatchpointInfo* interceptor_ICJI::getOSRInfo(unsigned* ilOffset) +{ + mcs->AddCall("getOSRInfo"); + return original_ICorJitInfo->getOSRInfo(ilOffset); +} + /**********************************************************************************/ // // ICorModuleInfo @@ -931,13 +945,6 @@ void interceptor_ICJI::setVars(CORINFO_METHOD_HANDLE ftn, // [IN] meth original_ICorJitInfo->setVars(ftn, cVars, vars); } -// Provide patchpoint info for the method currently being jitted. -void interceptor_ICJI::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) -{ - mcs->AddCall("setPatchpointInfo"); - original_ICorJitInfo->setPatchpointInfo(patchpointInfo); -} - /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp index da8af1193dc237..d043fde7ed949e 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -237,6 +237,19 @@ void interceptor_ICJI::getGSCookie(GSCookie* pCookieVal, // OUT original_ICorJitInfo->getGSCookie(pCookieVal, ppCookieVal); } + +// Provide patchpoint info for the method currently being jitted. +void interceptor_ICJI::setPatchpointInfo(PatchpointInfo* patchpointInfo) +{ + original_ICorJitInfo->setPatchpointInfo(patchpointInfo); +} + +// Get OSR info for the method currently being jitted +PatchpointInfo* interceptor_ICJI::getOSRInfo(unsigned* ilOffset) +{ + return original_ICorJitInfo->getOSRInfo(ilOffset); +} + /**********************************************************************************/ // // ICorModuleInfo @@ -830,12 +843,6 @@ void interceptor_ICJI::setVars(CORINFO_METHOD_HANDLE ftn, // [IN] meth original_ICorJitInfo->setVars(ftn, cVars, vars); } -// Provide patchpoint info for the method currently being jitted. -void interceptor_ICJI::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) -{ - original_ICorJitInfo->setPatchpointInfo(patchpointInfo); -} - /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. diff --git a/src/coreclr/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp index f90293a78fa419..ba742ecfd83d11 100644 --- a/src/coreclr/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp @@ -288,6 +288,21 @@ void MyICJI::getGSCookie(GSCookie* pCookieVal, // OUT jitInstance->mc->repGetGSCookie(pCookieVal, ppCookieVal); } +// Provide patchpoint info for the method currently being jitted. +void MyICJI::setPatchpointInfo(PatchpointInfo* patchpointInfo) +{ + jitInstance->mc->cr->AddCall("setPatchpointInfo"); + jitInstance->mc->cr->recSetPatchpointInfo(patchpointInfo); + freeArray(patchpointInfo); // See note in recSetPatchpointInfo... we own destroying this array +} + +// Get OSR info for the method currently being jitted +PatchpointInfo* MyICJI::getOSRInfo(unsigned* ilOffset) +{ + jitInstance->mc->cr->AddCall("getOSRInfo"); + return jitInstance->mc->repGetOSRInfo(ilOffset); +} + /**********************************************************************************/ // // ICorModuleInfo @@ -1003,14 +1018,6 @@ void MyICJI::setVars(CORINFO_METHOD_HANDLE ftn, // [IN] method of inte freeArray(vars); // See note in recSetVars... we own destroying this array } -// Provide patchpoint info for the method currently being jitted. -void MyICJI::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) -{ - jitInstance->mc->cr->AddCall("setPatchpointInfo"); - jitInstance->mc->cr->recSetPatchpointInfo(patchpointInfo); - freeArray(patchpointInfo); // See note in recSetPatchpointInfo... we own destroying this array -} - /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. diff --git a/src/coreclr/src/inc/corinfo.h b/src/coreclr/src/inc/corinfo.h index 6c9036fc294a9a..9978fac982c5cd 100644 --- a/src/coreclr/src/inc/corinfo.h +++ b/src/coreclr/src/inc/corinfo.h @@ -338,13 +338,6 @@ struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR } }; -// CORINFO_PATCHPOINT_INFO -// -// Communicates layout of the original method stack to the jit -// for an On-Stack Replacement (OSR) jit request. - -struct CORINFO_PATCHPOINT_INFO; - // CorInfoHelpFunc defines the set of helpers (accessed via the ICorDynamicInfo::getHelperFtn()) // These helpers can be called by native code which executes in the runtime. // Compilers can emit calls to these helpers. @@ -1092,6 +1085,11 @@ inline bool dontInline(CorInfoInline val) { return(val < 0); } +// Patchpoint info is passed back and forth across the interface +// but is opaque. + +struct PatchpointInfo; + // Cookie types consumed by the code generator (these are opaque values // not inspected by the code generator): @@ -1173,12 +1171,6 @@ struct CORINFO_SIG_INFO bool hasTypeArg() { return ((callConv & CORINFO_CALLCONV_PARAMTYPE) != 0); } }; -struct CORINFO_OSR_INFO -{ - unsigned ilOffset; // IL offset of the patchpoint - CORINFO_PATCHPOINT_INFO* patchpointInfo; // patchpoint info blob for the original method -}; - struct CORINFO_METHOD_INFO { CORINFO_METHOD_HANDLE ftn; @@ -2160,6 +2152,16 @@ class ICorStaticInfo GSCookie ** ppCookieVal // OUT ) = 0; + // Provide patchpoint info for the method currently being jitted. + virtual void setPatchpointInfo( + PatchpointInfo* patchpointInfo + ) = 0; + + // Get patchpoint info and il offset for the method currently being jitted. + virtual PatchpointInfo* getOSRInfo( + unsigned *ilOffset // [OUT] il offset of OSR entry point + ) = 0; + /**********************************************************************************/ // // ICorModuleInfo @@ -2673,16 +2675,6 @@ class ICorStaticInfo /*-------------------------- Misc ---------------------------------------*/ - // Provide patchpoint info for the method currently being jitted. - virtual void setPatchpointInfo( - CORINFO_PATCHPOINT_INFO* patchpointInfo - ) = 0; - - // Retrieve OSR info for the method currently being jitted. - virtual CORINFO_PATCHPOINT_INFO* getOSRInfo( - unsigned *ilOffset, // [OUT] il offset of OSR entry point - ) = 0; - // Used to allocate memory that needs to handed to the EE. // For eg, use this to allocated memory for reporting debug info, // which will be handed to the EE by setVars() and setBoundaries() diff --git a/src/coreclr/src/inc/patchpointinfo.h b/src/coreclr/src/inc/patchpointinfo.h new file mode 100644 index 00000000000000..c3deed24238c3f --- /dev/null +++ b/src/coreclr/src/inc/patchpointinfo.h @@ -0,0 +1,163 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// -------------------------------------------------------------------------------- +// patchpointinfo.h +// -------------------------------------------------------------------------------- + +#include + +#ifndef _PATCHPOINTINFO_H_ +#define _PATCHPOINTINFO_H_ + +#ifdef FEATURE_ON_STACK_REPLACEMENT + +// -------------------------------------------------------------------------------- +// Describes information needed to make an OSR transition +// - location of Il-visible locals and other important state on the +// original (Tier0) method frame +// - total size of the original frame, and SP-FP delta +// +// Currently the patchpoint info is independent of the IL offset of the patchpoint. +// +// This data is produced when jitting a Tier0 method with OSR enabled, and consumed +// by the Tier1/OSR jit request. +// +struct PatchpointInfo +{ + // Determine how much storage is needed to hold this info + static unsigned ComputeSize(unsigned localCount) + { + unsigned baseSize = sizeof(PatchpointInfo); + unsigned variableSize = localCount * sizeof(int); + unsigned totalSize = baseSize + variableSize; + return totalSize; + } + + // Initialize + void Initialize(unsigned localCount, unsigned ilSize, int fpToSpDelta) + { + m_patchpointInfoSize = ComputeSize(localCount); + m_ilSize = ilSize; + m_fpToSpDelta = fpToSpDelta; + m_numberOfLocals = localCount; + m_genericContextArgOffset = -1; + m_keptAliveThisOffset = -1; + m_securityCookieOffset = -1; + } + + // Total size of this patchpoint info record, in bytes + unsigned PatchpointInfoSize() const + { + return m_patchpointInfoSize; + } + + // IL Size of the original method + unsigned ILSize() const + { + return m_ilSize; + } + + // FP to SP delta of the original method + int FpToSpDelta() const + { + return m_fpToSpDelta; + } + + // Number of locals in the original method (including special locals) + unsigned NumberOfLocals() const + { + return m_numberOfLocals; + } + + // Original method caller SP offset for generic context arg + int GenericContextArgOffset() const + { + return m_genericContextArgOffset; + } + + void SetGenericContextArgOffset(int offset) + { + m_genericContextArgOffset = offset; + } + + // Original method FP relative offset for kept-alive this + int KeptAliveThisOffset() const + { + return m_keptAliveThisOffset; + } + + bool HasKeptAliveThis() const + { + return m_keptAliveThisOffset != -1; + } + + void SetKeptAliveThisOffset(int offset) + { + m_keptAliveThisOffset = offset; + } + + // Original method FP relative offset for security cookie + int SecurityCookieOffset() const + { + return m_securityCookieOffset; + } + + bool HasSecurityCookie() const + { + return m_securityCookieOffset != -1; + } + + void SetSecurityCookieOffset(int offset) + { + m_securityCookieOffset = offset; + } + + // True if this local was address exposed in the original method + bool IsExposed(unsigned localNum) const + { + return ((m_offsetAndExposureData[localNum] & EXPOSURE_MASK) != 0); + } + + void SetIsExposed(unsigned localNum) + { + m_offsetAndExposureData[localNum] |= EXPOSURE_MASK; + } + + // FP relative offset of this local in the original method + int Offset(unsigned localNum) const + { + return (m_offsetAndExposureData[localNum] & ~EXPOSURE_MASK); + } + + void SetOffset(unsigned localNum, int offset) + { + m_offsetAndExposureData[localNum] = offset; + } + +private: + enum + { + EXPOSURE_MASK = 0x1 + }; + + unsigned m_patchpointInfoSize; + unsigned m_ilSize; + unsigned m_numberOfLocals; + int m_fpToSpDelta; + int m_genericContextArgOffset; + int m_keptAliveThisOffset; + int m_securityCookieOffset; + int m_offsetAndExposureData[]; +}; + +#else + +struct PatchpointInfo {}; + +#endif // FEATURE_ON_STACK_REPLACEMENT + +typedef DPTR(struct PatchpointInfo) PTR_PatchpointInfo; + +#endif // _PATCHPOINTINFO_H_ diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index f18bb96df5e512..d232ec75fbb7fa 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -28,6 +28,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "gcinfoencoder.h" #endif +#include "patchpointinfo.h" + /*****************************************************************************/ const BYTE genTypeSizes[] = { @@ -6507,7 +6509,7 @@ void CodeGen::genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg, // Initialize args and locals for OSR. Note this may include promoted fields. if (compiler->opts.IsOSR()) { - CORINFO_PATCHPOINT_INFO* patchpointInfo = compiler->info.compPatchpointInfo; + PatchpointInfo* patchpointInfo = compiler->info.compPatchpointInfo; // basic sanity checks (make sure we're OSRing the right method) assert(patchpointInfo->NumberOfLocals() == compiler->info.compLocalsCount); @@ -7262,8 +7264,8 @@ void CodeGen::genFnProlog() // in the original frame that impact RBP and RSP on entry to the OSR method. if (compiler->opts.IsOSR()) { - CORINFO_PATCHPOINT_INFO* patchpointInfo = compiler->info.compPatchpointInfo; - const int originalFrameSize = patchpointInfo->FpToSpDelta(); + PatchpointInfo* patchpointInfo = compiler->info.compPatchpointInfo; + const int originalFrameSize = patchpointInfo->FpToSpDelta(); compiler->unwindPush(REG_FPBASE); compiler->unwindAllocStack(originalFrameSize); @@ -8443,8 +8445,8 @@ void CodeGen::genFnEpilog(BasicBlock* block) // will save and restore what it needs. if (compiler->opts.IsOSR()) { - CORINFO_PATCHPOINT_INFO* patchpointInfo = compiler->info.compPatchpointInfo; - const int originalFrameSize = patchpointInfo->FpToSpDelta(); + PatchpointInfo* patchpointInfo = compiler->info.compPatchpointInfo; + const int originalFrameSize = patchpointInfo->FpToSpDelta(); // Use add since we know the SP-to-FP delta of the original method. // @@ -8567,8 +8569,8 @@ void CodeGen::genFnEpilog(BasicBlock* block) // will save and restore what it needs. if (compiler->opts.IsOSR()) { - CORINFO_PATCHPOINT_INFO* patchpointInfo = compiler->info.compPatchpointInfo; - const int originalFrameSize = patchpointInfo->FpToSpDelta(); + PatchpointInfo* patchpointInfo = compiler->info.compPatchpointInfo; + const int originalFrameSize = patchpointInfo->FpToSpDelta(); // Use add since we know the SP-to-FP delta of the original method. // We also need to skip over the slot where we pushed RBP. diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index 283a58b2e6a688..f78ddb38911fef 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -22,6 +22,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "lower.h" #include "gcinfo.h" #include "gcinfoencoder.h" +#include "patchpointinfo.h" /***************************************************************************** * diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 4570aed2763150..66a0a3a23c4e27 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -22,6 +22,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "lower.h" #include "stacklevelsetter.h" #include "jittelemetry.h" +#include "patchpointinfo.h" #if defined(DEBUG) // Column settings for COMPlus_JitDumpIR. We could(should) make these programmable. @@ -4937,9 +4938,8 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags assert(codeGen->isFramePointerUsed()); // Allocate patchpoint info storage from runtime, and fill in initial bits of data. - const unsigned patchpointInfoSize = CORINFO_PATCHPOINT_INFO::ComputeSize(info.compLocalsCount); - CORINFO_PATCHPOINT_INFO* const patchpointInfo = - (CORINFO_PATCHPOINT_INFO*)info.compCompHnd->allocateArray(patchpointInfoSize); + const unsigned patchpointInfoSize = PatchpointInfo::ComputeSize(info.compLocalsCount); + PatchpointInfo* const patchpointInfo = (PatchpointInfo*)info.compCompHnd->allocateArray(patchpointInfoSize); patchpointInfo->Initialize(info.compLocalsCount, info.compILCodeSize, codeGen->genSPtoFPdelta() + TARGET_POINTER_SIZE); @@ -5291,16 +5291,10 @@ int Compiler::compCompile(CORINFO_METHOD_HANDLE methodHnd, } else if (compileFlags->IsSet(JitFlags::JIT_FLAG_OSR)) { - info.compILEntry = methodInfo->osrInfo.ilOffset; - info.compPatchpointInfo = methodInfo->osrInfo.patchpointInfo; - + // Fetch OSR info from the runtime + info.compPatchpointInfo = info.compCompHnd->getOSRInfo(&info.compILEntry); assert(info.compPatchpointInfo != nullptr); } - else - { - // If JIT_FLAG_OSR is not set, we should not be passed valid OSR info - assert(methodInfo->osrInfo.patchpointInfo == nullptr); - } virtualStubParamInfo = new (this, CMK_Unknown) VirtualStubParamInfo(IsTargetAbi(CORINFO_CORERT_ABI)); diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 82ced69c91593c..6f9c9f79db32b1 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -8820,12 +8820,12 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // The following holds the class attributes for the method we're compiling. unsigned compClassAttr; - const BYTE* compCode; - IL_OFFSET compILCodeSize; // The IL code size - IL_OFFSET compILImportSize; // Estimated amount of IL actually imported - IL_OFFSET compILEntry; // The IL entry point (normally 0) - CORINFO_PATCHPOINT_INFO* compPatchpointInfo; // Patchpoint data for OSR (normally nullptr) - UNATIVE_OFFSET compNativeCodeSize; // The native code size, after instructions are issued. This + const BYTE* compCode; + IL_OFFSET compILCodeSize; // The IL code size + IL_OFFSET compILImportSize; // Estimated amount of IL actually imported + IL_OFFSET compILEntry; // The IL entry point (normally 0) + PatchpointInfo* compPatchpointInfo; // Patchpoint data for OSR (normally nullptr) + UNATIVE_OFFSET compNativeCodeSize; // The native code size, after instructions are issued. This // is less than (compTotalHotCodeSize + compTotalColdCodeSize) only if: // (1) the code is not hot/cold split, and we issued less code than we expected, or // (2) the code is hot/cold split, and we issued less code than we expected diff --git a/src/coreclr/src/jit/gcencode.cpp b/src/coreclr/src/jit/gcencode.cpp index 75cf7c22edf7ac..12381ce0e3de55 100644 --- a/src/coreclr/src/jit/gcencode.cpp +++ b/src/coreclr/src/jit/gcencode.cpp @@ -22,6 +22,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #endif #include "gcinfotypes.h" +#include "patchpointinfo.h" ReturnKind GCTypeToReturnKind(CorInfoGCType gcType) { @@ -3895,8 +3896,8 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz if (compiler->opts.IsOSR()) { - CORINFO_PATCHPOINT_INFO* ppInfo = compiler->info.compPatchpointInfo; - offset = ppInfo->GenericContextArgOffset(); + PatchpointInfo* ppInfo = compiler->info.compPatchpointInfo; + offset = ppInfo->GenericContextArgOffset(); assert(offset != -1); } else @@ -3917,8 +3918,8 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz if (compiler->opts.IsOSR()) { - CORINFO_PATCHPOINT_INFO* ppInfo = compiler->info.compPatchpointInfo; - offset = ppInfo->GenericContextArgOffset(); + PatchpointInfo* ppInfo = compiler->info.compPatchpointInfo; + offset = ppInfo->GenericContextArgOffset(); assert(offset != -1); } else @@ -3946,8 +3947,8 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz // pushed RA and RBP for that frame. But ppInfo's FpToSpDelta also accounts for the // pseudo-RA between the original method frame and the OSR frame. So the net adjustment // is simply FpToSpDelta plus one register. - CORINFO_PATCHPOINT_INFO* ppInfo = compiler->info.compPatchpointInfo; - int adjustment = ppInfo->FpToSpDelta() + REGSIZE_BYTES; + PatchpointInfo* ppInfo = compiler->info.compPatchpointInfo; + int adjustment = ppInfo->FpToSpDelta() + REGSIZE_BYTES; offset -= adjustment; JITDUMP("OSR cookie adjustment %d, final caller-SP offset %d\n", adjustment, offset); } diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 4b5b0f77babd38..cd7f96665f130f 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -21,6 +21,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "emit.h" #include "register_arg_convention.h" #include "jitstd/algorithm.h" +#include "patchpointinfo.h" /*****************************************************************************/ @@ -5843,7 +5844,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() { if (lvaKeepAliveAndReportThis()) { - CORINFO_PATCHPOINT_INFO* ppInfo = info.compPatchpointInfo; + PatchpointInfo* ppInfo = info.compPatchpointInfo; assert(ppInfo->HasKeptAliveThis()); int originalOffset = ppInfo->KeptAliveThisOffset(); lvaCachedGenericContextArgOffs = originalFrameStkOffs + originalOffset; diff --git a/src/coreclr/src/vm/codeversion.cpp b/src/coreclr/src/vm/codeversion.cpp index 3f6df7e37525af..159342e4ee442c 100644 --- a/src/coreclr/src/vm/codeversion.cpp +++ b/src/coreclr/src/vm/codeversion.cpp @@ -8,6 +8,7 @@ #include "common.h" #include "codeversion.h" +#include "patchpointinfo.h" #ifdef FEATURE_CODE_VERSIONING #include "threadsuspend.h" @@ -64,7 +65,9 @@ NativeCodeVersionNode::NativeCodeVersionNode( NativeCodeVersionId id, MethodDesc* pMethodDesc, ReJITID parentId, - NativeCodeVersion::OptimizationTier optimizationTier) + NativeCodeVersion::OptimizationTier optimizationTier, + PatchpointInfo* patchpointInfo, + unsigned ilOffset) : m_pNativeCode(NULL), m_pMethodDesc(pMethodDesc), @@ -77,13 +80,12 @@ NativeCodeVersionNode::NativeCodeVersionNode( #ifdef HAVE_GCCOVER m_gcCover(PTR_NULL), #endif - m_flags(0) -{ - #ifdef FEATURE_ON_STACK_REPLACEMENT - m_osrInfo.ilOffset = 0; - m_osrInfo.patchpointInfo = NULL; + m_patchpointInfo(patchpointInfo), + m_ilOffset(ilOffset), #endif + m_flags(0) +{ } #endif @@ -188,22 +190,13 @@ void NativeCodeVersionNode::SetOptimizationTier(NativeCodeVersion::OptimizationT #ifdef FEATURE_ON_STACK_REPLACEMENT -CORINFO_OSR_INFO* NativeCodeVersionNode::GetOSRInfo() +PatchpointInfo* NativeCodeVersionNode::GetOSRInfo(unsigned * ilOffset) { LIMITED_METHOD_DAC_CONTRACT; - return &m_osrInfo; + *ilOffset = m_ilOffset; + return m_patchpointInfo; } -#ifndef DACCESS_COMPILE - -void NativeCodeVersionNode::SetOSRInfo(CORINFO_OSR_INFO * info) -{ - LIMITED_METHOD_CONTRACT; - m_osrInfo = *info; -} - -#endif - #endif // FEATURE_ON_STACK_REPLACEMENT #ifdef HAVE_GCCOVER @@ -438,12 +431,12 @@ void NativeCodeVersion::SetOptimizationTier(OptimizationTier tier) #ifdef FEATURE_ON_STACK_REPLACEMENT -CORINFO_OSR_INFO * NativeCodeVersion::GetOSRInfo() +PatchpointInfo * NativeCodeVersion::GetOSRInfo(unsigned * ilOffset) { LIMITED_METHOD_DAC_CONTRACT; if (m_storageKind == StorageKind::Explicit) { - return AsNode()->GetOSRInfo(); + return AsNode()->GetOSRInfo(ilOffset); } else { @@ -451,21 +444,6 @@ CORINFO_OSR_INFO * NativeCodeVersion::GetOSRInfo() } } -#ifndef DACCESS_COMPILE -void NativeCodeVersion::SetOSRInfo(CORINFO_OSR_INFO * info) -{ - WRAPPER_NO_CONTRACT; - if (m_storageKind == StorageKind::Explicit) - { - AsNode()->SetOSRInfo(info); - } - else - { - _ASSERTE(!"Cannot set OSR info here"); - } -} -#endif - #endif @@ -1079,11 +1057,14 @@ void ILCodeVersion::SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap) HRESULT ILCodeVersion::AddNativeCodeVersion( MethodDesc* pClosedMethodDesc, NativeCodeVersion::OptimizationTier optimizationTier, - NativeCodeVersion* pNativeCodeVersion) + NativeCodeVersion* pNativeCodeVersion, + PatchpointInfo* patchpointInfo, + unsigned ilOffset + ) { LIMITED_METHOD_CONTRACT; CodeVersionManager* pManager = GetModule()->GetCodeVersionManager(); - HRESULT hr = pManager->AddNativeCodeVersion(*this, pClosedMethodDesc, optimizationTier, pNativeCodeVersion); + HRESULT hr = pManager->AddNativeCodeVersion(*this, pClosedMethodDesc, optimizationTier, pNativeCodeVersion, patchpointInfo, ilOffset); if (FAILED(hr)) { _ASSERTE(hr == E_OUTOFMEMORY); @@ -1756,7 +1737,9 @@ HRESULT CodeVersionManager::AddNativeCodeVersion( ILCodeVersion ilCodeVersion, MethodDesc* pClosedMethodDesc, NativeCodeVersion::OptimizationTier optimizationTier, - NativeCodeVersion* pNativeCodeVersion) + NativeCodeVersion* pNativeCodeVersion, + PatchpointInfo* patchpointInfo, + unsigned ilOffset) { LIMITED_METHOD_CONTRACT; _ASSERTE(LockOwnedByCurrentThread()); @@ -1770,7 +1753,7 @@ HRESULT CodeVersionManager::AddNativeCodeVersion( } NativeCodeVersionId newId = pMethodVersioningState->AllocateVersionId(); - NativeCodeVersionNode* pNativeCodeVersionNode = new (nothrow) NativeCodeVersionNode(newId, pClosedMethodDesc, ilCodeVersion.GetVersionId(), optimizationTier); + NativeCodeVersionNode* pNativeCodeVersionNode = new (nothrow) NativeCodeVersionNode(newId, pClosedMethodDesc, ilCodeVersion.GetVersionId(), optimizationTier, patchpointInfo, ilOffset); if (pNativeCodeVersionNode == NULL) { return E_OUTOFMEMORY; diff --git a/src/coreclr/src/vm/codeversion.h b/src/coreclr/src/vm/codeversion.h index f55d9faa5ca4ad..805396dfa2a072 100644 --- a/src/coreclr/src/vm/codeversion.h +++ b/src/coreclr/src/vm/codeversion.h @@ -42,6 +42,11 @@ class GCCoverageInfo; typedef DPTR(class GCCoverageInfo) PTR_GCCoverageInfo; #endif +#ifdef FEATURE_ON_STACK_REPLACEMENT +struct PatchpointInfo; +typedef DPTR(struct PatchpointInfo) PTR_PatchpointInfo; +#endif + class NativeCodeVersion { #ifdef FEATURE_CODE_VERSIONING @@ -86,10 +91,7 @@ class NativeCodeVersion #endif // FEATURE_TIERED_COMPILATION #ifdef FEATURE_ON_STACK_REPLACEMENT - CORINFO_OSR_INFO * GetOSRInfo(); -#ifndef DACCESS_COMPILE - void SetOSRInfo(CORINFO_OSR_INFO* info); -#endif + PatchpointInfo * GetOSRInfo(unsigned * iloffset); #endif // FEATURE_ON_STACK_REPLACEMENT #ifdef HAVE_GCCOVER @@ -175,7 +177,8 @@ class ILCodeVersion void SetIL(COR_ILMETHOD* pIL); void SetJitFlags(DWORD flags); void SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap); - HRESULT AddNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion::OptimizationTier optimizationTier, NativeCodeVersion* pNativeCodeVersion); + HRESULT AddNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion::OptimizationTier optimizationTier, + NativeCodeVersion* pNativeCodeVersion, PatchpointInfo* patchpointInfo = NULL, unsigned ilOffset = 0); HRESULT GetOrCreateActiveNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion); HRESULT SetActiveNativeCodeVersion(NativeCodeVersion activeNativeCodeVersion, BOOL fEESuspended); #endif //DACCESS_COMPILE @@ -254,7 +257,8 @@ class NativeCodeVersionNode public: #ifndef DACCESS_COMPILE - NativeCodeVersionNode(NativeCodeVersionId id, MethodDesc* pMethod, ReJITID parentId, NativeCodeVersion::OptimizationTier optimizationTier); + NativeCodeVersionNode(NativeCodeVersionId id, MethodDesc* pMethod, ReJITID parentId, NativeCodeVersion::OptimizationTier optimizationTier, + PatchpointInfo* patchpointInfo, unsigned ilOffset); #endif #ifdef DEBUG @@ -285,10 +289,7 @@ class NativeCodeVersionNode #endif #ifdef FEATURE_ON_STACK_REPLACEMENT - CORINFO_OSR_INFO * GetOSRInfo(); -#ifndef DACCESS_COMPILE - void SetOSRInfo(CORINFO_OSR_INFO * info); -#endif + PatchpointInfo * GetOSRInfo(unsigned * ilOffset); #endif private: @@ -308,7 +309,8 @@ class NativeCodeVersionNode PTR_GCCoverageInfo m_gcCover; #endif #ifdef FEATURE_ON_STACK_REPLACEMENT - CORINFO_OSR_INFO m_osrInfo; + PTR_PatchpointInfo m_patchpointInfo; + unsigned m_ilOffset; #endif enum NativeCodeVersionNodeFlags @@ -613,7 +615,8 @@ class CodeVersionManager }; HRESULT AddILCodeVersion(Module* pModule, mdMethodDef methodDef, ReJITID rejitId, ILCodeVersion* pILCodeVersion); - HRESULT AddNativeCodeVersion(ILCodeVersion ilCodeVersion, MethodDesc* pClosedMethodDesc, NativeCodeVersion::OptimizationTier optimizationTier, NativeCodeVersion* pNativeCodeVersion); + HRESULT AddNativeCodeVersion(ILCodeVersion ilCodeVersion, MethodDesc* pClosedMethodDesc, NativeCodeVersion::OptimizationTier optimizationTier, NativeCodeVersion* pNativeCodeVersion, + PatchpointInfo* patchpointInfo = NULL, unsigned ilOffset = 0); PCODE PublishVersionableCodeIfNecessary(MethodDesc* pMethodDesc, bool *doBackpatchRef, bool *doFullBackpatchRef); HRESULT PublishNativeCodeVersion(MethodDesc* pMethodDesc, NativeCodeVersion nativeCodeVersion, BOOL fEESuspended); HRESULT GetOrCreateMethodDescVersioningState(MethodDesc* pMethod, MethodDescVersioningState** ppMethodDescVersioningState); diff --git a/src/coreclr/src/vm/debuginfostore.cpp b/src/coreclr/src/vm/debuginfostore.cpp index 8af8a1769bc3b3..478c530a7eb74d 100644 --- a/src/coreclr/src/vm/debuginfostore.cpp +++ b/src/coreclr/src/vm/debuginfostore.cpp @@ -8,6 +8,7 @@ #include "common.h" #include "debuginfostore.h" #include "nibblestream.h" +#include "patchpointinfo.h" #ifdef _DEBUG @@ -440,7 +441,7 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( IN ULONG iOffsetMapping, IN ICorDebugInfo::NativeVarInfo * pNativeVarInfo, IN ULONG iNativeVarInfo, - IN CORINFO_PATCHPOINT_INFO * patchpointInfo, + IN PatchpointInfo * patchpointInfo, IN OUT SBuffer * pDebugInfoBuffer, IN LoaderHeap * pLoaderHeap ) @@ -454,10 +455,15 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( // Patchpoint info is currently uncompressed. DWORD cbPatchpointInfo = 0; + +#ifdef FEATURE_ON_STACK_REPLACEMENT if (patchpointInfo != NULL) { cbPatchpointInfo = patchpointInfo->PatchpointInfoSize(); } +#else + _ASSERTE(patchpointInfo == NULL); +#endif // Actually do the compression. These will throw on oom. NibbleWriter boundsBuffer; @@ -505,6 +511,8 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( BYTE *ptr = ptrStart; +#ifdef FEATURE_ON_STACK_REPLACEMENT + // First byte is a flag byte: // 0 - no patchpoint info // 1 - patchpoint info @@ -517,6 +525,8 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( ptr += cbPatchpointInfo; } +#endif + memcpy(ptr, pHeader, cbHeader); ptr += cbHeader; @@ -539,13 +549,6 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( #endif // DACCESS_COMPILE -//----------------------------------------------------------------------------- -// Compression routines -// DAC only needs to run the uncompression routines. -//----------------------------------------------------------------------------- - -typedef DPTR(CORINFO_PATCHPOINT_INFO) PTR_CORINFO_PATCHPOINT_INFO; - //----------------------------------------------------------------------------- // Uncompression (restore) routines //----------------------------------------------------------------------------- @@ -574,13 +577,15 @@ void CompressDebugInfo::RestoreBoundariesAndVars( if (pcVars != NULL) *pcVars = 0; if (ppVars != NULL) *ppVars = NULL; +#ifdef FEATURE_ON_STACK_REPLACEMENT + // Check flag byte and skip over any patchpoint info BYTE flagByte = *pDebugInfo; pDebugInfo++; if (flagByte == 1) { - PTR_CORINFO_PATCHPOINT_INFO patchpointInfo = dac_cast(pDebugInfo); + PTR_PatchpointInfo patchpointInfo = dac_cast(pDebugInfo); pDebugInfo += patchpointInfo->PatchpointInfoSize(); } else @@ -588,6 +593,8 @@ void CompressDebugInfo::RestoreBoundariesAndVars( _ASSERTE(flagByte == 0); } +#endif + NibbleReader r(pDebugInfo, 12 /* maximum size of compressed 2 UINT32s */); ULONG cbBounds = r.ReadEncodedU32(); @@ -651,7 +658,9 @@ void CompressDebugInfo::RestoreBoundariesAndVars( } } -CORINFO_PATCHPOINT_INFO * CompressDebugInfo::RestorePatchpointInfo(IN PTR_BYTE pDebugInfo) +#ifdef FEATURE_ON_STACK_REPLACEMENT + +PatchpointInfo * CompressDebugInfo::RestorePatchpointInfo(IN PTR_BYTE pDebugInfo) { CONTRACTL { @@ -662,7 +671,7 @@ CORINFO_PATCHPOINT_INFO * CompressDebugInfo::RestorePatchpointInfo(IN PTR_BYTE p } CONTRACTL_END; - PTR_CORINFO_PATCHPOINT_INFO patchpointInfo = NULL; + PTR_PatchpointInfo patchpointInfo = NULL; // Check flag byte. BYTE flagByte = *pDebugInfo; @@ -670,7 +679,7 @@ CORINFO_PATCHPOINT_INFO * CompressDebugInfo::RestorePatchpointInfo(IN PTR_BYTE p if (flagByte == 1) { - patchpointInfo = dac_cast(pDebugInfo); + patchpointInfo = dac_cast(pDebugInfo); } else { @@ -680,6 +689,8 @@ CORINFO_PATCHPOINT_INFO * CompressDebugInfo::RestorePatchpointInfo(IN PTR_BYTE p return patchpointInfo; } +#endif + #ifdef DACCESS_COMPILE void CompressDebugInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo) { diff --git a/src/coreclr/src/vm/debuginfostore.h b/src/coreclr/src/vm/debuginfostore.h index c9195ad423bf77..eac10328e1ae1e 100644 --- a/src/coreclr/src/vm/debuginfostore.h +++ b/src/coreclr/src/vm/debuginfostore.h @@ -83,7 +83,7 @@ class CompressDebugInfo IN ULONG iOffsetMapping, IN ICorDebugInfo::NativeVarInfo * pNativeVarInfo, IN ULONG iNativeVarInfo, - IN CORINFO_PATCHPOINT_INFO * patchpointInfo, + IN PatchpointInfo * patchpointInfo, IN OUT SBuffer * pDebugInfoBuffer, IN LoaderHeap * pLoaderHeap ); @@ -99,9 +99,11 @@ class CompressDebugInfo OUT ICorDebugInfo::NativeVarInfo **ppVars ); - static CORINFO_PATCHPOINT_INFO * RestorePatchpointInfo( +#ifdef FEATURE_ON_STACK_REPLACEMENT + static PatchpointInfo * RestorePatchpointInfo( IN PTR_BYTE pDebugInfo ); +#endif #ifdef DACCESS_COMPILE static void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo); diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp index 20be815b83a0d8..cba420dec56f8a 100644 --- a/src/coreclr/src/vm/jithelpers.cpp +++ b/src/coreclr/src/vm/jithelpers.cpp @@ -5147,6 +5147,18 @@ static PCODE JitPatchpointWorker(void* ip, int ilOffset) EECodeInfo codeInfo((PCODE)ip); MethodDesc* pMD = codeInfo.GetMethodDesc(); + // Fetch the patchpoint info for the current method + EEJitManager* jitMgr = ExecutionManager::GetEEJitManager(); + CodeHeader* codeHdr = jitMgr->GetCodeHeaderFromStartAddress(codeInfo.GetStartAddress()); + PTR_BYTE debugInfo = codeHdr->GetDebugInfo(); + PatchpointInfo* patchpointInfo = CompressDebugInfo::RestorePatchpointInfo(debugInfo); + + if (patchpointInfo == NULL) + { + // log ... + return NULL; + } + // Find the il method version corresponding to this version of the method // and set up a new native code version for it. ReJITID rejitId = ReJitManager::GetReJitId(pMD, codeInfo.GetStartAddress()); @@ -5156,7 +5168,7 @@ static PCODE JitPatchpointWorker(void* ip, int ilOffset) // Request a new native version that is optimized. CodeVersionManager::TableLockHolder lock(codeVersionManager); ILCodeVersion ilCodeVersion = codeVersionManager->GetILCodeVersion(pMD, rejitId); - HRESULT hr = ilCodeVersion.AddNativeCodeVersion(pMD, NativeCodeVersion::OptimizationTier1, &osrNativeCodeVersion); + HRESULT hr = ilCodeVersion.AddNativeCodeVersion(pMD, NativeCodeVersion::OptimizationTier1, &osrNativeCodeVersion, patchpointInfo, ilOffset); if (FAILED(hr)) { // log ... @@ -5164,24 +5176,6 @@ static PCODE JitPatchpointWorker(void* ip, int ilOffset) } } - // Prepare info for the jit - CORINFO_OSR_INFO osrInfo; - osrInfo.ilOffset = ilOffset; - - // Fetch the patchpoint info for the current method - EEJitManager* jitMgr = ExecutionManager::GetEEJitManager(); - CodeHeader* codeHdr = jitMgr->GetCodeHeaderFromStartAddress(codeInfo.GetStartAddress()); - PTR_BYTE debugInfo = codeHdr->GetDebugInfo(); - CORINFO_PATCHPOINT_INFO* patchpointInfo = CompressDebugInfo::RestorePatchpointInfo(debugInfo); - osrInfo.patchpointInfo = patchpointInfo; - - if (osrInfo.patchpointInfo == NULL) - { - // log ... - return NULL; - } - osrNativeCodeVersion.SetOSRInfo(&osrInfo); - // Invoke the jit to compile the OSR version PrepareCodeConfigBuffer configBuffer(osrNativeCodeVersion); PrepareCodeConfig *config = configBuffer.GetConfig(); diff --git a/src/coreclr/src/vm/jitinterface.cpp b/src/coreclr/src/vm/jitinterface.cpp index 90b068c67e7678..51c20b66c7e715 100644 --- a/src/coreclr/src/vm/jitinterface.cpp +++ b/src/coreclr/src/vm/jitinterface.cpp @@ -7731,10 +7731,6 @@ getMethodInfoHelper( ftn, true); - // Set these to default values. Will overwrite later if actually doing an OSR jit request. - methInfo->osrInfo.ilOffset = 0; - methInfo->osrInfo.patchpointInfo = NULL; - } // getMethodInfoHelper //--------------------------------------------------------------------------------------- @@ -10948,7 +10944,7 @@ void CEEJitInfo::setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo EE_TO_JIT_TRANSITION(); } -void CEEJitInfo::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +void CEEJitInfo::setPatchpointInfo(PatchpointInfo* patchpointInfo) { CONTRACTL { THROWS; @@ -10970,7 +10966,7 @@ void CEEJitInfo::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) EE_TO_JIT_TRANSITION(); } -CORINFO_PATCHPOINT_INFO* CEEJitInfo::getPatchpointInfo(unsigned* ilOffset) +PatchpointInfo* CEEJitInfo::getOSRInfo(unsigned* ilOffset) { CONTRACTL { THROWS; @@ -10978,8 +10974,8 @@ CORINFO_PATCHPOINT_INFO* CEEJitInfo::getPatchpointInfo(unsigned* ilOffset) MODE_PREEMPTIVE; } CONTRACTL_END; - CORINFO_PATCHPONT_INFO* result = NULL; - *ilOffset = = 0; + PatchpointInfo* result = NULL; + *ilOffset = 0; JIT_TO_EE_TRANSITION(); @@ -11002,19 +10998,19 @@ void CEEJitInfo::CompressDebugInfo() } CONTRACTL_END; #ifdef FEATURE_ON_STACK_REPLACEMENT - CORINFO_PATCHPOINT_INFO* ppInfo = m_pPatchpointInfo; + PatchpointInfo* patchpointInfo = m_pPatchpointInfo; #else - CORINFO_PATCHPOINT_INFO* ppInfo = NULL; + PatchpointInfo* patchpointInfo = NULL; #endif // Don't track JIT info for DynamicMethods. if (m_pMethodBeingCompiled->IsDynamicMethod() && !g_pConfig->GetTrackDynamicMethodDebugInfo()) { - _ASSERTE(ppInfo == NULL); + _ASSERTE(patchpointInfo == NULL); return; } - if ((m_iOffsetMapping == 0) && (m_iNativeVarInfo == 0) && (ppInfo == NULL)) + if ((m_iOffsetMapping == 0) && (m_iNativeVarInfo == 0) && (patchpointInfo == NULL)) return; JIT_TO_EE_TRANSITION(); @@ -11024,7 +11020,7 @@ void CEEJitInfo::CompressDebugInfo() PTR_BYTE pDebugInfo = CompressDebugInfo::CompressBoundariesAndVars( m_pOffsetMapping, m_iOffsetMapping, m_pNativeVarInfo, m_iNativeVarInfo, - ppInfo, + patchpointInfo, NULL, m_pMethodBeingCompiled->GetLoaderAllocator()->GetLowFrequencyHeap()); @@ -12771,10 +12767,12 @@ PCODE UnsafeJitFunction(NativeCodeVersion nativeCodeVersion, COR_ILMETHOD_DECODE #endif #ifdef FEATURE_ON_STACK_REPLACEMENT + // If this is an OSR jit request, grab the OSR info so we can pass it to the jit if (flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_OSR)) { - CORINFO_OSR_INFO *osrInfo = nativeCodeVersion.GetOSRInfo(); - jitInfo.SetOSRInfo(osrInfo); + unsigned ilOffset = 0; + PatchpointInfo* patchpointInfo = nativeCodeVersion.GetOSRInfo(&ilOffset); + jitInfo.SetOSRInfo(patchpointInfo, ilOffset); } #endif @@ -13950,13 +13948,13 @@ void CEEInfo::setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::N UNREACHABLE(); // only called on derived class. } -void CEEInfo::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +void CEEInfo::setPatchpointInfo(PatchpointInfo* patchpointInfo) { LIMITED_METHOD_CONTRACT; UNREACHABLE(); // only called on derived class. } -CORINFO_PATCHPOINT_INFO* CEEInfo::getPatchpointInfo(unsigned* ilOffset) +PatchpointInfo* CEEInfo::getOSRInfo(unsigned* ilOffset) { LIMITED_METHOD_CONTRACT; UNREACHABLE(); // only called on derived class. diff --git a/src/coreclr/src/vm/jitinterface.h b/src/coreclr/src/vm/jitinterface.h index 3cea1393c4216c..c709016e749062 100644 --- a/src/coreclr/src/vm/jitinterface.h +++ b/src/coreclr/src/vm/jitinterface.h @@ -526,6 +526,9 @@ class CEEInfo : public ICorJitInfo CORINFO_CLASS_HANDLE getBuiltinClass(CorInfoClassId classId); void getGSCookie(GSCookie * pCookieVal, GSCookie ** ppCookieVal); + void setPatchpointInfo(PatchpointInfo* patchpointInfo); + PatchpointInfo* getOSRInfo(unsigned* ilOffset); + // "System.Int32" ==> CORINFO_TYPE_INT.. CorInfoType getTypeForPrimitiveValueClass( CORINFO_CLASS_HANDLE cls @@ -823,8 +826,6 @@ class CEEInfo : public ICorJitInfo ICorDebugInfo::ILVarInfo **vars, bool *extendOthers); void setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo *vars); - void setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo); - CORINFO_PATCHPOINT_INFO* getOSRInfo(ULONG32* ilOffset); // ICorArgInfo stuff @@ -1364,12 +1365,12 @@ class CEEJitInfo : public CEEInfo #endif #ifdef FEATURE_ON_STACK_REPLACEMENT - void SetOSRInfo(CORINFO_OSR_INFO* info) + void SetOSRInfo(PatchpointInfo* patchpointInfo, unsigned ilOffset) { _ASSERTE(m_pPatchpointInfo == NULL); - _ASSERTE(info->patchpointInfo != NULL); - m_pPatchpointInfo = info->patchpointInfo; - m_ilOffset = info->ilOffset; + _ASSERTE(patchpointInfo != NULL); + m_pPatchpointInfo = patchpointInfo; + m_ilOffset = ilOffset; m_fOwnsPatchpointInfo = false; } #endif @@ -1404,7 +1405,7 @@ class CEEJitInfo : public CEEInfo #ifdef FEATURE_ON_STACK_REPLACEMENT m_fOwnsPatchpointInfo(false), m_pPatchpointInfo(NULL), - m_ilOffset(0) + m_ilOffset(0), #endif m_gphCache() { @@ -1445,7 +1446,6 @@ class CEEJitInfo : public CEEInfo ULONG32 cMap, ICorDebugInfo::OffsetMapping *pMap); void setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo *vars); - void setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo); void CompressDebugInfo(); void* getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ @@ -1474,6 +1474,9 @@ class CEEJitInfo : public CEEInfo void BackoutJitData(EEJitManager * jitMgr); + void setPatchpointInfo(PatchpointInfo* patchpointInfo); + PatchpointInfo* getOSRInfo(unsigned* ilOffset); + protected : EEJitManager* m_jitManager; // responsible for allocating memory CodeHeader* m_CodeHeader; // descriptor for JITTED code @@ -1510,9 +1513,9 @@ protected : ICorDebugInfo::NativeVarInfo * m_pNativeVarInfo; #ifdef FEATURE_ON_STACK_REPLACEMENT - bool m_fOwnsPatchpointInfo; - CORINFO_PATCHPOINT_INFO * m_pPatchpointInfo; - unsigned m_ilOffset; + bool m_fOwnsPatchpointInfo; + PatchpointInfo * m_pPatchpointInfo; + unsigned m_ilOffset; #endif // The first time a call is made to CEEJitInfo::GetProfilingHandle() from this thread diff --git a/src/coreclr/src/vm/method.hpp b/src/coreclr/src/vm/method.hpp index d80d06942e304a..c3d97a185dcf6a 100644 --- a/src/coreclr/src/vm/method.hpp +++ b/src/coreclr/src/vm/method.hpp @@ -2205,9 +2205,6 @@ class VersionedPrepareCodeConfig : public PrepareCodeConfig virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse); virtual COR_ILMETHOD* GetILHeader(); virtual CORJIT_FLAGS GetJitCompilationFlags(); -#ifdef FEATURE_ON_STACK_REPLACEMENT - virtual CORINFO_OSR_INFO * GetOSRInfo(); -#endif private: ILCodeVersion m_ilCodeVersion; }; diff --git a/src/coreclr/src/vm/prestub.cpp b/src/coreclr/src/vm/prestub.cpp index 3d36033ce9a7bd..884b0795163942 100644 --- a/src/coreclr/src/vm/prestub.cpp +++ b/src/coreclr/src/vm/prestub.cpp @@ -1349,8 +1349,9 @@ CORJIT_FLAGS VersionedPrepareCodeConfig::GetJitCompilationFlags() #endif #ifdef FEATURE_ON_STACK_REPLACEMENT - CORINFO_OSR_INFO * osrInfo = GetOSRInfo(); - if (osrInfo->patchpointInfo != NULL) + unsigned ilOffset = 0; + PatchpointInfo * patchpointInfo = m_nativeCodeVersion.GetOSRInfo(&ilOffset); + if (patchpointInfo != NULL) { flags.Add(CORJIT_FLAGS::CORJIT_FLAG_OSR); } @@ -1379,13 +1380,6 @@ PrepareCodeConfigBuffer::PrepareCodeConfigBuffer(NativeCodeVersion codeVersion) config->FinishConfiguration(); } -#ifdef FEATURE_ON_STACK_REPLACEMENT -CORINFO_OSR_INFO * VersionedPrepareCodeConfig::GetOSRInfo() -{ - return m_nativeCodeVersion.GetOSRInfo(); -} -#endif - #endif //FEATURE_CODE_VERSIONING #ifdef FEATURE_INSTANTIATINGSTUB_AS_IL diff --git a/src/coreclr/src/zap/zapinfo.cpp b/src/coreclr/src/zap/zapinfo.cpp index cd762988e0a740..eab30c7c9ca033 100644 --- a/src/coreclr/src/zap/zapinfo.cpp +++ b/src/coreclr/src/zap/zapinfo.cpp @@ -2943,7 +2943,13 @@ void ZapInfo::setVars(CORINFO_METHOD_HANDLE ftn, return; } -void ZapInfo::setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) +void ZapInfo::setPatchpointInfo(PatchpointInfo* patchpointInfo) +{ + // No patchpoint info when prejitting + UNREACHABLE(); +} + +PatchpointInfo* ZapInfo::getOSRInfo(unsigned * ilOffset) { // No patchpoint info when prejitting UNREACHABLE(); diff --git a/src/coreclr/src/zap/zapinfo.h b/src/coreclr/src/zap/zapinfo.h index e9595a9b5ee64b..7f8b837db0bbe6 100644 --- a/src/coreclr/src/zap/zapinfo.h +++ b/src/coreclr/src/zap/zapinfo.h @@ -475,7 +475,6 @@ class ZapInfo ICorDebugInfo::ILVarInfo **vars, bool *extendOthers); void setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo*vars); - void setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo); void * allocateArray(size_t cBytes); void freeArray(void *array); @@ -702,6 +701,10 @@ class ZapInfo void getGSCookie(GSCookie * pCookieVal, GSCookie** ppCookieVal); + + void setPatchpointInfo(PatchpointInfo * patchpointInfo); + PatchpointInfo * getOSRInfo(unsigned * ilOffset); + // ICorErrorInfo HRESULT GetErrorHRESULT(struct _EXCEPTION_POINTERS *pExceptionPointers); From b1468e0ac7c73076ad94bf8b38399546f3723b7c Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 6 Mar 2020 18:25:31 -0800 Subject: [PATCH 07/31] Introduce new opt tier for OSR, rework code version locking. --- src/coreclr/src/vm/codeversion.cpp | 7 ++----- src/coreclr/src/vm/codeversion.h | 1 + src/coreclr/src/vm/jithelpers.cpp | 14 ++++++-------- src/coreclr/src/vm/method.hpp | 1 + src/coreclr/src/vm/prestub.cpp | 13 ++++--------- src/coreclr/src/vm/tieredcompilation.cpp | 6 ++++++ 6 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/coreclr/src/vm/codeversion.cpp b/src/coreclr/src/vm/codeversion.cpp index a0ef82ef3b01cc..33e1410cc16402 100644 --- a/src/coreclr/src/vm/codeversion.cpp +++ b/src/coreclr/src/vm/codeversion.cpp @@ -44,8 +44,7 @@ void NativeCodeVersion::SetGCCoverageInfo(PTR_GCCoverageInfo gcCover) } #endif -bool NativeCodeVersion::operator==(const NativeCodeVersion & rhs) const { return m_pMethodDesc == rhs.m_pMethodDesc; } -bool NativeCodeVersion::operator!=(const NativeCodeVersion & rhs) const { return !operator==(rhs); } + #else // FEATURE_CODE_VERSIONING @@ -74,9 +73,7 @@ NativeCodeVersionNode::NativeCodeVersionNode( m_ilOffset(ilOffset), #endif m_flags(0) -{ - -} +{} #endif PCODE NativeCodeVersionNode::GetNativeCode() const diff --git a/src/coreclr/src/vm/codeversion.h b/src/coreclr/src/vm/codeversion.h index 210147b0a008c0..c4a65d100ce776 100644 --- a/src/coreclr/src/vm/codeversion.h +++ b/src/coreclr/src/vm/codeversion.h @@ -77,6 +77,7 @@ class NativeCodeVersion { OptimizationTier0, OptimizationTier1, + OptimizationTier1OSR, OptimizationTierOptimized, // may do less optimizations than tier 1 }; #ifdef FEATURE_TIERED_COMPILATION diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp index da939b4e77659d..6fcc30e3d41d1d 100644 --- a/src/coreclr/src/vm/jithelpers.cpp +++ b/src/coreclr/src/vm/jithelpers.cpp @@ -5067,16 +5067,14 @@ static PCODE JitPatchpointWorker(void* ip, int ilOffset) return NULL; } - // Find the il method version corresponding to this version of the method - // and set up a new native code version for it. - ReJITID rejitId = ReJitManager::GetReJitId(pMD, codeInfo.GetStartAddress()); - CodeVersionManager* codeVersionManager = pMD->GetCodeVersionManager(); + // Set up a new native code version for the OSR variant of this method. NativeCodeVersion osrNativeCodeVersion; { - // Request a new native version that is optimized. - CodeVersionManager::TableLockHolder lock(codeVersionManager); - ILCodeVersion ilCodeVersion = codeVersionManager->GetILCodeVersion(pMD, rejitId); - HRESULT hr = ilCodeVersion.AddNativeCodeVersion(pMD, NativeCodeVersion::OptimizationTier1, &osrNativeCodeVersion, patchpointInfo, ilOffset); + CodeVersionManager::LockHolder codeVersioningLockHolder; + + NativeCodeVersion currentNativeCodeVersion = codeInfo.GetNativeCodeVersion(); + ILCodeVersion ilCodeVersion = currentNativeCodeVersion.GetILCodeVersion(); + HRESULT hr = ilCodeVersion.AddNativeCodeVersion(pMD, NativeCodeVersion::OptimizationTier1OSR, &osrNativeCodeVersion, patchpointInfo, ilOffset); if (FAILED(hr)) { // log ... diff --git a/src/coreclr/src/vm/method.hpp b/src/coreclr/src/vm/method.hpp index 2223535081fa9a..ac10c8c341e3bf 100644 --- a/src/coreclr/src/vm/method.hpp +++ b/src/coreclr/src/vm/method.hpp @@ -2103,6 +2103,7 @@ class PrepareCodeConfig Optimized, QuickJitted, OptimizedTier1, + OptimizedTier1OSR, Count }; diff --git a/src/coreclr/src/vm/prestub.cpp b/src/coreclr/src/vm/prestub.cpp index ad97c96cb1e444..03cb080af1541b 100644 --- a/src/coreclr/src/vm/prestub.cpp +++ b/src/coreclr/src/vm/prestub.cpp @@ -1258,6 +1258,9 @@ PrepareCodeConfig::JitOptimizationTier PrepareCodeConfig::GetJitOptimizationTier case NativeCodeVersion::OptimizationTier1: return JitOptimizationTier::OptimizedTier1; + case NativeCodeVersion::OptimizationTier1OSR: + return JitOptimizationTier::OptimizedTier1OSR; + case NativeCodeVersion::OptimizationTierOptimized: return JitOptimizationTier::Optimized; @@ -1282,6 +1285,7 @@ const char *PrepareCodeConfig::GetJitOptimizationTierStr(PrepareCodeConfig *conf case JitOptimizationTier::Optimized: return "Optimized"; case JitOptimizationTier::QuickJitted: return "QuickJitted"; case JitOptimizationTier::OptimizedTier1: return "OptimizedTier1"; + case JitOptimizationTier::OptimizedTier1OSR: return "OptimizedTier1OSR"; default: UNREACHABLE(); @@ -1346,15 +1350,6 @@ CORJIT_FLAGS VersionedPrepareCodeConfig::GetJitCompilationFlags() flags.Add(TieredCompilationManager::GetJitFlags(m_nativeCodeVersion)); #endif -#ifdef FEATURE_ON_STACK_REPLACEMENT - unsigned ilOffset = 0; - PatchpointInfo * patchpointInfo = m_nativeCodeVersion.GetOSRInfo(&ilOffset); - if (patchpointInfo != NULL) - { - flags.Add(CORJIT_FLAGS::CORJIT_FLAG_OSR); - } -#endif - return flags; } diff --git a/src/coreclr/src/vm/tieredcompilation.cpp b/src/coreclr/src/vm/tieredcompilation.cpp index 9eccebd614eb3a..3b286e0fb5b9f3 100644 --- a/src/coreclr/src/vm/tieredcompilation.cpp +++ b/src/coreclr/src/vm/tieredcompilation.cpp @@ -924,6 +924,12 @@ CORJIT_FLAGS TieredCompilationManager::GetJitFlags(NativeCodeVersion nativeCodeV flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0); break; +#ifdef FEATURE_ON_STACK_REPLACEMENT + case NativeCodeVersion::OptimizationTier1OSR: + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_OSR); + // fall through +#endif + case NativeCodeVersion::OptimizationTier1: flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1); // fall through From 11c4edb365e4f07383d1838a8e72a9518a4e9c11 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 6 Mar 2020 18:41:00 -0800 Subject: [PATCH 08/31] update crossgen2 for jit interface changes --- .../tools/Common/JitInterface/CorInfoBase.cs | 339 +++++++++--------- .../tools/Common/JitInterface/CorInfoImpl.cs | 3 - .../tools/Common/JitInterface/CorInfoTypes.cs | 2 +- .../ThunkGenerator/ThunkInput.txt | 5 +- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 27 +- .../crossgen2/jitinterface/jitinterface.h | 28 +- 6 files changed, 220 insertions(+), 184 deletions(-) diff --git a/src/coreclr/src/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/src/tools/Common/JitInterface/CorInfoBase.cs index 17cef6d7f457da..68033365afb248 100644 --- a/src/coreclr/src/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/src/tools/Common/JitInterface/CorInfoBase.cs @@ -62,6 +62,10 @@ unsafe partial class CorInfoImpl [UnmanagedFunctionPointerAttribute(default(CallingConvention))] delegate void __getGSCookie(IntPtr _this, IntPtr* ppException, IntPtr* pCookieVal, IntPtr** ppCookieVal); [UnmanagedFunctionPointerAttribute(default(CallingConvention))] + delegate void __setPatchpointInfo(IntPtr _this, IntPtr* ppException, PatchpointInfo* patchpointInfo); + [UnmanagedFunctionPointerAttribute(default(CallingConvention))] + delegate PatchpointInfo* __getOSRInfo(IntPtr _this, IntPtr* ppException, ref uint ilOffset); + [UnmanagedFunctionPointerAttribute(default(CallingConvention))] delegate void __resolveToken(IntPtr _this, IntPtr* ppException, ref CORINFO_RESOLVED_TOKEN pResolvedToken); [UnmanagedFunctionPointerAttribute(default(CallingConvention))] delegate void __tryResolveToken(IntPtr _this, IntPtr* ppException, ref CORINFO_RESOLVED_TOKEN pResolvedToken); @@ -200,8 +204,6 @@ unsafe partial class CorInfoImpl [UnmanagedFunctionPointerAttribute(default(CallingConvention))] delegate void __setVars(IntPtr _this, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftn, uint cVars, NativeVarInfo* vars); [UnmanagedFunctionPointerAttribute(default(CallingConvention))] - delegate void __setPatchpointInfo(IntPtr _this, IntPtr* ppException, CORINFO_PATCHPOINT_INFO* patchpointInfo); - [UnmanagedFunctionPointerAttribute(default(CallingConvention))] delegate void* __allocateArray(IntPtr _this, IntPtr* ppException, UIntPtr cBytes); [UnmanagedFunctionPointerAttribute(default(CallingConvention))] delegate void __freeArray(IntPtr _this, IntPtr* ppException, void* array); @@ -689,6 +691,33 @@ static void _getGSCookie(IntPtr thisHandle, IntPtr* ppException, IntPtr* pCookie } } + static void _setPatchpointInfo(IntPtr thisHandle, IntPtr* ppException, PatchpointInfo* patchpointInfo) + { + var _this = GetThis(thisHandle); + try + { + _this.setPatchpointInfo(patchpointInfo); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + } + } + + static PatchpointInfo* _getOSRInfo(IntPtr thisHandle, IntPtr* ppException, ref uint ilOffset) + { + var _this = GetThis(thisHandle); + try + { + return _this.getOSRInfo(ref ilOffset); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default(PatchpointInfo*); + } + } + static void _resolveToken(IntPtr thisHandle, IntPtr* ppException, ref CORINFO_RESOLVED_TOKEN pResolvedToken) { var _this = GetThis(thisHandle); @@ -1643,19 +1672,6 @@ static void _setVars(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRU } } - static void _setPatchpointInfo(IntPtr thisHandle, IntPtr* ppException, CORINFO_PATCHPOINT_INFO* patchpointInfo) - { - var _this = GetThis(thisHandle); - try - { - _this.setPatchpointInfo(patchpointInfo); - } - catch (Exception ex) - { - *ppException = _this.AllocException(ex); - } - } - static void* _allocateArray(IntPtr thisHandle, IntPtr* ppException, UIntPtr cBytes) { var _this = GetThis(thisHandle); @@ -2658,8 +2674,8 @@ static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, ref CORJIT_FLAG static IntPtr GetUnmanagedCallbacks(out Object keepAlive) { - IntPtr * callbacks = (IntPtr *)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 168); - Object[] delegates = new Object[168]; + IntPtr * callbacks = (IntPtr *)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 169); + Object[] delegates = new Object[169]; var d0 = new __getMethodAttribs(_getMethodAttribs); callbacks[0] = Marshal.GetFunctionPointerForDelegate(d0); @@ -2736,435 +2752,438 @@ static IntPtr GetUnmanagedCallbacks(out Object keepAlive) var d24 = new __getGSCookie(_getGSCookie); callbacks[24] = Marshal.GetFunctionPointerForDelegate(d24); delegates[24] = d24; - var d25 = new __resolveToken(_resolveToken); + var d25 = new __setPatchpointInfo(_setPatchpointInfo); callbacks[25] = Marshal.GetFunctionPointerForDelegate(d25); delegates[25] = d25; - var d26 = new __tryResolveToken(_tryResolveToken); + var d26 = new __getOSRInfo(_getOSRInfo); callbacks[26] = Marshal.GetFunctionPointerForDelegate(d26); delegates[26] = d26; - var d27 = new __findSig(_findSig); + var d27 = new __resolveToken(_resolveToken); callbacks[27] = Marshal.GetFunctionPointerForDelegate(d27); delegates[27] = d27; - var d28 = new __findCallSiteSig(_findCallSiteSig); + var d28 = new __tryResolveToken(_tryResolveToken); callbacks[28] = Marshal.GetFunctionPointerForDelegate(d28); delegates[28] = d28; - var d29 = new __getTokenTypeAsHandle(_getTokenTypeAsHandle); + var d29 = new __findSig(_findSig); callbacks[29] = Marshal.GetFunctionPointerForDelegate(d29); delegates[29] = d29; - var d30 = new __isValidToken(_isValidToken); + var d30 = new __findCallSiteSig(_findCallSiteSig); callbacks[30] = Marshal.GetFunctionPointerForDelegate(d30); delegates[30] = d30; - var d31 = new __isValidStringRef(_isValidStringRef); + var d31 = new __getTokenTypeAsHandle(_getTokenTypeAsHandle); callbacks[31] = Marshal.GetFunctionPointerForDelegate(d31); delegates[31] = d31; - var d32 = new __getStringLiteral(_getStringLiteral); + var d32 = new __isValidToken(_isValidToken); callbacks[32] = Marshal.GetFunctionPointerForDelegate(d32); delegates[32] = d32; - var d33 = new __asCorInfoType(_asCorInfoType); + var d33 = new __isValidStringRef(_isValidStringRef); callbacks[33] = Marshal.GetFunctionPointerForDelegate(d33); delegates[33] = d33; - var d34 = new __getClassName(_getClassName); + var d34 = new __getStringLiteral(_getStringLiteral); callbacks[34] = Marshal.GetFunctionPointerForDelegate(d34); delegates[34] = d34; - var d35 = new __getClassNameFromMetadata(_getClassNameFromMetadata); + var d35 = new __asCorInfoType(_asCorInfoType); callbacks[35] = Marshal.GetFunctionPointerForDelegate(d35); delegates[35] = d35; - var d36 = new __getTypeInstantiationArgument(_getTypeInstantiationArgument); + var d36 = new __getClassName(_getClassName); callbacks[36] = Marshal.GetFunctionPointerForDelegate(d36); delegates[36] = d36; - var d37 = new __appendClassName(_appendClassName); + var d37 = new __getClassNameFromMetadata(_getClassNameFromMetadata); callbacks[37] = Marshal.GetFunctionPointerForDelegate(d37); delegates[37] = d37; - var d38 = new __isValueClass(_isValueClass); + var d38 = new __getTypeInstantiationArgument(_getTypeInstantiationArgument); callbacks[38] = Marshal.GetFunctionPointerForDelegate(d38); delegates[38] = d38; - var d39 = new __canInlineTypeCheck(_canInlineTypeCheck); + var d39 = new __appendClassName(_appendClassName); callbacks[39] = Marshal.GetFunctionPointerForDelegate(d39); delegates[39] = d39; - var d40 = new __getClassAttribs(_getClassAttribs); + var d40 = new __isValueClass(_isValueClass); callbacks[40] = Marshal.GetFunctionPointerForDelegate(d40); delegates[40] = d40; - var d41 = new __isStructRequiringStackAllocRetBuf(_isStructRequiringStackAllocRetBuf); + var d41 = new __canInlineTypeCheck(_canInlineTypeCheck); callbacks[41] = Marshal.GetFunctionPointerForDelegate(d41); delegates[41] = d41; - var d42 = new __getClassModule(_getClassModule); + var d42 = new __getClassAttribs(_getClassAttribs); callbacks[42] = Marshal.GetFunctionPointerForDelegate(d42); delegates[42] = d42; - var d43 = new __getModuleAssembly(_getModuleAssembly); + var d43 = new __isStructRequiringStackAllocRetBuf(_isStructRequiringStackAllocRetBuf); callbacks[43] = Marshal.GetFunctionPointerForDelegate(d43); delegates[43] = d43; - var d44 = new __getAssemblyName(_getAssemblyName); + var d44 = new __getClassModule(_getClassModule); callbacks[44] = Marshal.GetFunctionPointerForDelegate(d44); delegates[44] = d44; - var d45 = new __LongLifetimeMalloc(_LongLifetimeMalloc); + var d45 = new __getModuleAssembly(_getModuleAssembly); callbacks[45] = Marshal.GetFunctionPointerForDelegate(d45); delegates[45] = d45; - var d46 = new __LongLifetimeFree(_LongLifetimeFree); + var d46 = new __getAssemblyName(_getAssemblyName); callbacks[46] = Marshal.GetFunctionPointerForDelegate(d46); delegates[46] = d46; - var d47 = new __getClassModuleIdForStatics(_getClassModuleIdForStatics); + var d47 = new __LongLifetimeMalloc(_LongLifetimeMalloc); callbacks[47] = Marshal.GetFunctionPointerForDelegate(d47); delegates[47] = d47; - var d48 = new __getClassSize(_getClassSize); + var d48 = new __LongLifetimeFree(_LongLifetimeFree); callbacks[48] = Marshal.GetFunctionPointerForDelegate(d48); delegates[48] = d48; - var d49 = new __getHeapClassSize(_getHeapClassSize); + var d49 = new __getClassModuleIdForStatics(_getClassModuleIdForStatics); callbacks[49] = Marshal.GetFunctionPointerForDelegate(d49); delegates[49] = d49; - var d50 = new __canAllocateOnStack(_canAllocateOnStack); + var d50 = new __getClassSize(_getClassSize); callbacks[50] = Marshal.GetFunctionPointerForDelegate(d50); delegates[50] = d50; - var d51 = new __getClassAlignmentRequirement(_getClassAlignmentRequirement); + var d51 = new __getHeapClassSize(_getHeapClassSize); callbacks[51] = Marshal.GetFunctionPointerForDelegate(d51); delegates[51] = d51; - var d52 = new __getClassGClayout(_getClassGClayout); + var d52 = new __canAllocateOnStack(_canAllocateOnStack); callbacks[52] = Marshal.GetFunctionPointerForDelegate(d52); delegates[52] = d52; - var d53 = new __getClassNumInstanceFields(_getClassNumInstanceFields); + var d53 = new __getClassAlignmentRequirement(_getClassAlignmentRequirement); callbacks[53] = Marshal.GetFunctionPointerForDelegate(d53); delegates[53] = d53; - var d54 = new __getFieldInClass(_getFieldInClass); + var d54 = new __getClassGClayout(_getClassGClayout); callbacks[54] = Marshal.GetFunctionPointerForDelegate(d54); delegates[54] = d54; - var d55 = new __checkMethodModifier(_checkMethodModifier); + var d55 = new __getClassNumInstanceFields(_getClassNumInstanceFields); callbacks[55] = Marshal.GetFunctionPointerForDelegate(d55); delegates[55] = d55; - var d56 = new __getNewHelper(_getNewHelper); + var d56 = new __getFieldInClass(_getFieldInClass); callbacks[56] = Marshal.GetFunctionPointerForDelegate(d56); delegates[56] = d56; - var d57 = new __getNewArrHelper(_getNewArrHelper); + var d57 = new __checkMethodModifier(_checkMethodModifier); callbacks[57] = Marshal.GetFunctionPointerForDelegate(d57); delegates[57] = d57; - var d58 = new __getCastingHelper(_getCastingHelper); + var d58 = new __getNewHelper(_getNewHelper); callbacks[58] = Marshal.GetFunctionPointerForDelegate(d58); delegates[58] = d58; - var d59 = new __getSharedCCtorHelper(_getSharedCCtorHelper); + var d59 = new __getNewArrHelper(_getNewArrHelper); callbacks[59] = Marshal.GetFunctionPointerForDelegate(d59); delegates[59] = d59; - var d60 = new __getTypeForBox(_getTypeForBox); + var d60 = new __getCastingHelper(_getCastingHelper); callbacks[60] = Marshal.GetFunctionPointerForDelegate(d60); delegates[60] = d60; - var d61 = new __getBoxHelper(_getBoxHelper); + var d61 = new __getSharedCCtorHelper(_getSharedCCtorHelper); callbacks[61] = Marshal.GetFunctionPointerForDelegate(d61); delegates[61] = d61; - var d62 = new __getUnBoxHelper(_getUnBoxHelper); + var d62 = new __getTypeForBox(_getTypeForBox); callbacks[62] = Marshal.GetFunctionPointerForDelegate(d62); delegates[62] = d62; - var d63 = new __getReadyToRunHelper(_getReadyToRunHelper); + var d63 = new __getBoxHelper(_getBoxHelper); callbacks[63] = Marshal.GetFunctionPointerForDelegate(d63); delegates[63] = d63; - var d64 = new __getReadyToRunDelegateCtorHelper(_getReadyToRunDelegateCtorHelper); + var d64 = new __getUnBoxHelper(_getUnBoxHelper); callbacks[64] = Marshal.GetFunctionPointerForDelegate(d64); delegates[64] = d64; - var d65 = new __getHelperName(_getHelperName); + var d65 = new __getReadyToRunHelper(_getReadyToRunHelper); callbacks[65] = Marshal.GetFunctionPointerForDelegate(d65); delegates[65] = d65; - var d66 = new __initClass(_initClass); + var d66 = new __getReadyToRunDelegateCtorHelper(_getReadyToRunDelegateCtorHelper); callbacks[66] = Marshal.GetFunctionPointerForDelegate(d66); delegates[66] = d66; - var d67 = new __classMustBeLoadedBeforeCodeIsRun(_classMustBeLoadedBeforeCodeIsRun); + var d67 = new __getHelperName(_getHelperName); callbacks[67] = Marshal.GetFunctionPointerForDelegate(d67); delegates[67] = d67; - var d68 = new __getBuiltinClass(_getBuiltinClass); + var d68 = new __initClass(_initClass); callbacks[68] = Marshal.GetFunctionPointerForDelegate(d68); delegates[68] = d68; - var d69 = new __getTypeForPrimitiveValueClass(_getTypeForPrimitiveValueClass); + var d69 = new __classMustBeLoadedBeforeCodeIsRun(_classMustBeLoadedBeforeCodeIsRun); callbacks[69] = Marshal.GetFunctionPointerForDelegate(d69); delegates[69] = d69; - var d70 = new __getTypeForPrimitiveNumericClass(_getTypeForPrimitiveNumericClass); + var d70 = new __getBuiltinClass(_getBuiltinClass); callbacks[70] = Marshal.GetFunctionPointerForDelegate(d70); delegates[70] = d70; - var d71 = new __canCast(_canCast); + var d71 = new __getTypeForPrimitiveValueClass(_getTypeForPrimitiveValueClass); callbacks[71] = Marshal.GetFunctionPointerForDelegate(d71); delegates[71] = d71; - var d72 = new __areTypesEquivalent(_areTypesEquivalent); + var d72 = new __getTypeForPrimitiveNumericClass(_getTypeForPrimitiveNumericClass); callbacks[72] = Marshal.GetFunctionPointerForDelegate(d72); delegates[72] = d72; - var d73 = new __compareTypesForCast(_compareTypesForCast); + var d73 = new __canCast(_canCast); callbacks[73] = Marshal.GetFunctionPointerForDelegate(d73); delegates[73] = d73; - var d74 = new __compareTypesForEquality(_compareTypesForEquality); + var d74 = new __areTypesEquivalent(_areTypesEquivalent); callbacks[74] = Marshal.GetFunctionPointerForDelegate(d74); delegates[74] = d74; - var d75 = new __mergeClasses(_mergeClasses); + var d75 = new __compareTypesForCast(_compareTypesForCast); callbacks[75] = Marshal.GetFunctionPointerForDelegate(d75); delegates[75] = d75; - var d76 = new __isMoreSpecificType(_isMoreSpecificType); + var d76 = new __compareTypesForEquality(_compareTypesForEquality); callbacks[76] = Marshal.GetFunctionPointerForDelegate(d76); delegates[76] = d76; - var d77 = new __getParentType(_getParentType); + var d77 = new __mergeClasses(_mergeClasses); callbacks[77] = Marshal.GetFunctionPointerForDelegate(d77); delegates[77] = d77; - var d78 = new __getChildType(_getChildType); + var d78 = new __isMoreSpecificType(_isMoreSpecificType); callbacks[78] = Marshal.GetFunctionPointerForDelegate(d78); delegates[78] = d78; - var d79 = new __satisfiesClassConstraints(_satisfiesClassConstraints); + var d79 = new __getParentType(_getParentType); callbacks[79] = Marshal.GetFunctionPointerForDelegate(d79); delegates[79] = d79; - var d80 = new __isSDArray(_isSDArray); + var d80 = new __getChildType(_getChildType); callbacks[80] = Marshal.GetFunctionPointerForDelegate(d80); delegates[80] = d80; - var d81 = new __getArrayRank(_getArrayRank); + var d81 = new __satisfiesClassConstraints(_satisfiesClassConstraints); callbacks[81] = Marshal.GetFunctionPointerForDelegate(d81); delegates[81] = d81; - var d82 = new __getArrayInitializationData(_getArrayInitializationData); + var d82 = new __isSDArray(_isSDArray); callbacks[82] = Marshal.GetFunctionPointerForDelegate(d82); delegates[82] = d82; - var d83 = new __canAccessClass(_canAccessClass); + var d83 = new __getArrayRank(_getArrayRank); callbacks[83] = Marshal.GetFunctionPointerForDelegate(d83); delegates[83] = d83; - var d84 = new __getFieldName(_getFieldName); + var d84 = new __getArrayInitializationData(_getArrayInitializationData); callbacks[84] = Marshal.GetFunctionPointerForDelegate(d84); delegates[84] = d84; - var d85 = new __getFieldClass(_getFieldClass); + var d85 = new __canAccessClass(_canAccessClass); callbacks[85] = Marshal.GetFunctionPointerForDelegate(d85); delegates[85] = d85; - var d86 = new __getFieldType(_getFieldType); + var d86 = new __getFieldName(_getFieldName); callbacks[86] = Marshal.GetFunctionPointerForDelegate(d86); delegates[86] = d86; - var d87 = new __getFieldOffset(_getFieldOffset); + var d87 = new __getFieldClass(_getFieldClass); callbacks[87] = Marshal.GetFunctionPointerForDelegate(d87); delegates[87] = d87; - var d88 = new __getFieldInfo(_getFieldInfo); + var d88 = new __getFieldType(_getFieldType); callbacks[88] = Marshal.GetFunctionPointerForDelegate(d88); delegates[88] = d88; - var d89 = new __isFieldStatic(_isFieldStatic); + var d89 = new __getFieldOffset(_getFieldOffset); callbacks[89] = Marshal.GetFunctionPointerForDelegate(d89); delegates[89] = d89; - var d90 = new __getBoundaries(_getBoundaries); + var d90 = new __getFieldInfo(_getFieldInfo); callbacks[90] = Marshal.GetFunctionPointerForDelegate(d90); delegates[90] = d90; - var d91 = new __setBoundaries(_setBoundaries); + var d91 = new __isFieldStatic(_isFieldStatic); callbacks[91] = Marshal.GetFunctionPointerForDelegate(d91); delegates[91] = d91; - var d92 = new __getVars(_getVars); + var d92 = new __getBoundaries(_getBoundaries); callbacks[92] = Marshal.GetFunctionPointerForDelegate(d92); delegates[92] = d92; - var d93 = new __setVars(_setVars); + var d93 = new __setBoundaries(_setBoundaries); callbacks[93] = Marshal.GetFunctionPointerForDelegate(d93); delegates[93] = d93; - var d94 = new __setPatchpointInfo(_setPatchpointInfo); + var d94 = new __getVars(_getVars); callbacks[94] = Marshal.GetFunctionPointerForDelegate(d94); delegates[94] = d94; - var d95 = new __allocateArray(_allocateArray); + var d95 = new __setVars(_setVars); callbacks[95] = Marshal.GetFunctionPointerForDelegate(d95); delegates[95] = d95; - var d96 = new __freeArray(_freeArray); + var d96 = new __allocateArray(_allocateArray); callbacks[96] = Marshal.GetFunctionPointerForDelegate(d96); delegates[96] = d96; - var d97 = new __getArgNext(_getArgNext); + var d97 = new __freeArray(_freeArray); callbacks[97] = Marshal.GetFunctionPointerForDelegate(d97); delegates[97] = d97; - var d98 = new __getArgType(_getArgType); + var d98 = new __getArgNext(_getArgNext); callbacks[98] = Marshal.GetFunctionPointerForDelegate(d98); delegates[98] = d98; - var d99 = new __getArgClass(_getArgClass); + var d99 = new __getArgType(_getArgType); callbacks[99] = Marshal.GetFunctionPointerForDelegate(d99); delegates[99] = d99; - var d100 = new __getHFAType(_getHFAType); + var d100 = new __getArgClass(_getArgClass); callbacks[100] = Marshal.GetFunctionPointerForDelegate(d100); delegates[100] = d100; - var d101 = new __GetErrorHRESULT(_GetErrorHRESULT); + var d101 = new __getHFAType(_getHFAType); callbacks[101] = Marshal.GetFunctionPointerForDelegate(d101); delegates[101] = d101; - var d102 = new __GetErrorMessage(_GetErrorMessage); + var d102 = new __GetErrorHRESULT(_GetErrorHRESULT); callbacks[102] = Marshal.GetFunctionPointerForDelegate(d102); delegates[102] = d102; - var d103 = new __FilterException(_FilterException); + var d103 = new __GetErrorMessage(_GetErrorMessage); callbacks[103] = Marshal.GetFunctionPointerForDelegate(d103); delegates[103] = d103; - var d104 = new __HandleException(_HandleException); + var d104 = new __FilterException(_FilterException); callbacks[104] = Marshal.GetFunctionPointerForDelegate(d104); delegates[104] = d104; - var d105 = new __ThrowExceptionForJitResult(_ThrowExceptionForJitResult); + var d105 = new __HandleException(_HandleException); callbacks[105] = Marshal.GetFunctionPointerForDelegate(d105); delegates[105] = d105; - var d106 = new __ThrowExceptionForHelper(_ThrowExceptionForHelper); + var d106 = new __ThrowExceptionForJitResult(_ThrowExceptionForJitResult); callbacks[106] = Marshal.GetFunctionPointerForDelegate(d106); delegates[106] = d106; - var d107 = new __runWithErrorTrap(_runWithErrorTrap); + var d107 = new __ThrowExceptionForHelper(_ThrowExceptionForHelper); callbacks[107] = Marshal.GetFunctionPointerForDelegate(d107); delegates[107] = d107; - var d108 = new __getEEInfo(_getEEInfo); + var d108 = new __runWithErrorTrap(_runWithErrorTrap); callbacks[108] = Marshal.GetFunctionPointerForDelegate(d108); delegates[108] = d108; - var d109 = new __getJitTimeLogFilename(_getJitTimeLogFilename); + var d109 = new __getEEInfo(_getEEInfo); callbacks[109] = Marshal.GetFunctionPointerForDelegate(d109); delegates[109] = d109; - var d110 = new __getMethodDefFromMethod(_getMethodDefFromMethod); + var d110 = new __getJitTimeLogFilename(_getJitTimeLogFilename); callbacks[110] = Marshal.GetFunctionPointerForDelegate(d110); delegates[110] = d110; - var d111 = new __getMethodName(_getMethodName); + var d111 = new __getMethodDefFromMethod(_getMethodDefFromMethod); callbacks[111] = Marshal.GetFunctionPointerForDelegate(d111); delegates[111] = d111; - var d112 = new __getMethodNameFromMetadata(_getMethodNameFromMetadata); + var d112 = new __getMethodName(_getMethodName); callbacks[112] = Marshal.GetFunctionPointerForDelegate(d112); delegates[112] = d112; - var d113 = new __getMethodHash(_getMethodHash); + var d113 = new __getMethodNameFromMetadata(_getMethodNameFromMetadata); callbacks[113] = Marshal.GetFunctionPointerForDelegate(d113); delegates[113] = d113; - var d114 = new __findNameOfToken(_findNameOfToken); + var d114 = new __getMethodHash(_getMethodHash); callbacks[114] = Marshal.GetFunctionPointerForDelegate(d114); delegates[114] = d114; - var d115 = new __getSystemVAmd64PassStructInRegisterDescriptor(_getSystemVAmd64PassStructInRegisterDescriptor); + var d115 = new __findNameOfToken(_findNameOfToken); callbacks[115] = Marshal.GetFunctionPointerForDelegate(d115); delegates[115] = d115; - var d116 = new __getThreadTLSIndex(_getThreadTLSIndex); + var d116 = new __getSystemVAmd64PassStructInRegisterDescriptor(_getSystemVAmd64PassStructInRegisterDescriptor); callbacks[116] = Marshal.GetFunctionPointerForDelegate(d116); delegates[116] = d116; - var d117 = new __getInlinedCallFrameVptr(_getInlinedCallFrameVptr); + var d117 = new __getThreadTLSIndex(_getThreadTLSIndex); callbacks[117] = Marshal.GetFunctionPointerForDelegate(d117); delegates[117] = d117; - var d118 = new __getAddrOfCaptureThreadGlobal(_getAddrOfCaptureThreadGlobal); + var d118 = new __getInlinedCallFrameVptr(_getInlinedCallFrameVptr); callbacks[118] = Marshal.GetFunctionPointerForDelegate(d118); delegates[118] = d118; - var d119 = new __getHelperFtn(_getHelperFtn); + var d119 = new __getAddrOfCaptureThreadGlobal(_getAddrOfCaptureThreadGlobal); callbacks[119] = Marshal.GetFunctionPointerForDelegate(d119); delegates[119] = d119; - var d120 = new __getFunctionEntryPoint(_getFunctionEntryPoint); + var d120 = new __getHelperFtn(_getHelperFtn); callbacks[120] = Marshal.GetFunctionPointerForDelegate(d120); delegates[120] = d120; - var d121 = new __getFunctionFixedEntryPoint(_getFunctionFixedEntryPoint); + var d121 = new __getFunctionEntryPoint(_getFunctionEntryPoint); callbacks[121] = Marshal.GetFunctionPointerForDelegate(d121); delegates[121] = d121; - var d122 = new __getMethodSync(_getMethodSync); + var d122 = new __getFunctionFixedEntryPoint(_getFunctionFixedEntryPoint); callbacks[122] = Marshal.GetFunctionPointerForDelegate(d122); delegates[122] = d122; - var d123 = new __getLazyStringLiteralHelper(_getLazyStringLiteralHelper); + var d123 = new __getMethodSync(_getMethodSync); callbacks[123] = Marshal.GetFunctionPointerForDelegate(d123); delegates[123] = d123; - var d124 = new __embedModuleHandle(_embedModuleHandle); + var d124 = new __getLazyStringLiteralHelper(_getLazyStringLiteralHelper); callbacks[124] = Marshal.GetFunctionPointerForDelegate(d124); delegates[124] = d124; - var d125 = new __embedClassHandle(_embedClassHandle); + var d125 = new __embedModuleHandle(_embedModuleHandle); callbacks[125] = Marshal.GetFunctionPointerForDelegate(d125); delegates[125] = d125; - var d126 = new __embedMethodHandle(_embedMethodHandle); + var d126 = new __embedClassHandle(_embedClassHandle); callbacks[126] = Marshal.GetFunctionPointerForDelegate(d126); delegates[126] = d126; - var d127 = new __embedFieldHandle(_embedFieldHandle); + var d127 = new __embedMethodHandle(_embedMethodHandle); callbacks[127] = Marshal.GetFunctionPointerForDelegate(d127); delegates[127] = d127; - var d128 = new __embedGenericHandle(_embedGenericHandle); + var d128 = new __embedFieldHandle(_embedFieldHandle); callbacks[128] = Marshal.GetFunctionPointerForDelegate(d128); delegates[128] = d128; - var d129 = new __getLocationOfThisType(_getLocationOfThisType); + var d129 = new __embedGenericHandle(_embedGenericHandle); callbacks[129] = Marshal.GetFunctionPointerForDelegate(d129); delegates[129] = d129; - var d130 = new __getAddressOfPInvokeTarget(_getAddressOfPInvokeTarget); + var d130 = new __getLocationOfThisType(_getLocationOfThisType); callbacks[130] = Marshal.GetFunctionPointerForDelegate(d130); delegates[130] = d130; - var d131 = new __GetCookieForPInvokeCalliSig(_GetCookieForPInvokeCalliSig); + var d131 = new __getAddressOfPInvokeTarget(_getAddressOfPInvokeTarget); callbacks[131] = Marshal.GetFunctionPointerForDelegate(d131); delegates[131] = d131; - var d132 = new __canGetCookieForPInvokeCalliSig(_canGetCookieForPInvokeCalliSig); + var d132 = new __GetCookieForPInvokeCalliSig(_GetCookieForPInvokeCalliSig); callbacks[132] = Marshal.GetFunctionPointerForDelegate(d132); delegates[132] = d132; - var d133 = new __getJustMyCodeHandle(_getJustMyCodeHandle); + var d133 = new __canGetCookieForPInvokeCalliSig(_canGetCookieForPInvokeCalliSig); callbacks[133] = Marshal.GetFunctionPointerForDelegate(d133); delegates[133] = d133; - var d134 = new __GetProfilingHandle(_GetProfilingHandle); + var d134 = new __getJustMyCodeHandle(_getJustMyCodeHandle); callbacks[134] = Marshal.GetFunctionPointerForDelegate(d134); delegates[134] = d134; - var d135 = new __getCallInfo(_getCallInfo); + var d135 = new __GetProfilingHandle(_GetProfilingHandle); callbacks[135] = Marshal.GetFunctionPointerForDelegate(d135); delegates[135] = d135; - var d136 = new __canAccessFamily(_canAccessFamily); + var d136 = new __getCallInfo(_getCallInfo); callbacks[136] = Marshal.GetFunctionPointerForDelegate(d136); delegates[136] = d136; - var d137 = new __isRIDClassDomainID(_isRIDClassDomainID); + var d137 = new __canAccessFamily(_canAccessFamily); callbacks[137] = Marshal.GetFunctionPointerForDelegate(d137); delegates[137] = d137; - var d138 = new __getClassDomainID(_getClassDomainID); + var d138 = new __isRIDClassDomainID(_isRIDClassDomainID); callbacks[138] = Marshal.GetFunctionPointerForDelegate(d138); delegates[138] = d138; - var d139 = new __getFieldAddress(_getFieldAddress); + var d139 = new __getClassDomainID(_getClassDomainID); callbacks[139] = Marshal.GetFunctionPointerForDelegate(d139); delegates[139] = d139; - var d140 = new __getStaticFieldCurrentClass(_getStaticFieldCurrentClass); + var d140 = new __getFieldAddress(_getFieldAddress); callbacks[140] = Marshal.GetFunctionPointerForDelegate(d140); delegates[140] = d140; - var d141 = new __getVarArgsHandle(_getVarArgsHandle); + var d141 = new __getStaticFieldCurrentClass(_getStaticFieldCurrentClass); callbacks[141] = Marshal.GetFunctionPointerForDelegate(d141); delegates[141] = d141; - var d142 = new __canGetVarArgsHandle(_canGetVarArgsHandle); + var d142 = new __getVarArgsHandle(_getVarArgsHandle); callbacks[142] = Marshal.GetFunctionPointerForDelegate(d142); delegates[142] = d142; - var d143 = new __constructStringLiteral(_constructStringLiteral); + var d143 = new __canGetVarArgsHandle(_canGetVarArgsHandle); callbacks[143] = Marshal.GetFunctionPointerForDelegate(d143); delegates[143] = d143; - var d144 = new __emptyStringLiteral(_emptyStringLiteral); + var d144 = new __constructStringLiteral(_constructStringLiteral); callbacks[144] = Marshal.GetFunctionPointerForDelegate(d144); delegates[144] = d144; - var d145 = new __getFieldThreadLocalStoreID(_getFieldThreadLocalStoreID); + var d145 = new __emptyStringLiteral(_emptyStringLiteral); callbacks[145] = Marshal.GetFunctionPointerForDelegate(d145); delegates[145] = d145; - var d146 = new __setOverride(_setOverride); + var d146 = new __getFieldThreadLocalStoreID(_getFieldThreadLocalStoreID); callbacks[146] = Marshal.GetFunctionPointerForDelegate(d146); delegates[146] = d146; - var d147 = new __addActiveDependency(_addActiveDependency); + var d147 = new __setOverride(_setOverride); callbacks[147] = Marshal.GetFunctionPointerForDelegate(d147); delegates[147] = d147; - var d148 = new __GetDelegateCtor(_GetDelegateCtor); + var d148 = new __addActiveDependency(_addActiveDependency); callbacks[148] = Marshal.GetFunctionPointerForDelegate(d148); delegates[148] = d148; - var d149 = new __MethodCompileComplete(_MethodCompileComplete); + var d149 = new __GetDelegateCtor(_GetDelegateCtor); callbacks[149] = Marshal.GetFunctionPointerForDelegate(d149); delegates[149] = d149; - var d150 = new __getTailCallCopyArgsThunk(_getTailCallCopyArgsThunk); + var d150 = new __MethodCompileComplete(_MethodCompileComplete); callbacks[150] = Marshal.GetFunctionPointerForDelegate(d150); delegates[150] = d150; - var d151 = new __convertPInvokeCalliToCall(_convertPInvokeCalliToCall); + var d151 = new __getTailCallCopyArgsThunk(_getTailCallCopyArgsThunk); callbacks[151] = Marshal.GetFunctionPointerForDelegate(d151); delegates[151] = d151; - var d152 = new __allocMem(_allocMem); + var d152 = new __convertPInvokeCalliToCall(_convertPInvokeCalliToCall); callbacks[152] = Marshal.GetFunctionPointerForDelegate(d152); delegates[152] = d152; - var d153 = new __reserveUnwindInfo(_reserveUnwindInfo); + var d153 = new __allocMem(_allocMem); callbacks[153] = Marshal.GetFunctionPointerForDelegate(d153); delegates[153] = d153; - var d154 = new __allocUnwindInfo(_allocUnwindInfo); + var d154 = new __reserveUnwindInfo(_reserveUnwindInfo); callbacks[154] = Marshal.GetFunctionPointerForDelegate(d154); delegates[154] = d154; - var d155 = new __allocGCInfo(_allocGCInfo); + var d155 = new __allocUnwindInfo(_allocUnwindInfo); callbacks[155] = Marshal.GetFunctionPointerForDelegate(d155); delegates[155] = d155; - var d156 = new __setEHcount(_setEHcount); + var d156 = new __allocGCInfo(_allocGCInfo); callbacks[156] = Marshal.GetFunctionPointerForDelegate(d156); delegates[156] = d156; - var d157 = new __setEHinfo(_setEHinfo); + var d157 = new __setEHcount(_setEHcount); callbacks[157] = Marshal.GetFunctionPointerForDelegate(d157); delegates[157] = d157; - var d158 = new __logMsg(_logMsg); + var d158 = new __setEHinfo(_setEHinfo); callbacks[158] = Marshal.GetFunctionPointerForDelegate(d158); delegates[158] = d158; - var d159 = new __doAssert(_doAssert); + var d159 = new __logMsg(_logMsg); callbacks[159] = Marshal.GetFunctionPointerForDelegate(d159); delegates[159] = d159; - var d160 = new __reportFatalError(_reportFatalError); + var d160 = new __doAssert(_doAssert); callbacks[160] = Marshal.GetFunctionPointerForDelegate(d160); delegates[160] = d160; - var d161 = new __allocMethodBlockCounts(_allocMethodBlockCounts); + var d161 = new __reportFatalError(_reportFatalError); callbacks[161] = Marshal.GetFunctionPointerForDelegate(d161); delegates[161] = d161; - var d162 = new __getMethodBlockCounts(_getMethodBlockCounts); + var d162 = new __allocMethodBlockCounts(_allocMethodBlockCounts); callbacks[162] = Marshal.GetFunctionPointerForDelegate(d162); delegates[162] = d162; - var d163 = new __recordCallSite(_recordCallSite); + var d163 = new __getMethodBlockCounts(_getMethodBlockCounts); callbacks[163] = Marshal.GetFunctionPointerForDelegate(d163); delegates[163] = d163; - var d164 = new __recordRelocation(_recordRelocation); + var d164 = new __recordCallSite(_recordCallSite); callbacks[164] = Marshal.GetFunctionPointerForDelegate(d164); delegates[164] = d164; - var d165 = new __getRelocTypeHint(_getRelocTypeHint); + var d165 = new __recordRelocation(_recordRelocation); callbacks[165] = Marshal.GetFunctionPointerForDelegate(d165); delegates[165] = d165; - var d166 = new __getExpectedTargetArchitecture(_getExpectedTargetArchitecture); + var d166 = new __getRelocTypeHint(_getRelocTypeHint); callbacks[166] = Marshal.GetFunctionPointerForDelegate(d166); delegates[166] = d166; - var d167 = new __getJitFlags(_getJitFlags); + var d167 = new __getExpectedTargetArchitecture(_getExpectedTargetArchitecture); callbacks[167] = Marshal.GetFunctionPointerForDelegate(d167); delegates[167] = d167; + var d168 = new __getJitFlags(_getJitFlags); + callbacks[168] = Marshal.GetFunctionPointerForDelegate(d168); + delegates[168] = d168; keepAlive = delegates; return (IntPtr)callbacks; diff --git a/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs index 6f23ac6f5a0347..30731de43bc50d 100644 --- a/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs @@ -417,9 +417,6 @@ private MethodIL Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, C Get_CORINFO_SIG_INFO(method, &methodInfo->args); Get_CORINFO_SIG_INFO(methodIL.GetLocals(), &methodInfo->locals); - methodInfo->osrInfo.ILOffset = 0; - methodInfo->osrInfo.PatchpointInfo = default(CORINFO_PATCHPOINT_INFO*); - return methodIL; } diff --git a/src/coreclr/src/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/src/tools/Common/JitInterface/CorInfoTypes.cs index 247448648f75a4..1647c4fb75da59 100644 --- a/src/coreclr/src/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/src/tools/Common/JitInterface/CorInfoTypes.cs @@ -92,7 +92,7 @@ public struct CORINFO_VarArgInfo { } - public struct CORINFO_PATCHPOINT_INFO + public struct PatchpointInfo { } diff --git a/src/coreclr/src/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/src/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 5f473260357d8c..c3f4f21333d00e 100644 --- a/src/coreclr/src/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/src/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -95,7 +95,7 @@ CORINFO_GENERICHANDLE_RESULT*,ref CORINFO_GENERICHANDLE_RESULT,void* CORINFO_METHOD_INFO*,CORINFO_METHOD_INFO*,void* CORINFO_FIELD_INFO*,CORINFO_FIELD_INFO*,void* CORINFO_CALL_INFO*,CORINFO_CALL_INFO*,void* -CORINFO_PATCHPOINT_INFO*,CORINFO_PATCHPOINT_INFO*,void* +PatchpointInfo*,PatchpointInfo*,void* DelegateCtorArgs*,ref DelegateCtorArgs,void* ICorDynamicInfo*,IntPtr,void* va_list,IntPtr @@ -185,6 +185,8 @@ FUNCTIONS void methodMustBeLoadedBeforeCodeIsRun( CORINFO_METHOD_HANDLE method ); CORINFO_METHOD_HANDLE mapMethodDeclToMethodImpl( CORINFO_METHOD_HANDLE method ); void getGSCookie( GSCookie * pCookieVal, GSCookie ** ppCookieVal ); + void setPatchpointInfo(PatchpointInfo* patchpointInfo); + PatchpointInfo* getOSRInfo(unsigned * ilOffset); void resolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken); void tryResolveToken(CORINFO_RESOLVED_TOKEN * pResolvedToken); void findSig( CORINFO_MODULE_HANDLE module, unsigned sigTOK, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO *sig ); @@ -254,7 +256,6 @@ FUNCTIONS void setBoundaries(CORINFO_METHOD_HANDLE ftn, ULONG32 cMap, ICorDebugInfo::OffsetMapping* pMap) void getVars(CORINFO_METHOD_HANDLE ftn, ULONG32* cVars, ICorDebugInfo::ILVarInfo** vars, bool* extendOthers) void setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo* vars) - void setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo); void*allocateArray(size_t cBytes); void freeArray(void*array); CORINFO_ARG_LIST_HANDLE getArgNext(CORINFO_ARG_LIST_HANDLE args); diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 8f93e1fa0f6826..a8b909c58202df 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -810,15 +810,6 @@ private void setBoundaries(CORINFO_METHOD_STRUCT_* ftn, uint cMap, OffsetMapping } } - /// - /// Record patchpoint info for the method - /// - private void setPatchpointInfo(CORINFO_PATCHPOINT_INFO* patchpointInfo) - { - // No patchpoint info when prejitting - throw new NotImplementedException(); - } - private void PublishEmptyCode() { _methodCodeNode.SetCode(new ObjectNode.ObjectData(Array.Empty(), null, 1, Array.Empty())); @@ -2151,6 +2142,24 @@ private void getGSCookie(IntPtr* pCookieVal, IntPtr** ppCookieVal) *ppCookieVal = (IntPtr *)ObjectToHandle(_compilation.NodeFactory.GetReadyToRunHelperCell(ReadyToRunHelper.GSCookie)); } + /// + /// Record patchpoint info for the method + /// + private void setPatchpointInfo(PatchpointInfo* patchpointInfo) + { + // No patchpoint info when prejitting + throw new NotImplementedException(); + } + + /// + /// Retrieve OSR info for the method + /// + private PatchpointInfo* getOSRInfo(ref uint ilOffset) + { + // No patchpoint info when prejitting + throw new NotImplementedException(); + } + private void getMethodVTableOffset(CORINFO_METHOD_STRUCT_* method, ref uint offsetOfIndirection, ref uint offsetAfterIndirection, ref bool isRelative) { throw new NotImplementedException("getMethodVTableOffset"); } private void expandRawHandleIntrinsic(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref CORINFO_GENERICHANDLE_RESULT pResult) diff --git a/src/coreclr/src/tools/crossgen2/jitinterface/jitinterface.h b/src/coreclr/src/tools/crossgen2/jitinterface/jitinterface.h index 9ad0d2546b8b46..f000e5f4891b5a 100644 --- a/src/coreclr/src/tools/crossgen2/jitinterface/jitinterface.h +++ b/src/coreclr/src/tools/crossgen2/jitinterface/jitinterface.h @@ -35,6 +35,8 @@ struct JitInterfaceCallbacks void (* methodMustBeLoadedBeforeCodeIsRun)(void * thisHandle, CorInfoException** ppException, void* method); void* (* mapMethodDeclToMethodImpl)(void * thisHandle, CorInfoException** ppException, void* method); void (* getGSCookie)(void * thisHandle, CorInfoException** ppException, void* pCookieVal, void** ppCookieVal); + void (* setPatchpointInfo)(void * thisHandle, CorInfoException** ppException, void* patchpointInfo); + void* (* getOSRInfo)(void * thisHandle, CorInfoException** ppException, unsigned* ilOffset); void (* resolveToken)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken); void (* tryResolveToken)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken); void (* findSig)(void * thisHandle, CorInfoException** ppException, void* module, unsigned sigTOK, void* context, void* sig); @@ -104,7 +106,6 @@ struct JitInterfaceCallbacks void (* setBoundaries)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int cMap, void* pMap); void (* getVars)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int* cVars, void* vars, bool* extendOthers); void (* setVars)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int cVars, void* vars); - void (* setPatchpointInfo)(void * thisHandle, CorInfoException** ppException, void* patchpointInfo); void* (* allocateArray)(void * thisHandle, CorInfoException** ppException, size_t cBytes); void (* freeArray)(void * thisHandle, CorInfoException** ppException, void* array); void* (* getArgNext)(void * thisHandle, CorInfoException** ppException, void* args); @@ -408,6 +409,23 @@ class JitInterfaceWrapper throw pException; } + virtual void setPatchpointInfo(void* patchpointInfo) + { + CorInfoException* pException = nullptr; + _callbacks->setPatchpointInfo(_thisHandle, &pException, patchpointInfo); + if (pException != nullptr) + throw pException; + } + + virtual void* getOSRInfo(unsigned* ilOffset) + { + CorInfoException* pException = nullptr; + void* _ret = _callbacks->getOSRInfo(_thisHandle, &pException, ilOffset); + if (pException != nullptr) + throw pException; + return _ret; + } + virtual void resolveToken(void* pResolvedToken) { CorInfoException* pException = nullptr; @@ -1017,14 +1035,6 @@ class JitInterfaceWrapper throw pException; } - virtual void setPatchpointInfo(void* patchpointInfo) - { - CorInfoException* pException = nullptr; - _callbacks->setPatchpointInfo(_thisHandle, &pException, patchpointInfo); - if (pException != nullptr) - throw pException; - } - virtual void* allocateArray(size_t cBytes) { CorInfoException* pException = nullptr; From 2ae1fc89bb7a5a4157d0947faa69b4537d2f8a1c Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 6 Mar 2020 20:14:22 -0800 Subject: [PATCH 09/31] remove unneeded change --- src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp b/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp index ac3955d6a08e32..2acd2b636de3cf 100644 --- a/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp +++ b/src/coreclr/src/ToolBox/superpmi/mcs/verbdumpmap.cpp @@ -40,9 +40,7 @@ void DumpMap(int index, MethodContext* mc) DumpPrimToConsoleBare(mc, cmi.args.retType, (DWORDLONG)cmi.args.retTypeClass); printf(" %s(", methodName); DumpSigToConsoleBare(mc, &cmi.args); - printf(")\""); - - printf("\n"); + printf(")\"\n"); } int verbDumpMap::DoWork(const char* nameOfInput) From 6620f5cb3813e6e6f8b1bb7e0760b7e11e8cfa2e Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Sat, 7 Mar 2020 10:30:53 -0800 Subject: [PATCH 10/31] always define PatchpointInfo struct --- src/coreclr/src/inc/patchpointinfo.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/coreclr/src/inc/patchpointinfo.h b/src/coreclr/src/inc/patchpointinfo.h index c3deed24238c3f..35bb46cf1df280 100644 --- a/src/coreclr/src/inc/patchpointinfo.h +++ b/src/coreclr/src/inc/patchpointinfo.h @@ -11,8 +11,6 @@ #ifndef _PATCHPOINTINFO_H_ #define _PATCHPOINTINFO_H_ -#ifdef FEATURE_ON_STACK_REPLACEMENT - // -------------------------------------------------------------------------------- // Describes information needed to make an OSR transition // - location of Il-visible locals and other important state on the @@ -152,12 +150,6 @@ struct PatchpointInfo int m_offsetAndExposureData[]; }; -#else - -struct PatchpointInfo {}; - -#endif // FEATURE_ON_STACK_REPLACEMENT - typedef DPTR(struct PatchpointInfo) PTR_PatchpointInfo; #endif // _PATCHPOINTINFO_H_ From e8246a8801fb910142a9e44ace35d6c0503236a8 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Sat, 7 Mar 2020 17:13:11 -0800 Subject: [PATCH 11/31] Revise how patchpoint info is stored in debug blob. When prejitting, or when OSR feature not defined, no flag byte is present. When jitting and OSR feature is defined, flag byte 1 indicates patchpoint info, flag byte 0, no patchpoint info. --- .../ReadyToRun/DebugInfoTableNode.cs | 7 ----- src/coreclr/src/vm/codeman.cpp | 22 +++++++++---- src/coreclr/src/vm/debuginfostore.cpp | 31 +++++++++++-------- src/coreclr/src/vm/debuginfostore.h | 3 +- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs index 33f1dd83452ca4..68f04436441446 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs @@ -84,13 +84,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) foreach (MethodWithGCInfo method in factory.EnumerateCompiledMethods()) { MemoryStream methodDebugBlob = new MemoryStream(); - -// #if FEATURE_ON_STACK_REPLACEMENT - // First byte of the debug info blob is an OSR flag byte. - // Currently always zero when prejitting; there is - // no OSR data to follow. - methodDebugBlob.WriteByte(0); -// #endif byte[] bounds = CreateBoundsBlobForMethod(method); byte[] vars = CreateVarBlobForMethod(method); diff --git a/src/coreclr/src/vm/codeman.cpp b/src/coreclr/src/vm/codeman.cpp index d7a49dec3eae18..85d2fda45e4495 100644 --- a/src/coreclr/src/vm/codeman.cpp +++ b/src/coreclr/src/vm/codeman.cpp @@ -3581,12 +3581,20 @@ BOOL EEJitManager::GetBoundariesAndVars( if (pDebugInfo == NULL) return FALSE; +#ifdef FEATURE_ON_STACK_REPLACEMENT + BOOL hasFlagByte = TRUE; +#else + BOOL hasFlagByte = FALSE; +#endif + // Uncompress. This allocates memory and may throw. CompressDebugInfo::RestoreBoundariesAndVars( fpNew, pNewData, // allocators pDebugInfo, // input - pcMap, ppMap, - pcVars, ppVars); // output + pcMap, ppMap, // output + pcVars, ppVars, // output + hasFlagByte + ); return TRUE; } @@ -5521,8 +5529,9 @@ BOOL NativeImageJitManager::GetBoundariesAndVars( CompressDebugInfo::RestoreBoundariesAndVars( fpNew, pNewData, // allocators pDebugInfo, // input - pcMap, ppMap, - pcVars, ppVars); // output + pcMap, ppMap, // output + pcVars, ppVars, // output + FALSE); // no patchpoint info return TRUE; } @@ -6735,8 +6744,9 @@ BOOL ReadyToRunJitManager::GetBoundariesAndVars( CompressDebugInfo::RestoreBoundariesAndVars( fpNew, pNewData, // allocators pDebugInfo, // input - pcMap, ppMap, - pcVars, ppVars); // output + pcMap, ppMap, // output + pcVars, ppVars, // output + FALSE); // no patchpoint info return TRUE; } diff --git a/src/coreclr/src/vm/debuginfostore.cpp b/src/coreclr/src/vm/debuginfostore.cpp index 478c530a7eb74d..f66988a4da4b90 100644 --- a/src/coreclr/src/vm/debuginfostore.cpp +++ b/src/coreclr/src/vm/debuginfostore.cpp @@ -560,7 +560,8 @@ void CompressDebugInfo::RestoreBoundariesAndVars( OUT ULONG32 * pcMap, // number of entries in ppMap OUT ICorDebugInfo::OffsetMapping **ppMap, // pointer to newly allocated array OUT ULONG32 *pcVars, - OUT ICorDebugInfo::NativeVarInfo **ppVars + OUT ICorDebugInfo::NativeVarInfo **ppVars, + BOOL hasFlagByte ) { CONTRACTL @@ -578,21 +579,25 @@ void CompressDebugInfo::RestoreBoundariesAndVars( if (ppVars != NULL) *ppVars = NULL; #ifdef FEATURE_ON_STACK_REPLACEMENT - - // Check flag byte and skip over any patchpoint info - BYTE flagByte = *pDebugInfo; - pDebugInfo++; - - if (flagByte == 1) - { - PTR_PatchpointInfo patchpointInfo = dac_cast(pDebugInfo); - pDebugInfo += patchpointInfo->PatchpointInfoSize(); - } - else + if (hasFlagByte) { - _ASSERTE(flagByte == 0); + // Check flag byte and skip over any patchpoint info + BYTE flagByte = *pDebugInfo; + pDebugInfo++; + + if (flagByte == 1) + { + PTR_PatchpointInfo patchpointInfo = dac_cast(pDebugInfo); + pDebugInfo += patchpointInfo->PatchpointInfoSize(); + } + else + { + _ASSERTE(flagByte == 0); + } } +#else + _ASSERTE(!hasFlagByte); #endif NibbleReader r(pDebugInfo, 12 /* maximum size of compressed 2 UINT32s */); diff --git a/src/coreclr/src/vm/debuginfostore.h b/src/coreclr/src/vm/debuginfostore.h index eac10328e1ae1e..54843309bbfbb9 100644 --- a/src/coreclr/src/vm/debuginfostore.h +++ b/src/coreclr/src/vm/debuginfostore.h @@ -96,7 +96,8 @@ class CompressDebugInfo OUT ULONG32 * pcMap, // number of entries in ppMap OUT ICorDebugInfo::OffsetMapping **ppMap, // pointer to newly allocated array OUT ULONG32 *pcVars, - OUT ICorDebugInfo::NativeVarInfo **ppVars + OUT ICorDebugInfo::NativeVarInfo **ppVars, + BOOL hasFlagByte ); #ifdef FEATURE_ON_STACK_REPLACEMENT From 60da2b4bd3e122343730d939b5fed4e256b8ca5b Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 9 Mar 2020 11:51:00 -0700 Subject: [PATCH 12/31] update doc --- docs/design/features/OnStackReplacement.md | 129 ++++++++++----------- 1 file changed, 63 insertions(+), 66 deletions(-) diff --git a/docs/design/features/OnStackReplacement.md b/docs/design/features/OnStackReplacement.md index 88cdf87bad9aa9..f78f7b753dd170 100644 --- a/docs/design/features/OnStackReplacement.md +++ b/docs/design/features/OnStackReplacement.md @@ -17,7 +17,7 @@ some prototype investigations, and describes how OSR might be used to re-host Edit and Continue and support more general transitions like deoptimization. * [Background](#1-Background) -* [Design Prinicples](#2-Design-Principles) +* [Design Principles](#2-Design-Principles) * [An Overview of OSR](#3-An-Overview-of-OSR) * [Complications](#4-Complications) * [The Prototype](#5-The-Prototype) @@ -141,11 +141,13 @@ in the IL. Additional state like the return address, implicit arguments, and so on must also be accounted for. As with GC safepoints, patchpoints can be handled in a _fully interruptible_ -manner here most any instruction boundary is a patchpoint, or a _partially +manner where most any instruction boundary is a patchpoint, or a _partially interruptible_ manner, where only some instruction boundaries are patchpoints. Also, as with GC, it is acceptable (if suboptimal) to over-identify the live state at a patch point. For instance, the live set can include values that never -end up being consumed by the new method. +end up being consumed by the new method (the upshot here is that we can simply +decide all the visible IL state is live everywhere, and so avoid running +liveness analysis in Tier0.) Also, as with GC safepoints, it is desirable to keep the volume of information that must be retained to describe patchpoints to a minimum. Most methods @@ -162,13 +164,13 @@ so that the method itself cannot execute indefinitely between patchpoints. Note by this rule, methods that do not contain any loops will not have any patchpoints. -From a compilation standpoint, it would be ideal if patchpoints were also stack -empty points, as this tends to minimize the live state. However, there is no -guarantee that execution of a method will reach stack empty points with any -frequency. So, a fully general patchpoint mechanism must handle the case where -the evaluation stack is not empty. However, it may be acceptable to only allow -patchpoints at stack empty points, as loops that execute with non-empty -evaluation stacks are likely rare. +From a compilation standpoint, it would be ideal if patchpoints were also IL +stack empty points, as this tends to minimize and regularize the live state. +However, there is no guarantee that execution of a method will reach stack empty +points with any frequency. So, a fully general patchpoint mechanism must handle +the case where the evaluation stack is not empty. However, it may be acceptable +to only allow patchpoints at stack empty points, as loops that execute with +non-empty evaluation stacks are likely rare. It is also beneficial if patchpoint selection works via a fairly simple set of rules, and here we propose that using the set of _lexical back edges_ or @@ -179,19 +181,21 @@ When generating unoptimized code, it is thus sufficient to note the target of any backwards branch in IL, the set of those locations (filtered to just the subset where the IL stack is empty) are the candidate patchpoints in the method. -With the above restrictions, the live state at every patchpoint in the method -can be reported in the same way. And because the patchpoints are necessarily at -basic block boundaries, and the jit will not allocate user state to registers -across such boundaries, patchpoint reporting in these cases is _untracked_ -(corresponds to untracked GC lifetime): that is to say, a single patchpoint -descriptor suffices for the entire method. Further, this information is a -superset of the current GC info, so the additional data needed to describe a -patchpoint is simply the set of live non-GC slots on the stack. +We can also rely on the fact that in our current unoptimized code, no IL state +is kept in registers across IL stack empty points—all the IL state is +stored in the native stack frame. This means that each patchpoint's live state +description is the same—he set of stack frame locations holding the IL +state. + +So, with the above restrictions, a single patchpoint descriptor suffices for the +entire method (analogous to the concept of _untracked_ GC lifetimes in the GC +info). Further, this information is a superset of the current GC info, so the +additional data needed to describe a patchpoint is simply the set of live non-GC +slots on the native stack frame. [Note: more general schemes like _deopt_ will require something more sophisticated.] - #### 3.1.2 Option I: non-stack empty patchpoints If it turns out we must also allow patchpoints at non-stack empty points, then @@ -432,10 +436,14 @@ Instead of generating an alternative that can only be used to transition from one specific patchpoint, the alternative method can offer multiple entry points to allow transition from some or all of the patchpoints in the original method. -Such a method would have many prologs and would require additional jit work. It -might also require reworking or suppressing some optimizations where we assume -that the jit method entry dominates all the code in the method � this would no -longer be true. +Note: After thinking about this a bit more, I think we can implement this +variant without needing multiple prologs—instead we can pass the IL offset +of the OSR entry point as a hidden argument to the OSR method, and have a switch +on that argument in the first body block to jump to the right place in the +method. This might be a viable option to control the potential explosion of OSR +variants for methods with many patchpoints. This method would still be OSR +specific—that is, it could not also serve as a normally callable Tier1 +method. #### 3.3.4 Option 4: Method Fragment @@ -449,7 +457,7 @@ fragment. The prototype generates partial methods with transition prolog. Per 4.1 below, the OSR method frame incorporates the (live portion of the) original method -frame instead of supplanting it.** +frame instead of supplanting it. ### 3.4 Transitions @@ -599,7 +607,7 @@ require special consideration. This is something that needs further investigation. **Prototype: The OSR methods have somewhat unusual unwind records that may be -confusing the debugger stack trace.** +confusing the (Windbg) debugger stack trace.** ### 4.6 Proposed Tier-0 Optimizations @@ -871,8 +879,9 @@ different stack offsets. ### 5.2 More Complex Examples If the OSR method needs to save and restore registers, then the epilog will have -two stack pointer adjustments: the first to reach the register save area on the OSR -frame, the second to reach the saved RBP and return address on the original frame. +two stack pointer adjustments: the first to reach the register save area on the +OSR frame, the second to reach the saved RBP and return address on the original +frame. For example: ```asm @@ -895,56 +904,31 @@ with unwind info: CodeOffset: 0x00 UnwindOp: UWOP_PUSH_NONVOL (0) OpInfo: rbp (5) ``` -If the OSR method needs to save RBP, we may see two RBP restores in the epilog; this -does not appear to cause problems during execution, as the "last one wins" when unwinding. +If the OSR method needs to save RBP, we may see two RBP restores in the epilog; +this does not appear to cause problems during execution, as the "last one wins" +when unwinding. However, the debugger (at least windbg) may end up being confused; any tool simply following the RBP chain will see the original frame is still "linked" into the active stack. -### 5.3 CORINFO_OSR_INFO and CORINFO_PATCHPOINT_INFO +### 5.3 PatchpointInfo -As noted above, when the jit is invoked to create the OSR method, it is passed -some extra data: +As noted above, when the jit is invoked to create the OSR method, it asks the +runtime for some extra data: * The IL offset of the OSR entry point -* A description of the original method frame +* `PatchpointInfo`: a description of the original method frame -These are bundled by the runtime into a new runtime data structure: -```C++ -struct CORINFO_OSR_INFO -{ - unsigned ilOffset; // IL offset of the patchpoint - CORINFO_PATCHPOINT_INFO patchpointInfo; // original frame info -}; -``` -which is part of the method info: -```C++ -struct CORINFO_METHOD_INFO -{ - CORINFO_METHOD_HANDLE ftn; - CORINFO_MODULE_HANDLE scope; - BYTE * ILCode; - unsigned ILCodeSize; - unsigned maxStack; - unsigned EHcount; - CorInfoOptions options; - CorInfoRegionKind regionKind; - CORINFO_SIG_INFO args; - CORINFO_SIG_INFO locals; - CORINFO_OSR_INFO osrInfo; -}; -``` -The `patchpointInfo` is produced by the jit when jitting the Tier0 method. It is +`PatchpointInfo` is produced by the jit when jitting the Tier0 method. It is allocated by the runtime similarly to other codegen metadata like GC info and unwind info and is likewise associated with the original method. When the -runtime helper decides to kick off an OSR jit, it bundles the IL offset (passed -to the helper as an argument and the patchpointInfo (retrieved from the method) -and attaches those to the method info. +runtime helper decides to kick off an OSR jit, it sets things up so that the jit +can retrieve this data. -Since the `patchpointInfo` is produced and consumed by the jit its format is -currently opaque to the runtime. It has the following general layout: +Since the `PatchpointInfo` is produced and consumed by the jit its format is +largely opaque to the runtime. It has the following general layout: ```C++ -struct CORINFO_PATCHPOINT_INFO +struct PatchpointInfo { unsigned m_patchpointInfoSize; unsigned m_ilSize; @@ -973,6 +957,19 @@ Initial data shows a general improvement in startup time, in particular for applications where startup was impacted by disabling quick-jitting of methods with loops (see dotnet/coreclr#24252). +### 5.5 Prototype Limitations and Workarounds + +* x64 only +* Struct promotion is currently disabled for OSR methods +* No OSR for synchronous methods +* No OSR for methods with profiler hooks +* No OSR for methods with localloc +* No OSR from "handler" regions (catch/finally/filter) + +The prototype trigger strategy is a hybrid: it has a per-frame local counter and +a per-patchpoint global counter (kept by the runtime). This is probably +something we need to re-assess. + ## 6 Edit and Continue As mentioned in the introduction, OSR is similar to Edit and Continue (EnC). EnC @@ -1065,7 +1062,7 @@ live state via inlining carefully tracked, one can transition from optimized code via OSR. Given the need to preserve address artifacts, this transition must be done -gradually -- first creating a frame for the innermost inlined method that +gradually—first creating a frame for the innermost inlined method that extends the original frame, then, when this innermost method returns, creating a frame for the next innermost inlined method, and so on, until finally the root method frame returns and can clean up the optimized method frame as well. From 5b4ec2d413171ec6206fabe699d4492f329fcc81 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 9 Mar 2020 17:02:33 -0700 Subject: [PATCH 13/31] moving OSR runtime data to loader allocator --- src/coreclr/src/vm/CMakeLists.txt | 2 + src/coreclr/src/vm/ceemain.cpp | 1 + src/coreclr/src/vm/eehash.inl | 2 +- src/coreclr/src/vm/jithelpers.cpp | 95 +++------------------- src/coreclr/src/vm/loaderallocator.cpp | 16 ++++ src/coreclr/src/vm/loaderallocator.hpp | 15 ++++ src/coreclr/src/vm/onstackreplacement.cpp | 71 +++++++++++++++++ src/coreclr/src/vm/onstackreplacement.h | 96 +++++++++++++++++++++++ 8 files changed, 215 insertions(+), 83 deletions(-) create mode 100644 src/coreclr/src/vm/onstackreplacement.cpp create mode 100644 src/coreclr/src/vm/onstackreplacement.h diff --git a/src/coreclr/src/vm/CMakeLists.txt b/src/coreclr/src/vm/CMakeLists.txt index 30471d40716370..449e731408f909 100644 --- a/src/coreclr/src/vm/CMakeLists.txt +++ b/src/coreclr/src/vm/CMakeLists.txt @@ -100,6 +100,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON methodtable.cpp nativeimage.cpp object.cpp + onstackreplacement.cpp pefile.cpp peimage.cpp peimagelayout.cpp @@ -207,6 +208,7 @@ set(VM_HEADERS_DAC_AND_WKS_COMMON methodtable.inl object.h object.inl + onstackreplacement.h pefile.h pefile.inl peimage.h diff --git a/src/coreclr/src/vm/ceemain.cpp b/src/coreclr/src/vm/ceemain.cpp index 23b31b6e07e1dc..e375b5120a0538 100644 --- a/src/coreclr/src/vm/ceemain.cpp +++ b/src/coreclr/src/vm/ceemain.cpp @@ -681,6 +681,7 @@ void EEStartupHelper(COINITIEE fFlags) CodeVersionManager::StaticInitialize(); TieredCompilationManager::StaticInitialize(); CallCountingManager::StaticInitialize(); + OnStackReplacementManager::StaticInitialize(); InitThreadManager(); STRESS_LOG0(LF_STARTUP, LL_ALWAYS, "Returned successfully from InitThreadManager"); diff --git a/src/coreclr/src/vm/eehash.inl b/src/coreclr/src/vm/eehash.inl index e49a80e022ebe6..ff16a7a7aca03d 100644 --- a/src/coreclr/src/vm/eehash.inl +++ b/src/coreclr/src/vm/eehash.inl @@ -633,7 +633,7 @@ FORCEINLINE EEHashEntry_t *EEHashTableBase: // from m_pVolatileBucketTable!!!!!!! A racing condition would occur. DWORD dwOldNumBuckets; - BucketTable* pBucketTable=m_pVolatileBucketTable; + BucketTable* pBucketTable=(BucketTable*)(PTR_BucketTable)m_pVolatileBucketTable.Load(); dwOldNumBuckets = pBucketTable->m_dwNumBuckets; _ASSERTE(pBucketTable->m_dwNumBuckets != 0); diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp index 1ee7697799ab7d..d67be97aac31a9 100644 --- a/src/coreclr/src/vm/jithelpers.cpp +++ b/src/coreclr/src/vm/jithelpers.cpp @@ -54,6 +54,7 @@ #include "runtimehandles.h" #include "castcache.h" +#include "onstackreplacement.h" //======================================================================== // @@ -4993,55 +4994,16 @@ HCIMPLEND #ifdef FEATURE_ON_STACK_REPLACEMENT -// Runtime state for a patchpoint. -// -// Patchpoint identity is currently the return address -// of the helper call in the jitted code. -// -// Todo: handle cases where IP addresses get recycled -struct PerPatchpointInfo -{ - PerPatchpointInfo() : - m_osrMethodCode(0), - m_patchpointCount(0), - m_flags(0), - m_patchpointId(0) - { - } - - enum - { - patchpoint_triggered = 0x1, - patchpoint_invalid = 0x2 - }; - - PCODE m_osrMethodCode; - LONG m_patchpointCount; - LONG m_flags; - - int m_patchpointId; -}; - -typedef EEPtrHashTable JitPatchpointTable; -JitPatchpointTable *g_pJitPatchpointTable = NULL; -CrstStatic g_pJitPatchpointCrst; -static int s_patchpointId = 0; -static bool s_warnedLimitedRange = false; - // Helper method to jit the OSR version of a method. // // Returns the address of the jitted code. // Returns NULL if osr method can't be created. -static PCODE JitPatchpointWorker(void* ip, int ilOffset) +static PCODE JitPatchpointWorker(MethodDesc* pMD, EECodeInfo& codeInfo, int ilOffset) { PCODE osrVariant = NULL; GCX_PREEMP(); - // Find the method desc for this bit of code - EECodeInfo codeInfo((PCODE)ip); - MethodDesc* pMD = codeInfo.GetMethodDesc(); - // Fetch the patchpoint info for the current method EEJitManager* jitMgr = ExecutionManager::GetEEJitManager(); CodeHeader* codeHdr = jitMgr->GetCodeHeaderFromStartAddress(codeInfo.GetStartAddress()); @@ -5078,13 +5040,13 @@ static PCODE JitPatchpointWorker(void* ip, int ilOffset) } // Helper method wrapper to set up a frame so we can invoke methods that might GC -HCIMPL2(PCODE, JIT_Patchpoint_Framed, void* ip, int ilOffset) +HCIMPL3(PCODE, JIT_Patchpoint_Framed, MethodDesc* pMD, EECodeInfo& codeInfo, int ilOffset) { PCODE result = NULL; HELPER_METHOD_FRAME_BEGIN_RET_0(); - result = JitPatchpointWorker(ip, ilOffset); + result = JitPatchpointWorker(pMD, codeInfo, ilOffset); HELPER_METHOD_FRAME_END(); @@ -5110,25 +5072,14 @@ void JIT_Patchpoint(int* counter, int ilOffset) STATIC_CONTRACT_MODE_COOPERATIVE; // Patchpoint identity is the helper return address - void* const ip = _ReturnAddress(); + PCODE ip = (PCODE)_ReturnAddress(); // Fetch or setup patchpoint info for this patchpoint. - PerPatchpointInfo * ppInfo = NULL; - BOOL hasData = g_pJitPatchpointTable->GetValueSpeculative(ip, (HashDatum*)&ppInfo); - - if (!hasData) - { - CrstHolder lock(&g_pJitPatchpointCrst); - hasData = g_pJitPatchpointTable->GetValue(ip, (HashDatum*)&ppInfo); - - if (!hasData) - { - // Todo: find right heap for this data - ppInfo = new (nothrow) PerPatchpointInfo(); - g_pJitPatchpointTable->InsertValue(ip, (HashDatum)ppInfo); - ppInfo->m_patchpointId = ++s_patchpointId; - } - } + EECodeInfo codeInfo(ip); + MethodDesc* pMD = codeInfo.GetMethodDesc(); + LoaderAllocator* allocator = pMD->GetLoaderAllocator(); + OnStackReplacementManager* manager = allocator->GetOnStackReplacementManager(); + PerPatchpointInfo * ppInfo = manager->GetPerPatchpointInfo(ip); const int verbose = g_pConfig->OSR_Verbose(); const int ppId = ppInfo->m_patchpointId; @@ -5187,12 +5138,6 @@ void JIT_Patchpoint(int* counter, int ilOffset) const int lowId = g_pConfig->OSR_LowId(); const int highId = g_pConfig->OSR_HighId(); - if (!s_warnedLimitedRange && ((lowId != -1) || (highId != 10000000))) - { - printf("\n### Runtime: PATCHPOINTS LIMITED to range [%d,%d]\n\n", lowId, highId); - s_warnedLimitedRange = true; - } - if ((ppId < lowId) || (ppId > highId)) { if (verbose > 0) @@ -5286,9 +5231,6 @@ void JIT_Patchpoint(int* counter, int ilOffset) #if _DEBUG if (verbose > 0) { - EECodeInfo codeInfo((PCODE)ip); - MethodDesc* pMD = codeInfo.GetMethodDesc(); - printf("### Runtime: patchpoint [%d] 0x%p TRIGGER hit:%d bump:%d native:0x%x il:0x%x in 0x%p %s::%s %s\n", ppId, ip, @@ -5300,7 +5242,7 @@ void JIT_Patchpoint(int* counter, int ilOffset) #endif // Invoke the helper to build the OSR method - osrMethodCode = HCCALL2(JIT_Patchpoint_Framed, ip, ilOffset); + osrMethodCode = HCCALL3(JIT_Patchpoint_Framed, pMD, codeInfo, ilOffset); // If that failed, mark the patchpoint as invaild. if (osrMethodCode == NULL) @@ -5352,11 +5294,11 @@ void JIT_Patchpoint(int* counter, int ilOffset) } // Now unwind back to the original method caller frame. - EECodeInfo codeInfo(GetIP(&frameContext)); + EECodeInfo callerCodeInfo(GetIP(&frameContext)); frameContext.ContextFlags = CONTEXT_FULL; ULONG_PTR establisherFrame = 0; PVOID handlerData = NULL; - RtlVirtualUnwind(UNW_FLAG_NHANDLER, codeInfo.GetModuleBase(), GetIP(&frameContext), codeInfo.GetFunctionEntry(), + RtlVirtualUnwind(UNW_FLAG_NHANDLER, callerCodeInfo.GetModuleBase(), GetIP(&frameContext), callerCodeInfo.GetFunctionEntry(), &frameContext, &handlerData, &establisherFrame, NULL); // Now, set FP and SP back to the values they had just before this helper was called, @@ -5529,17 +5471,6 @@ void InitJITHelpers2() if (!tempGenericHandleCache->Init(59, &sLock)) COMPlusThrowOM(); g_pJitGenericHandleCache = tempGenericHandleCache.Extract(); - -#ifdef FEATURE_ON_STACK_REPLACEMENT - g_pJitPatchpointCrst.Init(CrstJitPatchpoint, CRST_UNSAFE_COOPGC); - - // Allocate and initialize the patchpoint table - NewHolder tempJitPatchpointTable (new JitPatchpointTable()); - LockOwner sLock2 = {&g_pJitPatchpointCrst, IsOwnerOfCrst}; - if (!tempJitPatchpointTable->Init(59, &sLock2)) - COMPlusThrowOM(); - g_pJitPatchpointTable = tempJitPatchpointTable.Extract(); -#endif // FEATURE_ON_STACK_REPLACEMENT } #if defined(TARGET_AMD64) || defined(TARGET_ARM) diff --git a/src/coreclr/src/vm/loaderallocator.cpp b/src/coreclr/src/vm/loaderallocator.cpp index d39cf0a2a44f9c..0544c53b95ad6b 100644 --- a/src/coreclr/src/vm/loaderallocator.cpp +++ b/src/coreclr/src/vm/loaderallocator.cpp @@ -58,6 +58,10 @@ LoaderAllocator::LoaderAllocator() m_callCountingManager = NULL; #endif +#ifdef FEATURE_ON_STACK_REPLACEMENT + m_onStackReplacementManager = NULL; +#endif + m_fGCPressure = false; m_fTerminated = false; m_fUnloaded = false; @@ -1222,6 +1226,10 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) m_callCountingManager = new CallCountingManager(); } #endif + +#ifdef FEATURE_ON_STACK_REPLACEMENT + m_onStackReplacementManager = new OnStackReplacementManager(); +#endif } @@ -1343,6 +1351,14 @@ void LoaderAllocator::Terminate() } #endif +#ifdef FEATURE_ON_STACK_REPLACEMENT + if (m_onStackReplacementManager != NULL) + { + delete m_onStackReplacementManager; + m_onStackReplacementManager = NULL; + } +#endif + // In collectible types we merge the low frequency and high frequency heaps // So don't destroy them twice. if ((m_pLowFrequencyHeap != NULL) && (m_pLowFrequencyHeap != m_pHighFrequencyHeap)) diff --git a/src/coreclr/src/vm/loaderallocator.hpp b/src/coreclr/src/vm/loaderallocator.hpp index 850eb77bc38f6c..83dc1a44c242f9 100644 --- a/src/coreclr/src/vm/loaderallocator.hpp +++ b/src/coreclr/src/vm/loaderallocator.hpp @@ -23,6 +23,7 @@ class FuncPtrStubs; #include "callcounting.h" #include "methoddescbackpatchinfo.h" #include "crossloaderallocatorhash.h" +#include "onstackreplacement.h" #define VPTRU_LoaderAllocator 0x3200 @@ -281,6 +282,10 @@ class LoaderAllocator MethodDescBackpatchInfoTracker m_methodDescBackpatchInfoTracker; #endif +#ifdef FEATURE_ON_STACK_REPLACEMENT + PTR_OnStackReplacementManager m_onStackReplacementManager; +#endif + #ifndef DACCESS_COMPILE public: @@ -609,6 +614,16 @@ class LoaderAllocator return &m_methodDescBackpatchInfoTracker; } #endif + +#ifdef FEATURE_ON_STACK_REPLACEMENT +public: + PTR_OnStackReplacementManager GetOnStackReplacementManager() + { + LIMITED_METHOD_CONTRACT; + return m_onStackReplacementManager; + } +#endif // FEATURE_ON_STACK_REPLACEMENT + }; // class LoaderAllocator typedef VPTR(LoaderAllocator) PTR_LoaderAllocator; diff --git a/src/coreclr/src/vm/onstackreplacement.cpp b/src/coreclr/src/vm/onstackreplacement.cpp new file mode 100644 index 00000000000000..8e9747222b40e4 --- /dev/null +++ b/src/coreclr/src/vm/onstackreplacement.cpp @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: onstackreplacement.cpp +// +// =========================================================================== + +#include "common.h" +#include "onstackreplacement.h" + +#ifdef FEATURE_ON_STACK_REPLACEMENT + +int OnStackReplacementManager::s_patchpointId = 0; +CrstStatic OnStackReplacementManager::s_lock; + +void OnStackReplacementManager::StaticInitialize() +{ + WRAPPER_NO_CONTRACT; + s_lock.Init(CrstJitPatchpoint, CrstFlags(CRST_UNSAFE_COOPGC)); +} + +OnStackReplacementManager::OnStackReplacementManager() : m_jitPatchpointTable() +{ + CONTRACTL + { + GC_NOTRIGGER; + CAN_TAKE_LOCK; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + + LockOwner lock = {&s_lock, IsOwnerOfCrst}; + m_jitPatchpointTable.Init(INITIAL_TABLE_SIZE, &lock); +} + +// Fetch or create patchpoint info for this patchpoint. +PerPatchpointInfo* OnStackReplacementManager::GetPerPatchpointInfo(PCODE ip) +{ + CONTRACTL + { + GC_NOTRIGGER; + CAN_TAKE_LOCK; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + PTR_PCODE ppId = dac_cast(ip); + PTR_PerPatchpointInfo ppInfo = NULL; + + BOOL hasData = m_jitPatchpointTable.GetValueSpeculative(ppId, (HashDatum*)&ppInfo); + + if (!hasData) + { + CrstHolder lock(&s_lock); + hasData = m_jitPatchpointTable.GetValue(ppId, (HashDatum*)&ppInfo); + + if (!hasData) + { + ppInfo = dac_cast(new PerPatchpointInfo()); + m_jitPatchpointTable.InsertValue(ppId, (HashDatum)ppInfo); + ppInfo->m_patchpointId = ++s_patchpointId; + } + } + + return ppInfo; +} + +#endif // FEATURE_ON_STACK_REPLACEMENT + + diff --git a/src/coreclr/src/vm/onstackreplacement.h b/src/coreclr/src/vm/onstackreplacement.h new file mode 100644 index 00000000000000..f81f611dd6f83f --- /dev/null +++ b/src/coreclr/src/vm/onstackreplacement.h @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: onstackreplacement.h +// +// =========================================================================== + +#ifndef ON_STACK_REPLACEMENT_H +#define ON_STACK_REPLACEMENT_H + +#ifdef FEATURE_ON_STACK_REPLACEMENT + +#include "daccess.h" +#include "eehash.h" + +// PerPatchpointInfo is the runtime state tracked for each active patchpoint. +// +// A patchpoint becomes active when the JIT_HELP_PATCHPOINT helper is invoked +// by jitted code. +// +struct PerPatchpointInfo +{ + PerPatchpointInfo() : + m_osrMethodCode(0), + m_patchpointCount(0), + m_flags(0), + m_patchpointId(0) + { + } + + // Flag bits + enum + { + patchpoint_triggered = 0x1, + patchpoint_invalid = 0x2 + }; + + PCODE m_osrMethodCode; + LONG m_patchpointCount; + LONG m_flags; + + int m_patchpointId; +}; + +typedef DPTR(PerPatchpointInfo) PTR_PerPatchpointInfo; +typedef EEPtrHashTable JitPatchpointTable; + +// OnStackReplacementManager keeps track of mapping from patchpoint id to +// per patchpoint info. +// +// Patchpoint identity is currently the return address of the helper call +// in the jitted code. +// +class OnStackReplacementManager +{ +public: + static void StaticInitialize(); + +public: + OnStackReplacementManager(); + +public: + PerPatchpointInfo* GetPerPatchpointInfo(PCODE ip); + +private: + + enum + { + INITIAL_TABLE_SIZE = 10 + }; + + static int s_patchpointId; + static CrstStatic s_lock; + +private: + + JitPatchpointTable m_jitPatchpointTable; +}; + +#else // FEATURE_TIERED_COMPILATION + +class OnStackReplacementManager +{ +public: + static void StaticInitialize() {} +public: + + OnStackReplacementManager() {} +}; + +#endif // FEATURE_TIERED_COMPILATION + +typedef DPTR(OnStackReplacementManager) PTR_OnStackReplacementManager; + +#endif // ON_STACK_REPLACEMENT_H From 4e8e33f21716c8c3fe2be9f163e48efd4e8de7ce Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 9 Mar 2020 19:58:36 -0700 Subject: [PATCH 14/31] logging --- src/coreclr/src/inc/clrconfigvalues.h | 5 +- src/coreclr/src/vm/eeconfig.cpp | 5 +- src/coreclr/src/vm/eeconfig.h | 8 ++- src/coreclr/src/vm/jithelpers.cpp | 96 +++++++++------------------ 4 files changed, 44 insertions(+), 70 deletions(-) diff --git a/src/coreclr/src/inc/clrconfigvalues.h b/src/coreclr/src/inc/clrconfigvalues.h index 8cedb2bd069cc9..aed8843b06a45e 100644 --- a/src/coreclr/src/inc/clrconfigvalues.h +++ b/src/coreclr/src/inc/clrconfigvalues.h @@ -652,9 +652,8 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DeleteCallCountingStubsAfter, W("TC_DeleteC #ifdef FEATURE_ON_STACK_REPLACEMENT RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_CounterBump, W("OSR_CounterBump"), 1000, "Counter reload value when a patchpoint is hit") RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_HitLimit, W("OSR_HitLimit"), 10, "Number of times a patchpoint must call back to trigger an OSR transition") -RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_Verbose, W("OSR_Verbose"), 0, "Nonzero enables OSR diganostic output") -RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_LowId, W("OSR_LowId"), -1, "Low end of enabled patchpoint range (inclusive)"); -RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_HighId, W("OSR_HighId"), 10000000, "High end of enabled patchpoint range (inclusive)"); +CONFIG_DWORD_INFO(INTERNAL_OSR_LowId, W("OSR_LowId"), -1, "Low end of enabled patchpoint range (inclusive)"); +CONFIG_DWORD_INFO(INTERNAL_OSR_HighId, W("OSR_HighId"), 10000000, "High end of enabled patchpoint range (inclusive)"); #endif /// diff --git a/src/coreclr/src/vm/eeconfig.cpp b/src/coreclr/src/vm/eeconfig.cpp index 1881f656c4b121..f3dbe66fd7c6ff 100644 --- a/src/coreclr/src/vm/eeconfig.cpp +++ b/src/coreclr/src/vm/eeconfig.cpp @@ -342,7 +342,6 @@ HRESULT EEConfig::Init() #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) - dwOSR_Verbose = 0; dwOSR_HitLimit = 10; dwOSR_CounterBump = 5000; #endif @@ -1276,9 +1275,11 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) - dwOSR_Verbose = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_Verbose); dwOSR_HitLimit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_HitLimit); dwOSR_CounterBump = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_CounterBump); +#endif + +#if defined(FEATURE_ON_STACK_REPLACEMENT) && defined(_DEBUG) dwOSR_LowId = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_LowId); dwOSR_HighId = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_OSR_HighId); #endif diff --git a/src/coreclr/src/vm/eeconfig.h b/src/coreclr/src/vm/eeconfig.h index 1816a450a47bcc..7eb0e89ff8596d 100644 --- a/src/coreclr/src/vm/eeconfig.h +++ b/src/coreclr/src/vm/eeconfig.h @@ -292,9 +292,11 @@ class EEConfig #if defined(FEATURE_ON_STACK_REPLACEMENT) // OSR Config - DWORD OSR_Verbose() const { LIMITED_METHOD_CONTRACT; return dwOSR_Verbose; } DWORD OSR_CounterBump() const { LIMITED_METHOD_CONTRACT; return dwOSR_CounterBump; } DWORD OSR_HitLimit() const { LIMITED_METHOD_CONTRACT; return dwOSR_HitLimit; } +#endif + +#if defined(FEATURE_ON_STACK_REPLACEMENT) && defined(_DEBUG) DWORD OSR_LowId() const { LIMITED_METHOD_CONTRACT; return dwOSR_LowId; } DWORD OSR_HighId() const { LIMITED_METHOD_CONTRACT; return dwOSR_HighId; } #endif @@ -1037,9 +1039,11 @@ class EEConfig #endif #if defined(FEATURE_ON_STACK_REPLACEMENT) - DWORD dwOSR_Verbose; DWORD dwOSR_HitLimit; DWORD dwOSR_CounterBump; +#endif + +#if defined(FEATURE_ON_STACK_REPLACEMENT) && defined(_DEBUG) DWORD dwOSR_LowId; DWORD dwOSR_HighId; #endif diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp index d67be97aac31a9..04ad5b6f8c66d2 100644 --- a/src/coreclr/src/vm/jithelpers.cpp +++ b/src/coreclr/src/vm/jithelpers.cpp @@ -5012,7 +5012,8 @@ static PCODE JitPatchpointWorker(MethodDesc* pMD, EECodeInfo& codeInfo, int ilOf if (patchpointInfo == NULL) { - // log ... + // Unexpected, but not fatal + STRESS_LOG1(LF_TIEREDCOMPILATION, LL_WARNING, "JitPatchpointWorker: failed to restore patchpoint info for Method=0x%pM\n", pMD); return NULL; } @@ -5026,12 +5027,16 @@ static PCODE JitPatchpointWorker(MethodDesc* pMD, EECodeInfo& codeInfo, int ilOf HRESULT hr = ilCodeVersion.AddNativeCodeVersion(pMD, NativeCodeVersion::OptimizationTier1OSR, &osrNativeCodeVersion, patchpointInfo, ilOffset); if (FAILED(hr)) { - // log ... + // Unexpected, but not fatal + STRESS_LOG1(LF_TIEREDCOMPILATION, LL_WARNING, "JitPatchpointWorker: failed to add native code version for Method=0x%pM\n", pMD); return NULL; } } // Invoke the jit to compile the OSR version + LOG((LF_TIEREDCOMPILATION, LL_INFO10, "JitPatchpointWorker: creating OSR version of Method=0x%pM (%s::%s) at offset %d\n", + pMD, pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, ilOffset)); + PrepareCodeConfigBuffer configBuffer(osrNativeCodeVersion); PrepareCodeConfig *config = configBuffer.GetConfig(); osrVariant = pMD->PrepareCode(config); @@ -5081,7 +5086,6 @@ void JIT_Patchpoint(int* counter, int ilOffset) OnStackReplacementManager* manager = allocator->GetOnStackReplacementManager(); PerPatchpointInfo * ppInfo = manager->GetPerPatchpointInfo(ip); - const int verbose = g_pConfig->OSR_Verbose(); const int ppId = ppInfo->m_patchpointId; const int counterBump = g_pConfig->OSR_CounterBump(); @@ -5100,22 +5104,11 @@ void JIT_Patchpoint(int* counter, int ilOffset) // so we can update it without worrying about other threads. *counter = counterBump; - // OSR is only supported on x64, currently. -#if !defined(TARGET_AMD64) - if (verbose > 1) - { - printf("### Runtime: no OSR for this isa/abi yet [%d] 0x%p\n", ppId, ip); - } - return; -#endif - - // Is this an invalid patchpoint? If so, just return to the Tier0 method. + // Is this a patchpoint that was previously marked as invalid? If so, just return to the Tier0 method. if ((ppInfo->m_flags & PerPatchpointInfo::patchpoint_invalid) == PerPatchpointInfo::patchpoint_invalid) { - if (verbose > 1) - { - printf("### Runtime: INVALID patchpoint [%d] 0x%p\n", ppId, ip); - } + LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "Jit_Patchpoint: invalid patchpoint [%d] (0x%p) in Method=0x%pM (%s::%s) at offset %d\n", + ppId, ip, pMD, pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, ilOffset)); return; } @@ -5135,18 +5128,19 @@ void JIT_Patchpoint(int* counter, int ilOffset) // that point until the method returns, if we trigger on an // early patchpoint in a method, we may never see triggers on // a later one. + +#ifdef _DEBUG const int lowId = g_pConfig->OSR_LowId(); const int highId = g_pConfig->OSR_HighId(); if ((ppId < lowId) || (ppId > highId)) { - if (verbose > 0) - { - printf("### Runtime: IGNORING patchpoint [%d] 0x%p\n", ppId, ip); - } + LOG((LF_TIEREDCOMPILATION, LL_INFO10, "Jit_Patchpoint: ignoring patchpoint [%d] (0x%p) in Method=0x%pM (%s::%s) at offset %d\n", + ppId, ip, pMD, pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, ilOffset)); return; } - +#endif + // Second, only request the OSR method if this patchpoint has // been hit often enough. // @@ -5173,14 +5167,13 @@ void JIT_Patchpoint(int* counter, int ilOffset) // we're not too eager to create OSR methods (since there is // some jit cost), but are eager enough to transition before // we run too much Tier0 code. - + // const int hitLimit = g_pConfig->OSR_HitLimit(); const int hitCount = InterlockedIncrement(&ppInfo->m_patchpointCount); - - if ((verbose > 1) || ((verbose > 0) && (hitCount == 1))) - { - printf("### Runtime: patchpoint [%d] 0x%p hit:%d (limit %d)\n", ppId, ip, hitCount, hitLimit); - } + const int hitLogLevel = (hitCount == 1) ? LL_INFO10 : LL_INFO1000; + + LOG((LF_TIEREDCOMPILATION, hitLogLevel, "Jit_Patchpoint: patchpoint [%d] (0x%p) hit %d in Method=0x%pM (%s::%s) [il offset %d] (limit %d)\n", + ppId, ip, hitCount, pMD, pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, ilOffset, hitLimit)); // Defer, if we haven't yet reached the limit if (hitCount < hitLimit) @@ -5192,11 +5185,7 @@ void JIT_Patchpoint(int* counter, int ilOffset) LONG oldFlags = ppInfo->m_flags; if ((oldFlags & PerPatchpointInfo::patchpoint_triggered) == PerPatchpointInfo::patchpoint_triggered) { - // Some other thread is already working on the OSR method, just come back later. - if (verbose > 1) - { - printf("### Runtime: AWAITING patchpoint [%d] 0x%p\n", ppId, ip); - } + LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "Jit_Patchpoint: AWAITING OSR method for patchpoint [%d] (0x%p)\n", ppId, ip)); return; } @@ -5205,11 +5194,7 @@ void JIT_Patchpoint(int* counter, int ilOffset) if (!triggerTransition) { - // Some other thread won the race, just come back later. - if (verbose > 1) - { - printf("### Runtime: (lost race) AWAITING patchpoint [%d] 0x%p\n", ppId, ip); - } + LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "Jit_Patchpoint: (lost race) AWAITING OSR method for patchpoint [%d] (0x%p)\n", ppId, ip)); return; } @@ -5227,19 +5212,7 @@ void JIT_Patchpoint(int* counter, int ilOffset) // // In this prototype we want to expose bugs in the jitted code // for OSR methods, so we stick with synchronous creation. - -#if _DEBUG - if (verbose > 0) - { - printf("### Runtime: patchpoint [%d] 0x%p TRIGGER hit:%d bump:%d native:0x%x il:0x%x in 0x%p %s::%s %s\n", - ppId, - ip, - hitCount, counterBump, - codeInfo.GetRelOffset(), ilOffset, - pMD, - pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, pMD->m_pszDebugMethodSignature); - } -#endif + LOG((LF_TIEREDCOMPILATION, LL_INFO10, "Jit_Patchpoint: patchpoint [%d] (0x%p) TRIGGER at count %d\n", ppId, ip, hitCount)); // Invoke the helper to build the OSR method osrMethodCode = HCCALL3(JIT_Patchpoint_Framed, pMD, codeInfo, ilOffset); @@ -5247,10 +5220,9 @@ void JIT_Patchpoint(int* counter, int ilOffset) // If that failed, mark the patchpoint as invaild. if (osrMethodCode == NULL) { - if (verbose > 0) - { - printf("### Runtime: unable to create OSR method, marking patchpoint [%d] 0x%p INVALID\n", ppId, ip); - } + // Unexpected, but not fatal + STRESS_LOG5(LF_TIEREDCOMPILATION, LL_WARNING, "Jit_Patchpoint: patchpoint [%d] (0x%p) OSR method creation failed," + " marking patchpoint invalid for Method=0x%pM il offset %d\n", ppId, ip, hitCount, pMD, ilOffset); InterlockedOr(&ppInfo->m_flags, (LONG)PerPatchpointInfo::patchpoint_invalid); return; @@ -5289,8 +5261,9 @@ void JIT_Patchpoint(int* counter, int ilOffset) // We expect to be back at the right IP if ((UINT_PTR)ip != GetIP(&frameContext)) { - printf("### Runtime: patchpoint [%d] 0x%p TRANSITION -- odd that context has ip %p\n", - ppId, ip, GetIP(&frameContext)); + // Should be fatal + STRESS_LOG3(LF_TIEREDCOMPILATION, LL_INFO10, "Jit_Patchpoint: patchpoint [%d] (0x%p) TRANSITION" + " unexpected context IP 0x%p\n", ppId, ip, GetIP(&frameContext)); } // Now unwind back to the original method caller frame. @@ -5319,12 +5292,9 @@ void JIT_Patchpoint(int* counter, int ilOffset) // Note we can get here w/o triggering, if there is an existing OSR method and // we hit the patchpoint. - if (((verbose > 0) && isNewMethod) || (verbose > 1)) - { - printf("### Runtime: patchpoint [%d] 0x%p TRANSITION%s RSP %p RBP %p RIP %p (prev RBP %p)\n", - ppId, ip, isNewMethod ? "" : " (existing)", currentSP, currentFP, osrMethodCode , *(char**)currentFP); - } - + const int transitionLogLevel = isNewMethod ? LL_INFO10 : LL_INFO1000; + LOG((LF_TIEREDCOMPILATION, transitionLogLevel, "Jit_Patchpoint: patchpoint [%d] (0x%p) TRANSITION to ip 0x%p\n", ppId, ip, osrMethodCode)); + // Install new entry point as IP SetIP(&frameContext, osrMethodCode); From a75df6b8cae04a08c9ba605327bcf782375c2e3c Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 10 Mar 2020 00:37:12 -0700 Subject: [PATCH 15/31] more doc updates --- docs/design/features/OnStackReplacement.md | 37 ++++++++++++++++------ 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/docs/design/features/OnStackReplacement.md b/docs/design/features/OnStackReplacement.md index f78f7b753dd170..905696875046ea 100644 --- a/docs/design/features/OnStackReplacement.md +++ b/docs/design/features/OnStackReplacement.md @@ -664,9 +664,27 @@ scheme, where there is an initial prolog that emulates the original version prolog and duplicates its saves, and then a subsequent "shrink wrapped" prolog & epilog that saves any additional registers in a disjoint area. -**Prototype: runtime recovers most callee-saves from original method via virtual -unwind; new method just saves what is needed in its own save area. The only -register save live on the old frame is the saved FP.** +**Prototype:** When it is time to transition, the patchpoint helper virtually +unwinds two frames from the stack—its own frame, and the frame for the +original method. So the unwound context restores the callee saves done by the +original method. That turns out to be sufficient. + +You might think the helper would need to carefully save all the register state +on entry, but that's not the case. Because the original method is un-optimized, +there isn't any live IL state in registers across the call to the patchpoint +helper—all the live IL state for the method is on the original +frame—so the argument and caller-save registers are dead at the +patchpoint. Thus only part of register state that is significant for ongoing +computation is the callee-saves, which are recovered via virtual unwind, and the +frame and stack pointers of the original method, which are likewise recovered by +virtual unwind. + +With this context in hand, the helper then "calls" the OSR method by restoring +the context. The OSR method performs its own callee-saves as needed, and +recovers the arguments/IL state from the original frame. + +If we were to support patchpoints in optimized code things would be more +complicated. ### 4.9 Synchronous Methods @@ -810,8 +828,9 @@ G_M6138_IG07: Because Tier0 is unoptimized code, the patchpoint sequence is currently unoptimized. This leads to a moderate amount of code bloat in methods with patchpoints. The overall code size impact of patchpoints (as measured by -`jit-diff`) is around 2%, but this is a bit misleading, as many Tier0 methods -won't require patchpoints. This is something that can be improved. +`jit-diff`) is around 2%, but this is this is an understatement of the impact to +methods that have patchpoints, as most Tier0 methods won't require patchpoints. +This is something that can be improved. The OSR method for this patchpoint is: ```asm @@ -1014,7 +1033,7 @@ So, for EnC, we see that: generated by the jit for the original method. - The trigger is a set of special breakpoints placed into the original method native code by the debugger when an edit is applied to the method. -- When an EnC breakpoint is hit, the debugger can chose whether or not to +- When an EnC breakpoint is hit, the debugger can choose whether or not to initiate a transition. - If the debugger initiates a transition, it is done synchronously: the new version of the method is jitted if necessary and the currently active frame is @@ -1053,9 +1072,9 @@ original frame, and two alternate methods (or N alternate methods). The general solution is to first ensure that the live state never decreases. The patchpoint locations are determined early, and any values truly live at a patchpoint at that initial stage of compilation are forced to remain live at -that patchpoint always. So, some dead stores are inhibited, and some forms of -code motion are inhibited (e.g. one cannot sink a store to a local out of a -loop, as the patchpoint at loop top would not observe the updated value). +that patchpoint always. So, some dead store elimination is inhibited, and some +forms of code motion are inhibited (e.g. one cannot sink a store to a local out +of a loop, as the patchpoint at loop top would not observe the updated value). With all the "naive" state guaranteed live at a patchpoint, and any additions to live state via inlining carefully tracked, one can transition from optimized From 53f2144a30204337f018538645fff9cf8345407e Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 10 Mar 2020 10:05:35 -0700 Subject: [PATCH 16/31] feedback on jit changes --- src/coreclr/src/jit/codegencommon.cpp | 6 +- src/coreclr/src/jit/compiler.cpp | 150 ++++++++++++++------------ src/coreclr/src/jit/compiler.h | 2 + src/coreclr/src/jit/gcencode.cpp | 4 +- 4 files changed, 90 insertions(+), 72 deletions(-) diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index 3113b0d4bc7f0c..acb5ff0bbc6ee6 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -7983,9 +7983,11 @@ void CodeGen::genFnProlog() // Update the arg initial register locations. compiler->lvaUpdateArgsWithInitialReg(); + // Home incoming arguments and generate any required inits. + // OSR handles this by moving the values from the original frame. + // if (!compiler->opts.IsOSR()) { - // OSR handles this by moving the values from the original frame. FOREACH_REGISTER_FILE(regState) { if (regState->rsCalleeRegArgMaskLiveIn) @@ -8591,7 +8593,7 @@ void CodeGen::genFnEpilog(BasicBlock* block) // Extra OSR adjust to get to where RBP was saved by the original frame, and // restore RBP. // - // Note the other callee saves made in that frame are dead, the current method + // Note the other callee saves made in that frame are dead, the OSR method // will save and restore what it needs. if (compiler->opts.IsOSR()) { diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 8863d187ebbe80..4a4c5a83ce24f1 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -4936,74 +4936,7 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags #endif // Generate PatchpointInfo - if (doesMethodHavePatchpoints()) - { - // Currently we always transition from Tier0 unoptimized code, and - // so we expect the method to have a frame pointer. - assert(codeGen->isFramePointerUsed()); - - // Allocate patchpoint info storage from runtime, and fill in initial bits of data. - const unsigned patchpointInfoSize = PatchpointInfo::ComputeSize(info.compLocalsCount); - PatchpointInfo* const patchpointInfo = (PatchpointInfo*)info.compCompHnd->allocateArray(patchpointInfoSize); - patchpointInfo->Initialize(info.compLocalsCount, info.compILCodeSize, - codeGen->genSPtoFPdelta() + TARGET_POINTER_SIZE); - - JITDUMP("--OSR--- FP-SP delta is %d\n", patchpointInfo->FpToSpDelta()); - - // We record offsets for all the "locals" here. Could restrict - // this to just the IL locals with some extra logic, and save a bit of space, - // but would need to adjust all consumers, too. - for (unsigned lclNum = 0; lclNum < info.compLocalsCount; lclNum++) - { - LclVarDsc* const varDsc = lvaGetDesc(lclNum); - - // We expect all these to have stack homes, and be FP relative - assert(varDsc->lvOnFrame); - assert(varDsc->lvFramePointerBased); - - // Record FramePtr relative offset (no localloc yet) - patchpointInfo->SetOffset(lclNum, varDsc->lvStkOffs); - - // Note if IL stream contained an address-of that potentially leads to exposure. - // This bit of IL may be skipped by OSR partial importation. - if (varDsc->lvHasLdAddrOp) - { - patchpointInfo->SetIsExposed(lclNum); - } - - JITDUMP("--OSR-- V%02u is at offset %d%s\n", lclNum, patchpointInfo->Offset(lclNum), - patchpointInfo->IsExposed(lclNum) ? " (exposed)" : ""); - } - - // Special offsets - - if (lvaReportParamTypeArg() || lvaKeepAliveAndReportThis()) - { - const int offset = lvaToCallerSPRelativeOffset(lvaCachedGenericContextArgOffset(), true); - patchpointInfo->SetGenericContextArgOffset(offset); - JITDUMP("--OSR-- cached generic context offset is CallerSP %d\n", - patchpointInfo->GenericContextArgOffset()); - } - - if (lvaKeepAliveAndReportThis()) - { - const int offset = lvaCachedGenericContextArgOffset(); - patchpointInfo->SetKeptAliveThisOffset(offset); - JITDUMP("--OSR-- kept-alive this offset is FP %d\n", patchpointInfo->KeptAliveThisOffset()); - } - - if (compGSReorderStackLayout) - { - assert(lvaGSSecurityCookie != BAD_VAR_NUM); - LclVarDsc* const varDsc = lvaGetDesc(lvaGSSecurityCookie); - patchpointInfo->SetSecurityCookieOffset(varDsc->lvStkOffs); - JITDUMP("--OSR-- security cookie V%02u offset is FP %d\n", lvaGSSecurityCookie, - patchpointInfo->SecurityCookieOffset()); - } - - // Register this with the runtime. - info.compCompHnd->setPatchpointInfo(patchpointInfo); - } + generatePatchpointInfo(); RecordStateAtEndOfCompilation(); @@ -5032,6 +4965,87 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags #endif // FUNC_INFO_LOGGING } +//------------------------------------------------------------------------ +// generatePatchpointInfo: allocate and fill in patchpoint info data, +// and report it to the VM +// +void Compiler::generatePatchpointInfo() +{ + if (!doesMethodHavePatchpoints()) + { + // Nothing to report + return; + } + + // Patchpoints are only found in Tier0 code, which is unoptimized, and so + // should always have frame pointer. + assert(codeGen->isFramePointerUsed()); + + // Allocate patchpoint info storage from runtime, and fill in initial bits of data. + const unsigned patchpointInfoSize = PatchpointInfo::ComputeSize(info.compLocalsCount); + PatchpointInfo* const patchpointInfo = (PatchpointInfo*)info.compCompHnd->allocateArray(patchpointInfoSize); + + // The +TARGET_POINTER_SIZE here is to account for the extra slot the runtime + // creates when it simulates calling the OSR method (the "pseudo return address" slot). + patchpointInfo->Initialize(info.compLocalsCount, info.compILCodeSize, + codeGen->genSPtoFPdelta() + TARGET_POINTER_SIZE); + + JITDUMP("--OSR--- FP-SP delta is %d\n", patchpointInfo->FpToSpDelta()); + + // We record offsets for all the "locals" here. Could restrict + // this to just the IL locals with some extra logic, and save a bit of space, + // but would need to adjust all consumers, too. + for (unsigned lclNum = 0; lclNum < info.compLocalsCount; lclNum++) + { + LclVarDsc* const varDsc = lvaGetDesc(lclNum); + + // We expect all these to have stack homes, and be FP relative + assert(varDsc->lvOnFrame); + assert(varDsc->lvFramePointerBased); + + // Record FramePtr relative offset (no localloc yet) + patchpointInfo->SetOffset(lclNum, varDsc->lvStkOffs); + + // Note if IL stream contained an address-of that potentially leads to exposure. + // This bit of IL may be skipped by OSR partial importation. + if (varDsc->lvHasLdAddrOp) + { + patchpointInfo->SetIsExposed(lclNum); + } + + JITDUMP("--OSR-- V%02u is at offset %d%s\n", lclNum, patchpointInfo->Offset(lclNum), + patchpointInfo->IsExposed(lclNum) ? " (exposed)" : ""); + } + + // Special offsets + + if (lvaReportParamTypeArg() || lvaKeepAliveAndReportThis()) + { + const int offset = lvaToCallerSPRelativeOffset(lvaCachedGenericContextArgOffset(), true); + patchpointInfo->SetGenericContextArgOffset(offset); + JITDUMP("--OSR-- cached generic context offset is CallerSP %d\n", patchpointInfo->GenericContextArgOffset()); + } + + if (lvaKeepAliveAndReportThis()) + { + const int offset = lvaCachedGenericContextArgOffset(); + patchpointInfo->SetKeptAliveThisOffset(offset); + JITDUMP("--OSR-- kept-alive this offset is FP %d\n", patchpointInfo->KeptAliveThisOffset()); + } + + if (compGSReorderStackLayout) + { + assert(lvaGSSecurityCookie != BAD_VAR_NUM); + LclVarDsc* const varDsc = lvaGetDesc(lvaGSSecurityCookie); + patchpointInfo->SetSecurityCookieOffset(varDsc->lvStkOffs); + JITDUMP("--OSR-- security cookie V%02u offset is FP %d\n", lvaGSSecurityCookie, + patchpointInfo->SecurityCookieOffset()); + } + + // Register this with the runtime. + info.compCompHnd->setPatchpointInfo(patchpointInfo); +} + //------------------------------------------------------------------------ // ResetOptAnnotations: Clear annotations produced during global optimizations. // diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 479956122aa3a6..bb9f17df724d77 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -9135,6 +9135,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ArenaAllocator* compGetArenaAllocator(); + void generatePatchpointInfo(); + #if MEASURE_MEM_ALLOC static bool s_dspMemStats; // Display per-phase memory statistics for every function #endif // MEASURE_MEM_ALLOC diff --git a/src/coreclr/src/jit/gcencode.cpp b/src/coreclr/src/jit/gcencode.cpp index 9e26b2b611ea73..a346b9fd1ab258 100644 --- a/src/coreclr/src/jit/gcencode.cpp +++ b/src/coreclr/src/jit/gcencode.cpp @@ -3938,11 +3938,11 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz if (compiler->opts.IsOSR()) { // The offset computed above already includes the OSR frame adjustment, plus the - // pop of the "pseudo0RA" from the OSR frame. + // pop of the "pseudo return address" from the OSR frame. // // To get to caller-SP, we need to subtract off the original frame size and the // pushed RA and RBP for that frame. But ppInfo's FpToSpDelta also accounts for the - // pseudo-RA between the original method frame and the OSR frame. So the net adjustment + // pseudo RA between the original method frame and the OSR frame. So the net adjustment // is simply FpToSpDelta plus one register. PatchpointInfo* ppInfo = compiler->info.compPatchpointInfo; int adjustment = ppInfo->FpToSpDelta() + REGSIZE_BYTES; From d9dcc20a8b45ee216c17b4a9056b6cd9a8763bac Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 10 Mar 2020 10:21:23 -0700 Subject: [PATCH 17/31] reduce patchpoint info size --- src/coreclr/src/inc/patchpointinfo.h | 14 ++------------ src/coreclr/src/jit/codegencommon.cpp | 1 - src/coreclr/src/jit/compiler.cpp | 3 +-- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/coreclr/src/inc/patchpointinfo.h b/src/coreclr/src/inc/patchpointinfo.h index 35bb46cf1df280..135ad0135a5496 100644 --- a/src/coreclr/src/inc/patchpointinfo.h +++ b/src/coreclr/src/inc/patchpointinfo.h @@ -34,10 +34,8 @@ struct PatchpointInfo } // Initialize - void Initialize(unsigned localCount, unsigned ilSize, int fpToSpDelta) + void Initialize(unsigned localCount, int fpToSpDelta) { - m_patchpointInfoSize = ComputeSize(localCount); - m_ilSize = ilSize; m_fpToSpDelta = fpToSpDelta; m_numberOfLocals = localCount; m_genericContextArgOffset = -1; @@ -48,13 +46,7 @@ struct PatchpointInfo // Total size of this patchpoint info record, in bytes unsigned PatchpointInfoSize() const { - return m_patchpointInfoSize; - } - - // IL Size of the original method - unsigned ILSize() const - { - return m_ilSize; + return ComputeSize(m_numberOfLocals); } // FP to SP delta of the original method @@ -140,8 +132,6 @@ struct PatchpointInfo EXPOSURE_MASK = 0x1 }; - unsigned m_patchpointInfoSize; - unsigned m_ilSize; unsigned m_numberOfLocals; int m_fpToSpDelta; int m_genericContextArgOffset; diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index acb5ff0bbc6ee6..0c098e9b541a7f 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -6663,7 +6663,6 @@ void CodeGen::genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg, // basic sanity checks (make sure we're OSRing the right method) assert(patchpointInfo->NumberOfLocals() == compiler->info.compLocalsCount); - assert(patchpointInfo->ILSize() == compiler->info.compILCodeSize); const int originalFrameSize = patchpointInfo->FpToSpDelta(); const unsigned patchpointInfoLen = patchpointInfo->NumberOfLocals(); diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 4a4c5a83ce24f1..c1cb89b0862409 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -4987,8 +4987,7 @@ void Compiler::generatePatchpointInfo() // The +TARGET_POINTER_SIZE here is to account for the extra slot the runtime // creates when it simulates calling the OSR method (the "pseudo return address" slot). - patchpointInfo->Initialize(info.compLocalsCount, info.compILCodeSize, - codeGen->genSPtoFPdelta() + TARGET_POINTER_SIZE); + patchpointInfo->Initialize(info.compLocalsCount, codeGen->genSPtoFPdelta() + TARGET_POINTER_SIZE); JITDUMP("--OSR--- FP-SP delta is %d\n", patchpointInfo->FpToSpDelta()); From a96b1e70c8ec2d2cc9841e474e53de7948551946 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 10 Mar 2020 11:22:12 -0700 Subject: [PATCH 18/31] Revise daczation of OnStackReplacementManager Since GetPerPatchpointInfo is side effecting, it does not need to be present for dac compiles. This allows me to undo the edit to eehash.inl. --- src/coreclr/src/vm/eehash.inl | 2 +- src/coreclr/src/vm/onstackreplacement.cpp | 4 ++++ src/coreclr/src/vm/onstackreplacement.h | 7 ++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/coreclr/src/vm/eehash.inl b/src/coreclr/src/vm/eehash.inl index ff16a7a7aca03d..e49a80e022ebe6 100644 --- a/src/coreclr/src/vm/eehash.inl +++ b/src/coreclr/src/vm/eehash.inl @@ -633,7 +633,7 @@ FORCEINLINE EEHashEntry_t *EEHashTableBase: // from m_pVolatileBucketTable!!!!!!! A racing condition would occur. DWORD dwOldNumBuckets; - BucketTable* pBucketTable=(BucketTable*)(PTR_BucketTable)m_pVolatileBucketTable.Load(); + BucketTable* pBucketTable=m_pVolatileBucketTable; dwOldNumBuckets = pBucketTable->m_dwNumBuckets; _ASSERTE(pBucketTable->m_dwNumBuckets != 0); diff --git a/src/coreclr/src/vm/onstackreplacement.cpp b/src/coreclr/src/vm/onstackreplacement.cpp index 8e9747222b40e4..e4b3b00d55730c 100644 --- a/src/coreclr/src/vm/onstackreplacement.cpp +++ b/src/coreclr/src/vm/onstackreplacement.cpp @@ -14,6 +14,8 @@ int OnStackReplacementManager::s_patchpointId = 0; CrstStatic OnStackReplacementManager::s_lock; +#ifndef DACCESS_COMPILE + void OnStackReplacementManager::StaticInitialize() { WRAPPER_NO_CONTRACT; @@ -66,6 +68,8 @@ PerPatchpointInfo* OnStackReplacementManager::GetPerPatchpointInfo(PCODE ip) return ppInfo; } +#endif // !DACCESS_COMPILE + #endif // FEATURE_ON_STACK_REPLACEMENT diff --git a/src/coreclr/src/vm/onstackreplacement.h b/src/coreclr/src/vm/onstackreplacement.h index f81f611dd6f83f..40e1bf5e2812da 100644 --- a/src/coreclr/src/vm/onstackreplacement.h +++ b/src/coreclr/src/vm/onstackreplacement.h @@ -54,14 +54,19 @@ typedef EEPtrHashTable JitPatchpointTable; // class OnStackReplacementManager { +#if DACCESS_COMPILE +public: + OnStackReplacementManager() {}; +#else public: static void StaticInitialize(); public: OnStackReplacementManager(); - + public: PerPatchpointInfo* GetPerPatchpointInfo(PCODE ip); +#endif // DACCESS_COMPILE private: From a1fef8659cc2398e7d77e691568321f02b5900e2 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 11 Mar 2020 13:09:28 -0700 Subject: [PATCH 19/31] rework allocation of runtime data Lazily allocate the OnStackReplacementManager; keep the per patchpoint data on the low frequency heap. Make the patchpointID debug only. --- src/coreclr/src/inc/CrstTypes.def | 1 + src/coreclr/src/inc/crsttypes.h | 2 +- src/coreclr/src/vm/jithelpers.cpp | 16 ++++++----- src/coreclr/src/vm/loaderallocator.cpp | 35 ++++++++++++++++++++--- src/coreclr/src/vm/loaderallocator.hpp | 6 +--- src/coreclr/src/vm/onstackreplacement.cpp | 9 +++--- src/coreclr/src/vm/onstackreplacement.h | 19 ++++++++---- 7 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/coreclr/src/inc/CrstTypes.def b/src/coreclr/src/inc/CrstTypes.def index 640bbcd4b16e35..06f900a79be900 100644 --- a/src/coreclr/src/inc/CrstTypes.def +++ b/src/coreclr/src/inc/CrstTypes.def @@ -352,6 +352,7 @@ Crst JitGenericHandleCache End Crst JitPatchpoint + AcquiredBefore LoaderHeap End Crst JitPerf diff --git a/src/coreclr/src/inc/crsttypes.h b/src/coreclr/src/inc/crsttypes.h index 41966e970a1c18..98d24c2efafcae 100644 --- a/src/coreclr/src/inc/crsttypes.h +++ b/src/coreclr/src/inc/crsttypes.h @@ -255,7 +255,7 @@ int g_rgCrstLevelMap[] = 8, // CrstJit 0, // CrstJitGenericHandleCache 16, // CrstJitInlineTrackingMap - 0, // CrstJitPatchpoint + 3, // CrstJitPatchpoint -1, // CrstJitPerf 6, // CrstJumpStubCache 0, // CrstLeafLock diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp index d029be1a7af7d7..45928ac10b74c0 100644 --- a/src/coreclr/src/vm/jithelpers.cpp +++ b/src/coreclr/src/vm/jithelpers.cpp @@ -5099,9 +5099,6 @@ void JIT_Patchpoint(int* counter, int ilOffset) OnStackReplacementManager* manager = allocator->GetOnStackReplacementManager(); PerPatchpointInfo * ppInfo = manager->GetPerPatchpointInfo(ip); - const int ppId = ppInfo->m_patchpointId; - const int counterBump = g_pConfig->OSR_CounterBump(); - // In the current prototype, counter is shared by all patchpoints // in a method, so no matter what happens below, we don't want to // impair those other patchpoints. @@ -5115,8 +5112,13 @@ void JIT_Patchpoint(int* counter, int ilOffset) // // In the prototype, counter is a location in a stack frame, // so we can update it without worrying about other threads. + const int counterBump = g_pConfig->OSR_CounterBump(); *counter = counterBump; +#if _DEBUG + const int ppId = ppInfo->m_patchpointId; +#endif + // Is this a patchpoint that was previously marked as invalid? If so, just return to the Tier0 method. if ((ppInfo->m_flags & PerPatchpointInfo::patchpoint_invalid) == PerPatchpointInfo::patchpoint_invalid) { @@ -5234,8 +5236,8 @@ void JIT_Patchpoint(int* counter, int ilOffset) if (osrMethodCode == NULL) { // Unexpected, but not fatal - STRESS_LOG5(LF_TIEREDCOMPILATION, LL_WARNING, "Jit_Patchpoint: patchpoint [%d] (0x%p) OSR method creation failed," - " marking patchpoint invalid for Method=0x%pM il offset %d\n", ppId, ip, hitCount, pMD, ilOffset); + STRESS_LOG4(LF_TIEREDCOMPILATION, LL_WARNING, "Jit_Patchpoint: patchpoint (0x%p) OSR method creation failed," + " marking patchpoint invalid for Method=0x%pM il offset %d\n", ip, hitCount, pMD, ilOffset); InterlockedOr(&ppInfo->m_flags, (LONG)PerPatchpointInfo::patchpoint_invalid); return; @@ -5275,8 +5277,8 @@ void JIT_Patchpoint(int* counter, int ilOffset) if ((UINT_PTR)ip != GetIP(&frameContext)) { // Should be fatal - STRESS_LOG3(LF_TIEREDCOMPILATION, LL_INFO10, "Jit_Patchpoint: patchpoint [%d] (0x%p) TRANSITION" - " unexpected context IP 0x%p\n", ppId, ip, GetIP(&frameContext)); + STRESS_LOG2(LF_TIEREDCOMPILATION, LL_INFO10, "Jit_Patchpoint: patchpoint (0x%p) TRANSITION" + " unexpected context IP 0x%p\n", ip, GetIP(&frameContext)); } // Now unwind back to the original method caller frame. diff --git a/src/coreclr/src/vm/loaderallocator.cpp b/src/coreclr/src/vm/loaderallocator.cpp index 0544c53b95ad6b..406f45a89e3290 100644 --- a/src/coreclr/src/vm/loaderallocator.cpp +++ b/src/coreclr/src/vm/loaderallocator.cpp @@ -1226,10 +1226,6 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) m_callCountingManager = new CallCountingManager(); } #endif - -#ifdef FEATURE_ON_STACK_REPLACEMENT - m_onStackReplacementManager = new OnStackReplacementManager(); -#endif } @@ -2035,3 +2031,34 @@ BOOL LoaderAllocator::InsertComInteropData(MethodTable* pMT, InteropMethodTableD #endif // FEATURE_COMINTEROP #endif // !DACCESS_COMPILE + + +#ifdef FEATURE_ON_STACK_REPLACEMENT +#ifndef DACCESS_COMPILE +PTR_OnStackReplacementManager LoaderAllocator::GetOnStackReplacementManager() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + if (m_onStackReplacementManager == NULL) + { + OnStackReplacementManager * newManager = new OnStackReplacementManager(this); + + if (FastInterlockCompareExchangePointer(&m_onStackReplacementManager, newManager, NULL) != NULL) + { + // some thread swooped in and set the field + delete newManager; + } + } + _ASSERTE(m_onStackReplacementManager != NULL); + return m_onStackReplacementManager; +} +#endif // +#endif // FEATURE_ON_STACK_REPLACEMENT + diff --git a/src/coreclr/src/vm/loaderallocator.hpp b/src/coreclr/src/vm/loaderallocator.hpp index 83dc1a44c242f9..fcbd2a3cb5b258 100644 --- a/src/coreclr/src/vm/loaderallocator.hpp +++ b/src/coreclr/src/vm/loaderallocator.hpp @@ -617,11 +617,7 @@ class LoaderAllocator #ifdef FEATURE_ON_STACK_REPLACEMENT public: - PTR_OnStackReplacementManager GetOnStackReplacementManager() - { - LIMITED_METHOD_CONTRACT; - return m_onStackReplacementManager; - } + PTR_OnStackReplacementManager GetOnStackReplacementManager(); #endif // FEATURE_ON_STACK_REPLACEMENT }; // class LoaderAllocator diff --git a/src/coreclr/src/vm/onstackreplacement.cpp b/src/coreclr/src/vm/onstackreplacement.cpp index e4b3b00d55730c..606890998773bc 100644 --- a/src/coreclr/src/vm/onstackreplacement.cpp +++ b/src/coreclr/src/vm/onstackreplacement.cpp @@ -22,18 +22,18 @@ void OnStackReplacementManager::StaticInitialize() s_lock.Init(CrstJitPatchpoint, CrstFlags(CRST_UNSAFE_COOPGC)); } -OnStackReplacementManager::OnStackReplacementManager() : m_jitPatchpointTable() +OnStackReplacementManager::OnStackReplacementManager(LoaderAllocator * loaderAllocator) : m_jitPatchpointTable(), m_allocator(loaderAllocator) { CONTRACTL { GC_NOTRIGGER; CAN_TAKE_LOCK; - MODE_PREEMPTIVE; + MODE_ANY; } CONTRACTL_END; LockOwner lock = {&s_lock, IsOwnerOfCrst}; - m_jitPatchpointTable.Init(INITIAL_TABLE_SIZE, &lock); + m_jitPatchpointTable.Init(INITIAL_TABLE_SIZE, &lock, m_allocator->GetLowFrequencyHeap()); } // Fetch or create patchpoint info for this patchpoint. @@ -59,7 +59,8 @@ PerPatchpointInfo* OnStackReplacementManager::GetPerPatchpointInfo(PCODE ip) if (!hasData) { - ppInfo = dac_cast(new PerPatchpointInfo()); + void * pMem = m_allocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PerPatchpointInfo))); + ppInfo = dac_cast(new (pMem) PerPatchpointInfo()); m_jitPatchpointTable.InsertValue(ppId, (HashDatum)ppInfo); ppInfo->m_patchpointId = ++s_patchpointId; } diff --git a/src/coreclr/src/vm/onstackreplacement.h b/src/coreclr/src/vm/onstackreplacement.h index 40e1bf5e2812da..cbd510ab8a9793 100644 --- a/src/coreclr/src/vm/onstackreplacement.h +++ b/src/coreclr/src/vm/onstackreplacement.h @@ -24,8 +24,10 @@ struct PerPatchpointInfo PerPatchpointInfo() : m_osrMethodCode(0), m_patchpointCount(0), - m_flags(0), - m_patchpointId(0) + m_flags(0) +#if _DEBUG + , m_patchpointId(0) +#endif { } @@ -36,11 +38,17 @@ struct PerPatchpointInfo patchpoint_invalid = 0x2 }; + // The OSR method entry point for this patchpoint. + // NULL if no method has yet been jitted, or jitting failed. PCODE m_osrMethodCode; + // Number of times jitted code has called the helper at this patchpoint. LONG m_patchpointCount; + // Status of this patchpoint LONG m_flags; +#if _DEBUG int m_patchpointId; +#endif }; typedef DPTR(PerPatchpointInfo) PTR_PerPatchpointInfo; @@ -56,13 +64,13 @@ class OnStackReplacementManager { #if DACCESS_COMPILE public: - OnStackReplacementManager() {}; + OnStackReplacementManager(LoaderAllocator *) {}; #else public: static void StaticInitialize(); public: - OnStackReplacementManager(); + OnStackReplacementManager(LoaderAllocator * loaderHeaAllocator); public: PerPatchpointInfo* GetPerPatchpointInfo(PCODE ip); @@ -80,6 +88,7 @@ class OnStackReplacementManager private: + PTR_LoaderAllocator m_allocator; JitPatchpointTable m_jitPatchpointTable; }; @@ -91,7 +100,7 @@ class OnStackReplacementManager static void StaticInitialize() {} public: - OnStackReplacementManager() {} + OnStackReplacementManager(LoaderAllocator *) {} }; #endif // FEATURE_TIERED_COMPILATION From c042f52474275a721b1f005ba0b68ed9c1a8ced5 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 11 Mar 2020 13:37:43 -0700 Subject: [PATCH 20/31] add debug check for scratch bb --- src/coreclr/src/jit/flowgraph.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/src/jit/flowgraph.cpp b/src/coreclr/src/jit/flowgraph.cpp index c4769ad9168886..72ff80b4e34af5 100644 --- a/src/coreclr/src/jit/flowgraph.cpp +++ b/src/coreclr/src/jit/flowgraph.cpp @@ -498,7 +498,7 @@ void Compiler::fgEnsureFirstBBisScratch() } //------------------------------------------------------------------------ -// fgFirstBBisScratch: Check if fgFirstBB is a scratch block +// fgIsFirstBBScratch: Check if fgFirstBB is a scratch block // // Returns: // true if fgFirstBB is a scratch block. @@ -20911,6 +20911,7 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef #endif // DEBUG fgDebugCheckBlockLinks(); + fgFirstBBisScratch(); if (fgBBcount > 10000 && expensiveDebugCheckLevel < 1) { From 13b4616a6c7e8b558533311455b2cdbba59688a6 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 11 Mar 2020 14:05:55 -0700 Subject: [PATCH 21/31] release mode fixes; move more to debug --- src/coreclr/src/vm/onstackreplacement.cpp | 10 +++++++++- src/coreclr/src/vm/onstackreplacement.h | 5 ++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/coreclr/src/vm/onstackreplacement.cpp b/src/coreclr/src/vm/onstackreplacement.cpp index 606890998773bc..d0301f92142fe4 100644 --- a/src/coreclr/src/vm/onstackreplacement.cpp +++ b/src/coreclr/src/vm/onstackreplacement.cpp @@ -11,9 +11,13 @@ #ifdef FEATURE_ON_STACK_REPLACEMENT -int OnStackReplacementManager::s_patchpointId = 0; + CrstStatic OnStackReplacementManager::s_lock; +#if _DEBUG +int OnStackReplacementManager::s_patchpointId = 0; +#endif + #ifndef DACCESS_COMPILE void OnStackReplacementManager::StaticInitialize() @@ -62,7 +66,11 @@ PerPatchpointInfo* OnStackReplacementManager::GetPerPatchpointInfo(PCODE ip) void * pMem = m_allocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PerPatchpointInfo))); ppInfo = dac_cast(new (pMem) PerPatchpointInfo()); m_jitPatchpointTable.InsertValue(ppId, (HashDatum)ppInfo); + +#if _DEBUG ppInfo->m_patchpointId = ++s_patchpointId; +#endif + } } diff --git a/src/coreclr/src/vm/onstackreplacement.h b/src/coreclr/src/vm/onstackreplacement.h index cbd510ab8a9793..8ef3188cfc22a8 100644 --- a/src/coreclr/src/vm/onstackreplacement.h +++ b/src/coreclr/src/vm/onstackreplacement.h @@ -83,9 +83,12 @@ class OnStackReplacementManager INITIAL_TABLE_SIZE = 10 }; - static int s_patchpointId; static CrstStatic s_lock; +#if _DEBUG + static int s_patchpointId; +#endif + private: PTR_LoaderAllocator m_allocator; From aff5ebcf7d623a8c42b69b0792b69a6921859bf8 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 11 Mar 2020 16:03:58 -0700 Subject: [PATCH 22/31] fix initializer order --- src/coreclr/src/vm/onstackreplacement.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/vm/onstackreplacement.cpp b/src/coreclr/src/vm/onstackreplacement.cpp index d0301f92142fe4..4bb80efc5e94b2 100644 --- a/src/coreclr/src/vm/onstackreplacement.cpp +++ b/src/coreclr/src/vm/onstackreplacement.cpp @@ -26,7 +26,7 @@ void OnStackReplacementManager::StaticInitialize() s_lock.Init(CrstJitPatchpoint, CrstFlags(CRST_UNSAFE_COOPGC)); } -OnStackReplacementManager::OnStackReplacementManager(LoaderAllocator * loaderAllocator) : m_jitPatchpointTable(), m_allocator(loaderAllocator) +OnStackReplacementManager::OnStackReplacementManager(LoaderAllocator * loaderAllocator) : m_allocator(loaderAllocator), m_jitPatchpointTable() { CONTRACTL { From 926681643194cd4beb1c98ae7ca996da730a66ad Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 12 Mar 2020 15:41:43 -0700 Subject: [PATCH 23/31] fix test case --- src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.cs b/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.cs index 5552cf91179a1a..652475c5814371 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.cs +++ b/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.cs @@ -21,6 +21,6 @@ public static unsafe int Main() result += &a - &b; } - return (int)(result / 1000 * distance); + return (int)(result / (1000 * distance)); } } From eef7ad520fd9e22a4bd576bc48dcede9ca3ef760 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 12 Mar 2020 15:42:56 -0700 Subject: [PATCH 24/31] review feedback --- src/coreclr/src/jit/codegencommon.cpp | 77 +++++++++++++++++++-------- src/coreclr/src/jit/lclvars.cpp | 61 ++++++++++----------- 2 files changed, 82 insertions(+), 56 deletions(-) diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index 0c098e9b541a7f..c087fbf43bbc20 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -6705,23 +6705,64 @@ void CodeGen::genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg, const emitAttr size = emitTypeSize(lclTyp); const int stkOffs = patchpointInfo->Offset(lclNum) + fieldOffset; - // stkOffs is the original frame RBP-relative offset - // to the var. We want either an RSP or RBP relative - // offset for the current frame. + // Original frames always use frame pointers, so + // stkOffs is the original frame-relative offset + // to the variable. // - // If using RSP, we need to add the SP-to-FP delta of - // this frame and the SP-to-FP delta of the original - // frame... that translates from this frame's RSP to - // the old frame RBP. We then add the original frame's - // RBP relative offset. + // We need to determine the stack or frame-pointer relative + // offset for this variable in the current frame. // - // If using RBP, we need to add the SP-to-FP delta of - // the original frame and then add the original - // frame's RBP relative offset. + // If current frame does not use a frame pointer, we need to + // add the SP-to-FP delta of this frame and the SP-to-FP delta + // of the original frame; that translates from this frame's + // stack pointer the old frame frame pointer. + // + // We then add the original frame's frame-pointer relative + // offset (note this offset is usually negative -- the stack + // grows down, so locals are below the frame pointer). + // + // /-----original frame-----/ + // / return address / + // / saved RBP --+ / <--- Original frame ptr --+ + // / ... | / | + // / ... (stkOffs) / | + // / ... | / | + // / variable --+ / | + // / ... / (original frame sp-fp delta) + // / ... / | + // /-----OSR frame ---------/ | + // / pseudo return address / --+ + // / ... / | + // / ... / (this frame sp-fp delta) + // / ... / | + // /------------------------/ <--- Stack ptr --+ + // + // If the current frame is using a frame pointer, we need to + // add the SP-to-FP delta of/ the original frame and then add + // the original frame's frame-pointer relative offset. + // + // /-----original frame-----/ + // / return address / + // / saved RBP --+ / <--- Original frame ptr --+ + // / ... | / | + // / ... (stkOffs) / | + // / ... | / | + // / variable --+ / | + // / ... / (original frame sp-fp delta) + // / ... / | + // /-----OSR frame ---------/ | + // / pseudo return address / --+ + // / saved RBP / <--- Frame ptr --+ + // / ... / + // / ... / + // / ... / + // /------------------------/ + int offset = originalFrameSize + stkOffs; if (isFramePointerUsed()) { + // also adjust for saved RPB on this frame offset += TARGET_POINTER_SIZE; } else @@ -7612,18 +7653,8 @@ void CodeGen::genFnProlog() } } - // With OSR we may have untracked locals that were initialized by the original method. - if (hasUntrLcl) - { - if (compiler->opts.IsOSR()) - { - // todo: suitable assertion here - } - else - { - assert(genInitStkLclCnt > 0); - } - } + // TODO-Cleanup: Add suitable assert for the OSR case. + assert(compiler->opts.IsOSR() || ((genInitStkLclCnt > 0) == hasUntrLcl)); #ifdef DEBUG if (verbose) diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index cd7f96665f130f..3c6204113f81be 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -1849,6 +1849,7 @@ bool Compiler::StructPromotionHelper::CanPromoteStructVar(unsigned lclNum) return false; } + // TODO-CQ: enable promotion for OSR locals if (compiler->lvaIsOSRLocal(lclNum)) { JITDUMP(" struct promotion of V%02u is disabled because it is an OSR local\n", lclNum); @@ -5666,7 +5667,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() // If we are an OSR method, we "inherit" the frame of the original method, // and the stack is already double aligned on entry (since the return adddress push - // and any special alignment push happend "before"). + // and any special alignment push happened "before"). if (opts.IsOSR()) { originalFrameSize = info.compPatchpointInfo->FpToSpDelta(); @@ -6018,39 +6019,20 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() // will refer to their memory homes. if (lvaIsOSRLocal(lclNum)) { - // Todo: verify it's ok to handle independent fields that are live on entry - // to the OSR method and in memory at entry this way... - if (varDsc->lvIsStructField) - { - const unsigned parentLclNum = varDsc->lvParentLcl; - assert(parentLclNum < info.compLocalsCount); - - LclVarDsc* parentVarDsc = &lvaTable[parentLclNum]; - int originalParentOffset = info.compPatchpointInfo->Offset(parentLclNum); - int parentOffset = originalFrameStkOffs + originalParentOffset; - int fieldOffset = parentOffset + varDsc->lvFldOffset; + // TODO-CQ: enable struct promotion for OSR locals; when that + // happens, figure out how to properly refer to the original + // frame slots for the promoted fields. + assert(!varDsc->lvIsStructField); - JITDUMP("---OSR--- V%02u (on old frame) (promoted field of V%02u) new virt offset %d\n", lclNum, - parentLclNum, fieldOffset); + // Add frampointer-relative offset of this OSR live local in the original frame + // to the offset of original frame in our new frame. + int originalOffset = info.compPatchpointInfo->Offset(lclNum); + int offset = originalFrameStkOffs + originalOffset; - lvaTable[lclNum].lvStkOffs = fieldOffset; - } - else - { - assert(lclNum < info.compLocalsCount); - - // Add frampointer-relative offset of this OSR live local in the original frame - // to the offset of original frame in our new frame. - int originalOffset = info.compPatchpointInfo->Offset(lclNum); - int offset = originalFrameStkOffs + originalOffset; - - JITDUMP("---OSR--- V%02u (on old frame) old rbp offset %d old frame offset %d new virt offset %d\n", - lclNum, originalOffset, originalFrameStkOffs, offset); - - lvaTable[lclNum].lvStkOffs = offset; - continue; - } + JITDUMP("---OSR--- V%02u (on old frame) old rbp offset %d old frame offset %d new virt offset %d\n", + lclNum, originalOffset, originalFrameStkOffs, offset); + lvaTable[lclNum].lvStkOffs = offset; continue; } @@ -7358,10 +7340,23 @@ int Compiler::lvaGetCallerSPRelativeOffset(unsigned varNum) int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased) const { - // Todo: this should not be called for OSR - // methods -- instead we shoud fetch the offsets from the ppInfo assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT); + // TODO-Cleanup + // + // This current should not be called for OSR as caller SP relative + // offsets computed below do not reflect the extra stack space + // taken up by the original method frame. + // + // We should make it work. + // + // Instead we record the needed offsets in the patchpoint info + // when doing the original method compile(see special offsets + // in generatePatchpointInfo) and consume those values in the OSR + // compile. If we fix this we may be able to reduce the size + // of the patchpoint info and have less special casing for these + // frame slots. + if (isFpBased) { offset += codeGen->genCallerSPtoFPdelta(); From bab99a223fa4b9eddef4375b51dc08825119a41c Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 12 Mar 2020 16:24:12 -0700 Subject: [PATCH 25/31] fix gcc build issue --- src/coreclr/src/inc/clrconfigvalues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/inc/clrconfigvalues.h b/src/coreclr/src/inc/clrconfigvalues.h index 681f304ff01273..175a376b18f575 100644 --- a/src/coreclr/src/inc/clrconfigvalues.h +++ b/src/coreclr/src/inc/clrconfigvalues.h @@ -650,7 +650,7 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_TC_DeleteCallCountingStubsAfter, W("TC_DeleteC #ifdef FEATURE_ON_STACK_REPLACEMENT RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_CounterBump, W("OSR_CounterBump"), 1000, "Counter reload value when a patchpoint is hit") RETAIL_CONFIG_DWORD_INFO(INTERNAL_OSR_HitLimit, W("OSR_HitLimit"), 10, "Number of times a patchpoint must call back to trigger an OSR transition") -CONFIG_DWORD_INFO(INTERNAL_OSR_LowId, W("OSR_LowId"), -1, "Low end of enabled patchpoint range (inclusive)"); +CONFIG_DWORD_INFO(INTERNAL_OSR_LowId, W("OSR_LowId"), (DWORD)-1, "Low end of enabled patchpoint range (inclusive)"); CONFIG_DWORD_INFO(INTERNAL_OSR_HighId, W("OSR_HighId"), 10000000, "High end of enabled patchpoint range (inclusive)"); #endif From 9a59878b074f2d870112ee64981a986abcdcc9e5 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 12 Mar 2020 18:50:51 -0700 Subject: [PATCH 26/31] keep two pointers on jit interface --- src/coreclr/src/vm/jitinterface.cpp | 9 ++++----- src/coreclr/src/vm/jitinterface.h | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/coreclr/src/vm/jitinterface.cpp b/src/coreclr/src/vm/jitinterface.cpp index 0d731e940c97e9..dc6459ab4c3b28 100644 --- a/src/coreclr/src/vm/jitinterface.cpp +++ b/src/coreclr/src/vm/jitinterface.cpp @@ -10946,9 +10946,8 @@ void CEEJitInfo::setPatchpointInfo(PatchpointInfo* patchpointInfo) #ifdef FEATURE_ON_STACK_REPLACEMENT // We receive ownership of the array - _ASSERTE(m_pPatchpointInfo == NULL); - m_fOwnsPatchpointInfo = true; - m_pPatchpointInfo = patchpointInfo; + _ASSERTE(m_pPatchpointInfoFromJit == NULL); + m_pPatchpointInfoFromJit = patchpointInfo; #else UNREACHABLE(); #endif @@ -10970,7 +10969,7 @@ PatchpointInfo* CEEJitInfo::getOSRInfo(unsigned* ilOffset) JIT_TO_EE_TRANSITION(); #ifdef FEATURE_ON_STACK_REPLACEMENT - result = m_pPatchpointInfo; + result = m_pPatchpointInfoFromRuntime; *ilOffset = m_ilOffset; #endif @@ -10988,7 +10987,7 @@ void CEEJitInfo::CompressDebugInfo() } CONTRACTL_END; #ifdef FEATURE_ON_STACK_REPLACEMENT - PatchpointInfo* patchpointInfo = m_pPatchpointInfo; + PatchpointInfo* patchpointInfo = m_pPatchpointInfoFromJit; #else PatchpointInfo* patchpointInfo = NULL; #endif diff --git a/src/coreclr/src/vm/jitinterface.h b/src/coreclr/src/vm/jitinterface.h index 9c2bd217bf616b..637f63ced18a45 100644 --- a/src/coreclr/src/vm/jitinterface.h +++ b/src/coreclr/src/vm/jitinterface.h @@ -1298,11 +1298,11 @@ class CEEJitInfo : public CEEInfo m_pNativeVarInfo = NULL; #ifdef FEATURE_ON_STACK_REPLACEMENT - if (m_fOwnsPatchpointInfo && (m_pPatchpointInfo != NULL)) - delete [] ((BYTE*) m_pPatchpointInfo); + if (m_pPatchpointInfoFromJit != NULL) + delete [] ((BYTE*) m_pPatchpointInfoFromJit); - m_pPatchpointInfo = NULL; - m_fOwnsPatchpointInfo = false; + m_pPatchpointInfoFromJit = NULL; + m_pPatchpointInfoFromRuntime = NULL; m_ilOffset = 0; #endif @@ -1369,13 +1369,13 @@ class CEEJitInfo : public CEEInfo #endif #ifdef FEATURE_ON_STACK_REPLACEMENT + // Called by the runtime to supply patchpoint information to the jit. void SetOSRInfo(PatchpointInfo* patchpointInfo, unsigned ilOffset) { - _ASSERTE(m_pPatchpointInfo == NULL); + _ASSERTE(m_pPatchpointInfoFromRuntime == NULL); _ASSERTE(patchpointInfo != NULL); - m_pPatchpointInfo = patchpointInfo; + m_pPatchpointInfoFromRuntime = patchpointInfo; m_ilOffset = ilOffset; - m_fOwnsPatchpointInfo = false; } #endif @@ -1407,8 +1407,8 @@ class CEEJitInfo : public CEEInfo m_iNativeVarInfo(0), m_pNativeVarInfo(NULL), #ifdef FEATURE_ON_STACK_REPLACEMENT - m_fOwnsPatchpointInfo(false), - m_pPatchpointInfo(NULL), + m_pPatchpointInfoFromJit(NULL), + m_pPatchpointInfoFromRuntime(NULL), m_ilOffset(0), #endif m_gphCache() @@ -1439,8 +1439,8 @@ class CEEJitInfo : public CEEInfo delete [] ((BYTE*) m_pNativeVarInfo); #ifdef FEATURE_ON_STACK_REPLACEMENT - if (m_fOwnsPatchpointInfo && (m_pPatchpointInfo != NULL)) - delete [] ((BYTE*) m_pPatchpointInfo); + if (m_pPatchpointInfoFromJit != NULL) + delete [] ((BYTE*) m_pPatchpointInfoFromJit); #endif } @@ -1517,8 +1517,8 @@ protected : ICorDebugInfo::NativeVarInfo * m_pNativeVarInfo; #ifdef FEATURE_ON_STACK_REPLACEMENT - bool m_fOwnsPatchpointInfo; - PatchpointInfo * m_pPatchpointInfo; + PatchpointInfo * m_pPatchpointInfoFromJit; + PatchpointInfo * m_pPatchpointInfoFromRuntime; unsigned m_ilOffset; #endif From a63bca13a1cb52bdcc047883f28adebdf524f931 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 13 Mar 2020 18:48:05 -0700 Subject: [PATCH 27/31] review feedback --- src/coreclr/src/jit/flowgraph.cpp | 5 ++--- src/coreclr/src/jit/importer.cpp | 2 +- src/coreclr/src/jit/jitconfigvalues.h | 6 +++--- src/coreclr/src/jit/lclvars.cpp | 4 ++-- src/coreclr/src/jit/patchpoint.cpp | 2 +- src/coreclr/src/vm/jithelpers.cpp | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/coreclr/src/jit/flowgraph.cpp b/src/coreclr/src/jit/flowgraph.cpp index 72ff80b4e34af5..8ba6d2f8cc7e0c 100644 --- a/src/coreclr/src/jit/flowgraph.cpp +++ b/src/coreclr/src/jit/flowgraph.cpp @@ -498,7 +498,7 @@ void Compiler::fgEnsureFirstBBisScratch() } //------------------------------------------------------------------------ -// fgIsFirstBBScratch: Check if fgFirstBB is a scratch block +// fgFirstBBisScratch: Check if fgFirstBB is a scratch block // // Returns: // true if fgFirstBB is a scratch block. @@ -5990,8 +5990,7 @@ void Compiler::fgFindBasicBlocks() } // If we are doing OSR, add an entry block that simply branches to the right IL offset. - // Might need to do something special if we're entering try regions? - if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_OSR)) + if (opts.IsOSR()) { // Remember the original entry block in case this method is tail recursive. fgEntryBB = fgLookupBB(0); diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index d6a131ce9b678b..80557fa3ac8c05 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -10535,7 +10535,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (compHasBackwardJump) { // Are patchpoints enabled? - if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && (JitConfig.JitPatchpoint() > 0)) + if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && (JitConfig.TC_OnStackReplacement() > 0)) { // We don't inline at Tier0, if we do, we may need rethink our approach. // Could probably support inlines that don't introduce flow. diff --git a/src/coreclr/src/jit/jitconfigvalues.h b/src/coreclr/src/jit/jitconfigvalues.h index b11c54e90c27b0..65b92b55ba1338 100644 --- a/src/coreclr/src/jit/jitconfigvalues.h +++ b/src/coreclr/src/jit/jitconfigvalues.h @@ -395,9 +395,9 @@ CONFIG_INTEGER(JitGuardedDevirtualizationGuessBestClass, W("JitGuardedDevirtuali #endif // DEBUG // Enable insertion of patchpoints into Tier0 methods with loops. -CONFIG_INTEGER(JitPatchpoint, W("JitPatchpoint"), 0) -// Initial patchpoint counter value -CONFIG_INTEGER(JitPatchpointInitialCounter, W("JitPatchpointInitialCounter"), 1000) +CONFIG_INTEGER(TC_OnStackReplacement, W("TC_OnStackReplacement"), 0) +// Initial patchpoint counter value used by jitted code +CONFIG_INTEGER(TC_OnStackReplacement_InitialCounter, W("TC_OnStackReplacement_InitialCounter"), 1000) #if defined(DEBUG) // JitFunctionFile: Name of a file that contains a list of functions. If the currently compiled function is in the diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 3c6204113f81be..76e17f08a09841 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -282,7 +282,7 @@ void Compiler::lvaInitTypeRef() if (opts.IsOSR() && info.compPatchpointInfo->IsExposed(varNum)) { - JITDUMP("-- V%02u is osr exposed\n", varNum); + JITDUMP("-- V%02u is OSR exposed\n", varNum); varDsc->lvHasLdAddrOp = 1; if (varDsc->lvType != TYP_STRUCT) @@ -1042,7 +1042,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo) if (opts.IsOSR() && info.compPatchpointInfo->IsExposed(varDscInfo->varNum)) { - JITDUMP("-- V%02u is osr exposed\n", varDscInfo->varNum); + JITDUMP("-- V%02u is OSR exposed\n", varDscInfo->varNum); varDsc->lvHasLdAddrOp = 1; lvaSetVarAddrExposed(varDscInfo->varNum); } diff --git a/src/coreclr/src/jit/patchpoint.cpp b/src/coreclr/src/jit/patchpoint.cpp index 681bb774ae0269..e9aca3ba497b6d 100644 --- a/src/coreclr/src/jit/patchpoint.cpp +++ b/src/coreclr/src/jit/patchpoint.cpp @@ -172,7 +172,7 @@ class PatchpointTransformer { assert((block->bbFlags & BBF_PATCHPOINT) == 0); - int initialCounterValue = JitConfig.JitPatchpointInitialCounter(); + int initialCounterValue = JitConfig.TC_OnStackReplacement_InitialCounter(); if (initialCounterValue < 0) { diff --git a/src/coreclr/src/vm/jithelpers.cpp b/src/coreclr/src/vm/jithelpers.cpp index 45928ac10b74c0..fee81e181b8e75 100644 --- a/src/coreclr/src/vm/jithelpers.cpp +++ b/src/coreclr/src/vm/jithelpers.cpp @@ -5232,7 +5232,7 @@ void JIT_Patchpoint(int* counter, int ilOffset) // Invoke the helper to build the OSR method osrMethodCode = HCCALL3(JIT_Patchpoint_Framed, pMD, codeInfo, ilOffset); - // If that failed, mark the patchpoint as invaild. + // If that failed, mark the patchpoint as invalid. if (osrMethodCode == NULL) { // Unexpected, but not fatal From 04cedab01cc9ffde6bfe017ededc080c0b025630 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 13 Mar 2020 19:02:49 -0700 Subject: [PATCH 28/31] no patchpoints if feature not defined --- src/coreclr/src/jit/importer.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 80557fa3ac8c05..04079ba490910a 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -10531,7 +10531,9 @@ void Compiler::impImportBlockCode(BasicBlock* block) impBeginTreeList(); - // Are there any plausible patchpoint locations in this method? +#ifdef FEATURE_ON_STACK_REPLACEMENT + + // Are there any places in the method where we might add a patchpoint? if (compHasBackwardJump) { // Are patchpoints enabled? @@ -10541,8 +10543,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) // Could probably support inlines that don't introduce flow. assert(!compIsForInlining()); - // Is this block a patchpoint? - // Current strategy is stack-empty backwards branch targets + // Is the start of this block a suitable patchpoint? + // Current strategy is blocks that are stack-empty and backwards branch targets if (block->bbFlags & BBF_BACKWARD_JUMP_TARGET && (verCurrentState.esStackDepth == 0)) { block->bbFlags |= BBF_PATCHPOINT; @@ -10556,6 +10558,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) assert((block->bbFlags & BBF_BACKWARD_JUMP_TARGET) == 0); } +#endif // FEATURE_ON_STACK_REPLACEMENT + /* Walk the opcodes that comprise the basic block */ const BYTE* codeAddr = info.compCode + block->bbCodeOffs; From f8ad74112b92a4ecd772a608efc015171c94525a Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Sat, 14 Mar 2020 10:19:08 -0700 Subject: [PATCH 29/31] update test projects to enable OSR --- .../src/JIT/opt/OSR/addressexposedlocal.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/doublestackalloc.csproj | 14 ++++++++++++++ src/coreclr/tests/src/JIT/opt/OSR/example.csproj | 14 ++++++++++++++ .../src/JIT/opt/OSR/genericmethodpatchpoint.csproj | 14 ++++++++++++++ src/coreclr/tests/src/JIT/opt/OSR/innerloop.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/integersumloop.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/livelocaladdress.csproj | 14 ++++++++++++++ .../src/JIT/opt/OSR/livelocalstackalloc.csproj | 14 ++++++++++++++ src/coreclr/tests/src/JIT/opt/OSR/mainloop.csproj | 14 ++++++++++++++ src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj | 13 +++++++++++++ .../tests/src/JIT/opt/OSR/mainlooptry.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/mainlooptry2.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/mainlooptry3.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/mainlooptry4.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/memoryargument.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/nesteddoloops.csproj | 14 ++++++++++++++ .../src/JIT/opt/OSR/originalstackalloc.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/osrcontainstry.csproj | 14 ++++++++++++++ src/coreclr/tests/src/JIT/opt/OSR/promoted.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/tailrecurse.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/tailrecursetry.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/tailrecursetry2.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/twoosrmethods.csproj | 14 ++++++++++++++ .../tests/src/JIT/opt/OSR/twoosrmethods1.csproj | 14 ++++++++++++++ 24 files changed, 335 insertions(+) diff --git a/src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.csproj b/src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.csproj index e8168a04617420..881e48ea778627 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/addressexposedlocal.csproj @@ -8,4 +8,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.csproj b/src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.csproj index e8168a04617420..881e48ea778627 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/doublestackalloc.csproj @@ -8,4 +8,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/example.csproj b/src/coreclr/tests/src/JIT/opt/OSR/example.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/example.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/example.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.csproj b/src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/genericmethodpatchpoint.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/innerloop.csproj b/src/coreclr/tests/src/JIT/opt/OSR/innerloop.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/innerloop.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/innerloop.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/integersumloop.csproj b/src/coreclr/tests/src/JIT/opt/OSR/integersumloop.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/integersumloop.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/integersumloop.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.csproj b/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.csproj index 995a53defbf6b4..53a9cc32ce5e5c 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/livelocaladdress.csproj @@ -8,4 +8,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.csproj b/src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.csproj index e8168a04617420..881e48ea778627 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/livelocalstackalloc.csproj @@ -8,4 +8,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainloop.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainloop.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/mainloop.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainloop.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj index 19781e26c20d81..d2be8a4ae77eca 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj @@ -7,4 +7,17 @@ + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry2.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry3.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainlooptry4.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/memoryargument.csproj b/src/coreclr/tests/src/JIT/opt/OSR/memoryargument.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/memoryargument.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/memoryargument.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.csproj b/src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/nesteddoloops.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.csproj b/src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.csproj index e8168a04617420..881e48ea778627 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/originalstackalloc.csproj @@ -8,4 +8,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.csproj b/src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.csproj index e8168a04617420..881e48ea778627 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/osrcontainstry.csproj @@ -8,4 +8,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/promoted.csproj b/src/coreclr/tests/src/JIT/opt/OSR/promoted.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/promoted.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/promoted.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.csproj b/src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/tailrecurse.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.csproj b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.csproj b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.csproj index e8168a04617420..881e48ea778627 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/tailrecursetry2.csproj @@ -8,4 +8,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.csproj b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods.csproj @@ -7,4 +7,18 @@ + + + + diff --git a/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.csproj b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.csproj index 19781e26c20d81..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/twoosrmethods1.csproj @@ -7,4 +7,18 @@ + + + + From 8a06e85430707681e09ef262516f3db7be602a41 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Sat, 14 Mar 2020 17:30:22 -0700 Subject: [PATCH 30/31] fix test project --- src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj b/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj index d2be8a4ae77eca..9620f75474a935 100644 --- a/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj +++ b/src/coreclr/tests/src/JIT/opt/OSR/mainloop2.csproj @@ -7,6 +7,7 @@ + Date: Mon, 16 Mar 2020 13:42:00 -0700 Subject: [PATCH 31/31] don't over-allocate debug store size --- src/coreclr/src/vm/debuginfostore.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/src/vm/debuginfostore.cpp b/src/coreclr/src/vm/debuginfostore.cpp index f66988a4da4b90..e3a18ff305056e 100644 --- a/src/coreclr/src/vm/debuginfostore.cpp +++ b/src/coreclr/src/vm/debuginfostore.cpp @@ -493,7 +493,12 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( DWORD cbHeader; PVOID pHeader = w.GetBlob(&cbHeader); +#ifdef FEATURE_ON_STACK_REPLACEMENT S_UINT32 cbFinalSize = S_UINT32(1) + S_UINT32(cbPatchpointInfo) + S_UINT32(cbHeader) + S_UINT32(cbBounds) + S_UINT32(cbVars); +#else + S_UINT32 cbFinalSize = S_UINT32(cbHeader) + S_UINT32(cbBounds) + S_UINT32(cbVars); +#endif + if (cbFinalSize.IsOverflow()) ThrowHR(COR_E_OVERFLOW);