// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/simplified-lowering.h"

#include <limits>

#include "src/address-map.h"
#include "src/base/bits.h"
#include "src/code-factory.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/diamond.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operation-typer.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/representation-change.h"
#include "src/compiler/simplified-operator.h"
#include "src/compiler/type-cache.h"
#include "src/conversions-inl.h"
#include "src/objects.h"

namespace v8 {
namespace internal {
namespace compiler {

// Macro for outputting trace information from representation inference.
#define TRACE(...)                                      \
  do {                                                  \
    if (FLAG_trace_representation) PrintF(__VA_ARGS__); \
  } while (false)

// Representation selection and lowering of {Simplified} operators to machine
// operators are interwined. We use a fixpoint calculation to compute both the
// output representation and the best possible lowering for {Simplified} nodes.
// Representation change insertion ensures that all values are in the correct
// machine representation after this phase, as dictated by the machine
// operators themselves.
enum Phase {
  // 1.) PROPAGATE: Traverse the graph from the end, pushing usage information
  //     backwards from uses to definitions, around cycles in phis, according
  //     to local rules for each operator.
  //     During this phase, the usage information for a node determines the best
  //     possible lowering for each operator so far, and that in turn determines
  //     the output representation.
  //     Therefore, to be correct, this phase must iterate to a fixpoint before
  //     the next phase can begin.
  PROPAGATE,

  // 2.) RETYPE: Propagate types from type feedback forwards.
  RETYPE,

  // 3.) LOWER: perform lowering for all {Simplified} nodes by replacing some
  //     operators for some nodes, expanding some nodes to multiple nodes, or
  //     removing some (redundant) nodes.
  //     During this phase, use the {RepresentationChanger} to insert
  //     representation changes between uses that demand a particular
  //     representation and nodes that produce a different representation.
  LOWER
};

namespace {

MachineRepresentation MachineRepresentationFromArrayType(
    ExternalArrayType array_type) {
  switch (array_type) {
    case kExternalUint8Array:
    case kExternalUint8ClampedArray:
    case kExternalInt8Array:
      return MachineRepresentation::kWord8;
    case kExternalUint16Array:
    case kExternalInt16Array:
      return MachineRepresentation::kWord16;
    case kExternalUint32Array:
    case kExternalInt32Array:
      return MachineRepresentation::kWord32;
    case kExternalFloat32Array:
      return MachineRepresentation::kFloat32;
    case kExternalFloat64Array:
      return MachineRepresentation::kFloat64;
  }
  UNREACHABLE();
  return MachineRepresentation::kNone;
}

UseInfo CheckedUseInfoAsWord32FromHint(
    NumberOperationHint hint, CheckForMinusZeroMode minus_zero_mode =
                                  CheckForMinusZeroMode::kCheckForMinusZero) {
  switch (hint) {
    case NumberOperationHint::kSignedSmall:
      return UseInfo::CheckedSignedSmallAsWord32(minus_zero_mode);
    case NumberOperationHint::kSigned32:
      return UseInfo::CheckedSigned32AsWord32(minus_zero_mode);
    case NumberOperationHint::kNumber:
      return UseInfo::CheckedNumberAsWord32();
    case NumberOperationHint::kNumberOrOddball:
      return UseInfo::CheckedNumberOrOddballAsWord32();
  }
  UNREACHABLE();
  return UseInfo::None();
}

UseInfo CheckedUseInfoAsFloat64FromHint(NumberOperationHint hint) {
  switch (hint) {
    case NumberOperationHint::kSignedSmall:
    case NumberOperationHint::kSigned32:
      // Not used currently.
      UNREACHABLE();
      break;
    case NumberOperationHint::kNumber:
      return UseInfo::CheckedNumberAsFloat64();
    case NumberOperationHint::kNumberOrOddball:
      return UseInfo::CheckedNumberOrOddballAsFloat64();
  }
  UNREACHABLE();
  return UseInfo::None();
}

UseInfo TruncatingUseInfoFromRepresentation(MachineRepresentation rep) {
  switch (rep) {
    case MachineRepresentation::kTaggedSigned:
    case MachineRepresentation::kTaggedPointer:
    case MachineRepresentation::kTagged:
      return UseInfo::AnyTagged();
    case MachineRepresentation::kFloat64:
      return UseInfo::TruncatingFloat64();
    case MachineRepresentation::kFloat32:
      return UseInfo::Float32();
    case MachineRepresentation::kWord64:
      return UseInfo::TruncatingWord64();
    case MachineRepresentation::kWord8:
    case MachineRepresentation::kWord16:
    case MachineRepresentation::kWord32:
      return UseInfo::TruncatingWord32();
    case MachineRepresentation::kBit:
      return UseInfo::Bool();
    case MachineRepresentation::kSimd128:  // Fall through.
    case MachineRepresentation::kNone:
      break;
  }
  UNREACHABLE();
  return UseInfo::None();
}


UseInfo UseInfoForBasePointer(const FieldAccess& access) {
  return access.tag() != 0 ? UseInfo::AnyTagged() : UseInfo::PointerInt();
}


UseInfo UseInfoForBasePointer(const ElementAccess& access) {
  return access.tag() != 0 ? UseInfo::AnyTagged() : UseInfo::PointerInt();
}

void ReplaceEffectControlUses(Node* node, Node* effect, Node* control) {
  for (Edge edge : node->use_edges()) {
    if (NodeProperties::IsControlEdge(edge)) {
      edge.UpdateTo(control);
    } else if (NodeProperties::IsEffectEdge(edge)) {
      edge.UpdateTo(effect);
    } else {
      DCHECK(NodeProperties::IsValueEdge(edge) ||
             NodeProperties::IsContextEdge(edge));
    }
  }
}

void ChangeToPureOp(Node* node, const Operator* new_op) {
  if (node->op()->EffectInputCount() > 0) {
    DCHECK_LT(0, node->op()->ControlInputCount());
    // Disconnect the node from effect and control chains.
    Node* control = NodeProperties::GetControlInput(node);
    Node* effect = NodeProperties::GetEffectInput(node);
    ReplaceEffectControlUses(node, effect, control);
    node->TrimInputCount(new_op->ValueInputCount());
  } else {
    DCHECK_EQ(0, node->op()->ControlInputCount());
  }
  NodeProperties::ChangeOp(node, new_op);
}

#ifdef DEBUG
// Helpers for monotonicity checking.
class InputUseInfos {
 public:
  explicit InputUseInfos(Zone* zone) : input_use_infos_(zone) {}

  void SetAndCheckInput(Node* node, int index, UseInfo use_info) {
    if (input_use_infos_.empty()) {
      input_use_infos_.resize(node->InputCount(), UseInfo::None());
    }
    // Check that the new use informatin is a super-type of the old
    // one.
    CHECK(IsUseLessGeneral(input_use_infos_[index], use_info));
    input_use_infos_[index] = use_info;
  }

 private:
  ZoneVector<UseInfo> input_use_infos_;

  static bool IsUseLessGeneral(UseInfo use1, UseInfo use2) {
    return use1.truncation().IsLessGeneralThan(use2.truncation());
  }
};

#endif  // DEBUG

}  // namespace


class RepresentationSelector {
 public:
  // Information for each node tracked during the fixpoint.
  class NodeInfo final {
   public:
    // Adds new use to the node. Returns true if something has changed
    // and the node has to be requeued.
    bool AddUse(UseInfo info) {
      Truncation old_truncation = truncation_;
      truncation_ = Truncation::Generalize(truncation_, info.truncation());
      return truncation_ != old_truncation;
    }

    void set_queued() { state_ = kQueued; }
    void set_visited() { state_ = kVisited; }
    void set_pushed() { state_ = kPushed; }
    void reset_state() { state_ = kUnvisited; }
    bool visited() const { return state_ == kVisited; }
    bool queued() const { return state_ == kQueued; }
    bool unvisited() const { return state_ == kUnvisited; }
    Truncation truncation() const { return truncation_; }
    void set_output(MachineRepresentation output) { representation_ = output; }

    MachineRepresentation representation() const { return representation_; }

    // Helpers for feedback typing.
    void set_feedback_type(Type* type) { feedback_type_ = type; }
    Type* feedback_type() const { return feedback_type_; }
    void set_weakened() { weakened_ = true; }
    bool weakened() const { return weakened_; }
    void set_restriction_type(Type* type) { restriction_type_ = type; }
    Type* restriction_type() const { return restriction_type_; }

   private:
    enum State : uint8_t { kUnvisited, kPushed, kVisited, kQueued };
    State state_ = kUnvisited;
    MachineRepresentation representation_ =
        MachineRepresentation::kNone;             // Output representation.
    Truncation truncation_ = Truncation::None();  // Information about uses.

    Type* restriction_type_ = Type::Any();
    Type* feedback_type_ = nullptr;
    bool weakened_ = false;
  };

  RepresentationSelector(JSGraph* jsgraph, Zone* zone,
                         RepresentationChanger* changer,
                         SourcePositionTable* source_positions)
      : jsgraph_(jsgraph),
        zone_(zone),
        count_(jsgraph->graph()->NodeCount()),
        info_(count_, zone),
#ifdef DEBUG
        node_input_use_infos_(count_, InputUseInfos(zone), zone),
#endif
        nodes_(zone),
        replacements_(zone),
        phase_(PROPAGATE),
        changer_(changer),
        queue_(zone),
        typing_stack_(zone),
        source_positions_(source_positions),
        type_cache_(TypeCache::Get()),
        op_typer_(jsgraph->isolate(), graph_zone()) {
  }

  // Forward propagation of types from type feedback.
  void RunTypePropagationPhase() {
    // Run type propagation.
    TRACE("--{Type propagation phase}--\n");
    phase_ = RETYPE;
    ResetNodeInfoState();

    DCHECK(typing_stack_.empty());
    typing_stack_.push({graph()->end(), 0});
    GetInfo(graph()->end())->set_pushed();
    while (!typing_stack_.empty()) {
      NodeState& current = typing_stack_.top();

      // If there is an unvisited input, push it and continue.
      bool pushed_unvisited = false;
      while (current.input_index < current.node->InputCount()) {
        Node* input = current.node->InputAt(current.input_index);
        NodeInfo* input_info = GetInfo(input);
        current.input_index++;
        if (input_info->unvisited()) {
          input_info->set_pushed();
          typing_stack_.push({input, 0});
          pushed_unvisited = true;
          break;
        }
      }
      if (pushed_unvisited) continue;

      // Process the top of the stack.
      Node* node = current.node;
      typing_stack_.pop();
      NodeInfo* info = GetInfo(node);
      info->set_visited();
      bool updated = UpdateFeedbackType(node);
      TRACE(" visit #%d: %s\n", node->id(), node->op()->mnemonic());
      VisitNode(node, info->truncation(), nullptr);
      TRACE("  ==> output ");
      PrintOutputInfo(info);
      TRACE("\n");
      if (updated) {
        for (Node* const user : node->uses()) {
          if (GetInfo(user)->visited()) {
            GetInfo(user)->set_queued();
            queue_.push(user);
          }
        }
      }
    }

    // Process the revisit queue.
    while (!queue_.empty()) {
      Node* node = queue_.front();
      queue_.pop();
      NodeInfo* info = GetInfo(node);
      info->set_visited();
      bool updated = UpdateFeedbackType(node);
      TRACE(" visit #%d: %s\n", node->id(), node->op()->mnemonic());
      VisitNode(node, info->truncation(), nullptr);
      TRACE("  ==> output ");
      PrintOutputInfo(info);
      TRACE("\n");
      if (updated) {
        for (Node* const user : node->uses()) {
          if (GetInfo(user)->visited()) {
            GetInfo(user)->set_queued();
            queue_.push(user);
          }
        }
      }
    }
  }

  void ResetNodeInfoState() {
    // Clean up for the next phase.
    for (NodeInfo& info : info_) {
      info.reset_state();
    }
  }

  Type* TypeOf(Node* node) {
    Type* type = GetInfo(node)->feedback_type();
    return type == nullptr ? NodeProperties::GetType(node) : type;
  }

  Type* FeedbackTypeOf(Node* node) {
    Type* type = GetInfo(node)->feedback_type();
    return type == nullptr ? Type::None() : type;
  }

  Type* TypePhi(Node* node) {
    int arity = node->op()->ValueInputCount();
    Type* type = FeedbackTypeOf(node->InputAt(0));
    for (int i = 1; i < arity; ++i) {
      type = op_typer_.Merge(type, FeedbackTypeOf(node->InputAt(i)));
    }
    return type;
  }

  Type* TypeSelect(Node* node) {
    return op_typer_.Merge(FeedbackTypeOf(node->InputAt(1)),
                           FeedbackTypeOf(node->InputAt(2)));
  }

  bool UpdateFeedbackType(Node* node) {
    if (node->op()->ValueOutputCount() == 0) return false;

    NodeInfo* info = GetInfo(node);
    Type* type = info->feedback_type();
    Type* new_type = type;

    // For any non-phi node just wait until we get all inputs typed. We only
    // allow untyped inputs for phi nodes because phis are the only places
    // where cycles need to be broken.
    if (node->opcode() != IrOpcode::kPhi) {
      for (int i = 0; i < node->op()->ValueInputCount(); i++) {
        if (GetInfo(node->InputAt(i))->feedback_type() == nullptr) {
          return false;
        }
      }
    }

    switch (node->opcode()) {
#define DECLARE_CASE(Name)                                       \
  case IrOpcode::k##Name: {                                      \
    new_type = op_typer_.Name(FeedbackTypeOf(node->InputAt(0)),  \
                              FeedbackTypeOf(node->InputAt(1))); \
    break;                                                       \
  }
      SIMPLIFIED_NUMBER_BINOP_LIST(DECLARE_CASE)
#undef DECLARE_CASE

#define DECLARE_CASE(Name)                                                \
  case IrOpcode::k##Name: {                                               \
    new_type =                                                            \
        Type::Intersect(op_typer_.Name(FeedbackTypeOf(node->InputAt(0)),  \
                                       FeedbackTypeOf(node->InputAt(1))), \
                        info->restriction_type(), graph_zone());          \
    break;                                                                \
  }
      SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_CASE)
#undef DECLARE_CASE

#define DECLARE_CASE(Name)                                       \
  case IrOpcode::k##Name: {                                      \
    new_type = op_typer_.Name(FeedbackTypeOf(node->InputAt(0))); \
    break;                                                       \
  }
      SIMPLIFIED_NUMBER_UNOP_LIST(DECLARE_CASE)
#undef DECLARE_CASE

      case IrOpcode::kPlainPrimitiveToNumber:
        new_type = op_typer_.ToNumber(FeedbackTypeOf(node->InputAt(0)));
        break;

      case IrOpcode::kPhi: {
        new_type = TypePhi(node);
        if (type != nullptr) {
          new_type = Weaken(node, type, new_type);
        }
        break;
      }

      case IrOpcode::kTypeGuard: {
        new_type = op_typer_.TypeTypeGuard(node->op(),
                                           FeedbackTypeOf(node->InputAt(0)));
        break;
      }

      case IrOpcode::kSelect: {
        new_type = TypeSelect(node);
        break;
      }

      default:
        // Shortcut for operations that we do not handle.
        if (type == nullptr) {
          GetInfo(node)->set_feedback_type(NodeProperties::GetType(node));
          return true;
        }
        return false;
    }
    // We need to guarantee that the feedback type is a subtype of the upper
    // bound. Naively that should hold, but weakening can actually produce
    // a bigger type if we are unlucky with ordering of phi typing. To be
    // really sure, just intersect the upper bound with the feedback type.
    new_type = Type::Intersect(GetUpperBound(node), new_type, graph_zone());

