Rename InlineCallFrame:: getCallerSkippingDeadFrames to something more descriptive
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGOSRExitCompilerCommon.cpp
1 /*
2  * Copyright (C) 2013-2015 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 "DFGOSRExitCompilerCommon.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "DFGJITCode.h"
32 #include "DFGOperations.h"
33 #include "JIT.h"
34 #include "JSCJSValueInlines.h"
35 #include "JSCInlines.h"
36
37 namespace JSC { namespace DFG {
38
39 void handleExitCounts(CCallHelpers& jit, const OSRExitBase& exit)
40 {
41     jit.add32(AssemblyHelpers::TrustedImm32(1), AssemblyHelpers::AbsoluteAddress(&exit.m_count));
42     
43     jit.move(AssemblyHelpers::TrustedImmPtr(jit.codeBlock()), GPRInfo::regT0);
44     
45     AssemblyHelpers::Jump tooFewFails;
46     
47     jit.load32(AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfOSRExitCounter()), GPRInfo::regT2);
48     jit.add32(AssemblyHelpers::TrustedImm32(1), GPRInfo::regT2);
49     jit.store32(GPRInfo::regT2, AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfOSRExitCounter()));
50     
51     jit.move(AssemblyHelpers::TrustedImmPtr(jit.baselineCodeBlock()), GPRInfo::regT0);
52     AssemblyHelpers::Jump reoptimizeNow = jit.branch32(
53         AssemblyHelpers::GreaterThanOrEqual,
54         AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter()),
55         AssemblyHelpers::TrustedImm32(0));
56     
57     // We want to figure out if there's a possibility that we're in a loop. For the outermost
58     // code block in the inline stack, we handle this appropriately by having the loop OSR trigger
59     // check the exit count of the replacement of the CodeBlock from which we are OSRing. The
60     // problem is the inlined functions, which might also have loops, but whose baseline versions
61     // don't know where to look for the exit count. Figure out if those loops are severe enough
62     // that we had tried to OSR enter. If so, then we should use the loop reoptimization trigger.
63     // Otherwise, we should use the normal reoptimization trigger.
64     
65     AssemblyHelpers::JumpList loopThreshold;
66     
67     for (InlineCallFrame* inlineCallFrame = exit.m_codeOrigin.inlineCallFrame; inlineCallFrame; inlineCallFrame = inlineCallFrame->directCaller.inlineCallFrame) {
68         loopThreshold.append(
69             jit.branchTest8(
70                 AssemblyHelpers::NonZero,
71                 AssemblyHelpers::AbsoluteAddress(
72                     inlineCallFrame->baselineCodeBlock->ownerScriptExecutable()->addressOfDidTryToEnterInLoop())));
73     }
74     
75     jit.move(
76         AssemblyHelpers::TrustedImm32(jit.codeBlock()->exitCountThresholdForReoptimization()),
77         GPRInfo::regT1);
78     
79     if (!loopThreshold.empty()) {
80         AssemblyHelpers::Jump done = jit.jump();
81
82         loopThreshold.link(&jit);
83         jit.move(
84             AssemblyHelpers::TrustedImm32(
85                 jit.codeBlock()->exitCountThresholdForReoptimizationFromLoop()),
86             GPRInfo::regT1);
87         
88         done.link(&jit);
89     }
90     
91     tooFewFails = jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, GPRInfo::regT1);
92     
93     reoptimizeNow.link(&jit);
94     
95     // Reoptimize as soon as possible.
96 #if !NUMBER_OF_ARGUMENT_REGISTERS
97     jit.poke(GPRInfo::regT0);
98     jit.poke(AssemblyHelpers::TrustedImmPtr(&exit), 1);
99 #else
100     jit.move(GPRInfo::regT0, GPRInfo::argumentGPR0);
101     jit.move(AssemblyHelpers::TrustedImmPtr(&exit), GPRInfo::argumentGPR1);
102 #endif
103     jit.move(AssemblyHelpers::TrustedImmPtr(bitwise_cast<void*>(triggerReoptimizationNow)), GPRInfo::nonArgGPR0);
104     jit.call(GPRInfo::nonArgGPR0);
105     AssemblyHelpers::Jump doneAdjusting = jit.jump();
106     
107     tooFewFails.link(&jit);
108     
109     // Adjust the execution counter such that the target is to only optimize after a while.
110     int32_t activeThreshold =
111         jit.baselineCodeBlock()->adjustedCounterValue(
112             Options::thresholdForOptimizeAfterLongWarmUp());
113     int32_t targetValue = applyMemoryUsageHeuristicsAndConvertToInt(
114         activeThreshold, jit.baselineCodeBlock());
115     int32_t clippedValue;
116     switch (jit.codeBlock()->jitType()) {
117     case JITCode::DFGJIT:
118         clippedValue = BaselineExecutionCounter::clippedThreshold(jit.codeBlock()->globalObject(), targetValue);
119         break;
120     case JITCode::FTLJIT:
121         clippedValue = UpperTierExecutionCounter::clippedThreshold(jit.codeBlock()->globalObject(), targetValue);
122         break;
123     default:
124         RELEASE_ASSERT_NOT_REACHED();
125 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
126         clippedValue = 0; // Make some compilers, and mhahnenberg, happy.
127 #endif
128         break;
129     }
130     jit.store32(AssemblyHelpers::TrustedImm32(-clippedValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter()));
131     jit.store32(AssemblyHelpers::TrustedImm32(activeThreshold), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionActiveThreshold()));
132     jit.store32(AssemblyHelpers::TrustedImm32(formattedTotalExecutionCount(clippedValue)), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionTotalCount()));
133     
134     doneAdjusting.link(&jit);
135 }
136
137 void reifyInlinedCallFrames(CCallHelpers& jit, const OSRExitBase& exit)
138 {
139     // FIXME: We shouldn't leave holes on the stack when performing an OSR exit
140     // in presence of inlined tail calls.
141     // https://bugs.webkit.org/show_bug.cgi?id=147511
142     ASSERT(jit.baselineCodeBlock()->jitType() == JITCode::BaselineJIT);
143     jit.storePtr(AssemblyHelpers::TrustedImmPtr(jit.baselineCodeBlock()), AssemblyHelpers::addressFor((VirtualRegister)JSStack::CodeBlock));
144
145     const CodeOrigin* codeOrigin;
146     for (codeOrigin = &exit.m_codeOrigin; codeOrigin && codeOrigin->inlineCallFrame; codeOrigin = codeOrigin->inlineCallFrame->getCallerSkippingTailCalls()) {
147         InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
148         CodeBlock* baselineCodeBlock = jit.baselineCodeBlockFor(*codeOrigin);
149         InlineCallFrame::Kind trueCallerCallKind;
150         CodeOrigin* trueCaller = inlineCallFrame->getCallerSkippingTailCalls(&trueCallerCallKind);
151         GPRReg callerFrameGPR = GPRInfo::callFrameRegister;
152
153         if (!trueCaller) {
154             ASSERT(inlineCallFrame->isTail());
155             jit.loadPtr(AssemblyHelpers::Address(GPRInfo::callFrameRegister, CallFrame::returnPCOffset()), GPRInfo::regT3);
156             jit.storePtr(GPRInfo::regT3, AssemblyHelpers::addressForByteOffset(inlineCallFrame->returnPCOffset()));
157             jit.loadPtr(AssemblyHelpers::Address(GPRInfo::callFrameRegister, CallFrame::callerFrameOffset()), GPRInfo::regT3);
158             callerFrameGPR = GPRInfo::regT3;
159         } else {
160             CodeBlock* baselineCodeBlockForCaller = jit.baselineCodeBlockFor(*trueCaller);
161             unsigned callBytecodeIndex = trueCaller->bytecodeIndex;
162             void* jumpTarget = nullptr;
163
164             switch (trueCallerCallKind) {
165             case InlineCallFrame::Call:
166             case InlineCallFrame::Construct:
167             case InlineCallFrame::CallVarargs:
168             case InlineCallFrame::ConstructVarargs:
169             case InlineCallFrame::TailCall:
170             case InlineCallFrame::TailCallVarargs: {
171                 CallLinkInfo* callLinkInfo =
172                     baselineCodeBlockForCaller->getCallLinkInfoForBytecodeIndex(callBytecodeIndex);
173                 RELEASE_ASSERT(callLinkInfo);
174
175                 jumpTarget = callLinkInfo->callReturnLocation().executableAddress();
176                 break;
177             }
178
179             case InlineCallFrame::GetterCall:
180             case InlineCallFrame::SetterCall: {
181                 StructureStubInfo* stubInfo =
182                     baselineCodeBlockForCaller->findStubInfo(CodeOrigin(callBytecodeIndex));
183                 RELEASE_ASSERT(stubInfo);
184
185                 jumpTarget = stubInfo->callReturnLocation.labelAtOffset(
186                     stubInfo->patch.deltaCallToDone).executableAddress();
187                 break;
188             }
189
190             default:
191                 RELEASE_ASSERT_NOT_REACHED();
192             }
193
194             if (trueCaller->inlineCallFrame) {
195                 jit.addPtr(
196                     AssemblyHelpers::TrustedImm32(trueCaller->inlineCallFrame->stackOffset * sizeof(EncodedJSValue)),
197                     GPRInfo::callFrameRegister,
198                     GPRInfo::regT3);
199                 callerFrameGPR = GPRInfo::regT3;
200             }
201
202             jit.storePtr(AssemblyHelpers::TrustedImmPtr(jumpTarget), AssemblyHelpers::addressForByteOffset(inlineCallFrame->returnPCOffset()));
203         }
204
205         jit.storePtr(AssemblyHelpers::TrustedImmPtr(baselineCodeBlock), AssemblyHelpers::addressFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::CodeBlock)));
206
207         // Restore the inline call frame's callee save registers.
208         // If this inlined frame is a tail call that will return back to the original caller, we need to
209         // copy the prior contents of the tag registers already saved for the outer frame to this frame.
210         jit.emitSaveOrCopyCalleeSavesFor(
211             baselineCodeBlock,
212             static_cast<VirtualRegister>(inlineCallFrame->stackOffset),
213             trueCaller ? AssemblyHelpers::UseExistingTagRegisterContents : AssemblyHelpers::CopySavedTagRegistersFromBaseFrame,
214             GPRInfo::regT2);
215
216         if (!inlineCallFrame->isVarargs())
217             jit.store32(AssemblyHelpers::TrustedImm32(inlineCallFrame->arguments.size()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ArgumentCount)));
218 #if USE(JSVALUE64)
219         jit.store64(callerFrameGPR, AssemblyHelpers::addressForByteOffset(inlineCallFrame->callerFrameOffset()));
220         uint32_t locationBits = CallSiteIndex(codeOrigin->bytecodeIndex).bits();
221         jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ArgumentCount)));
222         if (!inlineCallFrame->isClosureCall)
223             jit.store64(AssemblyHelpers::TrustedImm64(JSValue::encode(JSValue(inlineCallFrame->calleeConstant()))), AssemblyHelpers::addressFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::Callee)));
224 #else // USE(JSVALUE64) // so this is the 32-bit part
225         jit.storePtr(callerFrameGPR, AssemblyHelpers::addressForByteOffset(inlineCallFrame->callerFrameOffset()));
226         Instruction* instruction = baselineCodeBlock->instructions().begin() + codeOrigin->bytecodeIndex;
227         uint32_t locationBits = CallSiteIndex(instruction).bits();
228         jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::ArgumentCount)));
229         jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::Callee)));
230         if (!inlineCallFrame->isClosureCall)
231             jit.storePtr(AssemblyHelpers::TrustedImmPtr(inlineCallFrame->calleeConstant()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + JSStack::Callee)));
232 #endif // USE(JSVALUE64) // ending the #else part, so directly above is the 32-bit part
233     }
234
235     // Don't need to set the toplevel code origin if we only did inline tail calls
236     if (codeOrigin) {
237 #if USE(JSVALUE64)
238     uint32_t locationBits = CallSiteIndex(codeOrigin->bytecodeIndex).bits();
239 #else
240     Instruction* instruction = jit.baselineCodeBlock()->instructions().begin() + codeOrigin->bytecodeIndex;
241     uint32_t locationBits = CallSiteIndex(instruction).bits();
242 #endif
243         jit.store32(AssemblyHelpers::TrustedImm32(locationBits), AssemblyHelpers::tagFor((VirtualRegister)(JSStack::ArgumentCount)));
244     }
245 }
246
247 static void osrWriteBarrier(CCallHelpers& jit, GPRReg owner, GPRReg scratch)
248 {
249     AssemblyHelpers::Jump ownerIsRememberedOrInEden = jit.jumpIfIsRememberedOrInEden(owner);
250
251     // We need these extra slots because setupArgumentsWithExecState will use poke on x86.
252 #if CPU(X86)
253     jit.subPtr(MacroAssembler::TrustedImm32(sizeof(void*) * 3), MacroAssembler::stackPointerRegister);
254 #endif
255
256     jit.setupArgumentsWithExecState(owner);
257     jit.move(MacroAssembler::TrustedImmPtr(reinterpret_cast<void*>(operationOSRWriteBarrier)), scratch);
258     jit.call(scratch);
259
260 #if CPU(X86)
261     jit.addPtr(MacroAssembler::TrustedImm32(sizeof(void*) * 3), MacroAssembler::stackPointerRegister);
262 #endif
263
264     ownerIsRememberedOrInEden.link(&jit);
265 }
266
267 void adjustAndJumpToTarget(CCallHelpers& jit, const OSRExitBase& exit, bool isExitingToOpCatch)
268 {
269     jit.move(
270         AssemblyHelpers::TrustedImmPtr(
271             jit.codeBlock()->baselineAlternative()), GPRInfo::argumentGPR1);
272     osrWriteBarrier(jit, GPRInfo::argumentGPR1, GPRInfo::nonArgGPR0);
273
274     // We barrier all inlined frames -- and not just the current inline stack --
275     // because we don't know which inlined function owns the value profile that
276     // we'll update when we exit. In the case of "f() { a(); b(); }", if both
277     // a and b are inlined, we might exit inside b due to a bad value loaded
278     // from a.
279     // FIXME: MethodOfGettingAValueProfile should remember which CodeBlock owns
280     // the value profile.
281     InlineCallFrameSet* inlineCallFrames = jit.codeBlock()->jitCode()->dfgCommon()->inlineCallFrames.get();
282     if (inlineCallFrames) {
283         for (InlineCallFrame* inlineCallFrame : *inlineCallFrames) {
284             jit.move(
285                 AssemblyHelpers::TrustedImmPtr(
286                     inlineCallFrame->baselineCodeBlock.get()), GPRInfo::argumentGPR1);
287             osrWriteBarrier(jit, GPRInfo::argumentGPR1, GPRInfo::nonArgGPR0);
288         }
289     }
290
291     if (exit.m_codeOrigin.inlineCallFrame)
292         jit.addPtr(AssemblyHelpers::TrustedImm32(exit.m_codeOrigin.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister);
293
294     CodeBlock* codeBlockForExit = jit.baselineCodeBlockFor(exit.m_codeOrigin);
295     Vector<BytecodeAndMachineOffset>& decodedCodeMap = jit.decodedCodeMapFor(codeBlockForExit);
296     
297     BytecodeAndMachineOffset* mapping = binarySearch<BytecodeAndMachineOffset, unsigned>(decodedCodeMap, decodedCodeMap.size(), exit.m_codeOrigin.bytecodeIndex, BytecodeAndMachineOffset::getBytecodeIndex);
298     
299     ASSERT(mapping);
300     ASSERT(mapping->m_bytecodeIndex == exit.m_codeOrigin.bytecodeIndex);
301     
302     void* jumpTarget = codeBlockForExit->jitCode()->executableAddressAtOffset(mapping->m_machineCodeOffset);
303
304     jit.addPtr(AssemblyHelpers::TrustedImm32(JIT::stackPointerOffsetFor(codeBlockForExit) * sizeof(Register)), GPRInfo::callFrameRegister, AssemblyHelpers::stackPointerRegister);
305     if (isExitingToOpCatch) {
306         // Since we're jumping to op_catch, we need to set callFrameForCatch.
307         jit.storePtr(GPRInfo::callFrameRegister, jit.vm()->addressOfCallFrameForCatch());
308     }
309     
310     jit.move(AssemblyHelpers::TrustedImmPtr(jumpTarget), GPRInfo::regT2);
311     jit.jump(GPRInfo::regT2);
312 }
313
314 } } // namespace JSC::DFG
315
316 #endif // ENABLE(DFG_JIT)
317