/* -*- 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_CacheIRGenerator_h
#define jit_CacheIRGenerator_h

#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"

#include <stdint.h>

#include "jstypes.h"
#include "NamespaceImports.h"

#include "jit/CacheIR.h"
#include "jit/CacheIRWriter.h"
#include "jit/ICState.h"
#include "js/Id.h"
#include "js/RootingAPI.h"
#include "js/ScalarType.h"
#include "js/TypeDecls.h"
#include "js/Value.h"
#include "js/ValueArray.h"
#include "vm/Opcodes.h"

class JSFunction;

namespace JS {
struct XrayJitInfo;
}

namespace js {

class BoundFunctionObject;
class NativeObject;
class PropertyResult;
class ProxyObject;
enum class UnaryMathFunction : uint8_t;

namespace jit {

class BaselineFrame;
class Label;
class MacroAssembler;
struct Register;
enum class InlinableNative : uint16_t;

// Some ops refer to shapes that might be in other zones. Instead of putting
// cross-zone pointers in the caches themselves (which would complicate tracing
// enormously), these ops instead contain wrappers for objects in the target
// zone, which refer to the actual shape via a reserved slot.
void LoadShapeWrapperContents(MacroAssembler& masm, Register obj, Register dst,
                              Label* failure);

class MOZ_RAII IRGenerator {
 protected:
  CacheIRWriter writer;
  JSContext* cx_;
  HandleScript script_;
  jsbytecode* pc_;
  BaselineFrame* maybeFrame_;
  CacheKind cacheKind_;
  ICState::Mode mode_;
  bool isFirstStub_;
  uint8_t numOptimizedStubs_;

  // Important: This pointer may be passed to the profiler. If this is non-null,
  // it must point to a C string literal with static lifetime, not a heap- or
  // stack- allocated string.
  const char* stubName_ = nullptr;

  IRGenerator(const IRGenerator&) = delete;
  IRGenerator& operator=(const IRGenerator&) = delete;

  bool maybeGuardInt32Index(const Value& index, ValOperandId indexId,
                            uint32_t* int32Index, Int32OperandId* int32IndexId);

  IntPtrOperandId guardToIntPtrIndex(const Value& index, ValOperandId indexId,
                                     bool supportOOB);

  ObjOperandId guardDOMProxyExpandoObjectAndShape(ProxyObject* obj,
                                                  ObjOperandId objId,
                                                  const Value& expandoVal,
                                                  NativeObject* expandoObj);

  void emitIdGuard(ValOperandId valId, const Value& idVal, jsid id);

  OperandId emitNumericGuard(ValOperandId valId, const Value& v,
                             Scalar::Type type);

  StringOperandId emitToStringGuard(ValOperandId id, const Value& v);

  void emitCalleeGuard(ObjOperandId calleeId, JSFunction* callee);

  void emitOptimisticClassGuard(ObjOperandId objId, JSObject* obj,
                                GuardClassKind kind);

  gc::AllocSite* maybeCreateAllocSite();

  friend class CacheIRSpewer;

 public:
  explicit IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
                       CacheKind cacheKind, ICState state,
                       BaselineFrame* maybeFrame = nullptr);

  const CacheIRWriter& writerRef() const { return writer; }
  CacheKind cacheKind() const { return cacheKind_; }

  // See comment on `stubName_` above.
  const char* stubName() const { return stubName_; }

  static constexpr char* NotAttached = nullptr;
};

// GetPropIRGenerator generates CacheIR for a GetProp IC.
class MOZ_RAII GetPropIRGenerator : public IRGenerator {
  HandleValue val_;
  HandleValue idVal_;

  AttachDecision tryAttachNative(HandleObject obj, ObjOperandId objId,
                                 HandleId id, ValOperandId receiverId);
  AttachDecision tryAttachObjectLength(HandleObject obj, ObjOperandId objId,
                                       HandleId id);
  AttachDecision tryAttachTypedArray(HandleObject obj, ObjOperandId objId,
                                     HandleId id);
  AttachDecision tryAttachDataView(HandleObject obj, ObjOperandId objId,
                                   HandleId id);
  AttachDecision tryAttachArrayBufferMaybeShared(HandleObject obj,
                                                 ObjOperandId objId,
                                                 HandleId id);
  AttachDecision tryAttachRegExp(HandleObject obj, ObjOperandId objId,
                                 HandleId id);
  AttachDecision tryAttachMap(HandleObject obj, ObjOperandId objId,
                              HandleId id);
  AttachDecision tryAttachSet(HandleObject obj, ObjOperandId objId,
                              HandleId id);
  AttachDecision tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId,
                                          HandleId id);
  AttachDecision tryAttachWindowProxy(HandleObject obj, ObjOperandId objId,
                                      HandleId id);
  AttachDecision tryAttachCrossCompartmentWrapper(HandleObject obj,
                                                  ObjOperandId objId,
                                                  HandleId id);
  AttachDecision tryAttachXrayCrossCompartmentWrapper(HandleObject obj,
                                                      ObjOperandId objId,
                                                      HandleId id,
                                                      ValOperandId receiverId);
  AttachDecision tryAttachFunction(HandleObject obj, ObjOperandId objId,
                                   HandleId id);
  AttachDecision tryAttachArgumentsObjectIterator(HandleObject obj,
                                                  ObjOperandId objId,
                                                  HandleId id);

  AttachDecision tryAttachGenericProxy(Handle<ProxyObject*> obj,
                                       ObjOperandId objId, HandleId id,
                                       bool handleDOMProxies);
#ifdef JS_PUNBOX64
  AttachDecision tryAttachScriptedProxy(Handle<ProxyObject*> obj,
                                        ObjOperandId objId, HandleId id);
#endif
  AttachDecision tryAttachDOMProxyExpando(Handle<ProxyObject*> obj,
                                          ObjOperandId objId, HandleId id,
                                          ValOperandId receiverId);
  AttachDecision tryAttachDOMProxyShadowed(Handle<ProxyObject*> obj,
                                           ObjOperandId objId, HandleId id);
  AttachDecision tryAttachDOMProxyUnshadowed(Handle<ProxyObject*> obj,
                                             ObjOperandId objId, HandleId id,
                                             ValOperandId receiverId);
  AttachDecision tryAttachProxy(HandleObject obj, ObjOperandId objId,
                                HandleId id, ValOperandId receiverId);

  AttachDecision tryAttachPrimitive(ValOperandId valId, HandleId id);
  AttachDecision tryAttachStringChar(ValOperandId valId, ValOperandId indexId);
  AttachDecision tryAttachStringLength(ValOperandId valId, HandleId id);

  AttachDecision tryAttachArgumentsObjectArg(HandleObject obj,
                                             ObjOperandId objId, uint32_t index,
                                             Int32OperandId indexId);
  AttachDecision tryAttachArgumentsObjectArgHole(HandleObject obj,
                                                 ObjOperandId objId,
                                                 uint32_t index,
                                                 Int32OperandId indexId);
  AttachDecision tryAttachArgumentsObjectCallee(HandleObject obj,
                                                ObjOperandId objId,
                                                HandleId id);

  AttachDecision tryAttachDenseElement(HandleObject obj, ObjOperandId objId,
                                       uint32_t index, Int32OperandId indexId);
  AttachDecision tryAttachDenseElementHole(HandleObject obj, ObjOperandId objId,
                                           uint32_t index,
                                           Int32OperandId indexId);
  AttachDecision tryAttachSparseElement(HandleObject obj, ObjOperandId objId,
                                        uint32_t index, Int32OperandId indexId);
  AttachDecision tryAttachTypedArrayElement(HandleObject obj,
                                            ObjOperandId objId);

  AttachDecision tryAttachGenericElement(HandleObject obj, ObjOperandId objId,
                                         uint32_t index, Int32OperandId indexId,
                                         ValOperandId receiverId);

  AttachDecision tryAttachProxyElement(HandleObject obj, ObjOperandId objId);

  void attachMegamorphicNativeSlot(ObjOperandId objId, jsid id);

  void attachMegamorphicNativeSlotPermissive(ObjOperandId objId, jsid id);

  ValOperandId getElemKeyValueId() const {
    MOZ_ASSERT(cacheKind_ == CacheKind::GetElem ||
               cacheKind_ == CacheKind::GetElemSuper);
    return ValOperandId(1);
  }

  ValOperandId getSuperReceiverValueId() const {
    if (cacheKind_ == CacheKind::GetPropSuper) {
      return ValOperandId(1);
    }

    MOZ_ASSERT(cacheKind_ == CacheKind::GetElemSuper);
    return ValOperandId(2);
  }

  bool isSuper() const {
    return (cacheKind_ == CacheKind::GetPropSuper ||
            cacheKind_ == CacheKind::GetElemSuper);
  }

  // If this is a GetElem cache, emit instructions to guard the incoming Value
  // matches |id|.
  void maybeEmitIdGuard(jsid id);

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
                     ICState state, CacheKind cacheKind, HandleValue val,
                     HandleValue idVal);

  AttachDecision tryAttachStub();
};

// GetNameIRGenerator generates CacheIR for a GetName IC.
class MOZ_RAII GetNameIRGenerator : public IRGenerator {
  HandleObject env_;
  Handle<PropertyName*> name_;

  AttachDecision tryAttachGlobalNameValue(ObjOperandId objId, HandleId id);
  AttachDecision tryAttachGlobalNameGetter(ObjOperandId objId, HandleId id);
  AttachDecision tryAttachEnvironmentName(ObjOperandId objId, HandleId id);

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  GetNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
                     ICState state, HandleObject env,
                     Handle<PropertyName*> name);

  AttachDecision tryAttachStub();
};

// BindNameIRGenerator generates CacheIR for a BindName IC.
class MOZ_RAII BindNameIRGenerator : public IRGenerator {
  HandleObject env_;
  Handle<PropertyName*> name_;

  AttachDecision tryAttachGlobalName(ObjOperandId objId, HandleId id);
  AttachDecision tryAttachEnvironmentName(ObjOperandId objId, HandleId id);

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  BindNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
                      ICState state, HandleObject env,
                      Handle<PropertyName*> name);

  AttachDecision tryAttachStub();
};

// SetPropIRGenerator generates CacheIR for a SetProp IC.
class MOZ_RAII SetPropIRGenerator : public IRGenerator {
  HandleValue lhsVal_;
  HandleValue idVal_;
  HandleValue rhsVal_;

 public:
  enum class DeferType { None, AddSlot };

 private:
  DeferType deferType_ = DeferType::None;

  ValOperandId setElemKeyValueId() const {
    MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    return ValOperandId(1);
  }

  ValOperandId rhsValueId() const {
    if (cacheKind_ == CacheKind::SetProp) {
      return ValOperandId(1);
    }
    MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    return ValOperandId(2);
  }

  // If this is a SetElem cache, emit instructions to guard the incoming Value
  // matches |id|.
  void maybeEmitIdGuard(jsid id);

  AttachDecision tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId,
                                        HandleId id, ValOperandId rhsId);
  AttachDecision tryAttachMegamorphicSetSlot(HandleObject obj,
                                             ObjOperandId objId, HandleId id,
                                             ValOperandId rhsId);
  AttachDecision tryAttachSetter(HandleObject obj, ObjOperandId objId,
                                 HandleId id, ValOperandId rhsId);
  AttachDecision tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId,
                                         HandleId id, ValOperandId rhsId);
  AttachDecision tryAttachWindowProxy(HandleObject obj, ObjOperandId objId,
                                      HandleId id, ValOperandId rhsId);

  AttachDecision tryAttachSetDenseElement(HandleObject obj, ObjOperandId objId,
                                          uint32_t index,
                                          Int32OperandId indexId,
                                          ValOperandId rhsId);
  AttachDecision tryAttachSetTypedArrayElement(HandleObject obj,
                                               ObjOperandId objId,
                                               ValOperandId rhsId);

  AttachDecision tryAttachSetDenseElementHole(HandleObject obj,
                                              ObjOperandId objId,
                                              uint32_t index,
                                              Int32OperandId indexId,
                                              ValOperandId rhsId);

  AttachDecision tryAttachAddOrUpdateSparseElement(HandleObject obj,
                                                   ObjOperandId objId,
                                                   uint32_t index,
                                                   Int32OperandId indexId,
                                                   ValOperandId rhsId);

  AttachDecision tryAttachGenericProxy(Handle<ProxyObject*> obj,
                                       ObjOperandId objId, HandleId id,
                                       ValOperandId rhsId,
                                       bool handleDOMProxies);
  AttachDecision tryAttachDOMProxyShadowed(Handle<ProxyObject*> obj,
                                           ObjOperandId objId, HandleId id,
                                           ValOperandId rhsId);
  AttachDecision tryAttachDOMProxyUnshadowed(Handle<ProxyObject*> obj,
                                             ObjOperandId objId, HandleId id,
                                             ValOperandId rhsId);
  AttachDecision tryAttachDOMProxyExpando(Handle<ProxyObject*> obj,
                                          ObjOperandId objId, HandleId id,
                                          ValOperandId rhsId);
  AttachDecision tryAttachProxy(HandleObject obj, ObjOperandId objId,
                                HandleId id, ValOperandId rhsId);
  AttachDecision tryAttachProxyElement(HandleObject obj, ObjOperandId objId,
                                       ValOperandId rhsId);
  AttachDecision tryAttachMegamorphicSetElement(HandleObject obj,
                                                ObjOperandId objId,
                                                ValOperandId rhsId);

  bool canAttachAddSlotStub(HandleObject obj, HandleId id);

 public:
  SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
                     CacheKind cacheKind, ICState state, HandleValue lhsVal,
                     HandleValue idVal, HandleValue rhsVal);

  AttachDecision tryAttachStub();
  AttachDecision tryAttachAddSlotStub(Handle<Shape*> oldShape);
  void trackAttached(const char* name /* must be a C string literal */);

  DeferType deferType() const { return deferType_; }
};