    if (type != nullptr && new_type->Is(type)) return false;
    GetInfo(node)->set_feedback_type(new_type);
    if (FLAG_trace_representation) {
      PrintNodeFeedbackType(node);
    }
    return true;
  }

  void PrintNodeFeedbackType(Node* n) {
    OFStream os(stdout);
    os << "#" << n->id() << ":" << *n->op() << "(";
    int j = 0;
    for (Node* const i : n->inputs()) {
      if (j++ > 0) os << ", ";
      os << "#" << i->id() << ":" << i->op()->mnemonic();
    }
    os << ")";
    if (NodeProperties::IsTyped(n)) {
      os << "  [Static type: ";
      Type* static_type = NodeProperties::GetType(n);
      static_type->PrintTo(os);
      Type* feedback_type = GetInfo(n)->feedback_type();
      if (feedback_type != nullptr && feedback_type != static_type) {
        os << ", Feedback type: ";
        feedback_type->PrintTo(os);
      }
      os << "]";
    }
    os << std::endl;
  }

  Type* Weaken(Node* node, Type* previous_type, Type* current_type) {
    // If the types have nothing to do with integers, return the types.
    Type* const integer = type_cache_.kInteger;
    if (!previous_type->Maybe(integer)) {
      return current_type;
    }
    DCHECK(current_type->Maybe(integer));

    Type* current_integer =
        Type::Intersect(current_type, integer, graph_zone());
    Type* previous_integer =
        Type::Intersect(previous_type, integer, graph_zone());

    // Once we start weakening a node, we should always weaken.
    if (!GetInfo(node)->weakened()) {
      // Only weaken if there is range involved; we should converge quickly
      // for all other types (the exception is a union of many constants,
      // but we currently do not increase the number of constants in unions).
      Type* previous = previous_integer->GetRange();
      Type* current = current_integer->GetRange();
      if (current == nullptr || previous == nullptr) {
        return current_type;
      }
      // Range is involved => we are weakening.
      GetInfo(node)->set_weakened();
    }

    return Type::Union(current_type,
                       op_typer_.WeakenRange(previous_integer, current_integer),
                       graph_zone());
  }

  // Backward propagation of truncations.
  void RunTruncationPropagationPhase() {
    // Run propagation phase to a fixpoint.
    TRACE("--{Propagation phase}--\n");
    phase_ = PROPAGATE;
    EnqueueInitial(jsgraph_->graph()->end());
    // Process nodes from the queue until it is empty.
    while (!queue_.empty()) {
      Node* node = queue_.front();
      NodeInfo* info = GetInfo(node);
      queue_.pop();
      info->set_visited();
      TRACE(" visit #%d: %s (trunc: %s)\n", node->id(), node->op()->mnemonic(),
            info->truncation().description());
      VisitNode(node, info->truncation(), nullptr);
    }
  }

  void Run(SimplifiedLowering* lowering) {
    RunTruncationPropagationPhase();

    RunTypePropagationPhase();

    // Run lowering and change insertion phase.
    TRACE("--{Simplified lowering phase}--\n");
    phase_ = LOWER;
    // Process nodes from the collected {nodes_} vector.
    for (NodeVector::iterator i = nodes_.begin(); i != nodes_.end(); ++i) {
      Node* node = *i;
      NodeInfo* info = GetInfo(node);
      TRACE(" visit #%d: %s\n", node->id(), node->op()->mnemonic());
      // Reuse {VisitNode()} so the representation rules are in one place.
      SourcePositionTable::Scope scope(
          source_positions_, source_positions_->GetSourcePosition(node));
      VisitNode(node, info->truncation(), lowering);
    }

    // Perform the final replacements.
    for (NodeVector::iterator i = replacements_.begin();
         i != replacements_.end(); ++i) {
      Node* node = *i;
      Node* replacement = *(++i);
      node->ReplaceUses(replacement);
      node->Kill();
      // We also need to replace the node in the rest of the vector.
      for (NodeVector::iterator j = i + 1; j != replacements_.end(); ++j) {
        ++j;
        if (*j == node) *j = replacement;
      }
    }
  }

  void EnqueueInitial(Node* node) {
    NodeInfo* info = GetInfo(node);
    info->set_queued();
    nodes_.push_back(node);
    queue_.push(node);
  }

  // Enqueue {use_node}'s {index} input if the {use} contains new information
  // for that input node. Add the input to {nodes_} if this is the first time
  // it's been visited.
  void EnqueueInput(Node* use_node, int index,
                    UseInfo use_info = UseInfo::None()) {
    Node* node = use_node->InputAt(index);
    if (phase_ != PROPAGATE) return;
    NodeInfo* info = GetInfo(node);
#ifdef DEBUG
    // Check monotonicity of input requirements.
    node_input_use_infos_[use_node->id()].SetAndCheckInput(use_node, index,
                                                           use_info);
#endif  // DEBUG
    if (info->unvisited()) {
      // First visit of this node.
      info->set_queued();
      nodes_.push_back(node);
      queue_.push(node);
      TRACE("  initial #%i: ", node->id());
      info->AddUse(use_info);
      PrintTruncation(info->truncation());
      return;
    }
    TRACE("   queue #%i?: ", node->id());
    PrintTruncation(info->truncation());
    if (info->AddUse(use_info)) {
      // New usage information for the node is available.
      if (!info->queued()) {
        queue_.push(node);
        info->set_queued();
        TRACE("   added: ");
      } else {
        TRACE(" inqueue: ");
      }
      PrintTruncation(info->truncation());
    }
  }

  bool lower() const { return phase_ == LOWER; }
  bool retype() const { return phase_ == RETYPE; }
  bool propagate() const { return phase_ == PROPAGATE; }

  void SetOutput(Node* node, MachineRepresentation representation,
                 Type* restriction_type = Type::Any()) {
    NodeInfo* const info = GetInfo(node);
    switch (phase_) {
      case PROPAGATE:
        info->set_restriction_type(restriction_type);
        break;
      case RETYPE:
        DCHECK(info->restriction_type()->Is(restriction_type));
        DCHECK(restriction_type->Is(info->restriction_type()));
        info->set_output(representation);
        break;
      case LOWER:
        DCHECK_EQ(info->representation(), representation);
        DCHECK(info->restriction_type()->Is(restriction_type));
        DCHECK(restriction_type->Is(info->restriction_type()));
        break;
    }
  }

  Type* GetUpperBound(Node* node) { return NodeProperties::GetType(node); }

  bool InputCannotBe(Node* node, Type* type) {
    DCHECK_EQ(1, node->op()->ValueInputCount());
    return !GetUpperBound(node->InputAt(0))->Maybe(type);
  }

  bool InputIs(Node* node, Type* type) {
    DCHECK_EQ(1, node->op()->ValueInputCount());
    return GetUpperBound(node->InputAt(0))->Is(type);
  }

  bool BothInputsAreSigned32(Node* node) {
    return BothInputsAre(node, Type::Signed32());
  }

  bool BothInputsAreUnsigned32(Node* node) {
    return BothInputsAre(node, Type::Unsigned32());
  }

  bool BothInputsAre(Node* node, Type* type) {
    DCHECK_EQ(2, node->op()->ValueInputCount());
    return GetUpperBound(node->InputAt(0))->Is(type) &&
           GetUpperBound(node->InputAt(1))->Is(type);
  }

  bool OneInputCannotBe(Node* node, Type* type) {
    DCHECK_EQ(2, node->op()->ValueInputCount());
    return !GetUpperBound(node->InputAt(0))->Maybe(type) ||
           !GetUpperBound(node->InputAt(1))->Maybe(type);
  }

  void ConvertInput(Node* node, int index, UseInfo use) {
    Node* input = node->InputAt(index);
    // In the change phase, insert a change before the use if necessary.
    if (use.representation() == MachineRepresentation::kNone)
      return;  // No input requirement on the use.
    DCHECK_NOT_NULL(input);
    NodeInfo* input_info = GetInfo(input);
    MachineRepresentation input_rep = input_info->representation();
    if (input_rep != use.representation() ||
        use.type_check() != TypeCheckKind::kNone) {
      // Output representation doesn't match usage.
      TRACE("  change: #%d:%s(@%d #%d:%s) ", node->id(), node->op()->mnemonic(),
            index, input->id(), input->op()->mnemonic());
      TRACE(" from ");
      PrintOutputInfo(input_info);
      TRACE(" to ");
      PrintUseInfo(use);
      TRACE("\n");
      Node* n = changer_->GetRepresentationFor(
          input, input_info->representation(), TypeOf(input), node, use);
      node->ReplaceInput(index, n);
    }
  }

  void ProcessInput(Node* node, int index, UseInfo use) {
    switch (phase_) {
      case PROPAGATE:
        EnqueueInput(node, index, use);
        break;
      case RETYPE:
        break;
      case LOWER:
        ConvertInput(node, index, use);
        break;
    }
  }

  void ProcessRemainingInputs(Node* node, int index) {
    DCHECK_GE(index, NodeProperties::PastValueIndex(node));
    DCHECK_GE(index, NodeProperties::PastContextIndex(node));
    for (int i = std::max(index, NodeProperties::FirstEffectIndex(node));
         i < NodeProperties::PastEffectIndex(node); ++i) {
      EnqueueInput(node, i);  // Effect inputs: just visit
    }
    for (int i = std::max(index, NodeProperties::FirstControlIndex(node));
         i < NodeProperties::PastControlIndex(node); ++i) {
      EnqueueInput(node, i);  // Control inputs: just visit
    }
  }

  // The default, most general visitation case. For {node}, process all value,
  // context, frame state, effect, and control inputs, assuming that value
  // inputs should have {kRepTagged} representation and can observe all output
  // values {kTypeAny}.
  void VisitInputs(Node* node) {
    int tagged_count = node->op()->ValueInputCount() +
                       OperatorProperties::GetContextInputCount(node->op()) +
                       OperatorProperties::GetFrameStateInputCount(node->op());
    // Visit value, context and frame state inputs as tagged.
    for (int i = 0; i < tagged_count; i++) {
      ProcessInput(node, i, UseInfo::AnyTagged());
    }
    // Only enqueue other inputs (effects, control).
    for (int i = tagged_count; i < node->InputCount(); i++) {
      EnqueueInput(node, i);
    }
  }

  void VisitReturn(Node* node) {
    int tagged_limit = node->op()->ValueInputCount() +
                       OperatorProperties::GetContextInputCount(node->op()) +
                       OperatorProperties::GetFrameStateInputCount(node->op());
    // Visit integer slot count to pop
    ProcessInput(node, 0, UseInfo::TruncatingWord32());

    // Visit value, context and frame state inputs as tagged.
    for (int i = 1; i < tagged_limit; i++) {
      ProcessInput(node, i, UseInfo::AnyTagged());
    }
    // Only enqueue other inputs (effects, control).
    for (int i = tagged_limit; i < node->InputCount(); i++) {
      EnqueueInput(node, i);
    }
  }

  // Helper for an unused node.
  void VisitUnused(Node* node) {
    int value_count = node->op()->ValueInputCount() +
                      OperatorProperties::GetContextInputCount(node->op()) +
                      OperatorProperties::GetFrameStateInputCount(node->op());
    for (int i = 0; i < value_count; i++) {
      ProcessInput(node, i, UseInfo::None());
    }
    ProcessRemainingInputs(node, value_count);
    if (lower()) Kill(node);
  }

  // Helper for binops of the R x L -> O variety.
  void VisitBinop(Node* node, UseInfo left_use, UseInfo right_use,
                  MachineRepresentation output,
                  Type* restriction_type = Type::Any()) {
    DCHECK_EQ(2, node->op()->ValueInputCount());
    ProcessInput(node, 0, left_use);
    ProcessInput(node, 1, right_use);
    for (int i = 2; i < node->InputCount(); i++) {
      EnqueueInput(node, i);
    }
    SetOutput(node, output, restriction_type);
  }

  // Helper for binops of the I x I -> O variety.
  void VisitBinop(Node* node, UseInfo input_use, MachineRepresentation output,
                  Type* restriction_type = Type::Any()) {
    VisitBinop(node, input_use, input_use, output, restriction_type);
  }

  void VisitSpeculativeInt32Binop(Node* node) {
    DCHECK_EQ(2, node->op()->ValueInputCount());
    if (BothInputsAre(node, Type::NumberOrOddball())) {
      return VisitBinop(node, UseInfo::TruncatingWord32(),
                        MachineRepresentation::kWord32);
    }
    NumberOperationHint hint = NumberOperationHintOf(node->op());
    return VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
                      MachineRepresentation::kWord32);
  }

  // Helper for unops of the I -> O variety.
  void VisitUnop(Node* node, UseInfo input_use, MachineRepresentation output) {
    DCHECK_EQ(1, node->op()->ValueInputCount());
    ProcessInput(node, 0, input_use);
    ProcessRemainingInputs(node, 1);
    SetOutput(node, output);
  }

  // Helper for leaf nodes.
  void VisitLeaf(Node* node, MachineRepresentation output) {
    DCHECK_EQ(0, node->InputCount());
    SetOutput(node, output);
  }

  // Helpers for specific types of binops.
  void VisitFloat64Binop(Node* node) {
    VisitBinop(node, UseInfo::TruncatingFloat64(),
               MachineRepresentation::kFloat64);
  }
  void VisitWord32TruncatingBinop(Node* node) {
    VisitBinop(node, UseInfo::TruncatingWord32(),
               MachineRepresentation::kWord32);
  }

  // Infer representation for phi-like nodes.
  // The {node} parameter is only used to decide on the int64 representation.
  // Once the type system supports an external pointer type, the {node}
  // parameter can be removed.
  MachineRepresentation GetOutputInfoForPhi(Node* node, Type* type,
                                            Truncation use) {
    // Compute the representation.
    if (type->Is(Type::None())) {
      return MachineRepresentation::kNone;
    } else if (type->Is(Type::Signed32()) || type->Is(Type::Unsigned32())) {
      return MachineRepresentation::kWord32;
    } else if (type->Is(Type::NumberOrOddball()) && use.IsUsedAsWord32()) {
      return MachineRepresentation::kWord32;
    } else if (type->Is(Type::Boolean())) {
      return MachineRepresentation::kBit;
    } else if (type->Is(Type::NumberOrOddball()) && use.IsUsedAsFloat64()) {
      return MachineRepresentation::kFloat64;
    } else if (type->Is(
                   Type::Union(Type::SignedSmall(), Type::NaN(), zone()))) {
      // TODO(turbofan): For Phis that return either NaN or some Smi, it's
      // beneficial to not go all the way to double, unless the uses are
      // double uses. For tagging that just means some potentially expensive
      // allocation code; we might want to do the same for -0 as well?
      return MachineRepresentation::kTagged;
    } else if (type->Is(Type::Number())) {
      return MachineRepresentation::kFloat64;
    } else if (type->Is(Type::ExternalPointer())) {
      return MachineType::PointerRepresentation();
    }
    return MachineRepresentation::kTagged;
  }

  // Helper for handling selects.
  void VisitSelect(Node* node, Truncation truncation,
                   SimplifiedLowering* lowering) {
    ProcessInput(node, 0, UseInfo::Bool());

    MachineRepresentation output =
        GetOutputInfoForPhi(node, TypeOf(node), truncation);
    SetOutput(node, output);

    if (lower()) {
      // Update the select operator.
      SelectParameters p = SelectParametersOf(node->op());
      if (output != p.representation()) {
        NodeProperties::ChangeOp(node,
                                 lowering->common()->Select(output, p.hint()));
      }
    }
    // Convert inputs to the output representation of this phi, pass the
    // truncation truncation along.
    UseInfo input_use(output, truncation);
    ProcessInput(node, 1, input_use);
    ProcessInput(node, 2, input_use);
  }

  // Helper for handling phis.
  void VisitPhi(Node* node, Truncation truncation,
                SimplifiedLowering* lowering) {
    MachineRepresentation output =
        GetOutputInfoForPhi(node, TypeOf(node), truncation);
    // Only set the output representation if not running with type
    // feedback. (Feedback typing will set the representation.)
    SetOutput(node, output);

    int values = node->op()->ValueInputCount();
    if (lower()) {
      // Update the phi operator.
      if (output != PhiRepresentationOf(node->op())) {
        NodeProperties::ChangeOp(node, lowering->common()->Phi(output, values));
      }
    }

    // Convert inputs to the output representation of this phi, pass the
    // truncation along.
    UseInfo input_use(output, truncation);
    for (int i = 0; i < node->InputCount(); i++) {
      ProcessInput(node, i, i < values ? input_use : UseInfo::None());
    }
  }

  void VisitObjectIs(Node* node, Type* type, SimplifiedLowering* lowering) {
    Type* const input_type = TypeOf(node->InputAt(0));
    if (input_type->Is(type)) {
      VisitUnop(node, UseInfo::None(), MachineRepresentation::kBit);
      if (lower()) {
        DeferReplacement(node, lowering->jsgraph()->Int32Constant(1));
      }
    } else {
      VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
      if (lower() && !input_type->Maybe(type)) {
        DeferReplacement(node, lowering->jsgraph()->Int32Constant(0));
      }
    }
  }

  void VisitCall(Node* node, SimplifiedLowering* lowering) {
    const CallDescriptor* desc = CallDescriptorOf(node->op());
    int params = static_cast<int>(desc->ParameterCount());
    int value_input_count = node->op()->ValueInputCount();
    // Propagate representation information from call descriptor.
    for (int i = 0; i < value_input_count; i++) {
      if (i == 0) {
        // The target of the call.
        ProcessInput(node, i, UseInfo::Any());
      } else if ((i - 1) < params) {
        ProcessInput(node, i, TruncatingUseInfoFromRepresentation(
                                  desc->GetInputType(i).representation()));
      } else {
        ProcessInput(node, i, UseInfo::AnyTagged());
      }
    }
    ProcessRemainingInputs(node, value_input_count);

    if (desc->ReturnCount() > 0) {
      SetOutput(node, desc->GetReturnType(0).representation());
    } else {
      SetOutput(node, MachineRepresentation::kTagged);
    }
  }

  MachineSemantic DeoptValueSemanticOf(Type* type) {
    // We only need signedness to do deopt correctly.
    if (type->Is(Type::Signed32())) {
      return MachineSemantic::kInt32;
    } else if (type->Is(Type::Unsigned32())) {
      return MachineSemantic::kUint32;
    } else {
      return MachineSemantic::kAny;
    }
  }

  void VisitStateValues(Node* node) {
    if (propagate()) {
      for (int i = 0; i < node->InputCount(); i++) {
        EnqueueInput(node, i, UseInfo::Any());
      }
    } else if (lower()) {
      Zone* zone = jsgraph_->zone();
      ZoneVector<MachineType>* types =
          new (zone->New(sizeof(ZoneVector<MachineType>)))
              ZoneVector<MachineType>(node->InputCount(), zone);
      for (int i = 0; i < node->InputCount(); i++) {
        Node* input = node->InputAt(i);
        NodeInfo* input_info = GetInfo(input);
        Type* input_type = TypeOf(input);
        MachineRepresentation rep = input_type->IsInhabited()
                                        ? input_info->representation()
                                        : MachineRepresentation::kNone;
        MachineType machine_type(rep, DeoptValueSemanticOf(input_type));
        DCHECK(machine_type.representation() !=
                   MachineRepresentation::kWord32 ||
               machine_type.semantic() == MachineSemantic::kInt32 ||
               machine_type.semantic() == MachineSemantic::kUint32);
        (*types)[i] = machine_type;
      }
      NodeProperties::ChangeOp(node,
                               jsgraph_->common()->TypedStateValues(types));
    }
    SetOutput(node, MachineRepresentation::kTagged);
  }

  void VisitObjectState(Node* node) {
    if (propagate()) {
      for (int i = 0; i < node->InputCount(); i++) {
        Node* input = node->InputAt(i);
        Type* input_type = TypeOf(input);
        // TODO(turbofan): Special treatment for ExternalPointer here,
        // to avoid incompatible truncations. We really need a story
        // for the JSFunction::entry field.
        UseInfo use_info = input_type->Is(Type::ExternalPointer())
                               ? UseInfo::PointerInt()
                               : UseInfo::Any();
        EnqueueInput(node, i, use_info);
      }
    } else if (lower()) {
      Zone* zone = jsgraph_->zone();
      ZoneVector<MachineType>* types =
          new (zone->New(sizeof(ZoneVector<MachineType>)))
              ZoneVector<MachineType>(node->InputCount(), zone);
      for (int i = 0; i < node->InputCount(); i++) {
        Node* input = node->InputAt(i);
        NodeInfo* input_info = GetInfo(input);
        Type* input_type = TypeOf(input);
        // TODO(turbofan): Special treatment for ExternalPointer here,
        // to avoid incompatible truncations. We really need a story
        // for the JSFunction::entry field.
        if (input_type->Is(Type::ExternalPointer())) {
          (*types)[i] = MachineType::Pointer();
        } else {
          MachineRepresentation rep = input_type->IsInhabited()
                                          ? input_info->representation()
                                          : MachineRepresentation::kNone;
          MachineType machine_type(rep, DeoptValueSemanticOf(input_type));
          DCHECK(machine_type.representation() !=
                     MachineRepresentation::kWord32 ||
                 machine_type.semantic() == MachineSemantic::kInt32 ||
                 machine_type.semantic() == MachineSemantic::kUint32);
          DCHECK(machine_type.representation() != MachineRepresentation::kBit ||
                 input_type->Is(Type::Boolean()));
          (*types)[i] = machine_type;
        }
      }
      NodeProperties::ChangeOp(node,
                               jsgraph_->common()->TypedObjectState(types));
    }
    SetOutput(node, MachineRepresentation::kTagged);
  }

  const Operator* Int32Op(Node* node) {
    return changer_->Int32OperatorFor(node->opcode());
  }

  const Operator* Int32OverflowOp(Node* node) {
    return changer_->Int32OverflowOperatorFor(node->opcode());
  }

  const Operator* Uint32Op(Node* node) {
    return changer_->Uint32OperatorFor(node->opcode());
  }

  const Operator* Uint32OverflowOp(Node* node) {
    return changer_->Uint32OverflowOperatorFor(node->opcode());
  }

  const Operator* Float64Op(Node* node) {
    return changer_->Float64OperatorFor(node->opcode());
  }

  WriteBarrierKind WriteBarrierKindFor(
      BaseTaggedness base_taggedness,
      MachineRepresentation field_representation, Type* field_type,
      MachineRepresentation value_representation, Node* value) {
    if (base_taggedness == kTaggedBase &&
        CanBeTaggedPointer(field_representation)) {
      Type* value_type = NodeProperties::GetType(value);
      if (field_representation == MachineRepresentation::kTaggedSigned ||
          value_representation == MachineRepresentation::kTaggedSigned) {
        // Write barriers are only for stores of heap objects.
        return kNoWriteBarrier;
      }
      if (field_type->Is(Type::BooleanOrNullOrUndefined()) ||
          value_type->Is(Type::BooleanOrNullOrUndefined())) {
        // Write barriers are not necessary when storing true, false, null or
        // undefined, because these special oddballs are always in the root set.
        return kNoWriteBarrier;
      }
      if (value_type->IsHeapConstant()) {
        Handle<HeapObject> value_object = value_type->AsHeapConstant()->Value();
        RootIndexMap root_index_map(jsgraph_->isolate());
        int root_index = root_index_map.Lookup(*value_object);
        if (root_index != RootIndexMap::kInvalidRootIndex &&
            jsgraph_->isolate()->heap()->RootIsImmortalImmovable(root_index)) {
          // Write barriers are unnecessary for immortal immovable roots.
          return kNoWriteBarrier;
        }
        if (value_object->IsMap()) {
          // Write barriers for storing maps are cheaper.
          return kMapWriteBarrier;
        }
      }
      if (field_representation == MachineRepresentation::kTaggedPointer ||
          value_representation == MachineRepresentation::kTaggedPointer) {
        // Write barriers for heap objects are cheaper.
        return kPointerWriteBarrier;
      }
      NumberMatcher m(value);
      if (m.HasValue()) {
        if (IsSmiDouble(m.Value())) {
          // Storing a smi doesn't need a write barrier.
          return kNoWriteBarrier;
        }
        // The NumberConstant will be represented as HeapNumber.
        return kPointerWriteBarrier;
      }
      return kFullWriteBarrier;
    }
    return kNoWriteBarrier;
  }

  WriteBarrierKind WriteBarrierKindFor(
      BaseTaggedness base_taggedness,
      MachineRepresentation field_representation, int field_offset,
      Type* field_type, MachineRepresentation value_representation,
      Node* value) {
    if (base_taggedness == kTaggedBase &&
        field_offset == HeapObject::kMapOffset) {
      return kMapWriteBarrier;
    }
    return WriteBarrierKindFor(base_taggedness, field_representation,
                               field_type, value_representation, value);
  }

  Graph* graph() const { return jsgraph_->graph(); }
  CommonOperatorBuilder* common() const { return jsgraph_->common(); }
  SimplifiedOperatorBuilder* simplified() const {
    return jsgraph_->simplified();
  }

  void LowerToCheckedInt32Mul(Node* node, Truncation truncation,
                              Type* input0_type, Type* input1_type) {
    // If one of the inputs is positive and/or truncation is being applied,
    // there is no need to return -0.
    CheckForMinusZeroMode mz_mode =
        truncation.IsUsedAsWord32() ||
                (input0_type->Is(Type::OrderedNumber()) &&
                 input0_type->Min() > 0) ||
                (input1_type->Is(Type::OrderedNumber()) &&
                 input1_type->Min() > 0)
            ? CheckForMinusZeroMode::kDontCheckForMinusZero
            : CheckForMinusZeroMode::kCheckForMinusZero;

    NodeProperties::ChangeOp(node, simplified()->CheckedInt32Mul(mz_mode));
  }

  void ChangeToInt32OverflowOp(Node* node) {
    NodeProperties::ChangeOp(node, Int32OverflowOp(node));
  }

  void ChangeToUint32OverflowOp(Node* node) {
    NodeProperties::ChangeOp(node, Uint32OverflowOp(node));
  }

  void VisitSpeculativeAdditiveOp(Node* node, Truncation truncation,
                                  SimplifiedLowering* lowering) {
    // ToNumber(x) can throw if x is either a Receiver or a Symbol, so we can
    // only eliminate an unused speculative number operation if we know that
    // the inputs are PlainPrimitive, which excludes everything that's might
    // have side effects or throws during a ToNumber conversion.
    if (BothInputsAre(node, Type::PlainPrimitive())) {
      if (truncation.IsUnused()) return VisitUnused(node);
    }
    if (BothInputsAre(node, type_cache_.kAdditiveSafeIntegerOrMinusZero) &&
        (GetUpperBound(node)->Is(Type::Signed32()) ||
         GetUpperBound(node)->Is(Type::Unsigned32()) ||
         truncation.IsUsedAsWord32())) {
      // => Int32Add/Sub
      VisitWord32TruncatingBinop(node);
      if (lower()) ChangeToPureOp(node, Int32Op(node));
      return;
    }

    // Try to use type feedback.
    NumberOperationHint hint = NumberOperationHintOf(node->op());

    // Handle the case when no int32 checks on inputs are necessary
    // (but an overflow check is needed on the output).
    if (BothInputsAre(node, Type::Signed32()) ||
        (BothInputsAre(node, Type::Signed32OrMinusZero()) &&
         NodeProperties::GetType(node)->Is(type_cache_.kSafeInteger))) {
      // If both the inputs the feedback are int32, use the overflow op.
      if (hint == NumberOperationHint::kSignedSmall ||
          hint == NumberOperationHint::kSigned32) {
        VisitBinop(node, UseInfo::TruncatingWord32(),
                   MachineRepresentation::kWord32, Type::Signed32());
        if (lower()) ChangeToInt32OverflowOp(node);
        return;
      }
    }

    if (hint == NumberOperationHint::kSignedSmall ||
        hint == NumberOperationHint::kSigned32) {
      UseInfo left_use = CheckedUseInfoAsWord32FromHint(hint);
      // For CheckedInt32Add and CheckedInt32Sub, we don't need to do
      // a minus zero check for the right hand side, since we already
      // know that the left hand side is a proper Signed32 value,
      // potentially guarded by a check.
      UseInfo right_use = CheckedUseInfoAsWord32FromHint(
          hint, CheckForMinusZeroMode::kDontCheckForMinusZero);
      VisitBinop(node, left_use, right_use, MachineRepresentation::kWord32,
                 Type::Signed32());
      if (lower()) ChangeToInt32OverflowOp(node);
      return;
    }

    // default case => Float64Add/Sub
    VisitBinop(node, UseInfo::CheckedNumberOrOddballAsFloat64(),
               MachineRepresentation::kFloat64, Type::Number());
    if (lower()) {
      ChangeToPureOp(node, Float64Op(node));
    }
    return;
  }

  void VisitSpeculativeNumberModulus(Node* node, Truncation truncation,
                                     SimplifiedLowering* lowering) {
    // ToNumber(x) can throw if x is either a Receiver or a Symbol, so we
    // can only eliminate an unused speculative number operation if we know
    // that the inputs are PlainPrimitive, which excludes everything that's
    // might have side effects or throws during a ToNumber conversion.
    if (BothInputsAre(node, Type::PlainPrimitive())) {
      if (truncation.IsUnused()) return VisitUnused(node);
    }
    if (BothInputsAre(node, Type::Unsigned32OrMinusZeroOrNaN()) &&
        (truncation.IsUsedAsWord32() ||
         NodeProperties::GetType(node)->Is(Type::Unsigned32()))) {
      // => unsigned Uint32Mod
      VisitWord32TruncatingBinop(node);
      if (lower()) DeferReplacement(node, lowering->Uint32Mod(node));
      return;
    }
    if (BothInputsAre(node, Type::Signed32OrMinusZeroOrNaN()) &&
        (truncation.IsUsedAsWord32() ||
         NodeProperties::GetType(node)->Is(Type::Signed32()))) {
      // => signed Int32Mod
      VisitWord32TruncatingBinop(node);
      if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
      return;
    }

    // Try to use type feedback.
    NumberOperationHint hint = NumberOperationHintOf(node->op());

    // Handle the case when no uint32 checks on inputs are necessary
    // (but an overflow check is needed on the output).
    if (BothInputsAreUnsigned32(node)) {
      if (hint == NumberOperationHint::kSignedSmall ||
          hint == NumberOperationHint::kSigned32) {
        VisitBinop(node, UseInfo::TruncatingWord32(),
                   MachineRepresentation::kWord32, Type::Unsigned32());
        if (lower()) ChangeToUint32OverflowOp(node);
        return;
      }
    }

    // Handle the case when no int32 checks on inputs are necessary
    // (but an overflow check is needed on the output).
    if (BothInputsAre(node, Type::Signed32())) {
      // If both the inputs the feedback are int32, use the overflow op.
      if (hint == NumberOperationHint::kSignedSmall ||
          hint == NumberOperationHint::kSigned32) {
        VisitBinop(node, UseInfo::TruncatingWord32(),
                   MachineRepresentation::kWord32, Type::Signed32());
        if (lower()) ChangeToInt32OverflowOp(node);
        return;
      }
    }

    if (hint == NumberOperationHint::kSignedSmall ||
        hint == NumberOperationHint::kSigned32) {
      // If the result is truncated, we only need to check the inputs.
      if (truncation.IsUsedAsWord32()) {
        VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
                   MachineRepresentation::kWord32);
        if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
      } else if (BothInputsAre(node, Type::Unsigned32OrMinusZeroOrNaN())) {
        VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
                   MachineRepresentation::kWord32, Type::Unsigned32());
        if (lower()) DeferReplacement(node, lowering->Uint32Mod(node));
      } else {
        VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
                   MachineRepresentation::kWord32, Type::Signed32());
        if (lower()) ChangeToInt32OverflowOp(node);
      }
      return;
    }

    if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) &&
        TypeOf(node->InputAt(1))->Is(Type::Unsigned32()) &&
        (truncation.IsUsedAsWord32() ||
         NodeProperties::GetType(node)->Is(Type::Unsigned32()))) {
      // We can only promise Float64 truncation here, as the decision is
      // based on the feedback types of the inputs.
      VisitBinop(node,
                 UseInfo(MachineRepresentation::kWord32, Truncation::Float64()),
                 MachineRepresentation::kWord32, Type::Number());
      if (lower()) DeferReplacement(node, lowering->Uint32Mod(node));
      return;
    }
    if (TypeOf(node->InputAt(0))->Is(Type::Signed32()) &&
        TypeOf(node->InputAt(1))->Is(Type::Signed32()) &&
        (truncation.IsUsedAsWord32() ||
         NodeProperties::GetType(node)->Is(Type::Signed32()))) {
      // We can only promise Float64 truncation here, as the decision is
      // based on the feedback types of the inputs.
      VisitBinop(node,
                 UseInfo(MachineRepresentation::kWord32, Truncation::Float64()),
                 MachineRepresentation::kWord32, Type::Number());
      if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
      return;
    }
    // default case => Float64Mod
    VisitBinop(node, UseInfo::CheckedNumberOrOddballAsFloat64(),
               MachineRepresentation::kFloat64, Type::Number());
    if (lower()) ChangeToPureOp(node, Float64Op(node));
    return;
  }

  void VisitOsrGuard(Node* node) {
    VisitInputs(node);

    // Insert a dynamic check for the OSR value type if necessary.
    switch (OsrGuardTypeOf(node->op())) {
      case OsrGuardType::kUninitialized:
        // At this point, we should always have a type for the OsrValue.
        UNREACHABLE();
        break;
      case OsrGuardType::kSignedSmall:
        if (lower()) {
          NodeProperties::ChangeOp(node,
                                   simplified()->CheckedTaggedToTaggedSigned());
        }
        return SetOutput(node, MachineRepresentation::kTaggedSigned);
      case OsrGuardType::kAny:  // Nothing to check.
        if (lower()) {
          DeferReplacement(node, node->InputAt(0));
        }
        return SetOutput(node, MachineRepresentation::kTagged);
    }
    UNREACHABLE();
  }

  // Dispatching routine for visiting the node {node} with the usage {use}.
  // Depending on the operator, propagate new usage info to the inputs.
  void VisitNode(Node* node, Truncation truncation,
                 SimplifiedLowering* lowering) {
    // Unconditionally eliminate unused pure nodes (only relevant if there's
    // a pure operation in between two effectful ones, where the last one
    // is unused).
    // Note: We must not do this for constants, as they are cached and we
    // would thus kill the cached {node} during lowering (i.e. replace all
    // uses with Dead), but at that point some node lowering might have
    // already taken the constant {node} from the cache (while it was in
    // a sane state still) and we would afterwards replace that use with
    // Dead as well.
    if (node->op()->ValueInputCount() > 0 &&
        node->op()->HasProperty(Operator::kPure)) {
      if (truncation.IsUnused()) return VisitUnused(node);
    }
    switch (node->opcode()) {
      //------------------------------------------------------------------
      // Common operators.
      //------------------------------------------------------------------
      case IrOpcode::kStart:
        // We use Start as a terminator for the frame state chain, so even
        // tho Start doesn't really produce a value, we have to say Tagged
        // here, otherwise the input conversion will fail.
        return VisitLeaf(node, MachineRepresentation::kTagged);
      case IrOpcode::kParameter:
        // TODO(titzer): use representation from linkage.
        return VisitUnop(node, UseInfo::None(), MachineRepresentation::kTagged);
      case IrOpcode::kInt32Constant:
        return VisitLeaf(node, MachineRepresentation::kWord32);
      case IrOpcode::kInt64Constant:
        return VisitLeaf(node, MachineRepresentation::kWord64);
      case IrOpcode::kExternalConstant:
        return VisitLeaf(node, MachineType::PointerRepresentation());
      case IrOpcode::kNumberConstant:
        return VisitLeaf(node, MachineRepresentation::kTagged);
      case IrOpcode::kHeapConstant:
        return VisitLeaf(node, MachineRepresentation::kTaggedPointer);
      case IrOpcode::kPointerConstant: {
        VisitLeaf(node, MachineType::PointerRepresentation());
        if (lower()) {
          intptr_t const value = OpParameter<intptr_t>(node);
          DeferReplacement(node, lowering->jsgraph()->IntPtrConstant(value));
        }
        return;
      }

      case IrOpcode::kBranch:
        ProcessInput(node, 0, UseInfo::Bool());
        EnqueueInput(node, NodeProperties::FirstControlIndex(node));
        return;
      case IrOpcode::kSwitch:
        ProcessInput(node, 0, UseInfo::TruncatingWord32());
        EnqueueInput(node, NodeProperties::FirstControlIndex(node));
        return;
      case IrOpcode::kSelect:
        return VisitSelect(node, truncation, lowering);
      case IrOpcode::kPhi:
        return VisitPhi(node, truncation, lowering);
      case IrOpcode::kCall:
        return VisitCall(node, lowering);

      //------------------------------------------------------------------
      // JavaScript operators.
      //------------------------------------------------------------------
      case IrOpcode::kJSToBoolean: {
        if (truncation.IsUsedAsBool()) {
          ProcessInput(node, 0, UseInfo::Bool());
          ProcessInput(node, 1, UseInfo::None());
          SetOutput(node, MachineRepresentation::kBit);
          if (lower()) DeferReplacement(node, node->InputAt(0));
        } else {
          VisitInputs(node);
          SetOutput(node, MachineRepresentation::kTaggedPointer);
        }
        return;
      }
      case IrOpcode::kJSToNumber: {
        VisitInputs(node);
        // TODO(bmeurer): Optimize somewhat based on input type?
        if (truncation.IsUsedAsWord32()) {
          SetOutput(node, MachineRepresentation::kWord32);
          if (lower()) lowering->DoJSToNumberTruncatesToWord32(node, this);
        } else if (truncation.IsUsedAsFloat64()) {
          SetOutput(node, MachineRepresentation::kFloat64);
          if (lower()) lowering->DoJSToNumberTruncatesToFloat64(node, this);
        } else {
          SetOutput(node, MachineRepresentation::kTagged);
        }
        return;
      }

      //------------------------------------------------------------------
      // Simplified operators.
      //------------------------------------------------------------------
      case IrOpcode::kBooleanNot: {
        if (lower()) {
          NodeInfo* input_info = GetInfo(node->InputAt(0));
          if (input_info->representation() == MachineRepresentation::kBit) {
            // BooleanNot(x: kRepBit) => Word32Equal(x, #0)
            node->AppendInput(jsgraph_->zone(), jsgraph_->Int32Constant(0));
            NodeProperties::ChangeOp(node, lowering->machine()->Word32Equal());
          } else {
            DCHECK(CanBeTaggedPointer(input_info->representation()));
            // BooleanNot(x: kRepTagged) => WordEqual(x, #false)
            node->AppendInput(jsgraph_->zone(), jsgraph_->FalseConstant());
            NodeProperties::ChangeOp(node, lowering->machine()->WordEqual());
          }
        } else {
          // No input representation requirement; adapt during lowering.
          ProcessInput(node, 0, UseInfo::AnyTruncatingToBool());
          SetOutput(node, MachineRepresentation::kBit);
        }
        return;
      }
      case IrOpcode::kNumberEqual: {
        Type* const lhs_type = TypeOf(node->InputAt(0));
        Type* const rhs_type = TypeOf(node->InputAt(1));
        // Number comparisons reduce to integer comparisons for integer inputs.
        if ((lhs_type->Is(Type::Unsigned32()) &&
             rhs_type->Is(Type::Unsigned32())) ||
            (lhs_type->Is(Type::Unsigned32OrMinusZeroOrNaN()) &&
             rhs_type->Is(Type::Unsigned32OrMinusZeroOrNaN()) &&
             OneInputCannotBe(node, type_cache_.kZeroish))) {
          // => unsigned Int32Cmp
          VisitBinop(node, UseInfo::TruncatingWord32(),
                     MachineRepresentation::kBit);
          if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node));
          return;
        }
        if ((lhs_type->Is(Type::Signed32()) &&
             rhs_type->Is(Type::Signed32())) ||
            (lhs_type->Is(Type::Signed32OrMinusZeroOrNaN()) &&
             rhs_type->Is(Type::Signed32OrMinusZeroOrNaN()) &&
             OneInputCannotBe(node, type_cache_.kZeroish))) {
          // => signed Int32Cmp
          VisitBinop(node, UseInfo::TruncatingWord32(),
                     MachineRepresentation::kBit);
          if (lower()) NodeProperties::ChangeOp(node, Int32Op(node));
          return;
        }
        // => Float64Cmp
        VisitBinop(node, UseInfo::TruncatingFloat64(),
                   MachineRepresentation::kBit);
        if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
        return;
      }
      case IrOpcode::kNumberLessThan:
      case IrOpcode::kNumberLessThanOrEqual: {
        // Number comparisons reduce to integer comparisons for integer inputs.
        if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) &&
            TypeOf(node->InputAt(1))->Is(Type::Unsigned32())) {
          // => unsigned Int32Cmp
          VisitBinop(node, UseInfo::TruncatingWord32(),
                     MachineRepresentation::kBit);
          if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node));
        } else if (TypeOf(node->InputAt(0))->Is(Type::Signed32()) &&
                   TypeOf(node->InputAt(1))->Is(Type::Signed32())) {
          // => signed Int32Cmp
          VisitBinop(node, UseInfo::TruncatingWord32(),
                     MachineRepresentation::kBit);
          if (lower()) NodeProperties::ChangeOp(node, Int32Op(node));
        } else {
          // => Float64Cmp
          VisitBinop(node, UseInfo::TruncatingFloat64(),
                     MachineRepresentation::kBit);
          if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
        }
        return;
      }

      case IrOpcode::kSpeculativeNumberAdd:
      case IrOpcode::kSpeculativeNumberSubtract:
        return VisitSpeculativeAdditiveOp(node, truncation, lowering);

      case IrOpcode::kSpeculativeNumberLessThan:
      case IrOpcode::kSpeculativeNumberLessThanOrEqual:
      case IrOpcode::kSpeculativeNumberEqual: {
        // ToNumber(x) can throw if x is either a Receiver or a Symbol, so we
        // can only eliminate an unused speculative number operation if we know
        // that the inputs are PlainPrimitive, which excludes everything that's
        // might have side effects or throws during a ToNumber conversion.
        if (BothInputsAre(node, Type::PlainPrimitive())) {
          if (truncation.IsUnused()) return VisitUnused(node);
        }
        // Number comparisons reduce to integer comparisons for integer inputs.
        if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) &&
            TypeOf(node->InputAt(1))->Is(Type::Unsigned32())) {
          // => unsigned Int32Cmp
          VisitBinop(node, UseInfo::TruncatingWord32(),
                     MachineRepresentation::kBit);
          if (lower()) ChangeToPureOp(node, Uint32Op(node));
          return;
        } else if (TypeOf(node->InputAt(0))->Is(Type::Signed32()) &&
                   TypeOf(node->InputAt(1))->Is(Type::Signed32())) {
          // => signed Int32Cmp
          VisitBinop(node, UseInfo::TruncatingWord32(),
                     MachineRepresentation::kBit);
          if (lower()) ChangeToPureOp(node, Int32Op(node));
          return;
        }
        // Try to use type feedback.
        NumberOperationHint hint = NumberOperationHintOf(node->op());
        switch (hint) {
          case NumberOperationHint::kSignedSmall:
          case NumberOperationHint::kSigned32:
            VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
                       MachineRepresentation::kBit);
            if (lower()) ChangeToPureOp(node, Int32Op(node));
            return;
          case NumberOperationHint::kNumber:
          case NumberOperationHint::kNumberOrOddball:
            VisitBinop(node, CheckedUseInfoAsFloat64FromHint(hint),
                       MachineRepresentation::kBit);
            if (lower()) ChangeToPureOp(node, Float64Op(node));
            return;
        }
        UNREACHABLE();
        return;
      }

      case IrOpcode::kNumberAdd:
      case IrOpcode::kNumberSubtract: {
        if (BothInputsAre(node, type_cache_.kAdditiveSafeIntegerOrMinusZero) &&
            (GetUpperBound(node)->Is(Type::Signed32()) ||
             GetUpperBound(node)->Is(Type::Unsigned32()) ||
             truncation.IsUsedAsWord32())) {
          // => Int32Add/Sub
          VisitWord32TruncatingBinop(node);
          if (lower()) ChangeToPureOp(node, Int32Op(node));
        } else {
          // => Float64Add/Sub
          VisitFloat64Binop(node);
          if (lower()) ChangeToPureOp(node, Float64Op(node));
        }
        return;
      }
      case IrOpcode::kSpeculativeNumberMultiply: {
        // ToNumber(x) can throw if x is either a Receiver or a Symbol, so we
        // can only eliminate an unused speculative number operation if we know
        // that the inputs are PlainPrimitive, which excludes everything that's
        // might have side effects or throws during a ToNumber conversion.
        if (BothInputsAre(node, Type::PlainPrimitive())) {
          if (truncation.IsUnused()) return VisitUnused(node);
        }
        if (BothInputsAre(node, Type::Integral32()) &&
            (NodeProperties::GetType(node)->Is(Type::Signed32()) ||
             NodeProperties::GetType(node)->Is(Type::Unsigned32()) ||
             (truncation.IsUsedAsWord32() &&
              NodeProperties::GetType(node)->Is(
                  type_cache_.kSafeIntegerOrMinusZero)))) {
          // Multiply reduces to Int32Mul if the inputs are integers, and
          // (a) the output is either known to be Signed32, or
          // (b) the output is known to be Unsigned32, or
          // (c) the uses are truncating and the result is in the safe
          //     integer range.
          VisitWord32TruncatingBinop(node);
          if (lower()) ChangeToPureOp(node, Int32Op(node));
          return;
        }
        // Try to use type feedback.
        NumberOperationHint hint = NumberOperationHintOf(node->op());
        Type* input0_type = TypeOf(node->InputAt(0));
        Type* input1_type = TypeOf(node->InputAt(1));

        // Handle the case when no int32 checks on inputs are necessary
        // (but an overflow check is needed on the output).
        if (BothInputsAre(node, Type::Signed32())) {
          // If both the inputs the feedback are int32, use the overflow op.
          if (hint == NumberOperationHint::kSignedSmall ||
              hint == NumberOperationHint::kSigned32) {
            VisitBinop(node, UseInfo::TruncatingWord32(),
                       MachineRepresentation::kWord32, Type::Signed32());
            if (lower()) {
              LowerToCheckedInt32Mul(node, truncation, input0_type,
                                     input1_type);
            }
            return;
          }
        }

        if (hint == NumberOperationHint::kSignedSmall ||
            hint == NumberOperationHint::kSigned32) {
          VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
                     MachineRepresentation::kWord32, Type::Signed32());
          if (lower()) {
            LowerToCheckedInt32Mul(node, truncation, input0_type, input1_type);
          }
          return;
        }

        // Checked float64 x float64 => float64
        VisitBinop(node, UseInfo::CheckedNumberOrOddballAsFloat64(),
                   MachineRepresentation::kFloat64, Type::Number());
        if (lower()) ChangeToPureOp(node, Float64Op(node));
        return;
      }
      case IrOpcode::kNumberMultiply: {
        if (BothInputsAre(node, Type::Integral32()) &&
            (NodeProperties::GetType(node)->Is(Type::Signed32()) ||
             NodeProperties::GetType(node)->Is(Type::Unsigned32()) ||
             (truncation.IsUsedAsWord32() &&
              NodeProperties::GetType(node)->Is(
                  type_cache_.kSafeIntegerOrMinusZero)))) {
          // Multiply reduces to Int32Mul if the inputs are integers, and
          // (a) the output is either known to be Signed32, or
          // (b) the output is known to be Unsigned32, or
          // (c) the uses are truncating and the result is in the safe
          //     integer range.
          VisitWord32TruncatingBinop(node);
          if (lower()) ChangeToPureOp(node, Int32Op(node));
          return;
        }
        // Number x Number => Float64Mul
        VisitFloat64Binop(node);
        if (lower()) ChangeToPureOp(node, Float64Op(node));
        return;
      }
      case IrOpcode::kSpeculativeNumberDivide: {
        // ToNumber(x) can throw if x is either a Receiver or a Symbol, so we
        // can only eliminate an unused speculative number operation if we know
        // that the inputs are PlainPrimitive, which excludes everything that's
        // might have side effects or throws during a ToNumber conversion.
        if (BothInputsAre(node, Type::PlainPrimitive())) {
          if (truncation.IsUnused()) return VisitUnused(node);
        }
        if (BothInputsAreUnsigned32(node) && truncation.IsUsedAsWord32()) {
          // => unsigned Uint32Div
          VisitWord32TruncatingBinop(node);
          if (lower()) DeferReplacement(node, lowering->Uint32Div(node));
          return;
        }
        if (BothInputsAreSigned32(node)) {
          if (NodeProperties::GetType(node)->Is(Type::Signed32())) {
            // => signed Int32Div
            VisitWord32TruncatingBinop(node);
            if (lower()) DeferReplacement(node, lowering->Int32Div(node));
            return;
          }
          if (truncation.IsUsedAsWord32()) {
            // => signed Int32Div
            VisitWord32TruncatingBinop(node);
            if (lower()) DeferReplacement(node, lowering->Int32Div(node));
            return;
          }
        }

        // Try to use type feedback.
        NumberOperationHint hint = NumberOperationHintOf(node->op());

        // Handle the case when no uint32 checks on inputs are necessary
        // (but an overflow check is needed on the output).
        if (BothInputsAreUnsigned32(node)) {
          if (hint == NumberOperationHint::kSignedSmall ||
              hint == NumberOperationHint::kSigned32) {
            VisitBinop(node, UseInfo::TruncatingWord32(),
                       MachineRepresentation::kWord32, Type::Unsigned32());
            if (lower()) ChangeToUint32OverflowOp(node);
            return;
          }
        }

        // Handle the case when no int32 checks on inputs are necessary
        // (but an overflow check is needed on the output).
        if (BothInputsAreSigned32(node)) {
          // If both the inputs the feedback are int32, use the overflow op.
          if (hint == NumberOperationHint::kSignedSmall ||
              hint == NumberOperationHint::kSigned32) {
            VisitBinop(node, UseInfo::TruncatingWord32(),
                       MachineRepresentation::kWord32, Type::Signed32());
            if (lower()) ChangeToInt32OverflowOp(node);
            return;
          }
        }

        if (hint == NumberOperationHint::kSignedSmall ||
            hint == NumberOperationHint::kSigned32) {
          // If the result is truncated, we only need to check the inputs.
          if (truncation.IsUsedAsWord32()) {
            VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
                       MachineRepresentation::kWord32);
            if (lower()) DeferReplacement(node, lowering->Int32Div(node));
          } else {
            VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
                       MachineRepresentation::kWord32, Type::Signed32());
            if (lower()) ChangeToInt32OverflowOp(node);
          }
          return;
        }

        // default case => Float64Div
        VisitBinop(node, UseInfo::CheckedNumberOrOddballAsFloat64(),
                   MachineRepresentation::kFloat64, Type::Number());
        if (lower()) ChangeToPureOp(node, Float64Op(node));
        return;
      }
      case IrOpcode::kNumberDivide: {
        if (BothInputsAreUnsigned32(node) && truncation.IsUsedAsWord32()) {
          // => unsigned Uint32Div
          VisitWord32TruncatingBinop(node);
          if (lower()) DeferReplacement(node, lowering->Uint32Div(node));
          return;
        }
        if (BothInputsAreSigned32(node)) {
          if (NodeProperties::GetType(node)->Is(Type::Signed32())) {
            // => signed Int32Div
            VisitWord32TruncatingBinop(node);
            if (lower()) DeferReplacement(node, lowering->Int32Div(node));
            return;
          }
          if (truncation.IsUsedAsWord32()) {
            // => signed Int32Div
            VisitWord32TruncatingBinop(node);
            if (lower()) DeferReplacement(node, lowering->Int32Div(node));
            return;
          }
        }
        // Number x Number => Float64Div
        VisitFloat64Binop(node);
        if (lower()) ChangeToPureOp(node, Float64Op(node));
        return;
      }
      case IrOpcode::kSpeculativeNumberModulus:
        return VisitSpeculativeNumberModulus(node, truncation, lowering);
      case IrOpcode::kNumberModulus: {
        if (BothInputsAre(node, Type::Unsigned32OrMinusZeroOrNaN()) &&
            (truncation.IsUsedAsWord32() ||
             NodeProperties::GetType(node)->Is(Type::Unsigned32()))) {
          // => unsigned Uint32Mod
          VisitWord32TruncatingBinop(node);
          if (lower()) DeferReplacement(node, lowering->Uint32Mod(node));
          return;
        }
        if (BothInputsAre(node, Type::Signed32OrMinusZeroOrNaN()) &&
            (truncation.IsUsedAsWord32() ||
             NodeProperties::GetType(node)->Is(Type::Signed32()))) {
          // => signed Int32Mod
          VisitWord32TruncatingBinop(node);
          if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
          return;
        }
        if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) &&
            TypeOf(node->InputAt(1))->Is(Type::Unsigned32()) &&
            (truncation.IsUsedAsWord32() ||
             NodeProperties::GetType(node)->Is(Type::Unsigned32()))) {
          // We can only promise Float64 truncation here, as the decision is
          // based on the feedback types of the inputs.
          VisitBinop(node, UseInfo(MachineRepresentation::kWord32,
                                   Truncation::Float64()),
                     MachineRepresentation::kWord32);
          if (lower()) DeferReplacement(node, lowering->Uint32Mod(node));
          return;
        }
        if (TypeOf(node->InputAt(0))->Is(Type::Signed32()) &&
            TypeOf(node->InputAt(1))->Is(Type::Signed32()) &&
            (truncation.IsUsedAsWord32() ||
             NodeProperties::GetType(node)->Is(Type::Signed32()))) {
          // We can only promise Float64 truncation here, as the decision is
          // based on the feedback types of the inputs.
          VisitBinop(node, UseInfo(MachineRepresentation::kWord32,
                                   Truncation::Float64()),
                     MachineRepresentation::kWord32);
          if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
          return;
        }
        // default case => Float64Mod
        VisitFloat64Binop(node);
        if (lower()) ChangeToPureOp(node, Float64Op(node));
        return;
      }
      case IrOpcode::kNumberBitwiseOr:
      case IrOpcode::kNumberBitwiseXor:
      case IrOpcode::kNumberBitwiseAnd: {
        VisitWord32TruncatingBinop(node);
        if (lower()) NodeProperties::ChangeOp(node, Int32Op(node));
        return;
      }
      case IrOpcode::kSpeculativeNumberBitwiseOr:
      case IrOpcode::kSpeculativeNumberBitwiseXor:
      case IrOpcode::kSpeculativeNumberBitwiseAnd:
        VisitSpeculativeInt32Binop(node);
        if (lower()) {
          ChangeToPureOp(node, Int32Op(node));
        }
        return;
      case IrOpcode::kNumberShiftLeft: {
        Type* rhs_type = GetUpperBound(node->InputAt(1));
        VisitBinop(node, UseInfo::TruncatingWord32(),
                   UseInfo::TruncatingWord32(), MachineRepresentation::kWord32);
        if (lower()) {
          lowering->DoShift(node, lowering->machine()->Word32Shl(), rhs_type);
        }
        return;
      }
      case IrOpcode::kSpeculativeNumberShiftLeft: {
        // ToNumber(x) can throw if x is either a Receiver or a Symbol, so we
        // can only eliminate an unused speculative number operation if we know
        // that the inputs are PlainPrimitive, which excludes everything that's
        // might have side effects or throws during a ToNumber conversion.
        if (BothInputsAre(node, Type::PlainPrimitive())) {
          if (truncation.IsUnused()) return VisitUnused(node);
        }
        if (BothInputsAre(node, Type::NumberOrOddball())) {
          Type* rhs_type = GetUpperBound(node->InputAt(1));
          VisitBinop(node, UseInfo::TruncatingWord32(),
                     UseInfo::TruncatingWord32(),
                     MachineRepresentation::kWord32);
          if (lower()) {
            lowering->DoShift(node, lowering->machine()->Word32Shl(), rhs_type);
          }
          return;
        }
        NumberOperationHint hint = NumberOperationHintOf(node->op());
        Type* rhs_type = GetUpperBound(node->InputAt(1));
        VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
                   MachineRepresentation::kWord32, Type::Signed32());
        if (lower()) {
          lowering->DoShift(node, lowering->machine()->Word32Shl(), rhs_type);
        }
        return;
      }
      case IrOpcode::kNumberShiftRight: {
        Type* rhs_type = GetUpperBound(node->InputAt(1));
        VisitBinop(node, UseInfo::TruncatingWord32(),
                   UseInfo::TruncatingWord32(), MachineRepresentation::kWord32);
        if (lower()) {
          lowering->DoShift(node, lowering->machine()->Word32Sar(), rhs_type);
        }
        return;
      }
      case IrOpcode::kSpeculativeNumberShiftRight: {
        // ToNumber(x) can throw if x is either a Receiver or a Symbol, so we
        // can only eliminate an unused speculative number operation if we know
        // that the inputs are PlainPrimitive, which excludes everything that's
        // might have side effects or throws during a ToNumber conversion.
        if (BothInputsAre(node, Type::PlainPrimitive())) {
          if (truncation.IsUnused()) return VisitUnused(node);
        }
        if (BothInputsAre(node, Type::NumberOrOddball())) {
          Type* rhs_type = GetUpperBound(node->InputAt(1));
          VisitBinop(node, UseInfo::TruncatingWord32(),
                     UseInfo::TruncatingWord32(),
                     MachineRepresentation::kWord32);
          if (lower()) {
            lowering->DoShift(node, lowering->machine()->Word32Sar(), rhs_type);
          }
          return;
        }
        NumberOperationHint hint = NumberOperationHintOf(node->op());
        Type* rhs_type = GetUpperBound(node->InputAt(1));
        VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
                   MachineRepresentation::kWord32, Type::Signed32());
        if (lower()) {
          lowering->DoShift(node, lowering->machine()->Word32Sar(), rhs_type);
        }
        return;
      }
      case IrOpcode::kNumberShiftRightLogical: {
        Type* rhs_type = GetUpperBound(node->InputAt(1));
        VisitBinop(node, UseInfo::TruncatingWord32(),
                   UseInfo::TruncatingWord32(), MachineRepresentation::kWord32);
        if (lower()) {
          lowering->DoShift(node, lowering->machine()->Word32Shr(), rhs_type);
        }
        return;
      }
      case IrOpcode::kSpeculativeNumberShiftRightLogical: {
        // ToNumber(x) can throw if x is either a Receiver or a Symbol, so we
        // can only eliminate an unused speculative number operation if we know
        // that the inputs are PlainPrimitive, which excludes everything that's
        // might have side effects or throws during a ToNumber conversion.
        if (BothInputsAre(node, Type::PlainPrimitive())) {
          if (truncation.IsUnused()) return VisitUnused(node);
        }
        if (BothInputsAre(node, Type::NumberOrOddball())) {
          Type* rhs_type = GetUpperBound(node->InputAt(1));
          VisitBinop(node, UseInfo::TruncatingWord32(),
                     UseInfo::TruncatingWord32(),
                     MachineRepresentation::kWord32);
          if (lower()) {
            lowering->DoShift(node, lowering->machine()->Word32Shr(), rhs_type);
          }
          return;
        }
        NumberOperationHint hint = NumberOperationHintOf(node->op());
        Type* rhs_type = GetUpperBound(node->InputAt(1));
        VisitBinop(node, CheckedUseInfoAsWord32FromHint(hint),
                   MachineRepresentation::kWord32, Type::Unsigned32());
        if (lower()) {
          lowering->DoShift(node, lowering->machine()->Word32Shr(), rhs_type);
        }
        return;
      }
      case IrOpcode::kNumberAbs: {
        if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32())) {
          VisitUnop(node, UseInfo::TruncatingWord32(),
                    MachineRepresentation::kWord32);
          if (lower()) DeferReplacement(node, node->InputAt(0));
        } else if (TypeOf(node->InputAt(0))->Is(Type::Signed32())) {
          VisitUnop(node, UseInfo::TruncatingWord32(),
                    MachineRepresentation::kWord32);
          if (lower()) DeferReplacement(node, lowering->Int32Abs(node));
        } else if (TypeOf(node->InputAt(0))
                       ->Is(type_cache_.kPositiveIntegerOrMinusZeroOrNaN)) {
          VisitUnop(node, UseInfo::TruncatingFloat64(),
                    MachineRepresentation::kFloat64);
          if (lower()) DeferReplacement(node, node->InputAt(0));
        } else {
          VisitUnop(node, UseInfo::TruncatingFloat64(),
                    MachineRepresentation::kFloat64);
          if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
        }
        return;
      }
      case IrOpcode::kNumberClz32: {
        VisitUnop(node, UseInfo::TruncatingWord32(),
                  MachineRepresentation::kWord32);
        if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node));
        return;
      }
      case IrOpcode::kNumberImul: {
        VisitBinop(node, UseInfo::TruncatingWord32(),
                   UseInfo::TruncatingWord32(), MachineRepresentation::kWord32);
        if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node));
        return;
      }
      case IrOpcode::kNumberFround: {
        VisitUnop(node, UseInfo::TruncatingFloat64(),
                  MachineRepresentation::kFloat32);
        if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
        return;
      }
      case IrOpcode::kNumberMax: {
        // TODO(turbofan): We should consider feedback types here as well.
        if (BothInputsAreUnsigned32(node)) {
          VisitWord32TruncatingBinop(node);
          if (lower()) {
            lowering->DoMax(node, lowering->machine()->Uint32LessThan(),
                            MachineRepresentation::kWord32);
          }
        } else if (BothInputsAreSigned32(node)) {
          VisitWord32TruncatingBinop(node);
          if (lower()) {
            lowering->DoMax(node, lowering->machine()->Int32LessThan(),
                            MachineRepresentation::kWord32);
          }
        } else if (BothInputsAre(node, Type::PlainNumber())) {
          VisitFloat64Binop(node);
          if (lower()) {
            lowering->DoMax(node, lowering->machine()->Float64LessThan(),
                            MachineRepresentation::kFloat64);
          }
        } else {
          VisitFloat64Binop(node);
          if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
        }
        return;
      }
      case IrOpcode::kNumberMin: {
        // TODO(turbofan): We should consider feedback types here as well.
        if (BothInputsAreUnsigned32(node)) {
          VisitWord32TruncatingBinop(node);
          if (lower()) {
            lowering->DoMin(node, lowering->machine()->Uint32LessThan(),
                            MachineRepresentation::kWord32);
          }
        } else if (BothInputsAreSigned32(node)) {
          VisitWord32TruncatingBinop(node);
          if (lower()) {
            lowering->DoMin(node, lowering->machine()->Int32LessThan(),
                            MachineRepresentation::kWord32);
          }
        } else if (BothInputsAre(node, Type::PlainNumber())) {
          VisitFloat64Binop(node);
          if (lower()) {
            lowering->DoMin(node, lowering->machine()->Float64LessThan(),
                            MachineRepresentation::kFloat64);
          }
        } else {
          VisitFloat64Binop(node);
          if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
        }
        return;
      }
      case IrOpcode::kNumberAtan2:
      case IrOpcode::kNumberPow: {
        VisitBinop(node, UseInfo::TruncatingFloat64(),
                   MachineRepresentation::kFloat64);
        if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
        return;
      }
      case IrOpcode::kNumberAcos:
      case IrOpcode::kNumberAcosh:
      case IrOpcode::kNumberAsin:
      case IrOpcode::kNumberAsinh:
      case IrOpcode::kNumberAtan:
      case IrOpcode::kNumberAtanh:
      case IrOpcode::kNumberCeil:
      case IrOpcode::kNumberCos:
      case IrOpcode::kNumberCosh:
      case IrOpcode::kNumberExp:
      case IrOpcode::kNumberExpm1:
      case IrOpcode::kNumberFloor:
      case IrOpcode::kNumberLog:
      case IrOpcode::kNumberLog1p:
      case IrOpcode::kNumberLog2:
      case IrOpcode::kNumberLog10:
      case IrOpcode::kNumberCbrt:
      case IrOpcode::kNumberSin:
      case IrOpcode::kNumberSinh:
      case IrOpcode::kNumberTan:
      case IrOpcode::kNumberTanh:
      case IrOpcode::kNumberTrunc: {
        VisitUnop(node, UseInfo::TruncatingFloat64(),
                  MachineRepresentation::kFloat64);
        if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
        return;
      }
      case IrOpcode::kNumberRound: {
        VisitUnop(node, UseInfo::TruncatingFloat64(),
                  MachineRepresentation::kFloat64);
        if (lower()) DeferReplacement(node, lowering->Float64Round(node));
        return;
      }
      case IrOpcode::kNumberSign: {
        if (InputIs(node, Type::Signed32())) {
          VisitUnop(node, UseInfo::TruncatingWord32(),
                    MachineRepresentation::kWord32);
          if (lower()) DeferReplacement(node, lowering->Int32Sign(node));
        } else {
          VisitUnop(node, UseInfo::TruncatingFloat64(),
                    MachineRepresentation::kFloat64);
          if (lower()) DeferReplacement(node, lowering->Float64Sign(node));
        }
        return;
      }
      case IrOpcode::kNumberSqrt: {
        VisitUnop(node, UseInfo::TruncatingFloat64(),
                  MachineRepresentation::kFloat64);
        if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
        return;
      }
      case IrOpcode::kNumberToBoolean: {
        Type* const input_type = TypeOf(node->InputAt(0));
        if (input_type->Is(Type::Integral32())) {
          VisitUnop(node, UseInfo::TruncatingWord32(),
                    MachineRepresentation::kBit);
          if (lower()) lowering->DoIntegral32ToBit(node);
        } else if (input_type->Is(Type::OrderedNumber())) {
          VisitUnop(node, UseInfo::TruncatingFloat64(),
                    MachineRepresentation::kBit);
          if (lower()) lowering->DoOrderedNumberToBit(node);
        } else {
          VisitUnop(node, UseInfo::TruncatingFloat64(),
                    MachineRepresentation::kBit);
          if (lower()) lowering->DoNumberToBit(node);
        }
        return;
      }
      case IrOpcode::kNumberToInt32: {
        // Just change representation if necessary.
        VisitUnop(node, UseInfo::TruncatingWord32(),
                  MachineRepresentation::kWord32);
        if (lower()) DeferReplacement(node, node->InputAt(0));
        return;
      }
      case IrOpcode::kNumberToUint32: {
        // Just change representation if necessary.
        VisitUnop(node, UseInfo::TruncatingWord32(),
                  MachineRepresentation::kWord32);
        if (lower()) DeferReplacement(node, node->InputAt(0));
        return;
      }
      case IrOpcode::kNumberToUint8Clamped: {
        Type* const input_type = TypeOf(node->InputAt(0));
        if (input_type->Is(type_cache_.kUint8OrMinusZeroOrNaN)) {
          VisitUnop(node, UseInfo::TruncatingWord32(),
                    MachineRepresentation::kWord32);
          if (lower()) DeferReplacement(node, node->InputAt(0));
        } else if (input_type->Is(Type::Unsigned32OrMinusZeroOrNaN())) {
          VisitUnop(node, UseInfo::TruncatingWord32(),
                    MachineRepresentation::kWord32);
          if (lower()) lowering->DoUnsigned32ToUint8Clamped(node);
        } else if (input_type->Is(Type::Signed32OrMinusZeroOrNaN())) {
          VisitUnop(node, UseInfo::TruncatingWord32(),
                    MachineRepresentation::kWord32);
          if (lower()) lowering->DoSigned32ToUint8Clamped(node);
        } else if (input_type->Is(type_cache_.kIntegerOrMinusZeroOrNaN)) {
          VisitUnop(node, UseInfo::TruncatingFloat64(),
                    MachineRepresentation::kFloat64);
          if (lower()) lowering->DoIntegerToUint8Clamped(node);
        } else {
          VisitUnop(node, UseInfo::TruncatingFloat64(),
                    MachineRepresentation::kFloat64);
          if (lower()) lowering->DoNumberToUint8Clamped(node);
        }
        return;
      }
      case IrOpcode::kReferenceEqual: {
        VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
        if (lower()) {
          NodeProperties::ChangeOp(node, lowering->machine()->WordEqual());
        }
        return;
      }
      case IrOpcode::kStringEqual:
      case IrOpcode::kStringLessThan:
      case IrOpcode::kStringLessThanOrEqual: {
        return VisitBinop(node, UseInfo::AnyTagged(),
                          MachineRepresentation::kTaggedPointer);
      }
      case IrOpcode::kStringCharCodeAt: {
        VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(),
                   MachineRepresentation::kWord32);
        return;
      }
      case IrOpcode::kStringFromCharCode: {
        VisitUnop(node, UseInfo::TruncatingWord32(),
                  MachineRepresentation::kTaggedPointer);
        return;
      }
      case IrOpcode::kStringFromCodePoint: {
        VisitUnop(node, UseInfo::TruncatingWord32(),
                  MachineRepresentation::kTaggedPointer);
        return;
      }

      case IrOpcode::kCheckBounds: {
        Type* index_type = TypeOf(node->InputAt(0));
        Type* length_type = TypeOf(node->InputAt(1));
        if (index_type->Is(Type::Unsigned32())) {
          VisitBinop(node, UseInfo::TruncatingWord32(),
                     MachineRepresentation::kWord32);
          if (lower() && index_type->Max() < length_type->Min()) {
            // The bounds check is redundant if we already know that
            // the index is within the bounds of [0.0, length[.
            DeferReplacement(node, node->InputAt(0));
          }
        } else {
          VisitBinop(node, UseInfo::CheckedSigned32AsWord32(),
                     UseInfo::TruncatingWord32(),
                     MachineRepresentation::kWord32);
        }
        return;
      }
      case IrOpcode::kCheckHeapObject: {
        if (InputCannotBe(node, Type::SignedSmall())) {
          VisitUnop(node, UseInfo::AnyTagged(),
                    MachineRepresentation::kTaggedPointer);
        } else {
          VisitUnop(node, UseInfo::CheckedHeapObjectAsTaggedPointer(),
                    MachineRepresentation::kTaggedPointer);
        }
        if (lower()) DeferReplacement(node, node->InputAt(0));
        return;
      }
      case IrOpcode::kCheckIf: {
        ProcessInput(node, 0, UseInfo::Bool());
        ProcessRemainingInputs(node, 1);
        SetOutput(node, MachineRepresentation::kNone);
        return;
      }
      case IrOpcode::kCheckNumber: {
        if (InputIs(node, Type::Number())) {
          if (truncation.IsUsedAsWord32()) {
            VisitUnop(node, UseInfo::TruncatingWord32(),
                      MachineRepresentation::kWord32);
          } else {
            // TODO(jarin,bmeurer): We need to go to Tagged here, because
            // otherwise we cannot distinguish the hole NaN (which might need to
            // be treated as undefined). We should have a dedicated Type for
            // that at some point, and maybe even a dedicated truncation.
            VisitUnop(node, UseInfo::AnyTagged(),
                      MachineRepresentation::kTagged);
          }
          if (lower()) DeferReplacement(node, node->InputAt(0));
        } else {
          VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
        }
        return;
      }
      case IrOpcode::kCheckSmi: {
        if (SmiValuesAre32Bits() && truncation.IsUsedAsWord32()) {
          VisitUnop(node, UseInfo::CheckedSignedSmallAsWord32(),
                    MachineRepresentation::kWord32);
        } else {
          VisitUnop(node, UseInfo::CheckedSignedSmallAsTaggedSigned(),
                    MachineRepresentation::kTaggedSigned);
        }
        if (lower()) DeferReplacement(node, node->InputAt(0));
        return;
      }
      case IrOpcode::kCheckString: {
        if (InputIs(node, Type::String())) {
          VisitUnop(node, UseInfo::AnyTagged(),
                    MachineRepresentation::kTaggedPointer);
          if (lower()) DeferReplacement(node, node->InputAt(0));
        } else {
          VisitUnop(node, UseInfo::AnyTagged(),
                    MachineRepresentation::kTaggedPointer);
        }
        return;
      }

      case IrOpcode::kAllocate: {
        ProcessInput(node, 0, UseInfo::TruncatingWord32());
        ProcessRemainingInputs(node, 1);
        SetOutput(node, MachineRepresentation::kTaggedPointer);
        return;
      }
      case IrOpcode::kLoadField: {
        if (truncation.IsUnused()) return VisitUnused(node);
        FieldAccess access = FieldAccessOf(node->op());
        MachineRepresentation const representation =
            access.machine_type.representation();
        VisitUnop(node, UseInfoForBasePointer(access), representation);
        return;
      }
      case IrOpcode::kStoreField: {
        FieldAccess access = FieldAccessOf(node->op());
        NodeInfo* input_info = GetInfo(node->InputAt(1));
        WriteBarrierKind write_barrier_kind = WriteBarrierKindFor(
            access.base_is_tagged, access.machine_type.representation(),
            access.offset, access.type, input_info->representation(),
            node->InputAt(1));
        ProcessInput(node, 0, UseInfoForBasePointer(access));
        ProcessInput(node, 1, TruncatingUseInfoFromRepresentation(
                                  access.machine_type.representation()));
        ProcessRemainingInputs(node, 2);
        SetOutput(node, MachineRepresentation::kNone);
        if (lower()) {
          if (write_barrier_kind < access.write_barrier_kind) {
            access.write_barrier_kind = write_barrier_kind;
            NodeProperties::ChangeOp(
                node, jsgraph_->simplified()->StoreField(access));
          }
        }
        return;
      }
      case IrOpcode::kLoadBuffer: {
        if (truncation.IsUnused()) return VisitUnused(node);
        BufferAccess access = BufferAccessOf(node->op());
        ProcessInput(node, 0, UseInfo::PointerInt());        // buffer
        ProcessInput(node, 1, UseInfo::TruncatingWord32());  // offset
        ProcessInput(node, 2, UseInfo::TruncatingWord32());  // length
        ProcessRemainingInputs(node, 3);

        MachineRepresentation output;
        if (truncation.IdentifiesUndefinedAndNaNAndZero()) {
          if (truncation.IdentifiesNaNAndZero()) {
            // If undefined is truncated to a non-NaN number, we can use
            // the load's representation.
            output = access.machine_type().representation();
          } else {
            // If undefined is truncated to a number, but the use can
            // observe NaN, we need to output at least the float32
            // representation.
            if (access.machine_type().representation() ==
                MachineRepresentation::kFloat32) {
              output = access.machine_type().representation();
            } else {
              output = MachineRepresentation::kFloat64;
            }
          }
        } else {
          // If undefined is not truncated away, we need to have the tagged
          // representation.
          output = MachineRepresentation::kTagged;
        }
        SetOutput(node, output);
        if (lower()) lowering->DoLoadBuffer(node, output, changer_);
        return;
      }
      case IrOpcode::kStoreBuffer: {
        BufferAccess access = BufferAccessOf(node->op());
        ProcessInput(node, 0, UseInfo::PointerInt());        // buffer
        ProcessInput(node, 1, UseInfo::TruncatingWord32());  // offset
        ProcessInput(node, 2, UseInfo::TruncatingWord32());  // length
        ProcessInput(node, 3,
                     TruncatingUseInfoFromRepresentation(
                         access.machine_type().representation()));  // value
        ProcessRemainingInputs(node, 4);
        SetOutput(node, MachineRepresentation::kNone);
        if (lower()) lowering->DoStoreBuffer(node);
        return;
      }
      case IrOpcode::kLoadElement: {
        if (truncation.IsUnused()) return VisitUnused(node);
        ElementAccess access = ElementAccessOf(node->op());
        VisitBinop(node, UseInfoForBasePointer(access),
                   UseInfo::TruncatingWord32(),
                   access.machine_type.representation());
        return;
      }
      case IrOpcode::kStoreElement: {
        ElementAccess access = ElementAccessOf(node->op());
        NodeInfo* input_info = GetInfo(node->InputAt(2));
        WriteBarrierKind write_barrier_kind = WriteBarrierKindFor(
            access.base_is_tagged, access.machine_type.representation(),
            access.type, input_info->representation(), node->InputAt(2));
        ProcessInput(node, 0, UseInfoForBasePointer(access));  // base
        ProcessInput(node, 1, UseInfo::TruncatingWord32());    // index
        ProcessInput(node, 2,
                     TruncatingUseInfoFromRepresentation(
                         access.machine_type.representation()));  // value
        ProcessRemainingInputs(node, 3);
        SetOutput(node, MachineRepresentation::kNone);
        if (lower()) {
          if (write_barrier_kind < access.write_barrier_kind) {
            access.write_barrier_kind = write_barrier_kind;
            NodeProperties::ChangeOp(
                node, jsgraph_->simplified()->StoreElement(access));
          }
        }
        return;
      }
      case IrOpcode::kLoadTypedElement: {
        MachineRepresentation const rep =
            MachineRepresentationFromArrayType(ExternalArrayTypeOf(node->op()));
        ProcessInput(node, 0, UseInfo::AnyTagged());         // buffer
        ProcessInput(node, 1, UseInfo::AnyTagged());         // base pointer
        ProcessInput(node, 2, UseInfo::PointerInt());        // external pointer
        ProcessInput(node, 3, UseInfo::TruncatingWord32());  // index
        ProcessRemainingInputs(node, 4);
        SetOutput(node, rep);
        return;
      }
      case IrOpcode::kStoreTypedElement: {
        MachineRepresentation const rep =
            MachineRepresentationFromArrayType(ExternalArrayTypeOf(node->op()));
        ProcessInput(node, 0, UseInfo::AnyTagged());         // buffer
        ProcessInput(node, 1, UseInfo::AnyTagged());         // base pointer
        ProcessInput(node, 2, UseInfo::PointerInt());        // external pointer
        ProcessInput(node, 3, UseInfo::TruncatingWord32());  // index
        ProcessInput(node, 4,
                     TruncatingUseInfoFromRepresentation(rep));  // value
        ProcessRemainingInputs(node, 5);
        SetOutput(node, MachineRepresentation::kNone);
        return;
      }
      case IrOpcode::kPlainPrimitiveToNumber: {
        if (InputIs(node, Type::Boolean())) {
          VisitUnop(node, UseInfo::Bool(), MachineRepresentation::kWord32);
          if (lower()) DeferReplacement(node, node->InputAt(0));
        } else if (InputIs(node, Type::String())) {
          VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
          if (lower()) lowering->DoStringToNumber(node);
        } else if (truncation.IsUsedAsWord32()) {
          if (InputIs(node, Type::NumberOrOddball())) {
            VisitUnop(node, UseInfo::TruncatingWord32(),
                      MachineRepresentation::kWord32);
            if (lower()) DeferReplacement(node, node->InputAt(0));
          } else {
            VisitUnop(node, UseInfo::AnyTagged(),
                      MachineRepresentation::kWord32);
            if (lower()) {
              NodeProperties::ChangeOp(node,
                                       simplified()->PlainPrimitiveToWord32());
            }
          }
        } else if (truncation.IsUsedAsFloat64()) {
          if (InputIs(node, Type::NumberOrOddball())) {
            VisitUnop(node, UseInfo::TruncatingFloat64(),
                      MachineRepresentation::kFloat64);
            if (lower()) DeferReplacement(node, node->InputAt(0));
          } else {
            VisitUnop(node, UseInfo::AnyTagged(),
                      MachineRepresentation::kFloat64);
            if (lower()) {
              NodeProperties::ChangeOp(node,
                                       simplified()->PlainPrimitiveToFloat64());
            }
          }
        } else {
          VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
        }
        return;
      }
      case IrOpcode::kObjectIsCallable: {
        // TODO(turbofan): Add Type::Callable to optimize this?
        VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
        return;
      }
      case IrOpcode::kObjectIsNumber: {
        VisitObjectIs(node, Type::Number(), lowering);
        return;
      }
      case IrOpcode::kObjectIsReceiver: {
        VisitObjectIs(node, Type::Receiver(), lowering);
        return;
      }
      case IrOpcode::kObjectIsSmi: {
        // TODO(turbofan): Optimize based on input representation.
        VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
        return;
      }
      case IrOpcode::kObjectIsString: {
        VisitObjectIs(node, Type::String(), lowering);
        return;
      }
      case IrOpcode::kObjectIsUndetectable: {
        VisitObjectIs(node, Type::Undetectable(), lowering);
        return;
      }
      case IrOpcode::kArrayBufferWasNeutered: {
        VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
        return;
      }
      case IrOpcode::kCheckFloat64Hole: {
        if (truncation.IsUnused()) return VisitUnused(node);
        CheckFloat64HoleMode mode = CheckFloat64HoleModeOf(node->op());
        ProcessInput(node, 0, UseInfo::TruncatingFloat64());
        ProcessRemainingInputs(node, 1);
        SetOutput(node, MachineRepresentation::kFloat64);
        if (truncation.IsUsedAsFloat64() &&
            mode == CheckFloat64HoleMode::kAllowReturnHole) {
          if (lower()) DeferReplacement(node, node->InputAt(0));
        }
        return;
      }
      case IrOpcode::kCheckTaggedHole: {
        VisitUnop(node, UseInfo::AnyTagged(),
                  MachineRepresentation::kTaggedPointer);
        return;
      }
      case IrOpcode::kConvertTaggedHoleToUndefined: {
        if (InputIs(node, Type::NumberOrOddball()) &&
            truncation.IsUsedAsWord32()) {
          // Propagate the Word32 truncation.
          VisitUnop(node, UseInfo::TruncatingWord32(),
                    MachineRepresentation::kWord32);
          if (lower()) DeferReplacement(node, node->InputAt(0));
        } else if (InputIs(node, Type::NumberOrOddball()) &&
                   truncation.IsUsedAsFloat64()) {
          // Propagate the Float64 truncation.
          VisitUnop(node, UseInfo::TruncatingFloat64(),
                    MachineRepresentation::kFloat64);
          if (lower()) DeferReplacement(node, node->InputAt(0));
        } else if (InputIs(node, Type::NonInternal())) {
          VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
          if (lower()) DeferReplacement(node, node->InputAt(0));
        } else {
          // TODO(turbofan): Add a (Tagged) truncation that identifies hole
          // and undefined, i.e. for a[i] === obj cases.
          VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
        }
        return;
      }
      case IrOpcode::kCheckMaps:
      case IrOpcode::kTransitionElementsKind: {
        VisitInputs(node);
        return SetOutput(node, MachineRepresentation::kNone);
      }
      case IrOpcode::kEnsureWritableFastElements:
        return VisitBinop(node, UseInfo::AnyTagged(),
                          MachineRepresentation::kTaggedPointer);
      case IrOpcode::kMaybeGrowFastElements: {
        ProcessInput(node, 0, UseInfo::AnyTagged());         // object
        ProcessInput(node, 1, UseInfo::AnyTagged());         // elements
        ProcessInput(node, 2, UseInfo::TruncatingWord32());  // index
        ProcessInput(node, 3, UseInfo::TruncatingWord32());  // length
        ProcessRemainingInputs(node, 4);
        SetOutput(node, MachineRepresentation::kTaggedPointer);
        return;
      }

      case IrOpcode::kNumberSilenceNaN:
        VisitUnop(node, UseInfo::TruncatingFloat64(),
                  MachineRepresentation::kFloat64);
        if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
        return;
      case IrOpcode::kStateValues:
        return VisitStateValues(node);
      case IrOpcode::kObjectState:
        return VisitObjectState(node);
      case IrOpcode::kTypeGuard: {
        // We just get rid of the sigma here. In principle, it should be
        // possible to refine the truncation and representation based on
        // the sigma's type.
        MachineRepresentation output =
            GetOutputInfoForPhi(node, TypeOf(node->InputAt(0)), truncation);
        VisitUnop(node, UseInfo(output, truncation), output);
        if (lower()) DeferReplacement(node, node->InputAt(0));
        return;
      }

      case IrOpcode::kOsrGuard:
        return VisitOsrGuard(node);

      case IrOpcode::kFinishRegion:
        VisitInputs(node);
        // Assume the output is tagged pointer.
        return SetOutput(node, MachineRepresentation::kTaggedPointer);

      case IrOpcode::kReturn:
        VisitReturn(node);
        // Assume the output is tagged.
        return SetOutput(node, MachineRepresentation::kTagged);

      // Operators with all inputs tagged and no or tagged output have uniform
      // handling.
      case IrOpcode::kEnd:
      case IrOpcode::kIfSuccess:
      case IrOpcode::kIfException:
      case IrOpcode::kIfTrue:
      case IrOpcode::kIfFalse:
      case IrOpcode::kDeoptimize:
      case IrOpcode::kEffectPhi:
      case IrOpcode::kTerminate:
      case IrOpcode::kFrameState:
      case IrOpcode::kCheckpoint:
      case IrOpcode::kLoop:
      case IrOpcode::kMerge:
      case IrOpcode::kThrow:
      case IrOpcode::kBeginRegion:
      case IrOpcode::kProjection:
      case IrOpcode::kOsrValue:
