// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef V8_HYDROGEN_INSTRUCTIONS_H_
#define V8_HYDROGEN_INSTRUCTIONS_H_

#include "v8.h"
#include "code-stubs.h"
#include "string-stream.h"
#include "zone.h"

namespace v8 {
namespace internal {

// Forward declarations.
class HBasicBlock;
class HEnvironment;
class HInstruction;
class HLoopInformation;
class HValue;
class LInstruction;
class LChunkBuilder;


// Type hierarchy:
//
// HValue
//   HInstruction
//     HAccessArgumentsAt
//     HApplyArguments
//     HArgumentsElements
//     HArgumentsLength
//     HArgumentsObject
//     HBinaryOperation
//       HArithmeticBinaryOperation
//         HAdd
//         HDiv
//         HMod
//         HMul
//         HSub
//       HBitwiseBinaryOperation
//         HBitAnd
//         HBitOr
//         HBitXor
//         HSar
//         HShl
//         HShr
//       HBoundsCheck
//       HCompare
//       HCompareJSObjectEq
//       HInstanceOf
//       HLoadKeyed
//         HLoadKeyedFastElement
//         HLoadKeyedGeneric
//       HPower
//       HStoreNamed
//         HStoreNamedField
//         HStoreNamedGeneric
//     HBlockEntry
//     HCall
//       HCallConstantFunction
//       HCallFunction
//       HCallGlobal
//       HCallKeyed
//       HCallKnownGlobal
//       HCallNamed
//       HCallNew
//       HCallRuntime
//     HCallStub
//     HConstant
//     HControlInstruction
//       HDeoptimize
//       HGoto
//       HUnaryControlInstruction
//         HBranch
//         HCompareMapAndBranch
//         HReturn
//         HThrow
//     HEnterInlined
//     HFunctionLiteral
//     HGlobalObject
//     HGlobalReceiver
//     HLeaveInlined
//     HLoadGlobal
//     HMaterializedLiteral
//       HArrayLiteral
//       HObjectLiteral
//       HRegExpLiteral
//     HOsrEntry
//     HParameter
//     HSimulate
//     HStackCheck
//     HStoreKeyed
//       HStoreKeyedFastElement
//       HStoreKeyedGeneric
//     HUnaryOperation
//       HArrayLength
//       HBitNot
//       HChange
//       HCheckFunction
//       HCheckInstanceType
//       HCheckMap
//       HCheckNonSmi
//       HCheckPrototypeMaps
//       HCheckSmi
//       HDeleteProperty
//       HLoadElements
//         HTypeofIs
//       HLoadNamedField
//       HLoadNamedGeneric
//       HLoadFunctionPrototype
//       HPushArgument
//       HTypeof
//       HUnaryMathOperation
//       HUnaryPredicate
//         HClassOfTest
//         HHasCachedArrayIndex
//         HHasInstanceType
//         HIsNull
//         HIsObject
//         HIsSmi
//       HValueOf
//     HUnknownOSRValue
//   HPhi

#define HYDROGEN_ALL_INSTRUCTION_LIST(V)       \
  V(ArithmeticBinaryOperation)                 \
  V(BinaryOperation)                           \
  V(BitwiseBinaryOperation)                    \
  V(Call)                                      \
  V(ControlInstruction)                        \
  V(Instruction)                               \
  V(LoadKeyed)                                 \
  V(MaterializedLiteral)                       \
  V(Phi)                                       \
  V(StoreKeyed)                                \
  V(StoreNamed)                                \
  V(UnaryControlInstruction)                   \
  V(UnaryOperation)                            \
  HYDROGEN_CONCRETE_INSTRUCTION_LIST(V)


#define HYDROGEN_CONCRETE_INSTRUCTION_LIST(V)  \
  V(AccessArgumentsAt)                         \
  V(Add)                                       \
  V(ApplyArguments)                            \
  V(ArgumentsElements)                         \
  V(ArgumentsLength)                           \
  V(ArgumentsObject)                           \
  V(ArrayLength)                               \
  V(ArrayLiteral)                              \
  V(BitAnd)                                    \
  V(BitNot)                                    \
  V(BitOr)                                     \
  V(BitXor)                                    \
  V(BlockEntry)                                \
  V(BoundsCheck)                               \
  V(Branch)                                    \
  V(CallConstantFunction)                      \
  V(CallFunction)                              \
  V(CallGlobal)                                \
  V(CallKeyed)                                 \
  V(CallKnownGlobal)                           \
  V(CallNamed)                                 \
  V(CallNew)                                   \
  V(CallRuntime)                               \
  V(CallStub)                                  \
  V(Change)                                    \
  V(CheckFunction)                             \
  V(CheckInstanceType)                         \
  V(CheckMap)                                  \
  V(CheckNonSmi)                               \
  V(CheckPrototypeMaps)                        \
  V(CheckSmi)                                  \
  V(Compare)                                   \
  V(CompareJSObjectEq)                         \
  V(CompareMapAndBranch)                       \
  V(Constant)                                  \
  V(DeleteProperty)                            \
  V(Deoptimize)                                \
  V(Div)                                       \
  V(EnterInlined)                              \
  V(FunctionLiteral)                           \
  V(GlobalObject)                              \
  V(GlobalReceiver)                            \
  V(Goto)                                      \
  V(InstanceOf)                                \
  V(IsNull)                                    \
  V(IsObject)                                  \
  V(IsSmi)                                     \
  V(HasInstanceType)                           \
  V(HasCachedArrayIndex)                       \
  V(ClassOfTest)                               \
  V(LeaveInlined)                              \
  V(LoadElements)                              \
  V(LoadGlobal)                                \
  V(LoadKeyedFastElement)                      \
  V(LoadKeyedGeneric)                          \
  V(LoadNamedField)                            \
  V(LoadNamedGeneric)                          \
  V(LoadFunctionPrototype)                     \
  V(Mod)                                       \
  V(Mul)                                       \
  V(ObjectLiteral)                             \
  V(OsrEntry)                                  \
  V(Parameter)                                 \
  V(Power)                                     \
  V(PushArgument)                              \
  V(RegExpLiteral)                             \
  V(Return)                                    \
  V(Sar)                                       \
  V(Shl)                                       \
  V(Shr)                                       \
  V(Simulate)                                  \
  V(StackCheck)                                \
  V(StoreGlobal)                               \
  V(StoreKeyedFastElement)                     \
  V(StoreKeyedGeneric)                         \
  V(StoreNamedField)                           \
  V(StoreNamedGeneric)                         \
  V(Sub)                                       \
  V(Throw)                                     \
  V(Typeof)                                    \
  V(TypeofIs)                                  \
  V(UnaryMathOperation)                        \
  V(UnknownOSRValue)                           \
  V(ValueOf)

#define GVN_FLAG_LIST(V)                       \
  V(Calls)                                     \
  V(InobjectFields)                            \
  V(BackingStoreFields)                        \
  V(ArrayElements)                             \
  V(GlobalVars)                                \
  V(Maps)                                      \
  V(ArrayLengths)                              \
  V(FunctionPrototypes)                        \
  V(OsrEntries)

#define DECLARE_INSTRUCTION(type)                   \
  virtual bool Is##type() const { return true; }    \
  static H##type* cast(HValue* value) {             \
    ASSERT(value->Is##type());                      \
    return reinterpret_cast<H##type*>(value);       \
  }                                                 \
  Opcode opcode() const { return HValue::k##type; }


#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic)              \
  virtual LInstruction* CompileToLithium(LChunkBuilder* builder); \
  virtual const char* Mnemonic() const { return mnemonic; }       \
  DECLARE_INSTRUCTION(type)



template<int kSize>
class HOperandVector : public EmbeddedVector<HValue*, kSize> {
 public:
  HOperandVector() : EmbeddedVector<HValue*, kSize>(NULL) { }
};


class Range: public ZoneObject {
 public:
  Range() : lower_(kMinInt),
            upper_(kMaxInt),
            next_(NULL),
            can_be_minus_zero_(false) { }

  Range(int32_t lower, int32_t upper)
      : lower_(lower), upper_(upper), next_(NULL), can_be_minus_zero_(false) { }

  bool IsInSmiRange() const {
    return lower_ >= Smi::kMinValue && upper_ <= Smi::kMaxValue;
  }
  void KeepOrder();
  void Verify() const;
  int32_t upper() const { return upper_; }
  int32_t lower() const { return lower_; }
  Range* next() const { return next_; }
  Range* CopyClearLower() const { return new Range(kMinInt, upper_); }
  Range* CopyClearUpper() const { return new Range(lower_, kMaxInt); }
  void ClearLower() { lower_ = kMinInt; }
  void ClearUpper() { upper_ = kMaxInt; }
  Range* Copy() const { return new Range(lower_, upper_); }
  bool IsMostGeneric() const { return lower_ == kMinInt && upper_ == kMaxInt; }
  int32_t Mask() const;
  void set_can_be_minus_zero(bool b) { can_be_minus_zero_ = b; }
  bool CanBeMinusZero() const { return CanBeZero() && can_be_minus_zero_; }
  bool CanBeZero() const { return upper_ >= 0 && lower_ <= 0; }
  bool CanBeNegative() const { return lower_ < 0; }
  bool Includes(int value) const {
    return lower_ <= value && upper_ >= value;
  }

  void Sar(int32_t value) {
    int32_t bits = value & 0x1F;
    lower_ = lower_ >> bits;
    upper_ = upper_ >> bits;
    set_can_be_minus_zero(false);
  }

  void Shl(int32_t value) {
    int32_t bits = value & 0x1F;
    int old_lower = lower_;
    int old_upper = upper_;
    lower_ = lower_ << bits;
    upper_ = upper_ << bits;
    if (old_lower != lower_ >> bits || old_upper != upper_ >> bits) {
      upper_ = kMaxInt;
      lower_ = kMinInt;
    }
    set_can_be_minus_zero(false);
  }

  // Adds a constant to the lower and upper bound of the range.
  void AddConstant(int32_t value);

  void StackUpon(Range* other) {
    Intersect(other);
    next_ = other;
  }

  void Intersect(Range* other) {
    upper_ = Min(upper_, other->upper_);
    lower_ = Max(lower_, other->lower_);
    bool b = CanBeMinusZero() && other->CanBeMinusZero();
    set_can_be_minus_zero(b);
  }

  void Union(Range* other) {
    upper_ = Max(upper_, other->upper_);
    lower_ = Min(lower_, other->lower_);
    bool b = CanBeMinusZero() || other->CanBeMinusZero();
    set_can_be_minus_zero(b);
  }

  // Compute a new result range and return true, if the operation
  // can overflow.
  bool AddAndCheckOverflow(Range* other);
  bool SubAndCheckOverflow(Range* other);
  bool MulAndCheckOverflow(Range* other);

 private:
  int32_t lower_;
  int32_t upper_;
  Range* next_;
  bool can_be_minus_zero_;
};


class Representation {
 public:
  enum Kind {
    kNone,
    kTagged,
    kDouble,
    kInteger32,
    kNumRepresentations
  };

  Representation() : kind_(kNone) { }

  static Representation None() { return Representation(kNone); }
  static Representation Tagged() { return Representation(kTagged); }
  static Representation Integer32() { return Representation(kInteger32); }
  static Representation Double() { return Representation(kDouble); }

  bool Equals(const Representation& other) const {
    return kind_ == other.kind_;
  }

  Kind kind() const { return kind_; }
  bool IsNone() const { return kind_ == kNone; }
  bool IsTagged() const { return kind_ == kTagged; }
  bool IsInteger32() const { return kind_ == kInteger32; }
  bool IsDouble() const { return kind_ == kDouble; }
  bool IsSpecialization() const {
    return kind_ == kInteger32 || kind_ == kDouble;
  }
  const char* Mnemonic() const;

 private:
  explicit Representation(Kind k) : kind_(k) { }

  Kind kind_;
};


class HType {
 public:
  HType() : type_(kUninitialized) { }

  static HType Tagged() { return HType(kTagged); }
  static HType TaggedPrimitive() { return HType(kTaggedPrimitive); }
  static HType TaggedNumber() { return HType(kTaggedNumber); }
  static HType Smi() { return HType(kSmi); }
  static HType HeapNumber() { return HType(kHeapNumber); }
  static HType String() { return HType(kString); }
  static HType Boolean() { return HType(kBoolean); }
  static HType NonPrimitive() { return HType(kNonPrimitive); }
  static HType JSArray() { return HType(kJSArray); }
  static HType JSObject() { return HType(kJSObject); }
  static HType Uninitialized() { return HType(kUninitialized); }

  // Return the weakest (least precise) common type.
  HType Combine(HType other) {
    return HType(static_cast<Type>(type_ & other.type_));
  }

  bool Equals(const HType& other) {
    return type_ == other.type_;
  }

  bool IsSubtypeOf(const HType& other) {
    return Combine(other).Equals(other);
  }

  bool IsTagged() {
    ASSERT(type_ != kUninitialized);
    return ((type_ & kTagged) == kTagged);
  }

  bool IsTaggedPrimitive() {
    ASSERT(type_ != kUninitialized);
    return ((type_ & kTaggedPrimitive) == kTaggedPrimitive);
  }

  bool IsTaggedNumber() {
    ASSERT(type_ != kUninitialized);
    return ((type_ & kTaggedNumber) == kTaggedNumber);
  }

  bool IsSmi() {
    ASSERT(type_ != kUninitialized);
    return ((type_ & kSmi) == kSmi);
  }

  bool IsHeapNumber() {
    ASSERT(type_ != kUninitialized);
    return ((type_ & kHeapNumber) == kHeapNumber);
  }

  bool IsString() {
    ASSERT(type_ != kUninitialized);
    return ((type_ & kString) == kString);
  }

  bool IsBoolean() {
    ASSERT(type_ != kUninitialized);
    return ((type_ & kBoolean) == kBoolean);
  }

  bool IsNonPrimitive() {
    ASSERT(type_ != kUninitialized);
    return ((type_ & kNonPrimitive) == kNonPrimitive);
  }

  bool IsJSArray() {
    ASSERT(type_ != kUninitialized);
    return ((type_ & kJSArray) == kJSArray);
  }

  bool IsJSObject() {
    ASSERT(type_ != kUninitialized);
    return ((type_ & kJSObject) == kJSObject);
  }

  bool IsUninitialized() {
    return type_ == kUninitialized;
  }

  static HType TypeFromValue(Handle<Object> value);

  const char* ToString();
  const char* ToShortString();

 private:
  enum Type {
    kTagged = 0x1,           // 0000 0000 0000 0001
    kTaggedPrimitive = 0x5,  // 0000 0000 0000 0101
    kTaggedNumber = 0xd,     // 0000 0000 0000 1101
    kSmi = 0x1d,             // 0000 0000 0001 1101
    kHeapNumber = 0x2d,      // 0000 0000 0010 1101
    kString = 0x45,          // 0000 0000 0100 0101
    kBoolean = 0x85,         // 0000 0000 1000 0101
    kNonPrimitive = 0x101,   // 0000 0001 0000 0001
    kJSObject = 0x301,       // 0000 0011 0000 0001
    kJSArray = 0x701,        // 0000 0111 1000 0001
    kUninitialized = 0x1fff  // 0001 1111 1111 1111
  };

  explicit HType(Type t) : type_(t) { }

  Type type_;
};


class HValue: public ZoneObject {
 public:
  static const int kNoNumber = -1;

  // There must be one corresponding kDepends flag for every kChanges flag and
  // the order of the kChanges flags must be exactly the same as of the kDepends
  // flags.
  enum Flag {
    // Declare global value numbering flags.
  #define DECLARE_DO(type) kChanges##type, kDependsOn##type,
    GVN_FLAG_LIST(DECLARE_DO)
  #undef DECLARE_DO
    kFlexibleRepresentation,
    kUseGVN,
    kCanOverflow,
    kBailoutOnMinusZero,
    kCanBeDivByZero,
    kIsArguments,
    kTruncatingToInt32,
    kLastFlag = kTruncatingToInt32
  };

  STATIC_ASSERT(kLastFlag < kBitsPerInt);

  static const int kChangesToDependsFlagsLeftShift = 1;

  static int ChangesFlagsMask() {
    int result = 0;
    // Create changes mask.
#define DECLARE_DO(type) result |= (1 << kChanges##type);
  GVN_FLAG_LIST(DECLARE_DO)
#undef DECLARE_DO
    return result;
  }

  static int DependsFlagsMask() {
    return ConvertChangesToDependsFlags(ChangesFlagsMask());
  }

  static int ConvertChangesToDependsFlags(int flags) {
    return flags << kChangesToDependsFlagsLeftShift;
  }

  // A flag mask to mark an instruction as having arbitrary side effects.
  static int AllSideEffects() {
    return ChangesFlagsMask() & ~(1 << kChangesOsrEntries);
  }

  static HValue* cast(HValue* value) { return value; }

  enum Opcode {
    // Declare a unique enum value for each hydrogen instruction.
  #define DECLARE_DO(type) k##type,
    HYDROGEN_ALL_INSTRUCTION_LIST(DECLARE_DO)
  #undef DECLARE_DO
    kMaxInstructionClass
  };

  HValue() : block_(NULL),
             id_(kNoNumber),
             uses_(2),
             type_(HType::Tagged()),
             range_(NULL),
             flags_(0) {}
  virtual ~HValue() {}

  HBasicBlock* block() const { return block_; }
  void SetBlock(HBasicBlock* block);

  int id() const { return id_; }
  void set_id(int id) { id_ = id; }

  const ZoneList<HValue*>* uses() const { return &uses_; }

  virtual bool EmitAtUses() const { return false; }
  Representation representation() const { return representation_; }
  void ChangeRepresentation(Representation r) {
    // Representation was already set and is allowed to be changed.
    ASSERT(!representation_.IsNone());
    ASSERT(!r.IsNone());
    ASSERT(CheckFlag(kFlexibleRepresentation));
    RepresentationChanged(r);
    representation_ = r;
  }

  HType type() const { return type_; }
  void set_type(HType type) {
    ASSERT(uses_.length() == 0);
    type_ = type;
  }

  // An operation needs to override this function iff:
  //   1) it can produce an int32 output.
  //   2) the true value of its output can potentially be minus zero.
  // The implementation must set a flag so that it bails out in the case where
  // it would otherwise output what should be a minus zero as an int32 zero.
  // If the operation also exists in a form that takes int32 and outputs int32
  // then the operation should return its input value so that we can propagate
  // back.  There are two operations that need to propagate back to more than
  // one input.  They are phi and binary add.  They always return NULL and
  // expect the caller to take care of things.
  virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited) {
    visited->Add(id());
    return NULL;
  }

  bool HasSideEffects() const {
    return (flags_ & AllSideEffects()) != 0;
  }
  bool IsDefinedAfter(HBasicBlock* other) const;

  // Operands.
  virtual int OperandCount() const { return 0; }
  virtual HValue* OperandAt(int index) const {
    UNREACHABLE();
    return NULL;
  }
  void SetOperandAt(int index, HValue* value);

  int LookupOperandIndex(int occurrence_index, HValue* op) const;
  bool UsesMultipleTimes(HValue* op) const;

  void ReplaceAndDelete(HValue* other);
  void ReplaceValue(HValue* other);
  void ReplaceAtUse(HValue* use, HValue* other);
  void ReplaceFirstAtUse(HValue* use, HValue* other, Representation r);
  bool HasNoUses() const { return uses_.is_empty(); }
  void ClearOperands();
  void Delete();

  int flags() const { return flags_; }
  void SetFlagMask(int mask) { flags_ |= mask; }
  void SetFlag(Flag f) { SetFlagMask(1 << f); }
  void ClearFlagMask(int mask) { flags_ &= ~mask; }
  void ClearFlag(Flag f) { ClearFlagMask(1 << f); }
  bool CheckFlag(Flag f) const { return CheckFlagMask(1 << f); }
  bool CheckFlagMask(int mask) const { return (flags_ & mask) != 0; }

  Range* range() const { return range_; }
  bool HasRange() const { return range_ != NULL; }
  void AddNewRange(Range* r);
  void RemoveLastAddedRange();
  void ComputeInitialRange();

  // Representation helpers.
  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::None();
  }
  virtual Representation InferredRepresentation() const {
    return representation();
  }