// HasPropIRGenerator generates CacheIR for a HasProp IC. Used for
// CacheKind::In / CacheKind::HasOwn.
class MOZ_RAII HasPropIRGenerator : public IRGenerator {
  HandleValue val_;
  HandleValue idVal_;

  AttachDecision tryAttachDense(HandleObject obj, ObjOperandId objId,
                                uint32_t index, Int32OperandId indexId);
  AttachDecision tryAttachDenseHole(HandleObject obj, ObjOperandId objId,
                                    uint32_t index, Int32OperandId indexId);
  AttachDecision tryAttachTypedArray(HandleObject obj, ObjOperandId objId,
                                     ValOperandId keyId);
  AttachDecision tryAttachSparse(HandleObject obj, ObjOperandId objId,
                                 Int32OperandId indexId);
  AttachDecision tryAttachArgumentsObjectArg(HandleObject obj,
                                             ObjOperandId objId,
                                             Int32OperandId indexId);
  AttachDecision tryAttachNamedProp(HandleObject obj, ObjOperandId objId,
                                    HandleId key, ValOperandId keyId);
  AttachDecision tryAttachMegamorphic(ObjOperandId objId, ValOperandId keyId);
  AttachDecision tryAttachSmallObjectVariableKey(HandleObject obj,
                                                 ObjOperandId objId, jsid key,
                                                 ValOperandId keyId);
  AttachDecision tryAttachNative(NativeObject* obj, ObjOperandId objId,
                                 jsid key, ValOperandId keyId,
                                 PropertyResult prop, NativeObject* holder);
  AttachDecision tryAttachSlotDoesNotExist(NativeObject* obj,
                                           ObjOperandId objId, jsid key,
                                           ValOperandId keyId);
  AttachDecision tryAttachDoesNotExist(HandleObject obj, ObjOperandId objId,
                                       HandleId key, ValOperandId keyId);
  AttachDecision tryAttachProxyElement(HandleObject obj, ObjOperandId objId,
                                       ValOperandId keyId);

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  // NOTE: Argument order is PROPERTY, OBJECT
  HasPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
                     ICState state, CacheKind cacheKind, HandleValue idVal,
                     HandleValue val);

  AttachDecision tryAttachStub();
};

