oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 1 | /* |
fpizlo@apple.com | bcfd8eb | 2015-02-26 19:51:52 +0000 | [diff] [blame] | 2 | * Copyright (C) 2013-2015 Apple Inc. All rights reserved. |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * 1. Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * 2. Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * |
| 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| 14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| 17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #ifndef FTLOSRExit_h |
| 27 | #define FTLOSRExit_h |
| 28 | |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 29 | #if ENABLE(FTL_JIT) |
| 30 | |
| 31 | #include "CodeOrigin.h" |
| 32 | #include "DFGExitProfile.h" |
| 33 | #include "DFGOSRExitBase.h" |
benjamin@webkit.org | 0badc6d | 2015-11-18 01:53:44 +0000 | [diff] [blame] | 34 | #include "FTLAbbreviatedTypes.h" |
fpizlo@apple.com | fc70ba6 | 2014-09-26 03:59:33 +0000 | [diff] [blame] | 35 | #include "FTLExitTimeObjectMaterialization.h" |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 36 | #include "FTLExitValue.h" |
| 37 | #include "FTLFormattedValue.h" |
sbarati@apple.com | 5bebda7 | 2015-11-10 07:48:54 +0000 | [diff] [blame] | 38 | #include "FTLStackMaps.h" |
sbarati@apple.com | c13003f | 2015-11-10 20:42:32 +0000 | [diff] [blame] | 39 | #include "FTLStackmapArgumentList.h" |
sbarati@apple.com | 5bebda7 | 2015-11-10 07:48:54 +0000 | [diff] [blame] | 40 | #include "HandlerInfo.h" |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 41 | #include "MethodOfGettingAValueProfile.h" |
| 42 | #include "Operands.h" |
sbarati@apple.com | 5bebda7 | 2015-11-10 07:48:54 +0000 | [diff] [blame] | 43 | #include "Reg.h" |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 44 | #include "ValueProfile.h" |
msaboff@apple.com | 62aa8b7 | 2013-09-26 22:53:54 +0000 | [diff] [blame] | 45 | #include "VirtualRegister.h" |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 46 | |
fpizlo@apple.com | 9339889 | 2015-07-10 21:19:51 +0000 | [diff] [blame] | 47 | namespace JSC { |
| 48 | |
| 49 | class TrackedReferences; |
| 50 | |
| 51 | namespace FTL { |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 52 | |
| 53 | // Tracks one OSR exit site within the FTL JIT. OSR exit in FTL works by deconstructing |
| 54 | // the crazy that is OSR down to simple SSA CFG primitives that any compiler backend |
fpizlo@apple.com | bcfd8eb | 2015-02-26 19:51:52 +0000 | [diff] [blame] | 55 | // (including of course LLVM) can grok and do meaningful things to. An exit is just a |
| 56 | // conditional branch in the emitted code where one destination is the continuation and |
| 57 | // the other is a basic block that performs a no-return tail-call to an exit thunk. |
| 58 | // This thunk takes as its arguments the live non-constant not-already-accounted-for |
| 59 | // bytecode state. To appreciate how this works consider the following JavaScript |
| 60 | // program, and its lowering down to LLVM IR including the relevant exits: |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 61 | // |
| 62 | // function foo(o) { |
| 63 | // var a = o.a; // predicted int |
| 64 | // var b = o.b; |
| 65 | // var c = o.c; // NB this is dead |
| 66 | // a = a | 5; // our example OSR exit: need to check if a is an int |
| 67 | // return a + b; |
| 68 | // } |
| 69 | // |
| 70 | // Just consider the "a | 5". In the DFG IR, this looks like: |
| 71 | // |
| 72 | // BitOr(Check:Int32:@a, Int32:5) |
| 73 | // |
fpizlo@apple.com | bcfd8eb | 2015-02-26 19:51:52 +0000 | [diff] [blame] | 74 | // Where @a is the node for the value of the 'a' variable. Conceptually, this node can |
| 75 | // be further broken down to the following (note that this particular lowering never |
| 76 | // actually happens - we skip this step and go straight to LLVM IR - but it's still |
| 77 | // useful to see this): |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 78 | // |
| 79 | // exitIf(@a is not int32); |
| 80 | // continuation; |
| 81 | // |
| 82 | // Where 'exitIf()' is a function that will exit if the argument is true, and |
| 83 | // 'continuation' is the stuff that we will do after the exitIf() check. (Note that |
| 84 | // FTL refers to 'exitIf()' as 'speculate()', which is in line with DFG terminology.) |
| 85 | // This then gets broken down to the following LLVM IR, assuming that %0 is the LLVM |
| 86 | // value corresponding to variable 'a', and %1 is the LLVM value for variable 'b': |
| 87 | // |
| 88 | // %2 = ... // the predictate corresponding to '@a is not int32' |
| 89 | // br i1 %2, label %3, label %4 |
| 90 | // ; <label>:3 |
| 91 | // call void exitThunk1(%0, %1) // pass 'a' and 'b', since they're both live-in-bytecode |
| 92 | // unreachable |
| 93 | // ; <label>:4 |
| 94 | // ... // code for the continuation |
| 95 | // |
| 96 | // Where 'exitThunk1' is the IR to get the exit thunk for *this* OSR exit. Each OSR |
| 97 | // exit will appear to LLVM to have a distinct exit thunk. |
| 98 | // |
| 99 | // Note that this didn't have to pass '5', 'o', or 'c' to the exit thunk. 5 is a |
| 100 | // constant and the DFG already knows that, and can already tell the OSR exit machinery |
| 101 | // what that contant is and which bytecode variables (if any) it needs to be dropped |
| 102 | // into. This is conveyed to the exit statically, via the OSRExit data structure below. |
| 103 | // See the code for ExitValue for details. 'o' is an argument, and arguments are always |
| 104 | // "flushed" - if you never assign them then their values are still in the argument |
| 105 | // stack slots, and if you do assign them then we eagerly store them into those slots. |
| 106 | // 'c' is dead in bytecode, and the DFG knows this; we statically tell the exit thunk |
| 107 | // that it's dead and don't have to pass anything. The exit thunk will "initialize" its |
| 108 | // value to Undefined. |
| 109 | // |
| 110 | // This approach to OSR exit has a number of virtues: |
| 111 | // |
| 112 | // - It is an entirely unsurprising representation for a compiler that already groks |
| 113 | // CFG-like IRs for C-like languages. All existing analyses and transformations just |
| 114 | // work. |
| 115 | // |
| 116 | // - It lends itself naturally to modern approaches to code motion. For example, you |
| 117 | // could sink operations from above the exit to below it, if you just duplicate the |
| 118 | // operation into the OSR exit block. This is both legal and desirable. It works |
| 119 | // because the backend sees the OSR exit block as being no different than any other, |
| 120 | // and LLVM already supports sinking if it sees that a value is only partially used. |
| 121 | // Hence there exists a value that dominates the exit but is only used by the exit |
| 122 | // thunk and not by the continuation, sinking ought to kick in for that value. |
| 123 | // Hoisting operations from below it to above it is also possible, for similar |
| 124 | // reasons. |
| 125 | // |
| 126 | // - The no-return tail-call to the OSR exit thunk can be subjected to specialized |
| 127 | // code-size reduction optimizations, though this is optional. For example, instead |
| 128 | // of actually emitting a call along with all that goes with it (like placing the |
| 129 | // arguments into argument position), the backend could choose to simply inform us |
| 130 | // where it had placed the arguments and expect the callee (i.e. the exit thunk) to |
| 131 | // figure it out from there. It could also tell us what we need to do to pop stack, |
fpizlo@apple.com | bcfd8eb | 2015-02-26 19:51:52 +0000 | [diff] [blame] | 132 | // although again, it doesn't have to; it could just emit that code normally. We do |
| 133 | // all of these things through the patchpoint/stackmap LLVM intrinsics. |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 134 | // |
| 135 | // - It could be extended to allow the backend to do its own exit hoisting, by using |
| 136 | // intrinsics (or meta-data, or something) to inform the backend that it's safe to |
| 137 | // make the predicate passed to 'exitIf()' more truthy. |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 138 | |
sbarati@apple.com | b887f0d | 2015-12-01 00:55:32 +0000 | [diff] [blame^] | 139 | enum class ExceptionType : uint8_t { |
sbarati@apple.com | 621078b | 2015-11-13 21:36:18 +0000 | [diff] [blame] | 140 | None, |
| 141 | CCallException, |
| 142 | JSCall, |
| 143 | GetById, |
sbarati@apple.com | b887f0d | 2015-12-01 00:55:32 +0000 | [diff] [blame^] | 144 | GetByIdCallOperation, |
sbarati@apple.com | 621078b | 2015-11-13 21:36:18 +0000 | [diff] [blame] | 145 | PutById, |
sbarati@apple.com | b887f0d | 2015-12-01 00:55:32 +0000 | [diff] [blame^] | 146 | PutByIdCallOperation, |
sbarati@apple.com | 621078b | 2015-11-13 21:36:18 +0000 | [diff] [blame] | 147 | LazySlowPath, |
sbarati@apple.com | b887f0d | 2015-12-01 00:55:32 +0000 | [diff] [blame^] | 148 | SubGenerator, |
sbarati@apple.com | 621078b | 2015-11-13 21:36:18 +0000 | [diff] [blame] | 149 | }; |
| 150 | |
sbarati@apple.com | cda241d | 2015-10-19 20:29:45 +0000 | [diff] [blame] | 151 | struct OSRExitDescriptor { |
| 152 | OSRExitDescriptor( |
sbarati@apple.com | 621078b | 2015-11-13 21:36:18 +0000 | [diff] [blame] | 153 | ExitKind, ExceptionType, DataFormat profileDataFormat, MethodOfGettingAValueProfile, |
fpizlo@apple.com | 9a5ab80 | 2013-11-12 03:28:41 +0000 | [diff] [blame] | 154 | CodeOrigin, CodeOrigin originForProfile, |
oliver@apple.com | cf49616 | 2013-07-25 04:05:22 +0000 | [diff] [blame] | 155 | unsigned numberOfArguments, unsigned numberOfLocals); |
sbarati@apple.com | cda241d | 2015-10-19 20:29:45 +0000 | [diff] [blame] | 156 | |
sbarati@apple.com | 621078b | 2015-11-13 21:36:18 +0000 | [diff] [blame] | 157 | bool isExceptionHandler() const; |
| 158 | |
sbarati@apple.com | cda241d | 2015-10-19 20:29:45 +0000 | [diff] [blame] | 159 | ExitKind m_kind; |
sbarati@apple.com | 621078b | 2015-11-13 21:36:18 +0000 | [diff] [blame] | 160 | ExceptionType m_exceptionType; |
sbarati@apple.com | cda241d | 2015-10-19 20:29:45 +0000 | [diff] [blame] | 161 | CodeOrigin m_codeOrigin; |
| 162 | CodeOrigin m_codeOriginForExitProfile; |
sbarati@apple.com | 5bebda7 | 2015-11-10 07:48:54 +0000 | [diff] [blame] | 163 | CodeOrigin m_semanticCodeOriginForCallFrameHeader; |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 164 | |
| 165 | // The first argument to the exit call may be a value we wish to profile. |
| 166 | // If that's the case, the format will be not Invalid and we'll have a |
| 167 | // method of getting a value profile. Note that all of the ExitArgument's |
| 168 | // are already aware of this possible off-by-one, so there is no need to |
| 169 | // correct them. |
basile_clement@apple.com | 3ed5a3a | 2015-09-04 18:24:38 +0000 | [diff] [blame] | 170 | DataFormat m_profileDataFormat; |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 171 | MethodOfGettingAValueProfile m_valueProfile; |
| 172 | |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 173 | Operands<ExitValue> m_values; |
fpizlo@apple.com | fc70ba6 | 2014-09-26 03:59:33 +0000 | [diff] [blame] | 174 | Bag<ExitTimeObjectMaterialization> m_materializations; |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 175 | |
fpizlo@apple.com | ea92c20 | 2013-10-10 04:24:57 +0000 | [diff] [blame] | 176 | uint32_t m_stackmapID; |
sbarati@apple.com | 5bebda7 | 2015-11-10 07:48:54 +0000 | [diff] [blame] | 177 | HandlerInfo m_baselineExceptionHandler; |
| 178 | bool m_isInvalidationPoint : 1; |
fpizlo@apple.com | ea92c20 | 2013-10-10 04:24:57 +0000 | [diff] [blame] | 179 | |
sbarati@apple.com | cda241d | 2015-10-19 20:29:45 +0000 | [diff] [blame] | 180 | void validateReferences(const TrackedReferences&); |
| 181 | }; |
| 182 | |
| 183 | struct OSRExit : public DFG::OSRExitBase { |
| 184 | OSRExit(OSRExitDescriptor&, uint32_t stackmapRecordIndex); |
| 185 | |
| 186 | OSRExitDescriptor& m_descriptor; |
| 187 | MacroAssemblerCodeRef m_code; |
| 188 | // Offset within the exit stubs of the stub for this exit. |
| 189 | unsigned m_patchableCodeOffset; |
| 190 | // Offset within Stackmap::records |
| 191 | uint32_t m_stackmapRecordIndex; |
sbarati@apple.com | b887f0d | 2015-12-01 00:55:32 +0000 | [diff] [blame^] | 192 | ExceptionType m_exceptionType; |
sbarati@apple.com | cda241d | 2015-10-19 20:29:45 +0000 | [diff] [blame] | 193 | |
sbarati@apple.com | 5bebda7 | 2015-11-10 07:48:54 +0000 | [diff] [blame] | 194 | RegisterSet registersToPreserveForCallThatMightThrow; |
| 195 | |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 196 | CodeLocationJump codeLocationForRepatch(CodeBlock* ftlCodeBlock) const; |
benjamin@webkit.org | 6f63a8b | 2015-02-18 07:04:10 +0000 | [diff] [blame] | 197 | void considerAddingAsFrequentExitSite(CodeBlock* profiledCodeBlock) |
msaboff@apple.com | 9589433 | 2014-01-29 19:18:54 +0000 | [diff] [blame] | 198 | { |
benjamin@webkit.org | 6f63a8b | 2015-02-18 07:04:10 +0000 | [diff] [blame] | 199 | OSRExitBase::considerAddingAsFrequentExitSite(profiledCodeBlock, ExitFromFTL); |
msaboff@apple.com | 9589433 | 2014-01-29 19:18:54 +0000 | [diff] [blame] | 200 | } |
sbarati@apple.com | 5bebda7 | 2015-11-10 07:48:54 +0000 | [diff] [blame] | 201 | |
| 202 | void gatherRegistersToSpillForCallIfException(StackMaps&, StackMaps::Record&); |
| 203 | void spillRegistersToSpillSlot(CCallHelpers&, int32_t stackSpillSlot); |
| 204 | void recoverRegistersFromSpillSlot(CCallHelpers& jit, int32_t stackSpillSlot); |
sbarati@apple.com | b887f0d | 2015-12-01 00:55:32 +0000 | [diff] [blame^] | 205 | |
| 206 | bool willArriveAtOSRExitFromGenericUnwind() const; |
| 207 | bool willArriveAtExitFromIndirectExceptionCheck() const; |
| 208 | bool willArriveAtOSRExitFromCallOperation() const; |
| 209 | bool needsRegisterRecoveryOnGenericUnwindOSRExitPath() const; |
oliver@apple.com | ea77149 | 2013-07-25 03:58:38 +0000 | [diff] [blame] | 210 | }; |
| 211 | |
| 212 | } } // namespace JSC::FTL |
| 213 | |
| 214 | #endif // ENABLE(FTL_JIT) |
| 215 | |
| 216 | #endif // FTLOSRExit_h |
| 217 | |