  // This gives the instruction an opportunity to replace itself with an
  // instruction that does the same in some better way.  To replace an
  // instruction with a new one, first add the new instruction to the graph,
  // then return it.  Return NULL to have the instruction deleted.
  virtual HValue* Canonicalize() { return this; }

  // Declare virtual type testers.
#define DECLARE_DO(type) virtual bool Is##type() const { return false; }
  HYDROGEN_ALL_INSTRUCTION_LIST(DECLARE_DO)
#undef DECLARE_DO

  bool Equals(HValue* other) const;
  virtual intptr_t Hashcode() const;

  // Printing support.
  virtual void PrintTo(StringStream* stream) const = 0;
  void PrintNameTo(StringStream* stream);
  static void PrintTypeTo(HType type, StringStream* stream);

  virtual const char* Mnemonic() const = 0;
  virtual Opcode opcode() const = 0;

  // Updated the inferred type of this instruction and returns true if
  // it has changed.
  bool UpdateInferredType();

  virtual HType CalculateInferredType() const;

  // Helper for type conversions used by normal and phi instructions.
  void InsertInputConversion(HInstruction* previous, int index, HType type);

#ifdef DEBUG
  virtual void Verify() const = 0;
#endif

 protected:
  virtual bool DataEquals(HValue* other) const { return true; }
  virtual void RepresentationChanged(Representation to) { }
  virtual Range* InferRange();
  virtual void DeleteFromGraph() = 0;
  virtual void InternalSetOperandAt(int index, HValue* value) { UNREACHABLE(); }
  void clear_block() {
    ASSERT(block_ != NULL);
    block_ = NULL;
  }