class MOZ_RAII CheckPrivateFieldIRGenerator : public IRGenerator {
  HandleValue val_;
  HandleValue idVal_;

  AttachDecision tryAttachNative(NativeObject* obj, ObjOperandId objId,
                                 jsid key, ValOperandId keyId,
                                 PropertyResult prop);

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  CheckPrivateFieldIRGenerator(JSContext* cx, HandleScript script,
                               jsbytecode* pc, ICState state,
                               CacheKind cacheKind, HandleValue idVal,
                               HandleValue val);
  AttachDecision tryAttachStub();
};

class MOZ_RAII InstanceOfIRGenerator : public IRGenerator {
  HandleValue lhsVal_;
  HandleObject rhsObj_;

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  InstanceOfIRGenerator(JSContext*, HandleScript, jsbytecode*, ICState,
                        HandleValue, HandleObject);

  AttachDecision tryAttachStub();
};

class MOZ_RAII TypeOfIRGenerator : public IRGenerator {
  HandleValue val_;

  AttachDecision tryAttachPrimitive(ValOperandId valId);
  AttachDecision tryAttachObject(ValOperandId valId);
  void trackAttached(const char* name /* must be a C string literal */);

 public:
  TypeOfIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState state,
                    HandleValue value);

  AttachDecision tryAttachStub();
};

