// Copyright 2012 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/code-stubs.h"

#include <sstream>

#include "src/arguments.h"
#include "src/ast/ast.h"
#include "src/bootstrapper.h"
#include "src/code-factory.h"
#include "src/code-stub-assembler.h"
#include "src/counters.h"
#include "src/factory.h"
#include "src/gdb-jit.h"
#include "src/heap/heap-inl.h"
#include "src/ic/ic-stats.h"
#include "src/ic/ic.h"
#include "src/macro-assembler.h"
#include "src/objects-inl.h"
#include "src/tracing/tracing-category-observer.h"

namespace v8 {
namespace internal {

using compiler::CodeAssemblerState;

RUNTIME_FUNCTION(UnexpectedStubMiss) {
  FATAL("Unexpected deopt of a stub");
  return Smi::kZero;
}

CodeStubDescriptor::CodeStubDescriptor(CodeStub* stub)
    : isolate_(stub->isolate()),
      call_descriptor_(stub->GetCallInterfaceDescriptor()),
      stack_parameter_count_(no_reg),
      hint_stack_parameter_count_(-1),
      function_mode_(NOT_JS_FUNCTION_STUB_MODE),
      deoptimization_handler_(NULL),
      miss_handler_(),
      has_miss_handler_(false) {
  stub->InitializeDescriptor(this);
}

CodeStubDescriptor::CodeStubDescriptor(Isolate* isolate, uint32_t stub_key)
    : isolate_(isolate),
      stack_parameter_count_(no_reg),
      hint_stack_parameter_count_(-1),
      function_mode_(NOT_JS_FUNCTION_STUB_MODE),
      deoptimization_handler_(NULL),
      miss_handler_(),
      has_miss_handler_(false) {
  CodeStub::InitializeDescriptor(isolate, stub_key, this);
}


void CodeStubDescriptor::Initialize(Address deoptimization_handler,
                                    int hint_stack_parameter_count,
                                    StubFunctionMode function_mode) {
  deoptimization_handler_ = deoptimization_handler;
  hint_stack_parameter_count_ = hint_stack_parameter_count;
  function_mode_ = function_mode;
}


void CodeStubDescriptor::Initialize(Register stack_parameter_count,
                                    Address deoptimization_handler,
                                    int hint_stack_parameter_count,
                                    StubFunctionMode function_mode) {
  Initialize(deoptimization_handler, hint_stack_parameter_count, function_mode);
  stack_parameter_count_ = stack_parameter_count;
}


bool CodeStub::FindCodeInCache(Code** code_out) {
  UnseededNumberDictionary* stubs = isolate()->heap()->code_stubs();
  int index = stubs->FindEntry(isolate(), GetKey());
  if (index != UnseededNumberDictionary::kNotFound) {
    *code_out = Code::cast(stubs->ValueAt(index));
    return true;
  }
  return false;
}


void CodeStub::RecordCodeGeneration(Handle<Code> code) {
  std::ostringstream os;
  os << *this;
  PROFILE(isolate(),
          CodeCreateEvent(CodeEventListener::STUB_TAG,
                          AbstractCode::cast(*code), os.str().c_str()));
  Counters* counters = isolate()->counters();
  counters->total_stubs_code_size()->Increment(code->instruction_size());
#ifdef DEBUG
  code->VerifyEmbeddedObjects();
#endif
}


Code::Kind CodeStub::GetCodeKind() const {
  return Code::STUB;
}


Code::Flags CodeStub::GetCodeFlags() const {
  return Code::ComputeFlags(GetCodeKind(), GetExtraICState());
}

Handle<Code> CodeStub::GetCodeCopy(const FindAndReplacePattern& pattern) {
  Handle<Code> ic = GetCode();
  ic = isolate()->factory()->CopyCode(ic);
  ic->FindAndReplace(pattern);
  RecordCodeGeneration(ic);
  return ic;
}

void CodeStub::DeleteStubFromCacheForTesting() {
  Heap* heap = isolate_->heap();
  Handle<UnseededNumberDictionary> dict(heap->code_stubs());
  dict = UnseededNumberDictionary::DeleteKey(dict, GetKey());
  heap->SetRootCodeStubs(*dict);
}

Handle<Code> PlatformCodeStub::GenerateCode() {
  Factory* factory = isolate()->factory();

  // Generate the new code.
  MacroAssembler masm(isolate(), NULL, 256, CodeObjectRequired::kYes);

  {
    // Update the static counter each time a new code stub is generated.
    isolate()->counters()->code_stubs()->Increment();

    // Generate the code for the stub.
    masm.set_generating_stub(true);
    // TODO(yangguo): remove this once we can serialize IC stubs.
    masm.enable_serializer();
    NoCurrentFrameScope scope(&masm);
    Generate(&masm);
  }

  // Create the code object.
  CodeDesc desc;
  masm.GetCode(&desc);
  // Copy the generated code into a heap object.
  Code::Flags flags = Code::ComputeFlags(GetCodeKind(), GetExtraICState());
  Handle<Code> new_object = factory->NewCode(
      desc, flags, masm.CodeObject(), NeedsImmovableCode());
  return new_object;
}


Handle<Code> CodeStub::GetCode() {
  Heap* heap = isolate()->heap();
  Code* code;
  if (UseSpecialCache() ? FindCodeInSpecialCache(&code)
                        : FindCodeInCache(&code)) {
    DCHECK(GetCodeKind() == code->kind());
    return Handle<Code>(code);
  }

  {
    HandleScope scope(isolate());

    Handle<Code> new_object = GenerateCode();
    new_object->set_stub_key(GetKey());
    FinishCode(new_object);
    RecordCodeGeneration(new_object);

#ifdef ENABLE_DISASSEMBLER
    if (FLAG_print_code_stubs) {
      CodeTracer::Scope trace_scope(isolate()->GetCodeTracer());
      OFStream os(trace_scope.file());
      std::ostringstream name;
      name << *this;
      new_object->Disassemble(name.str().c_str(), os);
      os << "\n";
    }
#endif

    if (UseSpecialCache()) {
      AddToSpecialCache(new_object);
    } else {
      // Update the dictionary and the root in Heap.
      Handle<UnseededNumberDictionary> dict =
          UnseededNumberDictionary::AtNumberPut(
              Handle<UnseededNumberDictionary>(heap->code_stubs()),
              GetKey(),
              new_object);
      heap->SetRootCodeStubs(*dict);
    }
    code = *new_object;
  }

  Activate(code);
  DCHECK(!NeedsImmovableCode() || Heap::IsImmovable(code) ||
         heap->code_space()->FirstPage()->Contains(code->address()));
  return Handle<Code>(code, isolate());
}


const char* CodeStub::MajorName(CodeStub::Major major_key) {
  switch (major_key) {
#define DEF_CASE(name) case name: return #name "Stub";
    CODE_STUB_LIST(DEF_CASE)
#undef DEF_CASE
    case NoCache:
      return "<NoCache>Stub";
    case NUMBER_OF_IDS:
      UNREACHABLE();
      return NULL;
  }
  return NULL;
}


void CodeStub::PrintBaseName(std::ostream& os) const {  // NOLINT
  os << MajorName(MajorKey());
}


void CodeStub::PrintName(std::ostream& os) const {  // NOLINT
  PrintBaseName(os);
  PrintState(os);
}


void CodeStub::Dispatch(Isolate* isolate, uint32_t key, void** value_out,
                        DispatchedCall call) {
  switch (MajorKeyFromKey(key)) {
#define DEF_CASE(NAME)             \
  case NAME: {                     \
    NAME##Stub stub(key, isolate); \
    CodeStub* pstub = &stub;       \
    call(pstub, value_out);        \
    break;                         \
  }
    CODE_STUB_LIST(DEF_CASE)
#undef DEF_CASE
    case NUMBER_OF_IDS:
    case NoCache:
      UNREACHABLE();
      break;
  }
}


static void InitializeDescriptorDispatchedCall(CodeStub* stub,
                                               void** value_out) {
  CodeStubDescriptor* descriptor_out =
      reinterpret_cast<CodeStubDescriptor*>(value_out);
  stub->InitializeDescriptor(descriptor_out);
  descriptor_out->set_call_descriptor(stub->GetCallInterfaceDescriptor());
}


void CodeStub::InitializeDescriptor(Isolate* isolate, uint32_t key,
                                    CodeStubDescriptor* desc) {
  void** value_out = reinterpret_cast<void**>(desc);
  Dispatch(isolate, key, value_out, &InitializeDescriptorDispatchedCall);
}


void CodeStub::GetCodeDispatchCall(CodeStub* stub, void** value_out) {
  Handle<Code>* code_out = reinterpret_cast<Handle<Code>*>(value_out);
  // Code stubs with special cache cannot be recreated from stub key.
  *code_out = stub->UseSpecialCache() ? Handle<Code>() : stub->GetCode();
}


MaybeHandle<Code> CodeStub::GetCode(Isolate* isolate, uint32_t key) {
  HandleScope scope(isolate);
  Handle<Code> code;
  void** value_out = reinterpret_cast<void**>(&code);
  Dispatch(isolate, key, value_out, &GetCodeDispatchCall);
  return scope.CloseAndEscape(code);
}


// static
void BinaryOpICStub::GenerateAheadOfTime(Isolate* isolate) {
  if (FLAG_minimal) return;
  // Generate the uninitialized versions of the stub.
  for (int op = Token::BIT_OR; op <= Token::MOD; ++op) {
    BinaryOpICStub stub(isolate, static_cast<Token::Value>(op));
    stub.GetCode();
  }

  // Generate special versions of the stub.
  BinaryOpICState::GenerateAheadOfTime(isolate, &GenerateAheadOfTime);
}


void BinaryOpICStub::PrintState(std::ostream& os) const {  // NOLINT
  os << state();
}


// static
void BinaryOpICStub::GenerateAheadOfTime(Isolate* isolate,
                                         const BinaryOpICState& state) {
  if (FLAG_minimal) return;
  BinaryOpICStub stub(isolate, state);
  stub.GetCode();
}


// static
void BinaryOpICWithAllocationSiteStub::GenerateAheadOfTime(Isolate* isolate) {
  // Generate special versions of the stub.
  BinaryOpICState::GenerateAheadOfTime(isolate, &GenerateAheadOfTime);
}


void BinaryOpICWithAllocationSiteStub::PrintState(
    std::ostream& os) const {  // NOLINT
  os << state();
}


// static
void BinaryOpICWithAllocationSiteStub::GenerateAheadOfTime(
    Isolate* isolate, const BinaryOpICState& state) {
  if (state.CouldCreateAllocationMementos()) {
    BinaryOpICWithAllocationSiteStub stub(isolate, state);
    stub.GetCode();
  }
}

void StringAddStub::PrintBaseName(std::ostream& os) const {  // NOLINT
  os << "StringAddStub_" << flags() << "_" << pretenure_flag();
}

void StringAddStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);
  Node* left = assembler.Parameter(Descriptor::kLeft);
  Node* right = assembler.Parameter(Descriptor::kRight);
  Node* context = assembler.Parameter(Descriptor::kContext);