  void set_representation(Representation r) {
    // Representation is set-once.
    ASSERT(representation_.IsNone() && !r.IsNone());
    representation_ = r;
  }

 private:
  void InternalReplaceAtUse(HValue* use, HValue* other);
  void RegisterUse(int index, HValue* new_value);

  HBasicBlock* block_;

  // The id of this instruction in the hydrogen graph, assigned when first
  // added to the graph. Reflects creation order.
  int id_;

  Representation representation_;
  ZoneList<HValue*> uses_;
  HType type_;
  Range* range_;
  int flags_;

  DISALLOW_COPY_AND_ASSIGN(HValue);
};


class HInstruction: public HValue {
 public:
  HInstruction* next() const { return next_; }
  HInstruction* previous() const { return previous_; }

  void PrintTo(StringStream* stream) const;
  virtual void PrintDataTo(StringStream* stream) const {}

  bool IsLinked() const { return block() != NULL; }
  void Unlink();
  void InsertBefore(HInstruction* next);
  void InsertAfter(HInstruction* previous);

  int position() const { return position_; }
  bool has_position() const { return position_ != RelocInfo::kNoPosition; }
  void set_position(int position) { position_ = position; }

  virtual LInstruction* CompileToLithium(LChunkBuilder* builder) = 0;

#ifdef DEBUG
  virtual void Verify() const;
#endif

  DECLARE_INSTRUCTION(Instruction)

 protected:
  HInstruction()
      : next_(NULL),
        previous_(NULL),
        position_(RelocInfo::kNoPosition) {
    SetFlag(kDependsOnOsrEntries);
  }

  virtual void DeleteFromGraph() { Unlink(); }

 private:
  void InitializeAsFirst(HBasicBlock* block) {
    ASSERT(!IsLinked());
    SetBlock(block);
  }

  HInstruction* next_;
  HInstruction* previous_;
  int position_;

  friend class HBasicBlock;
};


class HBlockEntry: public HInstruction {
 public:
  DECLARE_CONCRETE_INSTRUCTION(BlockEntry, "block_entry")
};


class HControlInstruction: public HInstruction {
 public:
  virtual HBasicBlock* FirstSuccessor() const { return NULL; }
  virtual HBasicBlock* SecondSuccessor() const { return NULL; }

  DECLARE_INSTRUCTION(ControlInstruction)
};


class HDeoptimize: public HControlInstruction {
 public:
  DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
};


class HGoto: public HControlInstruction {
 public:
  explicit HGoto(HBasicBlock* destination)
      : destination_(destination),
        include_stack_check_(false) {}

  virtual HBasicBlock* FirstSuccessor() const { return destination_; }
  void set_include_stack_check(bool include_stack_check) {
    include_stack_check_ = include_stack_check;
  }
  bool include_stack_check() const { return include_stack_check_; }

  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(Goto, "goto")

 private:
  HBasicBlock* destination_;
  bool include_stack_check_;
};


class HUnaryControlInstruction: public HControlInstruction {
 public:
  explicit HUnaryControlInstruction(HValue* value) {
    SetOperandAt(0, value);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  HValue* value() const { return OperandAt(0); }
  virtual int OperandCount() const { return 1; }
  virtual HValue* OperandAt(int index) const { return operands_[index]; }

  DECLARE_INSTRUCTION(UnaryControlInstruction)

 protected:
  virtual void InternalSetOperandAt(int index, HValue* value) {
    operands_[index] = value;
  }

 private:
  HOperandVector<1> operands_;
};


class HBranch: public HUnaryControlInstruction {
 public:
  HBranch(HBasicBlock* true_destination,
          HBasicBlock* false_destination,
          HValue* boolean_value)
      : HUnaryControlInstruction(boolean_value),
        true_destination_(true_destination),
        false_destination_(false_destination) {
    ASSERT(true_destination != NULL && false_destination != NULL);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::None();
  }

  virtual HBasicBlock* FirstSuccessor() const { return true_destination_; }
  virtual HBasicBlock* SecondSuccessor() const { return false_destination_; }

  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(Branch, "branch")

 private:
  HBasicBlock* true_destination_;
  HBasicBlock* false_destination_;
};


class HCompareMapAndBranch: public HUnaryControlInstruction {
 public:
  HCompareMapAndBranch(HValue* result,
                       Handle<Map> map,
                       HBasicBlock* true_destination,
                       HBasicBlock* false_destination)
      : HUnaryControlInstruction(result),
        map_(map),
        true_destination_(true_destination),
        false_destination_(false_destination) {
    ASSERT(true_destination != NULL);
    ASSERT(false_destination != NULL);
    ASSERT(!map.is_null());
  }

  virtual HBasicBlock* FirstSuccessor() const { return true_destination_; }
  virtual HBasicBlock* SecondSuccessor() const { return false_destination_; }

  virtual void PrintDataTo(StringStream* stream) const;

  Handle<Map> map() const { return map_; }

  DECLARE_CONCRETE_INSTRUCTION(CompareMapAndBranch, "compare_map_and_branch")

 private:
  Handle<Map> map_;
  HBasicBlock* true_destination_;
  HBasicBlock* false_destination_;
};


class HReturn: public HUnaryControlInstruction {
 public:
  explicit HReturn(HValue* result) : HUnaryControlInstruction(result) { }

  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(Return, "return")
};


class HThrow: public HUnaryControlInstruction {
 public:
  explicit HThrow(HValue* value) : HUnaryControlInstruction(value) { }

  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(Throw, "throw")
};


class HUnaryOperation: public HInstruction {
 public:
  explicit HUnaryOperation(HValue* value) {
    SetOperandAt(0, value);
  }

  HValue* value() const { return OperandAt(0); }
  virtual void PrintDataTo(StringStream* stream) const;
  virtual int OperandCount() const { return 1; }
  virtual HValue* OperandAt(int index) const { return operands_[index]; }

  DECLARE_INSTRUCTION(UnaryOperation)

 protected:
  virtual void InternalSetOperandAt(int index, HValue* value) {
    operands_[index] = value;
  }

 private:
  HOperandVector<1> operands_;
};


class HChange: public HUnaryOperation {
 public:
  HChange(HValue* value,
          Representation from,
          Representation to)
      : HUnaryOperation(value), from_(from), to_(to) {
    ASSERT(!from.IsNone() && !to.IsNone());
    ASSERT(!from.Equals(to));
    set_representation(to);
    SetFlag(kUseGVN);

    if (from.IsInteger32() && to.IsTagged() && value->range() != NULL &&
        value->range()->IsInSmiRange()) {
      set_type(HType::Smi());
    }
  }

  virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);

  Representation from() const { return from_; }
  Representation to() const { return to_; }
  virtual Representation RequiredInputRepresentation(int index) const {
    return from_;
  }

  bool CanTruncateToInt32() const {
    for (int i = 0; i < uses()->length(); ++i) {
      if (!uses()->at(i)->CheckFlag(HValue::kTruncatingToInt32)) return false;
    }
    return true;
  }

  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(Change,
                               CanTruncateToInt32() ? "truncate" : "change")

 protected:
  virtual bool DataEquals(HValue* other) const {
    if (!other->IsChange()) return false;
    HChange* change = HChange::cast(other);
    return value() == change->value()
        && to().Equals(change->to())
        && CanTruncateToInt32() == change->CanTruncateToInt32();
  }