class MOZ_RAII TypeOfEqIRGenerator : public IRGenerator {
  HandleValue val_;
  JSType type_;
  JSOp compareOp_;

  AttachDecision tryAttachPrimitive(ValOperandId valId);
  AttachDecision tryAttachObject(ValOperandId valId);
  void trackAttached(const char* name /* must be a C string literal */);

 public:
  TypeOfEqIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc,
                      ICState state, HandleValue value, JSType type,
                      JSOp compareOp);

  AttachDecision tryAttachStub();
};

class MOZ_RAII GetIteratorIRGenerator : public IRGenerator {
  HandleValue val_;

  AttachDecision tryAttachObject(ValOperandId valId);
  AttachDecision tryAttachNullOrUndefined(ValOperandId valId);
  AttachDecision tryAttachGeneric(ValOperandId valId);

 public:
  GetIteratorIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc,
                         ICState state, HandleValue value);

  AttachDecision tryAttachStub();

  void trackAttached(const char* name /* must be a C string literal */);
};

class MOZ_RAII OptimizeSpreadCallIRGenerator : public IRGenerator {
  HandleValue val_;

  AttachDecision tryAttachArray();
  AttachDecision tryAttachArguments();
  AttachDecision tryAttachNotOptimizable();

 public:
  OptimizeSpreadCallIRGenerator(JSContext* cx, HandleScript script,
                                jsbytecode* pc, ICState state,
                                HandleValue value);

  AttachDecision tryAttachStub();

  void trackAttached(const char* name /* must be a C string literal */);
};

class MOZ_RAII OptimizeGetIteratorIRGenerator : public IRGenerator {
  HandleValue val_;

  AttachDecision tryAttachArray();
  AttachDecision tryAttachNotOptimizable();

 public:
  OptimizeGetIteratorIRGenerator(JSContext* cx, HandleScript script,
                                 jsbytecode* pc, ICState state,
                                 HandleValue value);

  AttachDecision tryAttachStub();

  void trackAttached(const char* name /* must be a C string literal */);
};

enum class StringChar { CharCodeAt, CodePointAt, CharAt, At };
enum class ScriptedThisResult { NoAction, UninitializedThis, PlainObjectShape };

class MOZ_RAII CallIRGenerator : public IRGenerator {
 private:
  JSOp op_;
  uint32_t argc_;
  HandleValue callee_;
  HandleValue thisval_;
  HandleValue newTarget_;
  HandleValueArray args_;

  friend class InlinableNativeIRGenerator;

  ScriptedThisResult getThisShapeForScripted(HandleFunction calleeFunc,
                                             Handle<JSObject*> newTarget,
                                             MutableHandle<Shape*> result);