  if ((flags() & STRING_ADD_CHECK_LEFT) != 0) {
    DCHECK((flags() & STRING_ADD_CONVERT) != 0);
    // TODO(danno): The ToString and JSReceiverToPrimitive below could be
    // combined to avoid duplicate smi and instance type checks.
    left = assembler.ToString(context,
                              assembler.JSReceiverToPrimitive(context, left));
  }
  if ((flags() & STRING_ADD_CHECK_RIGHT) != 0) {
    DCHECK((flags() & STRING_ADD_CONVERT) != 0);
    // TODO(danno): The ToString and JSReceiverToPrimitive below could be
    // combined to avoid duplicate smi and instance type checks.
    right = assembler.ToString(context,
                               assembler.JSReceiverToPrimitive(context, right));
  }

  if ((flags() & STRING_ADD_CHECK_BOTH) == 0) {
    CodeStubAssembler::AllocationFlag flags =
        (pretenure_flag() == TENURED) ? CodeStubAssembler::kPretenured
                                      : CodeStubAssembler::kNone;
    assembler.Return(assembler.StringAdd(context, left, right, flags));
  } else {
    Callable callable = CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE,
                                               pretenure_flag());
    assembler.TailCallStub(callable, context, left, right);
  }
}

InlineCacheState CompareICStub::GetICState() const {
  CompareICState::State state = Max(left(), right());
  switch (state) {
    case CompareICState::UNINITIALIZED:
      return ::v8::internal::UNINITIALIZED;
    case CompareICState::BOOLEAN:
    case CompareICState::SMI:
    case CompareICState::NUMBER:
    case CompareICState::INTERNALIZED_STRING:
    case CompareICState::STRING:
    case CompareICState::UNIQUE_NAME:
    case CompareICState::RECEIVER:
    case CompareICState::KNOWN_RECEIVER:
      return MONOMORPHIC;
    case CompareICState::GENERIC:
      return ::v8::internal::GENERIC;
  }
  UNREACHABLE();
  return ::v8::internal::UNINITIALIZED;
}


Condition CompareICStub::GetCondition() const {
  return CompareIC::ComputeCondition(op());
}


void CompareICStub::Generate(MacroAssembler* masm) {
  switch (state()) {
    case CompareICState::UNINITIALIZED:
      GenerateMiss(masm);
      break;
    case CompareICState::BOOLEAN:
      GenerateBooleans(masm);
      break;
    case CompareICState::SMI:
      GenerateSmis(masm);
      break;
    case CompareICState::NUMBER:
      GenerateNumbers(masm);
      break;
    case CompareICState::STRING:
      GenerateStrings(masm);
      break;
    case CompareICState::INTERNALIZED_STRING:
      GenerateInternalizedStrings(masm);
      break;
    case CompareICState::UNIQUE_NAME:
      GenerateUniqueNames(masm);
      break;
    case CompareICState::RECEIVER:
      GenerateReceivers(masm);
      break;
    case CompareICState::KNOWN_RECEIVER:
      DCHECK(*known_map_ != NULL);
      GenerateKnownReceivers(masm);
      break;
    case CompareICState::GENERIC:
      GenerateGeneric(masm);
      break;
  }
}

Handle<Code> TurboFanCodeStub::GenerateCode() {
  const char* name = CodeStub::MajorName(MajorKey());
  Zone zone(isolate()->allocator(), ZONE_NAME);
  CallInterfaceDescriptor descriptor(GetCallInterfaceDescriptor());
  compiler::CodeAssemblerState state(isolate(), &zone, descriptor,
                                     GetCodeFlags(), name);
  GenerateAssembly(&state);
  return compiler::CodeAssembler::GenerateCode(&state);
}

void ElementsTransitionAndStoreStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef CodeStubAssembler::Label Label;
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  Node* receiver = assembler.Parameter(Descriptor::kReceiver);
  Node* key = assembler.Parameter(Descriptor::kName);
  Node* value = assembler.Parameter(Descriptor::kValue);
  Node* map = assembler.Parameter(Descriptor::kMap);
  Node* slot = assembler.Parameter(Descriptor::kSlot);
  Node* vector = assembler.Parameter(Descriptor::kVector);
  Node* context = assembler.Parameter(Descriptor::kContext);

  assembler.Comment(
      "ElementsTransitionAndStoreStub: from_kind=%s, to_kind=%s,"
      " is_jsarray=%d, store_mode=%d",
      ElementsKindToString(from_kind()), ElementsKindToString(to_kind()),
      is_jsarray(), store_mode());

  Label miss(&assembler);

  if (FLAG_trace_elements_transitions) {
    // Tracing elements transitions is the job of the runtime.
    assembler.Goto(&miss);
  } else {
    assembler.TransitionElementsKind(receiver, map, from_kind(), to_kind(),
                                     is_jsarray(), &miss);
    assembler.EmitElementStore(receiver, key, value, is_jsarray(), to_kind(),
                               store_mode(), &miss);
    assembler.Return(value);
  }

  assembler.Bind(&miss);
  {
    assembler.Comment("Miss");
    assembler.TailCallRuntime(Runtime::kElementsTransitionAndStoreIC_Miss,
                              context, receiver, key, value, map, slot, vector);
  }
}

void AllocateHeapNumberStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  Node* result = assembler.AllocateHeapNumber();
  assembler.Return(result);
}

void StringLengthStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  CodeStubAssembler assembler(state);
  compiler::Node* value = assembler.Parameter(0);
  compiler::Node* string = assembler.LoadJSValueValue(value);
  compiler::Node* result = assembler.LoadStringLength(string);
  assembler.Return(result);
}

#define BINARY_OP_STUB(Name)                                                  \
  void Name::GenerateAssembly(compiler::CodeAssemblerState* state) const {    \
    typedef BinaryOpWithVectorDescriptor Descriptor;                          \
    CodeStubAssembler assembler(state);                                       \
    assembler.Return(Generate(                                                \
        &assembler, assembler.Parameter(Descriptor::kLeft),                   \
        assembler.Parameter(Descriptor::kRight),                              \
        assembler.ChangeUint32ToWord(assembler.Parameter(Descriptor::kSlot)), \
        assembler.Parameter(Descriptor::kVector),                             \
        assembler.Parameter(Descriptor::kContext)));                          \
  }
BINARY_OP_STUB(AddWithFeedbackStub)
BINARY_OP_STUB(SubtractWithFeedbackStub)
BINARY_OP_STUB(MultiplyWithFeedbackStub)
BINARY_OP_STUB(DivideWithFeedbackStub)
BINARY_OP_STUB(ModulusWithFeedbackStub)
#undef BINARY_OP_STUB

// static
compiler::Node* AddWithFeedbackStub::Generate(CodeStubAssembler* assembler,
                                              compiler::Node* lhs,
                                              compiler::Node* rhs,
                                              compiler::Node* slot_id,
                                              compiler::Node* feedback_vector,
                                              compiler::Node* context) {
  typedef CodeStubAssembler::Label Label;
  typedef compiler::Node Node;
  typedef CodeStubAssembler::Variable Variable;

  // Shared entry for floating point addition.
  Label do_fadd(assembler), if_lhsisnotnumber(assembler, Label::kDeferred),
      check_rhsisoddball(assembler, Label::kDeferred),
      call_with_oddball_feedback(assembler), call_with_any_feedback(assembler),
      call_add_stub(assembler), end(assembler);
  Variable var_fadd_lhs(assembler, MachineRepresentation::kFloat64),
      var_fadd_rhs(assembler, MachineRepresentation::kFloat64),
      var_type_feedback(assembler, MachineRepresentation::kTaggedSigned),
      var_result(assembler, MachineRepresentation::kTagged);

  // Check if the {lhs} is a Smi or a HeapObject.
  Label if_lhsissmi(assembler), if_lhsisnotsmi(assembler);
  assembler->Branch(assembler->TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi);

  assembler->Bind(&if_lhsissmi);
  {
    // Check if the {rhs} is also a Smi.
    Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
    assembler->Branch(assembler->TaggedIsSmi(rhs), &if_rhsissmi,
                      &if_rhsisnotsmi);

    assembler->Bind(&if_rhsissmi);
    {
      // Try fast Smi addition first.
      Node* pair =
          assembler->IntPtrAddWithOverflow(assembler->BitcastTaggedToWord(lhs),
                                           assembler->BitcastTaggedToWord(rhs));
      Node* overflow = assembler->Projection(1, pair);

      // Check if the Smi additon overflowed.
      Label if_overflow(assembler), if_notoverflow(assembler);
      assembler->Branch(overflow, &if_overflow, &if_notoverflow);

      assembler->Bind(&if_overflow);
      {
        var_fadd_lhs.Bind(assembler->SmiToFloat64(lhs));
        var_fadd_rhs.Bind(assembler->SmiToFloat64(rhs));
        assembler->Goto(&do_fadd);
      }

      assembler->Bind(&if_notoverflow);
      {
        var_type_feedback.Bind(
            assembler->SmiConstant(BinaryOperationFeedback::kSignedSmall));
        var_result.Bind(assembler->BitcastWordToTaggedSigned(
            assembler->Projection(0, pair)));
        assembler->Goto(&end);
      }
    }

    assembler->Bind(&if_rhsisnotsmi);
    {
      // Load the map of {rhs}.
      Node* rhs_map = assembler->LoadMap(rhs);

      // Check if the {rhs} is a HeapNumber.
      assembler->GotoIfNot(assembler->IsHeapNumberMap(rhs_map),
                           &check_rhsisoddball);

      var_fadd_lhs.Bind(assembler->SmiToFloat64(lhs));
      var_fadd_rhs.Bind(assembler->LoadHeapNumberValue(rhs));
      assembler->Goto(&do_fadd);
    }
  }

  assembler->Bind(&if_lhsisnotsmi);
  {
    // Load the map of {lhs}.
    Node* lhs_map = assembler->LoadMap(lhs);

    // Check if {lhs} is a HeapNumber.
    assembler->GotoIfNot(assembler->IsHeapNumberMap(lhs_map),
                         &if_lhsisnotnumber);

    // Check if the {rhs} is Smi.
    Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
    assembler->Branch(assembler->TaggedIsSmi(rhs), &if_rhsissmi,
                      &if_rhsisnotsmi);

    assembler->Bind(&if_rhsissmi);
    {
      var_fadd_lhs.Bind(assembler->LoadHeapNumberValue(lhs));
      var_fadd_rhs.Bind(assembler->SmiToFloat64(rhs));
      assembler->Goto(&do_fadd);
    }

    assembler->Bind(&if_rhsisnotsmi);
    {
      // Load the map of {rhs}.
      Node* rhs_map = assembler->LoadMap(rhs);

      // Check if the {rhs} is a HeapNumber.
      assembler->GotoIfNot(assembler->IsHeapNumberMap(rhs_map),
                           &check_rhsisoddball);

      var_fadd_lhs.Bind(assembler->LoadHeapNumberValue(lhs));
      var_fadd_rhs.Bind(assembler->LoadHeapNumberValue(rhs));
      assembler->Goto(&do_fadd);
    }
  }

  assembler->Bind(&do_fadd);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kNumber));
    Node* value =
        assembler->Float64Add(var_fadd_lhs.value(), var_fadd_rhs.value());
    Node* result = assembler->AllocateHeapNumberWithValue(value);
    var_result.Bind(result);
    assembler->Goto(&end);
  }

  assembler->Bind(&if_lhsisnotnumber);
  {
    // No checks on rhs are done yet. We just know lhs is not a number or Smi.
    Label if_lhsisoddball(assembler), if_lhsisnotoddball(assembler);
    Node* lhs_instance_type = assembler->LoadInstanceType(lhs);
    Node* lhs_is_oddball = assembler->Word32Equal(
        lhs_instance_type, assembler->Int32Constant(ODDBALL_TYPE));
    assembler->Branch(lhs_is_oddball, &if_lhsisoddball, &if_lhsisnotoddball);

    assembler->Bind(&if_lhsisoddball);
    {
      assembler->GotoIf(assembler->TaggedIsSmi(rhs),
                        &call_with_oddball_feedback);

      // Load the map of the {rhs}.
      Node* rhs_map = assembler->LoadMap(rhs);

      // Check if {rhs} is a HeapNumber.
      assembler->Branch(assembler->IsHeapNumberMap(rhs_map),
                        &call_with_oddball_feedback, &check_rhsisoddball);
    }

    assembler->Bind(&if_lhsisnotoddball);
    {
      // Exit unless {lhs} is a string
      assembler->GotoIfNot(assembler->IsStringInstanceType(lhs_instance_type),
                           &call_with_any_feedback);

      // Check if the {rhs} is a smi, and exit the string check early if it is.
      assembler->GotoIf(assembler->TaggedIsSmi(rhs), &call_with_any_feedback);

      Node* rhs_instance_type = assembler->LoadInstanceType(rhs);

      // Exit unless {rhs} is a string. Since {lhs} is a string we no longer
      // need an Oddball check.
      assembler->GotoIfNot(assembler->IsStringInstanceType(rhs_instance_type),
                           &call_with_any_feedback);

      var_type_feedback.Bind(
          assembler->SmiConstant(BinaryOperationFeedback::kString));
      Callable callable = CodeFactory::StringAdd(
          assembler->isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
      var_result.Bind(assembler->CallStub(callable, context, lhs, rhs));

      assembler->Goto(&end);
    }
  }

  assembler->Bind(&check_rhsisoddball);
  {
    // Check if rhs is an oddball. At this point we know lhs is either a
    // Smi or number or oddball and rhs is not a number or Smi.
    Node* rhs_instance_type = assembler->LoadInstanceType(rhs);
    Node* rhs_is_oddball = assembler->Word32Equal(
        rhs_instance_type, assembler->Int32Constant(ODDBALL_TYPE));
    assembler->Branch(rhs_is_oddball, &call_with_oddball_feedback,
                      &call_with_any_feedback);
  }

  assembler->Bind(&call_with_oddball_feedback);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
    assembler->Goto(&call_add_stub);
  }

  assembler->Bind(&call_with_any_feedback);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kAny));
    assembler->Goto(&call_add_stub);
  }

  assembler->Bind(&call_add_stub);
  {
    Callable callable = CodeFactory::Add(assembler->isolate());
    var_result.Bind(assembler->CallStub(callable, context, lhs, rhs));
    assembler->Goto(&end);
  }

  assembler->Bind(&end);
  assembler->UpdateFeedback(var_type_feedback.value(), feedback_vector,
                            slot_id);
  return var_result.value();
}

