BaselineFrame.h (14962B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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 <algorithm> 11 12 #include "jit/CalleeToken.h" 13 #include "jit/JitFrames.h" 14 #include "jit/ScriptFromCalleeToken.h" 15 #include "vm/Stack.h" 16 17 namespace js { 18 namespace jit { 19 20 class ICEntry; 21 class ICScript; 22 class JSJitFrameIter; 23 24 // The stack looks like this, fp is the frame pointer: 25 // 26 // fp+y arguments 27 // fp => JitFrameLayout (frame header) 28 // fp-x BaselineFrame 29 // locals 30 // stack values 31 32 class BaselineFrame { 33 public: 34 enum Flags : uint32_t { 35 // The frame has a valid return value. See also InterpreterFrame::HAS_RVAL. 36 HAS_RVAL = 1 << 0, 37 38 // The frame is running in the Baseline interpreter instead of JIT. 39 RUNNING_IN_INTERPRETER = 1 << 1, 40 41 // An initial environment has been pushed on the environment chain for 42 // function frames that need a CallObject or eval frames that need a 43 // VarEnvironmentObject. 44 HAS_INITIAL_ENV = 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 // Frame has execution observed by a Debugger. 53 // 54 // See comment above 'isDebuggee' in vm/Realm.h for explanation 55 // of invariants of debuggee compartments, scripts, and frames. 56 DEBUGGEE = 1 << 6, 57 58 // Frame is executing Realm-independent Jitcode, which requires 59 // a valid interpreterScript_ field. 60 REALM_INDEPENDENT = 1 << 7, 61 }; 62 63 protected: // Silence Clang warning about unused private fields. 64 // The fields below are only valid if RUNNING_IN_INTERPRETER or 65 // isRealmIndependent(). 66 JSScript* interpreterScript_; 67 // The fields below are only valid if RUNNING_IN_INTERPRETER. 68 jsbytecode* interpreterPC_; 69 ICEntry* interpreterICEntry_; 70 71 JSObject* envChain_; // Environment chain (always initialized). 72 ICScript* icScript_; // IC script (initialized if Warp is enabled). 73 ArgumentsObject* argsObj_; // If HAS_ARGS_OBJ, the arguments object. 74 75 // We need to split the Value into 2 fields of 32 bits, otherwise the C++ 76 // compiler may add some padding between the fields. 77 uint32_t loScratchValue_; 78 uint32_t hiScratchValue_; 79 uint32_t flags_; 80 #ifdef DEBUG 81 // Size of the frame. Stored in DEBUG builds when calling into C++. This is 82 // BaselineFrame::Size() + the size of the local and expression stack Values. 83 // 84 // We don't store this in release builds because it's redundant with the frame 85 // size computed from the frame pointers. In debug builds it's still useful 86 // for assertions. 87 uint32_t debugFrameSize_; 88 #else 89 uint32_t unused_; 90 #endif 91 uint32_t loReturnValue_; // If HAS_RVAL, the frame's return value. 92 uint32_t hiReturnValue_; 93 94 public: 95 [[nodiscard]] bool initForOsr(InterpreterFrame* fp, uint32_t numStackValues); 96 97 #ifdef DEBUG 98 uint32_t debugFrameSize() const { return debugFrameSize_; } 99 void setDebugFrameSize(uint32_t frameSize) { debugFrameSize_ = frameSize; } 100 #endif 101 102 JSObject* environmentChain() const { return envChain_; } 103 void setEnvironmentChain(JSObject* envChain) { envChain_ = envChain; } 104 105 template <typename SpecificEnvironment> 106 inline void pushOnEnvironmentChain(SpecificEnvironment& env); 107 template <typename SpecificEnvironment> 108 inline void popOffEnvironmentChain(); 109 inline void replaceInnermostEnvironment(EnvironmentObject& env); 110 111 CalleeToken calleeToken() const { return framePrefix()->calleeToken(); } 112 void replaceCalleeToken(CalleeToken token) { 113 framePrefix()->replaceCalleeToken(token); 114 } 115 bool isConstructing() const { 116 return CalleeTokenIsConstructing(calleeToken()); 117 } 118 JSScript* script() const { 119 return MaybeForwardedScriptFromCalleeToken(calleeToken()); 120 } 121 JSFunction* callee() const { return CalleeTokenToFunction(calleeToken()); } 122 Value calleev() const { return ObjectValue(*callee()); } 123 124 size_t numValueSlots(size_t frameSize) const { 125 MOZ_ASSERT(frameSize == debugFrameSize()); 126 127 MOZ_ASSERT(frameSize >= BaselineFrame::Size()); 128 frameSize -= BaselineFrame::Size(); 129 130 MOZ_ASSERT((frameSize % sizeof(Value)) == 0); 131 return frameSize / sizeof(Value); 132 } 133 134 Value newTarget() const { 135 MOZ_ASSERT(isFunctionFrame()); 136 MOZ_ASSERT(!callee()->isArrow()); 137 138 if (isConstructing()) { 139 unsigned pushedArgs = std::max(numFormalArgs(), numActualArgs()); 140 return argv()[pushedArgs]; 141 } 142 return UndefinedValue(); 143 } 144 145 #ifdef DEBUG 146 size_t debugNumValueSlots() const { return numValueSlots(debugFrameSize()); } 147 #endif 148 149 Value* valueSlot(size_t slot) const { 150 #ifndef ENABLE_PORTABLE_BASELINE_INTERP 151 // Assert that we're within the frame, but only if the "debug 152 // frame size" has been set. Ordinarily if we are in C++ code 153 // looking upward at a baseline frame, it will be, because it is 154 // set for the *previous* frame when we push an exit frame and 155 // call back into C++ from generated baseline code. However, the 156 // portable baseline interpreter uses accessors on BaselineFrame 157 // directly within the active frame and so the "debug frame size" 158 // hasn't been set (and it would be expensive to constantly update 159 // it). Because this is only used for assertions, and is not 160 // needed for correctness, we can disable this check below when 161 // PBL is enabled. 162 MOZ_ASSERT(slot < debugNumValueSlots()); 163 #endif 164 return (Value*)this - (slot + 1); 165 } 166 167 static size_t frameSizeForNumValueSlots(size_t numValueSlots) { 168 return BaselineFrame::Size() + numValueSlots * sizeof(Value); 169 } 170 171 Value& unaliasedFormal( 172 unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { 173 MOZ_ASSERT(i < numFormalArgs()); 174 MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() && 175 !script()->formalIsAliased(i)); 176 return argv()[i]; 177 } 178 179 Value& unaliasedActual( 180 unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { 181 MOZ_ASSERT(i < numActualArgs()); 182 MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals()); 183 MOZ_ASSERT_IF(checkAliasing && i < numFormalArgs(), 184 !script()->formalIsAliased(i)); 185 return argv()[i]; 186 } 187 188 Value& unaliasedLocal(uint32_t i) const { 189 MOZ_ASSERT(i < script()->nfixed()); 190 return *valueSlot(i); 191 } 192 193 unsigned numActualArgs() const { return framePrefix()->numActualArgs(); } 194 unsigned numFormalArgs() const { return script()->function()->nargs(); } 195 Value& thisArgument() const { 196 MOZ_ASSERT(isFunctionFrame()); 197 return framePrefix()->thisv(); 198 } 199 Value* argv() const { return framePrefix()->actualArgs(); } 200 201 [[nodiscard]] bool saveGeneratorSlots(JSContext* cx, unsigned nslots, 202 ArrayObject* dest) const; 203 204 public: 205 void prepareForBaselineInterpreterToJitOSR() { 206 // Clearing the RUNNING_IN_INTERPRETER flag is sufficient, but we also null 207 // out the interpreter fields to ensure we don't use stale values. 208 flags_ &= ~RUNNING_IN_INTERPRETER; 209 if (JS::Prefs::experimental_self_hosted_cache() && script()->selfHosted()) { 210 flags_ |= REALM_INDEPENDENT; 211 } 212 interpreterPC_ = nullptr; 213 } 214 215 private: 216 bool uninlineIsProfilerSamplingEnabled(JSContext* cx); 217 218 public: 219 // Switch a JIT frame on the stack to Interpreter mode. The caller is 220 // responsible for patching the return address into this frame to a location 221 // in the interpreter code. Also assert profiler sampling has been suppressed 222 // so the sampler thread doesn't see an inconsistent state while we are 223 // patching frames. 224 void switchFromJitToInterpreter(JSContext* cx, jsbytecode* pc) { 225 MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx)); 226 MOZ_ASSERT(!runningInInterpreter()); 227 flags_ |= RUNNING_IN_INTERPRETER; 228 setInterpreterFields(pc); 229 } 230 void switchFromJitToInterpreterAtPrologue(JSContext* cx) { 231 MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx)); 232 MOZ_ASSERT(!runningInInterpreter()); 233 flags_ |= RUNNING_IN_INTERPRETER; 234 setInterpreterFieldsForPrologue(script()); 235 } 236 237 // Like switchFromJitToInterpreter, but set the interpreterICEntry_ field to 238 // nullptr. Initializing this field requires a binary search on the 239 // JitScript's ICEntry list but the exception handler never returns to this 240 // pc anyway so we can avoid the overhead. 241 void switchFromJitToInterpreterForExceptionHandler(JSContext* cx, 242 jsbytecode* pc) { 243 MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx)); 244 MOZ_ASSERT(!runningInInterpreter()); 245 flags_ |= RUNNING_IN_INTERPRETER; 246 interpreterScript_ = script(); 247 interpreterPC_ = pc; 248 interpreterICEntry_ = nullptr; 249 } 250 251 bool runningInInterpreter() const { return flags_ & RUNNING_IN_INTERPRETER; } 252 bool isRealmIndependent() const { return flags_ & REALM_INDEPENDENT; } 253 254 JSScript* interpreterScript() const { 255 MOZ_ASSERT(runningInInterpreter()); 256 return interpreterScript_; 257 } 258 259 jsbytecode* interpreterPC() const { 260 MOZ_ASSERT(runningInInterpreter()); 261 return interpreterPC_; 262 } 263 jsbytecode*& interpreterPC() { 264 MOZ_ASSERT(runningInInterpreter()); 265 return interpreterPC_; 266 } 267 268 ICEntry* interpreterICEntry() const { 269 MOZ_ASSERT(runningInInterpreter()); 270 return interpreterICEntry_; 271 } 272 ICEntry*& interpreterICEntry() { 273 MOZ_ASSERT(runningInInterpreter()); 274 return interpreterICEntry_; 275 } 276 277 void setInterpreterFields(JSScript* script, jsbytecode* pc); 278 279 void setInterpreterFields(jsbytecode* pc) { 280 setInterpreterFields(script(), pc); 281 } 282 283 // Initialize interpreter fields for resuming in the prologue (before the 284 // argument type check ICs). 285 void setInterpreterFieldsForPrologue(JSScript* script); 286 287 ICScript* icScript() const { return icScript_; } 288 void setICScript(ICScript* icScript) { icScript_ = icScript; } 289 290 // The script that owns the current ICScript. 291 JSScript* outerScript() const; 292 293 bool hasReturnValue() const { return flags_ & HAS_RVAL; } 294 MutableHandleValue returnValue() { 295 if (!hasReturnValue()) { 296 addressOfReturnValue()->setUndefined(); 297 } 298 return MutableHandleValue::fromMarkedLocation(addressOfReturnValue()); 299 } 300 void setReturnValue(const Value& v) { 301 returnValue().set(v); 302 flags_ |= HAS_RVAL; 303 } 304 inline Value* addressOfReturnValue() { 305 return reinterpret_cast<Value*>(&loReturnValue_); 306 } 307 308 bool hasInitialEnvironment() const { return flags_ & HAS_INITIAL_ENV; } 309 310 inline CallObject& callObj() const; 311 312 void setFlag(uint32_t flag) { flags_ |= flag; } 313 void setFlags(uint32_t flags) { flags_ = flags; } 314 315 [[nodiscard]] inline bool pushLexicalEnvironment(JSContext* cx, 316 Handle<LexicalScope*> scope); 317 template <bool IsDebuggee> 318 [[nodiscard]] inline bool freshenLexicalEnvironment( 319 JSContext* cx, const jsbytecode* pc = nullptr); 320 template <bool IsDebuggee> 321 [[nodiscard]] inline bool recreateLexicalEnvironment( 322 JSContext* cx, const jsbytecode* pc = nullptr); 323 324 [[nodiscard]] bool initFunctionEnvironmentObjects(JSContext* cx); 325 [[nodiscard]] bool pushClassBodyEnvironment(JSContext* cx, 326 Handle<ClassBodyScope*> scope); 327 [[nodiscard]] bool pushVarEnvironment(JSContext* cx, Handle<Scope*> scope); 328 329 void initArgsObjUnchecked(ArgumentsObject& argsobj) { 330 flags_ |= HAS_ARGS_OBJ; 331 argsObj_ = &argsobj; 332 } 333 void initArgsObj(ArgumentsObject& argsobj) { 334 MOZ_ASSERT(script()->needsArgsObj()); 335 initArgsObjUnchecked(argsobj); 336 } 337 bool hasArgsObj() const { return flags_ & HAS_ARGS_OBJ; } 338 ArgumentsObject& argsObj() const { 339 MOZ_ASSERT(hasArgsObj()); 340 MOZ_ASSERT(script()->needsArgsObj()); 341 return *argsObj_; 342 } 343 344 bool prevUpToDate() const { return flags_ & PREV_UP_TO_DATE; } 345 void setPrevUpToDate() { flags_ |= PREV_UP_TO_DATE; } 346 void unsetPrevUpToDate() { flags_ &= ~PREV_UP_TO_DATE; } 347 348 bool isDebuggee() const { return flags_ & DEBUGGEE; } 349 void setIsDebuggee() { flags_ |= DEBUGGEE; } 350 inline void unsetIsDebuggee(); 351 352 void trace(JSTracer* trc, const JSJitFrameIter& frame); 353 354 bool isGlobalFrame() const { return script()->isGlobalCode(); } 355 bool isModuleFrame() const { return script()->isModule(); } 356 bool isEvalFrame() const { return script()->isForEval(); } 357 bool isFunctionFrame() const { 358 return CalleeTokenIsFunction(calleeToken()) && !isModuleFrame(); 359 } 360 bool isDebuggerEvalFrame() const { return false; } 361 362 JitFrameLayout* framePrefix() const { 363 uint8_t* fp = (uint8_t*)this + Size(); 364 return (JitFrameLayout*)fp; 365 } 366 367 static size_t Size() { return sizeof(BaselineFrame); } 368 369 // The reverseOffsetOf methods below compute the offset relative to the 370 // frame's base pointer. Since the stack grows down, these offsets are 371 // negative. 372 373 #ifdef DEBUG 374 static int reverseOffsetOfDebugFrameSize() { 375 return -int(Size()) + offsetof(BaselineFrame, debugFrameSize_); 376 } 377 #endif 378 379 // The scratch value slot can either be used as a Value slot or as two 380 // separate 32-bit integer slots. 381 static int reverseOffsetOfScratchValueLow32() { 382 return -int(Size()) + offsetof(BaselineFrame, loScratchValue_); 383 } 384 static int reverseOffsetOfScratchValueHigh32() { 385 return -int(Size()) + offsetof(BaselineFrame, hiScratchValue_); 386 } 387 static int reverseOffsetOfScratchValue() { 388 return reverseOffsetOfScratchValueLow32(); 389 } 390 391 static int reverseOffsetOfEnvironmentChain() { 392 return -int(Size()) + offsetof(BaselineFrame, envChain_); 393 } 394 static int reverseOffsetOfArgsObj() { 395 return -int(Size()) + offsetof(BaselineFrame, argsObj_); 396 } 397 static int reverseOffsetOfFlags() { 398 return -int(Size()) + offsetof(BaselineFrame, flags_); 399 } 400 static int reverseOffsetOfReturnValue() { 401 return -int(Size()) + offsetof(BaselineFrame, loReturnValue_); 402 } 403 static int reverseOffsetOfInterpreterScript() { 404 return -int(Size()) + offsetof(BaselineFrame, interpreterScript_); 405 } 406 static int reverseOffsetOfInterpreterPC() { 407 return -int(Size()) + offsetof(BaselineFrame, interpreterPC_); 408 } 409 static int reverseOffsetOfInterpreterICEntry() { 410 return -int(Size()) + offsetof(BaselineFrame, interpreterICEntry_); 411 } 412 static int reverseOffsetOfICScript() { 413 return -int(Size()) + offsetof(BaselineFrame, icScript_); 414 } 415 static int reverseOffsetOfLocal(size_t index) { 416 return -int(Size()) - (index + 1) * sizeof(Value); 417 } 418 }; 419 420 // Ensure the frame is 8-byte aligned (required on ARM). 421 static_assert((sizeof(BaselineFrame) % 8) == 0, "frame must be 8-byte aligned"); 422 423 } // namespace jit 424 } // namespace js 425 426 #endif /* jit_BaselineFrame_h */