  ObjOperandId emitFunCallOrApplyGuard(Int32OperandId argcId);
  ObjOperandId emitFunCallGuard(Int32OperandId argcId);
  ObjOperandId emitFunApplyGuard(Int32OperandId argcId);
  mozilla::Maybe<ObjOperandId> emitFunApplyArgsGuard(
      CallFlags::ArgFormat format);

  void emitCallScriptedGuards(ObjOperandId calleeObjId, JSFunction* calleeFunc,
                              Int32OperandId argcId, CallFlags flags,
                              Shape* thisShape, bool isBoundFunction);

  AttachDecision tryAttachFunCall(HandleFunction calleeFunc);
  AttachDecision tryAttachFunApply(HandleFunction calleeFunc);
  AttachDecision tryAttachCallScripted(HandleFunction calleeFunc);
  AttachDecision tryAttachInlinableNative(HandleFunction calleeFunc,
                                          CallFlags flags);
  AttachDecision tryAttachWasmCall(HandleFunction calleeFunc);
  AttachDecision tryAttachCallNative(HandleFunction calleeFunc);
  AttachDecision tryAttachCallHook(HandleObject calleeObj);
  AttachDecision tryAttachBoundFunction(Handle<BoundFunctionObject*> calleeObj);
  AttachDecision tryAttachBoundNative(Handle<BoundFunctionObject*> calleeObj);
  AttachDecision tryAttachBoundFunCall(Handle<BoundFunctionObject*> calleeObj);
  AttachDecision tryAttachBoundFunApply(Handle<BoundFunctionObject*> calleeObj);
  AttachDecision tryAttachFunCallBound(Handle<JSFunction*> callee);
  AttachDecision tryAttachFunApplyBound(Handle<JSFunction*> callee);

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, JSOp op,
                  ICState state, BaselineFrame* frame, uint32_t argc,
                  HandleValue callee, HandleValue thisval,
                  HandleValue newTarget, HandleValueArray args);

  AttachDecision tryAttachStub();
};

class MOZ_RAII InlinableNativeIRGenerator {
  CallIRGenerator& generator_;
  CacheIRWriter& writer;
  JSContext* cx_;

  HandleFunction target_;
  HandleValue newTarget_;
  HandleValue thisval_;
  HandleValueArray args_;
  CallFlags flags_;

  HandleScript script() const { return generator_.script_; }
  JSObject* callee() const { return &generator_.callee_.toObject(); }
  bool isFirstStub() const { return generator_.isFirstStub_; }
  bool ignoresResult() const { return generator_.op_ == JSOp::CallIgnoresRv; }
  JSOp op() const { return generator_.op_; }
  uint32_t stackArgc() const { return generator_.argc_; }

  bool isCalleeBoundFunction() const;
  BoundFunctionObject* boundCallee() const;

  bool isTargetBoundFunction() const;
  BoundFunctionObject* boundTarget() const;

  ObjOperandId emitNativeCalleeGuard(Int32OperandId argcId);
  void emitOptimisticClassGuard(ObjOperandId objId, JSObject* obj,
                                GuardClassKind kind) {
    generator_.emitOptimisticClassGuard(objId, obj, kind);
  }

  ObjOperandId emitLoadArgsArray();

  ValOperandId loadBoundArgument(ObjOperandId calleeId, size_t argIndex);

  ValOperandId loadThis(ObjOperandId calleeId);

  ValOperandId loadArgument(ObjOperandId calleeId, ArgumentKind kind);

  ValOperandId loadArgumentIntrinsic(ArgumentKind kind) {
    // Intrinsics can't be called through bound functions
    MOZ_ASSERT(target_->isIntrinsic());
    MOZ_ASSERT(flags_.getArgFormat() == CallFlags::Standard);
    return writer.loadArgumentFixedSlot(kind, stackArgc(), flags_);
  }

  bool hasBoundArguments() const;

  Int32OperandId initializeInputOperand() {
    return Int32OperandId(writer.setInputOperandId(0));
  }

  auto emitToStringGuard(ValOperandId id, const Value& v) {
    return generator_.emitToStringGuard(id, v);
  }

  auto emitNumericGuard(ValOperandId valId, const Value& v, Scalar::Type type) {
    return generator_.emitNumericGuard(valId, v, type);
  }

  auto guardToIntPtrIndex(const Value& index, ValOperandId indexId,
                          bool supportOOB) {
    return generator_.guardToIntPtrIndex(index, indexId, supportOOB);
  }

  bool canAttachAtomicsReadWriteModify();

  struct AtomicsReadWriteModifyOperands {
    ObjOperandId objId;
    IntPtrOperandId intPtrIndexId;
    OperandId numericValueId;
  };

  AtomicsReadWriteModifyOperands emitAtomicsReadWriteModifyOperands();

  enum class DateComponent {
    FullYear,
    Month,
    Date,
    Day,
    Hours,
    Minutes,
    Seconds,
  };

