JSJitFrameIter.h (27232B)
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_JSJitFrameIter_h 8 #define jit_JSJitFrameIter_h 9 10 #include "mozilla/Maybe.h" 11 12 #include "jstypes.h" 13 14 #include "jit/JitCode.h" 15 #include "jit/MachineState.h" 16 #include "jit/Snapshots.h" 17 #include "js/ProfilingFrameIterator.h" 18 #include "vm/JSFunction.h" 19 #include "vm/JSScript.h" 20 21 namespace js { 22 23 class ArgumentsObject; 24 25 namespace jit { 26 27 enum class FrameType { 28 // A JS frame is analogous to a js::InterpreterFrame, representing one 29 // scripted function activation. IonJS frames are used by the optimizing 30 // compiler. 31 IonJS, 32 33 // JS frame used by the Baseline Interpreter and Baseline JIT. 34 BaselineJS, 35 36 // Frame pushed by Baseline stubs that make non-tail calls, so that the 37 // return address -> ICEntry mapping works. 38 BaselineStub, 39 40 // The entry frame is the initial prologue block transitioning from the VM 41 // into the Ion world. 42 CppToJSJit, 43 44 // This entry frame sits right before the baseline interpreter 45 // so that external profilers can identify which function is being 46 // interpreted. Only used under the --emit-interpreter-entry option. 47 BaselineInterpreterEntry, 48 49 // Ion IC calling a scripted getter/setter or a VMFunction. 50 IonICCall, 51 52 // An exit frame is necessary for transitioning from a JS frame into C++. 53 // From within C++, an exit frame is always the last frame in any 54 // JitActivation. 55 Exit, 56 57 // A bailout frame is a special IonJS jit frame after a bailout, and before 58 // the reconstruction of the BaselineJS frame. From within C++, a bailout 59 // frame is always the last frame in a JitActivation iff the bailout frame 60 // information is recorded on the JitActivation. 61 Bailout, 62 63 // A wasm to JS frame is constructed during fast calls from wasm to the JS 64 // jits, used as a marker to interleave JS jit and wasm frames. From the 65 // point of view of JS JITs, this is just another kind of entry frame. 66 WasmToJSJit, 67 68 // Frame for a TrampolineNative, a JS builtin implemented with a JIT 69 // trampoline. See jit/TrampolineNatives.h. 70 TrampolineNative, 71 }; 72 73 enum class ReadFrameArgsBehavior { 74 // Read all actual arguments. Will invoke the callback numActualArgs times. 75 Actuals, 76 77 // Read all argument values in the stack frame. Will invoke the callback 78 // max(numFormalArgs, numActualArgs) times. 79 ActualsAndFormals, 80 }; 81 82 class CommonFrameLayout; 83 class JitFrameLayout; 84 class ExitFrameLayout; 85 86 class BaselineFrame; 87 class JitActivation; 88 class SafepointIndex; 89 class OsiIndex; 90 91 // Iterate over the JIT stack to assert that all invariants are respected. 92 // - Check that all entry frames are aligned on JitStackAlignment. 93 94 void AssertJitStackInvariants(JSContext* cx); 95 96 // A JSJitFrameIter can iterate over a linear frame group of JS jit frames 97 // only. It will stop at the first frame that is not of the same kind, or at 98 // the end of an activation. 99 // 100 // If you want to handle every kind of frames (including wasm frames), use 101 // JitFrameIter. If you want to skip interleaved frames of other kinds, use 102 // OnlyJSJitFrameIter. 103 104 class JSJitFrameIter { 105 protected: 106 uint8_t* current_; 107 FrameType type_; 108 uint8_t* resumePCinCurrentFrame_ = nullptr; 109 110 // Size of the current Baseline frame. Equivalent to 111 // BaselineFrame::debugFrameSize_ in debug builds. 112 mozilla::Maybe<uint32_t> baselineFrameSize_; 113 114 private: 115 mutable const SafepointIndex* cachedSafepointIndex_ = nullptr; 116 const JitActivation* activation_; 117 118 void dumpBaseline() const; 119 120 public: 121 // See comment above the class. 122 explicit JSJitFrameIter(const JitActivation* activation); 123 124 // A constructor specialized for jit->wasm frames, which starts at a 125 // specific FP. 126 JSJitFrameIter(const JitActivation* activation, uint8_t* fp, bool unwinding); 127 128 void setResumePCInCurrentFrame(uint8_t* newAddr) { 129 resumePCinCurrentFrame_ = newAddr; 130 } 131 132 // Current frame information. 133 FrameType type() const { return type_; } 134 uint8_t* fp() const { return current_; } 135 const JitActivation* activation() const { return activation_; } 136 137 CommonFrameLayout* current() const { return (CommonFrameLayout*)current_; } 138 139 inline uint8_t* returnAddress() const; 140 141 // Return the pointer of the JitFrame, the iterator is assumed to be settled 142 // on a scripted frame. 143 JitFrameLayout* jsFrame() const; 144 145 inline ExitFrameLayout* exitFrame() const; 146 147 // Returns whether the JS frame has been invalidated and, if so, 148 // places the invalidated Ion script in |ionScript|. 149 bool checkInvalidation(IonScript** ionScript) const; 150 bool checkInvalidation() const; 151 152 bool isExitFrame() const { return type_ == FrameType::Exit; } 153 bool isScripted() const { 154 return type_ == FrameType::BaselineJS || type_ == FrameType::IonJS || 155 type_ == FrameType::Bailout; 156 } 157 bool isBaselineJS() const { return type_ == FrameType::BaselineJS; } 158 bool isIonScripted() const { 159 return type_ == FrameType::IonJS || type_ == FrameType::Bailout; 160 } 161 bool isIonJS() const { return type_ == FrameType::IonJS; } 162 bool isIonICCall() const { return type_ == FrameType::IonICCall; } 163 bool isBailoutJS() const { return type_ == FrameType::Bailout; } 164 bool isBaselineStub() const { return type_ == FrameType::BaselineStub; } 165 bool isBaselineInterpreterEntry() const { 166 return type_ == FrameType::BaselineInterpreterEntry; 167 } 168 bool isTrampolineNative() const { 169 return type_ == FrameType::TrampolineNative; 170 } 171 bool isBareExit() const; 172 bool isUnwoundJitExit() const; 173 template <typename T> 174 bool isExitFrameLayout() const; 175 176 static bool isEntry(FrameType type) { 177 return type == FrameType::CppToJSJit || type == FrameType::WasmToJSJit; 178 } 179 bool isEntry() const { return isEntry(type_); } 180 181 bool isFunctionFrame() const; 182 183 bool isConstructing() const; 184 185 void* calleeToken() const; 186 JSFunction* callee() const; 187 JSFunction* maybeCallee() const; 188 unsigned numActualArgs() const; 189 JSScript* script() const; 190 JSScript* maybeForwardedScript() const; 191 void baselineScriptAndPc(JSScript** scriptRes, jsbytecode** pcRes) const; 192 Value* actualArgs() const; 193 194 // Returns the address of the next instruction that will execute in this 195 // frame, once control returns to this frame. 196 uint8_t* resumePCinCurrentFrame() const { return resumePCinCurrentFrame_; } 197 198 // Previous frame information extracted from the current frame. 199 inline FrameType prevType() const; 200 uint8_t* prevFp() const; 201 202 // Functions used to iterate on frames. When prevType is an entry, 203 // the current frame is the last JS Jit frame. 204 bool done() const { return isEntry(); } 205 void operator++(); 206 207 // Returns the IonScript associated with this JS frame. 208 IonScript* ionScript() const; 209 210 // Returns the IonScript associated with this JS frame; the frame must 211 // not be invalidated. 212 IonScript* ionScriptFromCalleeToken() const; 213 214 // Returns the Safepoint associated with this JS frame. Incurs a lookup 215 // overhead. 216 const SafepointIndex* safepoint() const; 217 218 // Returns the OSI index associated with this JS frame. Incurs a lookup 219 // overhead. 220 const OsiIndex* osiIndex() const; 221 222 // Returns the Snapshot offset associated with this JS frame. Incurs a 223 // lookup overhead. 224 SnapshotOffset snapshotOffset() const; 225 226 uintptr_t* spillBase() const; 227 MachineState machineState() const; 228 229 template <class Op> 230 void unaliasedForEachActual(Op op) const { 231 MOZ_ASSERT(isBaselineJS()); 232 233 unsigned nactual = numActualArgs(); 234 Value* argv = actualArgs(); 235 for (unsigned i = 0; i < nactual; i++) { 236 op(argv[i]); 237 } 238 } 239 240 void dump() const; 241 242 inline BaselineFrame* baselineFrame() const; 243 244 // Returns the number of local and expression stack Values for the current 245 // Baseline frame. 246 inline uint32_t baselineFrameNumValueSlots() const; 247 }; 248 249 class JitcodeGlobalTable; 250 251 class JSJitProfilingFrameIterator { 252 uint8_t* fp_; 253 uint8_t* wasmCallerFP_ = nullptr; 254 // See JS::ProfilingFrameIterator::endStackAddress_ comment. 255 void* endStackAddress_ = nullptr; 256 FrameType type_; 257 void* resumePCinCurrentFrame_; 258 259 inline JSScript* frameScript() const; 260 [[nodiscard]] bool tryInitWithPC(void* pc); 261 [[nodiscard]] bool tryInitWithTable(JitcodeGlobalTable* table, void* pc, 262 bool forLastCallSite); 263 264 void moveToNextFrame(CommonFrameLayout* frame); 265 266 public: 267 JSJitProfilingFrameIterator(JSContext* cx, void* pc, void* sp); 268 explicit JSJitProfilingFrameIterator(CommonFrameLayout* exitFP); 269 270 void operator++(); 271 bool done() const { return fp_ == nullptr; } 272 273 const char* baselineInterpreterLabel() const; 274 void baselineInterpreterScriptPC(JSScript** script, jsbytecode** pc, 275 uint64_t* realmID, uint32_t* sourceId) const; 276 277 void* fp() const { 278 MOZ_ASSERT(!done()); 279 return fp_; 280 } 281 void* wasmCallerFP() const { 282 MOZ_ASSERT(done()); 283 MOZ_ASSERT(bool(wasmCallerFP_) == (type_ == FrameType::WasmToJSJit)); 284 return wasmCallerFP_; 285 } 286 inline JitFrameLayout* framePtr() const; 287 void* stackAddress() const { return fp(); } 288 FrameType frameType() const { 289 MOZ_ASSERT(!done()); 290 return type_; 291 } 292 void* resumePCinCurrentFrame() const { 293 MOZ_ASSERT(!done()); 294 return resumePCinCurrentFrame_; 295 } 296 297 void* endStackAddress() const { return endStackAddress_; } 298 }; 299 300 class RInstructionResults { 301 // Vector of results of recover instructions. 302 using Values = mozilla::Vector<HeapPtr<Value>, 1, SystemAllocPolicy>; 303 UniquePtr<Values> results_; 304 305 // The frame pointer is used as a key to check if the current frame already 306 // bailed out. 307 JitFrameLayout* fp_; 308 309 // Record if we tried and succeed at allocating and filling the vector of 310 // recover instruction results, if needed. This flag is needed in order to 311 // avoid evaluating the recover instruction twice. 312 bool initialized_; 313 314 public: 315 explicit RInstructionResults(JitFrameLayout* fp); 316 RInstructionResults(RInstructionResults&& src); 317 318 RInstructionResults& operator=(RInstructionResults&& rhs); 319 320 ~RInstructionResults(); 321 322 [[nodiscard]] bool init(JSContext* cx, uint32_t numResults); 323 bool isInitialized() const; 324 size_t length() const; 325 326 JitFrameLayout* frame() const; 327 328 HeapPtr<Value>& operator[](size_t index); 329 330 void trace(JSTracer* trc); 331 }; 332 333 struct MaybeReadFallback { 334 enum FallbackConsequence { Fallback_Invalidate, Fallback_DoNothing }; 335 336 JSContext* maybeCx = nullptr; 337 JitActivation* activation = nullptr; 338 const JSJitFrameIter* frame = nullptr; 339 const FallbackConsequence consequence = Fallback_Invalidate; 340 341 MaybeReadFallback() = default; 342 343 MaybeReadFallback(JSContext* cx, JitActivation* activation, 344 const JSJitFrameIter* frame, 345 FallbackConsequence consequence = Fallback_Invalidate) 346 : maybeCx(cx), 347 activation(activation), 348 frame(frame), 349 consequence(consequence) {} 350 351 bool canRecoverResults() { return maybeCx; } 352 }; 353 354 class RResumePoint; 355 356 // Reads frame information in snapshot-encoding order (that is, outermost frame 357 // to innermost frame). 358 class SnapshotIterator { 359 protected: 360 SnapshotReader snapshot_; 361 RecoverReader recover_; 362 JitFrameLayout* fp_; 363 const MachineState* machine_; 364 IonScript* ionScript_; 365 RInstructionResults* instructionResults_; 366 367 enum class ReadMethod : bool { 368 // Read the normal value. 369 Normal, 370 371 // Read the default value, or the normal value if there is no default. 372 AlwaysDefault, 373 }; 374 375 private: 376 // Read a spilled register from the machine state. 377 bool hasRegister(Register reg) const { return machine_->has(reg); } 378 uintptr_t fromRegister(Register reg) const { return machine_->read(reg); } 379 380 bool hasRegister(FloatRegister reg) const { return machine_->has(reg); } 381 template <typename T> 382 T fromRegister(FloatRegister reg) const { 383 return machine_->read<T>(reg); 384 } 385 386 // Read an uintptr_t from the stack. 387 bool hasStack(int32_t offset) const { return true; } 388 uintptr_t fromStack(int32_t offset) const; 389 390 bool hasInstructionResult(uint32_t index) const { 391 return instructionResults_; 392 } 393 bool hasInstructionResults() const { return instructionResults_; } 394 Value fromInstructionResult(uint32_t index) const; 395 396 Value allocationValue(const RValueAllocation& a, 397 ReadMethod rm = ReadMethod::Normal); 398 [[nodiscard]] bool allocationReadable(const RValueAllocation& a, 399 ReadMethod rm = ReadMethod::Normal); 400 void writeAllocationValuePayload(const RValueAllocation& a, const Value& v); 401 void warnUnreadableAllocation(); 402 403 public: 404 // Handle iterating over RValueAllocations of the snapshots. 405 inline RValueAllocation readAllocation() { 406 MOZ_ASSERT(moreAllocations()); 407 return snapshot_.readAllocation(); 408 } 409 void skip() { snapshot_.skipAllocation(); } 410 411 const RResumePoint* resumePoint() const; 412 const RInstruction* instruction() const { return recover_.instruction(); } 413 414 uint32_t numAllocations() const; 415 inline bool moreAllocations() const { 416 return snapshot_.numAllocationsRead() < numAllocations(); 417 } 418 419 JitFrameLayout* frame() { return fp_; }; 420 421 // Used by recover instruction to store the value back into the instruction 422 // results array. 423 void storeInstructionResult(const Value& v); 424 425 public: 426 // Exhibits frame properties contained in the snapshot. 427 uint32_t pcOffset() const; 428 ResumeMode resumeMode() const; 429 430 bool resumeAfter() const { 431 // Calls in outer frames are never considered resume-after. 432 MOZ_ASSERT_IF(moreFrames(), !IsResumeAfter(resumeMode())); 433 return IsResumeAfter(resumeMode()); 434 } 435 inline BailoutKind bailoutKind() const { return snapshot_.bailoutKind(); } 436 437 IonScript* ionScript() const { return ionScript_; } 438 439 public: 440 // Read the next instruction available and get ready to either skip it or 441 // evaluate it. 442 inline void nextInstruction() { 443 MOZ_ASSERT(snapshot_.numAllocationsRead() == numAllocations()); 444 recover_.nextInstruction(); 445 snapshot_.resetNumAllocationsRead(); 446 } 447 448 // Skip an Instruction by walking to the next instruction and by skipping 449 // all the allocations corresponding to this instruction. 450 void skipInstruction(); 451 452 inline bool moreInstructions() const { return recover_.moreInstructions(); } 453 454 // Register a vector used for storing the results of the evaluation of 455 // recover instructions. This vector should be registered before the 456 // beginning of the iteration. This function is in charge of allocating 457 // enough space for all instructions results, and return false iff it fails. 458 [[nodiscard]] bool initInstructionResults(MaybeReadFallback& fallback); 459 460 protected: 461 // This function is used internally for computing the result of the recover 462 // instructions. 463 [[nodiscard]] bool computeInstructionResults( 464 JSContext* cx, RInstructionResults* results) const; 465 466 public: 467 // Handle iterating over frames of the snapshots. 468 void nextFrame(); 469 void settleOnFrame(); 470 471 inline bool moreFrames() const { 472 // The last instruction is recovering the innermost frame, so as long as 473 // there is more instruction there is necesseray more frames. 474 return moreInstructions(); 475 } 476 477 public: 478 // Connect all informations about the current script in order to recover the 479 // content of baseline frames. 480 481 SnapshotIterator(const JSJitFrameIter& iter, 482 const MachineState* machineState); 483 SnapshotIterator(); 484 485 Value read() { return allocationValue(readAllocation()); } 486 487 // Like |read()| but also supports IntPtr and Int64 allocations, which are 488 // returned as BigInt values. 489 bool readMaybeUnpackedBigInt(JSContext* cx, MutableHandle<Value> result); 490 491 int32_t readInt32() { 492 Value val = read(); 493 MOZ_RELEASE_ASSERT(val.isInt32()); 494 return val.toInt32(); 495 } 496 497 double readNumber() { 498 Value val = read(); 499 MOZ_RELEASE_ASSERT(val.isNumber()); 500 return val.toNumber(); 501 } 502 503 JSString* readString() { 504 Value val = read(); 505 MOZ_RELEASE_ASSERT(val.isString()); 506 return val.toString(); 507 } 508 509 JS::BigInt* readBigInt() { 510 Value val = read(); 511 MOZ_RELEASE_ASSERT(val.isBigInt()); 512 return val.toBigInt(); 513 } 514 515 JSObject* readObject() { 516 Value val = read(); 517 MOZ_RELEASE_ASSERT(val.isObject()); 518 return &val.toObject(); 519 } 520 521 JS::GCCellPtr readGCCellPtr() { 522 Value val = read(); 523 MOZ_RELEASE_ASSERT(val.isGCThing()); 524 return val.toGCCellPtr(); 525 } 526 527 // Read the |Normal| value unless it is not available and that the snapshot 528 // provides a |Default| value. This is useful to avoid invalidations of the 529 // frame while we are only interested in a few properties which are provided 530 // by the |Default| value. 531 Value readWithDefault(RValueAllocation* alloc) { 532 *alloc = RValueAllocation(); 533 RValueAllocation a = readAllocation(); 534 if (allocationReadable(a)) { 535 return allocationValue(a); 536 } 537 538 *alloc = a; 539 return allocationValue(a, ReadMethod::AlwaysDefault); 540 } 541 542 Value maybeRead(const RValueAllocation& a, MaybeReadFallback& fallback); 543 Value maybeRead(MaybeReadFallback& fallback) { 544 RValueAllocation a = readAllocation(); 545 return maybeRead(a, fallback); 546 } 547 548 bool tryRead(Value* result); 549 550 private: 551 int64_t allocationInt64(const RValueAllocation& alloc); 552 intptr_t allocationIntPtr(const RValueAllocation& alloc); 553 554 public: 555 int64_t readInt64() { return allocationInt64(readAllocation()); } 556 557 intptr_t readIntPtr() { return allocationIntPtr(readAllocation()); } 558 559 // Read either a BigInt or unpacked BigInt. 560 JS::BigInt* readBigInt(JSContext* cx); 561 562 void traceAllocation(JSTracer* trc); 563 564 template <class Op> 565 void readFunctionFrameArgs(Op& op, ArgumentsObject** argsObj, Value* thisv, 566 unsigned start, unsigned end, JSScript* script, 567 MaybeReadFallback& fallback) { 568 // Assumes that the common frame arguments have already been read. 569 if (script->needsArgsObj()) { 570 if (argsObj) { 571 Value v = maybeRead(fallback); 572 if (v.isObject()) { 573 *argsObj = &v.toObject().as<ArgumentsObject>(); 574 } 575 } else { 576 skip(); 577 } 578 } 579 580 if (thisv) { 581 *thisv = maybeRead(fallback); 582 } else { 583 skip(); 584 } 585 586 unsigned i = 0; 587 if (end < start) { 588 i = start; 589 } 590 591 for (; i < start; i++) { 592 skip(); 593 } 594 for (; i < end; i++) { 595 // We are not always able to read values from the snapshots, some values 596 // such as non-gc things may still be live in registers and cause an 597 // error while reading the machine state. 598 Value v = maybeRead(fallback); 599 op(v); 600 } 601 } 602 603 // Iterate over all the allocations and return only the value of the 604 // allocation located at one index. 605 Value maybeReadAllocByIndex(size_t index); 606 607 #ifdef TRACK_SNAPSHOTS 608 void spewBailingFrom() const { snapshot_.spewBailingFrom(); } 609 #endif 610 }; 611 612 // Reads frame information in callstack order (that is, innermost frame to 613 // outermost frame). 614 class InlineFrameIterator { 615 const JSJitFrameIter* frame_; 616 SnapshotIterator start_; 617 SnapshotIterator si_; 618 uint32_t framesRead_; 619 620 // When the inline-frame-iterator is created, this variable is defined to 621 // UINT32_MAX. Then the first iteration of findNextFrame, which settle on 622 // the innermost frame, is used to update this counter to the number of 623 // frames contained in the recover buffer. 624 uint32_t frameCount_; 625 626 // The |calleeTemplate_| fields contains either the JSFunction or the 627 // template from which it is supposed to be cloned. The |calleeRVA_| is an 628 // Invalid value allocation, if the |calleeTemplate_| field is the effective 629 // JSFunction, and not its template. On the other hand, any other value 630 // allocation implies that the |calleeTemplate_| is the template JSFunction 631 // from which the effective one would be derived and cached by the Recover 632 // instruction result. 633 RootedFunction calleeTemplate_; 634 RValueAllocation calleeRVA_; 635 636 RootedScript script_; 637 jsbytecode* pc_; 638 uint32_t numActualArgs_; 639 640 // Register state, used by all snapshot iterators. 641 MachineState machine_; 642 643 struct Nop { 644 void operator()(const Value& v) {} 645 }; 646 647 private: 648 void findNextFrame(); 649 JSObject* computeEnvironmentChain(const Value& envChainValue, 650 MaybeReadFallback& fallback, 651 bool* hasInitialEnv = nullptr) const; 652 653 public: 654 InlineFrameIterator(JSContext* cx, const JSJitFrameIter* iter); 655 InlineFrameIterator(JSContext* cx, const InlineFrameIterator* iter); 656 657 bool more() const { return frame_ && framesRead_ < frameCount_; } 658 659 // Due to optimizations, we are not always capable of reading the callee of 660 // inlined frames without invalidating the IonCode. This function might 661 // return either the effective callee of the JSFunction which might be used 662 // to create it. 663 // 664 // As such, the |calleeTemplate()| can be used to read most of the metadata 665 // which are conserved across clones. 666 JSFunction* calleeTemplate() const { 667 MOZ_ASSERT(isFunctionFrame()); 668 return calleeTemplate_; 669 } 670 JSFunction* maybeCalleeTemplate() const { return calleeTemplate_; } 671 672 JSFunction* callee(MaybeReadFallback& fallback) const; 673 674 unsigned numActualArgs() const { 675 // The number of actual arguments for inline frames is determined by this 676 // iterator based on the caller's bytecode instruction (Call, FunCall, 677 // GetProp/SetProp, etc). For the outer function it's stored in the stack 678 // frame. 679 if (more()) { 680 return numActualArgs_; 681 } 682 683 return frame_->numActualArgs(); 684 } 685 686 template <class ArgOp, class LocalOp> 687 void readFrameArgsAndLocals(JSContext* cx, ArgOp& argOp, LocalOp& localOp, 688 JSObject** envChain, bool* hasInitialEnv, 689 Value* rval, ArgumentsObject** argsObj, 690 Value* thisv, ReadFrameArgsBehavior behavior, 691 MaybeReadFallback& fallback) const { 692 SnapshotIterator s(si_); 693 694 // Read the env chain. 695 if (envChain) { 696 Value envChainValue = s.maybeRead(fallback); 697 *envChain = 698 computeEnvironmentChain(envChainValue, fallback, hasInitialEnv); 699 } else { 700 s.skip(); 701 } 702 703 // Read return value. 704 if (rval) { 705 *rval = s.maybeRead(fallback); 706 } else { 707 s.skip(); 708 } 709 710 // Read arguments, which only function frames have. 711 if (isFunctionFrame()) { 712 unsigned nactual = numActualArgs(); 713 unsigned nformal = calleeTemplate()->nargs(); 714 715 // Read the formal arguments, which are taken from the inlined frame, 716 // because it will have the updated value when JSOp::SetArg is used. 717 unsigned numFormalsToRead; 718 if (behavior == ReadFrameArgsBehavior::Actuals) { 719 numFormalsToRead = std::min(nactual, nformal); 720 } else { 721 MOZ_ASSERT(behavior == ReadFrameArgsBehavior::ActualsAndFormals); 722 numFormalsToRead = nformal; 723 } 724 s.readFunctionFrameArgs(argOp, argsObj, thisv, 0, numFormalsToRead, 725 script(), fallback); 726 727 // Skip formals we didn't read. 728 for (unsigned i = numFormalsToRead; i < nformal; i++) { 729 s.skip(); 730 } 731 732 if (nactual > nformal) { 733 if (more()) { 734 // There is still a parent frame of this inlined frame. All 735 // arguments (also the overflown) are the last pushed values 736 // in the parent frame. To get the overflown arguments, we 737 // need to take them from there. 738 739 // The overflown arguments are not available in current frame. 740 // They are the last pushed arguments in the parent frame of 741 // this inlined frame. 742 InlineFrameIterator it(cx, this); 743 ++it; 744 unsigned argsObjAdj = it.script()->needsArgsObj() ? 1 : 0; 745 bool hasNewTarget = isConstructing(); 746 SnapshotIterator parent_s(it.snapshotIterator()); 747 748 // Skip over all slots until we get to the last slots 749 // (= arguments slots of callee) the +3 is for [this], [returnvalue], 750 // [envchain], and maybe +1 for [argsObj] 751 MOZ_ASSERT(parent_s.numAllocations() >= 752 nactual + 3 + argsObjAdj + hasNewTarget); 753 unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj - 754 hasNewTarget; 755 for (unsigned j = 0; j < skip; j++) { 756 parent_s.skip(); 757 } 758 759 // Get the overflown arguments 760 parent_s.skip(); // env chain 761 parent_s.skip(); // return value 762 parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr, nformal, 763 nactual, it.script(), fallback); 764 } else { 765 // There is no parent frame to this inlined frame, we can read 766 // from the frame's Value vector directly. 767 Value* argv = frame_->actualArgs(); 768 for (unsigned i = nformal; i < nactual; i++) { 769 argOp(argv[i]); 770 } 771 } 772 } 773 } 774 775 // At this point we've read all the formals in s, and can read the 776 // locals. 777 for (unsigned i = 0; i < script()->nfixed(); i++) { 778 localOp(s.maybeRead(fallback)); 779 } 780 } 781 782 template <class Op> 783 void unaliasedForEachActual(JSContext* cx, Op op, 784 MaybeReadFallback& fallback) const { 785 Nop nop; 786 readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, nullptr, nullptr, 787 nullptr, ReadFrameArgsBehavior::Actuals, fallback); 788 } 789 790 JSScript* script() const { return script_; } 791 jsbytecode* pc() const { return pc_; } 792 SnapshotIterator snapshotIterator() const { return si_; } 793 bool isFunctionFrame() const; 794 bool isModuleFrame() const; 795 bool isConstructing() const; 796 797 JSObject* environmentChain(MaybeReadFallback& fallback, 798 bool* hasInitialEnvironment = nullptr) const { 799 SnapshotIterator s(si_); 800 801 // envChain 802 Value v = s.maybeRead(fallback); 803 return computeEnvironmentChain(v, fallback, hasInitialEnvironment); 804 } 805 806 Value thisArgument(MaybeReadFallback& fallback) const { 807 SnapshotIterator s(si_); 808 809 // envChain 810 s.skip(); 811 812 // return value 813 s.skip(); 814 815 // Arguments object. 816 if (script()->needsArgsObj()) { 817 s.skip(); 818 } 819 820 return s.maybeRead(fallback); 821 } 822 823 InlineFrameIterator& operator++() { 824 findNextFrame(); 825 return *this; 826 } 827 828 void dump() const; 829 830 void resetOn(const JSJitFrameIter* iter); 831 832 const JSJitFrameIter& frame() const { return *frame_; } 833 834 // Inline frame number, 0 for the outermost (non-inlined) frame. 835 size_t frameNo() const { return frameCount() - framesRead_; } 836 size_t frameCount() const { 837 MOZ_ASSERT(frameCount_ != UINT32_MAX); 838 return frameCount_; 839 } 840 841 private: 842 InlineFrameIterator() = delete; 843 InlineFrameIterator(const InlineFrameIterator& iter) = delete; 844 }; 845 846 } // namespace jit 847 } // namespace js 848 849 #endif /* jit_JSJitFrameIter_h */