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/. */
  
  #ifndef jsion_move_group_resolver_h__
  #define jsion_move_group_resolver_h__
  
  #include "Registers.h"
  #include "InlineList.h"
  #include "IonAllocPolicy.h"
  
  namespace js {
  namespace ion {
  
  class MoveResolver
  {
    public:
      // This is similar to Operand, but carries more information. We're also not
      // guaranteed that Operand looks like this on all ISAs.
      class MoveOperand
      {
          enum Kind {
              REG,
              FLOAT_REG,
              ADDRESS,
              EFFECTIVE_ADDRESS
          };
  
          Kind kind_;
          uint32 code_;
          int32 disp_;
  
        public:
          enum AddressKind {
              MEMORY = ADDRESS,
              EFFECTIVE = EFFECTIVE_ADDRESS
          };
  
          MoveOperand()
          { }
          explicit MoveOperand(const Register &reg) : kind_(REG), code_(reg.code())
          { }
          explicit MoveOperand(const FloatRegister &reg) : kind_(FLOAT_REG), code_(reg.code())
          { }
          MoveOperand(const Register &reg, int32 disp, AddressKind addrKind = MEMORY)
              : kind_((Kind) addrKind),
              code_(reg.code()),
              disp_(disp)
          { }
          MoveOperand(const MoveOperand &other)
            : kind_(other.kind_),
              code_(other.code_),
              disp_(other.disp_)
          { }
          bool isFloatReg() const {
              return kind_ == FLOAT_REG;
          }
          bool isGeneralReg() const {
              return kind_ == REG;
          }
          bool isDouble() const {
              return kind_ == FLOAT_REG;
          }
          bool isMemory() const {
              return kind_ == ADDRESS;
          }
          bool isEffectiveAddress() const {
              return kind_ == EFFECTIVE_ADDRESS;
          }
          Register reg() const {
              JS_ASSERT(isGeneralReg());
              return Register::FromCode(code_);
          }
          FloatRegister floatReg() const {
              JS_ASSERT(isFloatReg());
              return FloatRegister::FromCode(code_);
          }
          Register base() const {
              JS_ASSERT(isMemory() || isEffectiveAddress());
              return Register::FromCode(code_);
          }
          int32 disp() const {
              return disp_;
          }
  
          bool operator ==(const MoveOperand &other) const {
              if (kind_ != other.kind_)
                  return false;
              if (code_ != other.code_)
                  return false;
              if (isMemory() || isEffectiveAddress())
                  return disp_ == other.disp_;
              return true;
          }
      };
  
      class Move
      {
        protected:
          MoveOperand from_;
          MoveOperand to_;
          bool cycle_;
  
        public:
          enum Kind {
              GENERAL,
              DOUBLE
          };
  
        protected:
          Kind kind_;
  
        public:
          Move()
          { }
          Move(const Move &other)
            : from_(other.from_),
              to_(other.to_),
              cycle_(other.cycle_),
              kind_(other.kind_)
          { }
          Move(const MoveOperand &from, const MoveOperand &to, Kind kind, bool cycle = false)
            : from_(from),
              to_(to),
              cycle_(cycle),
              kind_(kind)
          { }
  
          bool inCycle() const {
              return cycle_;
          }
          const MoveOperand &from() const {
              return from_;
          }
          const MoveOperand &to() const {
              return to_;
          }
          Kind kind() const {
              return kind_;
          }
      };
  
    private:
      struct PendingMove
        : public Move,
          public TempObject,
          public InlineListNode<PendingMove>
      {
          PendingMove()
          { }
          PendingMove(const MoveOperand &from, const MoveOperand &to, Kind kind)
            : Move(from, to, kind, false)
          { }
          
          void setInCycle() {
              JS_ASSERT(!inCycle());
              cycle_ = true;
          }
  
      };
  
      typedef InlineList<MoveResolver::PendingMove>::iterator PendingMoveIterator;
  
    private:
      // Moves that are definitely unblocked (constants to registers). These are
      // emitted last.
      js::Vector<Move, 16, SystemAllocPolicy> orderedMoves_;
      bool hasCycles_;
  
      TempObjectPool<PendingMove> movePool_;
  
      InlineList<PendingMove> pending_;
  
      PendingMove *findBlockingMove(const PendingMove *last);
  
      // Internal reset function. Does not clear lists.
      void resetState();
  
    public:
      MoveResolver();
  
      // Resolves a move group into two lists of ordered moves. These moves must
      // be executed in the order provided. Some moves may indicate that they
      // participate in a cycle. For every cycle there are two such moves, and it
      // is guaranteed that cycles do not nest inside each other in the list.
      //
      // After calling addMove() for each parallel move, resolve() performs the
      // cycle resolution algorithm. Calling addMove() again resets the resolver.
      bool addMove(const MoveOperand &from, const MoveOperand &to, Move::Kind kind);
      bool resolve();
  
      size_t numMoves() const {
          return orderedMoves_.length();
      }
      const Move &getMove(size_t i) const {
          return orderedMoves_[i];
      }
      bool hasCycles() const {
          return hasCycles_;
      }
  };
  
  } // namespace ion
  } // namespace js
  
  #endif // jsion_move_group_resolver_h__
  
  