/* -*- 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/. */

#ifndef jit_Recover_h
#define jit_Recover_h

#include "mozilla/Attributes.h"

#include "jit/MIR-wasm.h"
#include "jit/MIR.h"
#include "jit/Snapshots.h"

namespace js {
namespace jit {

// [SMDOC] IonMonkey Recover Instructions
//
// 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)                         \
  _(SignExtendInt32)              \
  _(Add)                          \
  _(Sub)                          \
  _(Mul)                          \
  _(Div)                          \
  _(Mod)                          \
  _(Not)                          \
  _(BigIntAdd)                    \
  _(BigIntSub)                    \
  _(BigIntMul)                    \
  _(BigIntDiv)                    \
  _(BigIntMod)                    \
  _(BigIntPow)                    \
  _(BigIntBitAnd)                 \
  _(BigIntBitOr)                  \
  _(BigIntBitXor)                 \
  _(BigIntLsh)                    \
  _(BigIntRsh)                    \
  _(BigIntIncrement)              \
  _(BigIntDecrement)              \
  _(BigIntNegate)                 \
  _(BigIntBitNot)                 \
  _(BigIntToIntPtr)               \
  _(IntPtrToBigInt)               \
  _(BigIntPtrAdd)                 \
  _(BigIntPtrSub)                 \
  _(BigIntPtrMul)                 \
  _(BigIntPtrDiv)                 \
  _(BigIntPtrMod)                 \
  _(BigIntPtrPow)                 \
  _(BigIntPtrBitAnd)              \
  _(BigIntPtrBitOr)               \
  _(BigIntPtrBitXor)              \
  _(BigIntPtrLsh)                 \
  _(BigIntPtrRsh)                 \
  _(BigIntPtrBitNot)              \
  _(Compare)                      \
  _(StrictConstantCompareInt32)   \
  _(StrictConstantCompareBoolean) \
  _(Concat)                       \
  _(StringLength)                 \
  _(ArgumentsLength)              \
  _(Floor)                        \
  _(Ceil)                         \
  _(Round)                        \
  _(Trunc)                        \
  _(CharCodeAt)                   \
  _(FromCharCode)                 \
  _(FromCharCodeEmptyIfNegative)  \
  _(Pow)                          \
  _(PowHalf)                      \
  _(MinMax)                       \
  _(Abs)                          \
  _(Sqrt)                         \
  _(Atan2)                        \
  _(Hypot)                        \
  _(NearbyInt)                    \
  _(Sign)                         \
  _(MathFunction)                 \
  _(Random)                       \
  _(StringSplit)                  \
  _(NaNToZero)                    \
  _(StringReplace)                \
  _(Substr)                       \
  _(TypeOf)                       \
  _(TypeOfName)                   \
  _(ToDouble)                     \
  _(ToFloat32)                    \
  _(ToFloat16)                    \
  _(TruncateToInt32)              \
  _(NewObject)                    \
  _(NewPlainObject)               \
  _(NewArrayObject)               \
  _(NewTypedArray)                \
  _(NewArray)                     \
  _(NewIterator)                  \
  _(NewCallObject)                \
  _(Lambda)                       \
  _(FunctionWithProto)            \
  _(ObjectKeys)                   \
  _(ObjectState)                  \
  _(ArrayState)                   \
  _(AtomicIsLockFree)             \
  _(Int64ToBigInt)                \
  _(BigIntAsIntN)                 \
  _(BigIntAsUintN)                \
  _(CreateArgumentsObject)        \
  _(CreateInlinedArgumentsObject) \
  _(Rest)                         \
  _(AssertRecoveredOnBailout)

class RResumePoint;
class SnapshotIterator;

class MOZ_NON_PARAM 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;

  // Call the copy constructor of a specific RInstruction, to do a copy of the
  // RInstruction content.
  virtual void cloneInto(RInstructionStorage* raw) const = 0;