// static
compiler::Node* SubtractWithFeedbackStub::Generate(
    CodeStubAssembler* assembler, compiler::Node* lhs, compiler::Node* rhs,
    compiler::Node* slot_id, compiler::Node* feedback_vector,
    compiler::Node* context) {
  typedef CodeStubAssembler::Label Label;
  typedef compiler::Node Node;
  typedef CodeStubAssembler::Variable Variable;

  // Shared entry for floating point subtraction.
  Label do_fsub(assembler), end(assembler), call_subtract_stub(assembler),
      if_lhsisnotnumber(assembler), check_rhsisoddball(assembler),
      call_with_any_feedback(assembler);
  Variable var_fsub_lhs(assembler, MachineRepresentation::kFloat64),
      var_fsub_rhs(assembler, MachineRepresentation::kFloat64),
      var_type_feedback(assembler, MachineRepresentation::kTaggedSigned),
      var_result(assembler, MachineRepresentation::kTagged);

  // Check if the {lhs} is a Smi or a HeapObject.
  Label if_lhsissmi(assembler), if_lhsisnotsmi(assembler);
  assembler->Branch(assembler->TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi);

  assembler->Bind(&if_lhsissmi);
  {
    // Check if the {rhs} is also a Smi.
    Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
    assembler->Branch(assembler->TaggedIsSmi(rhs), &if_rhsissmi,
                      &if_rhsisnotsmi);

    assembler->Bind(&if_rhsissmi);
    {
      // Try a fast Smi subtraction first.
      Node* pair =
          assembler->IntPtrSubWithOverflow(assembler->BitcastTaggedToWord(lhs),
                                           assembler->BitcastTaggedToWord(rhs));
      Node* overflow = assembler->Projection(1, pair);

      // Check if the Smi subtraction overflowed.
      Label if_overflow(assembler), if_notoverflow(assembler);
      assembler->Branch(overflow, &if_overflow, &if_notoverflow);

      assembler->Bind(&if_overflow);
      {
        // lhs, rhs - smi and result - number. combined - number.
        // The result doesn't fit into Smi range.
        var_fsub_lhs.Bind(assembler->SmiToFloat64(lhs));
        var_fsub_rhs.Bind(assembler->SmiToFloat64(rhs));
        assembler->Goto(&do_fsub);
      }

      assembler->Bind(&if_notoverflow);
      // lhs, rhs, result smi. combined - smi.
      var_type_feedback.Bind(
          assembler->SmiConstant(BinaryOperationFeedback::kSignedSmall));
      var_result.Bind(
          assembler->BitcastWordToTaggedSigned(assembler->Projection(0, pair)));
      assembler->Goto(&end);
    }

    assembler->Bind(&if_rhsisnotsmi);
    {
      // Load the map of the {rhs}.
      Node* rhs_map = assembler->LoadMap(rhs);

      // Check if {rhs} is a HeapNumber.
      assembler->GotoIfNot(assembler->IsHeapNumberMap(rhs_map),
                           &check_rhsisoddball);

      // Perform a floating point subtraction.
      var_fsub_lhs.Bind(assembler->SmiToFloat64(lhs));
      var_fsub_rhs.Bind(assembler->LoadHeapNumberValue(rhs));
      assembler->Goto(&do_fsub);
    }
  }

  assembler->Bind(&if_lhsisnotsmi);
  {
    // Load the map of the {lhs}.
    Node* lhs_map = assembler->LoadMap(lhs);

    // Check if the {lhs} is a HeapNumber.
    assembler->GotoIfNot(assembler->IsHeapNumberMap(lhs_map),
                         &if_lhsisnotnumber);

    // Check if the {rhs} is a Smi.
    Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
    assembler->Branch(assembler->TaggedIsSmi(rhs), &if_rhsissmi,
                      &if_rhsisnotsmi);

    assembler->Bind(&if_rhsissmi);
    {
      // Perform a floating point subtraction.
      var_fsub_lhs.Bind(assembler->LoadHeapNumberValue(lhs));
      var_fsub_rhs.Bind(assembler->SmiToFloat64(rhs));
      assembler->Goto(&do_fsub);
    }

    assembler->Bind(&if_rhsisnotsmi);
    {
      // Load the map of the {rhs}.
      Node* rhs_map = assembler->LoadMap(rhs);

      // Check if the {rhs} is a HeapNumber.
      assembler->GotoIfNot(assembler->IsHeapNumberMap(rhs_map),
                           &check_rhsisoddball);

      // Perform a floating point subtraction.
      var_fsub_lhs.Bind(assembler->LoadHeapNumberValue(lhs));
      var_fsub_rhs.Bind(assembler->LoadHeapNumberValue(rhs));
      assembler->Goto(&do_fsub);
    }
  }

  assembler->Bind(&do_fsub);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kNumber));
    Node* lhs_value = var_fsub_lhs.value();
    Node* rhs_value = var_fsub_rhs.value();
    Node* value = assembler->Float64Sub(lhs_value, rhs_value);
    var_result.Bind(assembler->AllocateHeapNumberWithValue(value));
    assembler->Goto(&end);
  }

  assembler->Bind(&if_lhsisnotnumber);
  {
    // No checks on rhs are done yet. We just know lhs is not a number or Smi.
    // Check if lhs is an oddball.
    Node* lhs_instance_type = assembler->LoadInstanceType(lhs);
    Node* lhs_is_oddball = assembler->Word32Equal(
        lhs_instance_type, assembler->Int32Constant(ODDBALL_TYPE));
    assembler->GotoIfNot(lhs_is_oddball, &call_with_any_feedback);

    Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
    assembler->Branch(assembler->TaggedIsSmi(rhs), &if_rhsissmi,
                      &if_rhsisnotsmi);

    assembler->Bind(&if_rhsissmi);
    {
      var_type_feedback.Bind(
          assembler->SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
      assembler->Goto(&call_subtract_stub);
    }

    assembler->Bind(&if_rhsisnotsmi);
    {
      // Load the map of the {rhs}.
      Node* rhs_map = assembler->LoadMap(rhs);

      // Check if {rhs} is a HeapNumber.
      assembler->GotoIfNot(assembler->IsHeapNumberMap(rhs_map),
                           &check_rhsisoddball);

      var_type_feedback.Bind(
          assembler->SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
      assembler->Goto(&call_subtract_stub);
    }
  }

  assembler->Bind(&check_rhsisoddball);
  {
    // Check if rhs is an oddball. At this point we know lhs is either a
    // Smi or number or oddball and rhs is not a number or Smi.
    Node* rhs_instance_type = assembler->LoadInstanceType(rhs);
    Node* rhs_is_oddball = assembler->Word32Equal(
        rhs_instance_type, assembler->Int32Constant(ODDBALL_TYPE));
    assembler->GotoIfNot(rhs_is_oddball, &call_with_any_feedback);

    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
    assembler->Goto(&call_subtract_stub);
  }

  assembler->Bind(&call_with_any_feedback);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kAny));
    assembler->Goto(&call_subtract_stub);
  }

  assembler->Bind(&call_subtract_stub);
  {
    Callable callable = CodeFactory::Subtract(assembler->isolate());
    var_result.Bind(assembler->CallStub(callable, context, lhs, rhs));
    assembler->Goto(&end);
  }

  assembler->Bind(&end);
  assembler->UpdateFeedback(var_type_feedback.value(), feedback_vector,
                            slot_id);
  return var_result.value();
}


