js/src/jit/Bailouts.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jit/Bailouts.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,212 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#ifndef jit_Bailouts_h
    1.11 +#define jit_Bailouts_h
    1.12 +
    1.13 +#include "jstypes.h"
    1.14 +
    1.15 +#include "jit/IonFrames.h"
    1.16 +#include "jit/JitFrameIterator.h"
    1.17 +#include "vm/Stack.h"
    1.18 +
    1.19 +namespace js {
    1.20 +namespace jit {
    1.21 +
    1.22 +// A "bailout" is a condition in which we need to recover an interpreter frame
    1.23 +// from an IonFrame. Bailouts can happen for the following reasons:
    1.24 +//   (1) A deoptimization guard, for example, an add overflows or a type check
    1.25 +//       fails.
    1.26 +//   (2) A check or assumption held by the JIT is invalidated by the VM, and
    1.27 +//       JIT code must be thrown away. This includes the GC possibly deciding
    1.28 +//       to evict live JIT code, or a Type Inference reflow.
    1.29 +//
    1.30 +// Note that bailouts as described here do not include normal Ion frame
    1.31 +// inspection, for example, if an exception must be built or the GC needs to
    1.32 +// scan an Ion frame for gcthings.
    1.33 +//
    1.34 +// The second type of bailout needs a different name - "deoptimization" or
    1.35 +// "deep bailout". Here we are concerned with eager (or maybe "shallow")
    1.36 +// bailouts, that happen from JIT code. These happen from guards, like:
    1.37 +//
    1.38 +//  cmp [obj + shape], 0x50M37TH1NG
    1.39 +//  jmp _bailout
    1.40 +//
    1.41 +// The bailout target needs to somehow translate the Ion frame (whose state
    1.42 +// will differ at each program point) to an interpreter frame. This state is
    1.43 +// captured into the IonScript's snapshot buffer, and for each bailout we know
    1.44 +// which snapshot corresponds to its state.
    1.45 +//
    1.46 +// Roughly, the following needs to happen at the bailout target.
    1.47 +//   (1) Move snapshot ID into a known stack location (registers cannot be
    1.48 +//       mutated).
    1.49 +//   (2) Spill all registers to the stack.
    1.50 +//   (3) Call a Bailout() routine, whose argument is the stack pointer.
    1.51 +//   (4) Bailout() will find the IonScript on the stack, use the snapshot ID
    1.52 +//       to find the structure of the frame, and then use the stack and spilled
    1.53 +//       registers to perform frame conversion.
    1.54 +//   (5) Bailout() returns, and the JIT must immediately return to the
    1.55 +//       interpreter (all frames are converted at once).
    1.56 +//
    1.57 +// (2) and (3) are implemented by a trampoline held in the compartment.
    1.58 +// Naively, we could implement (1) like:
    1.59 +//
    1.60 +//   _bailout_ID_1:
    1.61 +//     push 1
    1.62 +//     jmp _global_bailout_handler
    1.63 +//   _bailout_ID_2:
    1.64 +//     push 2
    1.65 +//     jmp _global_bailout_handler
    1.66 +//
    1.67 +// This takes about 10 extra bytes per guard. On some platforms, we can reduce
    1.68 +// this overhead to 4 bytes by creating a global jump table, shared again in
    1.69 +// the compartment:
    1.70 +//
    1.71 +//     call _global_bailout_handler
    1.72 +//     call _global_bailout_handler
    1.73 +//     call _global_bailout_handler
    1.74 +//     call _global_bailout_handler
    1.75 +//      ...
    1.76 +//    _global_bailout_handler:
    1.77 +//
    1.78 +// In the bailout handler, we can recompute which entry in the table was
    1.79 +// selected by subtracting the return addressed pushed by the call, from the
    1.80 +// start of the table, and then dividing by the size of a (call X) entry in the
    1.81 +// table. This gives us a number in [0, TableSize), which we call a
    1.82 +// "BailoutId".
    1.83 +//
    1.84 +// Then, we can provide a per-script mapping from BailoutIds to snapshots,
    1.85 +// which takes only four bytes per entry.
    1.86 +//
    1.87 +// This strategy does not work as given, because the bailout handler has no way
    1.88 +// to compute the location of an IonScript. Currently, we do not use frame
    1.89 +// pointers. To account for this we segregate frames into a limited set of
    1.90 +// "frame sizes", and create a table for each frame size. We also have the
    1.91 +// option of not using bailout tables, for platforms or situations where the
    1.92 +// 10 byte cost is more optimal than a bailout table. See IonFrames.h for more
    1.93 +// detail.
    1.94 +
    1.95 +static const BailoutId INVALID_BAILOUT_ID = BailoutId(-1);
    1.96 +
    1.97 +// Keep this arbitrarily small for now, for testing.
    1.98 +static const uint32_t BAILOUT_TABLE_SIZE = 16;
    1.99 +
   1.100 +// Bailout return codes.
   1.101 +// N.B. the relative order of these values is hard-coded into ::GenerateBailoutThunk.
   1.102 +static const uint32_t BAILOUT_RETURN_OK = 0;
   1.103 +static const uint32_t BAILOUT_RETURN_FATAL_ERROR = 1;
   1.104 +static const uint32_t BAILOUT_RETURN_OVERRECURSED = 2;
   1.105 +
   1.106 +class JitCompartment;
   1.107 +
   1.108 +// BailoutStack is an architecture specific pointer to the stack, given by the
   1.109 +// bailout handler.
   1.110 +class BailoutStack;
   1.111 +class InvalidationBailoutStack;
   1.112 +
   1.113 +// Must be implemented by each architecture.
   1.114 +
   1.115 +// This iterator is constructed at a time where there is no exit frame at the
   1.116 +// moment. They must be initialized to the first JS frame instead of the exit
   1.117 +// frame as usually done with JitFrameIterator.
   1.118 +class IonBailoutIterator : public JitFrameIterator
   1.119 +{
   1.120 +    MachineState machine_;
   1.121 +    uint32_t snapshotOffset_;
   1.122 +    size_t topFrameSize_;
   1.123 +    IonScript *topIonScript_;
   1.124 +
   1.125 +  public:
   1.126 +    IonBailoutIterator(const JitActivationIterator &activations, BailoutStack *sp);
   1.127 +    IonBailoutIterator(const JitActivationIterator &activations, InvalidationBailoutStack *sp);
   1.128 +    IonBailoutIterator(const JitActivationIterator &activations, const JitFrameIterator &frame);
   1.129 +
   1.130 +    SnapshotOffset snapshotOffset() const {
   1.131 +        JS_ASSERT(topIonScript_);
   1.132 +        return snapshotOffset_;
   1.133 +    }
   1.134 +    const MachineState &machineState() const {
   1.135 +        return machine_;
   1.136 +    }
   1.137 +    size_t topFrameSize() const {
   1.138 +        JS_ASSERT(topIonScript_);
   1.139 +        return topFrameSize_;
   1.140 +    }
   1.141 +    IonScript *ionScript() const {
   1.142 +        if (topIonScript_)
   1.143 +            return topIonScript_;
   1.144 +        return JitFrameIterator::ionScript();
   1.145 +    }
   1.146 +
   1.147 +    void dump() const;
   1.148 +};
   1.149 +
   1.150 +bool EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp);
   1.151 +
   1.152 +struct BaselineBailoutInfo;
   1.153 +
   1.154 +// Called from a bailout thunk. Returns a BAILOUT_* error code.
   1.155 +uint32_t Bailout(BailoutStack *sp, BaselineBailoutInfo **info);
   1.156 +
   1.157 +// Called from the invalidation thunk. Returns a BAILOUT_* error code.
   1.158 +uint32_t InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
   1.159 +                             BaselineBailoutInfo **info);
   1.160 +
   1.161 +class ExceptionBailoutInfo
   1.162 +{
   1.163 +    size_t frameNo_;
   1.164 +    jsbytecode *resumePC_;
   1.165 +    size_t numExprSlots_;
   1.166 +
   1.167 +  public:
   1.168 +    ExceptionBailoutInfo(size_t frameNo, jsbytecode *resumePC, size_t numExprSlots)
   1.169 +      : frameNo_(frameNo),
   1.170 +        resumePC_(resumePC),
   1.171 +        numExprSlots_(numExprSlots)
   1.172 +    { }
   1.173 +
   1.174 +    ExceptionBailoutInfo()
   1.175 +      : frameNo_(0),
   1.176 +        resumePC_(nullptr),
   1.177 +        numExprSlots_(0)
   1.178 +    { }
   1.179 +
   1.180 +    bool catchingException() const {
   1.181 +        return !!resumePC_;
   1.182 +    }
   1.183 +    bool propagatingIonExceptionForDebugMode() const {
   1.184 +        return !resumePC_;
   1.185 +    }
   1.186 +
   1.187 +    size_t frameNo() const {
   1.188 +        MOZ_ASSERT(catchingException());
   1.189 +        return frameNo_;
   1.190 +    }
   1.191 +    jsbytecode *resumePC() const {
   1.192 +        MOZ_ASSERT(catchingException());
   1.193 +        return resumePC_;
   1.194 +    }
   1.195 +    size_t numExprSlots() const {
   1.196 +        MOZ_ASSERT(catchingException());
   1.197 +        return numExprSlots_;
   1.198 +    }
   1.199 +};
   1.200 +
   1.201 +// Called from the exception handler to enter a catch or finally block.
   1.202 +// Returns a BAILOUT_* error code.
   1.203 +uint32_t ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
   1.204 +                                 ResumeFromException *rfe,
   1.205 +                                 const ExceptionBailoutInfo &excInfo,
   1.206 +                                 bool *overrecursed);
   1.207 +
   1.208 +uint32_t FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo);
   1.209 +
   1.210 +bool CheckFrequentBailouts(JSContext *cx, JSScript *script);
   1.211 +
   1.212 +} // namespace jit
   1.213 +} // namespace js
   1.214 +
   1.215 +#endif /* jit_Bailouts_h */

mercurial