michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jit_MoveResolver_h michael@0: #define jit_MoveResolver_h michael@0: michael@0: #include "jit/InlineList.h" michael@0: #include "jit/IonAllocPolicy.h" michael@0: #include "jit/Registers.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: // This is similar to Operand, but carries more information. We're also not michael@0: // guaranteed that Operand looks like this on all ISAs. michael@0: class MoveOperand michael@0: { michael@0: public: michael@0: enum Kind { michael@0: // A register in the "integer", aka "general purpose", class. michael@0: REG, michael@0: // A register in the "float" register class. michael@0: FLOAT_REG, michael@0: // A memory region. michael@0: MEMORY, michael@0: // The address of a memory region. michael@0: EFFECTIVE_ADDRESS michael@0: }; michael@0: michael@0: private: michael@0: Kind kind_; michael@0: uint32_t code_; michael@0: int32_t disp_; michael@0: michael@0: public: michael@0: MoveOperand() michael@0: { } michael@0: explicit MoveOperand(const Register ®) : kind_(REG), code_(reg.code()) michael@0: { } michael@0: explicit MoveOperand(const FloatRegister ®) : kind_(FLOAT_REG), code_(reg.code()) michael@0: { } michael@0: MoveOperand(const Register ®, int32_t disp, Kind kind = MEMORY) michael@0: : kind_(kind), michael@0: code_(reg.code()), michael@0: disp_(disp) michael@0: { michael@0: JS_ASSERT(isMemoryOrEffectiveAddress()); michael@0: michael@0: // With a zero offset, this is a plain reg-to-reg move. michael@0: if (disp == 0 && kind_ == EFFECTIVE_ADDRESS) michael@0: kind_ = REG; michael@0: } michael@0: MoveOperand(const MoveOperand &other) michael@0: : kind_(other.kind_), michael@0: code_(other.code_), michael@0: disp_(other.disp_) michael@0: { } michael@0: bool isFloatReg() const { michael@0: return kind_ == FLOAT_REG; michael@0: } michael@0: bool isGeneralReg() const { michael@0: return kind_ == REG; michael@0: } michael@0: bool isMemory() const { michael@0: return kind_ == MEMORY; michael@0: } michael@0: bool isEffectiveAddress() const { michael@0: return kind_ == EFFECTIVE_ADDRESS; michael@0: } michael@0: bool isMemoryOrEffectiveAddress() const { michael@0: return isMemory() || isEffectiveAddress(); michael@0: } michael@0: Register reg() const { michael@0: JS_ASSERT(isGeneralReg()); michael@0: return Register::FromCode(code_); michael@0: } michael@0: FloatRegister floatReg() const { michael@0: JS_ASSERT(isFloatReg()); michael@0: return FloatRegister::FromCode(code_); michael@0: } michael@0: Register base() const { michael@0: JS_ASSERT(isMemoryOrEffectiveAddress()); michael@0: return Register::FromCode(code_); michael@0: } michael@0: int32_t disp() const { michael@0: JS_ASSERT(isMemoryOrEffectiveAddress()); michael@0: return disp_; michael@0: } michael@0: michael@0: bool operator ==(const MoveOperand &other) const { michael@0: if (kind_ != other.kind_) michael@0: return false; michael@0: if (code_ != other.code_) michael@0: return false; michael@0: if (isMemoryOrEffectiveAddress()) michael@0: return disp_ == other.disp_; michael@0: return true; michael@0: } michael@0: bool operator !=(const MoveOperand &other) const { michael@0: return !operator==(other); michael@0: } michael@0: }; michael@0: michael@0: // This represents a move operation. michael@0: class MoveOp michael@0: { michael@0: protected: michael@0: MoveOperand from_; michael@0: MoveOperand to_; michael@0: bool cycleBegin_; michael@0: bool cycleEnd_; michael@0: michael@0: public: michael@0: enum Type { michael@0: GENERAL, michael@0: INT32, michael@0: FLOAT32, michael@0: DOUBLE michael@0: }; michael@0: michael@0: protected: michael@0: Type type_; michael@0: michael@0: // If cycleBegin_ is true, endCycleType_ is the type of the move at the end michael@0: // of the cycle. For example, given these moves: michael@0: // INT32 move a -> b michael@0: // GENERAL move b -> a michael@0: // the move resolver starts by copying b into a temporary location, so that michael@0: // the last move can read it. This copy needs to use use type GENERAL. michael@0: Type endCycleType_; michael@0: michael@0: public: michael@0: MoveOp() michael@0: { } michael@0: MoveOp(const MoveOperand &from, const MoveOperand &to, Type type) michael@0: : from_(from), michael@0: to_(to), michael@0: cycleBegin_(false), michael@0: cycleEnd_(false), michael@0: type_(type) michael@0: { } michael@0: michael@0: bool isCycleBegin() const { michael@0: return cycleBegin_; michael@0: } michael@0: bool isCycleEnd() const { michael@0: return cycleEnd_; michael@0: } michael@0: const MoveOperand &from() const { michael@0: return from_; michael@0: } michael@0: const MoveOperand &to() const { michael@0: return to_; michael@0: } michael@0: Type type() const { michael@0: return type_; michael@0: } michael@0: Type endCycleType() const { michael@0: JS_ASSERT(isCycleBegin()); michael@0: return endCycleType_; michael@0: } michael@0: }; michael@0: michael@0: class MoveResolver michael@0: { michael@0: private: michael@0: struct PendingMove michael@0: : public MoveOp, michael@0: public TempObject, michael@0: public InlineListNode michael@0: { michael@0: PendingMove() michael@0: { } michael@0: PendingMove(const MoveOperand &from, const MoveOperand &to, Type type) michael@0: : MoveOp(from, to, type) michael@0: { } michael@0: michael@0: void setCycleBegin(Type endCycleType) { michael@0: JS_ASSERT(!isCycleBegin() && !isCycleEnd()); michael@0: cycleBegin_ = true; michael@0: endCycleType_ = endCycleType; michael@0: } michael@0: void setCycleEnd() { michael@0: JS_ASSERT(!isCycleBegin() && !isCycleEnd()); michael@0: cycleEnd_ = true; michael@0: } michael@0: }; michael@0: michael@0: typedef InlineList::iterator PendingMoveIterator; michael@0: michael@0: private: michael@0: // Moves that are definitely unblocked (constants to registers). These are michael@0: // emitted last. michael@0: js::Vector orderedMoves_; michael@0: bool hasCycles_; michael@0: michael@0: TempObjectPool movePool_; michael@0: michael@0: InlineList pending_; michael@0: michael@0: PendingMove *findBlockingMove(const PendingMove *last); michael@0: michael@0: // Internal reset function. Does not clear lists. michael@0: void resetState(); michael@0: michael@0: public: michael@0: MoveResolver(); michael@0: michael@0: // Resolves a move group into two lists of ordered moves. These moves must michael@0: // be executed in the order provided. Some moves may indicate that they michael@0: // participate in a cycle. For every cycle there are two such moves, and it michael@0: // is guaranteed that cycles do not nest inside each other in the list. michael@0: // michael@0: // After calling addMove() for each parallel move, resolve() performs the michael@0: // cycle resolution algorithm. Calling addMove() again resets the resolver. michael@0: bool addMove(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type); michael@0: bool resolve(); michael@0: michael@0: size_t numMoves() const { michael@0: return orderedMoves_.length(); michael@0: } michael@0: const MoveOp &getMove(size_t i) const { michael@0: return orderedMoves_[i]; michael@0: } michael@0: bool hasCycles() const { michael@0: return hasCycles_; michael@0: } michael@0: void clearTempObjectPool() { michael@0: movePool_.clear(); michael@0: } michael@0: void setAllocator(TempAllocator &alloc) { michael@0: movePool_.setAllocator(alloc); michael@0: } michael@0: }; michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif /* jit_MoveResolver_h */