  AttachDecision tryAttachArrayPush();
  AttachDecision tryAttachArrayPopShift(InlinableNative native);
  AttachDecision tryAttachArrayJoin();
  AttachDecision tryAttachArraySlice();
  AttachDecision tryAttachArrayIsArray();
  AttachDecision tryAttachDataViewGet(Scalar::Type type);
  AttachDecision tryAttachDataViewSet(Scalar::Type type);
  AttachDecision tryAttachFunctionBind();
  AttachDecision tryAttachSpecializedFunctionBind(
      Handle<JSObject*> target, Handle<BoundFunctionObject*> templateObj);
  AttachDecision tryAttachUnsafeGetReservedSlot(InlinableNative native);
  AttachDecision tryAttachUnsafeSetReservedSlot();
  AttachDecision tryAttachIsSuspendedGenerator();
  AttachDecision tryAttachToObject();
  AttachDecision tryAttachToInteger();
  AttachDecision tryAttachToLength();
  AttachDecision tryAttachIsObject();
  AttachDecision tryAttachIsPackedArray();
  AttachDecision tryAttachIsCallable();
  AttachDecision tryAttachIsConstructor();
  AttachDecision tryAttachIsCrossRealmArrayConstructor();
  AttachDecision tryAttachCanOptimizeArraySpecies();
  AttachDecision tryAttachCanOptimizeStringProtoSymbolLookup();
  AttachDecision tryAttachGuardToClass(InlinableNative native);
  AttachDecision tryAttachGuardToClass(GuardClassKind kind);
  AttachDecision tryAttachGuardToEitherClass(GuardClassKind kind1,
                                             GuardClassKind kind2);
  AttachDecision tryAttachGuardToArrayBuffer();
  AttachDecision tryAttachGuardToSharedArrayBuffer();
  AttachDecision tryAttachHasClass(const JSClass* clasp,
                                   bool isPossiblyWrapped);
  AttachDecision tryAttachRegExpMatcherSearcher(InlinableNative native);
  AttachDecision tryAttachRegExpSearcherLastLimit();
  AttachDecision tryAttachRegExpHasCaptureGroups();
  AttachDecision tryAttachIsRegExpPrototypeOptimizable();
  AttachDecision tryAttachIsOptimizableRegExpObject();
  AttachDecision tryAttachIntrinsicRegExpBuiltinExec(InlinableNative native);
  AttachDecision tryAttachIntrinsicRegExpExec(InlinableNative native);
  AttachDecision tryAttachGetFirstDollarIndex();
  AttachDecision tryAttachSubstringKernel();
  AttachDecision tryAttachString();
  AttachDecision tryAttachStringConstructor();
  AttachDecision tryAttachStringToStringValueOf();
  AttachDecision tryAttachStringChar(StringChar kind);
  AttachDecision tryAttachStringCharCodeAt();
  AttachDecision tryAttachStringCodePointAt();
  AttachDecision tryAttachStringCharAt();
  AttachDecision tryAttachStringAt();
  AttachDecision tryAttachStringFromCharCode();
  AttachDecision tryAttachStringFromCodePoint();
  AttachDecision tryAttachStringIncludes();
  AttachDecision tryAttachStringIndexOf();
  AttachDecision tryAttachStringLastIndexOf();
  AttachDecision tryAttachStringStartsWith();
  AttachDecision tryAttachStringEndsWith();
  AttachDecision tryAttachStringToLowerCase();
  AttachDecision tryAttachStringToUpperCase();
  AttachDecision tryAttachStringTrim();
  AttachDecision tryAttachStringTrimStart();
  AttachDecision tryAttachStringTrimEnd();
  AttachDecision tryAttachStringReplaceString();
  AttachDecision tryAttachStringSplitString();
  AttachDecision tryAttachMathRandom();
  AttachDecision tryAttachMathAbs();
  AttachDecision tryAttachMathClz32();
  AttachDecision tryAttachMathSign();
  AttachDecision tryAttachMathImul();
  AttachDecision tryAttachMathFloor();
  AttachDecision tryAttachMathCeil();
  AttachDecision tryAttachMathTrunc();
  AttachDecision tryAttachMathRound();
  AttachDecision tryAttachMathSqrt();
  AttachDecision tryAttachMathFRound();
  AttachDecision tryAttachMathF16Round();
  AttachDecision tryAttachMathHypot();
  AttachDecision tryAttachMathATan2();
  AttachDecision tryAttachMathFunction(UnaryMathFunction fun);
  AttachDecision tryAttachMathPow();
  AttachDecision tryAttachMathMinMax(bool isMax);
  AttachDecision tryAttachSpreadMathMinMax(bool isMax);
  AttachDecision tryAttachIsTypedArray(bool isPossiblyWrapped);
  AttachDecision tryAttachIsTypedArrayConstructor();
  AttachDecision tryAttachTypedArrayByteOffset();
  AttachDecision tryAttachTypedArrayElementSize();
  AttachDecision tryAttachTypedArrayLength(bool isPossiblyWrapped,
                                           bool allowOutOfBounds);
  AttachDecision tryAttachIsConstructing();
  AttachDecision tryAttachGetNextMapSetEntryForIterator(bool isMap);
  AttachDecision tryAttachNewArrayIterator();
  AttachDecision tryAttachNewStringIterator();
  AttachDecision tryAttachNewRegExpStringIterator();
  AttachDecision tryAttachArrayIteratorPrototypeOptimizable();
  AttachDecision tryAttachObjectCreate();
  AttachDecision tryAttachObjectConstructor();
  AttachDecision tryAttachArrayConstructor();
  AttachDecision tryAttachTypedArrayConstructor();
  AttachDecision tryAttachMapSetConstructor(InlinableNative native);
  AttachDecision tryAttachNumber();
  AttachDecision tryAttachNumberParseInt();
  AttachDecision tryAttachNumberToString();
  AttachDecision tryAttachReflectGetPrototypeOf();
  AttachDecision tryAttachAtomicsCompareExchange();
  AttachDecision tryAttachAtomicsExchange();
  AttachDecision tryAttachAtomicsAdd();
  AttachDecision tryAttachAtomicsSub();
  AttachDecision tryAttachAtomicsAnd();
  AttachDecision tryAttachAtomicsOr();
  AttachDecision tryAttachAtomicsXor();
  AttachDecision tryAttachAtomicsLoad();
  AttachDecision tryAttachAtomicsStore();
  AttachDecision tryAttachAtomicsIsLockFree();
  AttachDecision tryAttachAtomicsPause();
  AttachDecision tryAttachBoolean();
  AttachDecision tryAttachBailout();
  AttachDecision tryAttachAssertFloat32();
  AttachDecision tryAttachAssertRecoveredOnBailout();
  AttachDecision tryAttachObjectIs();
  AttachDecision tryAttachObjectIsPrototypeOf();
  AttachDecision tryAttachObjectKeys();
  AttachDecision tryAttachObjectToString();
  AttachDecision tryAttachBigInt();
  AttachDecision tryAttachBigIntAsIntN();
  AttachDecision tryAttachBigIntAsUintN();
  AttachDecision tryAttachSetHas();
  AttachDecision tryAttachSetDelete();
  AttachDecision tryAttachSetAdd();
  AttachDecision tryAttachSetSize();
  AttachDecision tryAttachMapHas();
  AttachDecision tryAttachMapGet();
  AttachDecision tryAttachMapDelete();
  AttachDecision tryAttachMapSet();
  AttachDecision tryAttachDateGetTime(InlinableNative native);
  AttachDecision tryAttachDateGet(DateComponent component);
#ifdef FUZZING_JS_FUZZILLI
  AttachDecision tryAttachFuzzilliHash();
#endif