// static
compiler::Node* MultiplyWithFeedbackStub::Generate(
    CodeStubAssembler* assembler, compiler::Node* lhs, compiler::Node* rhs,
    compiler::Node* slot_id, compiler::Node* feedback_vector,
    compiler::Node* context) {
  using compiler::Node;
  typedef CodeStubAssembler::Label Label;
  typedef CodeStubAssembler::Variable Variable;

  // Shared entry point for floating point multiplication.
  Label do_fmul(assembler), if_lhsisnotnumber(assembler, Label::kDeferred),
      check_rhsisoddball(assembler, Label::kDeferred),
      call_with_oddball_feedback(assembler), call_with_any_feedback(assembler),
      call_multiply_stub(assembler), end(assembler);
  Variable var_lhs_float64(assembler, MachineRepresentation::kFloat64),
      var_rhs_float64(assembler, MachineRepresentation::kFloat64),
      var_result(assembler, MachineRepresentation::kTagged),
      var_type_feedback(assembler, MachineRepresentation::kTaggedSigned);

  Label lhs_is_smi(assembler), lhs_is_not_smi(assembler);
  assembler->Branch(assembler->TaggedIsSmi(lhs), &lhs_is_smi, &lhs_is_not_smi);

  assembler->Bind(&lhs_is_smi);
  {
    Label rhs_is_smi(assembler), rhs_is_not_smi(assembler);
    assembler->Branch(assembler->TaggedIsSmi(rhs), &rhs_is_smi,
                      &rhs_is_not_smi);

    assembler->Bind(&rhs_is_smi);
    {
      // Both {lhs} and {rhs} are Smis. The result is not necessarily a smi,
      // in case of overflow.
      var_result.Bind(assembler->SmiMul(lhs, rhs));
      var_type_feedback.Bind(assembler->SelectSmiConstant(
          assembler->TaggedIsSmi(var_result.value()),
          BinaryOperationFeedback::kSignedSmall,
          BinaryOperationFeedback::kNumber));
      assembler->Goto(&end);
    }

    assembler->Bind(&rhs_is_not_smi);
    {
      Node* rhs_map = assembler->LoadMap(rhs);

      // Check if {rhs} is a HeapNumber.
      assembler->GotoIfNot(assembler->IsHeapNumberMap(rhs_map),
                           &check_rhsisoddball);

      // Convert {lhs} to a double and multiply it with the value of {rhs}.
      var_lhs_float64.Bind(assembler->SmiToFloat64(lhs));
      var_rhs_float64.Bind(assembler->LoadHeapNumberValue(rhs));
      assembler->Goto(&do_fmul);
    }
  }

  assembler->Bind(&lhs_is_not_smi);
  {
    Node* lhs_map = assembler->LoadMap(lhs);

    // Check if {lhs} is a HeapNumber.
    assembler->GotoIfNot(assembler->IsHeapNumberMap(lhs_map),
                         &if_lhsisnotnumber);

    // Check if {rhs} is a Smi.
    Label rhs_is_smi(assembler), rhs_is_not_smi(assembler);
    assembler->Branch(assembler->TaggedIsSmi(rhs), &rhs_is_smi,
                      &rhs_is_not_smi);

    assembler->Bind(&rhs_is_smi);
    {
      // Convert {rhs} to a double and multiply it with the value of {lhs}.
      var_lhs_float64.Bind(assembler->LoadHeapNumberValue(lhs));
      var_rhs_float64.Bind(assembler->SmiToFloat64(rhs));
      assembler->Goto(&do_fmul);
    }

    assembler->Bind(&rhs_is_not_smi);
    {
      Node* rhs_map = assembler->LoadMap(rhs);

      // Check if {rhs} is a HeapNumber.
      assembler->GotoIfNot(assembler->IsHeapNumberMap(rhs_map),
                           &check_rhsisoddball);

      // Both {lhs} and {rhs} are HeapNumbers. Load their values and
      // multiply them.
      var_lhs_float64.Bind(assembler->LoadHeapNumberValue(lhs));
      var_rhs_float64.Bind(assembler->LoadHeapNumberValue(rhs));
      assembler->Goto(&do_fmul);
    }
  }

  assembler->Bind(&do_fmul);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kNumber));
    Node* value =
        assembler->Float64Mul(var_lhs_float64.value(), var_rhs_float64.value());
    Node* result = assembler->AllocateHeapNumberWithValue(value);
    var_result.Bind(result);
    assembler->Goto(&end);
  }

  assembler->Bind(&if_lhsisnotnumber);
  {
    // No checks on rhs are done yet. We just know lhs is not a number or Smi.
    // Check if lhs is an oddball.
    Node* lhs_instance_type = assembler->LoadInstanceType(lhs);
    Node* lhs_is_oddball = assembler->Word32Equal(
        lhs_instance_type, assembler->Int32Constant(ODDBALL_TYPE));
    assembler->GotoIfNot(lhs_is_oddball, &call_with_any_feedback);

    assembler->GotoIf(assembler->TaggedIsSmi(rhs), &call_with_oddball_feedback);

    // Load the map of the {rhs}.
    Node* rhs_map = assembler->LoadMap(rhs);

    // Check if {rhs} is a HeapNumber.
    assembler->Branch(assembler->IsHeapNumberMap(rhs_map),
                      &call_with_oddball_feedback, &check_rhsisoddball);
  }

  assembler->Bind(&check_rhsisoddball);
  {
    // Check if rhs is an oddball. At this point we know lhs is either a
    // Smi or number or oddball and rhs is not a number or Smi.
    Node* rhs_instance_type = assembler->LoadInstanceType(rhs);
    Node* rhs_is_oddball = assembler->Word32Equal(
        rhs_instance_type, assembler->Int32Constant(ODDBALL_TYPE));
    assembler->Branch(rhs_is_oddball, &call_with_oddball_feedback,
                      &call_with_any_feedback);
  }

  assembler->Bind(&call_with_oddball_feedback);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
    assembler->Goto(&call_multiply_stub);
  }

  assembler->Bind(&call_with_any_feedback);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kAny));
    assembler->Goto(&call_multiply_stub);
  }

  assembler->Bind(&call_multiply_stub);
  {
    Callable callable = CodeFactory::Multiply(assembler->isolate());
    var_result.Bind(assembler->CallStub(callable, context, lhs, rhs));
    assembler->Goto(&end);
  }

  assembler->Bind(&end);
  assembler->UpdateFeedback(var_type_feedback.value(), feedback_vector,
                            slot_id);
  return var_result.value();
}


// static
compiler::Node* DivideWithFeedbackStub::Generate(
    CodeStubAssembler* assembler, compiler::Node* dividend,
    compiler::Node* divisor, compiler::Node* slot_id,
    compiler::Node* feedback_vector, compiler::Node* context) {
  using compiler::Node;
  typedef CodeStubAssembler::Label Label;
  typedef CodeStubAssembler::Variable Variable;

  // Shared entry point for floating point division.
  Label do_fdiv(assembler), dividend_is_not_number(assembler, Label::kDeferred),
      check_divisor_for_oddball(assembler, Label::kDeferred),
      call_with_oddball_feedback(assembler), call_with_any_feedback(assembler),
      call_divide_stub(assembler), end(assembler);
  Variable var_dividend_float64(assembler, MachineRepresentation::kFloat64),
      var_divisor_float64(assembler, MachineRepresentation::kFloat64),
      var_result(assembler, MachineRepresentation::kTagged),
      var_type_feedback(assembler, MachineRepresentation::kTaggedSigned);

  Label dividend_is_smi(assembler), dividend_is_not_smi(assembler);
  assembler->Branch(assembler->TaggedIsSmi(dividend), &dividend_is_smi,
                    &dividend_is_not_smi);

  assembler->Bind(&dividend_is_smi);
  {
    Label divisor_is_smi(assembler), divisor_is_not_smi(assembler);
    assembler->Branch(assembler->TaggedIsSmi(divisor), &divisor_is_smi,
                      &divisor_is_not_smi);

    assembler->Bind(&divisor_is_smi);
    {
      Label bailout(assembler);

      // Do floating point division if {divisor} is zero.
      assembler->GotoIf(
          assembler->WordEqual(divisor, assembler->SmiConstant(0)), &bailout);

      // Do floating point division {dividend} is zero and {divisor} is
      // negative.
      Label dividend_is_zero(assembler), dividend_is_not_zero(assembler);
      assembler->Branch(
          assembler->WordEqual(dividend, assembler->SmiConstant(0)),
          &dividend_is_zero, &dividend_is_not_zero);

      assembler->Bind(&dividend_is_zero);
      {
        assembler->GotoIf(
            assembler->SmiLessThan(divisor, assembler->SmiConstant(0)),
            &bailout);
        assembler->Goto(&dividend_is_not_zero);
      }
      assembler->Bind(&dividend_is_not_zero);

      Node* untagged_divisor = assembler->SmiToWord32(divisor);
      Node* untagged_dividend = assembler->SmiToWord32(dividend);

      // Do floating point division if {dividend} is kMinInt (or kMinInt - 1
      // if the Smi size is 31) and {divisor} is -1.
      Label divisor_is_minus_one(assembler),
          divisor_is_not_minus_one(assembler);
      assembler->Branch(assembler->Word32Equal(untagged_divisor,
                                               assembler->Int32Constant(-1)),
                        &divisor_is_minus_one, &divisor_is_not_minus_one);

      assembler->Bind(&divisor_is_minus_one);
      {
        assembler->GotoIf(
            assembler->Word32Equal(
                untagged_dividend,
                assembler->Int32Constant(kSmiValueSize == 32 ? kMinInt
                                                             : (kMinInt >> 1))),
            &bailout);
        assembler->Goto(&divisor_is_not_minus_one);
      }
      assembler->Bind(&divisor_is_not_minus_one);

      Node* untagged_result =
          assembler->Int32Div(untagged_dividend, untagged_divisor);
      Node* truncated = assembler->Int32Mul(untagged_result, untagged_divisor);
      // Do floating point division if the remainder is not 0.
      assembler->GotoIf(assembler->Word32NotEqual(untagged_dividend, truncated),
                        &bailout);
      var_type_feedback.Bind(
          assembler->SmiConstant(BinaryOperationFeedback::kSignedSmall));
      var_result.Bind(assembler->SmiFromWord32(untagged_result));
      assembler->Goto(&end);

      // Bailout: convert {dividend} and {divisor} to double and do double
      // division.
      assembler->Bind(&bailout);
      {
        var_dividend_float64.Bind(assembler->SmiToFloat64(dividend));
        var_divisor_float64.Bind(assembler->SmiToFloat64(divisor));
        assembler->Goto(&do_fdiv);
      }
    }

    assembler->Bind(&divisor_is_not_smi);
    {
      Node* divisor_map = assembler->LoadMap(divisor);

      // Check if {divisor} is a HeapNumber.
      assembler->GotoIfNot(assembler->IsHeapNumberMap(divisor_map),
                           &check_divisor_for_oddball);

      // Convert {dividend} to a double and divide it with the value of
      // {divisor}.
      var_dividend_float64.Bind(assembler->SmiToFloat64(dividend));
      var_divisor_float64.Bind(assembler->LoadHeapNumberValue(divisor));
      assembler->Goto(&do_fdiv);
    }

    assembler->Bind(&dividend_is_not_smi);
    {
      Node* dividend_map = assembler->LoadMap(dividend);

      // Check if {dividend} is a HeapNumber.
      assembler->GotoIfNot(assembler->IsHeapNumberMap(dividend_map),
                           &dividend_is_not_number);

      // Check if {divisor} is a Smi.
      Label divisor_is_smi(assembler), divisor_is_not_smi(assembler);
      assembler->Branch(assembler->TaggedIsSmi(divisor), &divisor_is_smi,
                        &divisor_is_not_smi);

      assembler->Bind(&divisor_is_smi);
      {
        // Convert {divisor} to a double and use it for a floating point
        // division.
        var_dividend_float64.Bind(assembler->LoadHeapNumberValue(dividend));
        var_divisor_float64.Bind(assembler->SmiToFloat64(divisor));
        assembler->Goto(&do_fdiv);
      }

      assembler->Bind(&divisor_is_not_smi);
      {
        Node* divisor_map = assembler->LoadMap(divisor);

        // Check if {divisor} is a HeapNumber.
        assembler->GotoIfNot(assembler->IsHeapNumberMap(divisor_map),
                             &check_divisor_for_oddball);

        // Both {dividend} and {divisor} are HeapNumbers. Load their values
        // and divide them.
        var_dividend_float64.Bind(assembler->LoadHeapNumberValue(dividend));
        var_divisor_float64.Bind(assembler->LoadHeapNumberValue(divisor));
        assembler->Goto(&do_fdiv);
      }
    }
  }

  assembler->Bind(&do_fdiv);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kNumber));
    Node* value = assembler->Float64Div(var_dividend_float64.value(),
                                        var_divisor_float64.value());
    var_result.Bind(assembler->AllocateHeapNumberWithValue(value));
    assembler->Goto(&end);
  }

  assembler->Bind(&dividend_is_not_number);
  {
    // We just know dividend is not a number or Smi. No checks on divisor yet.
    // Check if dividend is an oddball.
    Node* dividend_instance_type = assembler->LoadInstanceType(dividend);
    Node* dividend_is_oddball = assembler->Word32Equal(
        dividend_instance_type, assembler->Int32Constant(ODDBALL_TYPE));
    assembler->GotoIfNot(dividend_is_oddball, &call_with_any_feedback);

    assembler->GotoIf(assembler->TaggedIsSmi(divisor),
                      &call_with_oddball_feedback);

    // Load the map of the {divisor}.
    Node* divisor_map = assembler->LoadMap(divisor);

    // Check if {divisor} is a HeapNumber.
    assembler->Branch(assembler->IsHeapNumberMap(divisor_map),
                      &call_with_oddball_feedback, &check_divisor_for_oddball);
  }

  assembler->Bind(&check_divisor_for_oddball);
  {
    // Check if divisor is an oddball. At this point we know dividend is either
    // a Smi or number or oddball and divisor is not a number or Smi.
    Node* divisor_instance_type = assembler->LoadInstanceType(divisor);
    Node* divisor_is_oddball = assembler->Word32Equal(
        divisor_instance_type, assembler->Int32Constant(ODDBALL_TYPE));
    assembler->Branch(divisor_is_oddball, &call_with_oddball_feedback,
                      &call_with_any_feedback);
  }

  assembler->Bind(&call_with_oddball_feedback);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
    assembler->Goto(&call_divide_stub);
  }

  assembler->Bind(&call_with_any_feedback);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kAny));
    assembler->Goto(&call_divide_stub);
  }

  assembler->Bind(&call_divide_stub);
  {
    Callable callable = CodeFactory::Divide(assembler->isolate());
    var_result.Bind(assembler->CallStub(callable, context, dividend, divisor));
    assembler->Goto(&end);
  }

  assembler->Bind(&end);
  assembler->UpdateFeedback(var_type_feedback.value(), feedback_vector,
                            slot_id);
  return var_result.value();
}