  // 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.
  [[nodiscard]] virtual 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);                          \
  explicit R##op(const R##op& src) = default;                           \
                                                                        \
 public:                                                                \
  Opcode opcode() const override { return RInstruction::Recover_##op; } \
  void cloneInto(RInstructionStorage* raw) const override {             \
    new (raw->addr()) R##op(*this);                                     \
  }

#define RINSTRUCTION_HEADER_NUM_OP_MAIN(op, numOp) \
  RINSTRUCTION_HEADER_(op)                         \
  uint32_t numOperands() const override { 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 pcOffsetAndMode_;  // Offset from script->code and ResumeMode.
  uint32_t numOperands_;      // Number of slots.

 public:
  RINSTRUCTION_HEADER_(ResumePoint)

  // Used to encode/decode pcOffsetAndMode_.
  static constexpr uint32_t PCOffsetShift = 4;
  static constexpr uint32_t ResumeModeMask = 0b1111;
  static_assert(uint32_t(ResumeMode::Last) <= ResumeModeMask);

  uint32_t pcOffset() const { return pcOffsetAndMode_ >> PCOffsetShift; }
  ResumeMode mode() const {
    return ResumeMode(pcOffsetAndMode_ & ResumeModeMask);
  }

  uint32_t numOperands() const override { return numOperands_; }
  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBitNot final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BitNot, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBitAnd final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BitAnd, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBitOr final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BitOr, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBitXor final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BitXor, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RLsh final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Lsh, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RRsh final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Rsh, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RUrsh final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Ursh, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RSignExtendInt32 final : public RInstruction {
 private:
  uint8_t mode_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(SignExtendInt32, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RAdd final : public RInstruction {
 private:
  bool isFloatOperation_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(Add, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RSub final : public RInstruction {
 private:
  bool isFloatOperation_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(Sub, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RMul final : public RInstruction {
 private:
  bool isFloatOperation_;
  uint8_t mode_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(Mul, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RDiv final : public RInstruction {
 private:
  bool isFloatOperation_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(Div, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RMod final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Mod, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RNot final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Not, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntAdd final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntAdd, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntSub final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntSub, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntMul final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntMul, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntDiv final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntDiv, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntMod final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntMod, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPow final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPow, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntBitAnd final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntBitAnd, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntBitOr final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntBitOr, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntBitXor final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntBitXor, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntLsh final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntLsh, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntRsh final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntRsh, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntIncrement final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntIncrement, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntDecrement final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntDecrement, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntNegate final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntNegate, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntBitNot final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntBitNot, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntToIntPtr final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntToIntPtr, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RIntPtrToBigInt final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(IntPtrToBigInt, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrAdd final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrAdd, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrSub final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrSub, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrMul final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrMul, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrDiv final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrDiv, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrMod final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrMod, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrPow final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrPow, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrBitAnd final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrBitAnd, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrBitOr final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrBitOr, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrBitXor final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrBitXor, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrLsh final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrLsh, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrRsh final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrRsh, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntPtrBitNot final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntPtrBitNot, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RCompare final : public RInstruction {
  JSOp jsop_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(Compare, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RStrictConstantCompareInt32 final : public RInstruction {
  JSOp jsop_;
  int32_t constant_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(StrictConstantCompareInt32, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RStrictConstantCompareBoolean final : public RInstruction {
  JSOp jsop_;
  bool constant_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(StrictConstantCompareBoolean, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RConcat final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Concat, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RStringLength final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(StringLength, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RArgumentsLength final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(ArgumentsLength, 0)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RFloor final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Floor, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RCeil final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Ceil, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RRound final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Round, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RTrunc final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Trunc, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RCharCodeAt final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(CharCodeAt, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RFromCharCode final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(FromCharCode, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RFromCharCodeEmptyIfNegative final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(FromCharCodeEmptyIfNegative, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RPow final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Pow, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RPowHalf final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(PowHalf, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RMinMax final : public RInstruction {
 private:
  bool isMax_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(MinMax, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RAbs final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Abs, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RSqrt final : public RInstruction {
 private:
  bool isFloatOperation_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(Sqrt, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RAtan2 final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Atan2, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RHypot final : public RInstruction {
 private:
  uint32_t numOperands_;

 public:
  RINSTRUCTION_HEADER_(Hypot)

  uint32_t numOperands() const override { return numOperands_; }

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RNearbyInt final : public RInstruction {
 private:
  uint8_t roundingMode_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(NearbyInt, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RSign final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Sign, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RMathFunction final : public RInstruction {
 private:
  UnaryMathFunction function_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(MathFunction, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RRandom final : public RInstruction {
  RINSTRUCTION_HEADER_NUM_OP_(Random, 0)
 public:
  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RStringSplit final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(StringSplit, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RNaNToZero final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(NaNToZero, 1);

  bool recover(JSContext* cx, SnapshotIterator& iter) const override;
};

class RStringReplace final : public RInstruction {
 private:
  bool isFlatReplacement_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(StringReplace, 3)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RSubstr final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Substr, 3)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RTypeOf final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(TypeOf, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RTypeOfName final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(TypeOfName, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RToDouble final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(ToDouble, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RToFloat32 final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(ToFloat32, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RToFloat16 final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(ToFloat16, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RTruncateToInt32 final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(TruncateToInt32, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RNewObject final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(NewObject, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RNewPlainObject final : public RInstruction {
 private:
  gc::AllocKind allocKind_;
  gc::Heap initialHeap_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(NewPlainObject, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RNewArrayObject final : public RInstruction {
 private:
  uint32_t length_;
  gc::Heap initialHeap_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(NewArrayObject, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RNewTypedArray final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(NewTypedArray, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RNewArray final : public RInstruction {
 private:
  uint32_t count_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(NewArray, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RNewIterator final : public RInstruction {
 private:
  uint8_t type_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(NewIterator, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RLambda final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(Lambda, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RFunctionWithProto final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(FunctionWithProto, 3)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RNewCallObject final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(NewCallObject, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RObjectKeys final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(ObjectKeys, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RObjectState final : public RInstruction {
 private:
  uint32_t numSlots_;  // Number of slots.

 public:
  RINSTRUCTION_HEADER_(ObjectState)

  uint32_t numSlots() const { return numSlots_; }
  uint32_t numOperands() const override {
    // +1 for the object.
    return numSlots() + 1;
  }

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RArrayState final : public RInstruction {
 private:
  uint32_t numElements_;

 public:
  RINSTRUCTION_HEADER_(ArrayState)

  uint32_t numElements() const { return numElements_; }
  uint32_t numOperands() const override {
    // +1 for the array.
    // +1 for the initalized length.
    return numElements() + 2;
  }

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RAtomicIsLockFree final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(AtomicIsLockFree, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RInt64ToBigInt final : public RInstruction {
  bool isSigned_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(Int64ToBigInt, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntAsIntN final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntAsIntN, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RBigIntAsUintN final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(BigIntAsUintN, 2)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RCreateArgumentsObject final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(CreateArgumentsObject, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RCreateInlinedArgumentsObject final : public RInstruction {
 private:
  uint32_t numActuals_;

 public:
  RINSTRUCTION_HEADER_(CreateInlinedArgumentsObject)

  uint32_t numActuals() const { return numActuals_; }
  uint32_t numOperands() const override {
    // +1 for the callObj.
    // +1 for the callee.
    return numActuals() + 2;
  }

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RRest final : public RInstruction {
  uint32_t numFormals_;

 public:
  RINSTRUCTION_HEADER_NUM_OP_(Rest, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

class RAssertRecoveredOnBailout final : public RInstruction {
 public:
  RINSTRUCTION_HEADER_NUM_OP_(AssertRecoveredOnBailout, 1)

  [[nodiscard]] bool recover(JSContext* cx,
                             SnapshotIterator& iter) const override;
};

#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<const RResumePoint*>(this);
}

}  // namespace jit
}  // namespace js

#endif /* jit_Recover_h */