  void trackAttached(const char* name /* must be a C string literal */) {
    return generator_.trackAttached(name);
  }

 public:
  InlinableNativeIRGenerator(CallIRGenerator& generator, HandleFunction target,
                             HandleValue newTarget, HandleValue thisValue,
                             HandleValueArray args, CallFlags flags)
      : generator_(generator),
        writer(generator.writer),
        cx_(generator.cx_),
        target_(target),
        newTarget_(newTarget),
        thisval_(thisValue),
        args_(args),
        flags_(flags) {}

  AttachDecision tryAttachStub();
};

class MOZ_RAII CompareIRGenerator : public IRGenerator {
  JSOp op_;
  HandleValue lhsVal_;
  HandleValue rhsVal_;

  AttachDecision tryAttachString(ValOperandId lhsId, ValOperandId rhsId);
  AttachDecision tryAttachObject(ValOperandId lhsId, ValOperandId rhsId);
  AttachDecision tryAttachSymbol(ValOperandId lhsId, ValOperandId rhsId);
  AttachDecision tryAttachStrictDifferentTypes(ValOperandId lhsId,
                                               ValOperandId rhsId);
  AttachDecision tryAttachInt32(ValOperandId lhsId, ValOperandId rhsId);
  AttachDecision tryAttachNumber(ValOperandId lhsId, ValOperandId rhsId);
  AttachDecision tryAttachBigInt(ValOperandId lhsId, ValOperandId rhsId);
  AttachDecision tryAttachAnyNullUndefined(ValOperandId lhsId,
                                           ValOperandId rhsId);
  AttachDecision tryAttachNullUndefined(ValOperandId lhsId, ValOperandId rhsId);
  AttachDecision tryAttachStringNumber(ValOperandId lhsId, ValOperandId rhsId);
  AttachDecision tryAttachPrimitiveSymbol(ValOperandId lhsId,
                                          ValOperandId rhsId);
  AttachDecision tryAttachBigIntInt32(ValOperandId lhsId, ValOperandId rhsId);
  AttachDecision tryAttachBigIntNumber(ValOperandId lhsId, ValOperandId rhsId);
  AttachDecision tryAttachBigIntString(ValOperandId lhsId, ValOperandId rhsId);

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  CompareIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState state,
                     JSOp op, HandleValue lhsVal, HandleValue rhsVal);

  AttachDecision tryAttachStub();
};

class MOZ_RAII ToBoolIRGenerator : public IRGenerator {
  HandleValue val_;

  AttachDecision tryAttachBool();
  AttachDecision tryAttachInt32();
  AttachDecision tryAttachNumber();
  AttachDecision tryAttachString();
  AttachDecision tryAttachSymbol();
  AttachDecision tryAttachNullOrUndefined();
  AttachDecision tryAttachObject();
  AttachDecision tryAttachBigInt();

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  ToBoolIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState state,
                    HandleValue val);

  AttachDecision tryAttachStub();
};