// All JavaScript operators except JSToNumber have uniform handling.
#define OPCODE_CASE(name) case IrOpcode::k##name:
        JS_SIMPLE_BINOP_LIST(OPCODE_CASE)
        JS_OTHER_UNOP_LIST(OPCODE_CASE)
        JS_OBJECT_OP_LIST(OPCODE_CASE)
        JS_CONTEXT_OP_LIST(OPCODE_CASE)
        JS_OTHER_OP_LIST(OPCODE_CASE)
#undef OPCODE_CASE
      case IrOpcode::kJSToInteger:
      case IrOpcode::kJSToLength:
      case IrOpcode::kJSToName:
      case IrOpcode::kJSToObject:
      case IrOpcode::kJSToString:
        VisitInputs(node);
        // Assume the output is tagged.
        return SetOutput(node, MachineRepresentation::kTagged);

      default:
        V8_Fatal(
            __FILE__, __LINE__,
            "Representation inference: unsupported opcode %i (%s), node #%i\n.",
            node->opcode(), node->op()->mnemonic(), node->id());
        break;
    }
    UNREACHABLE();
  }

  void DeferReplacement(Node* node, Node* replacement) {
    TRACE("defer replacement #%d:%s with #%d:%s\n", node->id(),
          node->op()->mnemonic(), replacement->id(),
          replacement->op()->mnemonic());

    // Disconnect the node from effect and control chains, if necessary.
    if (node->op()->EffectInputCount() > 0) {
      DCHECK_LT(0, node->op()->ControlInputCount());
      // Disconnect the node from effect and control chains.
      Node* control = NodeProperties::GetControlInput(node);
      Node* effect = NodeProperties::GetEffectInput(node);
      ReplaceEffectControlUses(node, effect, control);
    }

    replacements_.push_back(node);
    replacements_.push_back(replacement);

    node->NullAllInputs();  // Node is now dead.
  }

  void Kill(Node* node) {
    TRACE("killing #%d:%s\n", node->id(), node->op()->mnemonic());

    if (node->op()->EffectInputCount() == 1) {
      DCHECK_LT(0, node->op()->ControlInputCount());
      // Disconnect the node from effect and control chains.
      Node* control = NodeProperties::GetControlInput(node);
      Node* effect = NodeProperties::GetEffectInput(node);
      ReplaceEffectControlUses(node, effect, control);
    } else {
      DCHECK_EQ(0, node->op()->EffectInputCount());
      DCHECK_EQ(0, node->op()->ControlOutputCount());
      DCHECK_EQ(0, node->op()->EffectOutputCount());
    }

    node->ReplaceUses(jsgraph_->Dead());

    node->NullAllInputs();  // The {node} is now dead.
  }

  void PrintOutputInfo(NodeInfo* info) {
    if (FLAG_trace_representation) {
      OFStream os(stdout);
      os << info->representation();
    }
  }

  void PrintRepresentation(MachineRepresentation rep) {
    if (FLAG_trace_representation) {
      OFStream os(stdout);
      os << rep;
    }
  }

  void PrintTruncation(Truncation truncation) {
    if (FLAG_trace_representation) {
      OFStream os(stdout);
      os << truncation.description() << std::endl;
    }
  }

  void PrintUseInfo(UseInfo info) {
    if (FLAG_trace_representation) {
      OFStream os(stdout);
      os << info.representation() << ":" << info.truncation().description();
    }
  }

 private:
  JSGraph* jsgraph_;
  Zone* zone_;                      // Temporary zone.
  size_t const count_;              // number of nodes in the graph
  ZoneVector<NodeInfo> info_;       // node id -> usage information
