text: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
   * vim: set ts=4 sw=4 et 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/. */
  
  #if !defined jsjaeger_remat_h__ && defined JS_METHODJIT
  #define jsjaeger_remat_h__
  
  #include "jscntxt.h"
  #include "MachineRegs.h"
  #include "assembler/assembler/MacroAssembler.h"
  #include "vm/Stack.h"
  
  namespace js {
  namespace mjit {
  
  // Lightweight, union-able components of FrameEntry.
  struct StateRemat {
      typedef JSC::MacroAssembler::RegisterID RegisterID;
      typedef JSC::MacroAssembler::Address Address;
  
      static const int32_t CONSTANT = -int(UINT16_LIMIT * sizeof(Value));
  
      // This union encodes the fastest rematerialization of a non-constant
      // value. The |offset| field can be used to recover information
      // without this struct's helpers:
      //  1) A value in (CONSTANT, 0) is an argument slot.
      //  2) A value in [0, fp) is a register ID.
      //  3) A value in [fp, inf) is a local slot.
      union {
          RegisterID  reg_;
          int32_t     offset_;
      };
  
      static StateRemat FromInt32(int32_t i32) {
          StateRemat sr;
          sr.offset_ = i32;
          return sr;
      }
      static StateRemat FromRegister(RegisterID reg) {
          StateRemat sr;
          sr.reg_ = reg;
          JS_ASSERT(sr.inRegister());
          return sr;
      }
      static StateRemat FromAddress(Address address) {
          JS_ASSERT(address.base == JSFrameReg);
          StateRemat sr;
          sr.offset_ = address.offset;
          JS_ASSERT(sr.inMemory());
          return sr;
      }
  
      // Minimum number of bits needed to compactly store the int32_t
      // representation in a struct or union. This prevents bloating the IC
      // structs by an extra 8 bytes in some cases. 16 bits are needed to encode
      // the largest local:
      //   ((UINT16_LIMIT - 1) * sizeof(Value) + sizeof(StackFrame),
      // And an extra bit for the sign on arguments.
  #define MIN_STATE_REMAT_BITS        21
  
      bool isConstant() const { return offset_ == CONSTANT; }
      bool inRegister() const { return offset_ >= 0 &&
                                       offset_ <= int32_t(JSC::MacroAssembler::TotalRegisters); }
      bool inMemory() const {
          return offset_ >= int32_t(sizeof(StackFrame)) ||
                 offset_ < 0;
      }
  
      int32_t toInt32() const { return offset_; }
      Address address() const {
          JS_ASSERT(inMemory());
          return Address(JSFrameReg, offset_);
      }
      RegisterID reg() const {
          JS_ASSERT(inRegister());
          return reg_;
      }
  };
  
  /* Lightweight version of FrameEntry. */
  struct ValueRemat {
      typedef JSC::MacroAssembler::RegisterID RegisterID;
      typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
      union {
          struct {
              union {
                  int32_t     typeRemat_;
                  JSValueType knownType_;
              } type;
              int32_t dataRemat_   : MIN_STATE_REMAT_BITS;
              bool    isTypeKnown_ : 1;
          } s;
          jsval v_;
          FPRegisterID fpreg_;
      } u;
      bool isConstant_    : 1;
      bool isFPRegister_  : 1;
      bool isDataSynced   : 1;
      bool isTypeSynced   : 1;
  
      static ValueRemat FromConstant(const Value &v) {
          ValueRemat vr;
          vr.isConstant_ = true;
          vr.isFPRegister_ = false;
          vr.u.v_ = v;
          return vr;
      }
      static ValueRemat FromFPRegister(FPRegisterID fpreg) {
          ValueRemat vr;
          vr.isConstant_ = false;
          vr.isFPRegister_ = true;
          vr.u.fpreg_ = fpreg;
          return vr;
      }
      static ValueRemat FromKnownType(JSValueType type, RegisterID dataReg) {
          ValueRemat vr;
          vr.isConstant_ = false;
          vr.isFPRegister_ = false;
          vr.u.s.type.knownType_ = type;
          vr.u.s.isTypeKnown_ = true;
          vr.u.s.dataRemat_ = StateRemat::FromRegister(dataReg).toInt32();
  
          // Assert bitfields are okay.
          JS_ASSERT(vr.dataReg() == dataReg);
          return vr;
      }
      static ValueRemat FromRegisters(RegisterID typeReg, RegisterID dataReg) {
          ValueRemat vr;
          vr.isConstant_ = false;
          vr.isFPRegister_ = false;
          vr.u.s.isTypeKnown_ = false;
          vr.u.s.type.typeRemat_ = StateRemat::FromRegister(typeReg).toInt32();
          vr.u.s.dataRemat_ = StateRemat::FromRegister(dataReg).toInt32();
  
          // Assert bitfields are okay.
          JS_ASSERT(vr.dataReg() == dataReg);
          JS_ASSERT(vr.typeReg() == typeReg);
          return vr;
      }
  
      FPRegisterID fpReg() const {
          JS_ASSERT(isFPRegister());
          return u.fpreg_;
      }
      RegisterID dataReg() const {
          JS_ASSERT(!isConstant() && !isFPRegister());
          return dataRemat().reg();
      }
      RegisterID typeReg() const {
          JS_ASSERT(!isTypeKnown());
          return typeRemat().reg();
      }
  
      bool isConstant() const { return isConstant_; }
      bool isFPRegister() const { return isFPRegister_; }
      bool isTypeKnown() const { return isConstant() || isFPRegister() || u.s.isTypeKnown_; }
  
      StateRemat dataRemat() const {
          JS_ASSERT(!isConstant());
          return StateRemat::FromInt32(u.s.dataRemat_);
      }
      StateRemat typeRemat() const {
          JS_ASSERT(!isTypeKnown());
          return StateRemat::FromInt32(u.s.type.typeRemat_);
      }
      Value value() const {
          JS_ASSERT(isConstant());
          return u.v_;
      }
      JSValueType knownType() const {
          JS_ASSERT(isTypeKnown());
          if (isConstant()) {
              const Value v = value();
              if (v.isDouble())
                  return JSVAL_TYPE_DOUBLE;
              return v.extractNonDoubleType();
          }
          if (isFPRegister())
              return JSVAL_TYPE_DOUBLE;
          return u.s.type.knownType_;
      }
      bool isType(JSValueType type_) const {
          return isTypeKnown() && knownType() == type_;
      }
  };
  
  /*
   * Describes how to rematerialize a value during compilation.
   */
  struct RematInfo {
      typedef JSC::MacroAssembler::RegisterID RegisterID;
      typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
  
      enum SyncState {
          SYNCED,
          UNSYNCED
      };
  
      enum RematType {
          TYPE,
          DATA
      };
  
      /* Physical location. */
      enum PhysLoc {
          /*
           * Backing bits are in memory. No fast remat.
           */
          PhysLoc_Memory = 0,
  
          /* Backing bits are known at compile time. */
          PhysLoc_Constant,
  
          /* Backing bits are in a general purpose register. */
          PhysLoc_Register,
  
          /* Backing bits are part of a floating point register. */
          PhysLoc_FPRegister,
  
          /* Backing bits are invalid/unknown. */
          PhysLoc_Invalid
      };
  
      void setRegister(RegisterID reg) {
          reg_ = reg;
          location_ = PhysLoc_Register;
      }
  
      RegisterID reg() const {
          JS_ASSERT(inRegister());
          return reg_;
      }
  
      void setFPRegister(FPRegisterID reg) {
          fpreg_ = reg;
          location_ = PhysLoc_FPRegister;
      }
  
      FPRegisterID fpreg() const {
          JS_ASSERT(inFPRegister());
          return fpreg_;
      }
  
      void setMemory() {
          location_ = PhysLoc_Memory;
          sync_ = SYNCED;
      }
  
  #ifdef DEBUG
      void invalidate() {
          location_ = PhysLoc_Invalid;
      }
  #else
      void invalidate() {}
  #endif
  
      void setConstant() { location_ = PhysLoc_Constant; }
  
      bool isConstant() const {
          JS_ASSERT(location_ != PhysLoc_Invalid);
          return location_ == PhysLoc_Constant;
      }
  
      bool inRegister() const {
          JS_ASSERT(location_ != PhysLoc_Invalid);
          return location_ == PhysLoc_Register;
      }
  
      bool inFPRegister() const {
          JS_ASSERT(location_ != PhysLoc_Invalid);
          return location_ == PhysLoc_FPRegister;
      }
  
      bool inMemory() const {
          JS_ASSERT(location_ != PhysLoc_Invalid);
          return location_ == PhysLoc_Memory;
      }
  
      bool synced() const { return sync_ == SYNCED; }
      void sync() {
          JS_ASSERT(!synced());
          sync_ = SYNCED;
      }
      void unsync() {
          sync_ = UNSYNCED;
      }
  
      void inherit(const RematInfo &other) {
          JS_STATIC_ASSERT(sizeof(RegisterID) == sizeof(FPRegisterID));
          reg_ = other.reg_;
          location_ = other.location_;
      }
  
    private:
      union {
          /* Set if location is PhysLoc_Register. */
          RegisterID reg_;
  
          /*
           * Set if location is PhysLoc_FPRegister.  This must be the data for a FE,
           * and the known type is JSVAL_TYPE_DOUBLE.
           */
          FPRegisterID fpreg_;
      };
  
      /* Remat source. */
      PhysLoc location_;
  
      /* Sync state. */
      SyncState sync_;
  };
  
  template <class T>
  class MaybeRegister {
    public:
      MaybeRegister()
        : reg_((T)0), set(false)
      { }
  
      MaybeRegister(T reg)
        : reg_(reg), set(true)
      { }
  
      inline T reg() const { JS_ASSERT(set); return reg_; }
      inline void setReg(T r) { reg_ = r; set = true; }
      inline bool isSet() const { return set; }
  
      MaybeRegister<T> & operator =(const MaybeRegister<T> &other) {
          set = other.set;
          reg_ = other.reg_;
          return *this;
      }
  
      MaybeRegister<T> & operator =(T r) {
          setReg(r);
          return *this;
      }
  
    private:
      T reg_;
      bool set;
  };
  
  typedef MaybeRegister<JSC::MacroAssembler::RegisterID> MaybeRegisterID;
  typedef MaybeRegister<JSC::MacroAssembler::FPRegisterID> MaybeFPRegisterID;
  
  class MaybeJump {
      typedef JSC::MacroAssembler::Jump Jump;
    public:
      MaybeJump()
        : set(false)
      { }
  
      inline Jump getJump() const { JS_ASSERT(set); return jump; }
      inline Jump get() const { JS_ASSERT(set); return jump; }
      inline void setJump(const Jump &j) { jump = j; set = true; }
      inline bool isSet() const { return set; }
  
      inline MaybeJump &operator=(Jump j) { setJump(j); return *this; }
  
    private:
      Jump jump;
      bool set;
  };
  
  } /* namespace mjit */
  } /* namespace js */
  
  #endif
  
  