// static
compiler::Node* ModulusWithFeedbackStub::Generate(
    CodeStubAssembler* assembler, compiler::Node* dividend,
    compiler::Node* divisor, compiler::Node* slot_id,
    compiler::Node* feedback_vector, compiler::Node* context) {
  using compiler::Node;
  typedef CodeStubAssembler::Label Label;
  typedef CodeStubAssembler::Variable Variable;

  // Shared entry point for floating point division.
  Label do_fmod(assembler), dividend_is_not_number(assembler, Label::kDeferred),
      check_divisor_for_oddball(assembler, Label::kDeferred),
      call_with_oddball_feedback(assembler), call_with_any_feedback(assembler),
      call_modulus_stub(assembler), end(assembler);
  Variable var_dividend_float64(assembler, MachineRepresentation::kFloat64),
      var_divisor_float64(assembler, MachineRepresentation::kFloat64),
      var_result(assembler, MachineRepresentation::kTagged),
      var_type_feedback(assembler, MachineRepresentation::kTaggedSigned);

  Label dividend_is_smi(assembler), dividend_is_not_smi(assembler);
  assembler->Branch(assembler->TaggedIsSmi(dividend), &dividend_is_smi,
                    &dividend_is_not_smi);

  assembler->Bind(&dividend_is_smi);
  {
    Label divisor_is_smi(assembler), divisor_is_not_smi(assembler);
    assembler->Branch(assembler->TaggedIsSmi(divisor), &divisor_is_smi,
                      &divisor_is_not_smi);

    assembler->Bind(&divisor_is_smi);
    {
      var_result.Bind(assembler->SmiMod(dividend, divisor));
      var_type_feedback.Bind(assembler->SelectSmiConstant(
          assembler->TaggedIsSmi(var_result.value()),
          BinaryOperationFeedback::kSignedSmall,
          BinaryOperationFeedback::kNumber));
      assembler->Goto(&end);
    }

    assembler->Bind(&divisor_is_not_smi);
    {
      Node* divisor_map = assembler->LoadMap(divisor);

      // Check if {divisor} is a HeapNumber.
      assembler->GotoIfNot(assembler->IsHeapNumberMap(divisor_map),
                           &check_divisor_for_oddball);

      // Convert {dividend} to a double and divide it with the value of
      // {divisor}.
      var_dividend_float64.Bind(assembler->SmiToFloat64(dividend));
      var_divisor_float64.Bind(assembler->LoadHeapNumberValue(divisor));
      assembler->Goto(&do_fmod);
    }
  }

  assembler->Bind(&dividend_is_not_smi);
  {
    Node* dividend_map = assembler->LoadMap(dividend);

    // Check if {dividend} is a HeapNumber.
    assembler->GotoIfNot(assembler->IsHeapNumberMap(dividend_map),
                         &dividend_is_not_number);

    // Check if {divisor} is a Smi.
    Label divisor_is_smi(assembler), divisor_is_not_smi(assembler);
    assembler->Branch(assembler->TaggedIsSmi(divisor), &divisor_is_smi,
                      &divisor_is_not_smi);

    assembler->Bind(&divisor_is_smi);
    {
      // Convert {divisor} to a double and use it for a floating point
      // division.
      var_dividend_float64.Bind(assembler->LoadHeapNumberValue(dividend));
      var_divisor_float64.Bind(assembler->SmiToFloat64(divisor));
      assembler->Goto(&do_fmod);
    }

    assembler->Bind(&divisor_is_not_smi);
    {
      Node* divisor_map = assembler->LoadMap(divisor);

      // Check if {divisor} is a HeapNumber.
      assembler->GotoIfNot(assembler->IsHeapNumberMap(divisor_map),
                           &check_divisor_for_oddball);

      // Both {dividend} and {divisor} are HeapNumbers. Load their values
      // and divide them.
      var_dividend_float64.Bind(assembler->LoadHeapNumberValue(dividend));
      var_divisor_float64.Bind(assembler->LoadHeapNumberValue(divisor));
      assembler->Goto(&do_fmod);
    }
  }

  assembler->Bind(&do_fmod);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kNumber));
    Node* value = assembler->Float64Mod(var_dividend_float64.value(),
                                        var_divisor_float64.value());
    var_result.Bind(assembler->AllocateHeapNumberWithValue(value));
    assembler->Goto(&end);
  }

  assembler->Bind(&dividend_is_not_number);
  {
    // No checks on divisor yet. We just know dividend is not a number or Smi.
    // Check if dividend is an oddball.
    Node* dividend_instance_type = assembler->LoadInstanceType(dividend);
    Node* dividend_is_oddball = assembler->Word32Equal(
        dividend_instance_type, assembler->Int32Constant(ODDBALL_TYPE));
    assembler->GotoIfNot(dividend_is_oddball, &call_with_any_feedback);

    assembler->GotoIf(assembler->TaggedIsSmi(divisor),
                      &call_with_oddball_feedback);

    // Load the map of the {divisor}.
    Node* divisor_map = assembler->LoadMap(divisor);

    // Check if {divisor} is a HeapNumber.
    assembler->Branch(assembler->IsHeapNumberMap(divisor_map),
                      &call_with_oddball_feedback, &check_divisor_for_oddball);
  }

  assembler->Bind(&check_divisor_for_oddball);
  {
    // Check if divisor is an oddball. At this point we know dividend is either
    // a Smi or number or oddball and divisor is not a number or Smi.
    Node* divisor_instance_type = assembler->LoadInstanceType(divisor);
    Node* divisor_is_oddball = assembler->Word32Equal(
        divisor_instance_type, assembler->Int32Constant(ODDBALL_TYPE));
    assembler->Branch(divisor_is_oddball, &call_with_oddball_feedback,
                      &call_with_any_feedback);
  }

  assembler->Bind(&call_with_oddball_feedback);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
    assembler->Goto(&call_modulus_stub);
  }

  assembler->Bind(&call_with_any_feedback);
  {
    var_type_feedback.Bind(
        assembler->SmiConstant(BinaryOperationFeedback::kAny));
    assembler->Goto(&call_modulus_stub);
  }

  assembler->Bind(&call_modulus_stub);
  {
    Callable callable = CodeFactory::Modulus(assembler->isolate());
    var_result.Bind(assembler->CallStub(callable, context, dividend, divisor));
    assembler->Goto(&end);
  }

  assembler->Bind(&end);
  assembler->UpdateFeedback(var_type_feedback.value(), feedback_vector,
                            slot_id);
  return var_result.value();
}

void NumberToStringStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);
  Node* argument = assembler.Parameter(Descriptor::kArgument);
  Node* context = assembler.Parameter(Descriptor::kContext);
  assembler.Return(assembler.NumberToString(context, argument));
}

// ES6 section 21.1.3.19 String.prototype.substring ( start, end )
compiler::Node* SubStringStub::Generate(CodeStubAssembler* assembler,
                                        compiler::Node* string,
                                        compiler::Node* from,
                                        compiler::Node* to,
                                        compiler::Node* context) {
  return assembler->SubString(context, string, from, to);
}

void SubStringStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  CodeStubAssembler assembler(state);
  assembler.Return(Generate(&assembler,
                            assembler.Parameter(Descriptor::kString),
                            assembler.Parameter(Descriptor::kFrom),
                            assembler.Parameter(Descriptor::kTo),
                            assembler.Parameter(Descriptor::kContext)));
}

void StoreGlobalStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef CodeStubAssembler::Label Label;
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  assembler.Comment(
      "StoreGlobalStub: cell_type=%d, constant_type=%d, check_global=%d",
      cell_type(), PropertyCellType::kConstantType == cell_type()
                       ? static_cast<int>(constant_type())
                       : -1,
      check_global());

  Node* receiver = assembler.Parameter(Descriptor::kReceiver);
  Node* name = assembler.Parameter(Descriptor::kName);
  Node* value = assembler.Parameter(Descriptor::kValue);
  Node* slot = assembler.Parameter(Descriptor::kSlot);
  Node* vector = assembler.Parameter(Descriptor::kVector);
  Node* context = assembler.Parameter(Descriptor::kContext);

  Label miss(&assembler);

  if (check_global()) {
    // Check that the map of the global has not changed: use a placeholder map
    // that will be replaced later with the global object's map.
    Node* proxy_map = assembler.LoadMap(receiver);
    Node* global = assembler.LoadObjectField(proxy_map, Map::kPrototypeOffset);
    Node* map_cell = assembler.HeapConstant(isolate()->factory()->NewWeakCell(
        StoreGlobalStub::global_map_placeholder(isolate())));
    Node* expected_map = assembler.LoadWeakCellValueUnchecked(map_cell);
    Node* map = assembler.LoadMap(global);
    assembler.GotoIf(assembler.WordNotEqual(expected_map, map), &miss);
  }

  Node* weak_cell = assembler.HeapConstant(isolate()->factory()->NewWeakCell(
      StoreGlobalStub::property_cell_placeholder(isolate())));
  Node* cell = assembler.LoadWeakCellValue(weak_cell);
  assembler.GotoIf(assembler.TaggedIsSmi(cell), &miss);

  // Load the payload of the global parameter cell. A hole indicates that the
  // cell has been invalidated and that the store must be handled by the
  // runtime.
  Node* cell_contents =
      assembler.LoadObjectField(cell, PropertyCell::kValueOffset);

  PropertyCellType cell_type = this->cell_type();
  if (cell_type == PropertyCellType::kConstant ||
      cell_type == PropertyCellType::kUndefined) {
    // This is always valid for all states a cell can be in.
    assembler.GotoIf(assembler.WordNotEqual(cell_contents, value), &miss);
  } else {
    assembler.GotoIf(assembler.IsTheHole(cell_contents), &miss);

    // When dealing with constant types, the type may be allowed to change, as
    // long as optimized code remains valid.
    bool value_is_smi = false;
    if (cell_type == PropertyCellType::kConstantType) {
      switch (constant_type()) {
        case PropertyCellConstantType::kSmi:
          assembler.GotoIfNot(assembler.TaggedIsSmi(value), &miss);
          value_is_smi = true;
          break;
        case PropertyCellConstantType::kStableMap: {
          // It is sufficient here to check that the value and cell contents
          // have identical maps, no matter if they are stable or not or if they
          // are the maps that were originally in the cell or not. If optimized
          // code will deopt when a cell has a unstable map and if it has a
          // dependency on a stable map, it will deopt if the map destabilizes.
          assembler.GotoIf(assembler.TaggedIsSmi(value), &miss);
          assembler.GotoIf(assembler.TaggedIsSmi(cell_contents), &miss);
          Node* expected_map = assembler.LoadMap(cell_contents);
          Node* map = assembler.LoadMap(value);
          assembler.GotoIf(assembler.WordNotEqual(expected_map, map), &miss);
          break;
        }
      }
    }
    if (value_is_smi) {
      assembler.StoreObjectFieldNoWriteBarrier(cell, PropertyCell::kValueOffset,
                                               value);
    } else {
      assembler.StoreObjectField(cell, PropertyCell::kValueOffset, value);
    }
  }

  assembler.Return(value);

  assembler.Bind(&miss);
  {
    assembler.Comment("Miss");
    assembler.TailCallRuntime(Runtime::kStoreIC_Miss, context, value, slot,
                              vector, receiver, name);
  }
}

void KeyedLoadSloppyArgumentsStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef CodeStubAssembler::Label Label;
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  Node* receiver = assembler.Parameter(Descriptor::kReceiver);
  Node* key = assembler.Parameter(Descriptor::kName);
  Node* slot = assembler.Parameter(Descriptor::kSlot);
  Node* vector = assembler.Parameter(Descriptor::kVector);
  Node* context = assembler.Parameter(Descriptor::kContext);

  Label miss(&assembler);

  Node* result = assembler.LoadKeyedSloppyArguments(receiver, key, &miss);
  assembler.Return(result);

  assembler.Bind(&miss);
  {
    assembler.Comment("Miss");
    assembler.TailCallRuntime(Runtime::kKeyedLoadIC_Miss, context, receiver,
                              key, slot, vector);
  }
}

void KeyedStoreSloppyArgumentsStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef CodeStubAssembler::Label Label;
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  Node* receiver = assembler.Parameter(Descriptor::kReceiver);
  Node* key = assembler.Parameter(Descriptor::kName);
  Node* value = assembler.Parameter(Descriptor::kValue);
  Node* slot = assembler.Parameter(Descriptor::kSlot);
  Node* vector = assembler.Parameter(Descriptor::kVector);
  Node* context = assembler.Parameter(Descriptor::kContext);

  Label miss(&assembler);

  assembler.StoreKeyedSloppyArguments(receiver, key, value, &miss);
  assembler.Return(value);

  assembler.Bind(&miss);
  {
    assembler.Comment("Miss");
    assembler.TailCallRuntime(Runtime::kKeyedStoreIC_Miss, context, value, slot,
                              vector, receiver, key);
  }
}

void LoadScriptContextFieldStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  assembler.Comment("LoadScriptContextFieldStub: context_index=%d, slot=%d",
                    context_index(), slot_index());

  Node* context = assembler.Parameter(Descriptor::kContext);

  Node* script_context = assembler.LoadScriptContext(context, context_index());
  Node* result = assembler.LoadFixedArrayElement(script_context, slot_index());
  assembler.Return(result);
}

void StoreScriptContextFieldStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  assembler.Comment("StoreScriptContextFieldStub: context_index=%d, slot=%d",
                    context_index(), slot_index());

  Node* value = assembler.Parameter(Descriptor::kValue);
  Node* context = assembler.Parameter(Descriptor::kContext);

  Node* script_context = assembler.LoadScriptContext(context, context_index());
  assembler.StoreFixedArrayElement(
      script_context, assembler.IntPtrConstant(slot_index()), value);
  assembler.Return(value);
}

void StoreInterceptorStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  Node* receiver = assembler.Parameter(Descriptor::kReceiver);
  Node* name = assembler.Parameter(Descriptor::kName);
  Node* value = assembler.Parameter(Descriptor::kValue);
  Node* slot = assembler.Parameter(Descriptor::kSlot);
  Node* vector = assembler.Parameter(Descriptor::kVector);
  Node* context = assembler.Parameter(Descriptor::kContext);
  assembler.TailCallRuntime(Runtime::kStorePropertyWithInterceptor, context,
                            value, slot, vector, receiver, name);
}

void LoadIndexedInterceptorStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  typedef CodeStubAssembler::Label Label;
  CodeStubAssembler assembler(state);

  Node* receiver = assembler.Parameter(Descriptor::kReceiver);
  Node* key = assembler.Parameter(Descriptor::kName);
  Node* slot = assembler.Parameter(Descriptor::kSlot);
  Node* vector = assembler.Parameter(Descriptor::kVector);
  Node* context = assembler.Parameter(Descriptor::kContext);

  Label if_keyispositivesmi(&assembler), if_keyisinvalid(&assembler);
  assembler.Branch(assembler.TaggedIsPositiveSmi(key), &if_keyispositivesmi,
                   &if_keyisinvalid);
  assembler.Bind(&if_keyispositivesmi);
  assembler.TailCallRuntime(Runtime::kLoadElementWithInterceptor, context,
                            receiver, key);

  assembler.Bind(&if_keyisinvalid);
  assembler.TailCallRuntime(Runtime::kKeyedLoadIC_Miss, context, receiver, key,
                            slot, vector);
}

void CallICStub::PrintState(std::ostream& os) const {  // NOLINT
  os << convert_mode() << ", " << tail_call_mode();
}

