2 * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "DFGOSRExitCompiler.h"
29 #if ENABLE(DFG_JIT) && USE(JSVALUE64)
31 #include "DFGOperations.h"
32 #include "DFGOSRExitCompilerCommon.h"
33 #include "Operations.h"
34 #include "VirtualRegister.h"
36 #include <wtf/DataLog.h>
38 namespace JSC { namespace DFG {
40 void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecovery>& operands, SpeculationRecovery* recovery)
42 // 1) Pro-forma stuff.
43 #if DFG_ENABLE(DEBUG_VERBOSE)
44 dataLogF("OSR exit for (");
45 for (CodeOrigin codeOrigin = exit.m_codeOrigin; ; codeOrigin = codeOrigin.inlineCallFrame->caller) {
46 dataLogF("bc#%u", codeOrigin.bytecodeIndex);
47 if (!codeOrigin.inlineCallFrame)
49 dataLogF(" -> %p ", codeOrigin.inlineCallFrame->executable.get());
55 if (Options::printEachOSRExit()) {
56 SpeculationFailureDebugInfo* debugInfo = new SpeculationFailureDebugInfo;
57 debugInfo->codeBlock = m_jit.codeBlock();
59 m_jit.debugCall(debugOperationPrintSpeculationFailure, debugInfo);
62 #if DFG_ENABLE(JIT_BREAK_ON_SPECULATION_FAILURE)
66 #if DFG_ENABLE(SUCCESS_STATS)
67 static SamplingCounter counter("SpeculationFailure");
68 m_jit.emitCount(counter);
71 // 2) Perform speculation recovery. This only comes into play when an operation
72 // starts mutating state before verifying the speculation it has already made.
75 switch (recovery->type()) {
77 m_jit.sub32(recovery->src(), recovery->dest());
78 m_jit.or64(GPRInfo::tagTypeNumberRegister, recovery->dest());
81 case BooleanSpeculationCheck:
82 m_jit.xor64(AssemblyHelpers::TrustedImm32(static_cast<int32_t>(ValueFalse)), recovery->dest());
90 // 3) Refine some array and/or value profile, if appropriate.
92 if (!!exit.m_jsValueSource) {
93 if (exit.m_kind == BadCache || exit.m_kind == BadIndexingType) {
94 // If the instruction that this originated from has an array profile, then
95 // refine it. If it doesn't, then do nothing. The latter could happen for
96 // hoisted checks, or checks emitted for operations that didn't have array
97 // profiling - either ops that aren't array accesses at all, or weren't
98 // known to be array acceses in the bytecode. The latter case is a FIXME
99 // while the former case is an outcome of a CheckStructure not knowing why
100 // it was emitted (could be either due to an inline cache of a property
101 // property access, or due to an array profile).
103 CodeOrigin codeOrigin = exit.m_codeOriginForExitProfile;
104 if (ArrayProfile* arrayProfile = m_jit.baselineCodeBlockFor(codeOrigin)->getArrayProfile(codeOrigin.bytecodeIndex)) {
106 if (exit.m_jsValueSource.isAddress())
107 usedRegister = exit.m_jsValueSource.base();
109 usedRegister = exit.m_jsValueSource.gpr();
113 scratch1 = AssemblyHelpers::selectScratchGPR(usedRegister);
114 scratch2 = AssemblyHelpers::selectScratchGPR(usedRegister, scratch1);
116 m_jit.push(scratch1);
117 m_jit.push(scratch2);
120 if (exit.m_jsValueSource.isAddress()) {
122 m_jit.loadPtr(AssemblyHelpers::Address(exit.m_jsValueSource.asAddress()), value);
124 value = exit.m_jsValueSource.gpr();
126 m_jit.loadPtr(AssemblyHelpers::Address(value, JSCell::structureOffset()), scratch1);
127 m_jit.storePtr(scratch1, arrayProfile->addressOfLastSeenStructure());
128 m_jit.load8(AssemblyHelpers::Address(scratch1, Structure::indexingTypeOffset()), scratch1);
129 m_jit.move(AssemblyHelpers::TrustedImm32(1), scratch2);
130 m_jit.lshift32(scratch1, scratch2);
131 m_jit.or32(scratch2, AssemblyHelpers::AbsoluteAddress(arrayProfile->addressOfArrayModes()));
138 if (!!exit.m_valueProfile) {
139 EncodedJSValue* bucket = exit.m_valueProfile.getSpecFailBucket(0);
141 if (exit.m_jsValueSource.isAddress()) {
142 // We can't be sure that we have a spare register. So use the tagTypeNumberRegister,
143 // since we know how to restore it.
144 m_jit.load64(AssemblyHelpers::Address(exit.m_jsValueSource.asAddress()), GPRInfo::tagTypeNumberRegister);
145 m_jit.store64(GPRInfo::tagTypeNumberRegister, bucket);
146 m_jit.move(AssemblyHelpers::TrustedImm64(TagTypeNumber), GPRInfo::tagTypeNumberRegister);
148 m_jit.store64(exit.m_jsValueSource.gpr(), bucket);
152 // What follows is an intentionally simple OSR exit implementation that generates
153 // fairly poor code but is very easy to hack. In particular, it dumps all state that
154 // needs conversion into a scratch buffer so that in step 6, where we actually do the
155 // conversions, we know that all temp registers are free to use and the variable is
156 // definitely in a well-known spot in the scratch buffer regardless of whether it had
157 // originally been in a register or spilled. This allows us to decouple "where was
158 // the variable" from "how was it represented". Consider that the
159 // AlreadyInJSStackAsUnboxedInt32 recovery: it tells us that the value is in a
160 // particular place and that that place holds an unboxed int32. We have three different
161 // places that a value could be (stack, displaced, register) and a bunch of different
162 // ways of representing a value. The number of recoveries is three * a bunch. The code
163 // below means that we have to have three + a bunch cases rather than three * a bunch.
164 // Once we have loaded the value from wherever it was, the reboxing is the same
165 // regardless of its location. Likewise, before we do the reboxing, the way we get to
166 // the value (i.e. where we load it from) is the same regardless of its type. Because
167 // the code below always dumps everything into a scratch buffer first, the two
168 // questions become orthogonal, which simplifies adding new types and adding new
171 // This raises the question: does using such a suboptimal implementation of OSR exit,
172 // where we always emit code to dump all state into a scratch buffer only to then
173 // dump it right back into the stack, hurt us in any way? The asnwer is that OSR exits
174 // are rare. Our tiering strategy ensures this. This is because if an OSR exit is
175 // taken more than ~100 times, we jettison the DFG code block along with all of its
176 // exits. It is impossible for an OSR exit - i.e. the code we compile below - to
177 // execute frequently enough for the codegen to matter that much. It probably matters
178 // enough that we don't want to turn this into some super-slow function call, but so
179 // long as we're generating straight-line code, that code can be pretty bad. Also
180 // because we tend to exit only along one OSR exit from any DFG code block - that's an
181 // empirical result that we're extremely confident about - the code size of this
182 // doesn't matter much. Hence any attempt to optimize the codegen here is just purely
183 // harmful to the system: it probably won't reduce either net memory usage or net
184 // execution time. It will only prevent us from cleanly decoupling "where was the
185 // variable" from "how was it represented", which will make it more difficult to add
186 // features in the future and it will make it harder to reason about bugs.
188 // 4) Save all state from GPRs into the scratch buffer.
190 ScratchBuffer* scratchBuffer = m_jit.vm()->scratchBufferForSize(sizeof(EncodedJSValue) * operands.size());
191 EncodedJSValue* scratch = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0;
193 for (size_t index = 0; index < operands.size(); ++index) {
194 const ValueRecovery& recovery = operands[index];
196 switch (recovery.technique()) {
198 case UnboxedInt32InGPR:
200 case UnboxedInt52InGPR:
201 case UnboxedStrictInt52InGPR:
202 m_jit.store64(recovery.gpr(), scratch + index);
210 // And voila, all FPRs are free to reuse.
212 // 5) Save all state from FPRs into the scratch buffer.
214 for (size_t index = 0; index < operands.size(); ++index) {
215 const ValueRecovery& recovery = operands[index];
217 switch (recovery.technique()) {
219 m_jit.move(AssemblyHelpers::TrustedImmPtr(scratch + index), GPRInfo::regT0);
220 m_jit.storeDouble(recovery.fpr(), GPRInfo::regT0);
228 // Now, all FPRs are also free.
230 // 5) Save all state from the stack into the scratch buffer. For simplicity we
231 // do this even for state that's already in the right place on the stack.
232 // It makes things simpler later.
234 for (size_t index = 0; index < operands.size(); ++index) {
235 const ValueRecovery& recovery = operands[index];
236 int operand = operands.operandForIndex(index);
238 switch (recovery.technique()) {
239 case DisplacedInJSStack:
240 case Int32DisplacedInJSStack:
241 case DoubleDisplacedInJSStack:
242 case Int52DisplacedInJSStack:
243 case StrictInt52DisplacedInJSStack:
244 m_jit.load64(AssemblyHelpers::addressFor(recovery.virtualRegister()), GPRInfo::regT0);
245 m_jit.store64(GPRInfo::regT0, scratch + index);
248 case AlreadyInJSStackAsUnboxedInt32:
249 case AlreadyInJSStackAsUnboxedDouble:
250 case AlreadyInJSStackAsUnboxedInt52:
251 m_jit.load64(AssemblyHelpers::addressFor(operand), GPRInfo::regT0);
252 m_jit.store64(GPRInfo::regT0, scratch + index);
260 // 6) Do all data format conversions and store the results into the stack.
262 bool haveArguments = false;
264 for (size_t index = 0; index < operands.size(); ++index) {
265 const ValueRecovery& recovery = operands[index];
266 int operand = operands.operandForIndex(index);
268 switch (recovery.technique()) {
270 case DisplacedInJSStack:
271 m_jit.load64(scratch + index, GPRInfo::regT0);
272 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
275 case AlreadyInJSStackAsUnboxedInt32:
276 case UnboxedInt32InGPR:
277 case Int32DisplacedInJSStack:
278 m_jit.load64(scratch + index, GPRInfo::regT0);
279 m_jit.zeroExtend32ToPtr(GPRInfo::regT0, GPRInfo::regT0);
280 m_jit.or64(GPRInfo::tagTypeNumberRegister, GPRInfo::regT0);
281 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
284 case AlreadyInJSStackAsUnboxedInt52:
285 case UnboxedInt52InGPR:
286 case Int52DisplacedInJSStack:
287 m_jit.load64(scratch + index, GPRInfo::regT0);
289 AssemblyHelpers::TrustedImm32(JSValue::int52ShiftAmount), GPRInfo::regT0);
290 m_jit.boxInt52(GPRInfo::regT0, GPRInfo::regT0, GPRInfo::regT1, FPRInfo::fpRegT0);
291 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
294 case UnboxedStrictInt52InGPR:
295 case StrictInt52DisplacedInJSStack:
296 m_jit.load64(scratch + index, GPRInfo::regT0);
297 m_jit.boxInt52(GPRInfo::regT0, GPRInfo::regT0, GPRInfo::regT1, FPRInfo::fpRegT0);
298 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
302 m_jit.load64(scratch + index, GPRInfo::regT0);
303 m_jit.zeroExtend32ToPtr(GPRInfo::regT0, GPRInfo::regT0);
304 m_jit.boxInt52(GPRInfo::regT0, GPRInfo::regT0, GPRInfo::regT1, FPRInfo::fpRegT0);
305 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
308 case AlreadyInJSStackAsUnboxedDouble:
310 case DoubleDisplacedInJSStack:
311 m_jit.move(AssemblyHelpers::TrustedImmPtr(scratch + index), GPRInfo::regT0);
312 m_jit.loadDouble(GPRInfo::regT0, FPRInfo::fpRegT0);
313 m_jit.boxDouble(FPRInfo::fpRegT0, GPRInfo::regT0);
314 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
319 AssemblyHelpers::TrustedImm64(JSValue::encode(recovery.constant())),
320 AssemblyHelpers::addressFor(operand));
323 case ArgumentsThatWereNotCreated:
324 haveArguments = true;
332 // 7) Adjust the old JIT's execute counter. Since we are exiting OSR, we know
333 // that all new calls into this code will go to the new JIT, so the execute
334 // counter only affects call frames that performed OSR exit and call frames
335 // that were still executing the old JIT at the time of another call frame's
336 // OSR exit. We want to ensure that the following is true:
338 // (a) Code the performs an OSR exit gets a chance to reenter optimized
339 // code eventually, since optimized code is faster. But we don't
340 // want to do such reentery too aggressively (see (c) below).
342 // (b) If there is code on the call stack that is still running the old
343 // JIT's code and has never OSR'd, then it should get a chance to
344 // perform OSR entry despite the fact that we've exited.
346 // (c) Code the performs an OSR exit should not immediately retry OSR
347 // entry, since both forms of OSR are expensive. OSR entry is
348 // particularly expensive.
350 // (d) Frequent OSR failures, even those that do not result in the code
351 // running in a hot loop, result in recompilation getting triggered.
353 // To ensure (c), we'd like to set the execute counter to
354 // counterValueForOptimizeAfterWarmUp(). This seems like it would endanger
355 // (a) and (b), since then every OSR exit would delay the opportunity for
356 // every call frame to perform OSR entry. Essentially, if OSR exit happens
357 // frequently and the function has few loops, then the counter will never
358 // become non-negative and OSR entry will never be triggered. OSR entry
359 // will only happen if a loop gets hot in the old JIT, which does a pretty
360 // good job of ensuring (a) and (b). But that doesn't take care of (d),
361 // since each speculation failure would reset the execute counter.
362 // So we check here if the number of speculation failures is significantly
363 // larger than the number of successes (we want 90% success rate), and if
364 // there have been a large enough number of failures. If so, we set the
365 // counter to 0; otherwise we set the counter to
366 // counterValueForOptimizeAfterWarmUp().
368 handleExitCounts(m_jit, exit);
370 // 8) Reify inlined call frames.
372 reifyInlinedCallFrames(m_jit, exit);
374 // 9) Create arguments if necessary and place them into the appropriate aliased
378 HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
379 NullableHashTraits<InlineCallFrame*> > didCreateArgumentsObject;
381 for (size_t index = 0; index < operands.size(); ++index) {
382 const ValueRecovery& recovery = operands[index];
383 if (recovery.technique() != ArgumentsThatWereNotCreated)
385 int operand = operands.operandForIndex(index);
386 // Find the right inline call frame.
387 InlineCallFrame* inlineCallFrame = 0;
388 for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame;
390 current = current->caller.inlineCallFrame) {
391 if (current->stackOffset <= operand) {
392 inlineCallFrame = current;
397 if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
399 VirtualRegister argumentsRegister = m_jit.argumentsRegisterFor(inlineCallFrame);
400 if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) {
401 // We know this call frame optimized out an arguments object that
402 // the baseline JIT would have created. Do that creation now.
403 if (inlineCallFrame) {
404 m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0);
405 m_jit.setupArguments(GPRInfo::regT0);
407 m_jit.setupArgumentsExecState();
409 AssemblyHelpers::TrustedImmPtr(
410 bitwise_cast<void*>(operationCreateArguments)),
411 GPRInfo::nonArgGPR0);
412 m_jit.call(GPRInfo::nonArgGPR0);
413 m_jit.store64(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister));
415 GPRInfo::returnValueGPR,
416 AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
417 m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
420 m_jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
421 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
425 // 10) Load the result of the last bytecode operation into regT0.
427 if (exit.m_lastSetOperand.isValid())
428 m_jit.load64(AssemblyHelpers::addressFor(exit.m_lastSetOperand), GPRInfo::cachedResultRegister);
432 adjustAndJumpToTarget(m_jit, exit);
435 } } // namespace JSC::DFG
437 #endif // ENABLE(DFG_JIT) && USE(JSVALUE64)