text: // 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/base/adapters.h"
  #include "src/base/bits.h"
  #include "src/compiler/instruction-selector-impl.h"
  #include "src/compiler/node-matchers.h"
  #include "src/compiler/node-properties.h"
  
  namespace v8 {
  namespace internal {
  namespace compiler {
  
  #define TRACE_UNIMPL() \
    PrintF("UNIMPLEMENTED instr_sel: %s at line %d\n", __FUNCTION__, __LINE__)
  
  #define TRACE() PrintF("instr_sel: %s at line %d\n", __FUNCTION__, __LINE__)
  
  
  // Adds Mips-specific methods for generating InstructionOperands.
  class Mips64OperandGenerator final : public OperandGenerator {
   public:
    explicit Mips64OperandGenerator(InstructionSelector* selector)
        : OperandGenerator(selector) {}
  
    InstructionOperand UseOperand(Node* node, InstructionCode opcode) {
      if (CanBeImmediate(node, opcode)) {
        return UseImmediate(node);
      }
      return UseRegister(node);
    }
  
    bool CanBeImmediate(Node* node, InstructionCode opcode) {
      int64_t value;
      if (node->opcode() == IrOpcode::kInt32Constant)
        value = OpParameter<int32_t>(node);
      else if (node->opcode() == IrOpcode::kInt64Constant)
        value = OpParameter<int64_t>(node);
      else
        return false;
      switch (ArchOpcodeField::decode(opcode)) {
        case kMips64Shl:
        case kMips64Sar:
        case kMips64Shr:
          return is_uint5(value);
        case kMips64Dshl:
        case kMips64Dsar:
        case kMips64Dshr:
          return is_uint6(value);
        case kMips64Xor:
          return is_uint16(value);
        case kMips64Ldc1:
        case kMips64Sdc1:
          return is_int16(value + kIntSize);
        default:
          return is_int16(value);
      }
    }
  
   private:
    bool ImmediateFitsAddrMode1Instruction(int32_t imm) const {
      TRACE_UNIMPL();
      return false;
    }
  };
  
  
  static void VisitRR(InstructionSelector* selector, ArchOpcode opcode,
                      Node* node) {
    Mips64OperandGenerator g(selector);
    selector->Emit(opcode, g.DefineAsRegister(node),
                   g.UseRegister(node->InputAt(0)));
  }
  
  
  static void VisitRRR(InstructionSelector* selector, ArchOpcode opcode,
                       Node* node) {
    Mips64OperandGenerator g(selector);
    selector->Emit(opcode, g.DefineAsRegister(node),
                   g.UseRegister(node->InputAt(0)),
                   g.UseRegister(node->InputAt(1)));
  }
  
  
  static void VisitRRO(InstructionSelector* selector, ArchOpcode opcode,
                       Node* node) {
    Mips64OperandGenerator g(selector);
    selector->Emit(opcode, g.DefineAsRegister(node),
                   g.UseRegister(node->InputAt(0)),
                   g.UseOperand(node->InputAt(1), opcode));
  }
  
  
  static void VisitBinop(InstructionSelector* selector, Node* node,
                         InstructionCode opcode, FlagsContinuation* cont) {
    Mips64OperandGenerator g(selector);
    Int32BinopMatcher m(node);
    InstructionOperand inputs[4];
    size_t input_count = 0;
    InstructionOperand outputs[2];
    size_t output_count = 0;
  
    inputs[input_count++] = g.UseRegister(m.left().node());
    inputs[input_count++] = g.UseOperand(m.right().node(), opcode);
  
    if (cont->IsBranch()) {
      inputs[input_count++] = g.Label(cont->true_block());
      inputs[input_count++] = g.Label(cont->false_block());
    }
  
    outputs[output_count++] = g.DefineAsRegister(node);
    if (cont->IsSet()) {
      outputs[output_count++] = g.DefineAsRegister(cont->result());
    }
  
    DCHECK_NE(0u, input_count);
    DCHECK_NE(0u, output_count);
    DCHECK_GE(arraysize(inputs), input_count);
    DCHECK_GE(arraysize(outputs), output_count);
  
    selector->Emit(cont->Encode(opcode), output_count, outputs, input_count,
                   inputs);
  }
  
  
  static void VisitBinop(InstructionSelector* selector, Node* node,
                         InstructionCode opcode) {
    FlagsContinuation cont;
    VisitBinop(selector, node, opcode, &cont);
  }
  
  
  void InstructionSelector::VisitLoad(Node* node) {
    MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
    MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
    Mips64OperandGenerator g(this);
    Node* base = node->InputAt(0);
    Node* index = node->InputAt(1);
  
    ArchOpcode opcode;
    switch (rep) {
      case kRepFloat32:
        opcode = kMips64Lwc1;
        break;
      case kRepFloat64:
        opcode = kMips64Ldc1;
        break;
      case kRepBit:  // Fall through.
      case kRepWord8:
        opcode = typ == kTypeUint32 ? kMips64Lbu : kMips64Lb;
        break;
      case kRepWord16:
        opcode = typ == kTypeUint32 ? kMips64Lhu : kMips64Lh;
        break;
      case kRepWord32:
        opcode = kMips64Lw;
        break;
      case kRepTagged:  // Fall through.
      case kRepWord64:
        opcode = kMips64Ld;
        break;
      default:
        UNREACHABLE();
        return;
    }
  
    if (g.CanBeImmediate(index, opcode)) {
      Emit(opcode | AddressingModeField::encode(kMode_MRI),
           g.DefineAsRegister(node), g.UseRegister(base), g.UseImmediate(index));
    } else {
      InstructionOperand addr_reg = g.TempRegister();
      Emit(kMips64Dadd | AddressingModeField::encode(kMode_None), addr_reg,
           g.UseRegister(index), g.UseRegister(base));
      // Emit desired load opcode, using temp addr_reg.
      Emit(opcode | AddressingModeField::encode(kMode_MRI),
           g.DefineAsRegister(node), addr_reg, g.TempImmediate(0));
    }
  }
  
  
  void InstructionSelector::VisitStore(Node* node) {
    Mips64OperandGenerator g(this);
    Node* base = node->InputAt(0);
    Node* index = node->InputAt(1);
    Node* value = node->InputAt(2);
  
    StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
    MachineType rep = RepresentationOf(store_rep.machine_type());
    if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
      DCHECK(rep == kRepTagged);
      // TODO(dcarney): refactor RecordWrite function to take temp registers
      //                and pass them here instead of using fixed regs
      // TODO(dcarney): handle immediate indices.
      InstructionOperand temps[] = {g.TempRegister(t1), g.TempRegister(t2)};
      Emit(kMips64StoreWriteBarrier, g.NoOutput(), g.UseFixed(base, t0),
           g.UseFixed(index, t1), g.UseFixed(value, t2), arraysize(temps), temps);
      return;
    }
    DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
  
    ArchOpcode opcode;
    switch (rep) {
      case kRepFloat32:
        opcode = kMips64Swc1;
        break;
      case kRepFloat64:
        opcode = kMips64Sdc1;
        break;
      case kRepBit:  // Fall through.
      case kRepWord8:
        opcode = kMips64Sb;
        break;
      case kRepWord16:
        opcode = kMips64Sh;
        break;
      case kRepWord32:
        opcode = kMips64Sw;
        break;
      case kRepTagged:  // Fall through.
      case kRepWord64:
        opcode = kMips64Sd;
        break;
      default:
        UNREACHABLE();
        return;
    }
  
    if (g.CanBeImmediate(index, opcode)) {
      Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
           g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value));
    } else {
      InstructionOperand addr_reg = g.TempRegister();
      Emit(kMips64Dadd | AddressingModeField::encode(kMode_None), addr_reg,
           g.UseRegister(index), g.UseRegister(base));
      // Emit desired store opcode, using temp addr_reg.
      Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
           addr_reg, g.TempImmediate(0), g.UseRegister(value));
    }
  }
  
  
  void InstructionSelector::VisitWord32And(Node* node) {
    VisitBinop(this, node, kMips64And);
  }
  
  
  void InstructionSelector::VisitWord64And(Node* node) {
    VisitBinop(this, node, kMips64And);
  }
  
  
  void InstructionSelector::VisitWord32Or(Node* node) {
    VisitBinop(this, node, kMips64Or);
  }
  
  
  void InstructionSelector::VisitWord64Or(Node* node) {
    VisitBinop(this, node, kMips64Or);
  }
  
  
  void InstructionSelector::VisitWord32Xor(Node* node) {
    VisitBinop(this, node, kMips64Xor);
  }
  
  
  void InstructionSelector::VisitWord64Xor(Node* node) {
    VisitBinop(this, node, kMips64Xor);
  }
  
  
  void InstructionSelector::VisitWord32Shl(Node* node) {
    VisitRRO(this, kMips64Shl, node);
  }
  
  
  void InstructionSelector::VisitWord32Shr(Node* node) {
    VisitRRO(this, kMips64Shr, node);
  }
  
  
  void InstructionSelector::VisitWord32Sar(Node* node) {
    VisitRRO(this, kMips64Sar, node);
  }
  
  
  void InstructionSelector::VisitWord64Shl(Node* node) {
    Mips64OperandGenerator g(this);
    Int64BinopMatcher m(node);
    if ((m.left().IsChangeInt32ToInt64() || m.left().IsChangeUint32ToUint64()) &&
        m.right().IsInRange(32, 63)) {
      // There's no need to sign/zero-extend to 64-bit if we shift out the upper
      // 32 bits anyway.
      Emit(kMips64Dshl, g.DefineSameAsFirst(node),
           g.UseRegister(m.left().node()->InputAt(0)),
           g.UseImmediate(m.right().node()));
      return;
    }
    VisitRRO(this, kMips64Dshl, node);
  }
  
  
  void InstructionSelector::VisitWord64Shr(Node* node) {
    VisitRRO(this, kMips64Dshr, node);
  }
  
  
  void InstructionSelector::VisitWord64Sar(Node* node) {
    VisitRRO(this, kMips64Dsar, node);
  }
  
  
  void InstructionSelector::VisitWord32Ror(Node* node) {
    VisitRRO(this, kMips64Ror, node);
  }
  
  
  void InstructionSelector::VisitWord32Clz(Node* node) {
    VisitRR(this, kMips64Clz, node);
  }
  
  
  void InstructionSelector::VisitWord64Ror(Node* node) {
    VisitRRO(this, kMips64Dror, node);
  }
  
  
  void InstructionSelector::VisitInt32Add(Node* node) {
    Mips64OperandGenerator g(this);
    // TODO(plind): Consider multiply & add optimization from arm port.
    VisitBinop(this, node, kMips64Add);
  }
  
  
  void InstructionSelector::VisitInt64Add(Node* node) {
    Mips64OperandGenerator g(this);
    // TODO(plind): Consider multiply & add optimization from arm port.
    VisitBinop(this, node, kMips64Dadd);
  }
  
  
  void InstructionSelector::VisitInt32Sub(Node* node) {
    VisitBinop(this, node, kMips64Sub);
  }
  
  
  void InstructionSelector::VisitInt64Sub(Node* node) {
    VisitBinop(this, node, kMips64Dsub);
  }
  
  
  void InstructionSelector::VisitInt32Mul(Node* node) {
    Mips64OperandGenerator g(this);
    Int32BinopMatcher m(node);
    if (m.right().HasValue() && m.right().Value() > 0) {
      int32_t value = m.right().Value();
      if (base::bits::IsPowerOfTwo32(value)) {
        Emit(kMips64Shl | AddressingModeField::encode(kMode_None),
             g.DefineAsRegister(node), g.UseRegister(m.left().node()),
             g.TempImmediate(WhichPowerOf2(value)));
        return;
      }
      if (base::bits::IsPowerOfTwo32(value - 1)) {
        InstructionOperand temp = g.TempRegister();
        Emit(kMips64Shl | AddressingModeField::encode(kMode_None), temp,
             g.UseRegister(m.left().node()),
             g.TempImmediate(WhichPowerOf2(value - 1)));
        Emit(kMips64Add | AddressingModeField::encode(kMode_None),
             g.DefineAsRegister(node), g.UseRegister(m.left().node()), temp);
        return;
      }
      if (base::bits::IsPowerOfTwo32(value + 1)) {
        InstructionOperand temp = g.TempRegister();
        Emit(kMips64Shl | AddressingModeField::encode(kMode_None), temp,
             g.UseRegister(m.left().node()),
             g.TempImmediate(WhichPowerOf2(value + 1)));
        Emit(kMips64Sub | AddressingModeField::encode(kMode_None),
             g.DefineAsRegister(node), temp, g.UseRegister(m.left().node()));
        return;
      }
    }
    VisitRRR(this, kMips64Mul, node);
  }
  
  
  void InstructionSelector::VisitInt32MulHigh(Node* node) {
    VisitRRR(this, kMips64MulHigh, node);
  }
  
  
  void InstructionSelector::VisitUint32MulHigh(Node* node) {
    Mips64OperandGenerator g(this);
    InstructionOperand const dmul_operand = g.TempRegister();
    Emit(kMips64MulHighU, dmul_operand, g.UseRegister(node->InputAt(0)),
         g.UseRegister(node->InputAt(1)));
    Emit(kMips64Ext, g.DefineAsRegister(node), dmul_operand, g.TempImmediate(0),
         g.TempImmediate(32));
  }
  
  
  void InstructionSelector::VisitInt64Mul(Node* node) {
    Mips64OperandGenerator g(this);
    Int64BinopMatcher m(node);
    // TODO(dusmil): Add optimization for shifts larger than 32.
    if (m.right().HasValue() && m.right().Value() > 0) {
      int32_t value = static_cast<int32_t>(m.right().Value());
      if (base::bits::IsPowerOfTwo32(value)) {
        Emit(kMips64Dshl | AddressingModeField::encode(kMode_None),
             g.DefineAsRegister(node), g.UseRegister(m.left().node()),
             g.TempImmediate(WhichPowerOf2(value)));
        return;
      }
      if (base::bits::IsPowerOfTwo32(value - 1)) {
        InstructionOperand temp = g.TempRegister();
        Emit(kMips64Dshl | AddressingModeField::encode(kMode_None), temp,
             g.UseRegister(m.left().node()),
             g.TempImmediate(WhichPowerOf2(value - 1)));
        Emit(kMips64Dadd | AddressingModeField::encode(kMode_None),
             g.DefineAsRegister(node), g.UseRegister(m.left().node()), temp);
        return;
      }
      if (base::bits::IsPowerOfTwo32(value + 1)) {
        InstructionOperand temp = g.TempRegister();
        Emit(kMips64Dshl | AddressingModeField::encode(kMode_None), temp,
             g.UseRegister(m.left().node()),
             g.TempImmediate(WhichPowerOf2(value + 1)));
        Emit(kMips64Dsub | AddressingModeField::encode(kMode_None),
             g.DefineAsRegister(node), temp, g.UseRegister(m.left().node()));
        return;
      }
    }
    Emit(kMips64Dmul, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
         g.UseRegister(m.right().node()));
  }
  
  
  void InstructionSelector::VisitInt32Div(Node* node) {
    Mips64OperandGenerator g(this);
    Int32BinopMatcher m(node);
    Emit(kMips64Div, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
         g.UseRegister(m.right().node()));
  }
  
  
  void InstructionSelector::VisitUint32Div(Node* node) {
    Mips64OperandGenerator g(this);
    Int32BinopMatcher m(node);
    Emit(kMips64DivU, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
         g.UseRegister(m.right().node()));
  }
  
  
  void InstructionSelector::VisitInt32Mod(Node* node) {
    Mips64OperandGenerator g(this);
    Int32BinopMatcher m(node);
    Emit(kMips64Mod, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
         g.UseRegister(m.right().node()));
  }
  
  
  void InstructionSelector::VisitUint32Mod(Node* node) {
    Mips64OperandGenerator g(this);
    Int32BinopMatcher m(node);
    Emit(kMips64ModU, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
         g.UseRegister(m.right().node()));
  }
  
  
  void InstructionSelector::VisitInt64Div(Node* node) {
    Mips64OperandGenerator g(this);
    Int64BinopMatcher m(node);
    Emit(kMips64Ddiv, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
         g.UseRegister(m.right().node()));
  }
  
  
  void InstructionSelector::VisitUint64Div(Node* node) {
    Mips64OperandGenerator g(this);
    Int64BinopMatcher m(node);
    Emit(kMips64DdivU, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
         g.UseRegister(m.right().node()));
  }
  
  
  void InstructionSelector::VisitInt64Mod(Node* node) {
    Mips64OperandGenerator g(this);
    Int64BinopMatcher m(node);
    Emit(kMips64Dmod, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
         g.UseRegister(m.right().node()));
  }
  
  
  void InstructionSelector::VisitUint64Mod(Node* node) {
    Mips64OperandGenerator g(this);
    Int64BinopMatcher m(node);
    Emit(kMips64DmodU, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
         g.UseRegister(m.right().node()));
  }
  
  
  void InstructionSelector::VisitChangeFloat32ToFloat64(Node* node) {
    VisitRR(this, kMips64CvtDS, node);
  }
  
  
  void InstructionSelector::VisitChangeInt32ToFloat64(Node* node) {
    VisitRR(this, kMips64CvtDW, node);
  }
  
  
  void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) {
    VisitRR(this, kMips64CvtDUw, node);
  }
  
  
  void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) {
    VisitRR(this, kMips64TruncWD, node);
  }
  
  
  void InstructionSelector::VisitChangeFloat64ToUint32(Node* node) {
    VisitRR(this, kMips64TruncUwD, node);
  }
  
  
  void InstructionSelector::VisitChangeInt32ToInt64(Node* node) {
    Mips64OperandGenerator g(this);
    Emit(kMips64Shl, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)),
         g.TempImmediate(0));
  }
  
  
  void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
    Mips64OperandGenerator g(this);
    Emit(kMips64Dext, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)),
         g.TempImmediate(0), g.TempImmediate(32));
  }
  
  
  void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
    Mips64OperandGenerator g(this);
    Node* value = node->InputAt(0);
    if (CanCover(node, value)) {
      switch (value->opcode()) {
        case IrOpcode::kWord64Sar: {
          Int64BinopMatcher m(value);
          if (m.right().IsInRange(32, 63)) {
            // After smi untagging no need for truncate. Combine sequence.
            Emit(kMips64Dsar, g.DefineSameAsFirst(node),
                 g.UseRegister(m.left().node()), g.TempImmediate(kSmiShift));
            return;
          }
          break;
        }
        default:
          break;
      }
    }
    Emit(kMips64Ext, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)),
         g.TempImmediate(0), g.TempImmediate(32));
  }
  
  
  void InstructionSelector::VisitTruncateFloat64ToFloat32(Node* node) {
    VisitRR(this, kMips64CvtSD, node);
  }
  
  
  void InstructionSelector::VisitTruncateFloat64ToInt32(Node* node) {
    switch (TruncationModeOf(node->op())) {
      case TruncationMode::kJavaScript:
        return VisitRR(this, kArchTruncateDoubleToI, node);
      case TruncationMode::kRoundToZero:
        return VisitRR(this, kMips64TruncWD, node);
    }
    UNREACHABLE();
  }
  
  
  void InstructionSelector::VisitBitcastFloat32ToInt32(Node* node) {
    VisitRR(this, kMips64Float64ExtractLowWord32, node);
  }
  
  
  void InstructionSelector::VisitBitcastFloat64ToInt64(Node* node) {
    VisitRR(this, kMips64BitcastDL, node);
  }
  
  
  void InstructionSelector::VisitBitcastInt32ToFloat32(Node* node) {
    Mips64OperandGenerator g(this);
    Emit(kMips64Float64InsertLowWord32, g.DefineAsRegister(node),
         ImmediateOperand(ImmediateOperand::INLINE, 0),
         g.UseRegister(node->InputAt(0)));
  }
  
  
  void InstructionSelector::VisitBitcastInt64ToFloat64(Node* node) {
    VisitRR(this, kMips64BitcastLD, node);
  }
  
  
  void InstructionSelector::VisitFloat32Add(Node* node) {
    VisitRRR(this, kMips64AddS, node);
  }
  
  
  void InstructionSelector::VisitFloat64Add(Node* node) {
    VisitRRR(this, kMips64AddD, node);
  }
  
  
  void InstructionSelector::VisitFloat32Sub(Node* node) {
    VisitRRR(this, kMips64SubS, node);
  }
  
  
  void InstructionSelector::VisitFloat64Sub(Node* node) {
    Mips64OperandGenerator g(this);
    Float64BinopMatcher m(node);
    if (m.left().IsMinusZero() && m.right().IsFloat64RoundDown() &&
        CanCover(m.node(), m.right().node())) {
      if (m.right().InputAt(0)->opcode() == IrOpcode::kFloat64Sub &&
          CanCover(m.right().node(), m.right().InputAt(0))) {
        Float64BinopMatcher mright0(m.right().InputAt(0));
        if (mright0.left().IsMinusZero()) {
          Emit(kMips64Float64RoundUp, g.DefineAsRegister(node),
               g.UseRegister(mright0.right().node()));
          return;
        }
      }
    }
    VisitRRR(this, kMips64SubD, node);
  }
  
  
  void InstructionSelector::VisitFloat32Mul(Node* node) {
    VisitRRR(this, kMips64MulS, node);
  }
  
  
  void InstructionSelector::VisitFloat64Mul(Node* node) {
    VisitRRR(this, kMips64MulD, node);
  }
  
  
  void InstructionSelector::VisitFloat32Div(Node* node) {
    VisitRRR(this, kMips64DivS, node);
  }
  
  
  void InstructionSelector::VisitFloat64Div(Node* node) {
    VisitRRR(this, kMips64DivD, node);
  }
  
  
  void InstructionSelector::VisitFloat64Mod(Node* node) {
    Mips64OperandGenerator g(this);
    Emit(kMips64ModD, g.DefineAsFixed(node, f0),
         g.UseFixed(node->InputAt(0), f12),
         g.UseFixed(node->InputAt(1), f14))->MarkAsCall();
  }
  
  
  void InstructionSelector::VisitFloat32Max(Node* node) { UNREACHABLE(); }
  
  
  void InstructionSelector::VisitFloat64Max(Node* node) { UNREACHABLE(); }
  
  
  void InstructionSelector::VisitFloat32Min(Node* node) { UNREACHABLE(); }
  
  
  void InstructionSelector::VisitFloat64Min(Node* node) { UNREACHABLE(); }
  
  
  void InstructionSelector::VisitFloat32Abs(Node* node) {
    VisitRR(this, kMips64AbsS, node);
  }
  
  
  void InstructionSelector::VisitFloat64Abs(Node* node) {
    VisitRR(this, kMips64AbsD, node);
  }
  
  
  void InstructionSelector::VisitFloat32Sqrt(Node* node) {
    VisitRR(this, kMips64SqrtS, node);
  }
  
  
  void InstructionSelector::VisitFloat64Sqrt(Node* node) {
    VisitRR(this, kMips64SqrtD, node);
  }
  
  
  void InstructionSelector::VisitFloat64RoundDown(Node* node) {
    VisitRR(this, kMips64Float64RoundDown, node);
  }
  
  
  void InstructionSelector::VisitFloat64RoundTruncate(Node* node) {
    VisitRR(this, kMips64Float64RoundTruncate, node);
  }
  
  
  void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) {
    UNREACHABLE();
  }
  
  
  void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
    Mips64OperandGenerator g(this);
    const CallDescriptor* descriptor = OpParameter<const CallDescriptor*>(node);
  
    FrameStateDescriptor* frame_state_descriptor = nullptr;
    if (descriptor->NeedsFrameState()) {
      frame_state_descriptor = GetFrameStateDescriptor(
          node->InputAt(static_cast<int>(descriptor->InputCount())));
    }
  
    CallBuffer buffer(zone(), descriptor, frame_state_descriptor);
  
    // Compute InstructionOperands for inputs and outputs.
    InitializeCallBuffer(node, &buffer, true, true);
  
    // Prepare for C function call.
    if (descriptor->IsCFunctionCall()) {
      Emit(kArchPrepareCallCFunction |
               MiscField::encode(static_cast<int>(descriptor->CParameterCount())),
           0, nullptr, 0, nullptr);
  
      // Poke any stack arguments.
      int slot = kCArgSlotCount;
      for (Node* input : buffer.pushed_nodes) {
        Emit(kMips64StoreToStackSlot, g.NoOutput(), g.UseRegister(input),
             g.TempImmediate(slot << kPointerSizeLog2));
        ++slot;
      }
    } else {
      int push_count = static_cast<int>(descriptor->StackParameterCount());
      if (push_count > 0) {
        Emit(kMips64StackClaim, g.NoOutput(),
             g.TempImmediate(push_count << kPointerSizeLog2));
      }
      for (size_t n = 0; n < buffer.pushed_nodes.size(); ++n) {
        if (Node* input = buffer.pushed_nodes[n]) {
          Emit(kMips64StoreToStackSlot, g.NoOutput(), g.UseRegister(input),
               g.TempImmediate(static_cast<int>(n << kPointerSizeLog2)));
        }
      }
    }
  
    // Pass label of exception handler block.
    CallDescriptor::Flags flags = descriptor->flags();
    if (handler) {
      DCHECK_EQ(IrOpcode::kIfException, handler->front()->opcode());
      IfExceptionHint hint = OpParameter<IfExceptionHint>(handler->front());
      if (hint == IfExceptionHint::kLocallyCaught) {
        flags |= CallDescriptor::kHasLocalCatchHandler;
      }
      flags |= CallDescriptor::kHasExceptionHandler;
      buffer.instruction_args.push_back(g.Label(handler));
    }
  
    // Select the appropriate opcode based on the call type.
    InstructionCode opcode;
    switch (descriptor->kind()) {
      case CallDescriptor::kCallAddress:
        opcode =
            kArchCallCFunction |
            MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
        break;
      case CallDescriptor::kCallCodeObject:
        opcode = kArchCallCodeObject | MiscField::encode(flags);
        break;
      case CallDescriptor::kCallJSFunction:
        opcode = kArchCallJSFunction | MiscField::encode(flags);
        break;
      default:
        UNREACHABLE();
        return;
    }
    opcode |= MiscField::encode(flags);
  
    // Emit the call instruction.
    size_t const output_count = buffer.outputs.size();
    auto* outputs = output_count ? &buffer.outputs.front() : nullptr;
    Emit(opcode, output_count, outputs, buffer.instruction_args.size(),
         &buffer.instruction_args.front())->MarkAsCall();
  }
  
  
  void InstructionSelector::VisitTailCall(Node* node) {
    Mips64OperandGenerator g(this);
    const CallDescriptor* descriptor = OpParameter<const CallDescriptor*>(node);
    DCHECK_NE(0, descriptor->flags() & CallDescriptor::kSupportsTailCalls);
    DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kPatchableCallSite);
    DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kNeedsNopAfterCall);
  
    // TODO(turbofan): Relax restriction for stack parameters.
    if (linkage()->GetIncomingDescriptor()->CanTailCall(node)) {
      CallBuffer buffer(zone(), descriptor, nullptr);
  
      // Compute InstructionOperands for inputs and outputs.
      InitializeCallBuffer(node, &buffer, true, false);
  
      // Select the appropriate opcode based on the call type.
      InstructionCode opcode;
      switch (descriptor->kind()) {
        case CallDescriptor::kCallCodeObject:
          opcode = kArchTailCallCodeObject;
          break;
        case CallDescriptor::kCallJSFunction:
          opcode = kArchTailCallJSFunction;
          break;
        default:
          UNREACHABLE();
          return;
      }
      opcode |= MiscField::encode(descriptor->flags());
  
      // Emit the tailcall instruction.
      Emit(opcode, 0, nullptr, buffer.instruction_args.size(),
           &buffer.instruction_args.front());
    } else {
      FrameStateDescriptor* frame_state_descriptor =
          descriptor->NeedsFrameState()
              ? GetFrameStateDescriptor(
                    node->InputAt(static_cast<int>(descriptor->InputCount())))
              : nullptr;
  
      CallBuffer buffer(zone(), descriptor, frame_state_descriptor);
  
      // Compute InstructionOperands for inputs and outputs.
      InitializeCallBuffer(node, &buffer, true, false);
  
      const int32_t push_count = static_cast<int32_t>(buffer.pushed_nodes.size());
      if (push_count > 0) {
        Emit(kMips64StackClaim, g.NoOutput(),
             g.TempImmediate(push_count << kPointerSizeLog2));
      }
      int slot = push_count - 1;
      for (Node* input : base::Reversed(buffer.pushed_nodes)) {
        Emit(kMips64StoreToStackSlot, g.NoOutput(), g.UseRegister(input),
             g.TempImmediate(slot << kPointerSizeLog2));
        slot--;
      }
  
      // Select the appropriate opcode based on the call type.
      InstructionCode opcode;
      switch (descriptor->kind()) {
        case CallDescriptor::kCallCodeObject: {
          opcode = kArchCallCodeObject;
          break;
        }
        case CallDescriptor::kCallJSFunction:
          opcode = kArchCallJSFunction;
          break;
        default:
          UNREACHABLE();
          return;
      }
      opcode |= MiscField::encode(descriptor->flags());
  
      // Emit the call instruction.
      size_t const output_count = buffer.outputs.size();
      auto* outputs = output_count ? &buffer.outputs.front() : nullptr;
      Emit(opcode, output_count, outputs, buffer.instruction_args.size(),
           &buffer.instruction_args.front())->MarkAsCall();
      Emit(kArchRet, 0, nullptr, output_count, outputs);
    }
  }
  
  
  void InstructionSelector::VisitCheckedLoad(Node* node) {
    MachineType rep = RepresentationOf(OpParameter<MachineType>(node));
    MachineType typ = TypeOf(OpParameter<MachineType>(node));
    Mips64OperandGenerator g(this);
    Node* const buffer = node->InputAt(0);
    Node* const offset = node->InputAt(1);
    Node* const length = node->InputAt(2);
    ArchOpcode opcode;
    switch (rep) {
      case kRepWord8:
        opcode = typ == kTypeInt32 ? kCheckedLoadInt8 : kCheckedLoadUint8;
        break;
      case kRepWord16:
        opcode = typ == kTypeInt32 ? kCheckedLoadInt16 : kCheckedLoadUint16;
        break;
      case kRepWord32:
        opcode = kCheckedLoadWord32;
        break;
      case kRepWord64:
        opcode = kCheckedLoadWord64;
        break;
      case kRepFloat32:
        opcode = kCheckedLoadFloat32;
        break;
      case kRepFloat64:
        opcode = kCheckedLoadFloat64;
        break;
      default:
        UNREACHABLE();
        return;
    }
    InstructionOperand offset_operand = g.CanBeImmediate(offset, opcode)
                                            ? g.UseImmediate(offset)
                                            : g.UseRegister(offset);
  
    InstructionOperand length_operand = (!g.CanBeImmediate(offset, opcode))
                                            ? g.CanBeImmediate(length, opcode)
                                                  ? g.UseImmediate(length)
                                                  : g.UseRegister(length)
                                            : g.UseRegister(length);
  
    Emit(opcode | AddressingModeField::encode(kMode_MRI),
         g.DefineAsRegister(node), offset_operand, length_operand,
         g.UseRegister(buffer));
  }
  
  
  void InstructionSelector::VisitCheckedStore(Node* node) {
    MachineType rep = RepresentationOf(OpParameter<MachineType>(node));
    Mips64OperandGenerator g(this);
    Node* const buffer = node->InputAt(0);
    Node* const offset = node->InputAt(1);
    Node* const length = node->InputAt(2);
    Node* const value = node->InputAt(3);
    ArchOpcode opcode;
    switch (rep) {
      case kRepWord8:
        opcode = kCheckedStoreWord8;
        break;
      case kRepWord16:
        opcode = kCheckedStoreWord16;
        break;
      case kRepWord32:
        opcode = kCheckedStoreWord32;
        break;
      case kRepWord64:
        opcode = kCheckedStoreWord64;
        break;
      case kRepFloat32:
        opcode = kCheckedStoreFloat32;
        break;
      case kRepFloat64:
        opcode = kCheckedStoreFloat64;
        break;
      default:
        UNREACHABLE();
        return;
    }
    InstructionOperand offset_operand = g.CanBeImmediate(offset, opcode)
                                            ? g.UseImmediate(offset)
                                            : g.UseRegister(offset);
  
    InstructionOperand length_operand = (!g.CanBeImmediate(offset, opcode))
                                            ? g.CanBeImmediate(length, opcode)
                                                  ? g.UseImmediate(length)
                                                  : g.UseRegister(length)
                                            : g.UseRegister(length);
  
    Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
         offset_operand, length_operand, g.UseRegister(value),
         g.UseRegister(buffer));
  }
  
  
  namespace {
  
  // Shared routine for multiple compare operations.
  static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
                           InstructionOperand left, InstructionOperand right,
                           FlagsContinuation* cont) {
    Mips64OperandGenerator g(selector);
    opcode = cont->Encode(opcode);
    if (cont->IsBranch()) {
      selector->Emit(opcode, g.NoOutput(), left, right,
                     g.Label(cont->true_block()), g.Label(cont->false_block()));
    } else {
      DCHECK(cont->IsSet());
      selector->Emit(opcode, g.DefineAsRegister(cont->result()), left, right);
    }
  }
  
  
  // Shared routine for multiple float32 compare operations.
  void VisitFloat32Compare(InstructionSelector* selector, Node* node,
                           FlagsContinuation* cont) {
    Mips64OperandGenerator g(selector);
    Node* left = node->InputAt(0);
    Node* right = node->InputAt(1);
    VisitCompare(selector, kMips64CmpS, g.UseRegister(left), g.UseRegister(right),
                 cont);
  }
  
  
  // Shared routine for multiple float64 compare operations.
  void VisitFloat64Compare(InstructionSelector* selector, Node* node,
                           FlagsContinuation* cont) {
    Mips64OperandGenerator g(selector);
    Node* left = node->InputAt(0);
    Node* right = node->InputAt(1);
    VisitCompare(selector, kMips64CmpD, g.UseRegister(left), g.UseRegister(right),
                 cont);
  }
  
  
  // Shared routine for multiple word compare operations.
  void VisitWordCompare(InstructionSelector* selector, Node* node,
                        InstructionCode opcode, FlagsContinuation* cont,
                        bool commutative) {
    Mips64OperandGenerator g(selector);
    Node* left = node->InputAt(0);
    Node* right = node->InputAt(1);
  
    // Match immediates on left or right side of comparison.
    if (g.CanBeImmediate(right, opcode)) {
      switch (cont->condition()) {
        case kSignedLessThan:
        case kSignedGreaterThanOrEqual:
        case kUnsignedLessThan:
        case kUnsignedGreaterThanOrEqual:
          VisitCompare(selector, opcode, g.UseRegister(left),
                       g.UseImmediate(right), cont);
          break;
        default:
          VisitCompare(selector, opcode, g.UseRegister(left),
                       g.UseRegister(right), cont);
      }
    } else if (g.CanBeImmediate(left, opcode)) {
      if (!commutative) cont->Commute();
      switch (cont->condition()) {
        case kSignedLessThan:
        case kSignedGreaterThanOrEqual:
        case kUnsignedLessThan:
        case kUnsignedGreaterThanOrEqual:
          VisitCompare(selector, opcode, g.UseRegister(right),
                       g.UseImmediate(left), cont);
          break;
        default:
          VisitCompare(selector, opcode, g.UseRegister(right),
                       g.UseRegister(left), cont);
      }
    } else {
      VisitCompare(selector, opcode, g.UseRegister(left), g.UseRegister(right),
                   cont);
    }
  }
  
  
  void VisitWord32Compare(InstructionSelector* selector, Node* node,
                          FlagsContinuation* cont) {
    VisitWordCompare(selector, node, kMips64Cmp, cont, false);
  }
  
  
  void VisitWord64Compare(InstructionSelector* selector, Node* node,
                          FlagsContinuation* cont) {
    VisitWordCompare(selector, node, kMips64Cmp, cont, false);
  }
  
  }  // namespace
  
  
  void EmitWordCompareZero(InstructionSelector* selector, Node* value,
                           FlagsContinuation* cont) {
    Mips64OperandGenerator g(selector);
    InstructionCode opcode = cont->Encode(kMips64Cmp);
    InstructionOperand const value_operand = g.UseRegister(value);
    if (cont->IsBranch()) {
      selector->Emit(opcode, g.NoOutput(), value_operand, g.TempImmediate(0),
                     g.Label(cont->true_block()), g.Label(cont->false_block()));
    } else {
      selector->Emit(opcode, g.DefineAsRegister(cont->result()), value_operand,
                     g.TempImmediate(0));
    }
  }
  
  
  // Shared routine for word comparisons against zero.
  void VisitWordCompareZero(InstructionSelector* selector, Node* user,
                            Node* value, FlagsContinuation* cont) {
    while (selector->CanCover(user, value)) {
      switch (value->opcode()) {
        case IrOpcode::kWord32Equal: {
          // Combine with comparisons against 0 by simply inverting the
          // continuation.
          Int32BinopMatcher m(value);
          if (m.right().Is(0)) {
            user = value;
            value = m.left().node();
            cont->Negate();
            continue;
          }
          cont->OverwriteAndNegateIfEqual(kEqual);
          return VisitWord32Compare(selector, value, cont);
        }
        case IrOpcode::kInt32LessThan:
          cont->OverwriteAndNegateIfEqual(kSignedLessThan);
          return VisitWord32Compare(selector, value, cont);
        case IrOpcode::kInt32LessThanOrEqual:
          cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
          return VisitWord32Compare(selector, value, cont);
        case IrOpcode::kUint32LessThan:
          cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
          return VisitWord32Compare(selector, value, cont);
        case IrOpcode::kUint32LessThanOrEqual:
          cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
          return VisitWord32Compare(selector, value, cont);
        case IrOpcode::kWord64Equal: {
          // Combine with comparisons against 0 by simply inverting the
          // continuation.
          Int64BinopMatcher m(value);
          if (m.right().Is(0)) {
            user = value;
            value = m.left().node();
            cont->Negate();
            continue;
          }
          cont->OverwriteAndNegateIfEqual(kEqual);
          return VisitWord64Compare(selector, value, cont);
        }
        case IrOpcode::kInt64LessThan:
          cont->OverwriteAndNegateIfEqual(kSignedLessThan);
          return VisitWord64Compare(selector, value, cont);
        case IrOpcode::kInt64LessThanOrEqual:
          cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
          return VisitWord64Compare(selector, value, cont);
        case IrOpcode::kUint64LessThan:
          cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
          return VisitWord64Compare(selector, value, cont);
        case IrOpcode::kUint64LessThanOrEqual:
          cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
          return VisitWord64Compare(selector, value, cont);
        case IrOpcode::kFloat32Equal:
          cont->OverwriteAndNegateIfEqual(kEqual);
          return VisitFloat32Compare(selector, value, cont);
        case IrOpcode::kFloat32LessThan:
          cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
          return VisitFloat32Compare(selector, value, cont);
        case IrOpcode::kFloat32LessThanOrEqual:
          cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
          return VisitFloat32Compare(selector, value, cont);
        case IrOpcode::kFloat64Equal:
          cont->OverwriteAndNegateIfEqual(kEqual);
          return VisitFloat64Compare(selector, value, cont);
        case IrOpcode::kFloat64LessThan:
          cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
          return VisitFloat64Compare(selector, value, cont);
        case IrOpcode::kFloat64LessThanOrEqual:
          cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
          return VisitFloat64Compare(selector, value, cont);
        case IrOpcode::kProjection:
          // Check if this is the overflow output projection of an
          // <Operation>WithOverflow node.
          if (ProjectionIndexOf(value->op()) == 1u) {
            // We cannot combine the <Operation>WithOverflow with this branch
            // unless the 0th projection (the use of the actual value of the
            // <Operation> is either NULL, which means there's no use of the
            // actual value, or was already defined, which means it is scheduled
            // *AFTER* this branch).
            Node* const node = value->InputAt(0);
            Node* const result = NodeProperties::FindProjection(node, 0);
            if (result == NULL || selector->IsDefined(result)) {
              switch (node->opcode()) {
                case IrOpcode::kInt32AddWithOverflow:
                  cont->OverwriteAndNegateIfEqual(kOverflow);
                  return VisitBinop(selector, node, kMips64Dadd, cont);
                case IrOpcode::kInt32SubWithOverflow:
                  cont->OverwriteAndNegateIfEqual(kOverflow);
                  return VisitBinop(selector, node, kMips64Dsub, cont);
                default:
                  break;
              }
            }
          }
          break;
        case IrOpcode::kWord32And:
        case IrOpcode::kWord64And:
          return VisitWordCompare(selector, value, kMips64Tst, cont, true);
        default:
          break;
      }
      break;
    }
  
    // Continuation could not be combined with a compare, emit compare against 0.
    EmitWordCompareZero(selector, value, cont);
  }
  
  
  void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
                                        BasicBlock* fbranch) {
    FlagsContinuation cont(kNotEqual, tbranch, fbranch);
    VisitWordCompareZero(this, branch, branch->InputAt(0), &cont);
  }
  
  
  void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
    Mips64OperandGenerator g(this);
    InstructionOperand value_operand = g.UseRegister(node->InputAt(0));
  
    // Emit either ArchTableSwitch or ArchLookupSwitch.
    size_t table_space_cost = 10 + 2 * sw.value_range;
    size_t table_time_cost = 3;
    size_t lookup_space_cost = 2 + 2 * sw.case_count;
    size_t lookup_time_cost = sw.case_count;
    if (sw.case_count > 0 &&
        table_space_cost + 3 * table_time_cost <=
            lookup_space_cost + 3 * lookup_time_cost &&
        sw.min_value > std::numeric_limits<int32_t>::min()) {
      InstructionOperand index_operand = value_operand;
      if (sw.min_value) {
        index_operand = g.TempRegister();
        Emit(kMips64Sub, index_operand, value_operand,
             g.TempImmediate(sw.min_value));
      }
      // Generate a table lookup.
      return EmitTableSwitch(sw, index_operand);
    }
  
    // Generate a sequence of conditional jumps.
    return EmitLookupSwitch(sw, value_operand);
  }
  
  
  void InstructionSelector::VisitWord32Equal(Node* const node) {
    FlagsContinuation cont(kEqual, node);
    Int32BinopMatcher m(node);
    if (m.right().Is(0)) {
      return VisitWordCompareZero(this, m.node(), m.left().node(), &cont);
    }
  
    VisitWord32Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitInt32LessThan(Node* node) {
    FlagsContinuation cont(kSignedLessThan, node);
    VisitWord32Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) {
    FlagsContinuation cont(kSignedLessThanOrEqual, node);
    VisitWord32Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitUint32LessThan(Node* node) {
    FlagsContinuation cont(kUnsignedLessThan, node);
    VisitWord32Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) {
    FlagsContinuation cont(kUnsignedLessThanOrEqual, node);
    VisitWord32Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitInt32AddWithOverflow(Node* node) {
    if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
      FlagsContinuation cont(kOverflow, ovf);
      return VisitBinop(this, node, kMips64Dadd, &cont);
    }
    FlagsContinuation cont;
    VisitBinop(this, node, kMips64Dadd, &cont);
  }
  
  
  void InstructionSelector::VisitInt32SubWithOverflow(Node* node) {
    if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
      FlagsContinuation cont(kOverflow, ovf);
      return VisitBinop(this, node, kMips64Dsub, &cont);
    }
    FlagsContinuation cont;
    VisitBinop(this, node, kMips64Dsub, &cont);
  }
  
  
  void InstructionSelector::VisitWord64Equal(Node* const node) {
    FlagsContinuation cont(kEqual, node);
    Int64BinopMatcher m(node);
    if (m.right().Is(0)) {
      return VisitWordCompareZero(this, m.node(), m.left().node(), &cont);
    }
  
    VisitWord64Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitInt64LessThan(Node* node) {
    FlagsContinuation cont(kSignedLessThan, node);
    VisitWord64Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitInt64LessThanOrEqual(Node* node) {
    FlagsContinuation cont(kSignedLessThanOrEqual, node);
    VisitWord64Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitUint64LessThan(Node* node) {
    FlagsContinuation cont(kUnsignedLessThan, node);
    VisitWord64Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitUint64LessThanOrEqual(Node* node) {
    FlagsContinuation cont(kUnsignedLessThanOrEqual, node);
    VisitWord64Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitFloat32Equal(Node* node) {
    FlagsContinuation cont(kEqual, node);
    VisitFloat32Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitFloat32LessThan(Node* node) {
    FlagsContinuation cont(kUnsignedLessThan, node);
    VisitFloat32Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitFloat32LessThanOrEqual(Node* node) {
    FlagsContinuation cont(kUnsignedLessThanOrEqual, node);
    VisitFloat32Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitFloat64Equal(Node* node) {
    FlagsContinuation cont(kEqual, node);
    VisitFloat64Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitFloat64LessThan(Node* node) {
    FlagsContinuation cont(kUnsignedLessThan, node);
    VisitFloat64Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) {
    FlagsContinuation cont(kUnsignedLessThanOrEqual, node);
    VisitFloat64Compare(this, node, &cont);
  }
  
  
  void InstructionSelector::VisitFloat64ExtractLowWord32(Node* node) {
    VisitRR(this, kMips64Float64ExtractLowWord32, node);
  }
  
  
  void InstructionSelector::VisitFloat64ExtractHighWord32(Node* node) {
    VisitRR(this, kMips64Float64ExtractHighWord32, node);
  }
  
  
  void InstructionSelector::VisitFloat64InsertLowWord32(Node* node) {
    Mips64OperandGenerator g(this);
    Node* left = node->InputAt(0);
    Node* right = node->InputAt(1);
    Emit(kMips64Float64InsertLowWord32, g.DefineSameAsFirst(node),
         g.UseRegister(left), g.UseRegister(right));
  }
  
  
  void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) {
    Mips64OperandGenerator g(this);
    Node* left = node->InputAt(0);
    Node* right = node->InputAt(1);
    Emit(kMips64Float64InsertHighWord32, g.DefineSameAsFirst(node),
         g.UseRegister(left), g.UseRegister(right));
  }
  
  
  // static
  MachineOperatorBuilder::Flags
  InstructionSelector::SupportedMachineOperatorFlags() {
    return MachineOperatorBuilder::kFloat64RoundDown |
           MachineOperatorBuilder::kFloat64RoundTruncate;
  }
  
  }  // namespace compiler
  }  // namespace internal
  }  // namespace v8
  