void CallICStub::GenerateAssembly(compiler::CodeAssemblerState* state) const {
  typedef CodeStubAssembler::Label Label;
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  Node* context = assembler.Parameter(Descriptor::kContext);
  Node* target = assembler.Parameter(Descriptor::kTarget);
  Node* argc = assembler.Parameter(Descriptor::kActualArgumentsCount);
  Node* slot = assembler.Parameter(Descriptor::kSlot);
  Node* vector = assembler.Parameter(Descriptor::kVector);

  // TODO(bmeurer): The slot should actually be an IntPtr, but TurboFan's
  // SimplifiedLowering cannot deal with IntPtr machine type properly yet.
  slot = assembler.ChangeInt32ToIntPtr(slot);

  // Static checks to assert it is safe to examine the type feedback element.
  // We don't know that we have a weak cell. We might have a private symbol
  // or an AllocationSite, but the memory is safe to examine.
  // AllocationSite::kTransitionInfoOffset - contains a Smi or pointer to
  // FixedArray.
  // WeakCell::kValueOffset - contains a JSFunction or Smi(0)
  // Symbol::kHashFieldSlot - if the low bit is 1, then the hash is not
  // computed, meaning that it can't appear to be a pointer. If the low bit is
  // 0, then hash is computed, but the 0 bit prevents the field from appearing
  // to be a pointer.
  STATIC_ASSERT(WeakCell::kSize >= kPointerSize);
  STATIC_ASSERT(AllocationSite::kTransitionInfoOffset ==
                    WeakCell::kValueOffset &&
                WeakCell::kValueOffset == Symbol::kHashFieldSlot);

  // Increment the call count.
  // TODO(bmeurer): Would it be beneficial to use Int32Add on 64-bit?
  assembler.Comment("increment call count");
  Node* call_count =
      assembler.LoadFixedArrayElement(vector, slot, 1 * kPointerSize);
  Node* new_count = assembler.SmiAdd(call_count, assembler.SmiConstant(1));
  // Count is Smi, so we don't need a write barrier.
  assembler.StoreFixedArrayElement(vector, slot, new_count, SKIP_WRITE_BARRIER,
                                   1 * kPointerSize);

  Label call_function(&assembler), extra_checks(&assembler), call(&assembler);

  // The checks. First, does function match the recorded monomorphic target?
  Node* feedback_element = assembler.LoadFixedArrayElement(vector, slot);
  Node* feedback_value = assembler.LoadWeakCellValueUnchecked(feedback_element);
  Node* is_monomorphic = assembler.WordEqual(target, feedback_value);
  assembler.GotoIfNot(is_monomorphic, &extra_checks);

  // The compare above could have been a SMI/SMI comparison. Guard against
  // this convincing us that we have a monomorphic JSFunction.
  Node* is_smi = assembler.TaggedIsSmi(target);
  assembler.Branch(is_smi, &extra_checks, &call_function);

  assembler.Bind(&call_function);
  {
    // Call using CallFunction builtin.
    Callable callable =
        CodeFactory::CallFunction(isolate(), convert_mode(), tail_call_mode());
    assembler.TailCallStub(callable, context, target, argc);
  }

  assembler.Bind(&extra_checks);
  {
    Label check_initialized(&assembler), mark_megamorphic(&assembler),
        create_allocation_site(&assembler, Label::kDeferred),
        create_weak_cell(&assembler, Label::kDeferred);

    assembler.Comment("check if megamorphic");
    // Check if it is a megamorphic target.
    Node* is_megamorphic = assembler.WordEqual(
        feedback_element,
        assembler.HeapConstant(FeedbackVector::MegamorphicSentinel(isolate())));
    assembler.GotoIf(is_megamorphic, &call);

    assembler.Comment("check if it is an allocation site");
    assembler.GotoIfNot(
        assembler.IsAllocationSiteMap(assembler.LoadMap(feedback_element)),
        &check_initialized);

    // If it is not the Array() function, mark megamorphic.
    Node* context_slot = assembler.LoadContextElement(
        assembler.LoadNativeContext(context), Context::ARRAY_FUNCTION_INDEX);
    Node* is_array_function = assembler.WordEqual(context_slot, target);
    assembler.GotoIfNot(is_array_function, &mark_megamorphic);

    // Call ArrayConstructorStub.
    Callable callable = CodeFactory::ArrayConstructor(isolate());
    assembler.TailCallStub(callable, context, target, target, argc,
                           feedback_element);

    assembler.Bind(&check_initialized);
    {
      assembler.Comment("check if uninitialized");
      // Check if it is uninitialized target first.
      Node* is_uninitialized = assembler.WordEqual(
          feedback_element,
          assembler.HeapConstant(
              FeedbackVector::UninitializedSentinel(isolate())));
      assembler.GotoIfNot(is_uninitialized, &mark_megamorphic);

      assembler.Comment("handle unitinitialized");
      // If it is not a JSFunction mark it as megamorphic.
      Node* is_smi = assembler.TaggedIsSmi(target);
      assembler.GotoIf(is_smi, &mark_megamorphic);

      // Check if function is an object of JSFunction type.
      Node* is_js_function = assembler.IsJSFunction(target);
      assembler.GotoIfNot(is_js_function, &mark_megamorphic);

      // Check if it is the Array() function.
      Node* context_slot = assembler.LoadContextElement(
          assembler.LoadNativeContext(context), Context::ARRAY_FUNCTION_INDEX);
      Node* is_array_function = assembler.WordEqual(context_slot, target);
      assembler.GotoIf(is_array_function, &create_allocation_site);

      // Check if the function belongs to the same native context.
      Node* native_context = assembler.LoadNativeContext(
          assembler.LoadObjectField(target, JSFunction::kContextOffset));
      Node* is_same_native_context = assembler.WordEqual(
          native_context, assembler.LoadNativeContext(context));
      assembler.Branch(is_same_native_context, &create_weak_cell,
                       &mark_megamorphic);
    }

    assembler.Bind(&create_weak_cell);
    {
      // Wrap the {target} in a WeakCell and remember it.
      assembler.Comment("create weak cell");
      assembler.CreateWeakCellInFeedbackVector(vector, assembler.SmiTag(slot),
                                               target);

      // Call using CallFunction builtin.
      assembler.Goto(&call_function);
    }

    assembler.Bind(&create_allocation_site);
    {
      // Create an AllocationSite for the {target}.
      assembler.Comment("create allocation site");
      assembler.CreateAllocationSiteInFeedbackVector(vector,
                                                     assembler.SmiTag(slot));

      // Call using CallFunction builtin. CallICs have a PREMONOMORPHIC state.
      // They start collecting feedback only when a call is executed the second
      // time. So, do not pass any feedback here.
      assembler.Goto(&call_function);
    }

    assembler.Bind(&mark_megamorphic);
    {
      // Mark it as a megamorphic.
      // MegamorphicSentinel is created as a part of Heap::InitialObjects
      // and will not move during a GC. So it is safe to skip write barrier.
      DCHECK(Heap::RootIsImmortalImmovable(Heap::kmegamorphic_symbolRootIndex));
      assembler.StoreFixedArrayElement(
          vector, slot, assembler.HeapConstant(
                            FeedbackVector::MegamorphicSentinel(isolate())),
          SKIP_WRITE_BARRIER);
      assembler.Goto(&call);
    }
  }

  assembler.Bind(&call);
  {
    // Call using call builtin.
    assembler.Comment("call using Call builtin");
    Callable callable_call =
        CodeFactory::Call(isolate(), convert_mode(), tail_call_mode());
    assembler.TailCallStub(callable_call, context, target, argc);
  }
}

void CallICTrampolineStub::PrintState(std::ostream& os) const {  // NOLINT
  os << convert_mode() << ", " << tail_call_mode();
}

void CallICTrampolineStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  Node* context = assembler.Parameter(Descriptor::kContext);
  Node* target = assembler.Parameter(Descriptor::kTarget);
  Node* argc = assembler.Parameter(Descriptor::kActualArgumentsCount);
  Node* slot = assembler.Parameter(Descriptor::kSlot);
  Node* vector = assembler.LoadFeedbackVectorForStub();

  Callable callable =
      CodeFactory::CallIC(isolate(), convert_mode(), tail_call_mode());
  assembler.TailCallStub(callable, context, target, argc, slot, vector);
}

void JSEntryStub::FinishCode(Handle<Code> code) {
  Handle<FixedArray> handler_table =
      code->GetIsolate()->factory()->NewFixedArray(1, TENURED);
  handler_table->set(0, Smi::FromInt(handler_offset_));
  code->set_handler_table(*handler_table);
}

void TransitionElementsKindStub::InitializeDescriptor(
    CodeStubDescriptor* descriptor) {
  descriptor->Initialize(
      Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry);
}


void AllocateHeapNumberStub::InitializeDescriptor(
    CodeStubDescriptor* descriptor) {
  descriptor->Initialize(
      Runtime::FunctionForId(Runtime::kAllocateHeapNumber)->entry);
}


void ToBooleanICStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
  descriptor->Initialize(FUNCTION_ADDR(Runtime_ToBooleanIC_Miss));
  descriptor->SetMissHandler(Runtime::kToBooleanIC_Miss);
}


void BinaryOpICStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
  descriptor->Initialize(FUNCTION_ADDR(Runtime_BinaryOpIC_Miss));
  descriptor->SetMissHandler(Runtime::kBinaryOpIC_Miss);
}


void BinaryOpWithAllocationSiteStub::InitializeDescriptor(
    CodeStubDescriptor* descriptor) {
  descriptor->Initialize(
      FUNCTION_ADDR(Runtime_BinaryOpIC_MissWithAllocationSite));
}

void GetPropertyStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  typedef CodeStubAssembler::Label Label;
  typedef CodeStubAssembler::Variable Variable;
  CodeStubAssembler assembler(state);

  Label call_runtime(&assembler, Label::kDeferred),
      return_undefined(&assembler), end(&assembler);

  Node* object = assembler.Parameter(0);
  Node* key = assembler.Parameter(1);
  Node* context = assembler.Parameter(2);
  Variable var_result(&assembler, MachineRepresentation::kTagged);

  CodeStubAssembler::LookupInHolder lookup_property_in_holder =
      [&assembler, context, &var_result, &end](
          Node* receiver, Node* holder, Node* holder_map,
          Node* holder_instance_type, Node* unique_name, Label* next_holder,
          Label* if_bailout) {
        Variable var_value(&assembler, MachineRepresentation::kTagged);
        Label if_found(&assembler);
        assembler.TryGetOwnProperty(
            context, receiver, holder, holder_map, holder_instance_type,
            unique_name, &if_found, &var_value, next_holder, if_bailout);
        assembler.Bind(&if_found);
        {
          var_result.Bind(var_value.value());
          assembler.Goto(&end);
        }
      };

  CodeStubAssembler::LookupInHolder lookup_element_in_holder =
      [&assembler](
          Node* receiver, Node* holder, Node* holder_map,
          Node* holder_instance_type, Node* index, Label* next_holder,
          Label* if_bailout) {
        // Not supported yet.
        assembler.Use(next_holder);
        assembler.Goto(if_bailout);
      };

  assembler.TryPrototypeChainLookup(object, key, lookup_property_in_holder,
                                    lookup_element_in_holder, &return_undefined,
                                    &call_runtime);

  assembler.Bind(&return_undefined);
  {
    var_result.Bind(assembler.UndefinedConstant());
    assembler.Goto(&end);
  }

  assembler.Bind(&call_runtime);
  {
    var_result.Bind(
        assembler.CallRuntime(Runtime::kGetProperty, context, object, key));
    assembler.Goto(&end);
  }

  assembler.Bind(&end);
  assembler.Return(var_result.value());
}

void CreateAllocationSiteStub::GenerateAheadOfTime(Isolate* isolate) {
  CreateAllocationSiteStub stub(isolate);
  stub.GetCode();
}


void CreateWeakCellStub::GenerateAheadOfTime(Isolate* isolate) {
  CreateWeakCellStub stub(isolate);
  stub.GetCode();
}

void StoreSlowElementStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  Node* receiver = assembler.Parameter(Descriptor::kReceiver);
  Node* name = assembler.Parameter(Descriptor::kName);
  Node* value = assembler.Parameter(Descriptor::kValue);
  Node* slot = assembler.Parameter(Descriptor::kSlot);
  Node* vector = assembler.Parameter(Descriptor::kVector);
  Node* context = assembler.Parameter(Descriptor::kContext);

  assembler.TailCallRuntime(Runtime::kKeyedStoreIC_Slow, context, value, slot,
                            vector, receiver, name);
}

void StoreFastElementStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef CodeStubAssembler::Label Label;
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);

  assembler.Comment(
      "StoreFastElementStub: js_array=%d, elements_kind=%s, store_mode=%d",
      is_js_array(), ElementsKindToString(elements_kind()), store_mode());

  Node* receiver = assembler.Parameter(Descriptor::kReceiver);
  Node* key = assembler.Parameter(Descriptor::kName);
  Node* value = assembler.Parameter(Descriptor::kValue);
  Node* slot = assembler.Parameter(Descriptor::kSlot);
  Node* vector = assembler.Parameter(Descriptor::kVector);
  Node* context = assembler.Parameter(Descriptor::kContext);

  Label miss(&assembler);

  assembler.EmitElementStore(receiver, key, value, is_js_array(),
                             elements_kind(), store_mode(), &miss);
  assembler.Return(value);

  assembler.Bind(&miss);
  {
    assembler.Comment("Miss");
    assembler.TailCallRuntime(Runtime::kKeyedStoreIC_Miss, context, value, slot,
                              vector, receiver, key);
  }
}

