74d2b98d5570eb6a67f6e0b8051f5600f524c44e
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGOSRExitCompiler64.cpp
1 /*
2  * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
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 #include "config.h"
27 #include "DFGOSRExitCompiler.h"
28
29 #if ENABLE(DFG_JIT) && USE(JSVALUE64)
30
31 #include "DFGOperations.h"
32 #include "DFGOSRExitCompilerCommon.h"
33 #include "Operations.h"
34 #include "VirtualRegister.h"
35
36 #include <wtf/DataLog.h>
37
38 namespace JSC { namespace DFG {
39
40 void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecovery>& operands, SpeculationRecovery* recovery)
41 {
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)
48             break;
49         dataLogF(" -> %p ", codeOrigin.inlineCallFrame->executable.get());
50     }
51     dataLogF(")  ");
52     dataLog(operands);
53 #endif
54
55     if (Options::printEachOSRExit()) {
56         SpeculationFailureDebugInfo* debugInfo = new SpeculationFailureDebugInfo;
57         debugInfo->codeBlock = m_jit.codeBlock();
58         
59         m_jit.debugCall(debugOperationPrintSpeculationFailure, debugInfo);
60     }
61     
62 #if DFG_ENABLE(JIT_BREAK_ON_SPECULATION_FAILURE)
63     m_jit.breakpoint();
64 #endif
65     
66 #if DFG_ENABLE(SUCCESS_STATS)
67     static SamplingCounter counter("SpeculationFailure");
68     m_jit.emitCount(counter);
69 #endif
70     
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.
73     
74     if (recovery) {
75         switch (recovery->type()) {
76         case SpeculativeAdd:
77             m_jit.sub32(recovery->src(), recovery->dest());
78             m_jit.or64(GPRInfo::tagTypeNumberRegister, recovery->dest());
79             break;
80             
81         case BooleanSpeculationCheck:
82             m_jit.xor64(AssemblyHelpers::TrustedImm32(static_cast<int32_t>(ValueFalse)), recovery->dest());
83             break;
84             
85         default:
86             break;
87         }
88     }
89
90     // 3) Refine some array and/or value profile, if appropriate.
91     
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).
102             
103             CodeOrigin codeOrigin = exit.m_codeOriginForExitProfile;
104             if (ArrayProfile* arrayProfile = m_jit.baselineCodeBlockFor(codeOrigin)->getArrayProfile(codeOrigin.bytecodeIndex)) {
105                 GPRReg usedRegister;
106                 if (exit.m_jsValueSource.isAddress())
107                     usedRegister = exit.m_jsValueSource.base();
108                 else
109                     usedRegister = exit.m_jsValueSource.gpr();
110                 
111                 GPRReg scratch1;
112                 GPRReg scratch2;
113                 scratch1 = AssemblyHelpers::selectScratchGPR(usedRegister);
114                 scratch2 = AssemblyHelpers::selectScratchGPR(usedRegister, scratch1);
115                 
116                 m_jit.push(scratch1);
117                 m_jit.push(scratch2);
118                 
119                 GPRReg value;
120                 if (exit.m_jsValueSource.isAddress()) {
121                     value = scratch1;
122                     m_jit.loadPtr(AssemblyHelpers::Address(exit.m_jsValueSource.asAddress()), value);
123                 } else
124                     value = exit.m_jsValueSource.gpr();
125                 
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()));
132                 
133                 m_jit.pop(scratch2);
134                 m_jit.pop(scratch1);
135             }
136         }
137         
138         if (!!exit.m_valueProfile) {
139             EncodedJSValue* bucket = exit.m_valueProfile.getSpecFailBucket(0);
140             
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);
147             } else
148                 m_jit.store64(exit.m_jsValueSource.gpr(), bucket);
149         }
150     }
151     
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
169     // locations.
170     //
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.
187
188     // 4) Save all state from GPRs into the scratch buffer.
189     
190     ScratchBuffer* scratchBuffer = m_jit.vm()->scratchBufferForSize(sizeof(EncodedJSValue) * operands.size());
191     EncodedJSValue* scratch = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0;
192     
193     for (size_t index = 0; index < operands.size(); ++index) {
194         const ValueRecovery& recovery = operands[index];
195         
196         switch (recovery.technique()) {
197         case InGPR:
198         case UnboxedInt32InGPR:
199         case UInt32InGPR:
200         case UnboxedInt52InGPR:
201         case UnboxedStrictInt52InGPR:
202             m_jit.store64(recovery.gpr(), scratch + index);
203             break;
204             
205         default:
206             break;
207         }
208     }
209     
210     // And voila, all FPRs are free to reuse.
211     
212     // 5) Save all state from FPRs into the scratch buffer.
213     
214     for (size_t index = 0; index < operands.size(); ++index) {
215         const ValueRecovery& recovery = operands[index];
216         
217         switch (recovery.technique()) {
218         case InFPR:
219             m_jit.move(AssemblyHelpers::TrustedImmPtr(scratch + index), GPRInfo::regT0);
220             m_jit.storeDouble(recovery.fpr(), GPRInfo::regT0);
221             break;
222             
223         default:
224             break;
225         }
226     }
227     
228     // Now, all FPRs are also free.
229     
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.
233
234     for (size_t index = 0; index < operands.size(); ++index) {
235         const ValueRecovery& recovery = operands[index];
236         int operand = operands.operandForIndex(index);
237         
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);
246             break;
247             
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);
253             break;
254             
255         default:
256             break;
257         }
258     }
259     
260     // 6) Do all data format conversions and store the results into the stack.
261     
262     bool haveArguments = false;
263     
264     for (size_t index = 0; index < operands.size(); ++index) {
265         const ValueRecovery& recovery = operands[index];
266         int operand = operands.operandForIndex(index);
267         
268         switch (recovery.technique()) {
269         case InGPR:
270         case DisplacedInJSStack:
271             m_jit.load64(scratch + index, GPRInfo::regT0);
272             m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
273             break;
274             
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));
282             break;
283             
284         case AlreadyInJSStackAsUnboxedInt52:
285         case UnboxedInt52InGPR:
286         case Int52DisplacedInJSStack:
287             m_jit.load64(scratch + index, GPRInfo::regT0);
288             m_jit.rshift64(
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));
292             break;
293             
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));
299             break;
300             
301         case UInt32InGPR:
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));
306             break;
307             
308         case AlreadyInJSStackAsUnboxedDouble:
309         case InFPR:
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));
315             break;
316             
317         case Constant:
318             m_jit.store64(
319                 AssemblyHelpers::TrustedImm64(JSValue::encode(recovery.constant())),
320                 AssemblyHelpers::addressFor(operand));
321             break;
322             
323         case ArgumentsThatWereNotCreated:
324             haveArguments = true;
325             break;
326             
327         default:
328             break;
329         }
330     }
331     
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:
337     //
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).
341     //
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.
345     //
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.
349     //
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.
352     //
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().
367     
368     handleExitCounts(m_jit, exit);
369     
370     // 8) Reify inlined call frames.
371     
372     reifyInlinedCallFrames(m_jit, exit);
373     
374     // 9) Create arguments if necessary and place them into the appropriate aliased
375     //    registers.
376     
377     if (haveArguments) {
378         HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash,
379             NullableHashTraits<InlineCallFrame*> > didCreateArgumentsObject;
380
381         for (size_t index = 0; index < operands.size(); ++index) {
382             const ValueRecovery& recovery = operands[index];
383             if (recovery.technique() != ArgumentsThatWereNotCreated)
384                 continue;
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;
389                  current;
390                  current = current->caller.inlineCallFrame) {
391                 if (current->stackOffset <= operand) {
392                     inlineCallFrame = current;
393                     break;
394                 }
395             }
396
397             if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments())
398                 continue;
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);
406                 } else
407                     m_jit.setupArgumentsExecState();
408                 m_jit.move(
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));
414                 m_jit.store64(
415                     GPRInfo::returnValueGPR,
416                     AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister)));
417                 m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms.
418             }
419
420             m_jit.load64(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0);
421             m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand));
422         }
423     }
424     
425     // 10) Load the result of the last bytecode operation into regT0.
426     
427     if (exit.m_lastSetOperand.isValid())
428         m_jit.load64(AssemblyHelpers::addressFor(exit.m_lastSetOperand), GPRInfo::cachedResultRegister);
429     
430     // 11) And finish.
431     
432     adjustAndJumpToTarget(m_jit, exit);
433 }
434
435 } } // namespace JSC::DFG
436
437 #endif // ENABLE(DFG_JIT) && USE(JSVALUE64)