/* -*- 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_Recover_h #define jit_Recover_h #include "mozilla/Attributes.h" #include "jsarray.h" #include "jit/MIR.h" #include "jit/Snapshots.h" struct JSContext; namespace js { namespace jit { // This file contains all recover instructions. // // A recover instruction is an equivalent of a MIR instruction which is executed // before the reconstruction of a baseline frame. Recover instructions are used // by resume points to fill the value which are not produced by the code // compiled by IonMonkey. For example, if a value is optimized away by // IonMonkey, but required by Baseline, then we should have a recover // instruction to fill the missing baseline frame slot. // // Recover instructions are executed either during a bailout, or under a call // when the stack frame is introspected. If the stack is introspected, then any // use of recover instruction must lead to an invalidation of the code. // // For each MIR instruction where |canRecoverOnBailout| might return true, we // have a RInstruction of the same name. // // Recover instructions are encoded by the code generator into a compact buffer // (RecoverWriter). The MIR instruction method |writeRecoverData| should write a // tag in the |CompactBufferWriter| which is used by // |RInstruction::readRecoverData| to dispatch to the right Recover // instruction. Then |writeRecoverData| writes any local fields which are // necessary for the execution of the |recover| method. These fields are decoded // by the Recover instruction constructor which has a |CompactBufferReader| as // argument. The constructor of the Recover instruction should follow the same // sequence as the |writeRecoverData| method of the MIR instruction. // // Recover instructions are decoded by the |SnapshotIterator| (RecoverReader), // which is given as argument of the |recover| methods, in order to read the // operands. The number of operands read should be the same as the result of // |numOperands|, which corresponds to the number of operands of the MIR // instruction. Operands should be decoded in the same order as the operands of // the MIR instruction. // // The result of the |recover| method should either be a failure, or a value // stored on the |SnapshotIterator|, by using the |storeInstructionResult| // method. #define RECOVER_OPCODE_LIST(_) \ _(ResumePoint) \ _(BitNot) \ _(BitAnd) \ _(BitOr) \ _(BitXor) \ _(Lsh) \ _(Rsh) \ _(Ursh) \ _(SignExtend) \ _(Add) \ _(Sub) \ _(Mul) \ _(Div) \ _(Mod) \ _(Not) \ _(Concat) \ _(StringLength) \ _(ArgumentsLength) \ _(Floor) \ _(Ceil) \ _(Round) \ _(CharCodeAt) \ _(FromCharCode) \ _(Pow) \ _(PowHalf) \ _(MinMax) \ _(Abs) \ _(Sqrt) \ _(Atan2) \ _(Hypot) \ _(MathFunction) \ _(Random) \ _(StringSplit) \ _(NaNToZero) \ _(RegExpMatcher) \ _(RegExpSearcher) \ _(RegExpTester) \ _(StringReplace) \ _(TypeOf) \ _(ToDouble) \ _(ToFloat32) \ _(TruncateToInt32) \ _(NewObject) \ _(NewTypedArray) \ _(NewArray) \ _(NewDerivedTypedObject) \ _(CreateThisWithTemplate) \ _(Lambda) \ _(SimdBox) \ _(ObjectState) \ _(ArrayState) \ _(AtomicIsLockFree) \ _(AssertRecoveredOnBailout) class RResumePoint; class SnapshotIterator; class RInstruction { public: enum Opcode { # define DEFINE_OPCODES_(op) Recover_##op, RECOVER_OPCODE_LIST(DEFINE_OPCODES_) # undef DEFINE_OPCODES_ Recover_Invalid }; virtual Opcode opcode() const = 0; // As opposed to the MIR, there is no need to add more methods as every // other instruction is well abstracted under the "recover" method. bool isResumePoint() const { return opcode() == Recover_ResumePoint; } inline const RResumePoint* toResumePoint() const; // Number of allocations which are encoded in the Snapshot for recovering // the current instruction. virtual uint32_t numOperands() const = 0; // Function used to recover the value computed by this instruction. This // function reads its arguments from the allocations listed on the snapshot // iterator and stores its returned value on the snapshot iterator too. virtual MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const = 0; // Decode an RInstruction on top of the reserved storage space, based on the // tag written by the writeRecoverData function of the corresponding MIR // instruction. static void readRecoverData(CompactBufferReader& reader, RInstructionStorage* raw); }; #define RINSTRUCTION_HEADER_(op) \ private: \ friend class RInstruction; \ explicit R##op(CompactBufferReader& reader); \ \ public: \ Opcode opcode() const { \ return RInstruction::Recover_##op; \ } #define RINSTRUCTION_HEADER_NUM_OP_MAIN(op, numOp) \ RINSTRUCTION_HEADER_(op) \ virtual uint32_t numOperands() const { \ return numOp; \ } #ifdef DEBUG # define RINSTRUCTION_HEADER_NUM_OP_(op, numOp) \ RINSTRUCTION_HEADER_NUM_OP_MAIN(op, numOp) \ static_assert(M##op::staticNumOperands == numOp, "The recover instructions's numOperands should equal to the MIR's numOperands"); #else # define RINSTRUCTION_HEADER_NUM_OP_(op, numOp) \ RINSTRUCTION_HEADER_NUM_OP_MAIN(op, numOp) #endif class RResumePoint final : public RInstruction { private: uint32_t pcOffset_; // Offset from script->code. uint32_t numOperands_; // Number of slots. public: RINSTRUCTION_HEADER_(ResumePoint) uint32_t pcOffset() const { return pcOffset_; } virtual uint32_t numOperands() const { return numOperands_; } MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RBitNot final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(BitNot, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RBitAnd final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(BitAnd, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RBitOr final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(BitOr, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RBitXor final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(BitXor, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RLsh final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Lsh, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RRsh final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Rsh, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RUrsh final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Ursh, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RSignExtend final : public RInstruction { private: uint8_t mode_; public: RINSTRUCTION_HEADER_NUM_OP_(SignExtend, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RAdd final : public RInstruction { private: bool isFloatOperation_; public: RINSTRUCTION_HEADER_NUM_OP_(Add, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RSub final : public RInstruction { private: bool isFloatOperation_; public: RINSTRUCTION_HEADER_NUM_OP_(Sub, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RMul final : public RInstruction { private: bool isFloatOperation_; uint8_t mode_; public: RINSTRUCTION_HEADER_NUM_OP_(Mul, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RDiv final : public RInstruction { private: bool isFloatOperation_; public: RINSTRUCTION_HEADER_NUM_OP_(Div, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RMod final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Mod, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RNot final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Not, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RConcat final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Concat, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RStringLength final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(StringLength, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RArgumentsLength final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(ArgumentsLength, 0) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RFloor final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Floor, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RCeil final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Ceil, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RRound final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Round, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RCharCodeAt final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(CharCodeAt, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RFromCharCode final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(FromCharCode, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RPow final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Pow, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RPowHalf final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(PowHalf, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RMinMax final : public RInstruction { private: bool isMax_; public: RINSTRUCTION_HEADER_NUM_OP_(MinMax, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RAbs final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Abs, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RSqrt final : public RInstruction { private: bool isFloatOperation_; public: RINSTRUCTION_HEADER_NUM_OP_(Sqrt, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RAtan2 final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Atan2, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RHypot final : public RInstruction { private: uint32_t numOperands_; public: RINSTRUCTION_HEADER_(Hypot) virtual uint32_t numOperands() const { return numOperands_; } MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RMathFunction final : public RInstruction { private: uint8_t function_; public: RINSTRUCTION_HEADER_NUM_OP_(MathFunction, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RRandom final : public RInstruction { RINSTRUCTION_HEADER_NUM_OP_(Random, 0) public: MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RStringSplit final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(StringSplit, 3) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RNaNToZero final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(NaNToZero, 1); bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RRegExpMatcher final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(RegExpMatcher, 3) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RRegExpSearcher final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(RegExpSearcher, 3) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RRegExpTester final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(RegExpTester, 3) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RStringReplace final : public RInstruction { private: bool isFlatReplacement_; public: RINSTRUCTION_HEADER_NUM_OP_(StringReplace, 3) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RTypeOf final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(TypeOf, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RToDouble final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(ToDouble, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RToFloat32 final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(ToFloat32, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RTruncateToInt32 final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(TruncateToInt32, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RNewObject final : public RInstruction { private: MNewObject::Mode mode_; public: RINSTRUCTION_HEADER_NUM_OP_(NewObject, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RNewTypedArray final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(NewTypedArray, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RNewArray final : public RInstruction { private: uint32_t count_; public: RINSTRUCTION_HEADER_NUM_OP_(NewArray, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RNewDerivedTypedObject final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(NewDerivedTypedObject, 3) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RCreateThisWithTemplate final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(CreateThisWithTemplate, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RLambda final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Lambda, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RSimdBox final : public RInstruction { private: uint8_t type_; public: RINSTRUCTION_HEADER_NUM_OP_(SimdBox, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RObjectState final : public RInstruction { private: uint32_t numSlots_; // Number of slots. public: RINSTRUCTION_HEADER_(ObjectState) uint32_t numSlots() const { return numSlots_; } virtual uint32_t numOperands() const { // +1 for the object. return numSlots() + 1; } MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RArrayState final : public RInstruction { private: uint32_t numElements_; public: RINSTRUCTION_HEADER_(ArrayState) uint32_t numElements() const { return numElements_; } virtual uint32_t numOperands() const { // +1 for the array. // +1 for the initalized length. return numElements() + 2; } MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RAtomicIsLockFree final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(AtomicIsLockFree, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; class RAssertRecoveredOnBailout final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(AssertRecoveredOnBailout, 1) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const; }; #undef RINSTRUCTION_HEADER_ #undef RINSTRUCTION_HEADER_NUM_OP_ #undef RINSTRUCTION_HEADER_NUM_OP_MAIN const RResumePoint* RInstruction::toResumePoint() const { MOZ_ASSERT(isResumePoint()); return static_cast(this); } } // namespace jit } // namespace js #endif /* jit_Recover_h */