 private:
  Representation from_;
  Representation to_;
};


class HSimulate: public HInstruction {
 public:
  HSimulate(int ast_id, int pop_count, int environment_height)
      : ast_id_(ast_id),
        pop_count_(pop_count),
        environment_height_(environment_height),
        values_(2),
        assigned_indexes_(2) {}
  virtual ~HSimulate() {}

  virtual void PrintDataTo(StringStream* stream) const;

  bool HasAstId() const { return ast_id_ != AstNode::kNoNumber; }
  int ast_id() const { return ast_id_; }
  void set_ast_id(int id) {
    ASSERT(!HasAstId());
    ast_id_ = id;
  }

  int environment_height() const { return environment_height_; }
  int pop_count() const { return pop_count_; }
  const ZoneList<HValue*>* values() const { return &values_; }
  int GetAssignedIndexAt(int index) const {
    ASSERT(HasAssignedIndexAt(index));
    return assigned_indexes_[index];
  }
  bool HasAssignedIndexAt(int index) const {
    return assigned_indexes_[index] != kNoIndex;
  }
  void AddAssignedValue(int index, HValue* value) {
    AddValue(index, value);
  }
  void AddPushedValue(HValue* value) {
    AddValue(kNoIndex, value);
  }
  virtual int OperandCount() const { return values_.length(); }
  virtual HValue* OperandAt(int index) const { return values_[index]; }

  DECLARE_CONCRETE_INSTRUCTION(Simulate, "simulate")

#ifdef DEBUG
  virtual void Verify() const;
#endif

 protected:
  virtual void InternalSetOperandAt(int index, HValue* value) {
    values_[index] = value;
  }

 private:
  static const int kNoIndex = -1;
  void AddValue(int index, HValue* value) {
    assigned_indexes_.Add(index);
    // Resize the list of pushed values.
    values_.Add(NULL);
    // Set the operand through the base method in HValue to make sure that the
    // use lists are correctly updated.
    SetOperandAt(values_.length() - 1, value);
  }
  int ast_id_;
  int pop_count_;
  int environment_height_;
  ZoneList<HValue*> values_;
  ZoneList<int> assigned_indexes_;
};


class HStackCheck: public HInstruction {
 public:
  HStackCheck() { }

  DECLARE_CONCRETE_INSTRUCTION(Throw, "stack_check")
};


class HEnterInlined: public HInstruction {
 public:
  HEnterInlined(Handle<JSFunction> closure, FunctionLiteral* function)
      : closure_(closure), function_(function) {
  }

  virtual void PrintDataTo(StringStream* stream) const;

  Handle<JSFunction> closure() const { return closure_; }
  FunctionLiteral* function() const { return function_; }

  DECLARE_CONCRETE_INSTRUCTION(EnterInlined, "enter_inlined")

 private:
  Handle<JSFunction> closure_;
  FunctionLiteral* function_;
};


class HLeaveInlined: public HInstruction {
 public:
  HLeaveInlined() {}

  DECLARE_CONCRETE_INSTRUCTION(LeaveInlined, "leave_inlined")
};


class HPushArgument: public HUnaryOperation {
 public:
  explicit HPushArgument(HValue* value)
      : HUnaryOperation(value), argument_index_(-1) {
    set_representation(Representation::Tagged());
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  virtual void PrintDataTo(StringStream* stream) const;
  HValue* argument() const { return OperandAt(0); }
  int argument_index() const { return argument_index_; }
  void set_argument_index(int index) {
    ASSERT(argument_index_ == -1 || index == argument_index_);
    argument_index_ = index;
  }

  DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push_argument")

 private:
  int argument_index_;
};


class HGlobalObject: public HInstruction {
 public:
  HGlobalObject() {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
    SetFlag(kDependsOnCalls);
  }

  DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global_object")
};


class HGlobalReceiver: public HInstruction {
 public:
  HGlobalReceiver() {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
    SetFlag(kDependsOnCalls);
  }

  DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global_receiver")
};


class HCall: public HInstruction {
 public:
  // Construct a call with uninitialized arguments. The argument count
  // includes the receiver.
  explicit HCall(int count);

  virtual HType CalculateInferredType() const { return HType::Tagged(); }

  // TODO(3190496): This needs a cleanup. We don't want the arguments
  // be operands of the call instruction. This results in bad code quality.
  virtual int argument_count() const { return arguments_.length(); }
  virtual int OperandCount() const { return argument_count(); }
  virtual HValue* OperandAt(int index) const { return arguments_[index]; }
  virtual HPushArgument* PushArgumentAt(int index) const {
    return HPushArgument::cast(OperandAt(index));
  }
  virtual HValue* ArgumentAt(int index) const {
    return PushArgumentAt(index)->argument();
  }
  virtual void SetArgumentAt(int index, HPushArgument* push_argument);

  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_INSTRUCTION(Call)

 protected:
  virtual void InternalSetOperandAt(int index, HValue* value) {
    arguments_[index] = value;
  }

  int argument_count_;
  Vector<HValue*> arguments_;
};


class HCallConstantFunction: public HCall {
 public:
  HCallConstantFunction(Handle<JSFunction> function, int argument_count)
      : HCall(argument_count), function_(function) { }

  Handle<JSFunction> function() const { return function_; }
  bool IsApplyFunction() const {
    return function_->code() == Builtins::builtin(Builtins::FunctionApply);
  }

  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(CallConstantFunction, "call_constant_function")

 private:
  Handle<JSFunction> function_;
};


class HCallKeyed: public HCall {
 public:
  HCallKeyed(HValue* key, int argument_count)
      : HCall(argument_count + 1) {
    SetOperandAt(0, key);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  // TODO(3190496): This is a hack to get an additional operand that
  // is not an argument to work with the current setup. This _needs_ a cleanup.
  // (see HCall)
  virtual void PrintDataTo(StringStream* stream) const;
  HValue* key() const { return OperandAt(0); }
  virtual int argument_count() const { return arguments_.length() - 1; }
  virtual int OperandCount() const { return arguments_.length(); }
  virtual HValue* OperandAt(int index) const { return arguments_[index]; }
  virtual HPushArgument* PushArgumentAt(int index) const {
    return HPushArgument::cast(OperandAt(index + 1));
  }
  virtual void SetArgumentAt(int index, HPushArgument* push_argument) {
    HCall::SetArgumentAt(index + 1, push_argument);
  }

  DECLARE_CONCRETE_INSTRUCTION(CallKeyed, "call_keyed")
};


class HCallNamed: public HCall {
 public:
  HCallNamed(Handle<String> name, int argument_count)
      : HCall(argument_count), name_(name) { }
  virtual void PrintDataTo(StringStream* stream) const;

  Handle<String> name() const { return name_; }

  DECLARE_CONCRETE_INSTRUCTION(CallNamed, "call_named")

 private:
  Handle<String> name_;
};


class HCallFunction: public HCall {
 public:
  explicit HCallFunction(int argument_count) : HCall(argument_count) { }

  DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call_function")
};


class HCallGlobal: public HCall {
 public:
  HCallGlobal(Handle<String> name, int argument_count)
      : HCall(argument_count), name_(name) { }

  virtual void PrintDataTo(StringStream* stream) const;

  Handle<String> name() const { return name_; }

  DECLARE_CONCRETE_INSTRUCTION(CallGlobal, "call_global")

 private:
  Handle<String> name_;
};


class HCallKnownGlobal: public HCall {
 public:
  HCallKnownGlobal(Handle<JSFunction> target,
                   int argument_count)
      : HCall(argument_count), target_(target) { }

  Handle<JSFunction> target() const { return target_; }

  DECLARE_CONCRETE_INSTRUCTION(CallKnownGlobal, "call_known_global")

 private:
  Handle<JSFunction> target_;
};


class HCallNew: public HCall {
 public:
  explicit HCallNew(int argument_count) : HCall(argument_count) { }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  HValue* constructor() const { return ArgumentAt(0); }

  DECLARE_CONCRETE_INSTRUCTION(CallNew, "call_new")
};


class HCallRuntime: public HCall {
 public:
  HCallRuntime(Handle<String> name,
               Runtime::Function* c_function,
               int argument_count)
      : HCall(argument_count), c_function_(c_function), name_(name) { }
  virtual void PrintDataTo(StringStream* stream) const;

  Runtime::Function* function() const { return c_function_; }
  Handle<String> name() const { return name_; }

  DECLARE_CONCRETE_INSTRUCTION(CallRuntime, "call_runtime")

 private:
  Runtime::Function* c_function_;
  Handle<String> name_;
};


class HArrayLength: public HUnaryOperation {
 public:
  explicit HArrayLength(HValue* value) : HUnaryOperation(value) {
    // The length of an array is stored as a tagged value in the array
    // object. It is guaranteed to be 32 bit integer, but it can be
    // represented as either a smi or heap number.
    set_representation(Representation::Tagged());
    SetFlag(kDependsOnArrayLengths);
    SetFlag(kUseGVN);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  DECLARE_CONCRETE_INSTRUCTION(ArrayLength, "array_length")
};


class HBitNot: public HUnaryOperation {
 public:
  explicit HBitNot(HValue* value) : HUnaryOperation(value) {
    set_representation(Representation::Integer32());
    SetFlag(kUseGVN);
    SetFlag(kTruncatingToInt32);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Integer32();
  }
  virtual HType CalculateInferredType() const;

  DECLARE_CONCRETE_INSTRUCTION(BitNot, "bit_not")
};


class HUnaryMathOperation: public HUnaryOperation {
 public:
  HUnaryMathOperation(HValue* value, BuiltinFunctionId op)
      : HUnaryOperation(value), op_(op) {
    switch (op) {
      case kMathFloor:
      case kMathRound:
      case kMathCeil:
        set_representation(Representation::Integer32());
        break;
      case kMathAbs:
        set_representation(Representation::Tagged());
        SetFlag(kFlexibleRepresentation);
        break;
      case kMathSqrt:
      case kMathPowHalf:
      case kMathLog:
      case kMathSin:
      case kMathCos:
        set_representation(Representation::Double());
        break;
      default:
        UNREACHABLE();
    }
    SetFlag(kUseGVN);
  }