#ifdef DEBUG
  ZoneVector<InputUseInfos> node_input_use_infos_;  // Debug information about
                                                    // requirements on inputs.
#endif                                              // DEBUG
  NodeVector nodes_;                // collected nodes
  NodeVector replacements_;         // replacements to be done after lowering
  Phase phase_;                     // current phase of algorithm
  RepresentationChanger* changer_;  // for inserting representation changes
  ZoneQueue<Node*> queue_;          // queue for traversing the graph

  struct NodeState {
    Node* node;
    int input_index;
  };
  ZoneStack<NodeState> typing_stack_;  // stack for graph typing.
  // TODO(danno): RepresentationSelector shouldn't know anything about the
  // source positions table, but must for now since there currently is no other
  // way to pass down source position information to nodes created during
  // lowering. Once this phase becomes a vanilla reducer, it should get source
  // position information via the SourcePositionWrapper like all other reducers.
  SourcePositionTable* source_positions_;
  TypeCache const& type_cache_;
  OperationTyper op_typer_;  // helper for the feedback typer

  NodeInfo* GetInfo(Node* node) {
    DCHECK(node->id() < count_);
    return &info_[node->id()];
  }
  Zone* zone() { return zone_; }
  Zone* graph_zone() { return jsgraph_->zone(); }
};