// static
void StoreFastElementStub::GenerateAheadOfTime(Isolate* isolate) {
  if (FLAG_minimal) return;
  StoreFastElementStub(isolate, false, FAST_HOLEY_ELEMENTS, STANDARD_STORE)
      .GetCode();
  StoreFastElementStub(isolate, false, FAST_HOLEY_ELEMENTS,
                       STORE_AND_GROW_NO_TRANSITION).GetCode();
  for (int i = FIRST_FAST_ELEMENTS_KIND; i <= LAST_FAST_ELEMENTS_KIND; i++) {
    ElementsKind kind = static_cast<ElementsKind>(i);
    StoreFastElementStub(isolate, true, kind, STANDARD_STORE).GetCode();
    StoreFastElementStub(isolate, true, kind, STORE_AND_GROW_NO_TRANSITION)
        .GetCode();
  }
}

bool ToBooleanICStub::UpdateStatus(Handle<Object> object) {
  ToBooleanHints old_hints = hints();
  ToBooleanHints new_hints = old_hints;
  bool to_boolean_value = false;  // Dummy initialization.
  if (object->IsUndefined(isolate())) {
    new_hints |= ToBooleanHint::kUndefined;
    to_boolean_value = false;
  } else if (object->IsBoolean()) {
    new_hints |= ToBooleanHint::kBoolean;
    to_boolean_value = object->IsTrue(isolate());
  } else if (object->IsNull(isolate())) {
    new_hints |= ToBooleanHint::kNull;
    to_boolean_value = false;
  } else if (object->IsSmi()) {
    new_hints |= ToBooleanHint::kSmallInteger;
    to_boolean_value = Smi::cast(*object)->value() != 0;
  } else if (object->IsJSReceiver()) {
    new_hints |= ToBooleanHint::kReceiver;
    to_boolean_value = !object->IsUndetectable();
  } else if (object->IsString()) {
    DCHECK(!object->IsUndetectable());
    new_hints |= ToBooleanHint::kString;
    to_boolean_value = String::cast(*object)->length() != 0;
  } else if (object->IsSymbol()) {
    new_hints |= ToBooleanHint::kSymbol;
    to_boolean_value = true;
  } else if (object->IsHeapNumber()) {
    DCHECK(!object->IsUndetectable());
    new_hints |= ToBooleanHint::kHeapNumber;
    double value = HeapNumber::cast(*object)->value();
    to_boolean_value = value != 0 && !std::isnan(value);
  } else {
    // We should never see an internal object at runtime here!
    UNREACHABLE();
    to_boolean_value = true;
  }

  set_sub_minor_key(HintsBits::update(sub_minor_key(), new_hints));
  return to_boolean_value;
}

void ToBooleanICStub::PrintState(std::ostream& os) const {  // NOLINT
  os << hints();
}

void StubFailureTrampolineStub::GenerateAheadOfTime(Isolate* isolate) {
  StubFailureTrampolineStub stub1(isolate, NOT_JS_FUNCTION_STUB_MODE);
  StubFailureTrampolineStub stub2(isolate, JS_FUNCTION_STUB_MODE);
  stub1.GetCode();
  stub2.GetCode();
}


void ProfileEntryHookStub::EntryHookTrampoline(intptr_t function,
                                               intptr_t stack_pointer,
                                               Isolate* isolate) {
  FunctionEntryHook entry_hook = isolate->function_entry_hook();
  DCHECK(entry_hook != NULL);
  entry_hook(function, stack_pointer);
}

void CreateAllocationSiteStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  CodeStubAssembler assembler(state);
  assembler.Return(assembler.CreateAllocationSiteInFeedbackVector(
      assembler.Parameter(Descriptor::kVector),
      assembler.Parameter(Descriptor::kSlot)));
}

void CreateWeakCellStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  CodeStubAssembler assembler(state);
  assembler.Return(assembler.CreateWeakCellInFeedbackVector(
      assembler.Parameter(Descriptor::kVector),
      assembler.Parameter(Descriptor::kSlot),
      assembler.Parameter(Descriptor::kValue)));
}

void ArrayNoArgumentConstructorStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);
  Node* native_context = assembler.LoadObjectField(
      assembler.Parameter(Descriptor::kFunction), JSFunction::kContextOffset);
  bool track_allocation_site =
      AllocationSite::GetMode(elements_kind()) == TRACK_ALLOCATION_SITE &&
      override_mode() != DISABLE_ALLOCATION_SITES;
  Node* allocation_site = track_allocation_site
                              ? assembler.Parameter(Descriptor::kAllocationSite)
                              : nullptr;
  Node* array_map =
      assembler.LoadJSArrayElementsMap(elements_kind(), native_context);
  Node* array = assembler.AllocateJSArray(
      elements_kind(), array_map,
      assembler.IntPtrConstant(JSArray::kPreallocatedArrayElements),
      assembler.SmiConstant(Smi::kZero), allocation_site);
  assembler.Return(array);
}

void InternalArrayNoArgumentConstructorStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);
  Node* array_map =
      assembler.LoadObjectField(assembler.Parameter(Descriptor::kFunction),
                                JSFunction::kPrototypeOrInitialMapOffset);
  Node* array = assembler.AllocateJSArray(
      elements_kind(), array_map,
      assembler.IntPtrConstant(JSArray::kPreallocatedArrayElements),
      assembler.SmiConstant(Smi::kZero));
  assembler.Return(array);
}

namespace {

template <typename Descriptor>
void SingleArgumentConstructorCommon(CodeStubAssembler* assembler,
                                     ElementsKind elements_kind,
                                     compiler::Node* array_map,
                                     compiler::Node* allocation_site,
                                     AllocationSiteMode mode) {
  typedef compiler::Node Node;
  typedef CodeStubAssembler::Label Label;

  Label ok(assembler);
  Label smi_size(assembler);
  Label small_smi_size(assembler);
  Label call_runtime(assembler, Label::kDeferred);

  Node* size = assembler->Parameter(Descriptor::kArraySizeSmiParameter);
  assembler->Branch(assembler->TaggedIsSmi(size), &smi_size, &call_runtime);

  assembler->Bind(&smi_size);

  if (IsFastPackedElementsKind(elements_kind)) {
    Label abort(assembler, Label::kDeferred);
    assembler->Branch(
        assembler->SmiEqual(size, assembler->SmiConstant(Smi::kZero)),
        &small_smi_size, &abort);

    assembler->Bind(&abort);
    Node* reason =
        assembler->SmiConstant(Smi::FromInt(kAllocatingNonEmptyPackedArray));
    Node* context = assembler->Parameter(Descriptor::kContext);
    assembler->TailCallRuntime(Runtime::kAbort, context, reason);
  } else {
    int element_size =
        IsFastDoubleElementsKind(elements_kind) ? kDoubleSize : kPointerSize;
    int max_fast_elements =
        (kMaxRegularHeapObjectSize - FixedArray::kHeaderSize - JSArray::kSize -
         AllocationMemento::kSize) /
        element_size;
    assembler->Branch(
        assembler->SmiAboveOrEqual(
            size, assembler->SmiConstant(Smi::FromInt(max_fast_elements))),
        &call_runtime, &small_smi_size);
  }

  assembler->Bind(&small_smi_size);
  {
    Node* array = assembler->AllocateJSArray(
        elements_kind, array_map, size, size,
        mode == DONT_TRACK_ALLOCATION_SITE ? nullptr : allocation_site,
        CodeStubAssembler::SMI_PARAMETERS);
    assembler->Return(array);
  }

  assembler->Bind(&call_runtime);
  {
    Node* context = assembler->Parameter(Descriptor::kContext);
    Node* function = assembler->Parameter(Descriptor::kFunction);
    Node* array_size = assembler->Parameter(Descriptor::kArraySizeSmiParameter);
    Node* allocation_site = assembler->Parameter(Descriptor::kAllocationSite);
    assembler->TailCallRuntime(Runtime::kNewArray, context, function,
                               array_size, function, allocation_site);
  }
}
}  // namespace

void ArraySingleArgumentConstructorStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);
  Node* function = assembler.Parameter(Descriptor::kFunction);
  Node* native_context =
      assembler.LoadObjectField(function, JSFunction::kContextOffset);
  Node* array_map =
      assembler.LoadJSArrayElementsMap(elements_kind(), native_context);
  AllocationSiteMode mode = override_mode() == DISABLE_ALLOCATION_SITES
                                ? DONT_TRACK_ALLOCATION_SITE
                                : AllocationSite::GetMode(elements_kind());
  Node* allocation_site = assembler.Parameter(Descriptor::kAllocationSite);
  SingleArgumentConstructorCommon<Descriptor>(&assembler, elements_kind(),
                                              array_map, allocation_site, mode);
}

void InternalArraySingleArgumentConstructorStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);
  Node* function = assembler.Parameter(Descriptor::kFunction);
  Node* array_map = assembler.LoadObjectField(
      function, JSFunction::kPrototypeOrInitialMapOffset);
  SingleArgumentConstructorCommon<Descriptor>(
      &assembler, elements_kind(), array_map, assembler.UndefinedConstant(),
      DONT_TRACK_ALLOCATION_SITE);
}

void GrowArrayElementsStub::GenerateAssembly(
    compiler::CodeAssemblerState* state) const {
  typedef compiler::Node Node;
  CodeStubAssembler assembler(state);
  CodeStubAssembler::Label runtime(&assembler,
                                   CodeStubAssembler::Label::kDeferred);

  Node* object = assembler.Parameter(Descriptor::kObject);
  Node* key = assembler.Parameter(Descriptor::kKey);
  Node* context = assembler.Parameter(Descriptor::kContext);
  ElementsKind kind = elements_kind();

  Node* elements = assembler.LoadElements(object);
  Node* new_elements =
      assembler.TryGrowElementsCapacity(object, elements, kind, key, &runtime);
  assembler.Return(new_elements);

  assembler.Bind(&runtime);
  // TODO(danno): Make this a tail call when the stub is only used from TurboFan
  // code. This musn't be a tail call for now, since the caller site in lithium
  // creates a safepoint. This safepoint musn't have a different number of
  // arguments on the stack in the case that a GC happens from the slow-case
  // allocation path (zero, since all the stubs inputs are in registers) and
  // when the call happens (it would be two in the tail call case due to the
  // tail call pushing the arguments on the stack for the runtime call). By not
  // tail-calling, the runtime call case also has zero arguments on the stack
  // for the stub frame.
  assembler.Return(
      assembler.CallRuntime(Runtime::kGrowArrayElements, context, object, key));
}

ArrayConstructorStub::ArrayConstructorStub(Isolate* isolate)
    : PlatformCodeStub(isolate) {}

InternalArrayConstructorStub::InternalArrayConstructorStub(Isolate* isolate)
    : PlatformCodeStub(isolate) {}

}  // namespace internal
}  // namespace v8