  virtual void PrintDataTo(StringStream* stream) const;

  virtual HType CalculateInferredType() const;

  virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);

  virtual Representation RequiredInputRepresentation(int index) const {
    switch (op_) {
      case kMathFloor:
      case kMathRound:
      case kMathCeil:
      case kMathSqrt:
      case kMathPowHalf:
      case kMathLog:
      case kMathSin:
      case kMathCos:
        return Representation::Double();
        break;
      case kMathAbs:
        return representation();
        break;
      default:
        return Representation::None();
    }
  }

  virtual HValue* Canonicalize() {
    // If the input is integer32 then we replace the floor instruction
    // with its inputs.  This happens before the representation changes are
    // introduced.
    if (op() == kMathFloor) {
      if (value()->representation().IsInteger32()) return value();
    }
    return this;
  }

  BuiltinFunctionId op() const { return op_; }
  const char* OpName() const;

  DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation, "unary_math_operation")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HUnaryMathOperation* b = HUnaryMathOperation::cast(other);
    return op_ == b->op();
  }

 private:
  BuiltinFunctionId op_;
};


class HLoadElements: public HUnaryOperation {
 public:
  explicit HLoadElements(HValue* value) : HUnaryOperation(value) {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
    SetFlag(kDependsOnMaps);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  DECLARE_CONCRETE_INSTRUCTION(LoadElements, "load-elements")
};


class HCheckMap: public HUnaryOperation {
 public:
  HCheckMap(HValue* value, Handle<Map> map)
      : HUnaryOperation(value), map_(map) {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
    SetFlag(kDependsOnMaps);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }
  virtual void PrintDataTo(StringStream* stream) const;
  virtual HType CalculateInferredType() const;

#ifdef DEBUG
  virtual void Verify() const;
#endif

  Handle<Map> map() const { return map_; }

  DECLARE_CONCRETE_INSTRUCTION(CheckMap, "check_map")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HCheckMap* b = HCheckMap::cast(other);
    return map_.is_identical_to(b->map());
  }

 private:
  Handle<Map> map_;
};


class HCheckFunction: public HUnaryOperation {
 public:
  HCheckFunction(HValue* value, Handle<JSFunction> function)
      : HUnaryOperation(value), target_(function) {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }
  virtual void PrintDataTo(StringStream* stream) const;
  virtual HType CalculateInferredType() const;

#ifdef DEBUG
  virtual void Verify() const;
#endif

  Handle<JSFunction> target() const { return target_; }

  DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check_function")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HCheckFunction* b = HCheckFunction::cast(other);
    return target_.is_identical_to(b->target());
  }

 private:
  Handle<JSFunction> target_;
};


class HCheckInstanceType: public HUnaryOperation {
 public:
  // Check that the instance type is in the range [first, last] where
  // both first and last are included.
  HCheckInstanceType(HValue* value, InstanceType first, InstanceType last)
      : HUnaryOperation(value), first_(first), last_(last) {
    ASSERT(first <= last);
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

#ifdef DEBUG
  virtual void Verify() const;
#endif

  static HCheckInstanceType* NewIsJSObjectOrJSFunction(HValue* value);

  InstanceType first() const { return first_; }
  InstanceType last() const { return last_; }

  DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check_instance_type")

 protected:
  // TODO(ager): It could be nice to allow the ommision of instance
  // type checks if we have already performed an instance type check
  // with a larger range.
  virtual bool DataEquals(HValue* other) const {
    HCheckInstanceType* b = HCheckInstanceType::cast(other);
    return (first_ == b->first()) && (last_ == b->last());
  }

 private:
  InstanceType first_;
  InstanceType last_;
};


class HCheckNonSmi: public HUnaryOperation {
 public:
  explicit HCheckNonSmi(HValue* value) : HUnaryOperation(value) {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  virtual HType CalculateInferredType() const;

#ifdef DEBUG
  virtual void Verify() const;
#endif

  DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check_non_smi")
};


class HCheckPrototypeMaps: public HUnaryOperation {
 public:
  HCheckPrototypeMaps(HValue* value,
                      Handle<JSObject> holder,
                      Handle<Map> receiver_map)
      : HUnaryOperation(value),
        holder_(holder),
        receiver_map_(receiver_map) {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
    SetFlag(kDependsOnMaps);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

#ifdef DEBUG
  virtual void Verify() const;
#endif

  Handle<JSObject> holder() const { return holder_; }
  Handle<Map> receiver_map() const { return receiver_map_; }

  DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check_prototype_maps")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HCheckPrototypeMaps* b = HCheckPrototypeMaps::cast(other);
    return holder_.is_identical_to(b->holder()) &&
        receiver_map_.is_identical_to(b->receiver_map());
  }

 private:
  Handle<JSObject> holder_;
  Handle<Map> receiver_map_;
};


class HCheckSmi: public HUnaryOperation {
 public:
  explicit HCheckSmi(HValue* value) : HUnaryOperation(value) {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }
  virtual HType CalculateInferredType() const;

#ifdef DEBUG
  virtual void Verify() const;
#endif

  DECLARE_CONCRETE_INSTRUCTION(CheckSmi, "check_smi")
};


class HPhi: public HValue {
 public:
  explicit HPhi(int merged_index)
      : inputs_(2),
        merged_index_(merged_index),
        phi_id_(-1) {
    for (int i = 0; i < Representation::kNumRepresentations; i++) {
      non_phi_uses_[i] = 0;
      indirect_uses_[i] = 0;
    }
    ASSERT(merged_index >= 0);
    set_representation(Representation::Tagged());
    SetFlag(kFlexibleRepresentation);
  }

  virtual Representation InferredRepresentation() const {
    bool double_occurred = false;
    bool int32_occurred = false;
    for (int i = 0; i < OperandCount(); ++i) {
      HValue* value = OperandAt(i);
      if (value->representation().IsDouble()) double_occurred = true;
      if (value->representation().IsInteger32()) int32_occurred = true;
      if (value->representation().IsTagged()) return Representation::Tagged();
    }

    if (double_occurred) return Representation::Double();
    if (int32_occurred) return Representation::Integer32();
    return Representation::None();
  }

  virtual Range* InferRange();
  virtual Representation RequiredInputRepresentation(int index) const {
    return representation();
  }
  virtual HType CalculateInferredType() const;
  virtual int OperandCount() const { return inputs_.length(); }
  virtual HValue* OperandAt(int index) const { return inputs_[index]; }
  HValue* GetRedundantReplacement() const;
  void AddInput(HValue* value);

  bool HasReceiverOperand();

  int merged_index() const { return merged_index_; }

  virtual const char* Mnemonic() const { return "phi"; }

  virtual void PrintTo(StringStream* stream) const;

#ifdef DEBUG
  virtual void Verify() const;
#endif

  DECLARE_INSTRUCTION(Phi)

  void InitRealUses(int id);
  void AddNonPhiUsesFrom(HPhi* other);
  void AddIndirectUsesTo(int* use_count);

  int tagged_non_phi_uses() const {
    return non_phi_uses_[Representation::kTagged];
  }
  int int32_non_phi_uses() const {
    return non_phi_uses_[Representation::kInteger32];
  }
  int double_non_phi_uses() const {
    return non_phi_uses_[Representation::kDouble];
  }
  int tagged_indirect_uses() const {
    return indirect_uses_[Representation::kTagged];
  }
  int int32_indirect_uses() const {
    return indirect_uses_[Representation::kInteger32];
  }
  int double_indirect_uses() const {
    return indirect_uses_[Representation::kDouble];
  }
  int phi_id() { return phi_id_; }

 protected:
  virtual void DeleteFromGraph();
  virtual void InternalSetOperandAt(int index, HValue* value) {
    inputs_[index] = value;
  }

 private:
  ZoneList<HValue*> inputs_;
  int merged_index_;

  int non_phi_uses_[Representation::kNumRepresentations];
  int indirect_uses_[Representation::kNumRepresentations];
  int phi_id_;
};


class HArgumentsObject: public HInstruction {
 public:
  HArgumentsObject() {
    set_representation(Representation::Tagged());
    SetFlag(kIsArguments);
  }

  DECLARE_CONCRETE_INSTRUCTION(ArgumentsObject, "arguments-object")
};


class HConstant: public HInstruction {
 public:
  HConstant(Handle<Object> handle, Representation r);

  Handle<Object> handle() const { return handle_; }

  virtual bool EmitAtUses() const { return !representation().IsDouble(); }
  virtual void PrintDataTo(StringStream* stream) const;
  virtual HType CalculateInferredType() const;
  bool IsInteger() const { return handle_->IsSmi(); }
  HConstant* CopyToRepresentation(Representation r) const;
  HConstant* CopyToTruncatedInt32() const;
  bool HasInteger32Value() const { return has_int32_value_; }
  int32_t Integer32Value() const {
    ASSERT(HasInteger32Value());
    return int32_value_;
  }
  bool HasDoubleValue() const { return has_double_value_; }
  double DoubleValue() const {
    ASSERT(HasDoubleValue());
    return double_value_;
  }
  bool HasStringValue() const { return handle_->IsString(); }

  virtual intptr_t Hashcode() const {
    ASSERT(!Heap::allow_allocation(false));
    return reinterpret_cast<intptr_t>(*handle());
  }

#ifdef DEBUG
  virtual void Verify() const { }
#endif

  DECLARE_CONCRETE_INSTRUCTION(Constant, "constant")