SimplifiedLowering::SimplifiedLowering(JSGraph* jsgraph, Zone* zone,
                                       SourcePositionTable* source_positions)
    : jsgraph_(jsgraph),
      zone_(zone),
      type_cache_(TypeCache::Get()),
      source_positions_(source_positions) {}

void SimplifiedLowering::LowerAllNodes() {
  RepresentationChanger changer(jsgraph(), jsgraph()->isolate());
  RepresentationSelector selector(jsgraph(), zone_, &changer,
                                  source_positions_);
  selector.Run(this);
}

void SimplifiedLowering::DoJSToNumberTruncatesToFloat64(
    Node* node, RepresentationSelector* selector) {
  DCHECK_EQ(IrOpcode::kJSToNumber, node->opcode());
  Node* value = node->InputAt(0);
  Node* context = node->InputAt(1);
  Node* frame_state = node->InputAt(2);
  Node* effect = node->InputAt(3);
  Node* control = node->InputAt(4);
  Node* throwing;

  Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
  Node* branch0 =
      graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);

  Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
  Node* etrue0 = effect;
  Node* vtrue0;
  {
    vtrue0 = graph()->NewNode(simplified()->ChangeTaggedSignedToInt32(), value);
    vtrue0 = graph()->NewNode(machine()->ChangeInt32ToFloat64(), vtrue0);
  }

  Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
  Node* efalse0 = effect;
  Node* vfalse0;
  {
    throwing = vfalse0 = efalse0 =
        graph()->NewNode(ToNumberOperator(), ToNumberCode(), value, context,
                         frame_state, efalse0, if_false0);
    if_false0 = graph()->NewNode(common()->IfSuccess(), throwing);

    Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0);
    Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);

    Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
    Node* etrue1 = efalse0;
    Node* vtrue1;
    {
      vtrue1 =
          graph()->NewNode(simplified()->ChangeTaggedSignedToInt32(), vfalse0);
      vtrue1 = graph()->NewNode(machine()->ChangeInt32ToFloat64(), vtrue1);
    }

    Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
    Node* efalse1 = efalse0;
    Node* vfalse1;
    {
      vfalse1 = efalse1 = graph()->NewNode(
          simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), efalse0,
          efalse1, if_false1);
    }

    if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
    efalse0 =
        graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0);
    vfalse0 =
        graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2),
                         vtrue1, vfalse1, if_false0);
  }

  control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
  effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
  value = graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2),
                           vtrue0, vfalse0, control);

  // Replace effect and control uses appropriately.
  for (Edge edge : node->use_edges()) {
    if (NodeProperties::IsControlEdge(edge)) {
      if (edge.from()->opcode() == IrOpcode::kIfSuccess) {
        edge.from()->ReplaceUses(control);
        edge.from()->Kill();
      } else if (edge.from()->opcode() == IrOpcode::kIfException) {
        edge.UpdateTo(throwing);
      } else {
        UNREACHABLE();
      }
    } else if (NodeProperties::IsEffectEdge(edge)) {
      edge.UpdateTo(effect);
    }
  }

  selector->DeferReplacement(node, value);
}

