|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "jit/BaselineFrame-inl.h" |
|
8 |
|
9 #include "jit/BaselineJIT.h" |
|
10 #include "jit/Ion.h" |
|
11 #include "vm/Debugger.h" |
|
12 #include "vm/ScopeObject.h" |
|
13 |
|
14 #include "jit/IonFrames-inl.h" |
|
15 #include "vm/Stack-inl.h" |
|
16 |
|
17 using namespace js; |
|
18 using namespace js::jit; |
|
19 |
|
20 static void |
|
21 MarkLocals(BaselineFrame *frame, JSTracer *trc, unsigned start, unsigned end) |
|
22 { |
|
23 if (start < end) { |
|
24 // Stack grows down. |
|
25 Value *last = frame->valueSlot(end - 1); |
|
26 gc::MarkValueRootRange(trc, end - start, last, "baseline-stack"); |
|
27 } |
|
28 } |
|
29 |
|
30 void |
|
31 BaselineFrame::trace(JSTracer *trc, JitFrameIterator &frameIterator) |
|
32 { |
|
33 replaceCalleeToken(MarkCalleeToken(trc, calleeToken())); |
|
34 |
|
35 gc::MarkValueRoot(trc, &thisValue(), "baseline-this"); |
|
36 |
|
37 // Mark actual and formal args. |
|
38 if (isNonEvalFunctionFrame()) { |
|
39 unsigned numArgs = js::Max(numActualArgs(), numFormalArgs()); |
|
40 gc::MarkValueRootRange(trc, numArgs, argv(), "baseline-args"); |
|
41 } |
|
42 |
|
43 // Mark scope chain, if it exists. |
|
44 if (scopeChain_) |
|
45 gc::MarkObjectRoot(trc, &scopeChain_, "baseline-scopechain"); |
|
46 |
|
47 // Mark return value. |
|
48 if (hasReturnValue()) |
|
49 gc::MarkValueRoot(trc, returnValue().address(), "baseline-rval"); |
|
50 |
|
51 if (isEvalFrame()) |
|
52 gc::MarkScriptRoot(trc, &evalScript_, "baseline-evalscript"); |
|
53 |
|
54 if (hasArgsObj()) |
|
55 gc::MarkObjectRoot(trc, &argsObj_, "baseline-args-obj"); |
|
56 |
|
57 // Mark locals and stack values. |
|
58 JSScript *script = this->script(); |
|
59 size_t nfixed = script->nfixed(); |
|
60 size_t nlivefixed = script->nfixedvars(); |
|
61 |
|
62 if (nfixed != nlivefixed) { |
|
63 jsbytecode *pc; |
|
64 NestedScopeObject *staticScope; |
|
65 |
|
66 frameIterator.baselineScriptAndPc(nullptr, &pc); |
|
67 staticScope = script->getStaticScope(pc); |
|
68 while (staticScope && !staticScope->is<StaticBlockObject>()) |
|
69 staticScope = staticScope->enclosingNestedScope(); |
|
70 |
|
71 if (staticScope) { |
|
72 StaticBlockObject &blockObj = staticScope->as<StaticBlockObject>(); |
|
73 nlivefixed = blockObj.localOffset() + blockObj.numVariables(); |
|
74 } |
|
75 } |
|
76 |
|
77 JS_ASSERT(nlivefixed <= nfixed); |
|
78 JS_ASSERT(nlivefixed >= script->nfixedvars()); |
|
79 |
|
80 // NB: It is possible that numValueSlots() could be zero, even if nfixed is |
|
81 // nonzero. This is the case if the function has an early stack check. |
|
82 if (numValueSlots() == 0) |
|
83 return; |
|
84 |
|
85 JS_ASSERT(nfixed <= numValueSlots()); |
|
86 |
|
87 if (nfixed == nlivefixed) { |
|
88 // All locals are live. |
|
89 MarkLocals(this, trc, 0, numValueSlots()); |
|
90 } else { |
|
91 // Mark operand stack. |
|
92 MarkLocals(this, trc, nfixed, numValueSlots()); |
|
93 |
|
94 // Clear dead locals. |
|
95 while (nfixed > nlivefixed) |
|
96 unaliasedLocal(--nfixed, DONT_CHECK_ALIASING).setUndefined(); |
|
97 |
|
98 // Mark live locals. |
|
99 MarkLocals(this, trc, 0, nlivefixed); |
|
100 } |
|
101 } |
|
102 |
|
103 bool |
|
104 BaselineFrame::copyRawFrameSlots(AutoValueVector *vec) const |
|
105 { |
|
106 unsigned nfixed = script()->nfixed(); |
|
107 unsigned nformals = numFormalArgs(); |
|
108 |
|
109 if (!vec->resize(nformals + nfixed)) |
|
110 return false; |
|
111 |
|
112 mozilla::PodCopy(vec->begin(), argv(), nformals); |
|
113 for (unsigned i = 0; i < nfixed; i++) |
|
114 (*vec)[nformals + i] = *valueSlot(i); |
|
115 return true; |
|
116 } |
|
117 |
|
118 bool |
|
119 BaselineFrame::strictEvalPrologue(JSContext *cx) |
|
120 { |
|
121 JS_ASSERT(isStrictEvalFrame()); |
|
122 |
|
123 CallObject *callobj = CallObject::createForStrictEval(cx, this); |
|
124 if (!callobj) |
|
125 return false; |
|
126 |
|
127 pushOnScopeChain(*callobj); |
|
128 flags_ |= HAS_CALL_OBJ; |
|
129 return true; |
|
130 } |
|
131 |
|
132 bool |
|
133 BaselineFrame::heavyweightFunPrologue(JSContext *cx) |
|
134 { |
|
135 return initFunctionScopeObjects(cx); |
|
136 } |
|
137 |
|
138 bool |
|
139 BaselineFrame::initFunctionScopeObjects(JSContext *cx) |
|
140 { |
|
141 JS_ASSERT(isNonEvalFunctionFrame()); |
|
142 JS_ASSERT(fun()->isHeavyweight()); |
|
143 |
|
144 CallObject *callobj = CallObject::createForFunction(cx, this); |
|
145 if (!callobj) |
|
146 return false; |
|
147 |
|
148 pushOnScopeChain(*callobj); |
|
149 flags_ |= HAS_CALL_OBJ; |
|
150 return true; |
|
151 } |
|
152 |
|
153 bool |
|
154 BaselineFrame::initForOsr(InterpreterFrame *fp, uint32_t numStackValues) |
|
155 { |
|
156 mozilla::PodZero(this); |
|
157 |
|
158 scopeChain_ = fp->scopeChain(); |
|
159 |
|
160 if (fp->hasCallObjUnchecked()) |
|
161 flags_ |= BaselineFrame::HAS_CALL_OBJ; |
|
162 |
|
163 if (fp->isEvalFrame()) { |
|
164 flags_ |= BaselineFrame::EVAL; |
|
165 evalScript_ = fp->script(); |
|
166 } |
|
167 |
|
168 if (fp->script()->needsArgsObj() && fp->hasArgsObj()) { |
|
169 flags_ |= BaselineFrame::HAS_ARGS_OBJ; |
|
170 argsObj_ = &fp->argsObj(); |
|
171 } |
|
172 |
|
173 if (fp->hasHookData()) { |
|
174 flags_ |= BaselineFrame::HAS_HOOK_DATA; |
|
175 hookData_ = fp->hookData(); |
|
176 } |
|
177 |
|
178 if (fp->hasReturnValue()) |
|
179 setReturnValue(fp->returnValue()); |
|
180 |
|
181 // If the interpreter pushed an SPS frame when it entered the function, the |
|
182 // interpreter will pop it after the OSR trampoline returns. In order for |
|
183 // the Baseline frame to have its SPS flag set, it must have its own SPS |
|
184 // frame, which the Baseline code will pop on return. Note that the |
|
185 // profiler may have been enabled or disabled after the function was entered |
|
186 // but before OSR. |
|
187 JSContext *cx = GetJSContextFromJitCode(); |
|
188 SPSProfiler *p = &(cx->runtime()->spsProfiler); |
|
189 if (p->enabled()) { |
|
190 p->enter(fp->script(), fp->maybeFun()); |
|
191 flags_ |= BaselineFrame::HAS_PUSHED_SPS_FRAME; |
|
192 } |
|
193 |
|
194 frameSize_ = BaselineFrame::FramePointerOffset + |
|
195 BaselineFrame::Size() + |
|
196 numStackValues * sizeof(Value); |
|
197 |
|
198 JS_ASSERT(numValueSlots() == numStackValues); |
|
199 |
|
200 for (uint32_t i = 0; i < numStackValues; i++) |
|
201 *valueSlot(i) = fp->slots()[i]; |
|
202 |
|
203 if (cx->compartment()->debugMode()) { |
|
204 // In debug mode, update any Debugger.Frame objects for the |
|
205 // InterpreterFrame to point to the BaselineFrame. |
|
206 |
|
207 // The caller pushed a fake return address. ScriptFrameIter, used by the |
|
208 // debugger, wants a valid return address, but it's okay to just pick one. |
|
209 // In debug mode there's always at least 1 ICEntry (since there are always |
|
210 // debug prologue/epilogue calls). |
|
211 JitFrameIterator iter(cx); |
|
212 JS_ASSERT(iter.returnAddress() == nullptr); |
|
213 BaselineScript *baseline = fp->script()->baselineScript(); |
|
214 iter.current()->setReturnAddress(baseline->returnAddressForIC(baseline->icEntry(0))); |
|
215 |
|
216 if (!Debugger::handleBaselineOsr(cx, fp, this)) |
|
217 return false; |
|
218 } |
|
219 |
|
220 return true; |
|
221 } |