 protected:
  virtual Range* InferRange();

  virtual bool DataEquals(HValue* other) const {
    HConstant* other_constant = HConstant::cast(other);
    return handle().is_identical_to(other_constant->handle());
  }

 private:
  Handle<Object> handle_;
  HType constant_type_;

  // The following two values represent the int32 and the double value of the
  // given constant if there is a lossless conversion between the constant
  // and the specific representation.
  bool has_int32_value_;
  int32_t int32_value_;
  bool has_double_value_;
  double double_value_;
};


class HBinaryOperation: public HInstruction {
 public:
  HBinaryOperation(HValue* left, HValue* right) {
    ASSERT(left != NULL && right != NULL);
    SetOperandAt(0, left);
    SetOperandAt(1, right);
  }

  HValue* left() const { return OperandAt(0); }
  HValue* right() const { return OperandAt(1); }

  // TODO(kasperl): Move these helpers to the IA-32 Lithium
  // instruction sequence builder.
  HValue* LeastConstantOperand() const {
    if (IsCommutative() && left()->IsConstant()) return right();
    return left();
  }
  HValue* MostConstantOperand() const {
    if (IsCommutative() && left()->IsConstant()) return left();
    return right();
  }

  virtual bool IsCommutative() const { return false; }

  virtual void PrintDataTo(StringStream* stream) const;
  virtual int OperandCount() const { return operands_.length(); }
  virtual HValue* OperandAt(int index) const { return operands_[index]; }

  DECLARE_INSTRUCTION(BinaryOperation)

 protected:
  virtual void InternalSetOperandAt(int index, HValue* value) {
    operands_[index] = value;
  }

 private:
  HOperandVector<2> operands_;
};


class HApplyArguments: public HInstruction {
 public:
  HApplyArguments(HValue* function,
                  HValue* receiver,
                  HValue* length,
                  HValue* elements) {
    set_representation(Representation::Tagged());
    SetOperandAt(0, function);
    SetOperandAt(1, receiver);
    SetOperandAt(2, length);
    SetOperandAt(3, elements);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    // The length is untagged, all other inputs are tagged.
    return (index == 2)
        ? Representation::Integer32()
        : Representation::Tagged();
  }

  HValue* function() const { return OperandAt(0); }
  HValue* receiver() const { return OperandAt(1); }
  HValue* length() const { return OperandAt(2); }
  HValue* elements() const { return OperandAt(3); }

  virtual int OperandCount() const { return operands_.length(); }
  virtual HValue* OperandAt(int index) const { return operands_[index]; }

  DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply_arguments")



 protected:
  virtual void InternalSetOperandAt(int index, HValue* value) {
    operands_[index] = value;
  }

 private:
  HOperandVector<4> operands_;
};


class HArgumentsElements: public HInstruction {
 public:
  HArgumentsElements() {
    // The value produced by this instruction is a pointer into the stack
    // that looks as if it was a smi because of alignment.
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
  }

  DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements, "arguments_elements")
};


class HArgumentsLength: public HUnaryOperation {
 public:
  explicit HArgumentsLength(HValue* value) : HUnaryOperation(value) {
    set_representation(Representation::Integer32());
    SetFlag(kUseGVN);
  }

  DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments_length")
};


class HAccessArgumentsAt: public HInstruction {
 public:
  HAccessArgumentsAt(HValue* arguments, HValue* length, HValue* index) {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
    SetOperandAt(0, arguments);
    SetOperandAt(1, length);
    SetOperandAt(2, index);
  }

  virtual void PrintDataTo(StringStream* stream) const;

  virtual Representation RequiredInputRepresentation(int index) const {
    // The arguments elements is considered tagged.
    return index == 0
        ? Representation::Tagged()
        : Representation::Integer32();
  }

  HValue* arguments() const { return operands_[0]; }
  HValue* length() const { return operands_[1]; }
  HValue* index() const { return operands_[2]; }

  virtual int OperandCount() const { return operands_.length(); }
  virtual HValue* OperandAt(int index) const { return operands_[index]; }

  DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt, "access_arguments_at")

 protected:
  virtual void InternalSetOperandAt(int index, HValue* value) {
    operands_[index] = value;
  }

 private:
  HOperandVector<3> operands_;
};


class HBoundsCheck: public HBinaryOperation {
 public:
  HBoundsCheck(HValue* index, HValue* length)
      : HBinaryOperation(index, length) {
    SetFlag(kUseGVN);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Integer32();
  }

#ifdef DEBUG
  virtual void Verify() const;
#endif

  HValue* index() const { return left(); }
  HValue* length() const { return right(); }

  DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds_check")
};


class HBitwiseBinaryOperation: public HBinaryOperation {
 public:
  HBitwiseBinaryOperation(HValue* left, HValue* right)
      : HBinaryOperation(left, right) {
    // Default to truncating, Integer32, UseGVN.
    set_representation(Representation::Integer32());
    SetFlag(kTruncatingToInt32);
    SetFlag(kUseGVN);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Integer32();
  }

  DECLARE_INSTRUCTION(BitwiseBinaryOperation)
};


class HArithmeticBinaryOperation: public HBinaryOperation {
 public:
  HArithmeticBinaryOperation(HValue* left, HValue* right)
      : HBinaryOperation(left, right) {
    set_representation(Representation::Tagged());
    SetFlag(kFlexibleRepresentation);
    SetFlagMask(AllSideEffects());
  }

  virtual void RepresentationChanged(Representation to) {
    if (!to.IsTagged()) {
      ClearFlagMask(AllSideEffects());
      SetFlag(kUseGVN);
    }
  }

  virtual HType CalculateInferredType() const;
  virtual Representation RequiredInputRepresentation(int index) const {
    return representation();
  }
  virtual Representation InferredRepresentation() const {
    if (left()->representation().Equals(right()->representation())) {
      return left()->representation();
    }
    return HValue::InferredRepresentation();
  }

  DECLARE_INSTRUCTION(ArithmeticBinaryOperation)
};


class HCompare: public HBinaryOperation {
 public:
  HCompare(HValue* left, HValue* right, Token::Value token)
      : HBinaryOperation(left, right), token_(token) {
    ASSERT(Token::IsCompareOp(token));
    set_representation(Representation::Tagged());
    SetFlagMask(AllSideEffects());
  }

  void SetInputRepresentation(Representation r);
  virtual bool EmitAtUses() const { return uses()->length() <= 1; }
  virtual Representation RequiredInputRepresentation(int index) const {
    return input_representation_;
  }
  Representation GetInputRepresentation() const {
    return input_representation_;
  }
  Token::Value token() const { return token_; }
  virtual void PrintDataTo(StringStream* stream) const;

  virtual HType CalculateInferredType() const;

  virtual intptr_t Hashcode() const {
    return HValue::Hashcode() * 7 + token_;
  }

  DECLARE_CONCRETE_INSTRUCTION(Compare, "compare")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HCompare* comp = HCompare::cast(other);
    return token_ == comp->token();
  }

 private:
  Representation input_representation_;
  Token::Value token_;
};


class HCompareJSObjectEq: public HBinaryOperation {
 public:
  HCompareJSObjectEq(HValue* left, HValue* right)
      : HBinaryOperation(left, right) {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
  }

  virtual bool EmitAtUses() const { return uses()->length() <= 1; }
  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }
  virtual HType CalculateInferredType() const;

  DECLARE_CONCRETE_INSTRUCTION(CompareJSObjectEq, "compare-js-object-eq")
};


class HUnaryPredicate: public HUnaryOperation {
 public:
  explicit HUnaryPredicate(HValue* value) : HUnaryOperation(value) {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
  }
  virtual bool EmitAtUses() const { return uses()->length() <= 1; }
  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }
  virtual HType CalculateInferredType() const;
};


class HIsNull: public HUnaryPredicate {
 public:
  HIsNull(HValue* value, bool is_strict)
      : HUnaryPredicate(value), is_strict_(is_strict) { }

  bool is_strict() const { return is_strict_; }

  DECLARE_CONCRETE_INSTRUCTION(IsNull, "is_null")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HIsNull* b = HIsNull::cast(other);
    return is_strict_ == b->is_strict();
  }

 private:
  bool is_strict_;
};


class HIsObject: public HUnaryPredicate {
 public:
  explicit HIsObject(HValue* value) : HUnaryPredicate(value) { }

  DECLARE_CONCRETE_INSTRUCTION(IsObject, "is_object")
};


class HIsSmi: public HUnaryPredicate {
 public:
  explicit HIsSmi(HValue* value) : HUnaryPredicate(value) { }

  DECLARE_CONCRETE_INSTRUCTION(IsSmi, "is_smi")
};


class HHasInstanceType: public HUnaryPredicate {
 public:
  HHasInstanceType(HValue* value, InstanceType type)
      : HUnaryPredicate(value), from_(type), to_(type) { }
  HHasInstanceType(HValue* value, InstanceType from, InstanceType to)
      : HUnaryPredicate(value), from_(from), to_(to) {
    ASSERT(to == LAST_TYPE);  // Others not implemented yet in backend.
  }

  InstanceType from() { return from_; }
  InstanceType to() { return to_; }

  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(HasInstanceType, "has_instance_type")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HHasInstanceType* b = HHasInstanceType::cast(other);
    return (from_ == b->from()) && (to_ == b->to());
  }

 private:
  InstanceType from_;
  InstanceType to_;  // Inclusive range, not all combinations work.
};


class HHasCachedArrayIndex: public HUnaryPredicate {
 public:
  explicit HHasCachedArrayIndex(HValue* value) : HUnaryPredicate(value) { }

  DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndex, "has_cached_array_index")
};


class HClassOfTest: public HUnaryPredicate {
 public:
  HClassOfTest(HValue* value, Handle<String> class_name)
      : HUnaryPredicate(value), class_name_(class_name) { }

