diff options
Diffstat (limited to 'js/src/vm/JitActivation.cpp')
-rw-r--r-- | js/src/vm/JitActivation.cpp | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/js/src/vm/JitActivation.cpp b/js/src/vm/JitActivation.cpp new file mode 100644 index 0000000000..83ec19df8a --- /dev/null +++ b/js/src/vm/JitActivation.cpp @@ -0,0 +1,265 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/JitActivation.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_RELEASE_ASSERT + +#include <stddef.h> // size_t +#include <stdint.h> // uint8_t, uint32_t +#include <utility> // std::move + +#include "debugger/DebugAPI.h" // js::DebugAPI +#include "jit/JSJitFrameIter.h" // js::jit::InlineFrameIterator +#include "jit/RematerializedFrame.h" // js::jit::RematerializedFrame +#include "js/AllocPolicy.h" // js::ReportOutOfMemory +#include "vm/EnvironmentObject.h" // js::DebugEnvironments +#include "vm/JSContext.h" // JSContext +#include "vm/Realm.h" // js::AutoRealmUnchecked +#include "wasm/WasmCode.h" // js::wasm::Code +#include "wasm/WasmConstants.h" // js::wasm::Trap +#include "wasm/WasmFrameIter.h" // js::wasm::{RegisterState,StartUnwinding,UnwindState} +#include "wasm/WasmInstance.h" // js::wasm::Instance +#include "wasm/WasmProcess.h" // js::wasm::LookupCode + +#include "vm/Realm-inl.h" // js::~AutoRealm + +class JS_PUBLIC_API JSTracer; + +js::jit::JitActivation::JitActivation(JSContext* cx) + : Activation(cx, Jit), + packedExitFP_(nullptr), + encodedWasmExitReason_(0), + prevJitActivation_(cx->jitActivation), + ionRecovery_(cx), + bailoutData_(nullptr), + lastProfilingFrame_(nullptr), + lastProfilingCallSite_(nullptr) { + cx->jitActivation = this; + registerProfiling(); +} + +js::jit::JitActivation::~JitActivation() { + if (isProfiling()) { + unregisterProfiling(); + } + cx_->jitActivation = prevJitActivation_; + + // All reocvered value are taken from activation during the bailout. + MOZ_ASSERT(ionRecovery_.empty()); + + // The BailoutFrameInfo should have unregistered itself from the + // JitActivations. + MOZ_ASSERT(!bailoutData_); + + // Traps get handled immediately. + MOZ_ASSERT(!isWasmTrapping()); + + clearRematerializedFrames(); +} + +void js::jit::JitActivation::setBailoutData( + jit::BailoutFrameInfo* bailoutData) { + MOZ_ASSERT(!bailoutData_); + bailoutData_ = bailoutData; +} + +void js::jit::JitActivation::cleanBailoutData() { + MOZ_ASSERT(bailoutData_); + bailoutData_ = nullptr; +} + +void js::jit::JitActivation::removeRematerializedFrame(uint8_t* top) { + if (!rematerializedFrames_) { + return; + } + + if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) { + rematerializedFrames_->remove(p); + } +} + +void js::jit::JitActivation::clearRematerializedFrames() { + if (!rematerializedFrames_) { + return; + } + + for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); + e.popFront()) { + e.removeFront(); + } +} + +js::jit::RematerializedFrame* js::jit::JitActivation::getRematerializedFrame( + JSContext* cx, const JSJitFrameIter& iter, size_t inlineDepth, + MaybeReadFallback::FallbackConsequence consequence) { + MOZ_ASSERT(iter.activation() == this); + MOZ_ASSERT(iter.isIonScripted()); + + if (!rematerializedFrames_) { + rematerializedFrames_ = cx->make_unique<RematerializedFrameTable>(cx); + if (!rematerializedFrames_) { + return nullptr; + } + } + + uint8_t* top = iter.fp(); + RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top); + if (!p) { + RematerializedFrameVector frames(cx); + + // The unit of rematerialization is an uninlined frame and its inlined + // frames. Since inlined frames do not exist outside of snapshots, it + // is impossible to synchronize their rematerialized copies to + // preserve identity. Therefore, we always rematerialize an uninlined + // frame and all its inlined frames at once. + InlineFrameIterator inlineIter(cx, &iter); + MaybeReadFallback recover(cx, this, &iter, consequence); + + // Frames are often rematerialized with the cx inside a Debugger's + // realm. To recover slots and to create CallObjects, we need to + // be in the script's realm. + AutoRealmUnchecked ar(cx, iter.script()->realm()); + + if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, + recover, frames)) { + return nullptr; + } + + if (!rematerializedFrames_->add(p, top, std::move(frames))) { + ReportOutOfMemory(cx); + return nullptr; + } + + // See comment in unsetPrevUpToDateUntil. + DebugEnvironments::unsetPrevUpToDateUntil(cx, + p->value()[inlineDepth].get()); + } + + return p->value()[inlineDepth].get(); +} + +js::jit::RematerializedFrame* js::jit::JitActivation::lookupRematerializedFrame( + uint8_t* top, size_t inlineDepth) { + if (!rematerializedFrames_) { + return nullptr; + } + if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) { + return inlineDepth < p->value().length() ? p->value()[inlineDepth].get() + : nullptr; + } + return nullptr; +} + +void js::jit::JitActivation::removeRematerializedFramesFromDebugger( + JSContext* cx, uint8_t* top) { + // Ion bailout can fail due to overrecursion and OOM. In such cases we + // cannot honor any further Debugger hooks on the frame, and need to + // ensure that its Debugger.Frame entry is cleaned up. + if (!cx->realm()->isDebuggee() || !rematerializedFrames_) { + return; + } + if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) { + for (uint32_t i = 0; i < p->value().length(); i++) { + DebugAPI::handleUnrecoverableIonBailoutError(cx, p->value()[i].get()); + } + rematerializedFrames_->remove(p); + } +} + +void js::jit::JitActivation::traceRematerializedFrames(JSTracer* trc) { + if (!rematerializedFrames_) { + return; + } + for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); + e.popFront()) { + e.front().value().trace(trc); + } +} + +bool js::jit::JitActivation::registerIonFrameRecovery( + RInstructionResults&& results) { + // Check that there is no entry in the vector yet. + MOZ_ASSERT(!maybeIonFrameRecovery(results.frame())); + if (!ionRecovery_.append(std::move(results))) { + return false; + } + + return true; +} + +js::jit::RInstructionResults* js::jit::JitActivation::maybeIonFrameRecovery( + JitFrameLayout* fp) { + for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end(); + it++) { + if (it->frame() == fp) { + return it; + } + } + + return nullptr; +} + +void js::jit::JitActivation::removeIonFrameRecovery(JitFrameLayout* fp) { + RInstructionResults* elem = maybeIonFrameRecovery(fp); + if (!elem) { + return; + } + + ionRecovery_.erase(elem); +} + +void js::jit::JitActivation::traceIonRecovery(JSTracer* trc) { + for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end(); + it++) { + it->trace(trc); + } +} + +void js::jit::JitActivation::startWasmTrap(wasm::Trap trap, + uint32_t bytecodeOffset, + const wasm::RegisterState& state) { + MOZ_ASSERT(!isWasmTrapping()); + + bool unwound; + wasm::UnwindState unwindState; + MOZ_RELEASE_ASSERT(wasm::StartUnwinding(state, &unwindState, &unwound)); + // With return calls, it is possible to not unwind when there is only an + // entry left on the stack, e.g. the return call trampoline that is created + // to restore realm before returning to the interpreter entry stub. + MOZ_ASSERT_IF(unwound, trap == wasm::Trap::IndirectCallBadSig); + + void* pc = unwindState.pc; + const wasm::Frame* fp = wasm::Frame::fromUntaggedWasmExitFP(unwindState.fp); + + const wasm::Code& code = wasm::GetNearestEffectiveInstance(fp)->code(); + MOZ_RELEASE_ASSERT(&code == wasm::LookupCode(pc)); + + // If the frame was unwound, the bytecodeOffset must be recovered from the + // callsite so that it is accurate. + if (unwound) { + bytecodeOffset = code.lookupCallSite(pc)->lineOrBytecode(); + } + + setWasmExitFP(fp); + wasmTrapData_.emplace(); + wasmTrapData_->resumePC = + ((uint8_t*)state.pc) + jit::WasmTrapInstructionLength; + wasmTrapData_->unwoundPC = pc; + wasmTrapData_->trap = trap; + wasmTrapData_->bytecodeOffset = bytecodeOffset; + wasmTrapData_->failedUnwindSignatureMismatch = + !unwound && trap == wasm::Trap::IndirectCallBadSig; + + MOZ_ASSERT(isWasmTrapping()); +} + +void js::jit::JitActivation::finishWasmTrap() { + MOZ_ASSERT(isWasmTrapping()); + packedExitFP_ = nullptr; + wasmTrapData_.reset(); + MOZ_ASSERT(!isWasmTrapping()); +} |