diff options
Diffstat (limited to 'js/src/jit/Bailouts.h')
-rw-r--r-- | js/src/jit/Bailouts.h | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/js/src/jit/Bailouts.h b/js/src/jit/Bailouts.h new file mode 100644 index 000000000..747f59b7d --- /dev/null +++ b/js/src/jit/Bailouts.h @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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/. */ + +#ifndef jit_Bailouts_h +#define jit_Bailouts_h + +#include "jstypes.h" + +#include "jit/JitFrameIterator.h" +#include "jit/JitFrames.h" +#include "vm/Stack.h" + +namespace js { +namespace jit { + +// A "bailout" is a condition in which we need to recover an interpreter frame +// from an IonFrame. Bailouts can happen for the following reasons: +// (1) A deoptimization guard, for example, an add overflows or a type check +// fails. +// (2) A check or assumption held by the JIT is invalidated by the VM, and +// JIT code must be thrown away. This includes the GC possibly deciding +// to evict live JIT code, or a Type Inference reflow. +// +// Note that bailouts as described here do not include normal Ion frame +// inspection, for example, if an exception must be built or the GC needs to +// scan an Ion frame for gcthings. +// +// The second type of bailout needs a different name - "deoptimization" or +// "deep bailout". Here we are concerned with eager (or maybe "shallow") +// bailouts, that happen from JIT code. These happen from guards, like: +// +// cmp [obj + shape], 0x50M37TH1NG +// jmp _bailout +// +// The bailout target needs to somehow translate the Ion frame (whose state +// will differ at each program point) to an interpreter frame. This state is +// captured into the IonScript's snapshot buffer, and for each bailout we know +// which snapshot corresponds to its state. +// +// Roughly, the following needs to happen at the bailout target. +// (1) Move snapshot ID into a known stack location (registers cannot be +// mutated). +// (2) Spill all registers to the stack. +// (3) Call a Bailout() routine, whose argument is the stack pointer. +// (4) Bailout() will find the IonScript on the stack, use the snapshot ID +// to find the structure of the frame, and then use the stack and spilled +// registers to perform frame conversion. +// (5) Bailout() returns, and the JIT must immediately return to the +// interpreter (all frames are converted at once). +// +// (2) and (3) are implemented by a trampoline held in the compartment. +// Naively, we could implement (1) like: +// +// _bailout_ID_1: +// push 1 +// jmp _global_bailout_handler +// _bailout_ID_2: +// push 2 +// jmp _global_bailout_handler +// +// This takes about 10 extra bytes per guard. On some platforms, we can reduce +// this overhead to 4 bytes by creating a global jump table, shared again in +// the compartment: +// +// call _global_bailout_handler +// call _global_bailout_handler +// call _global_bailout_handler +// call _global_bailout_handler +// ... +// _global_bailout_handler: +// +// In the bailout handler, we can recompute which entry in the table was +// selected by subtracting the return addressed pushed by the call, from the +// start of the table, and then dividing by the size of a (call X) entry in the +// table. This gives us a number in [0, TableSize), which we call a +// "BailoutId". +// +// Then, we can provide a per-script mapping from BailoutIds to snapshots, +// which takes only four bytes per entry. +// +// This strategy does not work as given, because the bailout handler has no way +// to compute the location of an IonScript. Currently, we do not use frame +// pointers. To account for this we segregate frames into a limited set of +// "frame sizes", and create a table for each frame size. We also have the +// option of not using bailout tables, for platforms or situations where the +// 10 byte cost is more optimal than a bailout table. See JitFrames.h for more +// detail. + +static const BailoutId INVALID_BAILOUT_ID = BailoutId(-1); + +// Keep this arbitrarily small for now, for testing. +static const uint32_t BAILOUT_TABLE_SIZE = 16; + +// Bailout return codes. +// N.B. the relative order of these values is hard-coded into ::GenerateBailoutThunk. +static const uint32_t BAILOUT_RETURN_OK = 0; +static const uint32_t BAILOUT_RETURN_FATAL_ERROR = 1; +static const uint32_t BAILOUT_RETURN_OVERRECURSED = 2; + +// This address is a magic number made to cause crashes while indicating that we +// are making an attempt to mark the stack during a bailout. +static uint8_t * const FAKE_JIT_TOP_FOR_BAILOUT = reinterpret_cast<uint8_t*>(0xba1); + +// BailoutStack is an architecture specific pointer to the stack, given by the +// bailout handler. +class BailoutStack; +class InvalidationBailoutStack; + +// Must be implemented by each architecture. + +// This structure is constructed before recovering the baseline frames for a +// bailout. It records all information extracted from the stack, and which are +// needed for the JitFrameIterator. +class BailoutFrameInfo +{ + MachineState machine_; + uint8_t* framePointer_; + size_t topFrameSize_; + IonScript* topIonScript_; + uint32_t snapshotOffset_; + JitActivation* activation_; + + void attachOnJitActivation(const JitActivationIterator& activations); + + public: + BailoutFrameInfo(const JitActivationIterator& activations, BailoutStack* sp); + BailoutFrameInfo(const JitActivationIterator& activations, InvalidationBailoutStack* sp); + BailoutFrameInfo(const JitActivationIterator& activations, const JitFrameIterator& frame); + ~BailoutFrameInfo(); + + uint8_t* fp() const { + return framePointer_; + } + SnapshotOffset snapshotOffset() const { + return snapshotOffset_; + } + const MachineState* machineState() const { + return &machine_; + } + size_t topFrameSize() const { + return topFrameSize_; + } + IonScript* ionScript() const { + return topIonScript_; + } + JitActivation* activation() const { + return activation_; + } +}; + +MOZ_MUST_USE bool EnsureHasEnvironmentObjects(JSContext* cx, AbstractFramePtr fp); + +struct BaselineBailoutInfo; + +// Called from a bailout thunk. Returns a BAILOUT_* error code. +uint32_t Bailout(BailoutStack* sp, BaselineBailoutInfo** info); + +// Called from the invalidation thunk. Returns a BAILOUT_* error code. +uint32_t InvalidationBailout(InvalidationBailoutStack* sp, size_t* frameSizeOut, + BaselineBailoutInfo** info); + +class ExceptionBailoutInfo +{ + size_t frameNo_; + jsbytecode* resumePC_; + size_t numExprSlots_; + + public: + ExceptionBailoutInfo(size_t frameNo, jsbytecode* resumePC, size_t numExprSlots) + : frameNo_(frameNo), + resumePC_(resumePC), + numExprSlots_(numExprSlots) + { } + + ExceptionBailoutInfo() + : frameNo_(0), + resumePC_(nullptr), + numExprSlots_(0) + { } + + bool catchingException() const { + return !!resumePC_; + } + bool propagatingIonExceptionForDebugMode() const { + return !resumePC_; + } + + size_t frameNo() const { + MOZ_ASSERT(catchingException()); + return frameNo_; + } + jsbytecode* resumePC() const { + MOZ_ASSERT(catchingException()); + return resumePC_; + } + size_t numExprSlots() const { + MOZ_ASSERT(catchingException()); + return numExprSlots_; + } +}; + +// Called from the exception handler to enter a catch or finally block. +// Returns a BAILOUT_* error code. +uint32_t ExceptionHandlerBailout(JSContext* cx, const InlineFrameIterator& frame, + ResumeFromException* rfe, + const ExceptionBailoutInfo& excInfo, + bool* overrecursed); + +uint32_t FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfo); + +void CheckFrequentBailouts(JSContext* cx, JSScript* script, BailoutKind bailoutKind); + +} // namespace jit +} // namespace js + +#endif /* jit_Bailouts_h */ |