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 #include "jit/JitFrames.h"
11 #include "vm/Stack.h"
12 
13 namespace js {
14 namespace jit {
15 
16 struct BaselineDebugModeOSRInfo;
17 
18 // The stack looks like this, fp is the frame pointer:
19 //
20 // fp+y   arguments
21 // fp+x   JitFrameLayout (frame header)
22 // fp  => saved frame pointer
23 // fp-x   BaselineFrame
24 //        locals
25 //        stack values
26 
27 class BaselineFrame {
28  public:
29   enum Flags : uint32_t {
30     // The frame has a valid return value. See also InterpreterFrame::HAS_RVAL.
31     HAS_RVAL = 1 << 0,
32 
33     // An initial environment has been pushed on the environment chain for
34     // function frames that need a CallObject or eval frames that need a
35     // VarEnvironmentObject.
36     HAS_INITIAL_ENV = 1 << 2,
37 
38     // Frame has an arguments object, argsObj_.
39     HAS_ARGS_OBJ = 1 << 4,
40 
41     // See InterpreterFrame::PREV_UP_TO_DATE.
42     PREV_UP_TO_DATE = 1 << 5,
43 
44     // Frame has execution observed by a Debugger.
45     //
46     // See comment above 'isDebuggee' in vm/JSCompartment.h for explanation
47     // of invariants of debuggee compartments, scripts, and frames.
48     DEBUGGEE = 1 << 6,
49 
50     // (1 << 7 and 1 << 8 are unused)
51 
52     // Frame has over-recursed on an early check.
53     OVER_RECURSED = 1 << 9,
54 
55     // Frame has a BaselineRecompileInfo stashed in the scratch value
56     // slot. See PatchBaselineFramesForDebugMode.
57     HAS_DEBUG_MODE_OSR_INFO = 1 << 10,
58 
59     // This flag is intended for use whenever the frame is settled on a
60     // native code address without a corresponding ICEntry. In this case,
61     // the frame contains an explicit bytecode offset for frame iterators.
62     //
63     // There can also be an override pc if the frame has had its
64     // environment chain unwound to a pc during exception handling that is
65     // different from its current pc.
66     //
67     // This flag should never be set when we're executing JIT code.
68     HAS_OVERRIDE_PC = 1 << 11,
69 
70     // If set, we're handling an exception for this frame. This is set for
71     // debug mode OSR sanity checking when it handles corner cases which
72     // only arise during exception handling.
73     HANDLING_EXCEPTION = 1 << 12,
74   };
75 
76  protected:  // Silence Clang warning about unused private fields.
77   // We need to split the Value into 2 fields of 32 bits, otherwise the C++
78   // compiler may add some padding between the fields.
79 
80   union {
81     struct {
82       uint32_t loScratchValue_;
83       uint32_t hiScratchValue_;
84     };
85     BaselineDebugModeOSRInfo* debugModeOSRInfo_;
86   };
87   uint32_t loReturnValue_;  // If HAS_RVAL, the frame's return value.
88   uint32_t hiReturnValue_;
89   uint32_t frameSize_;
90   JSObject* envChain_;        // Environment chain (always initialized).
91   ArgumentsObject* argsObj_;  // If HAS_ARGS_OBJ, the arguments object.
92   uint32_t overrideOffset_;   // If HAS_OVERRIDE_PC, the bytecode offset.
93   uint32_t flags_;
94 
95  public:
96   // Distance between the frame pointer and the frame header (return address).
97   // This is the old frame pointer saved in the prologue.
98   static const uint32_t FramePointerOffset = sizeof(void*);
99 
100   MOZ_MUST_USE bool initForOsr(InterpreterFrame* fp, uint32_t numStackValues);
101 
frameSize()102   uint32_t frameSize() const { return frameSize_; }
setFrameSize(uint32_t frameSize)103   void setFrameSize(uint32_t frameSize) { frameSize_ = frameSize; }
addressOfFrameSize()104   inline uint32_t* addressOfFrameSize() { return &frameSize_; }
environmentChain()105   JSObject* environmentChain() const { return envChain_; }
setEnvironmentChain(JSObject * envChain)106   void setEnvironmentChain(JSObject* envChain) { envChain_ = envChain; }
addressOfEnvironmentChain()107   inline JSObject** addressOfEnvironmentChain() { return &envChain_; }
108 
addressOfScratchValue()109   inline Value* addressOfScratchValue() {
110     return reinterpret_cast<Value*>(&loScratchValue_);
111   }
112 
113   template <typename SpecificEnvironment>
114   inline void pushOnEnvironmentChain(SpecificEnvironment& env);
115   template <typename SpecificEnvironment>
116   inline void popOffEnvironmentChain();
117   inline void replaceInnermostEnvironment(EnvironmentObject& env);
118 
calleeToken()119   CalleeToken calleeToken() const {
120     uint8_t* pointer = (uint8_t*)this + Size() + offsetOfCalleeToken();
121     return *(CalleeToken*)pointer;
122   }
replaceCalleeToken(CalleeToken token)123   void replaceCalleeToken(CalleeToken token) {
124     uint8_t* pointer = (uint8_t*)this + Size() + offsetOfCalleeToken();
125     *(CalleeToken*)pointer = token;
126   }
isConstructing()127   bool isConstructing() const {
128     return CalleeTokenIsConstructing(calleeToken());
129   }
script()130   JSScript* script() const { return ScriptFromCalleeToken(calleeToken()); }
callee()131   JSFunction* callee() const { return CalleeTokenToFunction(calleeToken()); }
calleev()132   Value calleev() const { return ObjectValue(*callee()); }
numValueSlots()133   size_t numValueSlots() const {
134     size_t size = frameSize();
135 
136     MOZ_ASSERT(size >=
137                BaselineFrame::FramePointerOffset + BaselineFrame::Size());
138     size -= BaselineFrame::FramePointerOffset + BaselineFrame::Size();
139 
140     MOZ_ASSERT((size % sizeof(Value)) == 0);
141     return size / sizeof(Value);
142   }
valueSlot(size_t slot)143   Value* valueSlot(size_t slot) const {
144     MOZ_ASSERT(slot < numValueSlots());
145     return (Value*)this - (slot + 1);
146   }
147 
148   Value& unaliasedFormal(
149       unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
150     MOZ_ASSERT(i < numFormalArgs());
151     MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() &&
152                                      !script()->formalIsAliased(i));
153     return argv()[i];
154   }
155 
156   Value& unaliasedActual(
157       unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
158     MOZ_ASSERT(i < numActualArgs());
159     MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
160     MOZ_ASSERT_IF(checkAliasing && i < numFormalArgs(),
161                   !script()->formalIsAliased(i));
162     return argv()[i];
163   }
164 
unaliasedLocal(uint32_t i)165   Value& unaliasedLocal(uint32_t i) const {
166     MOZ_ASSERT(i < script()->nfixed());
167     return *valueSlot(i);
168   }
169 
numActualArgs()170   unsigned numActualArgs() const {
171     return *(size_t*)(reinterpret_cast<const uint8_t*>(this) +
172                       BaselineFrame::Size() + offsetOfNumActualArgs());
173   }
numFormalArgs()174   unsigned numFormalArgs() const {
175     return script()->functionNonDelazifying()->nargs();
176   }
thisArgument()177   Value& thisArgument() const {
178     MOZ_ASSERT(isFunctionFrame());
179     return *(Value*)(reinterpret_cast<const uint8_t*>(this) +
180                      BaselineFrame::Size() + offsetOfThis());
181   }
argv()182   Value* argv() const {
183     return (Value*)(reinterpret_cast<const uint8_t*>(this) +
184                     BaselineFrame::Size() + offsetOfArg(0));
185   }
186 
187  private:
evalNewTargetAddress()188   Value* evalNewTargetAddress() const {
189     MOZ_ASSERT(isEvalFrame());
190     MOZ_ASSERT(script()->isDirectEvalInFunction());
191     return (Value*)(reinterpret_cast<const uint8_t*>(this) +
192                     BaselineFrame::Size() + offsetOfEvalNewTarget());
193   }
194 
195  public:
newTarget()196   Value newTarget() const {
197     if (isEvalFrame()) return *evalNewTargetAddress();
198     MOZ_ASSERT(isFunctionFrame());
199     if (callee()->isArrow())
200       return callee()->getExtendedSlot(FunctionExtended::ARROW_NEWTARGET_SLOT);
201     if (isConstructing()) {
202       return *(Value*)(reinterpret_cast<const uint8_t*>(this) +
203                        BaselineFrame::Size() +
204                        offsetOfArg(Max(numFormalArgs(), numActualArgs())));
205     }
206     return UndefinedValue();
207   }
208 
hasReturnValue()209   bool hasReturnValue() const { return flags_ & HAS_RVAL; }
returnValue()210   MutableHandleValue returnValue() {
211     if (!hasReturnValue()) addressOfReturnValue()->setUndefined();
212     return MutableHandleValue::fromMarkedLocation(addressOfReturnValue());
213   }
setReturnValue(const Value & v)214   void setReturnValue(const Value& v) {
215     returnValue().set(v);
216     flags_ |= HAS_RVAL;
217   }
addressOfReturnValue()218   inline Value* addressOfReturnValue() {
219     return reinterpret_cast<Value*>(&loReturnValue_);
220   }
221 
hasInitialEnvironment()222   bool hasInitialEnvironment() const { return flags_ & HAS_INITIAL_ENV; }
223 
224   inline CallObject& callObj() const;
225 
setFlags(uint32_t flags)226   void setFlags(uint32_t flags) { flags_ = flags; }
addressOfFlags()227   uint32_t* addressOfFlags() { return &flags_; }
228 
229   inline MOZ_MUST_USE bool pushLexicalEnvironment(JSContext* cx,
230                                                   Handle<LexicalScope*> scope);
231   inline MOZ_MUST_USE bool freshenLexicalEnvironment(JSContext* cx);
232   inline MOZ_MUST_USE bool recreateLexicalEnvironment(JSContext* cx);
233 
234   MOZ_MUST_USE bool initFunctionEnvironmentObjects(JSContext* cx);
235   MOZ_MUST_USE bool pushVarEnvironment(JSContext* cx, HandleScope scope);
236 
initArgsObjUnchecked(ArgumentsObject & argsobj)237   void initArgsObjUnchecked(ArgumentsObject& argsobj) {
238     flags_ |= HAS_ARGS_OBJ;
239     argsObj_ = &argsobj;
240   }
initArgsObj(ArgumentsObject & argsobj)241   void initArgsObj(ArgumentsObject& argsobj) {
242     MOZ_ASSERT(script()->needsArgsObj());
243     initArgsObjUnchecked(argsobj);
244   }
hasArgsObj()245   bool hasArgsObj() const { return flags_ & HAS_ARGS_OBJ; }
argsObj()246   ArgumentsObject& argsObj() const {
247     MOZ_ASSERT(hasArgsObj());
248     MOZ_ASSERT(script()->needsArgsObj());
249     return *argsObj_;
250   }
251 
prevUpToDate()252   bool prevUpToDate() const { return flags_ & PREV_UP_TO_DATE; }
setPrevUpToDate()253   void setPrevUpToDate() { flags_ |= PREV_UP_TO_DATE; }
unsetPrevUpToDate()254   void unsetPrevUpToDate() { flags_ &= ~PREV_UP_TO_DATE; }
255 
isDebuggee()256   bool isDebuggee() const { return flags_ & DEBUGGEE; }
setIsDebuggee()257   void setIsDebuggee() { flags_ |= DEBUGGEE; }
258   inline void unsetIsDebuggee();
259 
isHandlingException()260   bool isHandlingException() const { return flags_ & HANDLING_EXCEPTION; }
setIsHandlingException()261   void setIsHandlingException() { flags_ |= HANDLING_EXCEPTION; }
unsetIsHandlingException()262   void unsetIsHandlingException() { flags_ &= ~HANDLING_EXCEPTION; }
263 
overRecursed()264   bool overRecursed() const { return flags_ & OVER_RECURSED; }
265 
setOverRecursed()266   void setOverRecursed() { flags_ |= OVER_RECURSED; }
267 
debugModeOSRInfo()268   BaselineDebugModeOSRInfo* debugModeOSRInfo() {
269     MOZ_ASSERT(flags_ & HAS_DEBUG_MODE_OSR_INFO);
270     return debugModeOSRInfo_;
271   }
272 
getDebugModeOSRInfo()273   BaselineDebugModeOSRInfo* getDebugModeOSRInfo() {
274     if (flags_ & HAS_DEBUG_MODE_OSR_INFO) return debugModeOSRInfo();
275     return nullptr;
276   }
277 
setDebugModeOSRInfo(BaselineDebugModeOSRInfo * info)278   void setDebugModeOSRInfo(BaselineDebugModeOSRInfo* info) {
279     flags_ |= HAS_DEBUG_MODE_OSR_INFO;
280     debugModeOSRInfo_ = info;
281   }
282 
283   void deleteDebugModeOSRInfo();
284 
285   // See the HAS_OVERRIDE_PC comment.
hasOverridePc()286   bool hasOverridePc() const { return flags_ & HAS_OVERRIDE_PC; }
287 
overridePc()288   jsbytecode* overridePc() const {
289     MOZ_ASSERT(hasOverridePc());
290     return script()->offsetToPC(overrideOffset_);
291   }
292 
maybeOverridePc()293   jsbytecode* maybeOverridePc() const {
294     if (hasOverridePc()) return overridePc();
295     return nullptr;
296   }
297 
setOverridePc(jsbytecode * pc)298   void setOverridePc(jsbytecode* pc) {
299     flags_ |= HAS_OVERRIDE_PC;
300     overrideOffset_ = script()->pcToOffset(pc);
301   }
302 
clearOverridePc()303   void clearOverridePc() { flags_ &= ~HAS_OVERRIDE_PC; }
304 
305   void trace(JSTracer* trc, const JSJitFrameIter& frame);
306 
isGlobalFrame()307   bool isGlobalFrame() const { return script()->isGlobalCode(); }
isModuleFrame()308   bool isModuleFrame() const { return script()->module(); }
isEvalFrame()309   bool isEvalFrame() const { return script()->isForEval(); }
isStrictEvalFrame()310   bool isStrictEvalFrame() const { return isEvalFrame() && script()->strict(); }
isNonStrictEvalFrame()311   bool isNonStrictEvalFrame() const {
312     return isEvalFrame() && !script()->strict();
313   }
314   bool isNonGlobalEvalFrame() const;
isNonStrictDirectEvalFrame()315   bool isNonStrictDirectEvalFrame() const {
316     return isNonStrictEvalFrame() && isNonGlobalEvalFrame();
317   }
isFunctionFrame()318   bool isFunctionFrame() const { return CalleeTokenIsFunction(calleeToken()); }
isDebuggerEvalFrame()319   bool isDebuggerEvalFrame() const { return false; }
320 
framePrefix()321   JitFrameLayout* framePrefix() const {
322     uint8_t* fp = (uint8_t*)this + Size() + FramePointerOffset;
323     return (JitFrameLayout*)fp;
324   }
325 
326   // Methods below are used by the compiler.
offsetOfCalleeToken()327   static size_t offsetOfCalleeToken() {
328     return FramePointerOffset + js::jit::JitFrameLayout::offsetOfCalleeToken();
329   }
offsetOfThis()330   static size_t offsetOfThis() {
331     return FramePointerOffset + js::jit::JitFrameLayout::offsetOfThis();
332   }
offsetOfEvalNewTarget()333   static size_t offsetOfEvalNewTarget() {
334     return FramePointerOffset +
335            js::jit::JitFrameLayout::offsetOfEvalNewTarget();
336   }
offsetOfArg(size_t index)337   static size_t offsetOfArg(size_t index) {
338     return FramePointerOffset +
339            js::jit::JitFrameLayout::offsetOfActualArg(index);
340   }
offsetOfNumActualArgs()341   static size_t offsetOfNumActualArgs() {
342     return FramePointerOffset +
343            js::jit::JitFrameLayout::offsetOfNumActualArgs();
344   }
Size()345   static size_t Size() { return sizeof(BaselineFrame); }
346 
347   // The reverseOffsetOf methods below compute the offset relative to the
348   // frame's base pointer. Since the stack grows down, these offsets are
349   // negative.
reverseOffsetOfFrameSize()350   static int reverseOffsetOfFrameSize() {
351     return -int(Size()) + offsetof(BaselineFrame, frameSize_);
352   }
reverseOffsetOfScratchValue()353   static int reverseOffsetOfScratchValue() {
354     return -int(Size()) + offsetof(BaselineFrame, loScratchValue_);
355   }
reverseOffsetOfEnvironmentChain()356   static int reverseOffsetOfEnvironmentChain() {
357     return -int(Size()) + offsetof(BaselineFrame, envChain_);
358   }
reverseOffsetOfArgsObj()359   static int reverseOffsetOfArgsObj() {
360     return -int(Size()) + offsetof(BaselineFrame, argsObj_);
361   }
reverseOffsetOfFlags()362   static int reverseOffsetOfFlags() {
363     return -int(Size()) + offsetof(BaselineFrame, flags_);
364   }
reverseOffsetOfReturnValue()365   static int reverseOffsetOfReturnValue() {
366     return -int(Size()) + offsetof(BaselineFrame, loReturnValue_);
367   }
reverseOffsetOfLocal(size_t index)368   static int reverseOffsetOfLocal(size_t index) {
369     return -int(Size()) - (index + 1) * sizeof(Value);
370   }
371 };
372 
373 // Ensure the frame is 8-byte aligned (required on ARM).
374 JS_STATIC_ASSERT(((sizeof(BaselineFrame) + BaselineFrame::FramePointerOffset) %
375                   8) == 0);
376 
377 }  // namespace jit
378 }  // namespace js
379 
380 #endif /* jit_BaselineFrame_h */
381