void SimplifiedLowering::DoJSToNumberTruncatesToWord32(
    Node* node, RepresentationSelector* selector) {
  DCHECK_EQ(IrOpcode::kJSToNumber, node->opcode());
  Node* value = node->InputAt(0);
  Node* context = node->InputAt(1);
  Node* frame_state = node->InputAt(2);
  Node* effect = node->InputAt(3);
  Node* control = node->InputAt(4);
  Node* throwing;

  Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
  Node* branch0 =
      graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);

  Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
  Node* etrue0 = effect;
  Node* vtrue0 =
      graph()->NewNode(simplified()->ChangeTaggedSignedToInt32(), value);

  Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
  Node* efalse0 = effect;
  Node* vfalse0;
  {
    throwing = vfalse0 = efalse0 =
        graph()->NewNode(ToNumberOperator(), ToNumberCode(), value, context,
                         frame_state, efalse0, if_false0);
    if_false0 = graph()->NewNode(common()->IfSuccess(), throwing);

    Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0);
    Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);

    Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
    Node* etrue1 = efalse0;
    Node* vtrue1 =
        graph()->NewNode(simplified()->ChangeTaggedSignedToInt32(), vfalse0);

    Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
    Node* efalse1 = efalse0;
    Node* vfalse1;
    {
      vfalse1 = efalse1 = graph()->NewNode(
          simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), efalse0,
          efalse1, if_false1);
      vfalse1 = graph()->NewNode(machine()->TruncateFloat64ToWord32(), vfalse1);
    }

    if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
    efalse0 =
        graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0);
    vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
                               vtrue1, vfalse1, if_false0);
  }

  control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
  effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
  value = graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
                           vtrue0, vfalse0, control);

  // Replace effect and control uses appropriately.
  for (Edge edge : node->use_edges()) {
    if (NodeProperties::IsControlEdge(edge)) {
      if (edge.from()->opcode() == IrOpcode::kIfSuccess) {
        edge.from()->ReplaceUses(control);
        edge.from()->Kill();
      } else if (edge.from()->opcode() == IrOpcode::kIfException) {
        edge.UpdateTo(throwing);
      } else {
        UNREACHABLE();
      }
    } else if (NodeProperties::IsEffectEdge(edge)) {
      edge.UpdateTo(effect);
    }
  }

  selector->DeferReplacement(node, value);
}