  DECLARE_CONCRETE_INSTRUCTION(ClassOfTest, "class_of_test")

  virtual void PrintDataTo(StringStream* stream) const;

  Handle<String> class_name() const { return class_name_; }

 protected:
  virtual bool DataEquals(HValue* other) const {
    HClassOfTest* b = HClassOfTest::cast(other);
    return class_name_.is_identical_to(b->class_name_);
  }

 private:
  Handle<String> class_name_;
};


class HTypeofIs: public HUnaryPredicate {
 public:
  HTypeofIs(HValue* value, Handle<String> type_literal)
      : HUnaryPredicate(value), type_literal_(type_literal) { }

  Handle<String> type_literal() { return type_literal_; }
  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(TypeofIs, "typeof_is")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HTypeofIs* b = HTypeofIs::cast(other);
    return type_literal_.is_identical_to(b->type_literal_);
  }

 private:
  Handle<String> type_literal_;
};


class HInstanceOf: public HBinaryOperation {
 public:
  HInstanceOf(HValue* left, HValue* right) : HBinaryOperation(left, right) {
    set_representation(Representation::Tagged());
    SetFlagMask(AllSideEffects());
  }

  virtual bool EmitAtUses() const { return uses()->length() <= 1; }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance_of")
};


class HPower: public HBinaryOperation {
 public:
  HPower(HValue* left, HValue* right)
      : HBinaryOperation(left, right) {
    set_representation(Representation::Double());
    SetFlag(kUseGVN);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return (index == 1) ? Representation::None() : Representation::Double();
  }

  DECLARE_CONCRETE_INSTRUCTION(Power, "power")
};


class HAdd: public HArithmeticBinaryOperation {
 public:
  HAdd(HValue* left, HValue* right) : HArithmeticBinaryOperation(left, right) {
    SetFlag(kCanOverflow);
  }

  // Add is only commutative if two integer values are added and not if two
  // tagged values are added (because it might be a String concatenation).
  virtual bool IsCommutative() const {
    return !representation().IsTagged();
  }

  virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);

  virtual HType CalculateInferredType() const;

  DECLARE_CONCRETE_INSTRUCTION(Add, "add")

 protected:
  virtual Range* InferRange();
};


class HSub: public HArithmeticBinaryOperation {
 public:
  HSub(HValue* left, HValue* right) : HArithmeticBinaryOperation(left, right) {
    SetFlag(kCanOverflow);
  }

  virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);

  DECLARE_CONCRETE_INSTRUCTION(Sub, "sub")

 protected:
  virtual Range* InferRange();
};


class HMul: public HArithmeticBinaryOperation {
 public:
  HMul(HValue* left, HValue* right) : HArithmeticBinaryOperation(left, right) {
    SetFlag(kCanOverflow);
  }

  virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);

  // Only commutative if it is certain that not two objects are multiplicated.
  virtual bool IsCommutative() const {
    return !representation().IsTagged();
  }

  DECLARE_CONCRETE_INSTRUCTION(Mul, "mul")

 protected:
  virtual Range* InferRange();
};


class HMod: public HArithmeticBinaryOperation {
 public:
  HMod(HValue* left, HValue* right) : HArithmeticBinaryOperation(left, right) {
    SetFlag(kCanBeDivByZero);
  }

  virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);

  DECLARE_CONCRETE_INSTRUCTION(Mod, "mod")

 protected:
  virtual Range* InferRange();
};


class HDiv: public HArithmeticBinaryOperation {
 public:
  HDiv(HValue* left, HValue* right) : HArithmeticBinaryOperation(left, right) {
    SetFlag(kCanBeDivByZero);
    SetFlag(kCanOverflow);
  }

  virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);

  DECLARE_CONCRETE_INSTRUCTION(Div, "div")

 protected:
  virtual Range* InferRange();
};


class HBitAnd: public HBitwiseBinaryOperation {
 public:
  HBitAnd(HValue* left, HValue* right)
      : HBitwiseBinaryOperation(left, right) { }

  virtual bool IsCommutative() const { return true; }
  virtual HType CalculateInferredType() const;

  DECLARE_CONCRETE_INSTRUCTION(BitAnd, "bit_and")

 protected:
  virtual Range* InferRange();
};


class HBitXor: public HBitwiseBinaryOperation {
 public:
  HBitXor(HValue* left, HValue* right)
      : HBitwiseBinaryOperation(left, right) { }

  virtual bool IsCommutative() const { return true; }
  virtual HType CalculateInferredType() const;

  DECLARE_CONCRETE_INSTRUCTION(BitXor, "bit_xor")
};


class HBitOr: public HBitwiseBinaryOperation {
 public:
  HBitOr(HValue* left, HValue* right)
      : HBitwiseBinaryOperation(left, right) { }

  virtual bool IsCommutative() const { return true; }
  virtual HType CalculateInferredType() const;

  DECLARE_CONCRETE_INSTRUCTION(BitOr, "bit_or")

 protected:
  virtual Range* InferRange();
};


class HShl: public HBitwiseBinaryOperation {
 public:
  HShl(HValue* left, HValue* right)
      : HBitwiseBinaryOperation(left, right) { }

  virtual Range* InferRange();
  virtual HType CalculateInferredType() const;

  DECLARE_CONCRETE_INSTRUCTION(Shl, "shl")
};


class HShr: public HBitwiseBinaryOperation {
 public:
  HShr(HValue* left, HValue* right)
      : HBitwiseBinaryOperation(left, right) { }

  virtual HType CalculateInferredType() const;

  DECLARE_CONCRETE_INSTRUCTION(Shr, "shr")
};


class HSar: public HBitwiseBinaryOperation {
 public:
  HSar(HValue* left, HValue* right)
      : HBitwiseBinaryOperation(left, right) { }

  virtual Range* InferRange();
  virtual HType CalculateInferredType() const;

  DECLARE_CONCRETE_INSTRUCTION(Sar, "sar")
};


class HOsrEntry: public HInstruction {
 public:
  explicit HOsrEntry(int ast_id) : ast_id_(ast_id) {
    SetFlag(kChangesOsrEntries);
  }

  int ast_id() const { return ast_id_; }

  DECLARE_CONCRETE_INSTRUCTION(OsrEntry, "osr_entry")

 private:
  int ast_id_;
};


class HParameter: public HInstruction {
 public:
  explicit HParameter(unsigned index) : index_(index) {
    set_representation(Representation::Tagged());
  }

  unsigned index() const { return index_; }

  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(Parameter, "parameter")

 private:
  unsigned index_;
};


class HCallStub: public HInstruction {
 public:
  HCallStub(CodeStub::Major major_key, int argument_count)
      : major_key_(major_key),
        argument_count_(argument_count),
        transcendental_type_(TranscendentalCache::kNumberOfCaches) {
    set_representation(Representation::Tagged());
    SetFlagMask(AllSideEffects());
  }

  CodeStub::Major major_key() { return major_key_; }
  int argument_count() { return argument_count_; }

  void set_transcendental_type(TranscendentalCache::Type transcendental_type) {
    transcendental_type_ = transcendental_type;
  }
  TranscendentalCache::Type transcendental_type() {
    return transcendental_type_;
  }
  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(CallStub, "call_stub")

 private:
  CodeStub::Major major_key_;
  int argument_count_;
  TranscendentalCache::Type transcendental_type_;
};


class HUnknownOSRValue: public HInstruction {
 public:
  HUnknownOSRValue() { set_representation(Representation::Tagged()); }

  DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue, "unknown_osr_value")
};


class HLoadGlobal: public HInstruction {
 public:
  HLoadGlobal(Handle<JSGlobalPropertyCell> cell, bool check_hole_value)
      : cell_(cell), check_hole_value_(check_hole_value) {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
    SetFlag(kDependsOnGlobalVars);
  }

  Handle<JSGlobalPropertyCell>  cell() const { return cell_; }
  bool check_hole_value() const { return check_hole_value_; }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }
  virtual void PrintDataTo(StringStream* stream) const;

  virtual intptr_t Hashcode() const {
    ASSERT(!Heap::allow_allocation(false));
    return reinterpret_cast<intptr_t>(*cell_);
  }

  DECLARE_CONCRETE_INSTRUCTION(LoadGlobal, "load_global")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HLoadGlobal* b = HLoadGlobal::cast(other);
    return cell_.is_identical_to(b->cell());
  }

 private:
  Handle<JSGlobalPropertyCell> cell_;
  bool check_hole_value_;
};


class HStoreGlobal: public HUnaryOperation {
 public:
  HStoreGlobal(HValue* value, Handle<JSGlobalPropertyCell> cell)
      : HUnaryOperation(value), cell_(cell) {
    SetFlag(kChangesGlobalVars);
  }

  Handle<JSGlobalPropertyCell> cell() const { return cell_; }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }
  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(StoreGlobal, "store_global")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HStoreGlobal* b = HStoreGlobal::cast(other);
    return cell_.is_identical_to(b->cell());
  }

 private:
  Handle<JSGlobalPropertyCell> cell_;
};


class HLoadNamedField: public HUnaryOperation {
 public:
  HLoadNamedField(HValue* object, bool is_in_object, int offset)
      : HUnaryOperation(object),
        is_in_object_(is_in_object),
        offset_(offset) {
    set_representation(Representation::Tagged());
    SetFlag(kUseGVN);
    if (is_in_object) {
      SetFlag(kDependsOnInobjectFields);
    } else {
      SetFlag(kDependsOnBackingStoreFields);
    }
  }

  HValue* object() const { return OperandAt(0); }
  bool is_in_object() const { return is_in_object_; }
  int offset() const { return offset_; }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }
  virtual void PrintDataTo(StringStream* stream) const;

  DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load_named_field")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HLoadNamedField* b = HLoadNamedField::cast(other);
    return is_in_object_ == b->is_in_object_ && offset_ == b->offset_;
  }

 private:
  bool is_in_object_;
  int offset_;
};


