// Copyright 2015 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/interpreter/bytecode-array-builder.h"

namespace v8 {
namespace internal {
namespace interpreter {

class BytecodeArrayBuilder::PreviousBytecodeHelper {
 public:
  explicit PreviousBytecodeHelper(const BytecodeArrayBuilder& array_builder)
      : array_builder_(array_builder),
        previous_bytecode_start_(array_builder_.last_bytecode_start_) {
    // This helper is expected to be instantiated only when the last bytecode is
    // in the same basic block.
    DCHECK(array_builder_.LastBytecodeInSameBlock());
  }

  // Returns the previous bytecode in the same basic block.
  MUST_USE_RESULT Bytecode GetBytecode() const {
    DCHECK_EQ(array_builder_.last_bytecode_start_, previous_bytecode_start_);
    return Bytecodes::FromByte(
        array_builder_.bytecodes()->at(previous_bytecode_start_));
  }

  // Returns the operand at operand_index for the previous bytecode in the
  // same basic block.
  MUST_USE_RESULT uint32_t GetOperand(int operand_index) const {
    DCHECK_EQ(array_builder_.last_bytecode_start_, previous_bytecode_start_);
    Bytecode bytecode = GetBytecode();
    DCHECK_GE(operand_index, 0);
    DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(bytecode));
    size_t operand_offset =
        previous_bytecode_start_ +
        Bytecodes::GetOperandOffset(bytecode, operand_index);
    OperandSize size = Bytecodes::GetOperandSize(bytecode, operand_index);
    switch (size) {
      default:
      case OperandSize::kNone:
        UNREACHABLE();
      case OperandSize::kByte:
        return static_cast<uint32_t>(
            array_builder_.bytecodes()->at(operand_offset));
      case OperandSize::kShort:
        uint16_t operand =
            (array_builder_.bytecodes()->at(operand_offset) << 8) +
            array_builder_.bytecodes()->at(operand_offset + 1);
        return static_cast<uint32_t>(operand);
    }
  }

  Handle<Object> GetConstantForIndexOperand(int operand_index) const {
    return array_builder_.constant_array_builder()->At(
        GetOperand(operand_index));
  }

 private:
  const BytecodeArrayBuilder& array_builder_;
  size_t previous_bytecode_start_;

  DISALLOW_COPY_AND_ASSIGN(PreviousBytecodeHelper);
};


BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone)
    : isolate_(isolate),
      zone_(zone),
      bytecodes_(zone),
      bytecode_generated_(false),
      constant_array_builder_(isolate, zone),
      last_block_end_(0),
      last_bytecode_start_(~0),
      exit_seen_in_block_(false),
      unbound_jumps_(0),
      parameter_count_(-1),
      local_register_count_(-1),
      context_register_count_(-1),
      temporary_register_count_(0),
      free_temporaries_(zone) {}


BytecodeArrayBuilder::~BytecodeArrayBuilder() { DCHECK_EQ(0, unbound_jumps_); }


void BytecodeArrayBuilder::set_locals_count(int number_of_locals) {
  local_register_count_ = number_of_locals;
  DCHECK_LE(context_register_count_, 0);
}


void BytecodeArrayBuilder::set_parameter_count(int number_of_parameters) {
  parameter_count_ = number_of_parameters;
}


void BytecodeArrayBuilder::set_context_count(int number_of_contexts) {
  context_register_count_ = number_of_contexts;
  DCHECK_GE(local_register_count_, 0);
}


Register BytecodeArrayBuilder::first_context_register() const {
  DCHECK_GT(context_register_count_, 0);
  return Register(local_register_count_);
}


Register BytecodeArrayBuilder::last_context_register() const {
  DCHECK_GT(context_register_count_, 0);
  return Register(local_register_count_ + context_register_count_ - 1);
}


Register BytecodeArrayBuilder::first_temporary_register() const {
  DCHECK_GT(temporary_register_count_, 0);
  return Register(fixed_register_count());
}


Register BytecodeArrayBuilder::last_temporary_register() const {
  DCHECK_GT(temporary_register_count_, 0);
  return Register(fixed_register_count() + temporary_register_count_ - 1);
}


Register BytecodeArrayBuilder::Parameter(int parameter_index) const {
  DCHECK_GE(parameter_index, 0);
  return Register::FromParameterIndex(parameter_index, parameter_count());
}


bool BytecodeArrayBuilder::RegisterIsParameterOrLocal(Register reg) const {
  return reg.is_parameter() || reg.index() < locals_count();
}


bool BytecodeArrayBuilder::RegisterIsTemporary(Register reg) const {
  return temporary_register_count_ > 0 && first_temporary_register() <= reg &&
         reg <= last_temporary_register();
}


Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray() {
  DCHECK_EQ(bytecode_generated_, false);
  EnsureReturn();

  int bytecode_size = static_cast<int>(bytecodes_.size());
  int register_count = fixed_register_count() + temporary_register_count_;
  int frame_size = register_count * kPointerSize;
  Factory* factory = isolate_->factory();
  Handle<FixedArray> constant_pool =
      constant_array_builder()->ToFixedArray(factory);
  Handle<BytecodeArray> output =
      factory->NewBytecodeArray(bytecode_size, &bytecodes_.front(), frame_size,
                                parameter_count(), constant_pool);
  bytecode_generated_ = true;
  return output;
}


template <size_t N>
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t(&operands)[N]) {
  // Don't output dead code.
  if (exit_seen_in_block_) return;

  DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), static_cast<int>(N));
  last_bytecode_start_ = bytecodes()->size();
  bytecodes()->push_back(Bytecodes::ToByte(bytecode));
  for (int i = 0; i < static_cast<int>(N); i++) {
    DCHECK(OperandIsValid(bytecode, i, operands[i]));
    switch (Bytecodes::GetOperandSize(bytecode, i)) {
      case OperandSize::kNone:
        UNREACHABLE();
      case OperandSize::kByte:
        bytecodes()->push_back(static_cast<uint8_t>(operands[i]));
        break;
      case OperandSize::kShort: {
        uint8_t operand_bytes[2];
        WriteUnalignedUInt16(operand_bytes, operands[i]);
        bytecodes()->insert(bytecodes()->end(), operand_bytes,
                            operand_bytes + 2);
        break;
      }
    }
  }
}