void SimplifiedLowering::DoLoadBuffer(Node* node,
                                      MachineRepresentation output_rep,
                                      RepresentationChanger* changer) {
  DCHECK_EQ(IrOpcode::kLoadBuffer, node->opcode());
  DCHECK_NE(MachineRepresentation::kNone, output_rep);
  MachineType const access_type = BufferAccessOf(node->op()).machine_type();
  if (output_rep != access_type.representation()) {
    Node* const buffer = node->InputAt(0);
    Node* const offset = node->InputAt(1);
    Node* const length = node->InputAt(2);
    Node* const effect = node->InputAt(3);
    Node* const control = node->InputAt(4);
    Node* const index =
        machine()->Is64()
            ? graph()->NewNode(machine()->ChangeUint32ToUint64(), offset)
            : offset;

    Node* check = graph()->NewNode(machine()->Uint32LessThan(), offset, length);
    Node* branch =
        graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

    Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
    Node* etrue = graph()->NewNode(machine()->Load(access_type), buffer, index,
                                   effect, if_true);
    Type* element_type =
        Type::Intersect(NodeProperties::GetType(node), Type::Number(), zone());
    Node* vtrue = changer->GetRepresentationFor(
        etrue, access_type.representation(), element_type, node,
        UseInfo(output_rep, Truncation::None()));

    Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
    Node* efalse = effect;
    Node* vfalse;
    if (output_rep == MachineRepresentation::kTagged) {
      vfalse = jsgraph()->UndefinedConstant();
    } else if (output_rep == MachineRepresentation::kFloat64) {
      vfalse =
          jsgraph()->Float64Constant(std::numeric_limits<double>::quiet_NaN());
    } else if (output_rep == MachineRepresentation::kFloat32) {
      vfalse =
          jsgraph()->Float32Constant(std::numeric_limits<float>::quiet_NaN());
    } else {
      vfalse = jsgraph()->Int32Constant(0);
    }

    Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
    Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge);

    // Replace effect uses of {node} with the {ephi}.
    NodeProperties::ReplaceUses(node, node, ephi);

    // Turn the {node} into a Phi.
    node->ReplaceInput(0, vtrue);
    node->ReplaceInput(1, vfalse);
    node->ReplaceInput(2, merge);
    node->TrimInputCount(3);
    NodeProperties::ChangeOp(node, common()->Phi(output_rep, 2));
  } else {
    NodeProperties::ChangeOp(node, machine()->CheckedLoad(access_type));
  }
}


void SimplifiedLowering::DoStoreBuffer(Node* node) {
  DCHECK_EQ(IrOpcode::kStoreBuffer, node->opcode());
  MachineRepresentation const rep =
      BufferAccessOf(node->op()).machine_type().representation();
  NodeProperties::ChangeOp(node, machine()->CheckedStore(rep));
}

Node* SimplifiedLowering::Float64Round(Node* const node) {
  Node* const one = jsgraph()->Float64Constant(1.0);
  Node* const one_half = jsgraph()->Float64Constant(0.5);
  Node* const input = node->InputAt(0);

  // Round up towards Infinity, and adjust if the difference exceeds 0.5.
  Node* result = graph()->NewNode(machine()->Float64RoundUp().placeholder(),
                                  node->InputAt(0));
  return graph()->NewNode(
      common()->Select(MachineRepresentation::kFloat64),
      graph()->NewNode(
          machine()->Float64LessThanOrEqual(),
          graph()->NewNode(machine()->Float64Sub(), result, one_half), input),
      result, graph()->NewNode(machine()->Float64Sub(), result, one));
}