class MOZ_RAII LazyConstantIRGenerator : public IRGenerator {
  HandleValue val_;

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  LazyConstantIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc,
                          ICState state, HandleValue val);

  AttachDecision tryAttachStub();
};

class MOZ_RAII UnaryArithIRGenerator : public IRGenerator {
  JSOp op_;
  HandleValue val_;
  HandleValue res_;

  AttachDecision tryAttachInt32();
  AttachDecision tryAttachNumber();
  AttachDecision tryAttachBitwise();
  AttachDecision tryAttachBigInt();
  AttachDecision tryAttachBigIntPtr();
  AttachDecision tryAttachStringInt32();
  AttachDecision tryAttachStringNumber();

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  UnaryArithIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc,
                        ICState state, JSOp op, HandleValue val,
                        HandleValue res);

  AttachDecision tryAttachStub();
};

class MOZ_RAII ToPropertyKeyIRGenerator : public IRGenerator {
  HandleValue val_;

  AttachDecision tryAttachInt32();
  AttachDecision tryAttachNumber();
  AttachDecision tryAttachString();
  AttachDecision tryAttachSymbol();

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  ToPropertyKeyIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc,
                           ICState state, HandleValue val);

  AttachDecision tryAttachStub();
};

class MOZ_RAII BinaryArithIRGenerator : public IRGenerator {
  JSOp op_;
  HandleValue lhs_;
  HandleValue rhs_;
  HandleValue res_;

  void trackAttached(const char* name /* must be a C string literal */);

  AttachDecision tryAttachInt32();
  AttachDecision tryAttachDouble();
  AttachDecision tryAttachBitwise();
  AttachDecision tryAttachStringConcat();
  AttachDecision tryAttachStringObjectConcat();
  AttachDecision tryAttachBigInt();
  AttachDecision tryAttachBigIntPtr();
  AttachDecision tryAttachStringInt32Arith();
  AttachDecision tryAttachStringNumberArith();
  AttachDecision tryAttachDateArith();

 public:
  BinaryArithIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc,
                         ICState state, JSOp op, HandleValue lhs,
                         HandleValue rhs, HandleValue res);

  AttachDecision tryAttachStub();
};

class MOZ_RAII NewArrayIRGenerator : public IRGenerator {
#ifdef JS_CACHEIR_SPEW
  JSOp op_;
#endif
  HandleObject templateObject_;

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  NewArrayIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc,
                      ICState state, JSOp op, HandleObject templateObj,
                      BaselineFrame* frame);

  AttachDecision tryAttachStub();
  AttachDecision tryAttachArrayObject();
};

class MOZ_RAII NewObjectIRGenerator : public IRGenerator {
#ifdef JS_CACHEIR_SPEW
  JSOp op_;
#endif
  HandleObject templateObject_;

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  NewObjectIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc,
                       ICState state, JSOp op, HandleObject templateObj,
                       BaselineFrame* frame);

  AttachDecision tryAttachStub();
  AttachDecision tryAttachPlainObject();
};

class MOZ_RAII LambdaIRGenerator : public IRGenerator {
#ifdef JS_CACHEIR_SPEW
  JSOp op_;
#endif
  Handle<JSFunction*> canonicalFunction_;

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  LambdaIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
                    ICState state, JSOp op,
                    Handle<JSFunction*> canonicalFunction,
                    BaselineFrame* frame);

  AttachDecision tryAttachStub();
  AttachDecision tryAttachFunctionClone();
};

// Returns true for bytecode ops that can use InlinableNativeIRGenerator.
inline bool BytecodeCallOpCanHaveInlinableNative(JSOp op) {
  return op == JSOp::Call || op == JSOp::CallContent || op == JSOp::New ||
         op == JSOp::NewContent || op == JSOp::CallIgnoresRv ||
         op == JSOp::SpreadCall;
}

inline bool BytecodeOpCanHaveAllocSite(JSOp op) {
  return BytecodeCallOpCanHaveInlinableNative(op) || op == JSOp::NewArray ||
         op == JSOp::NewObject || op == JSOp::NewInit || op == JSOp::CallIter ||
         op == JSOp::CallContentIter || op == JSOp::Lambda;
}

class MOZ_RAII CloseIterIRGenerator : public IRGenerator {
  HandleObject iter_;
  CompletionKind kind_;

  void trackAttached(const char* name /* must be a C string literal */);

 public:
  CloseIterIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc,
                       ICState state, HandleObject iter, CompletionKind kind);

  AttachDecision tryAttachStub();
  AttachDecision tryAttachNoReturnMethod();
  AttachDecision tryAttachScriptedReturn();
};

class MOZ_RAII GetImportIRGenerator : public IRGenerator {
  void trackAttached(const char* name /* must be a C string literal */);

 public:
  GetImportIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
                       ICState state);

  AttachDecision tryAttachStub();
  AttachDecision tryAttachInitialized();
};

// Retrieve Xray JIT info set by the embedder.
extern JS::XrayJitInfo* GetXrayJitInfo();

}  // namespace jit
}  // namespace js

#endif /* jit_CacheIRGenerator_h */