void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0,
                                  uint32_t operand1, uint32_t operand2,
                                  uint32_t operand3) {
  uint32_t operands[] = {operand0, operand1, operand2, operand3};
  Output(bytecode, operands);
}


void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0,
                                  uint32_t operand1, uint32_t operand2) {
  uint32_t operands[] = {operand0, operand1, operand2};
  Output(bytecode, operands);
}


void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0,
                                  uint32_t operand1) {
  uint32_t operands[] = {operand0, operand1};
  Output(bytecode, operands);
}


void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0) {
  uint32_t operands[] = {operand0};
  Output(bytecode, operands);
}


void BytecodeArrayBuilder::Output(Bytecode bytecode) {
  // Don't output dead code.
  if (exit_seen_in_block_) return;

  DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 0);
  last_bytecode_start_ = bytecodes()->size();
  bytecodes()->push_back(Bytecodes::ToByte(bytecode));
}


BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op,
                                                            Register reg,
                                                            Strength strength) {
  if (is_strong(strength)) {
    UNIMPLEMENTED();
  }

  Output(BytecodeForBinaryOperation(op), reg.ToOperand());
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CountOperation(Token::Value op,
                                                           Strength strength) {
  if (is_strong(strength)) {
    UNIMPLEMENTED();
  }

  Output(BytecodeForCountOperation(op));
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot() {
  Output(Bytecode::kLogicalNot);
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::TypeOf() {
  Output(Bytecode::kTypeOf);
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(
    Token::Value op, Register reg, Strength strength) {
  if (is_strong(strength)) {
    UNIMPLEMENTED();
  }

  Output(BytecodeForCompareOperation(op), reg.ToOperand());
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(
    v8::internal::Smi* smi) {
  int32_t raw_smi = smi->value();
  if (raw_smi == 0) {
    Output(Bytecode::kLdaZero);
  } else if (raw_smi >= -128 && raw_smi <= 127) {
    Output(Bytecode::kLdaSmi8, static_cast<uint8_t>(raw_smi));
  } else {
    LoadLiteral(Handle<Object>(smi, isolate_));
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(Handle<Object> object) {
  size_t entry = GetConstantPoolEntry(object);
  if (FitsInIdx8Operand(entry)) {
    Output(Bytecode::kLdaConstant, static_cast<uint8_t>(entry));
  } else if (FitsInIdx16Operand(entry)) {
    Output(Bytecode::kLdaConstantWide, static_cast<uint16_t>(entry));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadUndefined() {
  Output(Bytecode::kLdaUndefined);
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNull() {
  Output(Bytecode::kLdaNull);
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTheHole() {
  Output(Bytecode::kLdaTheHole);
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTrue() {
  Output(Bytecode::kLdaTrue);
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadFalse() {
  Output(Bytecode::kLdaFalse);
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadBooleanConstant(bool value) {
  if (value) {
    LoadTrue();
  } else {
    LoadFalse();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister(
    Register reg) {
  if (!IsRegisterInAccumulator(reg)) {
    Output(Bytecode::kLdar, reg.ToOperand());
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
    Register reg) {
  // TODO(oth): Avoid storing the accumulator in the register if the
  // previous bytecode loaded the accumulator with the same register.
  //
  // TODO(oth): If the previous bytecode is a MOV into this register,
  // the previous instruction can be removed. The logic for determining
  // these redundant MOVs appears complex.
  Output(Bytecode::kStar, reg.ToOperand());
  if (!IsRegisterInAccumulator(reg)) {
    Output(Bytecode::kStar, reg.ToOperand());
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from,
                                                         Register to) {
  DCHECK(from != to);
  Output(Bytecode::kMov, from.ToOperand(), to.ToOperand());
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::ExchangeRegisters(Register reg0,
                                                              Register reg1) {
  DCHECK(reg0 != reg1);
  if (FitsInReg8Operand(reg0)) {
    Output(Bytecode::kExchange, reg0.ToOperand(), reg1.ToWideOperand());
  } else if (FitsInReg8Operand(reg1)) {
    Output(Bytecode::kExchange, reg1.ToOperand(), reg0.ToWideOperand());
  } else {
    Output(Bytecode::kExchangeWide, reg0.ToWideOperand(), reg1.ToWideOperand());
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(
    const Handle<String> name, int feedback_slot, LanguageMode language_mode,
    TypeofMode typeof_mode) {
  // TODO(rmcilroy): Potentially store language and typeof information in an
  // operand rather than having extra bytecodes.
  Bytecode bytecode = BytecodeForLoadGlobal(language_mode, typeof_mode);
  size_t name_index = GetConstantPoolEntry(name);
  if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) {
    Output(bytecode, static_cast<uint8_t>(name_index),
           static_cast<uint8_t>(feedback_slot));
  } else if (FitsInIdx16Operand(name_index) &&
             FitsInIdx16Operand(feedback_slot)) {
    Output(BytecodeForWideOperands(bytecode), static_cast<uint16_t>(name_index),
           static_cast<uint16_t>(feedback_slot));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::StoreGlobal(
    const Handle<String> name, int feedback_slot, LanguageMode language_mode) {
  Bytecode bytecode = BytecodeForStoreGlobal(language_mode);
  size_t name_index = GetConstantPoolEntry(name);
  if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) {
    Output(bytecode, static_cast<uint8_t>(name_index),
           static_cast<uint8_t>(feedback_slot));
  } else if (FitsInIdx16Operand(name_index) &&
             FitsInIdx16Operand(feedback_slot)) {
    Output(BytecodeForWideOperands(bytecode), static_cast<uint16_t>(name_index),
           static_cast<uint16_t>(feedback_slot));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadContextSlot(Register context,
                                                            int slot_index) {
  DCHECK(slot_index >= 0);
  if (FitsInIdx8Operand(slot_index)) {
    Output(Bytecode::kLdaContextSlot, context.ToOperand(),
           static_cast<uint8_t>(slot_index));
  } else if (FitsInIdx16Operand(slot_index)) {
    Output(Bytecode::kLdaContextSlotWide, context.ToOperand(),
           static_cast<uint16_t>(slot_index));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::StoreContextSlot(Register context,
                                                             int slot_index) {
  DCHECK(slot_index >= 0);
  if (FitsInIdx8Operand(slot_index)) {
    Output(Bytecode::kStaContextSlot, context.ToOperand(),
           static_cast<uint8_t>(slot_index));
  } else if (FitsInIdx16Operand(slot_index)) {
    Output(Bytecode::kStaContextSlotWide, context.ToOperand(),
           static_cast<uint16_t>(slot_index));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupSlot(
    const Handle<String> name, TypeofMode typeof_mode) {
  Bytecode bytecode = (typeof_mode == INSIDE_TYPEOF)
                          ? Bytecode::kLdaLookupSlotInsideTypeof
                          : Bytecode::kLdaLookupSlot;
  size_t name_index = GetConstantPoolEntry(name);
  if (FitsInIdx8Operand(name_index)) {
    Output(bytecode, static_cast<uint8_t>(name_index));
  } else if (FitsInIdx16Operand(name_index)) {
    Output(BytecodeForWideOperands(bytecode),
           static_cast<uint16_t>(name_index));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::StoreLookupSlot(
    const Handle<String> name, LanguageMode language_mode) {
  Bytecode bytecode = BytecodeForStoreLookupSlot(language_mode);
  size_t name_index = GetConstantPoolEntry(name);
  if (FitsInIdx8Operand(name_index)) {
    Output(bytecode, static_cast<uint8_t>(name_index));
  } else if (FitsInIdx16Operand(name_index)) {
    Output(BytecodeForWideOperands(bytecode),
           static_cast<uint16_t>(name_index));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
    Register object, const Handle<String> name, int feedback_slot,
    LanguageMode language_mode) {
  Bytecode bytecode = BytecodeForLoadIC(language_mode);
  size_t name_index = GetConstantPoolEntry(name);
  if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) {
    Output(bytecode, object.ToOperand(), static_cast<uint8_t>(name_index),
           static_cast<uint8_t>(feedback_slot));
  } else if (FitsInIdx16Operand(name_index) &&
             FitsInIdx16Operand(feedback_slot)) {
    Output(BytecodeForWideOperands(bytecode), object.ToOperand(),
           static_cast<uint16_t>(name_index),
           static_cast<uint16_t>(feedback_slot));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty(
    Register object, int feedback_slot, LanguageMode language_mode) {
  Bytecode bytecode = BytecodeForKeyedLoadIC(language_mode);
  if (FitsInIdx8Operand(feedback_slot)) {
    Output(bytecode, object.ToOperand(), static_cast<uint8_t>(feedback_slot));
  } else if (FitsInIdx16Operand(feedback_slot)) {
    Output(BytecodeForWideOperands(bytecode), object.ToOperand(),
           static_cast<uint16_t>(feedback_slot));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty(
    Register object, const Handle<String> name, int feedback_slot,
    LanguageMode language_mode) {
  Bytecode bytecode = BytecodeForStoreIC(language_mode);
  size_t name_index = GetConstantPoolEntry(name);
  if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) {
    Output(bytecode, object.ToOperand(), static_cast<uint8_t>(name_index),
           static_cast<uint8_t>(feedback_slot));
  } else if (FitsInIdx16Operand(name_index) &&
             FitsInIdx16Operand(feedback_slot)) {
    Output(BytecodeForWideOperands(bytecode), object.ToOperand(),
           static_cast<uint16_t>(name_index),
           static_cast<uint16_t>(feedback_slot));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty(
    Register object, Register key, int feedback_slot,
    LanguageMode language_mode) {
  Bytecode bytecode = BytecodeForKeyedStoreIC(language_mode);
  if (FitsInIdx8Operand(feedback_slot)) {
    Output(bytecode, object.ToOperand(), key.ToOperand(),
           static_cast<uint8_t>(feedback_slot));
  } else if (FitsInIdx16Operand(feedback_slot)) {
    Output(BytecodeForWideOperands(bytecode), object.ToOperand(),
           key.ToOperand(), static_cast<uint16_t>(feedback_slot));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure(
    Handle<SharedFunctionInfo> shared_info, PretenureFlag tenured) {
  size_t entry = GetConstantPoolEntry(shared_info);
  DCHECK(FitsInImm8Operand(tenured));
  if (FitsInIdx8Operand(entry)) {
    Output(Bytecode::kCreateClosure, static_cast<uint8_t>(entry),
           static_cast<uint8_t>(tenured));
  } else if (FitsInIdx16Operand(entry)) {
    Output(Bytecode::kCreateClosureWide, static_cast<uint16_t>(entry),
           static_cast<uint8_t>(tenured));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments(
    CreateArgumentsType type) {
  // TODO(rmcilroy): Consider passing the type as a bytecode operand rather
  // than having two different bytecodes once we have better support for
  // branches in the InterpreterAssembler.
  Bytecode bytecode = BytecodeForCreateArguments(type);
  Output(bytecode);
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral(
    Handle<String> pattern, int literal_index, int flags) {
  DCHECK(FitsInImm8Operand(flags));  // Flags should fit in 8 bits.
  size_t pattern_entry = GetConstantPoolEntry(pattern);
  if (FitsInIdx8Operand(literal_index) && FitsInIdx8Operand(pattern_entry)) {
    Output(Bytecode::kCreateRegExpLiteral, static_cast<uint8_t>(pattern_entry),
           static_cast<uint8_t>(literal_index), static_cast<uint8_t>(flags));
  } else if (FitsInIdx16Operand(literal_index) &&
             FitsInIdx16Operand(pattern_entry)) {
    Output(Bytecode::kCreateRegExpLiteralWide,
           static_cast<uint16_t>(pattern_entry),
           static_cast<uint16_t>(literal_index), static_cast<uint8_t>(flags));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
    Handle<FixedArray> constant_elements, int literal_index, int flags) {
  DCHECK(FitsInImm8Operand(flags));  // Flags should fit in 8 bits.
  size_t constant_elements_entry = GetConstantPoolEntry(constant_elements);
  if (FitsInIdx8Operand(literal_index) &&
      FitsInIdx8Operand(constant_elements_entry)) {
    Output(Bytecode::kCreateArrayLiteral,
           static_cast<uint8_t>(constant_elements_entry),
           static_cast<uint8_t>(literal_index), static_cast<uint8_t>(flags));
  } else if (FitsInIdx16Operand(literal_index) &&
             FitsInIdx16Operand(constant_elements_entry)) {
    Output(Bytecode::kCreateArrayLiteralWide,
           static_cast<uint16_t>(constant_elements_entry),
           static_cast<uint16_t>(literal_index), static_cast<uint8_t>(flags));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral(
    Handle<FixedArray> constant_properties, int literal_index, int flags) {
  DCHECK(FitsInImm8Operand(flags));  // Flags should fit in 8 bits.
  size_t constant_properties_entry = GetConstantPoolEntry(constant_properties);
  if (FitsInIdx8Operand(literal_index) &&
      FitsInIdx8Operand(constant_properties_entry)) {
    Output(Bytecode::kCreateObjectLiteral,
           static_cast<uint8_t>(constant_properties_entry),
           static_cast<uint8_t>(literal_index), static_cast<uint8_t>(flags));
  } else if (FitsInIdx16Operand(literal_index) &&
             FitsInIdx16Operand(constant_properties_entry)) {
    Output(Bytecode::kCreateObjectLiteralWide,
           static_cast<uint16_t>(constant_properties_entry),
           static_cast<uint16_t>(literal_index), static_cast<uint8_t>(flags));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) {
  Output(Bytecode::kPushContext, context.ToOperand());
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) {
  Output(Bytecode::kPopContext, context.ToOperand());
  return *this;
}


bool BytecodeArrayBuilder::NeedToBooleanCast() {
  if (!LastBytecodeInSameBlock()) {
    return true;
  }
  PreviousBytecodeHelper previous_bytecode(*this);
  switch (previous_bytecode.GetBytecode()) {
    // If the previous bytecode puts a boolean in the accumulator return true.
    case Bytecode::kLdaTrue:
    case Bytecode::kLdaFalse:
    case Bytecode::kLogicalNot:
    case Bytecode::kTestEqual:
    case Bytecode::kTestNotEqual:
    case Bytecode::kTestEqualStrict:
    case Bytecode::kTestNotEqualStrict:
    case Bytecode::kTestLessThan:
    case Bytecode::kTestLessThanOrEqual:
    case Bytecode::kTestGreaterThan:
    case Bytecode::kTestGreaterThanOrEqual:
    case Bytecode::kTestInstanceOf:
    case Bytecode::kTestIn:
    case Bytecode::kForInDone:
      return false;
    default:
      return true;
  }
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToJSObject() {
  Output(Bytecode::kToObject);
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToName() {
  if (LastBytecodeInSameBlock()) {
    PreviousBytecodeHelper previous_bytecode(*this);
    switch (previous_bytecode.GetBytecode()) {
      case Bytecode::kToName:
      case Bytecode::kTypeOf:
        return *this;
      case Bytecode::kLdaConstantWide:
      case Bytecode::kLdaConstant: {
        Handle<Object> object = previous_bytecode.GetConstantForIndexOperand(0);
        if (object->IsName()) return *this;
        break;
      }
      default:
        break;
    }
  }
  Output(Bytecode::kToName);
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToNumber() {
  // TODO(rmcilroy): consider omitting if the preceeding bytecode always returns
  // a number.
  Output(Bytecode::kToNumber);
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) {
  if (label->is_forward_target()) {
    // An earlier jump instruction refers to this label. Update it's location.
    PatchJump(bytecodes()->end(), bytecodes()->begin() + label->offset());
    // Now treat as if the label will only be back referred to.
  }
  label->bind_to(bytecodes()->size());
  LeaveBasicBlock();
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(const BytecodeLabel& target,
                                                 BytecodeLabel* label) {
  DCHECK(!label->is_bound());
  DCHECK(target.is_bound());
  PatchJump(bytecodes()->begin() + target.offset(),
            bytecodes()->begin() + label->offset());
  label->bind_to(target.offset());
  LeaveBasicBlock();
  return *this;
}


// static
Bytecode BytecodeArrayBuilder::GetJumpWithConstantOperand(
    Bytecode jump_bytecode) {
  switch (jump_bytecode) {
    case Bytecode::kJump:
      return Bytecode::kJumpConstant;
    case Bytecode::kJumpIfTrue:
      return Bytecode::kJumpIfTrueConstant;
    case Bytecode::kJumpIfFalse:
      return Bytecode::kJumpIfFalseConstant;
    case Bytecode::kJumpIfToBooleanTrue:
      return Bytecode::kJumpIfToBooleanTrueConstant;
    case Bytecode::kJumpIfToBooleanFalse:
      return Bytecode::kJumpIfToBooleanFalseConstant;
    case Bytecode::kJumpIfNull:
      return Bytecode::kJumpIfNullConstant;
    case Bytecode::kJumpIfUndefined:
      return Bytecode::kJumpIfUndefinedConstant;
    default:
      UNREACHABLE();
      return static_cast<Bytecode>(-1);
  }
}


// static
Bytecode BytecodeArrayBuilder::GetJumpWithConstantWideOperand(
    Bytecode jump_bytecode) {
  switch (jump_bytecode) {
    case Bytecode::kJump:
      return Bytecode::kJumpConstantWide;
    case Bytecode::kJumpIfTrue:
      return Bytecode::kJumpIfTrueConstantWide;
    case Bytecode::kJumpIfFalse:
      return Bytecode::kJumpIfFalseConstantWide;
    case Bytecode::kJumpIfToBooleanTrue:
      return Bytecode::kJumpIfToBooleanTrueConstantWide;
    case Bytecode::kJumpIfToBooleanFalse:
      return Bytecode::kJumpIfToBooleanFalseConstantWide;
    case Bytecode::kJumpIfNull:
      return Bytecode::kJumpIfNullConstantWide;
    case Bytecode::kJumpIfUndefined:
      return Bytecode::kJumpIfUndefinedConstantWide;
    default:
      UNREACHABLE();
      return static_cast<Bytecode>(-1);
  }
}


// static
Bytecode BytecodeArrayBuilder::GetJumpWithToBoolean(Bytecode jump_bytecode) {
  switch (jump_bytecode) {
    case Bytecode::kJump:
    case Bytecode::kJumpIfNull:
    case Bytecode::kJumpIfUndefined:
      return jump_bytecode;
    case Bytecode::kJumpIfTrue:
      return Bytecode::kJumpIfToBooleanTrue;
    case Bytecode::kJumpIfFalse:
      return Bytecode::kJumpIfToBooleanFalse;
    default:
      UNREACHABLE();
  }
  return static_cast<Bytecode>(-1);
}


void BytecodeArrayBuilder::PatchIndirectJumpWith8BitOperand(
    const ZoneVector<uint8_t>::iterator& jump_location, int delta) {
  Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location);
  DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
  ZoneVector<uint8_t>::iterator operand_location = jump_location + 1;
  DCHECK_EQ(*operand_location, 0);
  if (FitsInImm8Operand(delta)) {
    // The jump fits within the range of an Imm8 operand, so cancel
    // the reservation and jump directly.
    constant_array_builder()->DiscardReservedEntry(OperandSize::kByte);
    *operand_location = static_cast<uint8_t>(delta);
  } else {
    // The jump does not fit within the range of an Imm8 operand, so
    // commit reservation putting the offset into the constant pool,
    // and update the jump instruction and operand.
    size_t entry = constant_array_builder()->CommitReservedEntry(
        OperandSize::kByte, handle(Smi::FromInt(delta), isolate()));
    DCHECK(FitsInIdx8Operand(entry));
    jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
    *jump_location = Bytecodes::ToByte(jump_bytecode);
    *operand_location = static_cast<uint8_t>(entry);
  }
}


void BytecodeArrayBuilder::PatchIndirectJumpWith16BitOperand(
    const ZoneVector<uint8_t>::iterator& jump_location, int delta) {
  DCHECK(Bytecodes::IsJumpConstantWide(Bytecodes::FromByte(*jump_location)));
  ZoneVector<uint8_t>::iterator operand_location = jump_location + 1;
  size_t entry = constant_array_builder()->CommitReservedEntry(
      OperandSize::kShort, handle(Smi::FromInt(delta), isolate()));
  DCHECK(FitsInIdx16Operand(entry));
  uint8_t operand_bytes[2];
  WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(entry));
  DCHECK(*operand_location == 0 && *(operand_location + 1) == 0);
  *operand_location++ = operand_bytes[0];
  *operand_location = operand_bytes[1];
}


void BytecodeArrayBuilder::PatchJump(
    const ZoneVector<uint8_t>::iterator& jump_target,
    const ZoneVector<uint8_t>::iterator& jump_location) {
  Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location);
  int delta = static_cast<int>(jump_target - jump_location);
  DCHECK(Bytecodes::IsJump(jump_bytecode));
  switch (Bytecodes::GetOperandSize(jump_bytecode, 0)) {
    case OperandSize::kByte:
      PatchIndirectJumpWith8BitOperand(jump_location, delta);
      break;
    case OperandSize::kShort:
      PatchIndirectJumpWith16BitOperand(jump_location, delta);
      break;
    case OperandSize::kNone:
      UNREACHABLE();
  }
  unbound_jumps_--;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode,
                                                       BytecodeLabel* label) {
  // Don't emit dead code.
  if (exit_seen_in_block_) return *this;

  // Check if the value in accumulator is boolean, if not choose an
  // appropriate JumpIfToBoolean bytecode.
  if (NeedToBooleanCast()) {
    jump_bytecode = GetJumpWithToBoolean(jump_bytecode);
  }

  if (label->is_bound()) {
    // Label has been bound already so this is a backwards jump.
    CHECK_GE(bytecodes()->size(), label->offset());
    CHECK_LE(bytecodes()->size(), static_cast<size_t>(kMaxInt));
    size_t abs_delta = bytecodes()->size() - label->offset();
    int delta = -static_cast<int>(abs_delta);

    if (FitsInImm8Operand(delta)) {
      Output(jump_bytecode, static_cast<uint8_t>(delta));
    } else {
      size_t entry =
          GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate()));
      if (FitsInIdx8Operand(entry)) {
        Output(GetJumpWithConstantOperand(jump_bytecode),
               static_cast<uint8_t>(entry));
      } else if (FitsInIdx16Operand(entry)) {
        Output(GetJumpWithConstantWideOperand(jump_bytecode),
               static_cast<uint16_t>(entry));
      } else {
        UNREACHABLE();
      }
    }
  } else {
    // The label has not yet been bound so this is a forward reference
    // that will be patched when the label is bound. We create a
    // reservation in the constant pool so the jump can be patched
    // when the label is bound. The reservation means the maximum size
    // of the operand for the constant is known and the jump can
    // be emitted into the bytecode stream with space for the operand.
    label->set_referrer(bytecodes()->size());
    unbound_jumps_++;
    OperandSize reserved_operand_size =
        constant_array_builder()->CreateReservedEntry();
    switch (reserved_operand_size) {
      case OperandSize::kByte:
        Output(jump_bytecode, 0);
        break;
      case OperandSize::kShort:
        Output(GetJumpWithConstantWideOperand(jump_bytecode), 0);
        break;
      case OperandSize::kNone:
        UNREACHABLE();
    }
  }
  LeaveBasicBlock();
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::Jump(BytecodeLabel* label) {
  return OutputJump(Bytecode::kJump, label);
}


BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfTrue(BytecodeLabel* label) {
  return OutputJump(Bytecode::kJumpIfTrue, label);
}


BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfFalse(BytecodeLabel* label) {
  return OutputJump(Bytecode::kJumpIfFalse, label);
}


BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNull(BytecodeLabel* label) {
  return OutputJump(Bytecode::kJumpIfNull, label);
}


BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfUndefined(
    BytecodeLabel* label) {
  return OutputJump(Bytecode::kJumpIfUndefined, label);
}


BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() {
  Output(Bytecode::kThrow);
  exit_seen_in_block_ = true;
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
  Output(Bytecode::kReturn);
  exit_seen_in_block_ = true;
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::ForInPrepare(
    Register cache_type, Register cache_array, Register cache_length) {
  Output(Bytecode::kForInPrepare, cache_type.ToOperand(),
         cache_array.ToOperand(), cache_length.ToOperand());
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::ForInDone(Register index,
                                                      Register cache_length) {
  Output(Bytecode::kForInDone, index.ToOperand(), cache_length.ToOperand());
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext(Register receiver,
                                                      Register cache_type,
                                                      Register cache_array,
                                                      Register index) {
  Output(Bytecode::kForInNext, receiver.ToOperand(), cache_type.ToOperand(),
         cache_array.ToOperand(), index.ToOperand());
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::ForInStep(Register index) {
  Output(Bytecode::kForInStep, index.ToOperand());
  return *this;
}


void BytecodeArrayBuilder::LeaveBasicBlock() {
  last_block_end_ = bytecodes()->size();
  exit_seen_in_block_ = false;
}


void BytecodeArrayBuilder::EnsureReturn() {
  if (!exit_seen_in_block_) {
    LoadUndefined();
    Return();
  }
}


BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
                                                 Register receiver,
                                                 size_t arg_count,
                                                 int feedback_slot) {
  if (FitsInIdx8Operand(arg_count) && FitsInIdx8Operand(feedback_slot)) {
    Output(Bytecode::kCall, callable.ToOperand(), receiver.ToOperand(),
           static_cast<uint8_t>(arg_count),
           static_cast<uint8_t>(feedback_slot));
  } else if (FitsInIdx16Operand(arg_count) &&
             FitsInIdx16Operand(feedback_slot)) {
    Output(Bytecode::kCallWide, callable.ToOperand(), receiver.ToOperand(),
           static_cast<uint16_t>(arg_count),
           static_cast<uint16_t>(feedback_slot));
  } else {
    UNIMPLEMENTED();
  }
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor,
                                                Register first_arg,
                                                size_t arg_count) {
  if (!first_arg.is_valid()) {
    DCHECK_EQ(0u, arg_count);
    first_arg = Register(0);
  }
  DCHECK(FitsInIdx8Operand(arg_count));
  Output(Bytecode::kNew, constructor.ToOperand(), first_arg.ToOperand(),
         static_cast<uint8_t>(arg_count));
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
    Runtime::FunctionId function_id, Register first_arg, size_t arg_count) {
  DCHECK_EQ(1, Runtime::FunctionForId(function_id)->result_size);
  DCHECK(FitsInIdx16Operand(function_id));
  DCHECK(FitsInIdx8Operand(arg_count));
  if (!first_arg.is_valid()) {
    DCHECK_EQ(0u, arg_count);
    first_arg = Register(0);
  }
  Output(Bytecode::kCallRuntime, static_cast<uint16_t>(function_id),
         first_arg.ToOperand(), static_cast<uint8_t>(arg_count));
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair(
    Runtime::FunctionId function_id, Register first_arg, size_t arg_count,
    Register first_return) {
  DCHECK_EQ(2, Runtime::FunctionForId(function_id)->result_size);
  DCHECK(FitsInIdx16Operand(function_id));
  DCHECK(FitsInIdx8Operand(arg_count));
  if (!first_arg.is_valid()) {
    DCHECK_EQ(0u, arg_count);
    first_arg = Register(0);
  }
  Output(Bytecode::kCallRuntimeForPair, static_cast<uint16_t>(function_id),
         first_arg.ToOperand(), static_cast<uint8_t>(arg_count),
         first_return.ToOperand());
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(int context_index,
                                                          Register receiver,
                                                          size_t arg_count) {
  DCHECK(FitsInIdx16Operand(context_index));
  DCHECK(FitsInIdx8Operand(arg_count));
  Output(Bytecode::kCallJSRuntime, static_cast<uint16_t>(context_index),
         receiver.ToOperand(), static_cast<uint8_t>(arg_count));
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::Delete(Register object,
                                                   LanguageMode language_mode) {
  Output(BytecodeForDelete(language_mode), object.ToOperand());
  return *this;
}


BytecodeArrayBuilder& BytecodeArrayBuilder::DeleteLookupSlot() {
  Output(Bytecode::kDeleteLookupSlot);
  return *this;
}


size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) {
  return constant_array_builder()->Insert(object);
}


int BytecodeArrayBuilder::BorrowTemporaryRegister() {
  if (free_temporaries_.empty()) {
    temporary_register_count_ += 1;
    return last_temporary_register().index();
  } else {
    auto pos = free_temporaries_.begin();
    int retval = *pos;
    free_temporaries_.erase(pos);
    return retval;
  }
}


int BytecodeArrayBuilder::BorrowTemporaryRegisterNotInRange(int start_index,
                                                            int end_index) {
  auto index = free_temporaries_.lower_bound(start_index);
  if (index == free_temporaries_.begin()) {
    // If start_index is the first free register, check for a register
    // greater than end_index.
    index = free_temporaries_.upper_bound(end_index);
    if (index == free_temporaries_.end()) {
      temporary_register_count_ += 1;
      return last_temporary_register().index();
    }
  } else {
    // If there is a free register < start_index
    index--;
  }

  int retval = *index;
  free_temporaries_.erase(index);
  return retval;
}


void BytecodeArrayBuilder::BorrowConsecutiveTemporaryRegister(int reg_index) {
  DCHECK(free_temporaries_.find(reg_index) != free_temporaries_.end());
  free_temporaries_.erase(reg_index);
}


void BytecodeArrayBuilder::ReturnTemporaryRegister(int reg_index) {
  DCHECK(free_temporaries_.find(reg_index) == free_temporaries_.end());
  free_temporaries_.insert(reg_index);
}


int BytecodeArrayBuilder::PrepareForConsecutiveTemporaryRegisters(
    size_t count) {
  if (count == 0) {
    return -1;
  }

  // Search within existing temporaries for a run.
  auto start = free_temporaries_.begin();
  size_t run_length = 0;
  for (auto run_end = start; run_end != free_temporaries_.end(); run_end++) {
    if (*run_end != *start + static_cast<int>(run_length)) {
      start = run_end;
      run_length = 0;
    }
    if (++run_length == count) {
      return *start;
    }
  }

  // Continue run if possible across existing last temporary.
  if (temporary_register_count_ > 0 &&
      (start == free_temporaries_.end() ||
       *start + static_cast<int>(run_length) !=
           last_temporary_register().index() + 1)) {
    run_length = 0;
  }

  // Ensure enough registers for run.
  while (run_length++ < count) {
    temporary_register_count_++;
    free_temporaries_.insert(last_temporary_register().index());
  }
  return last_temporary_register().index() - static_cast<int>(count) + 1;
}


bool BytecodeArrayBuilder::TemporaryRegisterIsLive(Register reg) const {
  if (temporary_register_count_ > 0) {
    DCHECK(reg.index() >= first_temporary_register().index() &&
           reg.index() <= last_temporary_register().index());
    return free_temporaries_.find(reg.index()) == free_temporaries_.end();
  } else {
    return false;
  }
}


bool BytecodeArrayBuilder::RegisterIsValid(Register reg) const {
  if (reg.is_function_context() || reg.is_function_closure() ||
      reg.is_new_target()) {
    return true;
  } else if (reg.is_parameter()) {
    int parameter_index = reg.ToParameterIndex(parameter_count_);
    return parameter_index >= 0 && parameter_index < parameter_count_;
  } else if (reg.index() < fixed_register_count()) {
    return true;
  } else {
    return TemporaryRegisterIsLive(reg);
  }
}


bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
                                          uint32_t operand_value) const {
  OperandType operand_type = Bytecodes::GetOperandType(bytecode, operand_index);
  switch (operand_type) {
    case OperandType::kNone:
      return false;
    case OperandType::kCount16:
    case OperandType::kIdx16:
      return static_cast<uint16_t>(operand_value) == operand_value;
    case OperandType::kCount8:
    case OperandType::kImm8:
    case OperandType::kIdx8:
      return static_cast<uint8_t>(operand_value) == operand_value;
    case OperandType::kMaybeReg8:
      if (operand_value == 0) {
        return true;
      }
    // Fall-through to kReg8 case.
    case OperandType::kReg8:
      return RegisterIsValid(
          Register::FromOperand(static_cast<uint8_t>(operand_value)));
    case OperandType::kRegPair8: {
      Register reg0 =
          Register::FromOperand(static_cast<uint8_t>(operand_value));
      Register reg1 = Register(reg0.index() + 1);
      return RegisterIsValid(reg0) && RegisterIsValid(reg1);
    }
    case OperandType::kReg16:
      if (bytecode != Bytecode::kExchange &&
          bytecode != Bytecode::kExchangeWide) {
        return false;
      }
      return RegisterIsValid(
          Register::FromWideOperand(static_cast<uint16_t>(operand_value)));
  }
  UNREACHABLE();
  return false;
}


bool BytecodeArrayBuilder::LastBytecodeInSameBlock() const {
  return last_bytecode_start_ < bytecodes()->size() &&
         last_bytecode_start_ >= last_block_end_;
}


bool BytecodeArrayBuilder::IsRegisterInAccumulator(Register reg) {
  if (LastBytecodeInSameBlock()) {
    PreviousBytecodeHelper previous_bytecode(*this);
    Bytecode bytecode = previous_bytecode.GetBytecode();
    if ((bytecode == Bytecode::kLdar || bytecode == Bytecode::kStar) &&
        (reg == Register::FromOperand(previous_bytecode.GetOperand(0)))) {
      return true;
    }
  }
  return false;
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) {
  switch (op) {
    case Token::Value::ADD:
      return Bytecode::kAdd;
    case Token::Value::SUB:
      return Bytecode::kSub;
    case Token::Value::MUL:
      return Bytecode::kMul;
    case Token::Value::DIV:
      return Bytecode::kDiv;
    case Token::Value::MOD:
      return Bytecode::kMod;
    case Token::Value::BIT_OR:
      return Bytecode::kBitwiseOr;
    case Token::Value::BIT_XOR:
      return Bytecode::kBitwiseXor;
    case Token::Value::BIT_AND:
      return Bytecode::kBitwiseAnd;
    case Token::Value::SHL:
      return Bytecode::kShiftLeft;
    case Token::Value::SAR:
      return Bytecode::kShiftRight;
    case Token::Value::SHR:
      return Bytecode::kShiftRightLogical;
    default:
      UNREACHABLE();
      return static_cast<Bytecode>(-1);
  }
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForCountOperation(Token::Value op) {
  switch (op) {
    case Token::Value::ADD:
      return Bytecode::kInc;
    case Token::Value::SUB:
      return Bytecode::kDec;
    default:
      UNREACHABLE();
      return static_cast<Bytecode>(-1);
  }
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) {
  switch (op) {
    case Token::Value::EQ:
      return Bytecode::kTestEqual;
    case Token::Value::NE:
      return Bytecode::kTestNotEqual;
    case Token::Value::EQ_STRICT:
      return Bytecode::kTestEqualStrict;
    case Token::Value::NE_STRICT:
      return Bytecode::kTestNotEqualStrict;
    case Token::Value::LT:
      return Bytecode::kTestLessThan;
    case Token::Value::GT:
      return Bytecode::kTestGreaterThan;
    case Token::Value::LTE:
      return Bytecode::kTestLessThanOrEqual;
    case Token::Value::GTE:
      return Bytecode::kTestGreaterThanOrEqual;
    case Token::Value::INSTANCEOF:
      return Bytecode::kTestInstanceOf;
    case Token::Value::IN:
      return Bytecode::kTestIn;
    default:
      UNREACHABLE();
      return static_cast<Bytecode>(-1);
  }
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForWideOperands(Bytecode bytecode) {
  switch (bytecode) {
    case Bytecode::kLoadICSloppy:
      return Bytecode::kLoadICSloppyWide;
    case Bytecode::kLoadICStrict:
      return Bytecode::kLoadICStrictWide;
    case Bytecode::kKeyedLoadICSloppy:
      return Bytecode::kKeyedLoadICSloppyWide;
    case Bytecode::kKeyedLoadICStrict:
      return Bytecode::kKeyedLoadICStrictWide;
    case Bytecode::kStoreICSloppy:
      return Bytecode::kStoreICSloppyWide;
    case Bytecode::kStoreICStrict:
      return Bytecode::kStoreICStrictWide;
    case Bytecode::kKeyedStoreICSloppy:
      return Bytecode::kKeyedStoreICSloppyWide;
    case Bytecode::kKeyedStoreICStrict:
      return Bytecode::kKeyedStoreICStrictWide;
    case Bytecode::kLdaGlobalSloppy:
      return Bytecode::kLdaGlobalSloppyWide;
    case Bytecode::kLdaGlobalStrict:
      return Bytecode::kLdaGlobalStrictWide;
    case Bytecode::kLdaGlobalInsideTypeofSloppy:
      return Bytecode::kLdaGlobalInsideTypeofSloppyWide;
    case Bytecode::kLdaGlobalInsideTypeofStrict:
      return Bytecode::kLdaGlobalInsideTypeofStrictWide;
    case Bytecode::kStaGlobalSloppy:
      return Bytecode::kStaGlobalSloppyWide;
    case Bytecode::kStaGlobalStrict:
      return Bytecode::kStaGlobalStrictWide;
    case Bytecode::kLdaLookupSlot:
      return Bytecode::kLdaLookupSlotWide;
    case Bytecode::kLdaLookupSlotInsideTypeof:
      return Bytecode::kLdaLookupSlotInsideTypeofWide;
    case Bytecode::kStaLookupSlotStrict:
      return Bytecode::kStaLookupSlotStrictWide;
    case Bytecode::kStaLookupSlotSloppy:
      return Bytecode::kStaLookupSlotSloppyWide;
    default:
      UNREACHABLE();
      return static_cast<Bytecode>(-1);
  }
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForLoadIC(LanguageMode language_mode) {
  switch (language_mode) {
    case SLOPPY:
      return Bytecode::kLoadICSloppy;
    case STRICT:
      return Bytecode::kLoadICStrict;
    case STRONG:
      UNIMPLEMENTED();
    default:
      UNREACHABLE();
  }
  return static_cast<Bytecode>(-1);
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForKeyedLoadIC(
    LanguageMode language_mode) {
  switch (language_mode) {
    case SLOPPY:
      return Bytecode::kKeyedLoadICSloppy;
    case STRICT:
      return Bytecode::kKeyedLoadICStrict;
    case STRONG:
      UNIMPLEMENTED();
    default:
      UNREACHABLE();
  }
  return static_cast<Bytecode>(-1);
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreIC(LanguageMode language_mode) {
  switch (language_mode) {
    case SLOPPY:
      return Bytecode::kStoreICSloppy;
    case STRICT:
      return Bytecode::kStoreICStrict;
    case STRONG:
      UNIMPLEMENTED();
    default:
      UNREACHABLE();
  }
  return static_cast<Bytecode>(-1);
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForKeyedStoreIC(
    LanguageMode language_mode) {
  switch (language_mode) {
    case SLOPPY:
      return Bytecode::kKeyedStoreICSloppy;
    case STRICT:
      return Bytecode::kKeyedStoreICStrict;
    case STRONG:
      UNIMPLEMENTED();
    default:
      UNREACHABLE();
  }
  return static_cast<Bytecode>(-1);
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForLoadGlobal(LanguageMode language_mode,
                                                     TypeofMode typeof_mode) {
  switch (language_mode) {
    case SLOPPY:
      return typeof_mode == INSIDE_TYPEOF
                 ? Bytecode::kLdaGlobalInsideTypeofSloppy
                 : Bytecode::kLdaGlobalSloppy;
    case STRICT:
      return typeof_mode == INSIDE_TYPEOF
                 ? Bytecode::kLdaGlobalInsideTypeofStrict
                 : Bytecode::kLdaGlobalStrict;
    case STRONG:
      UNIMPLEMENTED();
    default:
      UNREACHABLE();
  }
  return static_cast<Bytecode>(-1);
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreGlobal(
    LanguageMode language_mode) {
  switch (language_mode) {
    case SLOPPY:
      return Bytecode::kStaGlobalSloppy;
    case STRICT:
      return Bytecode::kStaGlobalStrict;
    case STRONG:
      UNIMPLEMENTED();
    default:
      UNREACHABLE();
  }
  return static_cast<Bytecode>(-1);
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreLookupSlot(
    LanguageMode language_mode) {
  switch (language_mode) {
    case SLOPPY:
      return Bytecode::kStaLookupSlotSloppy;
    case STRICT:
      return Bytecode::kStaLookupSlotStrict;
    case STRONG:
      UNIMPLEMENTED();
    default:
      UNREACHABLE();
  }
  return static_cast<Bytecode>(-1);
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForCreateArguments(
    CreateArgumentsType type) {
  switch (type) {
    case CreateArgumentsType::kMappedArguments:
      return Bytecode::kCreateMappedArguments;
    case CreateArgumentsType::kUnmappedArguments:
      return Bytecode::kCreateUnmappedArguments;
    default:
      UNREACHABLE();
  }
  return static_cast<Bytecode>(-1);
}


// static
Bytecode BytecodeArrayBuilder::BytecodeForDelete(LanguageMode language_mode) {
  switch (language_mode) {
    case SLOPPY:
      return Bytecode::kDeletePropertySloppy;
    case STRICT:
      return Bytecode::kDeletePropertyStrict;
    case STRONG:
      UNIMPLEMENTED();
    default:
      UNREACHABLE();
  }
  return static_cast<Bytecode>(-1);
}


// static
bool BytecodeArrayBuilder::FitsInIdx8Operand(int value) {
  return kMinUInt8 <= value && value <= kMaxUInt8;
}


// static
bool BytecodeArrayBuilder::FitsInIdx8Operand(size_t value) {
  return value <= static_cast<size_t>(kMaxUInt8);
}


// static
bool BytecodeArrayBuilder::FitsInImm8Operand(int value) {
  return kMinInt8 <= value && value <= kMaxInt8;
}


// static
bool BytecodeArrayBuilder::FitsInIdx16Operand(int value) {
  return kMinUInt16 <= value && value <= kMaxUInt16;
}


// static
bool BytecodeArrayBuilder::FitsInIdx16Operand(size_t value) {
  return value <= static_cast<size_t>(kMaxUInt16);
}


// static
bool BytecodeArrayBuilder::FitsInReg8Operand(Register value) {
  return kMinInt8 <= value.index() && value.index() <= kMaxInt8;
}


// static
bool BytecodeArrayBuilder::FitsInReg16Operand(Register value) {
  return kMinInt16 <= value.index() && value.index() <= kMaxInt16;
}

}  // namespace interpreter
}  // namespace internal
}  // namespace v8