class HLoadNamedGeneric: public HUnaryOperation {
 public:
  HLoadNamedGeneric(HValue* object, Handle<Object> name)
      : HUnaryOperation(object), name_(name) {
    set_representation(Representation::Tagged());
    SetFlagMask(AllSideEffects());
  }

  HValue* object() const { return OperandAt(0); }
  Handle<Object> name() const { return name_; }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric, "load_named_generic")

 protected:
  virtual bool DataEquals(HValue* other) const {
    HLoadNamedGeneric* b = HLoadNamedGeneric::cast(other);
    return name_.is_identical_to(b->name_);
  }

 private:
  Handle<Object> name_;
};


class HLoadFunctionPrototype: public HUnaryOperation {
 public:
  explicit HLoadFunctionPrototype(HValue* function)
      : HUnaryOperation(function) {
    set_representation(Representation::Tagged());
    SetFlagMask(kDependsOnFunctionPrototypes);
  }

  HValue* function() const { return OperandAt(0); }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype, "load_function_prototype")

 protected:
  virtual bool DataEquals(HValue* other) const { return true; }
};


class HLoadKeyed: public HBinaryOperation {
 public:
  HLoadKeyed(HValue* obj, HValue* key) : HBinaryOperation(obj, key) {
    set_representation(Representation::Tagged());
  }

  virtual void PrintDataTo(StringStream* stream) const;

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }
  HValue* object() const { return OperandAt(0); }
  HValue* key() const { return OperandAt(1); }

  DECLARE_INSTRUCTION(LoadKeyed)
};


class HLoadKeyedFastElement: public HLoadKeyed {
 public:
  HLoadKeyedFastElement(HValue* obj, HValue* key) : HLoadKeyed(obj, key) {
    SetFlag(kDependsOnArrayElements);
    SetFlag(kUseGVN);
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    // The key is supposed to be Integer32.
    return (index == 1) ? Representation::Integer32()
        : Representation::Tagged();
  }

  DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastElement,
                               "load_keyed_fast_element")
};


class HLoadKeyedGeneric: public HLoadKeyed {
 public:
  HLoadKeyedGeneric(HValue* obj, HValue* key) : HLoadKeyed(obj, key) {
    SetFlagMask(AllSideEffects());
  }

  DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric, "load_keyed_generic")
};


class HStoreNamed: public HBinaryOperation {
 public:
  HStoreNamed(HValue* obj, Handle<Object> name, HValue* val)
      : HBinaryOperation(obj, val), name_(name) {
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  virtual void PrintDataTo(StringStream* stream) const;

  HValue* object() const { return OperandAt(0); }
  Handle<Object> name() const { return name_; }
  HValue* value() const { return OperandAt(1); }
  void set_value(HValue* value) { SetOperandAt(1, value); }

  DECLARE_INSTRUCTION(StoreNamed)

 protected:
  virtual bool DataEquals(HValue* other) const {
    HStoreNamed* b = HStoreNamed::cast(other);
    return name_.is_identical_to(b->name_);
  }

 private:
  Handle<Object> name_;
};


class HStoreNamedField: public HStoreNamed {
 public:
  HStoreNamedField(HValue* obj,
                   Handle<Object> name,
                   HValue* val,
                   bool in_object,
                   int offset)
      : HStoreNamed(obj, name, val),
        is_in_object_(in_object),
        offset_(offset) {
    if (is_in_object_) {
      SetFlag(kChangesInobjectFields);
    } else {
      SetFlag(kChangesBackingStoreFields);
    }
  }

  DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store_named_field")

  virtual Representation RequiredInputRepresentation(int index) const {
    return  Representation::Tagged();
  }
  virtual void PrintDataTo(StringStream* stream) const;

  bool is_in_object() const { return is_in_object_; }
  int offset() const { return offset_; }
  Handle<Map> transition() const { return transition_; }
  void set_transition(Handle<Map> map) { transition_ = map; }

 private:
  bool is_in_object_;
  int offset_;
  Handle<Map> transition_;
};


class HStoreNamedGeneric: public HStoreNamed {
 public:
  HStoreNamedGeneric(HValue* obj, Handle<Object> name, HValue* val)
      : HStoreNamed(obj, name, val) {
    SetFlagMask(AllSideEffects());
  }

  DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store_named_generic")
};


class HStoreKeyed: public HInstruction {
 public:
  HStoreKeyed(HValue* obj, HValue* key, HValue* val) {
    SetOperandAt(0, obj);
    SetOperandAt(1, key);
    SetOperandAt(2, val);
  }

  virtual void PrintDataTo(StringStream* stream) const;
  virtual int OperandCount() const { return operands_.length(); }
  virtual HValue* OperandAt(int index) const { return operands_[index]; }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  HValue* object() const { return OperandAt(0); }
  HValue* key() const { return OperandAt(1); }
  HValue* value() const { return OperandAt(2); }

  DECLARE_INSTRUCTION(StoreKeyed)

 protected:
  virtual void InternalSetOperandAt(int index, HValue* value) {
    operands_[index] = value;
  }

 private:
  HOperandVector<3> operands_;
};


class HStoreKeyedFastElement: public HStoreKeyed {
 public:
  HStoreKeyedFastElement(HValue* obj, HValue* key, HValue* val)
      : HStoreKeyed(obj, key, val) {
    SetFlag(kChangesArrayElements);
  }

  bool NeedsWriteBarrier() const {
    return !value()->type().IsSmi();
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    // The key is supposed to be Integer32.
    return (index == 1) ? Representation::Integer32()
        : Representation::Tagged();
  }

  DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement,
                               "store_keyed_fast_element")
};


class HStoreKeyedGeneric: public HStoreKeyed {
 public:
  HStoreKeyedGeneric(HValue* obj, HValue* key, HValue* val)
      : HStoreKeyed(obj, key, val) {
    SetFlagMask(AllSideEffects());
  }

  DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store_keyed_generic")
};


class HMaterializedLiteral: public HInstruction {
 public:
  HMaterializedLiteral(int index, int depth)
      : literal_index_(index), depth_(depth) {
    set_representation(Representation::Tagged());
  }

  int literal_index() const { return literal_index_; }
  int depth() const { return depth_; }

  DECLARE_INSTRUCTION(MaterializedLiteral)

 private:
  int literal_index_;
  int depth_;
};


class HArrayLiteral: public HMaterializedLiteral {
 public:
  HArrayLiteral(Handle<FixedArray> constant_elements,
                int length,
                int literal_index,
                int depth)
      : HMaterializedLiteral(literal_index, depth),
        length_(length),
        constant_elements_(constant_elements) {}

  Handle<FixedArray> constant_elements() const { return constant_elements_; }
  int length() const { return length_; }

  bool IsCopyOnWrite() const;

  DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral, "array_literal")

 private:
  int length_;
  Handle<FixedArray> constant_elements_;
};


class HObjectLiteral: public HMaterializedLiteral {
 public:
  HObjectLiteral(Handle<FixedArray> constant_properties,
                 bool fast_elements,
                 int literal_index,
                 int depth)
      : HMaterializedLiteral(literal_index, depth),
        constant_properties_(constant_properties),
        fast_elements_(fast_elements) {}

  Handle<FixedArray> constant_properties() const {
    return constant_properties_;
  }
  bool fast_elements() const { return fast_elements_; }

  DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object_literal")

 private:
  Handle<FixedArray> constant_properties_;
  bool fast_elements_;
};


class HRegExpLiteral: public HMaterializedLiteral {
 public:
  HRegExpLiteral(Handle<String> pattern,
                 Handle<String> flags,
                 int literal_index)
      : HMaterializedLiteral(literal_index, 0),
        pattern_(pattern),
        flags_(flags) { }

  Handle<String> pattern() { return pattern_; }
  Handle<String> flags() { return flags_; }

  DECLARE_CONCRETE_INSTRUCTION(RegExpLiteral, "regexp_literal")

 private:
  Handle<String> pattern_;
  Handle<String> flags_;
};


class HFunctionLiteral: public HInstruction {
 public:
  HFunctionLiteral(Handle<SharedFunctionInfo> shared, bool pretenure)
      : shared_info_(shared), pretenure_(pretenure) {
    set_representation(Representation::Tagged());
  }

  DECLARE_CONCRETE_INSTRUCTION(FunctionLiteral, "function_literal")

  Handle<SharedFunctionInfo> shared_info() const { return shared_info_; }
  bool pretenure() const { return pretenure_; }

 private:
  Handle<SharedFunctionInfo> shared_info_;
  bool pretenure_;
};


class HTypeof: public HUnaryOperation {
 public:
  explicit HTypeof(HValue* value) : HUnaryOperation(value) {
    set_representation(Representation::Tagged());
  }

  DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof")
};


class HValueOf: public HUnaryOperation {
 public:
  explicit HValueOf(HValue* value) : HUnaryOperation(value) {
    set_representation(Representation::Tagged());
  }

  DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value_of")
};


class HDeleteProperty: public HBinaryOperation {
 public:
  HDeleteProperty(HValue* obj, HValue* key)
      : HBinaryOperation(obj, key) {
    set_representation(Representation::Tagged());
    SetFlagMask(AllSideEffects());
  }

  virtual Representation RequiredInputRepresentation(int index) const {
    return Representation::Tagged();
  }

  DECLARE_CONCRETE_INSTRUCTION(DeleteProperty, "delete_property")

  HValue* object() const { return left(); }
  HValue* key() const { return right(); }
};

#undef DECLARE_INSTRUCTION
#undef DECLARE_CONCRETE_INSTRUCTION

} }  // namespace v8::internal

#endif  // V8_HYDROGEN_INSTRUCTIONS_H_