Node* SimplifiedLowering::Float64Sign(Node* const node) {
  Node* const minus_one = jsgraph()->Float64Constant(-1.0);
  Node* const zero = jsgraph()->Float64Constant(0.0);
  Node* const one = jsgraph()->Float64Constant(1.0);

  Node* const input = node->InputAt(0);

  return graph()->NewNode(
      common()->Select(MachineRepresentation::kFloat64),
      graph()->NewNode(machine()->Float64LessThan(), input, zero), minus_one,
      graph()->NewNode(
          common()->Select(MachineRepresentation::kFloat64),
          graph()->NewNode(machine()->Float64LessThan(), zero, input), one,
          input));
}

Node* SimplifiedLowering::Int32Abs(Node* const node) {
  Node* const input = node->InputAt(0);

  // Generate case for absolute integer value.
  //
  //    let sign = input >> 31 in
  //    (input ^ sign) - sign

  Node* sign = graph()->NewNode(machine()->Word32Sar(), input,
                                jsgraph()->Int32Constant(31));
  return graph()->NewNode(machine()->Int32Sub(),
                          graph()->NewNode(machine()->Word32Xor(), input, sign),
                          sign);
}

Node* SimplifiedLowering::Int32Div(Node* const node) {
  Int32BinopMatcher m(node);
  Node* const zero = jsgraph()->Int32Constant(0);
  Node* const minus_one = jsgraph()->Int32Constant(-1);
  Node* const lhs = m.left().node();
  Node* const rhs = m.right().node();

  if (m.right().Is(-1)) {
    return graph()->NewNode(machine()->Int32Sub(), zero, lhs);
  } else if (m.right().Is(0)) {
    return rhs;
  } else if (machine()->Int32DivIsSafe() || m.right().HasValue()) {
    return graph()->NewNode(machine()->Int32Div(), lhs, rhs, graph()->start());
  }

  // General case for signed integer division.
  //
  //    if 0 < rhs then
  //      lhs / rhs
  //    else
  //      if rhs < -1 then
  //        lhs / rhs
  //      else if rhs == 0 then
  //        0
  //      else
  //        0 - lhs
  //
  // Note: We do not use the Diamond helper class here, because it really hurts
  // readability with nested diamonds.
  const Operator* const merge_op = common()->Merge(2);
  const Operator* const phi_op =
      common()->Phi(MachineRepresentation::kWord32, 2);

  Node* check0 = graph()->NewNode(machine()->Int32LessThan(), zero, rhs);
  Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check0,
                                   graph()->start());

  Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
  Node* true0 = graph()->NewNode(machine()->Int32Div(), lhs, rhs, if_true0);

  Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
  Node* false0;
  {
    Node* check1 = graph()->NewNode(machine()->Int32LessThan(), rhs, minus_one);
    Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);

    Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
    Node* true1 = graph()->NewNode(machine()->Int32Div(), lhs, rhs, if_true1);

    Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
    Node* false1;
    {
      Node* check2 = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
      Node* branch2 = graph()->NewNode(common()->Branch(), check2, if_false1);

      Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
      Node* true2 = zero;

      Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
      Node* false2 = graph()->NewNode(machine()->Int32Sub(), zero, lhs);

      if_false1 = graph()->NewNode(merge_op, if_true2, if_false2);
      false1 = graph()->NewNode(phi_op, true2, false2, if_false1);
    }

    if_false0 = graph()->NewNode(merge_op, if_true1, if_false1);
    false0 = graph()->NewNode(phi_op, true1, false1, if_false0);
  }

  Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0);
  return graph()->NewNode(phi_op, true0, false0, merge0);
}


Node* SimplifiedLowering::Int32Mod(Node* const node) {
  Int32BinopMatcher m(node);
  Node* const zero = jsgraph()->Int32Constant(0);
  Node* const minus_one = jsgraph()->Int32Constant(-1);
  Node* const lhs = m.left().node();
  Node* const rhs = m.right().node();

  if (m.right().Is(-1) || m.right().Is(0)) {
    return zero;
  } else if (m.right().HasValue()) {
    return graph()->NewNode(machine()->Int32Mod(), lhs, rhs, graph()->start());
  }

  // General case for signed integer modulus, with optimization for (unknown)
  // power of 2 right hand side.
  //
  //   if 0 < rhs then
  //     msk = rhs - 1
  //     if rhs & msk != 0 then
  //       lhs % rhs
  //     else
  //       if lhs < 0 then
  //         -(-lhs & msk)
  //       else
  //         lhs & msk
  //   else
  //     if rhs < -1 then
  //       lhs % rhs
  //     else
  //       zero
  //
  // Note: We do not use the Diamond helper class here, because it really hurts
  // readability with nested diamonds.
  const Operator* const merge_op = common()->Merge(2);
  const Operator* const phi_op =
      common()->Phi(MachineRepresentation::kWord32, 2);

  Node* check0 = graph()->NewNode(machine()->Int32LessThan(), zero, rhs);
  Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check0,
                                   graph()->start());

  Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
  Node* true0;
  {
    Node* msk = graph()->NewNode(machine()->Int32Add(), rhs, minus_one);

    Node* check1 = graph()->NewNode(machine()->Word32And(), rhs, msk);
    Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_true0);

    Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
    Node* true1 = graph()->NewNode(machine()->Int32Mod(), lhs, rhs, if_true1);

    Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
    Node* false1;
    {
      Node* check2 = graph()->NewNode(machine()->Int32LessThan(), lhs, zero);
      Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
                                       check2, if_false1);

      Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
      Node* true2 = graph()->NewNode(
          machine()->Int32Sub(), zero,
          graph()->NewNode(machine()->Word32And(),
                           graph()->NewNode(machine()->Int32Sub(), zero, lhs),
                           msk));

      Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
      Node* false2 = graph()->NewNode(machine()->Word32And(), lhs, msk);

      if_false1 = graph()->NewNode(merge_op, if_true2, if_false2);
      false1 = graph()->NewNode(phi_op, true2, false2, if_false1);
    }

    if_true0 = graph()->NewNode(merge_op, if_true1, if_false1);
    true0 = graph()->NewNode(phi_op, true1, false1, if_true0);
  }

  Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
  Node* false0;
  {
    Node* check1 = graph()->NewNode(machine()->Int32LessThan(), rhs, minus_one);
    Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
                                     check1, if_false0);

    Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
    Node* true1 = graph()->NewNode(machine()->Int32Mod(), lhs, rhs, if_true1);

    Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
    Node* false1 = zero;

    if_false0 = graph()->NewNode(merge_op, if_true1, if_false1);
    false0 = graph()->NewNode(phi_op, true1, false1, if_false0);
  }

  Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0);
  return graph()->NewNode(phi_op, true0, false0, merge0);
}

Node* SimplifiedLowering::Int32Sign(Node* const node) {
  Node* const minus_one = jsgraph()->Int32Constant(-1);
  Node* const zero = jsgraph()->Int32Constant(0);
  Node* const one = jsgraph()->Int32Constant(1);

  Node* const input = node->InputAt(0);

  return graph()->NewNode(
      common()->Select(MachineRepresentation::kWord32),
      graph()->NewNode(machine()->Int32LessThan(), input, zero), minus_one,
      graph()->NewNode(
          common()->Select(MachineRepresentation::kWord32),
          graph()->NewNode(machine()->Int32LessThan(), zero, input), one,
          zero));
}

Node* SimplifiedLowering::Uint32Div(Node* const node) {
  Uint32BinopMatcher m(node);
  Node* const zero = jsgraph()->Uint32Constant(0);
  Node* const lhs = m.left().node();
  Node* const rhs = m.right().node();

  if (m.right().Is(0)) {
    return zero;
  } else if (machine()->Uint32DivIsSafe() || m.right().HasValue()) {
    return graph()->NewNode(machine()->Uint32Div(), lhs, rhs, graph()->start());
  }

  Node* check = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
  Diamond d(graph(), common(), check, BranchHint::kFalse);
  Node* div = graph()->NewNode(machine()->Uint32Div(), lhs, rhs, d.if_false);
  return d.Phi(MachineRepresentation::kWord32, zero, div);
}


Node* SimplifiedLowering::Uint32Mod(Node* const node) {
  Uint32BinopMatcher m(node);
  Node* const minus_one = jsgraph()->Int32Constant(-1);
  Node* const zero = jsgraph()->Uint32Constant(0);
  Node* const lhs = m.left().node();
  Node* const rhs = m.right().node();

  if (m.right().Is(0)) {
    return zero;
  } else if (m.right().HasValue()) {
    return graph()->NewNode(machine()->Uint32Mod(), lhs, rhs, graph()->start());
  }

  // General case for unsigned integer modulus, with optimization for (unknown)
  // power of 2 right hand side.
  //
  //   if rhs then
  //     msk = rhs - 1
  //     if rhs & msk != 0 then
  //       lhs % rhs
  //     else
  //       lhs & msk
  //   else
  //     zero
  //
  // Note: We do not use the Diamond helper class here, because it really hurts
  // readability with nested diamonds.
  const Operator* const merge_op = common()->Merge(2);
  const Operator* const phi_op =
      common()->Phi(MachineRepresentation::kWord32, 2);

  Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), rhs,
                                   graph()->start());

  Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
  Node* true0;
  {
    Node* msk = graph()->NewNode(machine()->Int32Add(), rhs, minus_one);

    Node* check1 = graph()->NewNode(machine()->Word32And(), rhs, msk);
    Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_true0);

    Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
    Node* true1 = graph()->NewNode(machine()->Uint32Mod(), lhs, rhs, if_true1);

    Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
    Node* false1 = graph()->NewNode(machine()->Word32And(), lhs, msk);

    if_true0 = graph()->NewNode(merge_op, if_true1, if_false1);
    true0 = graph()->NewNode(phi_op, true1, false1, if_true0);
  }

  Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
  Node* false0 = zero;

  Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0);
  return graph()->NewNode(phi_op, true0, false0, merge0);
}

void SimplifiedLowering::DoMax(Node* node, Operator const* op,
                               MachineRepresentation rep) {
  Node* const lhs = node->InputAt(0);
  Node* const rhs = node->InputAt(1);

  node->ReplaceInput(0, graph()->NewNode(op, lhs, rhs));
  DCHECK_EQ(rhs, node->InputAt(1));
  node->AppendInput(graph()->zone(), lhs);
  NodeProperties::ChangeOp(node, common()->Select(rep));
}

void SimplifiedLowering::DoMin(Node* node, Operator const* op,
                               MachineRepresentation rep) {
  Node* const lhs = node->InputAt(0);
  Node* const rhs = node->InputAt(1);

  node->InsertInput(graph()->zone(), 0, graph()->NewNode(op, lhs, rhs));
  DCHECK_EQ(lhs, node->InputAt(1));
  DCHECK_EQ(rhs, node->InputAt(2));
  NodeProperties::ChangeOp(node, common()->Select(rep));
}

void SimplifiedLowering::DoShift(Node* node, Operator const* op,
                                 Type* rhs_type) {
  Node* const rhs = NodeProperties::GetValueInput(node, 1);
  if (!rhs_type->Is(type_cache_.kZeroToThirtyOne)) {
    node->ReplaceInput(1, graph()->NewNode(machine()->Word32And(), rhs,
                                           jsgraph()->Int32Constant(0x1f)));
  }
  DCHECK(op->HasProperty(Operator::kPure));
  ChangeToPureOp(node, op);
}

void SimplifiedLowering::DoStringToNumber(Node* node) {
  Operator::Properties properties = Operator::kEliminatable;
  Callable callable = CodeFactory::StringToNumber(isolate());
  CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
  CallDescriptor* desc = Linkage::GetStubCallDescriptor(
      isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
  node->InsertInput(graph()->zone(), 0,
                    jsgraph()->HeapConstant(callable.code()));
  node->AppendInput(graph()->zone(), jsgraph()->NoContextConstant());
  node->AppendInput(graph()->zone(), graph()->start());
  NodeProperties::ChangeOp(node, common()->Call(desc));
}

void SimplifiedLowering::DoIntegral32ToBit(Node* node) {
  Node* const input = node->InputAt(0);
  Node* const zero = jsgraph()->Int32Constant(0);
  Operator const* const op = machine()->Word32Equal();

  node->ReplaceInput(0, graph()->NewNode(op, input, zero));
  node->AppendInput(graph()->zone(), zero);
  NodeProperties::ChangeOp(node, op);
}

void SimplifiedLowering::DoOrderedNumberToBit(Node* node) {
  Node* const input = node->InputAt(0);

  node->ReplaceInput(0, graph()->NewNode(machine()->Float64Equal(), input,
                                         jsgraph()->Float64Constant(0.0)));
  node->AppendInput(graph()->zone(), jsgraph()->Int32Constant(0));
  NodeProperties::ChangeOp(node, machine()->Word32Equal());
}

void SimplifiedLowering::DoNumberToBit(Node* node) {
  Node* const input = node->InputAt(0);

  node->ReplaceInput(0, jsgraph()->Float64Constant(0.0));
  node->AppendInput(graph()->zone(),
                    graph()->NewNode(machine()->Float64Abs(), input));
  NodeProperties::ChangeOp(node, machine()->Float64LessThan());
}

void SimplifiedLowering::DoIntegerToUint8Clamped(Node* node) {
  Node* const input = node->InputAt(0);
  Node* const min = jsgraph()->Float64Constant(0.0);
  Node* const max = jsgraph()->Float64Constant(255.0);

  node->ReplaceInput(
      0, graph()->NewNode(machine()->Float64LessThan(), min, input));
  node->AppendInput(
      graph()->zone(),
      graph()->NewNode(
          common()->Select(MachineRepresentation::kFloat64),
          graph()->NewNode(machine()->Float64LessThan(), input, max), input,
          max));
  node->AppendInput(graph()->zone(), min);
  NodeProperties::ChangeOp(node,
                           common()->Select(MachineRepresentation::kFloat64));
}

void SimplifiedLowering::DoNumberToUint8Clamped(Node* node) {
  Node* const input = node->InputAt(0);
  Node* const min = jsgraph()->Float64Constant(0.0);
  Node* const max = jsgraph()->Float64Constant(255.0);

  node->ReplaceInput(
      0, graph()->NewNode(
             common()->Select(MachineRepresentation::kFloat64),
             graph()->NewNode(machine()->Float64LessThan(), min, input),
             graph()->NewNode(
                 common()->Select(MachineRepresentation::kFloat64),
                 graph()->NewNode(machine()->Float64LessThan(), input, max),
                 input, max),
             min));
  NodeProperties::ChangeOp(node,
                           machine()->Float64RoundTiesEven().placeholder());
}

void SimplifiedLowering::DoSigned32ToUint8Clamped(Node* node) {
  Node* const input = node->InputAt(0);
  Node* const min = jsgraph()->Int32Constant(0);
  Node* const max = jsgraph()->Int32Constant(255);

  node->ReplaceInput(
      0, graph()->NewNode(machine()->Int32LessThanOrEqual(), input, max));
  node->AppendInput(
      graph()->zone(),
      graph()->NewNode(common()->Select(MachineRepresentation::kWord32),
                       graph()->NewNode(machine()->Int32LessThan(), input, min),
                       min, input));
  node->AppendInput(graph()->zone(), max);
  NodeProperties::ChangeOp(node,
                           common()->Select(MachineRepresentation::kWord32));
}

void SimplifiedLowering::DoUnsigned32ToUint8Clamped(Node* node) {
  Node* const input = node->InputAt(0);
  Node* const max = jsgraph()->Uint32Constant(255u);

  node->ReplaceInput(
      0, graph()->NewNode(machine()->Uint32LessThanOrEqual(), input, max));
  node->AppendInput(graph()->zone(), input);
  node->AppendInput(graph()->zone(), max);
  NodeProperties::ChangeOp(node,
                           common()->Select(MachineRepresentation::kWord32));
}

Node* SimplifiedLowering::ToNumberCode() {
  if (!to_number_code_.is_set()) {
    Callable callable = CodeFactory::ToNumber(isolate());
    to_number_code_.set(jsgraph()->HeapConstant(callable.code()));
  }
  return to_number_code_.get();
}

Operator const* SimplifiedLowering::ToNumberOperator() {
  if (!to_number_operator_.is_set()) {
    Callable callable = CodeFactory::ToNumber(isolate());
    CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
    CallDescriptor* desc = Linkage::GetStubCallDescriptor(
        isolate(), graph()->zone(), callable.descriptor(), 0, flags,
        Operator::kNoProperties);
    to_number_operator_.set(common()->Call(desc));
  }
  return to_number_operator_.get();
}

}  // namespace compiler
}  // namespace internal
}  // namespace v8