|
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 #ifndef jit_BaselineFrame_h |
|
8 #define jit_BaselineFrame_h |
|
9 |
|
10 #ifdef JS_ION |
|
11 |
|
12 #include "jit/IonFrames.h" |
|
13 #include "vm/Stack.h" |
|
14 |
|
15 namespace js { |
|
16 namespace jit { |
|
17 |
|
18 struct BaselineDebugModeOSRInfo; |
|
19 |
|
20 // The stack looks like this, fp is the frame pointer: |
|
21 // |
|
22 // fp+y arguments |
|
23 // fp+x IonJSFrameLayout (frame header) |
|
24 // fp => saved frame pointer |
|
25 // fp-x BaselineFrame |
|
26 // locals |
|
27 // stack values |
|
28 |
|
29 // Eval frames |
|
30 // |
|
31 // Like js::InterpreterFrame, every BaselineFrame is either a global frame |
|
32 // or a function frame. Both global and function frames can optionally |
|
33 // be "eval frames". The callee token for eval function frames is the |
|
34 // enclosing function. BaselineFrame::evalScript_ stores the eval script |
|
35 // itself. |
|
36 class BaselineFrame |
|
37 { |
|
38 public: |
|
39 enum Flags { |
|
40 // The frame has a valid return value. See also InterpreterFrame::HAS_RVAL. |
|
41 HAS_RVAL = 1 << 0, |
|
42 |
|
43 // A call object has been pushed on the scope chain. |
|
44 HAS_CALL_OBJ = 1 << 2, |
|
45 |
|
46 // Frame has an arguments object, argsObj_. |
|
47 HAS_ARGS_OBJ = 1 << 4, |
|
48 |
|
49 // See InterpreterFrame::PREV_UP_TO_DATE. |
|
50 PREV_UP_TO_DATE = 1 << 5, |
|
51 |
|
52 // Eval frame, see the "eval frames" comment. |
|
53 EVAL = 1 << 6, |
|
54 |
|
55 // Frame has hookData_ set. |
|
56 HAS_HOOK_DATA = 1 << 7, |
|
57 |
|
58 // Frame has profiler entry pushed. |
|
59 HAS_PUSHED_SPS_FRAME = 1 << 8, |
|
60 |
|
61 // Frame has over-recursed on an early check. |
|
62 OVER_RECURSED = 1 << 9, |
|
63 |
|
64 // Frame has a BaselineRecompileInfo stashed in the scratch value |
|
65 // slot. See PatchBaselineFramesForDebugMOde. |
|
66 HAS_DEBUG_MODE_OSR_INFO = 1 << 10 |
|
67 }; |
|
68 |
|
69 protected: // Silence Clang warning about unused private fields. |
|
70 // We need to split the Value into 2 fields of 32 bits, otherwise the C++ |
|
71 // compiler may add some padding between the fields. |
|
72 uint32_t loScratchValue_; |
|
73 uint32_t hiScratchValue_; |
|
74 uint32_t loReturnValue_; // If HAS_RVAL, the frame's return value. |
|
75 uint32_t hiReturnValue_; |
|
76 uint32_t frameSize_; |
|
77 JSObject *scopeChain_; // Scope chain (always initialized). |
|
78 JSScript *evalScript_; // If isEvalFrame(), the current eval script. |
|
79 ArgumentsObject *argsObj_; // If HAS_ARGS_OBJ, the arguments object. |
|
80 void *hookData_; // If HAS_HOOK_DATA, debugger call hook data. |
|
81 uint32_t flags_; |
|
82 #if JS_BITS_PER_WORD == 32 |
|
83 uint32_t padding_; // Pad to 8-byte alignment. |
|
84 #endif |
|
85 |
|
86 public: |
|
87 // Distance between the frame pointer and the frame header (return address). |
|
88 // This is the old frame pointer saved in the prologue. |
|
89 static const uint32_t FramePointerOffset = sizeof(void *); |
|
90 |
|
91 bool initForOsr(InterpreterFrame *fp, uint32_t numStackValues); |
|
92 |
|
93 uint32_t frameSize() const { |
|
94 return frameSize_; |
|
95 } |
|
96 void setFrameSize(uint32_t frameSize) { |
|
97 frameSize_ = frameSize; |
|
98 } |
|
99 inline uint32_t *addressOfFrameSize() { |
|
100 return &frameSize_; |
|
101 } |
|
102 JSObject *scopeChain() const { |
|
103 return scopeChain_; |
|
104 } |
|
105 void setScopeChain(JSObject *scopeChain) { |
|
106 scopeChain_ = scopeChain; |
|
107 } |
|
108 inline JSObject **addressOfScopeChain() { |
|
109 return &scopeChain_; |
|
110 } |
|
111 |
|
112 inline Value *addressOfScratchValue() { |
|
113 return reinterpret_cast<Value *>(&loScratchValue_); |
|
114 } |
|
115 |
|
116 inline void pushOnScopeChain(ScopeObject &scope); |
|
117 inline void popOffScopeChain(); |
|
118 |
|
119 inline void popWith(JSContext *cx); |
|
120 |
|
121 CalleeToken calleeToken() const { |
|
122 uint8_t *pointer = (uint8_t *)this + Size() + offsetOfCalleeToken(); |
|
123 return *(CalleeToken *)pointer; |
|
124 } |
|
125 void replaceCalleeToken(CalleeToken token) { |
|
126 uint8_t *pointer = (uint8_t *)this + Size() + offsetOfCalleeToken(); |
|
127 *(CalleeToken *)pointer = token; |
|
128 } |
|
129 JSScript *script() const { |
|
130 if (isEvalFrame()) |
|
131 return evalScript(); |
|
132 return ScriptFromCalleeToken(calleeToken()); |
|
133 } |
|
134 JSFunction *fun() const { |
|
135 return CalleeTokenToFunction(calleeToken()); |
|
136 } |
|
137 JSFunction *maybeFun() const { |
|
138 return isFunctionFrame() ? fun() : nullptr; |
|
139 } |
|
140 JSFunction *callee() const { |
|
141 return CalleeTokenToFunction(calleeToken()); |
|
142 } |
|
143 Value calleev() const { |
|
144 return ObjectValue(*callee()); |
|
145 } |
|
146 size_t numValueSlots() const { |
|
147 size_t size = frameSize(); |
|
148 |
|
149 JS_ASSERT(size >= BaselineFrame::FramePointerOffset + BaselineFrame::Size()); |
|
150 size -= BaselineFrame::FramePointerOffset + BaselineFrame::Size(); |
|
151 |
|
152 JS_ASSERT((size % sizeof(Value)) == 0); |
|
153 return size / sizeof(Value); |
|
154 } |
|
155 Value *valueSlot(size_t slot) const { |
|
156 JS_ASSERT(slot < numValueSlots()); |
|
157 return (Value *)this - (slot + 1); |
|
158 } |
|
159 |
|
160 Value &unaliasedVar(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { |
|
161 JS_ASSERT(i < script()->nfixedvars()); |
|
162 JS_ASSERT_IF(checkAliasing, !script()->varIsAliased(i)); |
|
163 return *valueSlot(i); |
|
164 } |
|
165 |
|
166 Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { |
|
167 JS_ASSERT(i < numFormalArgs()); |
|
168 JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() && |
|
169 !script()->formalIsAliased(i)); |
|
170 return argv()[i]; |
|
171 } |
|
172 |
|
173 Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { |
|
174 JS_ASSERT(i < numActualArgs()); |
|
175 JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals()); |
|
176 JS_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i)); |
|
177 return argv()[i]; |
|
178 } |
|
179 |
|
180 Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { |
|
181 JS_ASSERT(i < script()->nfixed()); |
|
182 #ifdef DEBUG |
|
183 CheckLocalUnaliased(checkAliasing, script(), i); |
|
184 #endif |
|
185 return *valueSlot(i); |
|
186 } |
|
187 |
|
188 unsigned numActualArgs() const { |
|
189 return *(size_t *)(reinterpret_cast<const uint8_t *>(this) + |
|
190 BaselineFrame::Size() + |
|
191 offsetOfNumActualArgs()); |
|
192 } |
|
193 unsigned numFormalArgs() const { |
|
194 return script()->functionNonDelazifying()->nargs(); |
|
195 } |
|
196 Value &thisValue() const { |
|
197 return *(Value *)(reinterpret_cast<const uint8_t *>(this) + |
|
198 BaselineFrame::Size() + |
|
199 offsetOfThis()); |
|
200 } |
|
201 Value *argv() const { |
|
202 return (Value *)(reinterpret_cast<const uint8_t *>(this) + |
|
203 BaselineFrame::Size() + |
|
204 offsetOfArg(0)); |
|
205 } |
|
206 |
|
207 bool copyRawFrameSlots(AutoValueVector *vec) const; |
|
208 |
|
209 bool hasReturnValue() const { |
|
210 return flags_ & HAS_RVAL; |
|
211 } |
|
212 MutableHandleValue returnValue() { |
|
213 return MutableHandleValue::fromMarkedLocation(reinterpret_cast<Value *>(&loReturnValue_)); |
|
214 } |
|
215 void setReturnValue(const Value &v) { |
|
216 flags_ |= HAS_RVAL; |
|
217 returnValue().set(v); |
|
218 } |
|
219 inline Value *addressOfReturnValue() { |
|
220 return reinterpret_cast<Value *>(&loReturnValue_); |
|
221 } |
|
222 |
|
223 bool hasCallObj() const { |
|
224 return flags_ & HAS_CALL_OBJ; |
|
225 } |
|
226 |
|
227 inline CallObject &callObj() const; |
|
228 |
|
229 void setFlags(uint32_t flags) { |
|
230 flags_ = flags; |
|
231 } |
|
232 uint32_t *addressOfFlags() { |
|
233 return &flags_; |
|
234 } |
|
235 |
|
236 inline bool pushBlock(JSContext *cx, Handle<StaticBlockObject *> block); |
|
237 inline void popBlock(JSContext *cx); |
|
238 |
|
239 bool strictEvalPrologue(JSContext *cx); |
|
240 bool heavyweightFunPrologue(JSContext *cx); |
|
241 bool initFunctionScopeObjects(JSContext *cx); |
|
242 |
|
243 void initArgsObjUnchecked(ArgumentsObject &argsobj) { |
|
244 flags_ |= HAS_ARGS_OBJ; |
|
245 argsObj_ = &argsobj; |
|
246 } |
|
247 void initArgsObj(ArgumentsObject &argsobj) { |
|
248 JS_ASSERT(script()->needsArgsObj()); |
|
249 initArgsObjUnchecked(argsobj); |
|
250 } |
|
251 bool hasArgsObj() const { |
|
252 return flags_ & HAS_ARGS_OBJ; |
|
253 } |
|
254 ArgumentsObject &argsObj() const { |
|
255 JS_ASSERT(hasArgsObj()); |
|
256 JS_ASSERT(script()->needsArgsObj()); |
|
257 return *argsObj_; |
|
258 } |
|
259 |
|
260 bool prevUpToDate() const { |
|
261 return flags_ & PREV_UP_TO_DATE; |
|
262 } |
|
263 void setPrevUpToDate() { |
|
264 flags_ |= PREV_UP_TO_DATE; |
|
265 } |
|
266 |
|
267 JSScript *evalScript() const { |
|
268 JS_ASSERT(isEvalFrame()); |
|
269 return evalScript_; |
|
270 } |
|
271 |
|
272 bool hasHookData() const { |
|
273 return flags_ & HAS_HOOK_DATA; |
|
274 } |
|
275 |
|
276 void *maybeHookData() const { |
|
277 return hasHookData() ? hookData_ : nullptr; |
|
278 } |
|
279 |
|
280 void setHookData(void *v) { |
|
281 hookData_ = v; |
|
282 flags_ |= HAS_HOOK_DATA; |
|
283 } |
|
284 |
|
285 bool hasPushedSPSFrame() const { |
|
286 return flags_ & HAS_PUSHED_SPS_FRAME; |
|
287 } |
|
288 |
|
289 void setPushedSPSFrame() { |
|
290 flags_ |= HAS_PUSHED_SPS_FRAME; |
|
291 } |
|
292 |
|
293 void unsetPushedSPSFrame() { |
|
294 flags_ &= ~HAS_PUSHED_SPS_FRAME; |
|
295 } |
|
296 |
|
297 bool overRecursed() const { |
|
298 return flags_ & OVER_RECURSED; |
|
299 } |
|
300 |
|
301 void setOverRecursed() { |
|
302 flags_ |= OVER_RECURSED; |
|
303 } |
|
304 |
|
305 BaselineDebugModeOSRInfo *debugModeOSRInfo() { |
|
306 MOZ_ASSERT(flags_ & HAS_DEBUG_MODE_OSR_INFO); |
|
307 return *reinterpret_cast<BaselineDebugModeOSRInfo **>(&loScratchValue_); |
|
308 } |
|
309 |
|
310 BaselineDebugModeOSRInfo *getDebugModeOSRInfo() { |
|
311 if (flags_ & HAS_DEBUG_MODE_OSR_INFO) |
|
312 return debugModeOSRInfo(); |
|
313 return nullptr; |
|
314 } |
|
315 |
|
316 void setDebugModeOSRInfo(BaselineDebugModeOSRInfo *info) { |
|
317 flags_ |= HAS_DEBUG_MODE_OSR_INFO; |
|
318 *reinterpret_cast<BaselineDebugModeOSRInfo **>(&loScratchValue_) = info; |
|
319 } |
|
320 |
|
321 void deleteDebugModeOSRInfo(); |
|
322 |
|
323 void trace(JSTracer *trc, JitFrameIterator &frame); |
|
324 |
|
325 bool isFunctionFrame() const { |
|
326 return CalleeTokenIsFunction(calleeToken()); |
|
327 } |
|
328 bool isGlobalFrame() const { |
|
329 return !CalleeTokenIsFunction(calleeToken()); |
|
330 } |
|
331 bool isEvalFrame() const { |
|
332 return flags_ & EVAL; |
|
333 } |
|
334 bool isStrictEvalFrame() const { |
|
335 return isEvalFrame() && script()->strict(); |
|
336 } |
|
337 bool isNonStrictEvalFrame() const { |
|
338 return isEvalFrame() && !script()->strict(); |
|
339 } |
|
340 bool isDirectEvalFrame() const { |
|
341 return isEvalFrame() && script()->staticLevel() > 0; |
|
342 } |
|
343 bool isNonStrictDirectEvalFrame() const { |
|
344 return isNonStrictEvalFrame() && isDirectEvalFrame(); |
|
345 } |
|
346 bool isNonEvalFunctionFrame() const { |
|
347 return isFunctionFrame() && !isEvalFrame(); |
|
348 } |
|
349 bool isDebuggerFrame() const { |
|
350 return false; |
|
351 } |
|
352 bool isGeneratorFrame() const { |
|
353 return false; |
|
354 } |
|
355 |
|
356 IonJSFrameLayout *framePrefix() const { |
|
357 uint8_t *fp = (uint8_t *)this + Size() + FramePointerOffset; |
|
358 return (IonJSFrameLayout *)fp; |
|
359 } |
|
360 |
|
361 // Methods below are used by the compiler. |
|
362 static size_t offsetOfCalleeToken() { |
|
363 return FramePointerOffset + js::jit::IonJSFrameLayout::offsetOfCalleeToken(); |
|
364 } |
|
365 static size_t offsetOfThis() { |
|
366 return FramePointerOffset + js::jit::IonJSFrameLayout::offsetOfThis(); |
|
367 } |
|
368 static size_t offsetOfArg(size_t index) { |
|
369 return FramePointerOffset + js::jit::IonJSFrameLayout::offsetOfActualArg(index); |
|
370 } |
|
371 static size_t offsetOfNumActualArgs() { |
|
372 return FramePointerOffset + js::jit::IonJSFrameLayout::offsetOfNumActualArgs(); |
|
373 } |
|
374 static size_t Size() { |
|
375 return sizeof(BaselineFrame); |
|
376 } |
|
377 |
|
378 // The reverseOffsetOf methods below compute the offset relative to the |
|
379 // frame's base pointer. Since the stack grows down, these offsets are |
|
380 // negative. |
|
381 static int reverseOffsetOfFrameSize() { |
|
382 return -int(Size()) + offsetof(BaselineFrame, frameSize_); |
|
383 } |
|
384 static int reverseOffsetOfScratchValue() { |
|
385 return -int(Size()) + offsetof(BaselineFrame, loScratchValue_); |
|
386 } |
|
387 static int reverseOffsetOfScopeChain() { |
|
388 return -int(Size()) + offsetof(BaselineFrame, scopeChain_); |
|
389 } |
|
390 static int reverseOffsetOfArgsObj() { |
|
391 return -int(Size()) + offsetof(BaselineFrame, argsObj_); |
|
392 } |
|
393 static int reverseOffsetOfFlags() { |
|
394 return -int(Size()) + offsetof(BaselineFrame, flags_); |
|
395 } |
|
396 static int reverseOffsetOfEvalScript() { |
|
397 return -int(Size()) + offsetof(BaselineFrame, evalScript_); |
|
398 } |
|
399 static int reverseOffsetOfReturnValue() { |
|
400 return -int(Size()) + offsetof(BaselineFrame, loReturnValue_); |
|
401 } |
|
402 static int reverseOffsetOfLocal(size_t index) { |
|
403 return -int(Size()) - (index + 1) * sizeof(Value); |
|
404 } |
|
405 }; |
|
406 |
|
407 // Ensure the frame is 8-byte aligned (required on ARM). |
|
408 JS_STATIC_ASSERT(((sizeof(BaselineFrame) + BaselineFrame::FramePointerOffset) % 8) == 0); |
|
409 |
|
410 } // namespace jit |
|
411 } // namespace js |
|
412 |
|
413 #endif // JS_ION |
|
414 |
|
415 #endif /* jit_BaselineFrame_h */ |