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

#include "src/compiler/serializer-for-background-compilation.h"

#include <sstream>

#include "src/base/optional.h"
#include "src/compiler/access-info.h"
#include "src/compiler/bytecode-analysis.h"
#include "src/compiler/compilation-dependencies.h"
#include "src/compiler/js-heap-broker.h"
#include "src/compiler/serializer-hints.h"
#include "src/compiler/zone-stats.h"
#include "src/handles/handles-inl.h"
#include "src/ic/call-optimization.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/objects/code.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/shared-function-info-inl.h"
#include "src/zone/zone-containers.h"
#include "src/zone/zone.h"

namespace v8 {
namespace internal {
namespace compiler {

#define KILL_ENVIRONMENT_LIST(V) \
  V(Abort)                       \
  V(ReThrow)                     \
  V(Throw)

#define CLEAR_ACCUMULATOR_LIST(V) \
  V(CallRuntime)                  \
  V(CloneObject)                  \
  V(CreateArrayFromIterable)      \
  V(CreateEmptyObjectLiteral)     \
  V(CreateMappedArguments)        \
  V(CreateRestParameter)          \
  V(CreateUnmappedArguments)      \
  V(DeletePropertySloppy)         \
  V(DeletePropertyStrict)         \
  V(ForInContinue)                \
  V(ForInEnumerate)               \
  V(ForInStep)                    \
  V(LogicalNot)                   \
  V(SetPendingMessage)            \
  V(TestNull)                     \
  V(TestReferenceEqual)           \
  V(TestTypeOf)                   \
  V(TestUndefined)                \
  V(TestUndetectable)             \
  V(ToBooleanLogicalNot)          \
  V(ToName)                       \
  V(ToString)                     \
  V(TypeOf)

#define UNCONDITIONAL_JUMPS_LIST(V) \
  V(Jump)                           \
  V(JumpConstant)                   \
  V(JumpLoop)

#define CONDITIONAL_JUMPS_LIST(V) \
  V(JumpIfFalse)                  \
  V(JumpIfFalseConstant)          \
  V(JumpIfJSReceiver)             \
  V(JumpIfJSReceiverConstant)     \
  V(JumpIfNotNull)                \
  V(JumpIfNotNullConstant)        \
  V(JumpIfNotUndefined)           \
  V(JumpIfNotUndefinedConstant)   \
  V(JumpIfNull)                   \
  V(JumpIfNullConstant)           \
  V(JumpIfToBooleanFalse)         \
  V(JumpIfToBooleanFalseConstant) \
  V(JumpIfToBooleanTrue)          \
  V(JumpIfToBooleanTrueConstant)  \
  V(JumpIfTrue)                   \
  V(JumpIfTrueConstant)           \
  V(JumpIfUndefined)              \
  V(JumpIfUndefinedConstant)      \
  V(JumpIfUndefinedOrNull)        \
  V(JumpIfUndefinedOrNullConstant)

#define IGNORED_BYTECODE_LIST(V)      \
  V(CallRuntimeForPair)               \
  V(CollectTypeProfile)               \
  V(DebugBreak0)                      \
  V(DebugBreak1)                      \
  V(DebugBreak2)                      \
  V(DebugBreak3)                      \
  V(DebugBreak4)                      \
  V(DebugBreak5)                      \
  V(DebugBreak6)                      \
  V(DebugBreakExtraWide)              \
  V(DebugBreakWide)                   \
  V(Debugger)                         \
  V(IncBlockCounter)                  \
  V(ResumeGenerator)                  \
  V(SuspendGenerator)                 \
  V(ThrowIfNotSuperConstructor)       \
  V(ThrowSuperAlreadyCalledIfNotHole) \
  V(ThrowSuperNotCalledIfHole)        \
  V(ToObject)

#define UNREACHABLE_BYTECODE_LIST(V) \
  V(ExtraWide)                       \
  V(Illegal)                         \
  V(Wide)

#define BINARY_OP_LIST(V) \
  V(Add)                  \
  V(AddSmi)               \
  V(BitwiseAnd)           \
  V(BitwiseAndSmi)        \
  V(BitwiseOr)            \
  V(BitwiseOrSmi)         \
  V(BitwiseXor)           \
  V(BitwiseXorSmi)        \
  V(Div)                  \
  V(DivSmi)               \
  V(Exp)                  \
  V(ExpSmi)               \
  V(Mod)                  \
  V(ModSmi)               \
  V(Mul)                  \
  V(MulSmi)               \
  V(ShiftLeft)            \
  V(ShiftLeftSmi)         \
  V(ShiftRight)           \
  V(ShiftRightSmi)        \
  V(ShiftRightLogical)    \
  V(ShiftRightLogicalSmi) \
  V(Sub)                  \
  V(SubSmi)

#define UNARY_OP_LIST(V) \
  V(BitwiseNot)          \
  V(Dec)                 \
  V(Inc)                 \
  V(Negate)

#define COMPARE_OP_LIST(V)  \
  V(TestEqual)              \
  V(TestEqualStrict)        \
  V(TestGreaterThan)        \
  V(TestGreaterThanOrEqual) \
  V(TestLessThan)           \
  V(TestLessThanOrEqual)

#define SUPPORTED_BYTECODE_LIST(V)    \
  V(CallAnyReceiver)                  \
  V(CallJSRuntime)                    \
  V(CallNoFeedback)                   \
  V(CallProperty)                     \
  V(CallProperty0)                    \
  V(CallProperty1)                    \
  V(CallProperty2)                    \
  V(CallUndefinedReceiver)            \
  V(CallUndefinedReceiver0)           \
  V(CallUndefinedReceiver1)           \
  V(CallUndefinedReceiver2)           \
  V(CallWithSpread)                   \
  V(Construct)                        \
  V(ConstructWithSpread)              \
  V(CreateArrayLiteral)               \
  V(CreateBlockContext)               \
  V(CreateCatchContext)               \
  V(CreateClosure)                    \
  V(CreateEmptyArrayLiteral)          \
  V(CreateEvalContext)                \
  V(CreateFunctionContext)            \
  V(CreateObjectLiteral)              \
  V(CreateRegExpLiteral)              \
  V(CreateWithContext)                \
  V(ForInNext)                        \
  V(ForInPrepare)                     \
  V(GetIterator)                      \
  V(GetSuperConstructor)              \
  V(GetTemplateObject)                \
  V(InvokeIntrinsic)                  \
  V(LdaConstant)                      \
  V(LdaContextSlot)                   \
  V(LdaCurrentContextSlot)            \
  V(LdaImmutableContextSlot)          \
  V(LdaImmutableCurrentContextSlot)   \
  V(LdaModuleVariable)                \
  V(LdaFalse)                         \
  V(LdaGlobal)                        \
  V(LdaGlobalInsideTypeof)            \
  V(LdaKeyedProperty)                 \
  V(LdaLookupContextSlot)             \
  V(LdaLookupContextSlotInsideTypeof) \
  V(LdaLookupGlobalSlot)              \
  V(LdaLookupGlobalSlotInsideTypeof)  \
  V(LdaLookupSlot)                    \
  V(LdaLookupSlotInsideTypeof)        \
  V(LdaNamedProperty)                 \
  V(LdaNamedPropertyFromSuper)        \
  V(LdaNamedPropertyNoFeedback)       \
  V(LdaNull)                          \
  V(Ldar)                             \
  V(LdaSmi)                           \
  V(LdaTheHole)                       \
  V(LdaTrue)                          \
  V(LdaUndefined)                     \
  V(LdaZero)                          \
  V(Mov)                              \
  V(PopContext)                       \
  V(PushContext)                      \
  V(Return)                           \
  V(StaContextSlot)                   \
  V(StaCurrentContextSlot)            \
  V(StaDataPropertyInLiteral)         \
  V(StaGlobal)                        \
  V(StaInArrayLiteral)                \
  V(StaKeyedProperty)                 \
  V(StaLookupSlot)                    \
  V(StaModuleVariable)                \
  V(StaNamedOwnProperty)              \
  V(StaNamedProperty)                 \
  V(StaNamedPropertyNoFeedback)       \
  V(Star)                             \
  V(SwitchOnGeneratorState)           \
  V(SwitchOnSmiNoFeedback)            \
  V(TestIn)                           \
  V(TestInstanceOf)                   \
  V(ThrowReferenceErrorIfHole)        \
  V(ToNumber)                         \
  V(ToNumeric)                        \
  BINARY_OP_LIST(V)                   \
  COMPARE_OP_LIST(V)                  \
  CLEAR_ACCUMULATOR_LIST(V)           \
  CONDITIONAL_JUMPS_LIST(V)           \
  IGNORED_BYTECODE_LIST(V)            \
  KILL_ENVIRONMENT_LIST(V)            \
  UNARY_OP_LIST(V)                    \
  UNCONDITIONAL_JUMPS_LIST(V)         \
  UNREACHABLE_BYTECODE_LIST(V)

struct HintsImpl : public ZoneObject {
  explicit HintsImpl(Zone* zone) : zone_(zone) {}

  ConstantsSet constants_;
  MapsSet maps_;
  VirtualClosuresSet virtual_closures_;
  VirtualContextsSet virtual_contexts_;
  VirtualBoundFunctionsSet virtual_bound_functions_;

  Zone* const zone_;
};

void Hints::EnsureAllocated(Zone* zone, bool check_zone_equality) {
  if (IsAllocated()) {
    if (check_zone_equality) CHECK_EQ(zone, impl_->zone_);
    // ... else {zone} lives no longer than {impl_->zone_} but we have no way of
    // checking that.
  } else {
    impl_ = zone->New<HintsImpl>(zone);
  }
  DCHECK(IsAllocated());
}

struct VirtualBoundFunction {
  Hints const bound_target;
  HintsVector const bound_arguments;

  VirtualBoundFunction(Hints const& target, const HintsVector& arguments)
      : bound_target(target), bound_arguments(arguments) {}

  bool operator==(const VirtualBoundFunction& other) const {
    if (bound_arguments.size() != other.bound_arguments.size()) return false;
    if (bound_target != other.bound_target) return false;

    for (size_t i = 0; i < bound_arguments.size(); ++i) {
      if (bound_arguments[i] != other.bound_arguments[i]) return false;
    }
    return true;
  }
};

// A VirtualClosure is a SharedFunctionInfo and a FeedbackVector, plus
// Hints about the context in which a closure will be created from them.
class VirtualClosure {
 public:
  VirtualClosure(Handle<JSFunction> function, Isolate* isolate, Zone* zone);

  VirtualClosure(Handle<SharedFunctionInfo> shared,
                 Handle<FeedbackVector> feedback_vector,
                 Hints const& context_hints);

  Handle<SharedFunctionInfo> shared() const { return shared_; }
  Handle<FeedbackVector> feedback_vector() const { return feedback_vector_; }
  Hints const& context_hints() const { return context_hints_; }

  bool operator==(const VirtualClosure& other) const {
    // A feedback vector is never used for more than one SFI.  There might,
    // however, be two virtual closures with the same SFI and vector, but
    // different context hints. crbug.com/1024282 has a link to a document
    // describing why the context_hints_ might be different in that case.
    DCHECK_IMPLIES(feedback_vector_.equals(other.feedback_vector_),
                   shared_.equals(other.shared_));
    return feedback_vector_.equals(other.feedback_vector_) &&
           context_hints_ == other.context_hints_;
  }

 private:
  Handle<SharedFunctionInfo> const shared_;
  Handle<FeedbackVector> const feedback_vector_;
  Hints const context_hints_;
};

// A CompilationSubject is a VirtualClosure, optionally with a matching
// concrete closure.
class CompilationSubject {
 public:
  explicit CompilationSubject(VirtualClosure virtual_closure)
      : virtual_closure_(virtual_closure), closure_() {}

  // The zone parameter is to correctly initialize the virtual closure,
  // which contains zone-allocated context information.
  CompilationSubject(Handle<JSFunction> closure, Isolate* isolate, Zone* zone);

  const VirtualClosure& virtual_closure() const { return virtual_closure_; }
  MaybeHandle<JSFunction> closure() const { return closure_; }

 private:
  VirtualClosure const virtual_closure_;
  MaybeHandle<JSFunction> const closure_;
};

// A Callee is either a JSFunction (which may not have a feedback vector), or a
// VirtualClosure. Note that this is different from CompilationSubject, which
// always has a VirtualClosure.
class Callee {
 public:
  explicit Callee(Handle<JSFunction> jsfunction)
      : jsfunction_(jsfunction), virtual_closure_() {}
  explicit Callee(VirtualClosure const& virtual_closure)
      : jsfunction_(), virtual_closure_(virtual_closure) {}

  Handle<SharedFunctionInfo> shared(Isolate* isolate) const {
    return virtual_closure_.has_value()
               ? virtual_closure_->shared()
               : handle(jsfunction_.ToHandleChecked()->shared(), isolate);
  }

  bool HasFeedbackVector() const {
    Handle<JSFunction> function;
    return virtual_closure_.has_value() ||
           jsfunction_.ToHandleChecked()->has_feedback_vector();
  }

  CompilationSubject ToCompilationSubject(Isolate* isolate, Zone* zone) const {
    CHECK(HasFeedbackVector());
    return virtual_closure_.has_value()
               ? CompilationSubject(*virtual_closure_)
               : CompilationSubject(jsfunction_.ToHandleChecked(), isolate,
                                    zone);
  }

 private:
  MaybeHandle<JSFunction> const jsfunction_;
  base::Optional<VirtualClosure> const virtual_closure_;
};

// If a list of arguments (hints) is shorter than the function's parameter
// count, this enum expresses what we know about the missing arguments.
enum MissingArgumentsPolicy {
  kMissingArgumentsAreUndefined,  // ... as in the JS undefined value
  kMissingArgumentsAreUnknown,
};

// The SerializerForBackgroundCompilation makes sure that the relevant function
// data such as bytecode, SharedFunctionInfo and FeedbackVector, used by later
// optimizations in the compiler, is copied to the heap broker.
class SerializerForBackgroundCompilation {
 public:
  SerializerForBackgroundCompilation(
      ZoneStats* zone_stats, JSHeapBroker* broker,
      CompilationDependencies* dependencies, Handle<JSFunction> closure,
      SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset);
  Hints Run();  // NOTE: Returns empty for an
                // already-serialized function.

  class Environment;

 private:
  SerializerForBackgroundCompilation(
      ZoneStats* zone_stats, JSHeapBroker* broker,
      CompilationDependencies* dependencies, CompilationSubject function,
      base::Optional<Hints> new_target, const HintsVector& arguments,
      MissingArgumentsPolicy padding,
      SerializerForBackgroundCompilationFlags flags, int nesting_level);

  bool BailoutOnUninitialized(ProcessedFeedback const& feedback);

  void TraverseBytecode();

#define DECLARE_VISIT_BYTECODE(name, ...) \
  void Visit##name(interpreter::BytecodeArrayIterator* iterator);
  SUPPORTED_BYTECODE_LIST(DECLARE_VISIT_BYTECODE)
#undef DECLARE_VISIT_BYTECODE

  Hints& register_hints(interpreter::Register reg);

  // Return a vector containing the hints for the given register range (in
  // order). Also prepare these hints for feedback backpropagation by allocating
  // any that aren't yet allocated.
  HintsVector PrepareArgumentsHints(interpreter::Register first, size_t count);

  // Like above except that the hints have to be given directly.
  template <typename... MoreHints>
  HintsVector PrepareArgumentsHints(Hints* hints, MoreHints... more);

  void ProcessCalleeForCallOrConstruct(Callee const& callee,
                                       base::Optional<Hints> new_target,
                                       const HintsVector& arguments,
                                       SpeculationMode speculation_mode,
                                       MissingArgumentsPolicy padding,
                                       Hints* result_hints);
  void ProcessCalleeForCallOrConstruct(Handle<Object> callee,
                                       base::Optional<Hints> new_target,
                                       const HintsVector& arguments,
                                       SpeculationMode speculation_mode,
                                       MissingArgumentsPolicy padding,
                                       Hints* result_hints);
  void ProcessCallOrConstruct(Hints callee, base::Optional<Hints> new_target,
                              HintsVector* arguments, FeedbackSlot slot,
                              MissingArgumentsPolicy padding);
  void ProcessCallOrConstructRecursive(Hints const& callee,
                                       base::Optional<Hints> new_target,
                                       const HintsVector& arguments,
                                       SpeculationMode speculation_mode,
                                       MissingArgumentsPolicy padding,
                                       Hints* result_hints);
  void ProcessNewTargetForConstruct(Hints const& new_target,
                                    Hints* result_hints);
  void ProcessCallVarArgs(
      ConvertReceiverMode receiver_mode, Hints const& callee,
      interpreter::Register first_reg, int reg_count, FeedbackSlot slot,
      MissingArgumentsPolicy padding = kMissingArgumentsAreUndefined);
  void ProcessApiCall(Handle<SharedFunctionInfo> target,
                      const HintsVector& arguments);
  void ProcessReceiverMapForApiCall(FunctionTemplateInfoRef target,
                                    Handle<Map> receiver);
  void ProcessBuiltinCall(Handle<SharedFunctionInfo> target,
                          base::Optional<Hints> new_target,
                          const HintsVector& arguments,
                          SpeculationMode speculation_mode,
                          MissingArgumentsPolicy padding, Hints* result_hints);

  void ProcessJump(interpreter::BytecodeArrayIterator* iterator);

  void ProcessKeyedPropertyAccess(Hints* receiver, Hints const& key,
                                  FeedbackSlot slot, AccessMode access_mode,
                                  bool honor_bailout_on_uninitialized);
  void ProcessNamedPropertyAccess(Hints* receiver, NameRef const& name,
                                  FeedbackSlot slot, AccessMode access_mode);
  void ProcessNamedSuperPropertyAccess(Hints* receiver, NameRef const& name,
                                       FeedbackSlot slot,
                                       AccessMode access_mode);
  void ProcessNamedAccess(Hints* receiver, NamedAccessFeedback const& feedback,
                          AccessMode access_mode, Hints* result_hints);
  void ProcessNamedSuperAccess(Hints* receiver,
                               NamedAccessFeedback const& feedback,
                               AccessMode access_mode, Hints* result_hints);
  void ProcessElementAccess(Hints const& receiver, Hints const& key,
                            ElementAccessFeedback const& feedback,
                            AccessMode access_mode);
  void ProcessMinimorphicPropertyAccess(
      MinimorphicLoadPropertyAccessFeedback const& feedback,
      FeedbackSource const& source);

  void ProcessModuleVariableAccess(
      interpreter::BytecodeArrayIterator* iterator);

  void ProcessHintsForObjectCreate(Hints const& prototype);
  void ProcessMapHintsForPromises(Hints const& receiver_hints);
  void ProcessHintsForPromiseResolve(Hints const& resolution_hints);
  void ProcessHintsForHasInPrototypeChain(Hints const& instance_hints);
  void ProcessHintsForRegExpTest(Hints const& regexp_hints);
  PropertyAccessInfo ProcessMapForRegExpTest(MapRef map);
  void ProcessHintsForFunctionBind(Hints const& receiver_hints);
  void ProcessHintsForObjectGetPrototype(Hints const& object_hints);
  void ProcessConstantForOrdinaryHasInstance(HeapObjectRef const& constructor,
                                             bool* walk_prototypes);
  void ProcessConstantForInstanceOf(ObjectRef const& constant,
                                    bool* walk_prototypes);
  void ProcessHintsForOrdinaryHasInstance(Hints const& constructor_hints,
                                          Hints const& instance_hints);

  void ProcessGlobalAccess(FeedbackSlot slot, bool is_load);

  void ProcessCompareOperation(FeedbackSlot slot);
  void ProcessForIn(FeedbackSlot slot);
  void ProcessUnaryOrBinaryOperation(FeedbackSlot slot,
                                     bool honor_bailout_on_uninitialized);

  PropertyAccessInfo ProcessMapForNamedPropertyAccess(
      Hints* receiver, base::Optional<MapRef> receiver_map,
      MapRef lookup_start_object_map, NameRef const& name,
      AccessMode access_mode, base::Optional<JSObjectRef> concrete_receiver,
      Hints* result_hints);

  void ProcessCreateContext(interpreter::BytecodeArrayIterator* iterator,
                            int scopeinfo_operand_index);

  enum ContextProcessingMode {
    kIgnoreSlot,
    kSerializeSlot,
  };

  void ProcessContextAccess(Hints const& context_hints, int slot, int depth,
                            ContextProcessingMode mode,
                            Hints* result_hints = nullptr);
  void ProcessImmutableLoad(ContextRef const& context, int slot,
                            ContextProcessingMode mode,
                            Hints* new_accumulator_hints);
  void ProcessLdaLookupGlobalSlot(interpreter::BytecodeArrayIterator* iterator);
  void ProcessLdaLookupContextSlot(
      interpreter::BytecodeArrayIterator* iterator);

  // Performs extension lookups for [0, depth) like
  // BytecodeGraphBuilder::CheckContextExtensions().
  void ProcessCheckContextExtensions(int depth);

  Hints RunChildSerializer(CompilationSubject function,
                           base::Optional<Hints> new_target,
                           const HintsVector& arguments,
                           MissingArgumentsPolicy padding);

  // When (forward-)branching bytecodes are encountered, e.g. a conditional
  // jump, we call ContributeToJumpTargetEnvironment to "remember" the current
  // environment, associated with the jump target offset. When serialization
  // eventually reaches that offset, we call IncorporateJumpTargetEnvironment to
  // merge that environment back into whatever is the current environment then.
  // Note: Since there may be multiple jumps to the same target,
  // ContributeToJumpTargetEnvironment may actually do a merge as well.
  void ContributeToJumpTargetEnvironment(int target_offset);
  void IncorporateJumpTargetEnvironment(int target_offset);

  VirtualClosure function() const { return function_; }

  Hints& return_value_hints() { return return_value_hints_; }

  Handle<FeedbackVector> feedback_vector() const;
  Handle<BytecodeArray> bytecode_array() const;
  BytecodeAnalysis const& GetBytecodeAnalysis(
      SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);

  JSHeapBroker* broker() const { return broker_; }
  CompilationDependencies* dependencies() const { return dependencies_; }
  Zone* zone() { return zone_scope_.zone(); }
  Environment* environment() const { return environment_; }
  SerializerForBackgroundCompilationFlags flags() const { return flags_; }
  BailoutId osr_offset() const { return osr_offset_; }

  JSHeapBroker* const broker_;
  CompilationDependencies* const dependencies_;
  ZoneStats::Scope zone_scope_;
  SerializerForBackgroundCompilationFlags const flags_;
  // Instead of storing the virtual_closure here, we could extract it from the
  // {closure_hints_} but that would be cumbersome.
  VirtualClosure const function_;
  BailoutId const osr_offset_;
  ZoneUnorderedMap<int, Environment*> jump_target_environments_;
  Environment* const environment_;
  HintsVector const arguments_;
  Hints return_value_hints_;
  Hints closure_hints_;

  int nesting_level_ = 0;
};

void RunSerializerForBackgroundCompilation(
    ZoneStats* zone_stats, JSHeapBroker* broker,
    CompilationDependencies* dependencies, Handle<JSFunction> closure,
    SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset) {
  SerializerForBackgroundCompilation serializer(
      zone_stats, broker, dependencies, closure, flags, osr_offset);
  serializer.Run();
}

using BytecodeArrayIterator = interpreter::BytecodeArrayIterator;

VirtualClosure::VirtualClosure(Handle<SharedFunctionInfo> shared,
                               Handle<FeedbackVector> feedback_vector,
                               Hints const& context_hints)
    : shared_(shared),
      feedback_vector_(feedback_vector),
      context_hints_(context_hints) {
  // The checked invariant rules out recursion and thus avoids complexity.
  CHECK(context_hints_.virtual_closures().IsEmpty());
}

VirtualClosure::VirtualClosure(Handle<JSFunction> function, Isolate* isolate,
                               Zone* zone)
    : shared_(handle(function->shared(), isolate)),
      feedback_vector_(function->feedback_vector(), isolate),
      context_hints_(
          Hints::SingleConstant(handle(function->context(), isolate), zone)) {
  // The checked invariant rules out recursion and thus avoids complexity.
  CHECK(context_hints_.virtual_closures().IsEmpty());
}

CompilationSubject::CompilationSubject(Handle<JSFunction> closure,
                                       Isolate* isolate, Zone* zone)
    : virtual_closure_(closure, isolate, zone), closure_(closure) {
  CHECK(closure->has_feedback_vector());
}

Hints Hints::Copy(Zone* zone) const {
  if (!IsAllocated()) return *this;
  Hints result;
  result.EnsureAllocated(zone);
  result.impl_->constants_ = impl_->constants_;
  result.impl_->maps_ = impl_->maps_;
  result.impl_->virtual_contexts_ = impl_->virtual_contexts_;
  result.impl_->virtual_closures_ = impl_->virtual_closures_;
  result.impl_->virtual_bound_functions_ = impl_->virtual_bound_functions_;
  return result;
}

bool Hints::operator==(Hints const& other) const {
  if (impl_ == other.impl_) return true;
  if (IsEmpty() && other.IsEmpty()) return true;
  return IsAllocated() && other.IsAllocated() &&
         constants() == other.constants() &&
         virtual_closures() == other.virtual_closures() &&
         maps() == other.maps() &&
         virtual_contexts() == other.virtual_contexts() &&
         virtual_bound_functions() == other.virtual_bound_functions();
}

bool Hints::operator!=(Hints const& other) const { return !(*this == other); }

#ifdef ENABLE_SLOW_DCHECKS
bool Hints::Includes(Hints const& other) const {
  if (impl_ == other.impl_ || other.IsEmpty()) return true;
  return IsAllocated() && constants().Includes(other.constants()) &&
         virtual_closures().Includes(other.virtual_closures()) &&
         maps().Includes(other.maps());
}
#endif

Hints Hints::SingleConstant(Handle<Object> constant, Zone* zone) {
  Hints result;
  result.AddConstant(constant, zone, nullptr);
  return result;
}

Hints Hints::SingleMap(Handle<Map> map, Zone* zone) {
  Hints result;
  result.AddMap(map, zone, nullptr);
  return result;
}

ConstantsSet Hints::constants() const {
  return IsAllocated() ? impl_->constants_ : ConstantsSet();
}

MapsSet Hints::maps() const { return IsAllocated() ? impl_->maps_ : MapsSet(); }

VirtualClosuresSet Hints::virtual_closures() const {
  return IsAllocated() ? impl_->virtual_closures_ : VirtualClosuresSet();
}

VirtualContextsSet Hints::virtual_contexts() const {
  return IsAllocated() ? impl_->virtual_contexts_ : VirtualContextsSet();
}

VirtualBoundFunctionsSet Hints::virtual_bound_functions() const {
  return IsAllocated() ? impl_->virtual_bound_functions_
                       : VirtualBoundFunctionsSet();
}

void Hints::AddVirtualContext(VirtualContext const& virtual_context, Zone* zone,
                              JSHeapBroker* broker) {
  EnsureAllocated(zone);
  if (impl_->virtual_contexts_.Size() >= kMaxHintsSize) {
    TRACE_BROKER_MISSING(broker,
                         "opportunity - limit for virtual contexts reached.");
    return;
  }
  impl_->virtual_contexts_.Add(virtual_context, impl_->zone_);
}

void Hints::AddConstant(Handle<Object> constant, Zone* zone,
                        JSHeapBroker* broker) {
  EnsureAllocated(zone);
  if (impl_->constants_.Size() >= kMaxHintsSize) {
    TRACE_BROKER_MISSING(broker, "opportunity - limit for constants reached.");
    return;
  }
  impl_->constants_.Add(constant, impl_->zone_);
}

void Hints::AddMap(Handle<Map> map, Zone* zone, JSHeapBroker* broker,
                   bool check_zone_equality) {
  EnsureAllocated(zone, check_zone_equality);
  if (impl_->maps_.Size() >= kMaxHintsSize) {
    TRACE_BROKER_MISSING(broker, "opportunity - limit for maps reached.");
    return;
  }
  impl_->maps_.Add(map, impl_->zone_);
}

void Hints::AddVirtualClosure(VirtualClosure const& virtual_closure, Zone* zone,
                              JSHeapBroker* broker) {
  EnsureAllocated(zone);
  if (impl_->virtual_closures_.Size() >= kMaxHintsSize) {
    TRACE_BROKER_MISSING(broker,
                         "opportunity - limit for virtual closures reached.");
    return;
  }
  impl_->virtual_closures_.Add(virtual_closure, impl_->zone_);
}

void Hints::AddVirtualBoundFunction(VirtualBoundFunction const& bound_function,
                                    Zone* zone, JSHeapBroker* broker) {
  EnsureAllocated(zone);
  if (impl_->virtual_bound_functions_.Size() >= kMaxHintsSize) {
    TRACE_BROKER_MISSING(
        broker, "opportunity - limit for virtual bound functions reached.");
    return;
  }
  // TODO(mslekova): Consider filtering the hints in the added bound function,
  // for example: a) Remove any non-JS(Bound)Function constants, b) Truncate the
  // argument vector the formal parameter count.
  impl_->virtual_bound_functions_.Add(bound_function, impl_->zone_);
}

void Hints::Add(Hints const& other, Zone* zone, JSHeapBroker* broker) {
  if (impl_ == other.impl_ || other.IsEmpty()) return;
  EnsureAllocated(zone);
  if (!Union(other)) {
    TRACE_BROKER_MISSING(broker, "opportunity - hints limit reached.");
  }
}

Hints Hints::CopyToParentZone(Zone* zone, JSHeapBroker* broker) const {
  if (!IsAllocated()) return *this;

  Hints result;

  for (auto const& x : constants()) result.AddConstant(x, zone, broker);
  for (auto const& x : maps()) result.AddMap(x, zone, broker);
  for (auto const& x : virtual_contexts())
    result.AddVirtualContext(x, zone, broker);

  // Adding hints from a child serializer run means copying data out from
  // a zone that's being destroyed. VirtualClosures and VirtualBoundFunction
  // have zone allocated data, so we've got to make a deep copy to eliminate
  // traces of the dying zone.
  for (auto const& x : virtual_closures()) {
    VirtualClosure new_virtual_closure(
        x.shared(), x.feedback_vector(),
        x.context_hints().CopyToParentZone(zone, broker));
    result.AddVirtualClosure(new_virtual_closure, zone, broker);
  }
  for (auto const& x : virtual_bound_functions()) {
    HintsVector new_arguments_hints(zone);
    for (auto hint : x.bound_arguments) {
      new_arguments_hints.push_back(hint.CopyToParentZone(zone, broker));
    }
    VirtualBoundFunction new_bound_function(
        x.bound_target.CopyToParentZone(zone, broker), new_arguments_hints);
    result.AddVirtualBoundFunction(new_bound_function, zone, broker);
  }

  return result;
}

bool Hints::IsEmpty() const {
  if (!IsAllocated()) return true;
  return constants().IsEmpty() && maps().IsEmpty() &&
         virtual_closures().IsEmpty() && virtual_contexts().IsEmpty() &&
         virtual_bound_functions().IsEmpty();
}

std::ostream& operator<<(std::ostream& out,
                         const VirtualContext& virtual_context) {
  out << "Distance " << virtual_context.distance << " from "
      << Brief(*virtual_context.context) << std::endl;
  return out;
}

std::ostream& operator<<(std::ostream& out, const Hints& hints);

std::ostream& operator<<(std::ostream& out,
                         const VirtualClosure& virtual_closure) {
  out << Brief(*virtual_closure.shared()) << std::endl;
  out << Brief(*virtual_closure.feedback_vector()) << std::endl;
  !virtual_closure.context_hints().IsEmpty() &&
      out << virtual_closure.context_hints() << "):" << std::endl;
  return out;
}

std::ostream& operator<<(std::ostream& out,
                         const VirtualBoundFunction& virtual_bound_function) {
  out << std::endl << "    Target: " << virtual_bound_function.bound_target;
  out << "    Arguments:" << std::endl;
  for (auto hint : virtual_bound_function.bound_arguments) {
    out << "    " << hint;
  }
  return out;
}

std::ostream& operator<<(std::ostream& out, const Hints& hints) {
  out << "(impl_ = " << hints.impl_ << ")\n";
  for (Handle<Object> constant : hints.constants()) {
    out << "  constant " << Brief(*constant) << std::endl;
  }
  for (Handle<Map> map : hints.maps()) {
    out << "  map " << Brief(*map) << std::endl;
  }
  for (VirtualClosure const& virtual_closure : hints.virtual_closures()) {
    out << "  virtual closure " << virtual_closure << std::endl;
  }
  for (VirtualContext const& virtual_context : hints.virtual_contexts()) {
    out << "  virtual context " << virtual_context << std::endl;
  }
  for (VirtualBoundFunction const& virtual_bound_function :
       hints.virtual_bound_functions()) {
    out << "  virtual bound function " << virtual_bound_function << std::endl;
  }
  return out;
}

void Hints::Reset(Hints* other, Zone* zone) {
  other->EnsureShareable(zone);
  *this = *other;
  DCHECK(IsAllocated());
}

class SerializerForBackgroundCompilation::Environment : public ZoneObject {
 public:
  Environment(Zone* zone, CompilationSubject function);
  Environment(Zone* zone, Isolate* isolate, CompilationSubject function,
              base::Optional<Hints> new_target, const HintsVector& arguments,
              MissingArgumentsPolicy padding);

  bool IsDead() const { return !alive_; }

  void Kill() {
    DCHECK(!IsDead());
    alive_ = false;
    DCHECK(IsDead());
  }

  void Resurrect() {
    DCHECK(IsDead());
    alive_ = true;
    DCHECK(!IsDead());
  }

  // Merge {other} into {this} environment (leaving {other} unmodified).
  void Merge(Environment* other, Zone* zone, JSHeapBroker* broker);

  Hints const& current_context_hints() const { return current_context_hints_; }
  Hints const& accumulator_hints() const { return accumulator_hints_; }

  Hints& current_context_hints() { return current_context_hints_; }
  Hints& accumulator_hints() { return accumulator_hints_; }
  Hints& register_hints(interpreter::Register reg);

 private:
  friend std::ostream& operator<<(std::ostream& out, const Environment& env);

  Hints current_context_hints_;
  Hints accumulator_hints_;

  HintsVector parameters_hints_;  // First parameter is the receiver.
  HintsVector locals_hints_;

  bool alive_ = true;
};

SerializerForBackgroundCompilation::Environment::Environment(
    Zone* zone, CompilationSubject function)
    : parameters_hints_(function.virtual_closure()
                            .shared()
                            ->GetBytecodeArray()
                            .parameter_count(),
                        Hints(), zone),
      locals_hints_(function.virtual_closure()
                        .shared()
                        ->GetBytecodeArray()
                        .register_count(),
                    Hints(), zone) {
  // Consume the virtual_closure's context hint information.
  current_context_hints_ = function.virtual_closure().context_hints();
}

SerializerForBackgroundCompilation::Environment::Environment(
    Zone* zone, Isolate* isolate, CompilationSubject function,
    base::Optional<Hints> new_target, const HintsVector& arguments,
    MissingArgumentsPolicy padding)
    : Environment(zone, function) {
  // Set the hints for the actually passed arguments, at most up to
  // the parameter_count.
  for (size_t i = 0; i < std::min(arguments.size(), parameters_hints_.size());
       ++i) {
    parameters_hints_[i] = arguments[i];
  }

  if (padding == kMissingArgumentsAreUndefined) {
    Hints const undefined_hint =
        Hints::SingleConstant(isolate->factory()->undefined_value(), zone);
    for (size_t i = arguments.size(); i < parameters_hints_.size(); ++i) {
      parameters_hints_[i] = undefined_hint;
    }
  } else {
    DCHECK_EQ(padding, kMissingArgumentsAreUnknown);
  }

  // Set hints for new_target.
  interpreter::Register new_target_reg =
      function.virtual_closure()
          .shared()
          ->GetBytecodeArray()
          .incoming_new_target_or_generator_register();
  if (new_target_reg.is_valid()) {
    Hints& hints = register_hints(new_target_reg);
    CHECK(hints.IsEmpty());
    if (new_target.has_value()) hints = *new_target;
  }
}

Hints& SerializerForBackgroundCompilation::register_hints(
    interpreter::Register reg) {
  if (reg.is_function_closure()) return closure_hints_;
  return environment()->register_hints(reg);
}

Hints& SerializerForBackgroundCompilation::Environment::register_hints(
    interpreter::Register reg) {
  if (reg.is_current_context()) return current_context_hints_;
  if (reg.is_parameter()) {
    return parameters_hints_[reg.ToParameterIndex(
        static_cast<int>(parameters_hints_.size()))];
  }
  DCHECK(!reg.is_function_closure());
  CHECK_LT(reg.index(), locals_hints_.size());
  return locals_hints_[reg.index()];
}

void SerializerForBackgroundCompilation::Environment::Merge(
    Environment* other, Zone* zone, JSHeapBroker* broker) {
  // {other} is guaranteed to have the same layout because it comes from an
  // earlier bytecode in the same function.
  DCHECK_EQ(parameters_hints_.size(), other->parameters_hints_.size());
  DCHECK_EQ(locals_hints_.size(), other->locals_hints_.size());

  if (IsDead()) {
    parameters_hints_ = other->parameters_hints_;
    locals_hints_ = other->locals_hints_;
    current_context_hints_ = other->current_context_hints_;
    accumulator_hints_ = other->accumulator_hints_;
    Resurrect();
  } else {
    for (size_t i = 0; i < parameters_hints_.size(); ++i) {
      parameters_hints_[i].Merge(other->parameters_hints_[i], zone, broker);
    }
    for (size_t i = 0; i < locals_hints_.size(); ++i) {
      locals_hints_[i].Merge(other->locals_hints_[i], zone, broker);
    }
    current_context_hints_.Merge(other->current_context_hints_, zone, broker);
    accumulator_hints_.Merge(other->accumulator_hints_, zone, broker);
  }

  CHECK(!IsDead());
}

bool Hints::Union(Hints const& other) {
  CHECK(IsAllocated());
  if (impl_->constants_.Size() + other.constants().Size() > kMaxHintsSize ||
      impl_->maps_.Size() + other.maps().Size() > kMaxHintsSize ||
      impl_->virtual_closures_.Size() + other.virtual_closures().Size() >
          kMaxHintsSize ||
      impl_->virtual_contexts_.Size() + other.virtual_contexts().Size() >
          kMaxHintsSize ||
      impl_->virtual_bound_functions_.Size() +
              other.virtual_bound_functions().Size() >
          kMaxHintsSize) {
    return false;
  }
  Zone* zone = impl_->zone_;
  impl_->constants_.Union(other.constants(), zone);
  impl_->maps_.Union(other.maps(), zone);
  impl_->virtual_closures_.Union(other.virtual_closures(), zone);
  impl_->virtual_contexts_.Union(other.virtual_contexts(), zone);
  impl_->virtual_bound_functions_.Union(other.virtual_bound_functions(), zone);
  return true;
}

void Hints::Merge(Hints const& other, Zone* zone, JSHeapBroker* broker) {
  if (impl_ == other.impl_) {
    return;
  }
  if (!IsAllocated()) {
    *this = other.Copy(zone);
    DCHECK(IsAllocated());
    return;
  }
  *this = this->Copy(zone);
  if (!Union(other)) {
    TRACE_BROKER_MISSING(broker, "opportunity - hints limit reached.");
  }
  DCHECK(IsAllocated());
}

std::ostream& operator<<(
    std::ostream& out,
    const SerializerForBackgroundCompilation::Environment& env) {
  std::ostringstream output_stream;

  if (env.IsDead()) {
    output_stream << "dead\n";
  } else {
    output_stream << "alive\n";
    for (size_t i = 0; i < env.parameters_hints_.size(); ++i) {
      Hints const& hints = env.parameters_hints_[i];
      if (!hints.IsEmpty()) {
        if (i == 0) {
          output_stream << "Hints for <this>: ";
        } else {
          output_stream << "Hints for a" << i - 1 << ": ";
        }
        output_stream << hints;
      }
    }
    for (size_t i = 0; i < env.locals_hints_.size(); ++i) {
      Hints const& hints = env.locals_hints_[i];
      if (!hints.IsEmpty()) {
        output_stream << "Hints for r" << i << ": " << hints;
      }
    }
  }

  if (!env.current_context_hints().IsEmpty()) {
    output_stream << "Hints for <context>: " << env.current_context_hints();
  }
  if (!env.accumulator_hints().IsEmpty()) {
    output_stream << "Hints for <accumulator>: " << env.accumulator_hints();
  }

  out << output_stream.str();
  return out;
}

SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
    ZoneStats* zone_stats, JSHeapBroker* broker,
    CompilationDependencies* dependencies, Handle<JSFunction> closure,
    SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset)
    : broker_(broker),
      dependencies_(dependencies),
      zone_scope_(zone_stats, ZONE_NAME),
      flags_(flags),
      function_(closure, broker->isolate(), zone()),
      osr_offset_(osr_offset),
      jump_target_environments_(zone()),
      environment_(zone()->New<Environment>(
          zone(), CompilationSubject(closure, broker_->isolate(), zone()))),
      arguments_(zone()) {
  closure_hints_.AddConstant(closure, zone(), broker_);
  JSFunctionRef(broker, closure).Serialize();

  TRACE_BROKER(broker_, "Hints for <closure>: " << closure_hints_);
  TRACE_BROKER(broker_, "Initial environment:\n" << *environment_);
}

SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
    ZoneStats* zone_stats, JSHeapBroker* broker,
    CompilationDependencies* dependencies, CompilationSubject function,
    base::Optional<Hints> new_target, const HintsVector& arguments,
    MissingArgumentsPolicy padding,
    SerializerForBackgroundCompilationFlags flags, int nesting_level)
    : broker_(broker),
      dependencies_(dependencies),
      zone_scope_(zone_stats, ZONE_NAME),
      flags_(flags),
      function_(function.virtual_closure()),
      osr_offset_(BailoutId::None()),
      jump_target_environments_(zone()),
      environment_(zone()->New<Environment>(zone(), broker_->isolate(),
                                            function, new_target, arguments,
                                            padding)),
      arguments_(arguments),
      nesting_level_(nesting_level) {
  Handle<JSFunction> closure;
  if (function.closure().ToHandle(&closure)) {
    closure_hints_.AddConstant(closure, zone(), broker);
    JSFunctionRef(broker, closure).Serialize();
  } else {
    closure_hints_.AddVirtualClosure(function.virtual_closure(), zone(),
                                     broker);
  }

  TRACE_BROKER(broker_, "Hints for <closure>: " << closure_hints_);
  TRACE_BROKER(broker_, "Initial environment:\n" << *environment_);
}

bool SerializerForBackgroundCompilation::BailoutOnUninitialized(
    ProcessedFeedback const& feedback) {
  DCHECK(!environment()->IsDead());
  if (!(flags() &
        SerializerForBackgroundCompilationFlag::kBailoutOnUninitialized)) {
    return false;
  }
  if (!osr_offset().IsNone()) {
    // Exclude OSR from this optimization because we might end up skipping the
    // OSR entry point. TODO(neis): Support OSR?
    return false;
  }
  if (broker()->is_turboprop() &&
      feedback.slot_kind() == FeedbackSlotKind::kCall) {
    return false;
  }
  if (feedback.IsInsufficient()) {
    environment()->Kill();
    return true;
  }
  return false;
}

Hints SerializerForBackgroundCompilation::Run() {
  TraceScope tracer(broker(), this, "SerializerForBackgroundCompilation::Run");
  if (nesting_level_ >= FLAG_max_serializer_nesting) {
    TRACE_BROKER_MISSING(
        broker(),
        "opportunity - Reached max nesting level for "
        "SerializerForBackgroundCompilation::Run, bailing out.\n");
    return Hints();
  }

  TRACE_BROKER_MEMORY(broker(), "[serializer start] Broker zone usage: "
                                    << broker()->zone()->allocation_size());
  SharedFunctionInfoRef shared(broker(), function().shared());
  FeedbackVectorRef feedback_vector_ref(broker(), feedback_vector());
  if (!broker()->ShouldBeSerializedForCompilation(shared, feedback_vector_ref,
                                                  arguments_)) {
    TRACE_BROKER(broker(),
                 "opportunity - Already ran serializer for SharedFunctionInfo "
                     << Brief(*shared.object()) << ", bailing out.\n");
    return Hints();
  }

  {
    HintsVector arguments_copy_in_broker_zone(broker()->zone());
    for (auto const& hints : arguments_) {
      arguments_copy_in_broker_zone.push_back(
          hints.CopyToParentZone(broker()->zone(), broker()));
    }
    broker()->SetSerializedForCompilation(shared, feedback_vector_ref,
                                          arguments_copy_in_broker_zone);
  }

  // We eagerly call the {EnsureSourcePositionsAvailable} for all serialized
  // SFIs while still on the main thread. Source positions will later be used
  // by JSInliner::ReduceJSCall.
  if (flags() &
      SerializerForBackgroundCompilationFlag::kCollectSourcePositions) {
    SharedFunctionInfo::EnsureSourcePositionsAvailable(broker()->isolate(),
                                                       shared.object());
  }

  feedback_vector_ref.Serialize();
  TraverseBytecode();

  if (return_value_hints().IsEmpty()) {
    TRACE_BROKER(broker(), "Return value hints: none");
  } else {
    TRACE_BROKER(broker(), "Return value hints: " << return_value_hints());
  }
  TRACE_BROKER_MEMORY(broker(), "[serializer end] Broker zone usage: "
                                    << broker()->zone()->allocation_size());
  return return_value_hints();
}

class HandlerRangeMatcher {
 public:
  HandlerRangeMatcher(BytecodeArrayIterator const& bytecode_iterator,
                      Handle<BytecodeArray> bytecode_array)
      : bytecode_iterator_(bytecode_iterator) {
    HandlerTable table(*bytecode_array);
    for (int i = 0, n = table.NumberOfRangeEntries(); i < n; ++i) {
      ranges_.insert({table.GetRangeStart(i), table.GetRangeEnd(i),
                      table.GetRangeHandler(i)});
    }
    ranges_iterator_ = ranges_.cbegin();
  }

  using OffsetReporter = std::function<void(int handler_offset)>;

  void HandlerOffsetForCurrentPosition(const OffsetReporter& offset_reporter) {
    CHECK(!bytecode_iterator_.done());
    const int current_offset = bytecode_iterator_.current_offset();

    // Remove outdated try ranges from the stack.
    while (!stack_.empty()) {
      const int end = stack_.top().end;
      if (end < current_offset) {
        stack_.pop();
      } else {
        break;
      }
    }

    // Advance the iterator and maintain the stack.
    while (ranges_iterator_ != ranges_.cend() &&
           ranges_iterator_->start <= current_offset) {
      if (ranges_iterator_->end >= current_offset) {
        stack_.push(*ranges_iterator_);
        if (ranges_iterator_->start == current_offset) {
          offset_reporter(ranges_iterator_->handler);
        }
      }
      ranges_iterator_++;
    }

    if (!stack_.empty() && stack_.top().start < current_offset) {
      offset_reporter(stack_.top().handler);
    }
  }

 private:
  BytecodeArrayIterator const& bytecode_iterator_;

  struct Range {
    int start;
    int end;
    int handler;
    friend bool operator<(const Range& a, const Range& b) {
      if (a.start < b.start) return true;
      if (a.start == b.start) {
        if (a.end < b.end) return true;
        CHECK_GT(a.end, b.end);
      }
      return false;
    }
  };
  std::set<Range> ranges_;
  std::set<Range>::const_iterator ranges_iterator_;
  std::stack<Range> stack_;
};

Handle<FeedbackVector> SerializerForBackgroundCompilation::feedback_vector()
    const {
  return function().feedback_vector();
}

Handle<BytecodeArray> SerializerForBackgroundCompilation::bytecode_array()
    const {
  return handle(function().shared()->GetBytecodeArray(), broker()->isolate());
}

BytecodeAnalysis const& SerializerForBackgroundCompilation::GetBytecodeAnalysis(
    SerializationPolicy policy) {
  return broker()->GetBytecodeAnalysis(
      bytecode_array(), osr_offset(),
      flags() &
          SerializerForBackgroundCompilationFlag::kAnalyzeEnvironmentLiveness,
      policy);
}

void SerializerForBackgroundCompilation::TraverseBytecode() {
  BytecodeAnalysis const& bytecode_analysis =
      GetBytecodeAnalysis(SerializationPolicy::kSerializeIfNeeded);
  BytecodeArrayRef(broker(), bytecode_array()).SerializeForCompilation();

  BytecodeArrayIterator iterator(bytecode_array());
  HandlerRangeMatcher try_start_matcher(iterator, bytecode_array());

  bool has_one_shot_bytecode = false;
  for (; !iterator.done(); iterator.Advance()) {
    has_one_shot_bytecode =
        has_one_shot_bytecode ||
        interpreter::Bytecodes::IsOneShotBytecode(iterator.current_bytecode());

    int const current_offset = iterator.current_offset();

    // TODO(mvstanton): we might want to ignore the current environment if we
    // are at the start of a catch handler.
    IncorporateJumpTargetEnvironment(current_offset);

    TRACE_BROKER(broker(),
                 "Handling bytecode: " << current_offset << "  "
                                       << iterator.current_bytecode());
    TRACE_BROKER(broker(), "Current environment: " << *environment());

    if (environment()->IsDead()) {
      continue;  // Skip this bytecode since TF won't generate code for it.
    }

    auto save_handler_environments = [&](int handler_offset) {
      auto it = jump_target_environments_.find(handler_offset);
      if (it == jump_target_environments_.end()) {
        ContributeToJumpTargetEnvironment(handler_offset);
        TRACE_BROKER(broker(),
                     "Handler offset for current pos: " << handler_offset);
      }
    };
    try_start_matcher.HandlerOffsetForCurrentPosition(
        save_handler_environments);

    if (bytecode_analysis.IsLoopHeader(current_offset)) {
      // Graph builder might insert jumps to resume targets in the loop body.
      LoopInfo const& loop_info =
          bytecode_analysis.GetLoopInfoFor(current_offset);
      for (const auto& target : loop_info.resume_jump_targets()) {
        ContributeToJumpTargetEnvironment(target.target_offset());
      }
    }

    switch (iterator.current_bytecode()) {
#define DEFINE_BYTECODE_CASE(name)     \
  case interpreter::Bytecode::k##name: \
    Visit##name(&iterator);            \
    break;
      SUPPORTED_BYTECODE_LIST(DEFINE_BYTECODE_CASE)
#undef DEFINE_BYTECODE_CASE
    }
  }

  if (has_one_shot_bytecode) {
    broker()->isolate()->CountUsage(
        v8::Isolate::UseCounterFeature::kOptimizedFunctionWithOneShotBytecode);
  }
}

void SerializerForBackgroundCompilation::VisitGetIterator(
    BytecodeArrayIterator* iterator) {
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(0));
  FeedbackSlot load_slot = iterator->GetSlotOperand(1);
  FeedbackSlot call_slot = iterator->GetSlotOperand(2);

  Handle<Name> name = broker()->isolate()->factory()->iterator_symbol();
  ProcessNamedPropertyAccess(receiver, NameRef(broker(), name), load_slot,
                             AccessMode::kLoad);
  if (environment()->IsDead()) return;

  Hints callee;
  HintsVector args = PrepareArgumentsHints(receiver);

  ProcessCallOrConstruct(callee, base::nullopt, &args, call_slot,
                         kMissingArgumentsAreUndefined);
}

void SerializerForBackgroundCompilation::VisitGetSuperConstructor(
    BytecodeArrayIterator* iterator) {
  interpreter::Register dst = iterator->GetRegisterOperand(0);
  Hints result_hints;
  for (auto constant : environment()->accumulator_hints().constants()) {
    // For JSNativeContextSpecialization::ReduceJSGetSuperConstructor.
    if (!constant->IsJSFunction()) continue;
    MapRef map(broker(),
               handle(HeapObject::cast(*constant).map(), broker()->isolate()));
    map.SerializePrototype();
    ObjectRef proto = map.prototype();
    if (proto.IsHeapObject() && proto.AsHeapObject().map().is_constructor()) {
      result_hints.AddConstant(proto.object(), zone(), broker());
    }
  }
  register_hints(dst) = result_hints;
}

void SerializerForBackgroundCompilation::VisitGetTemplateObject(
    BytecodeArrayIterator* iterator) {
  TemplateObjectDescriptionRef description(
      broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  FeedbackSource source(feedback_vector(), slot);

  ProcessedFeedback const& feedback =
      broker()->ProcessFeedbackForTemplateObject(source);
  if (feedback.IsInsufficient()) {
    environment()->accumulator_hints() = Hints();
  } else {
    JSArrayRef template_object = feedback.AsTemplateObject().value();
    environment()->accumulator_hints() =
        Hints::SingleConstant(template_object.object(), zone());
  }
}

void SerializerForBackgroundCompilation::VisitLdaTrue(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints() = Hints::SingleConstant(
      broker()->isolate()->factory()->true_value(), zone());
}

void SerializerForBackgroundCompilation::VisitLdaFalse(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints() = Hints::SingleConstant(
      broker()->isolate()->factory()->false_value(), zone());
}

void SerializerForBackgroundCompilation::VisitLdaTheHole(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints() = Hints::SingleConstant(
      broker()->isolate()->factory()->the_hole_value(), zone());
}

void SerializerForBackgroundCompilation::VisitLdaUndefined(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints() = Hints::SingleConstant(
      broker()->isolate()->factory()->undefined_value(), zone());
}

void SerializerForBackgroundCompilation::VisitLdaNull(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints() = Hints::SingleConstant(
      broker()->isolate()->factory()->null_value(), zone());
}

void SerializerForBackgroundCompilation::VisitLdaZero(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints() = Hints::SingleConstant(
      handle(Smi::FromInt(0), broker()->isolate()), zone());
}

void SerializerForBackgroundCompilation::VisitLdaSmi(
    BytecodeArrayIterator* iterator) {
  Handle<Smi> smi(Smi::FromInt(iterator->GetImmediateOperand(0)),
                  broker()->isolate());
  environment()->accumulator_hints() = Hints::SingleConstant(smi, zone());
}

void SerializerForBackgroundCompilation::VisitInvokeIntrinsic(
    BytecodeArrayIterator* iterator) {
  Runtime::FunctionId functionId = iterator->GetIntrinsicIdOperand(0);
  // For JSNativeContextSpecialization::ReduceJSAsyncFunctionResolve and
  // JSNativeContextSpecialization::ReduceJSResolvePromise.
  switch (functionId) {
    case Runtime::kInlineAsyncFunctionResolve: {
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kAsyncFunctionResolve));
      interpreter::Register first_reg = iterator->GetRegisterOperand(1);
      size_t reg_count = iterator->GetRegisterCountOperand(2);
      CHECK_EQ(reg_count, 3);
      HintsVector args = PrepareArgumentsHints(first_reg, reg_count);
      Hints const& resolution_hints = args[1];  // The resolution object.
      ProcessHintsForPromiseResolve(resolution_hints);
      return;
    }
    case Runtime::kInlineAsyncGeneratorReject:
    case Runtime::kAsyncGeneratorReject: {
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kAsyncGeneratorReject));
      break;
    }
    case Runtime::kInlineAsyncGeneratorResolve:
    case Runtime::kAsyncGeneratorResolve: {
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kAsyncGeneratorResolve));
      break;
    }
    case Runtime::kInlineAsyncGeneratorYield:
    case Runtime::kAsyncGeneratorYield: {
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kAsyncGeneratorYield));
      break;
    }
    case Runtime::kInlineAsyncGeneratorAwaitUncaught:
    case Runtime::kAsyncGeneratorAwaitUncaught: {
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kAsyncGeneratorAwaitUncaught));
      break;
    }
    case Runtime::kInlineAsyncGeneratorAwaitCaught:
    case Runtime::kAsyncGeneratorAwaitCaught: {
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kAsyncGeneratorAwaitCaught));
      break;
    }
    case Runtime::kInlineAsyncFunctionAwaitUncaught:
    case Runtime::kAsyncFunctionAwaitUncaught: {
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kAsyncFunctionAwaitUncaught));
      break;
    }
    case Runtime::kInlineAsyncFunctionAwaitCaught:
    case Runtime::kAsyncFunctionAwaitCaught: {
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kAsyncFunctionAwaitCaught));
      break;
    }
    case Runtime::kInlineAsyncFunctionReject:
    case Runtime::kAsyncFunctionReject: {
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kAsyncFunctionReject));
      break;
    }
    case Runtime::kAsyncFunctionResolve: {
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kAsyncFunctionResolve));
      break;
    }
    case Runtime::kInlineCopyDataProperties:
    case Runtime::kCopyDataProperties: {
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kCopyDataProperties));
      break;
    }
    case Runtime::kInlineGetImportMetaObject: {
      Hints const& context_hints = environment()->current_context_hints();
      for (auto x : context_hints.constants()) {
        ContextRef(broker(), x)
            .GetModule(SerializationPolicy::kSerializeIfNeeded)
            .Serialize();
      }
      for (auto x : context_hints.virtual_contexts()) {
        ContextRef(broker(), x.context)
            .GetModule(SerializationPolicy::kSerializeIfNeeded)
            .Serialize();
      }
      break;
    }
    default: {
      break;
    }
  }
  environment()->accumulator_hints() = Hints();
}

void SerializerForBackgroundCompilation::VisitLdaConstant(
    BytecodeArrayIterator* iterator) {
  ObjectRef object(
      broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  environment()->accumulator_hints() =
      Hints::SingleConstant(object.object(), zone());
}

void SerializerForBackgroundCompilation::VisitPushContext(
    BytecodeArrayIterator* iterator) {
  register_hints(iterator->GetRegisterOperand(0))
      .Reset(&environment()->current_context_hints(), zone());
  environment()->current_context_hints().Reset(
      &environment()->accumulator_hints(), zone());
}

void SerializerForBackgroundCompilation::VisitPopContext(
    BytecodeArrayIterator* iterator) {
  environment()->current_context_hints().Reset(
      &register_hints(iterator->GetRegisterOperand(0)), zone());
}

void SerializerForBackgroundCompilation::ProcessImmutableLoad(
    ContextRef const& context_ref, int slot, ContextProcessingMode mode,
    Hints* result_hints) {
  DCHECK_EQ(mode, kSerializeSlot);
  base::Optional<ObjectRef> slot_value =
      context_ref.get(slot, SerializationPolicy::kSerializeIfNeeded);

  // If requested, record the object as a hint for the result value.
  if (result_hints != nullptr && slot_value.has_value()) {
    result_hints->AddConstant(slot_value.value().object(), zone(), broker());
  }
}

void SerializerForBackgroundCompilation::ProcessContextAccess(
    Hints const& context_hints, int slot, int depth, ContextProcessingMode mode,
    Hints* result_hints) {
  // This function is for JSContextSpecialization::ReduceJSLoadContext and
  // ReduceJSStoreContext. Those reductions attempt to eliminate as many
  // loads as possible by making use of constant Context objects. In the
  // case of an immutable load, ReduceJSLoadContext even attempts to load
  // the value at {slot}, replacing the load with a constant.
  for (auto x : context_hints.constants()) {
    if (x->IsContext()) {
      // Walk this context to the given depth and serialize the slot found.
      ContextRef context_ref(broker(), x);
      size_t remaining_depth = depth;
      context_ref = context_ref.previous(
          &remaining_depth, SerializationPolicy::kSerializeIfNeeded);
      if (remaining_depth == 0 && mode != kIgnoreSlot) {
        ProcessImmutableLoad(context_ref, slot, mode, result_hints);
      }
    }
  }
  for (auto x : context_hints.virtual_contexts()) {
    if (x.distance <= static_cast<unsigned int>(depth)) {
      ContextRef context_ref(broker(), x.context);
      size_t remaining_depth = depth - x.distance;
      context_ref = context_ref.previous(
          &remaining_depth, SerializationPolicy::kSerializeIfNeeded);
      if (remaining_depth == 0 && mode != kIgnoreSlot) {
        ProcessImmutableLoad(context_ref, slot, mode, result_hints);
      }
    }
  }
}

void SerializerForBackgroundCompilation::VisitLdaContextSlot(
    BytecodeArrayIterator* iterator) {
  Hints const& context_hints = register_hints(iterator->GetRegisterOperand(0));
  const int slot = iterator->GetIndexOperand(1);
  const int depth = iterator->GetUnsignedImmediateOperand(2);
  Hints new_accumulator_hints;
  ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot,
                       &new_accumulator_hints);
  environment()->accumulator_hints() = new_accumulator_hints;
}

void SerializerForBackgroundCompilation::VisitLdaCurrentContextSlot(
    BytecodeArrayIterator* iterator) {
  const int slot = iterator->GetIndexOperand(0);
  const int depth = 0;
  Hints const& context_hints = environment()->current_context_hints();
  Hints new_accumulator_hints;
  ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot,
                       &new_accumulator_hints);
  environment()->accumulator_hints() = new_accumulator_hints;
}

void SerializerForBackgroundCompilation::VisitLdaImmutableContextSlot(
    BytecodeArrayIterator* iterator) {
  const int slot = iterator->GetIndexOperand(1);
  const int depth = iterator->GetUnsignedImmediateOperand(2);
  Hints const& context_hints = register_hints(iterator->GetRegisterOperand(0));
  Hints new_accumulator_hints;
  ProcessContextAccess(context_hints, slot, depth, kSerializeSlot,
                       &new_accumulator_hints);
  environment()->accumulator_hints() = new_accumulator_hints;
}

void SerializerForBackgroundCompilation::VisitLdaImmutableCurrentContextSlot(
    BytecodeArrayIterator* iterator) {
  const int slot = iterator->GetIndexOperand(0);
  const int depth = 0;
  Hints const& context_hints = environment()->current_context_hints();
  Hints new_accumulator_hints;
  ProcessContextAccess(context_hints, slot, depth, kSerializeSlot,
                       &new_accumulator_hints);
  environment()->accumulator_hints() = new_accumulator_hints;
}

void SerializerForBackgroundCompilation::ProcessModuleVariableAccess(
    BytecodeArrayIterator* iterator) {
  const int slot = Context::EXTENSION_INDEX;
  const int depth = iterator->GetUnsignedImmediateOperand(1);
  Hints const& context_hints = environment()->current_context_hints();

  Hints result_hints;
  ProcessContextAccess(context_hints, slot, depth, kSerializeSlot,
                       &result_hints);
  for (Handle<Object> constant : result_hints.constants()) {
    ObjectRef object(broker(), constant);
    // For JSTypedLowering::BuildGetModuleCell.
    if (object.IsSourceTextModule()) object.AsSourceTextModule().Serialize();
  }
}

void SerializerForBackgroundCompilation::VisitLdaModuleVariable(
    BytecodeArrayIterator* iterator) {
  ProcessModuleVariableAccess(iterator);
}

void SerializerForBackgroundCompilation::VisitStaModuleVariable(
    BytecodeArrayIterator* iterator) {
  ProcessModuleVariableAccess(iterator);
}

void SerializerForBackgroundCompilation::VisitStaLookupSlot(
    BytecodeArrayIterator* iterator) {
  ObjectRef(broker(),
            iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  environment()->accumulator_hints() = Hints();
}

void SerializerForBackgroundCompilation::VisitStaContextSlot(
    BytecodeArrayIterator* iterator) {
  const int slot = iterator->GetIndexOperand(1);
  const int depth = iterator->GetUnsignedImmediateOperand(2);
  Hints const& hints = register_hints(iterator->GetRegisterOperand(0));
  ProcessContextAccess(hints, slot, depth, kIgnoreSlot);
}

void SerializerForBackgroundCompilation::VisitStaCurrentContextSlot(
    BytecodeArrayIterator* iterator) {
  const int slot = iterator->GetIndexOperand(0);
  const int depth = 0;
  Hints const& context_hints = environment()->current_context_hints();
  ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot);
}

void SerializerForBackgroundCompilation::VisitLdar(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints().Reset(
      &register_hints(iterator->GetRegisterOperand(0)), zone());
}

void SerializerForBackgroundCompilation::VisitStar(
    BytecodeArrayIterator* iterator) {
  interpreter::Register reg = iterator->GetRegisterOperand(0);
  register_hints(reg).Reset(&environment()->accumulator_hints(), zone());
}

void SerializerForBackgroundCompilation::VisitMov(
    BytecodeArrayIterator* iterator) {
  interpreter::Register src = iterator->GetRegisterOperand(0);
  interpreter::Register dst = iterator->GetRegisterOperand(1);
  register_hints(dst).Reset(&register_hints(src), zone());
}

void SerializerForBackgroundCompilation::VisitCreateRegExpLiteral(
    BytecodeArrayIterator* iterator) {
  Handle<String> constant_pattern = Handle<String>::cast(
      iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  StringRef description(broker(), constant_pattern);
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  FeedbackSource source(feedback_vector(), slot);
  broker()->ProcessFeedbackForRegExpLiteral(source);
  environment()->accumulator_hints() = Hints();
}

void SerializerForBackgroundCompilation::VisitCreateArrayLiteral(
    BytecodeArrayIterator* iterator) {
  Handle<ArrayBoilerplateDescription> array_boilerplate_description =
      Handle<ArrayBoilerplateDescription>::cast(
          iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  ArrayBoilerplateDescriptionRef description(broker(),
                                             array_boilerplate_description);
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  FeedbackSource source(feedback_vector(), slot);
  broker()->ProcessFeedbackForArrayOrObjectLiteral(source);
  environment()->accumulator_hints() = Hints();
}

void SerializerForBackgroundCompilation::VisitCreateEmptyArrayLiteral(
    BytecodeArrayIterator* iterator) {
  FeedbackSlot slot = iterator->GetSlotOperand(0);
  FeedbackSource source(feedback_vector(), slot);
  broker()->ProcessFeedbackForArrayOrObjectLiteral(source);
  environment()->accumulator_hints() = Hints();
}

void SerializerForBackgroundCompilation::VisitCreateObjectLiteral(
    BytecodeArrayIterator* iterator) {
  Handle<ObjectBoilerplateDescription> constant_properties =
      Handle<ObjectBoilerplateDescription>::cast(
          iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  ObjectBoilerplateDescriptionRef description(broker(), constant_properties);
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  FeedbackSource source(feedback_vector(), slot);
  broker()->ProcessFeedbackForArrayOrObjectLiteral(source);
  environment()->accumulator_hints() = Hints();
}

void SerializerForBackgroundCompilation::VisitCreateFunctionContext(
    BytecodeArrayIterator* iterator) {
  ProcessCreateContext(iterator, 0);
}

void SerializerForBackgroundCompilation::VisitCreateBlockContext(
    BytecodeArrayIterator* iterator) {
  ProcessCreateContext(iterator, 0);
}

void SerializerForBackgroundCompilation::VisitCreateEvalContext(
    BytecodeArrayIterator* iterator) {
  ProcessCreateContext(iterator, 0);
}

void SerializerForBackgroundCompilation::VisitCreateWithContext(
    BytecodeArrayIterator* iterator) {
  ProcessCreateContext(iterator, 1);
}

void SerializerForBackgroundCompilation::VisitCreateCatchContext(
    BytecodeArrayIterator* iterator) {
  ProcessCreateContext(iterator, 1);
}

void SerializerForBackgroundCompilation::VisitForInNext(
    BytecodeArrayIterator* iterator) {
  FeedbackSlot slot = iterator->GetSlotOperand(3);
  ProcessForIn(slot);
}

void SerializerForBackgroundCompilation::VisitForInPrepare(
    BytecodeArrayIterator* iterator) {
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  ProcessForIn(slot);
}

void SerializerForBackgroundCompilation::ProcessCreateContext(
    interpreter::BytecodeArrayIterator* iterator, int scopeinfo_operand_index) {
  Handle<ScopeInfo> scope_info =
      Handle<ScopeInfo>::cast(iterator->GetConstantForIndexOperand(
          scopeinfo_operand_index, broker()->isolate()));
  ScopeInfoRef scope_info_ref(broker(), scope_info);
  scope_info_ref.SerializeScopeInfoChain();

  Hints const& current_context_hints = environment()->current_context_hints();
  Hints result_hints;

  // For each constant context, we must create a virtual context from
  // it of distance one.
  for (auto x : current_context_hints.constants()) {
    if (x->IsContext()) {
      Handle<Context> as_context(Handle<Context>::cast(x));
      result_hints.AddVirtualContext(VirtualContext(1, as_context), zone(),
                                     broker());
    }
  }

  // For each virtual context, we must create a virtual context from
  // it of distance {existing distance} + 1.
  for (auto x : current_context_hints.virtual_contexts()) {
    result_hints.AddVirtualContext(VirtualContext(x.distance + 1, x.context),
                                   zone(), broker());
  }

  environment()->accumulator_hints() = result_hints;
}

void SerializerForBackgroundCompilation::VisitCreateClosure(
    BytecodeArrayIterator* iterator) {
  Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(
      iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  Handle<FeedbackCell> feedback_cell =
      feedback_vector()->GetClosureFeedbackCell(iterator->GetIndexOperand(1));
  FeedbackCellRef feedback_cell_ref(broker(), feedback_cell);
  Handle<Object> cell_value(feedback_cell->value(), broker()->isolate());
  ObjectRef cell_value_ref(broker(), cell_value);

  Hints result_hints;
  if (cell_value->IsFeedbackVector()) {
    VirtualClosure virtual_closure(shared,
                                   Handle<FeedbackVector>::cast(cell_value),
                                   environment()->current_context_hints());
    result_hints.AddVirtualClosure(virtual_closure, zone(), broker());
  }
  environment()->accumulator_hints() = result_hints;
}

void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver(
    BytecodeArrayIterator* iterator) {
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  interpreter::Register first_reg = iterator->GetRegisterOperand(1);
  int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
  FeedbackSlot slot = iterator->GetSlotOperand(3);
  ProcessCallVarArgs(ConvertReceiverMode::kNullOrUndefined, callee, first_reg,
                     reg_count, slot);
}

void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver0(
    BytecodeArrayIterator* iterator) {
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  FeedbackSlot slot = iterator->GetSlotOperand(1);

  Hints const receiver = Hints::SingleConstant(
      broker()->isolate()->factory()->undefined_value(), zone());
  HintsVector parameters({receiver}, zone());

  ProcessCallOrConstruct(callee, base::nullopt, &parameters, slot,
                         kMissingArgumentsAreUndefined);
}

namespace {
void PrepareArgumentsHintsInternal(Zone* zone, HintsVector* args) {}

template <typename... MoreHints>
void PrepareArgumentsHintsInternal(Zone* zone, HintsVector* args, Hints* hints,
                                   MoreHints... more) {
  hints->EnsureShareable(zone);
  args->push_back(*hints);
  PrepareArgumentsHintsInternal(zone, args, more...);
}
}  // namespace

template <typename... MoreHints>
HintsVector SerializerForBackgroundCompilation::PrepareArgumentsHints(
    Hints* hints, MoreHints... more) {
  HintsVector args(zone());
  PrepareArgumentsHintsInternal(zone(), &args, hints, more...);
  return args;
}

HintsVector SerializerForBackgroundCompilation::PrepareArgumentsHints(
    interpreter::Register first, size_t count) {
  HintsVector result(zone());
  const int reg_base = first.index();
  for (int i = 0; i < static_cast<int>(count); ++i) {
    Hints& hints = register_hints(interpreter::Register(reg_base + i));
    hints.EnsureShareable(zone());
    result.push_back(hints);
  }
  return result;
}

void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver1(
    BytecodeArrayIterator* iterator) {
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  Hints* arg0 = &register_hints(iterator->GetRegisterOperand(1));
  FeedbackSlot slot = iterator->GetSlotOperand(2);

  Hints receiver = Hints::SingleConstant(
      broker()->isolate()->factory()->undefined_value(), zone());
  HintsVector args = PrepareArgumentsHints(&receiver, arg0);

  ProcessCallOrConstruct(callee, base::nullopt, &args, slot,
                         kMissingArgumentsAreUndefined);
}

void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver2(
    BytecodeArrayIterator* iterator) {
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  Hints* arg0 = &register_hints(iterator->GetRegisterOperand(1));
  Hints* arg1 = &register_hints(iterator->GetRegisterOperand(2));
  FeedbackSlot slot = iterator->GetSlotOperand(3);

  Hints receiver = Hints::SingleConstant(
      broker()->isolate()->factory()->undefined_value(), zone());
  HintsVector args = PrepareArgumentsHints(&receiver, arg0, arg1);

  ProcessCallOrConstruct(callee, base::nullopt, &args, slot,
                         kMissingArgumentsAreUndefined);
}

void SerializerForBackgroundCompilation::VisitCallAnyReceiver(
    BytecodeArrayIterator* iterator) {
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  interpreter::Register first_reg = iterator->GetRegisterOperand(1);
  int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
  FeedbackSlot slot = iterator->GetSlotOperand(3);
  ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count,
                     slot);
}

void SerializerForBackgroundCompilation::VisitCallNoFeedback(
    BytecodeArrayIterator* iterator) {
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  interpreter::Register first_reg = iterator->GetRegisterOperand(1);
  int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
  ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count,
                     FeedbackSlot::Invalid());
}

void SerializerForBackgroundCompilation::VisitCallProperty(
    BytecodeArrayIterator* iterator) {
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  interpreter::Register first_reg = iterator->GetRegisterOperand(1);
  int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
  FeedbackSlot slot = iterator->GetSlotOperand(3);
  ProcessCallVarArgs(ConvertReceiverMode::kNotNullOrUndefined, callee,
                     first_reg, reg_count, slot);
}

void SerializerForBackgroundCompilation::VisitCallProperty0(
    BytecodeArrayIterator* iterator) {
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(1));
  FeedbackSlot slot = iterator->GetSlotOperand(2);

  HintsVector args = PrepareArgumentsHints(receiver);

  ProcessCallOrConstruct(callee, base::nullopt, &args, slot,
                         kMissingArgumentsAreUndefined);
}

void SerializerForBackgroundCompilation::VisitCallProperty1(
    BytecodeArrayIterator* iterator) {
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(1));
  Hints* arg0 = &register_hints(iterator->GetRegisterOperand(2));
  FeedbackSlot slot = iterator->GetSlotOperand(3);

  HintsVector args = PrepareArgumentsHints(receiver, arg0);

  ProcessCallOrConstruct(callee, base::nullopt, &args, slot,
                         kMissingArgumentsAreUndefined);
}

void SerializerForBackgroundCompilation::VisitCallProperty2(
    BytecodeArrayIterator* iterator) {
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(1));
  Hints* arg0 = &register_hints(iterator->GetRegisterOperand(2));
  Hints* arg1 = &register_hints(iterator->GetRegisterOperand(3));
  FeedbackSlot slot = iterator->GetSlotOperand(4);

  HintsVector args = PrepareArgumentsHints(receiver, arg0, arg1);

  ProcessCallOrConstruct(callee, base::nullopt, &args, slot,
                         kMissingArgumentsAreUndefined);
}

void SerializerForBackgroundCompilation::VisitCallWithSpread(
    BytecodeArrayIterator* iterator) {
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  interpreter::Register first_reg = iterator->GetRegisterOperand(1);
  int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
  FeedbackSlot slot = iterator->GetSlotOperand(3);
  ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count,
                     slot, kMissingArgumentsAreUnknown);
}

void SerializerForBackgroundCompilation::VisitCallJSRuntime(
    BytecodeArrayIterator* iterator) {
  const int runtime_index = iterator->GetNativeContextIndexOperand(0);
  ObjectRef constant =
      broker()
          ->target_native_context()
          .get(runtime_index, SerializationPolicy::kSerializeIfNeeded)
          .value();
  Hints const callee = Hints::SingleConstant(constant.object(), zone());
  interpreter::Register first_reg = iterator->GetRegisterOperand(1);
  int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
  ProcessCallVarArgs(ConvertReceiverMode::kNullOrUndefined, callee, first_reg,
                     reg_count, FeedbackSlot::Invalid());
}

Hints SerializerForBackgroundCompilation::RunChildSerializer(
    CompilationSubject function, base::Optional<Hints> new_target,
    const HintsVector& arguments, MissingArgumentsPolicy padding) {
  SerializerForBackgroundCompilation child_serializer(
      zone_scope_.zone_stats(), broker(), dependencies(), function, new_target,
      arguments, padding, flags(), nesting_level_ + 1);
  Hints result = child_serializer.Run();
  // The Hints returned by the call to Run are allocated in the zone
  // created by the child serializer. Adding those hints to a hints
  // object created in our zone will preserve the information.
  return result.CopyToParentZone(zone(), broker());
}

void SerializerForBackgroundCompilation::ProcessCalleeForCallOrConstruct(
    Callee const& callee, base::Optional<Hints> new_target,
    const HintsVector& arguments, SpeculationMode speculation_mode,
    MissingArgumentsPolicy padding, Hints* result_hints) {
  Handle<SharedFunctionInfo> shared = callee.shared(broker()->isolate());
  if (shared->IsApiFunction()) {
    ProcessApiCall(shared, arguments);
    DCHECK_NE(shared->GetInlineability(), SharedFunctionInfo::kIsInlineable);
  } else if (shared->HasBuiltinId()) {
    ProcessBuiltinCall(shared, new_target, arguments, speculation_mode, padding,
                       result_hints);
    DCHECK_NE(shared->GetInlineability(), SharedFunctionInfo::kIsInlineable);
  } else if ((flags() &
              SerializerForBackgroundCompilationFlag::kEnableTurboInlining) &&
             shared->GetInlineability() == SharedFunctionInfo::kIsInlineable &&
             callee.HasFeedbackVector()) {
    CompilationSubject subject =
        callee.ToCompilationSubject(broker()->isolate(), zone());
    result_hints->Add(
        RunChildSerializer(subject, new_target, arguments, padding), zone(),
        broker());
  }
}

namespace {
// Returns the innermost bound target and inserts all bound arguments and
// {original_arguments} into {expanded_arguments} in the appropriate order.
JSReceiverRef UnrollBoundFunction(JSBoundFunctionRef const& bound_function,
                                  JSHeapBroker* broker,
                                  const HintsVector& original_arguments,
                                  HintsVector* expanded_arguments, Zone* zone) {
  DCHECK(expanded_arguments->empty());

  JSReceiverRef target = bound_function.AsJSReceiver();
  HintsVector reversed_bound_arguments(zone);
  for (; target.IsJSBoundFunction();
       target = target.AsJSBoundFunction().bound_target_function()) {
    for (int i = target.AsJSBoundFunction().bound_arguments().length() - 1;
         i >= 0; --i) {
      Hints const arg = Hints::SingleConstant(
          target.AsJSBoundFunction().bound_arguments().get(i).object(), zone);
      reversed_bound_arguments.push_back(arg);
    }
    Hints const arg = Hints::SingleConstant(
        target.AsJSBoundFunction().bound_this().object(), zone);
    reversed_bound_arguments.push_back(arg);
  }

  expanded_arguments->insert(expanded_arguments->end(),
                             reversed_bound_arguments.rbegin(),
                             reversed_bound_arguments.rend());
  expanded_arguments->insert(expanded_arguments->end(),
                             original_arguments.begin(),
                             original_arguments.end());

  return target;
}
}  // namespace

void SerializerForBackgroundCompilation::ProcessCalleeForCallOrConstruct(
    Handle<Object> callee, base::Optional<Hints> new_target,
    const HintsVector& arguments, SpeculationMode speculation_mode,
    MissingArgumentsPolicy padding, Hints* result_hints) {
  const HintsVector* actual_arguments = &arguments;
  HintsVector expanded_arguments(zone());
  if (callee->IsJSBoundFunction()) {
    JSBoundFunctionRef bound_function(broker(),
                                      Handle<JSBoundFunction>::cast(callee));
    if (!bound_function.Serialize()) return;
    callee = UnrollBoundFunction(bound_function, broker(), arguments,
                                 &expanded_arguments, zone())
                 .object();
    actual_arguments = &expanded_arguments;
  }
  if (!callee->IsJSFunction()) return;

  JSFunctionRef function(broker(), Handle<JSFunction>::cast(callee));
  function.Serialize();
  Callee new_callee(function.object());
  ProcessCalleeForCallOrConstruct(new_callee, new_target, *actual_arguments,
                                  speculation_mode, padding, result_hints);
}

void SerializerForBackgroundCompilation::ProcessCallOrConstruct(
    Hints callee, base::Optional<Hints> new_target, HintsVector* arguments,
    FeedbackSlot slot, MissingArgumentsPolicy padding) {
  SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation;

  if (!slot.IsInvalid()) {
    FeedbackSource source(feedback_vector(), slot);
    ProcessedFeedback const& feedback =
        broker()->ProcessFeedbackForCall(source);
    if (BailoutOnUninitialized(feedback)) return;

    if (!feedback.IsInsufficient()) {
      // Incorporate feedback into hints copy to simplify processing.
      // TODO(neis): Modify the original hints instead?
      speculation_mode = feedback.AsCall().speculation_mode();
      // Incorporate target feedback into hints copy to simplify processing.
      base::Optional<HeapObjectRef> target = feedback.AsCall().target();
      if (target.has_value() &&
          (target->map().is_callable() || target->IsFeedbackCell())) {
        callee = callee.Copy(zone());
        // TODO(mvstanton): if the map isn't callable then we have an allocation
        // site, and it may make sense to add the Array JSFunction constant.
        if (new_target.has_value()) {
          // Construct; feedback is new_target, which often is also the callee.
          new_target = new_target->Copy(zone());
          new_target->AddConstant(target->object(), zone(), broker());
          callee.AddConstant(target->object(), zone(), broker());
        } else {
          // Call; target is feedback cell or callee.
          if (target->IsFeedbackCell() &&
              target->AsFeedbackCell().value().IsFeedbackVector()) {
            FeedbackVectorRef vector =
                target->AsFeedbackCell().value().AsFeedbackVector();
            vector.Serialize();
            VirtualClosure virtual_closure(
                vector.shared_function_info().object(), vector.object(),
                Hints());
            callee.AddVirtualClosure(virtual_closure, zone(), broker());
          } else {
            callee.AddConstant(target->object(), zone(), broker());
          }
        }
      }
    }
  }

  Hints result_hints_from_new_target;
  if (new_target.has_value()) {
    ProcessNewTargetForConstruct(*new_target, &result_hints_from_new_target);
    // These hints are a good guess at the resulting object, so they are useful
    // for both the accumulator and the constructor call's receiver. The latter
    // is still missing completely in {arguments} so add it now.
    arguments->insert(arguments->begin(), result_hints_from_new_target);
  }

  // For JSNativeContextSpecialization::InferRootMap
  Hints new_accumulator_hints = result_hints_from_new_target.Copy(zone());

  ProcessCallOrConstructRecursive(callee, new_target, *arguments,
                                  speculation_mode, padding,
                                  &new_accumulator_hints);
  environment()->accumulator_hints() = new_accumulator_hints;
}

void SerializerForBackgroundCompilation::ProcessCallOrConstructRecursive(
    Hints const& callee, base::Optional<Hints> new_target,
    const HintsVector& arguments, SpeculationMode speculation_mode,
    MissingArgumentsPolicy padding, Hints* result_hints) {
  // For JSCallReducer::ReduceJSCall and JSCallReducer::ReduceJSConstruct.
  for (auto constant : callee.constants()) {
    ProcessCalleeForCallOrConstruct(constant, new_target, arguments,
                                    speculation_mode, padding, result_hints);
  }

  // For JSCallReducer::ReduceJSCall and JSCallReducer::ReduceJSConstruct.
  for (auto hint : callee.virtual_closures()) {
    ProcessCalleeForCallOrConstruct(Callee(hint), new_target, arguments,
                                    speculation_mode, padding, result_hints);
  }

  for (auto hint : callee.virtual_bound_functions()) {
    HintsVector new_arguments = hint.bound_arguments;
    new_arguments.insert(new_arguments.end(), arguments.begin(),
                         arguments.end());
    ProcessCallOrConstructRecursive(hint.bound_target, new_target,
                                    new_arguments, speculation_mode, padding,
                                    result_hints);
  }
}

void SerializerForBackgroundCompilation::ProcessNewTargetForConstruct(
    Hints const& new_target_hints, Hints* result_hints) {
  for (Handle<Object> target : new_target_hints.constants()) {
    if (target->IsJSBoundFunction()) {
      // Unroll the bound function.
      while (target->IsJSBoundFunction()) {
        target = handle(
            Handle<JSBoundFunction>::cast(target)->bound_target_function(),
            broker()->isolate());
      }
    }
    if (target->IsJSFunction()) {
      Handle<JSFunction> new_target(Handle<JSFunction>::cast(target));
      if (new_target->has_prototype_slot(broker()->isolate()) &&
          new_target->has_initial_map()) {
        result_hints->AddMap(
            handle(new_target->initial_map(), broker()->isolate()), zone(),
            broker());
      }
    }
  }

  for (auto const& virtual_bound_function :
       new_target_hints.virtual_bound_functions()) {
    ProcessNewTargetForConstruct(virtual_bound_function.bound_target,
                                 result_hints);
  }
}

void SerializerForBackgroundCompilation::ProcessCallVarArgs(
    ConvertReceiverMode receiver_mode, Hints const& callee,
    interpreter::Register first_reg, int reg_count, FeedbackSlot slot,
    MissingArgumentsPolicy padding) {
  HintsVector args = PrepareArgumentsHints(first_reg, reg_count);
  // The receiver is either given in the first register or it is implicitly
  // the {undefined} value.
  if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) {
    args.insert(args.begin(),
                Hints::SingleConstant(
                    broker()->isolate()->factory()->undefined_value(), zone()));
  }
  ProcessCallOrConstruct(callee, base::nullopt, &args, slot, padding);
}

void SerializerForBackgroundCompilation::ProcessApiCall(
    Handle<SharedFunctionInfo> target, const HintsVector& arguments) {
  ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                          Builtins::kCallFunctionTemplate_CheckAccess));
  ObjectRef(broker(),
            broker()->isolate()->builtins()->builtin_handle(
                Builtins::kCallFunctionTemplate_CheckCompatibleReceiver));
  ObjectRef(
      broker(),
      broker()->isolate()->builtins()->builtin_handle(
          Builtins::kCallFunctionTemplate_CheckAccessAndCompatibleReceiver));

  FunctionTemplateInfoRef target_template_info(
      broker(),
      handle(target->function_data(kAcquireLoad), broker()->isolate()));
  if (!target_template_info.has_call_code()) return;
  target_template_info.SerializeCallCode();

  SharedFunctionInfoRef target_ref(broker(), target);
  target_ref.SerializeFunctionTemplateInfo();

  if (target_template_info.accept_any_receiver() &&
      target_template_info.is_signature_undefined()) {
    return;
  }

  if (arguments.empty()) return;
  Hints const& receiver_hints = arguments[0];
  for (auto hint : receiver_hints.constants()) {
    if (hint->IsUndefined()) {
      // The receiver is the global proxy.
      Handle<JSGlobalProxy> global_proxy =
          broker()->target_native_context().global_proxy_object().object();
      ProcessReceiverMapForApiCall(
          target_template_info,
          handle(global_proxy->map(), broker()->isolate()));
      continue;
    }

    if (!hint->IsJSReceiver()) continue;
    Handle<JSReceiver> receiver(Handle<JSReceiver>::cast(hint));

    ProcessReceiverMapForApiCall(target_template_info,
                                 handle(receiver->map(), broker()->isolate()));
  }

  for (auto receiver_map : receiver_hints.maps()) {
    ProcessReceiverMapForApiCall(target_template_info, receiver_map);
  }
}

void SerializerForBackgroundCompilation::ProcessReceiverMapForApiCall(
    FunctionTemplateInfoRef target, Handle<Map> receiver) {
  if (!receiver->is_access_check_needed()) {
    MapRef receiver_map(broker(), receiver);
    TRACE_BROKER(broker(), "Serializing holder for target: " << target);
    target.LookupHolderOfExpectedType(receiver_map,
                                      SerializationPolicy::kSerializeIfNeeded);
  }
}

void SerializerForBackgroundCompilation::ProcessHintsForObjectCreate(
    Hints const& prototype) {
  for (Handle<Object> constant_handle : prototype.constants()) {
    ObjectRef constant(broker(), constant_handle);
    if (constant.IsJSObject()) constant.AsJSObject().SerializeObjectCreateMap();
  }
}

void SerializerForBackgroundCompilation::ProcessBuiltinCall(
    Handle<SharedFunctionInfo> target, base::Optional<Hints> new_target,
    const HintsVector& arguments, SpeculationMode speculation_mode,
    MissingArgumentsPolicy padding, Hints* result_hints) {
  DCHECK(target->HasBuiltinId());
  const int builtin_id = target->builtin_id();
  const char* name = Builtins::name(builtin_id);
  TRACE_BROKER(broker(), "Serializing for call to builtin " << name);
  switch (builtin_id) {
    case Builtins::kObjectCreate: {
      if (arguments.size() >= 2) {
        ProcessHintsForObjectCreate(arguments[1]);
      } else {
        ProcessHintsForObjectCreate(Hints::SingleConstant(
            broker()->isolate()->factory()->undefined_value(), zone()));
      }
      break;
    }
    case Builtins::kPromisePrototypeCatch: {
      // For JSCallReducer::ReducePromisePrototypeCatch.
      if (speculation_mode != SpeculationMode::kDisallowSpeculation) {
        if (arguments.size() >= 1) {
          ProcessMapHintsForPromises(arguments[0]);
        }
      }
      break;
    }
    case Builtins::kPromisePrototypeFinally: {
      // For JSCallReducer::ReducePromisePrototypeFinally.
      if (speculation_mode != SpeculationMode::kDisallowSpeculation) {
        if (arguments.size() >= 1) {
          ProcessMapHintsForPromises(arguments[0]);
        }
        SharedFunctionInfoRef(
            broker(),
            broker()->isolate()->factory()->promise_catch_finally_shared_fun());
        SharedFunctionInfoRef(
            broker(),
            broker()->isolate()->factory()->promise_then_finally_shared_fun());
      }
      break;
    }
    case Builtins::kPromisePrototypeThen: {
      // For JSCallReducer::ReducePromisePrototypeThen.
      if (speculation_mode != SpeculationMode::kDisallowSpeculation) {
        if (arguments.size() >= 1) {
          ProcessMapHintsForPromises(arguments[0]);
        }
      }
      break;
    }
    case Builtins::kPromiseResolveTrampoline:
      // For JSCallReducer::ReducePromiseInternalResolve and
      // JSNativeContextSpecialization::ReduceJSResolvePromise.
      if (arguments.size() >= 1) {
        Hints const resolution_hints =
            arguments.size() >= 2
                ? arguments[1]
                : Hints::SingleConstant(
                      broker()->isolate()->factory()->undefined_value(),
                      zone());
        ProcessHintsForPromiseResolve(resolution_hints);
      }
      break;
    case Builtins::kRegExpPrototypeTest:
    case Builtins::kRegExpPrototypeTestFast:
      // For JSCallReducer::ReduceRegExpPrototypeTest.
      if (arguments.size() >= 1 &&
          speculation_mode != SpeculationMode::kDisallowSpeculation) {
        Hints const& regexp_hints = arguments[0];
        ProcessHintsForRegExpTest(regexp_hints);
      }
      break;
    case Builtins::kArrayEvery:
    case Builtins::kArrayFilter:
    case Builtins::kArrayForEach:
    case Builtins::kArrayPrototypeFind:
    case Builtins::kArrayPrototypeFindIndex:
    case Builtins::kArrayMap:
    case Builtins::kArraySome:
      if (arguments.size() >= 2 &&
          speculation_mode != SpeculationMode::kDisallowSpeculation) {
        Hints const& callback = arguments[1];
        // "Call(callbackfn, T, « kValue, k, O »)"
        HintsVector new_arguments(zone());
        new_arguments.push_back(
            arguments.size() < 3
                ? Hints::SingleConstant(
                      broker()->isolate()->factory()->undefined_value(), zone())
                : arguments[2]);                // T
        new_arguments.push_back(Hints());       // kValue
        new_arguments.push_back(Hints());       // k
        new_arguments.push_back(arguments[0]);  // O
        for (auto constant : callback.constants()) {
          ProcessCalleeForCallOrConstruct(
              constant, base::nullopt, new_arguments, speculation_mode,
              kMissingArgumentsAreUndefined, result_hints);
        }
        for (auto virtual_closure : callback.virtual_closures()) {
          ProcessCalleeForCallOrConstruct(
              Callee(virtual_closure), base::nullopt, new_arguments,
              speculation_mode, kMissingArgumentsAreUndefined, result_hints);
        }
      }
      break;
    case Builtins::kArrayReduce:
    case Builtins::kArrayReduceRight:
      if (arguments.size() >= 2 &&
          speculation_mode != SpeculationMode::kDisallowSpeculation) {
        Hints const& callback = arguments[1];
        // "Call(callbackfn, undefined, « accumulator, kValue, k, O »)"
        HintsVector new_arguments(zone());
        new_arguments.push_back(Hints::SingleConstant(
            broker()->isolate()->factory()->undefined_value(), zone()));
        new_arguments.push_back(Hints());       // accumulator
        new_arguments.push_back(Hints());       // kValue
        new_arguments.push_back(Hints());       // k
        new_arguments.push_back(arguments[0]);  // O
        for (auto constant : callback.constants()) {
          ProcessCalleeForCallOrConstruct(
              constant, base::nullopt, new_arguments, speculation_mode,
              kMissingArgumentsAreUndefined, result_hints);
        }
        for (auto virtual_closure : callback.virtual_closures()) {
          ProcessCalleeForCallOrConstruct(
              Callee(virtual_closure), base::nullopt, new_arguments,
              speculation_mode, kMissingArgumentsAreUndefined, result_hints);
        }
      }
      break;
    case Builtins::kFunctionPrototypeApply:
      if (arguments.size() >= 1) {
        // Drop hints for all arguments except the user-given receiver.
        Hints const new_receiver =
            arguments.size() >= 2
                ? arguments[1]
                : Hints::SingleConstant(
                      broker()->isolate()->factory()->undefined_value(),
                      zone());
        HintsVector new_arguments({new_receiver}, zone());
        for (auto constant : arguments[0].constants()) {
          ProcessCalleeForCallOrConstruct(
              constant, base::nullopt, new_arguments, speculation_mode,
              kMissingArgumentsAreUnknown, result_hints);
        }
        for (auto const& virtual_closure : arguments[0].virtual_closures()) {
          ProcessCalleeForCallOrConstruct(
              Callee(virtual_closure), base::nullopt, new_arguments,
              speculation_mode, kMissingArgumentsAreUnknown, result_hints);
        }
      }
      break;
    case Builtins::kPromiseConstructor:
      if (arguments.size() >= 1) {
        // "Call(executor, undefined, « resolvingFunctions.[[Resolve]],
        //                              resolvingFunctions.[[Reject]] »)"
        HintsVector new_arguments(
            {Hints::SingleConstant(
                broker()->isolate()->factory()->undefined_value(), zone())},
            zone());
        for (auto constant : arguments[0].constants()) {
          ProcessCalleeForCallOrConstruct(
              constant, base::nullopt, new_arguments,
              SpeculationMode::kDisallowSpeculation,
              kMissingArgumentsAreUnknown, result_hints);
        }
        for (auto const& virtual_closure : arguments[0].virtual_closures()) {
          ProcessCalleeForCallOrConstruct(
              Callee(virtual_closure), base::nullopt, new_arguments,
              SpeculationMode::kDisallowSpeculation,
              kMissingArgumentsAreUnknown, result_hints);
        }
      }
      SharedFunctionInfoRef(
          broker(), broker()
                        ->isolate()
                        ->factory()
                        ->promise_capability_default_reject_shared_fun());
      SharedFunctionInfoRef(
          broker(), broker()
                        ->isolate()
                        ->factory()
                        ->promise_capability_default_resolve_shared_fun());

      break;
    case Builtins::kFunctionPrototypeCall:
      if (arguments.size() >= 1) {
        HintsVector new_arguments(arguments.begin() + 1, arguments.end(),
                                  zone());
        for (auto constant : arguments[0].constants()) {
          ProcessCalleeForCallOrConstruct(constant, base::nullopt,
                                          new_arguments, speculation_mode,
                                          padding, result_hints);
        }
        for (auto const& virtual_closure : arguments[0].virtual_closures()) {
          ProcessCalleeForCallOrConstruct(
              Callee(virtual_closure), base::nullopt, new_arguments,
              speculation_mode, padding, result_hints);
        }
      }
      break;
    case Builtins::kReflectApply:
    case Builtins::kReflectConstruct:
      if (arguments.size() >= 2) {
        for (auto constant : arguments[1].constants()) {
          if (constant->IsJSFunction()) {
            JSFunctionRef(broker(), constant).Serialize();
          }
        }
      }
      break;
    case Builtins::kObjectPrototypeIsPrototypeOf:
      if (arguments.size() >= 2) {
        ProcessHintsForHasInPrototypeChain(arguments[1]);
      }
      break;
    case Builtins::kFunctionPrototypeHasInstance:
      // For JSCallReducer::ReduceFunctionPrototypeHasInstance.
      if (arguments.size() >= 2) {
        ProcessHintsForOrdinaryHasInstance(arguments[0], arguments[1]);
      }
      break;
    case Builtins::kFastFunctionPrototypeBind:
      if (arguments.size() >= 1 &&
          speculation_mode != SpeculationMode::kDisallowSpeculation) {
        Hints const& bound_target = arguments[0];
        ProcessHintsForFunctionBind(bound_target);
        HintsVector new_arguments(arguments.begin() + 1, arguments.end(),
                                  zone());
        result_hints->AddVirtualBoundFunction(
            VirtualBoundFunction(bound_target, new_arguments), zone(),
            broker());
      }
      break;
    case Builtins::kObjectGetPrototypeOf:
    case Builtins::kReflectGetPrototypeOf:
      if (arguments.size() >= 2) {
        ProcessHintsForObjectGetPrototype(arguments[1]);
      } else {
        Hints const undefined_hint = Hints::SingleConstant(
            broker()->isolate()->factory()->undefined_value(), zone());
        ProcessHintsForObjectGetPrototype(undefined_hint);
      }
      break;
    case Builtins::kObjectPrototypeGetProto:
      if (arguments.size() >= 1) {
        ProcessHintsForObjectGetPrototype(arguments[0]);
      }
      break;
    case Builtins::kMapIteratorPrototypeNext:
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kOrderedHashTableHealIndex));
      ObjectRef(broker(),
                broker()->isolate()->factory()->empty_ordered_hash_map());
      break;
    case Builtins::kSetIteratorPrototypeNext:
      ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
                              Builtins::kOrderedHashTableHealIndex));
      ObjectRef(broker(),
                broker()->isolate()->factory()->empty_ordered_hash_set());
      break;
    default:
      break;
  }
}

void SerializerForBackgroundCompilation::ProcessHintsForOrdinaryHasInstance(
    Hints const& constructor_hints, Hints const& instance_hints) {
  bool walk_prototypes = false;
  for (Handle<Object> constructor : constructor_hints.constants()) {
    // For JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance.
    if (constructor->IsHeapObject()) {
      ProcessConstantForOrdinaryHasInstance(
          HeapObjectRef(broker(), constructor), &walk_prototypes);
    }
  }
  // For JSNativeContextSpecialization::ReduceJSHasInPrototypeChain.
  if (walk_prototypes) ProcessHintsForHasInPrototypeChain(instance_hints);
}

void SerializerForBackgroundCompilation::ProcessHintsForHasInPrototypeChain(
    Hints const& instance_hints) {
  auto processMap = [&](Handle<Map> map_handle) {
    MapRef map(broker(), map_handle);
    while (map.IsJSObjectMap()) {
      map.SerializePrototype();
      map = map.prototype().map();
    }
  };

  for (auto hint : instance_hints.constants()) {
    if (!hint->IsHeapObject()) continue;
    Handle<HeapObject> object(Handle<HeapObject>::cast(hint));
    processMap(handle(object->map(), broker()->isolate()));
  }
  for (auto map_hint : instance_hints.maps()) {
    processMap(map_hint);
  }
}

void SerializerForBackgroundCompilation::ProcessHintsForPromiseResolve(
    Hints const& resolution_hints) {
  auto processMap = [&](Handle<Map> map) {
    broker()->GetPropertyAccessInfo(
        MapRef(broker(), map),
        NameRef(broker(), broker()->isolate()->factory()->then_string()),
        AccessMode::kLoad, dependencies(),
        SerializationPolicy::kSerializeIfNeeded);
  };

  for (auto hint : resolution_hints.constants()) {
    if (!hint->IsHeapObject()) continue;
    Handle<HeapObject> resolution(Handle<HeapObject>::cast(hint));
    processMap(handle(resolution->map(), broker()->isolate()));
  }
  for (auto map_hint : resolution_hints.maps()) {
    processMap(map_hint);
  }
}

void SerializerForBackgroundCompilation::ProcessMapHintsForPromises(
    Hints const& receiver_hints) {
  // We need to serialize the prototypes on each receiver map.
  for (auto constant : receiver_hints.constants()) {
    if (!constant->IsJSPromise()) continue;
    Handle<Map> map(Handle<HeapObject>::cast(constant)->map(),
                    broker()->isolate());
    MapRef(broker(), map).SerializePrototype();
  }
  for (auto map : receiver_hints.maps()) {
    if (!map->IsJSPromiseMap()) continue;
    MapRef(broker(), map).SerializePrototype();
  }
}

PropertyAccessInfo SerializerForBackgroundCompilation::ProcessMapForRegExpTest(
    MapRef map) {
  PropertyAccessInfo ai_exec = broker()->GetPropertyAccessInfo(
      map, NameRef(broker(), broker()->isolate()->factory()->exec_string()),
      AccessMode::kLoad, dependencies(),
      SerializationPolicy::kSerializeIfNeeded);

  Handle<JSObject> holder;
  if (ai_exec.IsDataConstant() && ai_exec.holder().ToHandle(&holder)) {
    // The property is on the prototype chain.
    JSObjectRef holder_ref(broker(), holder);
    holder_ref.GetOwnDataProperty(ai_exec.field_representation(),
                                  ai_exec.field_index(),
                                  SerializationPolicy::kSerializeIfNeeded);
  }
  return ai_exec;
}

void SerializerForBackgroundCompilation::ProcessHintsForRegExpTest(
    Hints const& regexp_hints) {
  for (auto hint : regexp_hints.constants()) {
    if (!hint->IsJSRegExp()) continue;
    Handle<JSRegExp> regexp(Handle<JSRegExp>::cast(hint));
    Handle<Map> regexp_map(regexp->map(), broker()->isolate());
    PropertyAccessInfo ai_exec =
        ProcessMapForRegExpTest(MapRef(broker(), regexp_map));
    Handle<JSObject> holder;
    if (ai_exec.IsDataConstant() && !ai_exec.holder().ToHandle(&holder)) {
      // The property is on the object itself.
      JSObjectRef holder_ref(broker(), regexp);
      holder_ref.GetOwnDataProperty(ai_exec.field_representation(),
                                    ai_exec.field_index(),
                                    SerializationPolicy::kSerializeIfNeeded);
    }
  }

  for (auto map : regexp_hints.maps()) {
    if (!map->IsJSRegExpMap()) continue;
    ProcessMapForRegExpTest(MapRef(broker(), map));
  }
}

namespace {
void ProcessMapForFunctionBind(MapRef map) {
  map.SerializePrototype();
  int min_nof_descriptors = std::max({JSFunction::kLengthDescriptorIndex,
                                      JSFunction::kNameDescriptorIndex}) +
                            1;
  if (map.NumberOfOwnDescriptors() >= min_nof_descriptors) {
    map.SerializeOwnDescriptor(
        InternalIndex(JSFunction::kLengthDescriptorIndex));
    map.SerializeOwnDescriptor(InternalIndex(JSFunction::kNameDescriptorIndex));
  }
}
}  // namespace

void SerializerForBackgroundCompilation::ProcessHintsForFunctionBind(
    Hints const& receiver_hints) {
  for (auto constant : receiver_hints.constants()) {
    if (!constant->IsJSFunction()) continue;
    JSFunctionRef function(broker(), constant);
    function.Serialize();
    ProcessMapForFunctionBind(function.map());
  }

  for (auto map : receiver_hints.maps()) {
    if (!map->IsJSFunctionMap()) continue;
    MapRef map_ref(broker(), map);
    ProcessMapForFunctionBind(map_ref);
  }
}

void SerializerForBackgroundCompilation::ProcessHintsForObjectGetPrototype(
    Hints const& object_hints) {
  for (auto constant : object_hints.constants()) {
    if (!constant->IsHeapObject()) continue;
    HeapObjectRef object(broker(), constant);
    object.map().SerializePrototype();
  }

  for (auto map : object_hints.maps()) {
    MapRef map_ref(broker(), map);
    map_ref.SerializePrototype();
  }
}

void SerializerForBackgroundCompilation::ContributeToJumpTargetEnvironment(
    int target_offset) {
  auto it = jump_target_environments_.find(target_offset);
  if (it == jump_target_environments_.end()) {
    jump_target_environments_[target_offset] =
        zone()->New<Environment>(*environment());
  } else {
    it->second->Merge(environment(), zone(), broker());
  }
}

void SerializerForBackgroundCompilation::IncorporateJumpTargetEnvironment(
    int target_offset) {
  auto it = jump_target_environments_.find(target_offset);
  if (it != jump_target_environments_.end()) {
    environment()->Merge(it->second, zone(), broker());
    jump_target_environments_.erase(it);
  }
}

void SerializerForBackgroundCompilation::ProcessJump(
    interpreter::BytecodeArrayIterator* iterator) {
  int jump_target = iterator->GetJumpTargetOffset();
  if (iterator->current_offset() < jump_target) {
    ContributeToJumpTargetEnvironment(jump_target);
  }
}

void SerializerForBackgroundCompilation::VisitReturn(
    BytecodeArrayIterator* iterator) {
  return_value_hints().Add(environment()->accumulator_hints(), zone(),
                           broker());
  environment()->Kill();
}

void SerializerForBackgroundCompilation::VisitSwitchOnSmiNoFeedback(
    interpreter::BytecodeArrayIterator* iterator) {
  interpreter::JumpTableTargetOffsets targets =
      iterator->GetJumpTableTargetOffsets();
  for (const auto& target : targets) {
    ContributeToJumpTargetEnvironment(target.target_offset);
  }
}

void SerializerForBackgroundCompilation::VisitSwitchOnGeneratorState(
    interpreter::BytecodeArrayIterator* iterator) {
  for (const auto& target : GetBytecodeAnalysis().resume_jump_targets()) {
    ContributeToJumpTargetEnvironment(target.target_offset());
  }
}

void SerializerForBackgroundCompilation::VisitConstruct(
    BytecodeArrayIterator* iterator) {
  Hints& new_target = environment()->accumulator_hints();
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  interpreter::Register first_reg = iterator->GetRegisterOperand(1);
  size_t reg_count = iterator->GetRegisterCountOperand(2);
  FeedbackSlot slot = iterator->GetSlotOperand(3);

  HintsVector args = PrepareArgumentsHints(first_reg, reg_count);

  ProcessCallOrConstruct(callee, new_target, &args, slot,
                         kMissingArgumentsAreUndefined);
}

void SerializerForBackgroundCompilation::VisitConstructWithSpread(
    BytecodeArrayIterator* iterator) {
  Hints const& new_target = environment()->accumulator_hints();
  Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
  interpreter::Register first_reg = iterator->GetRegisterOperand(1);
  size_t reg_count = iterator->GetRegisterCountOperand(2);
  FeedbackSlot slot = iterator->GetSlotOperand(3);

  DCHECK_GT(reg_count, 0);
  reg_count--;  // Pop the spread element.
  HintsVector args = PrepareArgumentsHints(first_reg, reg_count);

  ProcessCallOrConstruct(callee, new_target, &args, slot,
                         kMissingArgumentsAreUnknown);
}

void SerializerForBackgroundCompilation::ProcessGlobalAccess(FeedbackSlot slot,
                                                             bool is_load) {
  if (slot.IsInvalid() || feedback_vector().is_null()) return;
  FeedbackSource source(feedback_vector(), slot);
  ProcessedFeedback const& feedback =
      broker()->ProcessFeedbackForGlobalAccess(source);

  if (is_load) {
    Hints result_hints;
    if (feedback.kind() == ProcessedFeedback::kGlobalAccess) {
      // We may be able to contribute to accumulator constant hints.
      base::Optional<ObjectRef> value =
          feedback.AsGlobalAccess().GetConstantHint();
      if (value.has_value()) {
        result_hints.AddConstant(value->object(), zone(), broker());
      }
    } else {
      DCHECK(feedback.IsInsufficient());
    }
    environment()->accumulator_hints() = result_hints;
  }
}

void SerializerForBackgroundCompilation::VisitLdaGlobal(
    BytecodeArrayIterator* iterator) {
  NameRef(broker(),
          iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  ProcessGlobalAccess(slot, true);
}

void SerializerForBackgroundCompilation::VisitLdaGlobalInsideTypeof(
    BytecodeArrayIterator* iterator) {
  VisitLdaGlobal(iterator);
}

void SerializerForBackgroundCompilation::VisitLdaLookupSlot(
    BytecodeArrayIterator* iterator) {
  ObjectRef(broker(),
            iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  environment()->accumulator_hints() = Hints();
}

void SerializerForBackgroundCompilation::VisitLdaLookupSlotInsideTypeof(
    BytecodeArrayIterator* iterator) {
  ObjectRef(broker(),
            iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  environment()->accumulator_hints() = Hints();
}

void SerializerForBackgroundCompilation::ProcessCheckContextExtensions(
    int depth) {
  // for BytecodeGraphBuilder::CheckContextExtensions.
  Hints const& context_hints = environment()->current_context_hints();
  for (int i = 0; i < depth; i++) {
    ProcessContextAccess(context_hints, Context::EXTENSION_INDEX, i,
                         kSerializeSlot);
  }
  SharedFunctionInfoRef shared(broker(), function().shared());
  shared.SerializeScopeInfoChain();
}

void SerializerForBackgroundCompilation::ProcessLdaLookupGlobalSlot(
    BytecodeArrayIterator* iterator) {
  ProcessCheckContextExtensions(iterator->GetUnsignedImmediateOperand(2));
  // TODO(neis): BytecodeGraphBilder may insert a JSLoadGlobal.
  VisitLdaGlobal(iterator);
}

void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlot(
    BytecodeArrayIterator* iterator) {
  ProcessLdaLookupGlobalSlot(iterator);
}

void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlotInsideTypeof(
    BytecodeArrayIterator* iterator) {
  ProcessLdaLookupGlobalSlot(iterator);
}

void SerializerForBackgroundCompilation::VisitStaGlobal(
    BytecodeArrayIterator* iterator) {
  NameRef(broker(),
          iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  ProcessGlobalAccess(slot, false);
}

void SerializerForBackgroundCompilation::ProcessLdaLookupContextSlot(
    BytecodeArrayIterator* iterator) {
  const int slot_index = iterator->GetIndexOperand(1);
  const int depth = iterator->GetUnsignedImmediateOperand(2);
  NameRef(broker(),
          iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  ProcessCheckContextExtensions(depth);
  environment()->accumulator_hints() = Hints();
  ProcessContextAccess(environment()->current_context_hints(), slot_index,
                       depth, kIgnoreSlot);
}

void SerializerForBackgroundCompilation::VisitLdaLookupContextSlot(
    BytecodeArrayIterator* iterator) {
  ProcessLdaLookupContextSlot(iterator);
}

void SerializerForBackgroundCompilation::VisitLdaLookupContextSlotInsideTypeof(
    BytecodeArrayIterator* iterator) {
  ProcessLdaLookupContextSlot(iterator);
}

// TODO(neis): Avoid duplicating this.
namespace {
template <class MapContainer>
MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) {
  MapHandles result;
  for (Handle<Map> map : maps) {
    if (Map::TryUpdate(isolate, map).ToHandle(&map) &&
        !map->is_abandoned_prototype_map()) {
      DCHECK(!map->is_deprecated());
      result.push_back(map);
    }
  }
  return result;
}
}  // namespace

void SerializerForBackgroundCompilation::ProcessCompareOperation(
    FeedbackSlot slot) {
  if (slot.IsInvalid() || feedback_vector().is_null()) return;
  FeedbackSource source(function().feedback_vector(), slot);
  ProcessedFeedback const& feedback =
      broker()->ProcessFeedbackForCompareOperation(source);
  if (BailoutOnUninitialized(feedback)) return;
  environment()->accumulator_hints() = Hints();
}

void SerializerForBackgroundCompilation::ProcessForIn(FeedbackSlot slot) {
  if (slot.IsInvalid() || feedback_vector().is_null()) return;
  FeedbackSource source(feedback_vector(), slot);
  ProcessedFeedback const& feedback = broker()->ProcessFeedbackForForIn(source);
  if (BailoutOnUninitialized(feedback)) return;
  environment()->accumulator_hints() = Hints();
}

void SerializerForBackgroundCompilation::ProcessUnaryOrBinaryOperation(
    FeedbackSlot slot, bool honor_bailout_on_uninitialized) {
  if (slot.IsInvalid() || feedback_vector().is_null()) return;
  FeedbackSource source(feedback_vector(), slot);
  // Internally V8 uses binary op feedback also for unary ops.
  ProcessedFeedback const& feedback =
      broker()->ProcessFeedbackForBinaryOperation(source);
  if (honor_bailout_on_uninitialized && BailoutOnUninitialized(feedback)) {
    return;
  }
  environment()->accumulator_hints() = Hints();
}

PropertyAccessInfo
SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
    Hints* receiver, base::Optional<MapRef> receiver_map,
    MapRef lookup_start_object_map, NameRef const& name, AccessMode access_mode,
    base::Optional<JSObjectRef> concrete_receiver, Hints* result_hints) {
  DCHECK_IMPLIES(concrete_receiver.has_value(), receiver_map.has_value());

  // For JSNativeContextSpecialization::InferRootMap
  lookup_start_object_map.SerializeRootMap();

  // For JSNativeContextSpecialization::ReduceNamedAccess.
  JSGlobalProxyRef global_proxy =
      broker()->target_native_context().global_proxy_object();
  JSGlobalObjectRef global_object =
      broker()->target_native_context().global_object();
  if (lookup_start_object_map.equals(global_proxy.map())) {
    base::Optional<PropertyCellRef> cell = global_object.GetPropertyCell(
        name, SerializationPolicy::kSerializeIfNeeded);
    if (access_mode == AccessMode::kLoad && cell.has_value()) {
      result_hints->AddConstant(cell->value().object(), zone(), broker());
    }
  }

  PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo(
      lookup_start_object_map, name, access_mode, dependencies(),
      SerializationPolicy::kSerializeIfNeeded);

  // For JSNativeContextSpecialization::InlinePropertySetterCall
  // and InlinePropertyGetterCall.
  if (access_info.IsAccessorConstant() && !access_info.constant().is_null()) {
    if (access_info.constant()->IsJSFunction()) {
      JSFunctionRef function(broker(), access_info.constant());

      if (receiver_map.has_value()) {
        // For JSCallReducer and JSInlining(Heuristic).
        HintsVector arguments(
            {Hints::SingleMap(receiver_map->object(), zone())}, zone());
        // In the case of a setter any added result hints won't make sense, but
        // they will be ignored anyways by Process*PropertyAccess due to the
        // access mode not being kLoad.
        ProcessCalleeForCallOrConstruct(
            function.object(), base::nullopt, arguments,
            SpeculationMode::kDisallowSpeculation,
            kMissingArgumentsAreUndefined, result_hints);

        // For JSCallReducer::ReduceCallApiFunction.
        Handle<SharedFunctionInfo> sfi = function.shared().object();
        if (sfi->IsApiFunction()) {
          FunctionTemplateInfoRef fti_ref(
              broker(), handle(sfi->get_api_func_data(), broker()->isolate()));
          if (fti_ref.has_call_code()) {
            fti_ref.SerializeCallCode();
            ProcessReceiverMapForApiCall(fti_ref, receiver_map->object());
          }
        }
      }
    } else if (access_info.constant()->IsJSBoundFunction()) {
      JSBoundFunctionRef function(broker(), access_info.constant());

      // For JSCallReducer::ReduceJSCall.
      function.Serialize();
    } else {
      FunctionTemplateInfoRef fti(broker(), access_info.constant());
      if (fti.has_call_code()) fti.SerializeCallCode();
    }
  } else if (access_info.IsModuleExport()) {
    // For JSNativeContextSpecialization::BuildPropertyLoad
    DCHECK(!access_info.constant().is_null());
    CellRef(broker(), access_info.constant());
  }

  switch (access_mode) {
    case AccessMode::kLoad:
      // For PropertyAccessBuilder::TryBuildLoadConstantDataField
      if (access_info.IsDataConstant()) {
        base::Optional<JSObjectRef> holder;
        Handle<JSObject> prototype;
        if (access_info.holder().ToHandle(&prototype)) {
          holder = JSObjectRef(broker(), prototype);
        } else {
          CHECK_IMPLIES(concrete_receiver.has_value(),
                        concrete_receiver->map().equals(*receiver_map));
          holder = concrete_receiver;
        }

        if (holder.has_value()) {
          base::Optional<ObjectRef> constant(holder->GetOwnDataProperty(
              access_info.field_representation(), access_info.field_index(),
              SerializationPolicy::kSerializeIfNeeded));
          if (constant.has_value()) {
            result_hints->AddConstant(constant->object(), zone(), broker());
          }
        }
      }
      break;
    case AccessMode::kStore:
    case AccessMode::kStoreInLiteral:
      // For MapInference (StoreField case).
      if (access_info.IsDataField() || access_info.IsDataConstant()) {
        Handle<Map> transition_map;
        if (access_info.transition_map().ToHandle(&transition_map)) {
          MapRef map_ref(broker(), transition_map);
          TRACE_BROKER(broker(), "Propagating transition map "
                                     << map_ref << " to receiver hints.");
          receiver->AddMap(transition_map, zone(), broker_, false);
        }
      }
      break;
    case AccessMode::kHas:
      break;
  }

  return access_info;
}

void SerializerForBackgroundCompilation::ProcessMinimorphicPropertyAccess(
    MinimorphicLoadPropertyAccessFeedback const& feedback,
    FeedbackSource const& source) {
  broker()->GetPropertyAccessInfo(feedback, source,
                                  SerializationPolicy::kSerializeIfNeeded);
}

void SerializerForBackgroundCompilation::VisitLdaKeyedProperty(
    BytecodeArrayIterator* iterator) {
  Hints const& key = environment()->accumulator_hints();
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(0));
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kLoad, true);
}

void SerializerForBackgroundCompilation::ProcessKeyedPropertyAccess(
    Hints* receiver, Hints const& key, FeedbackSlot slot,
    AccessMode access_mode, bool honor_bailout_on_uninitialized) {
  if (slot.IsInvalid() || feedback_vector().is_null()) return;
  FeedbackSource source(feedback_vector(), slot);
  ProcessedFeedback const& feedback =
      broker()->ProcessFeedbackForPropertyAccess(source, access_mode,
                                                 base::nullopt);
  if (honor_bailout_on_uninitialized && BailoutOnUninitialized(feedback)) {
    return;
  }

  Hints new_accumulator_hints;
  switch (feedback.kind()) {
    case ProcessedFeedback::kElementAccess:
      ProcessElementAccess(*receiver, key, feedback.AsElementAccess(),
                           access_mode);
      break;
    case ProcessedFeedback::kNamedAccess:
      ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode,
                         &new_accumulator_hints);
      break;
    case ProcessedFeedback::kInsufficient:
      break;
    default:
      UNREACHABLE();
  }

  if (access_mode == AccessMode::kLoad) {
    environment()->accumulator_hints() = new_accumulator_hints;
  }
}

void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess(
    Hints* receiver, NameRef const& name, FeedbackSlot slot,
    AccessMode access_mode) {
  if (slot.IsInvalid() || feedback_vector().is_null()) return;
  FeedbackSource source(feedback_vector(), slot);
  ProcessedFeedback const& feedback =
      broker()->ProcessFeedbackForPropertyAccess(source, access_mode, name);
  if (BailoutOnUninitialized(feedback)) return;

  Hints new_accumulator_hints;
  switch (feedback.kind()) {
    case ProcessedFeedback::kNamedAccess:
      DCHECK(name.equals(feedback.AsNamedAccess().name()));
      ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode,
                         &new_accumulator_hints);
      break;
    case ProcessedFeedback::kMinimorphicPropertyAccess:
      DCHECK(name.equals(feedback.AsMinimorphicPropertyAccess().name()));
      ProcessMinimorphicPropertyAccess(feedback.AsMinimorphicPropertyAccess(),
                                       source);
      break;
    case ProcessedFeedback::kInsufficient:
      break;
    default:
      UNREACHABLE();
  }

  if (access_mode == AccessMode::kLoad) {
    environment()->accumulator_hints() = new_accumulator_hints;
  }
}

void SerializerForBackgroundCompilation::ProcessNamedSuperPropertyAccess(
    Hints* receiver, NameRef const& name, FeedbackSlot slot,
    AccessMode access_mode) {
  if (slot.IsInvalid() || feedback_vector().is_null()) return;
  FeedbackSource source(feedback_vector(), slot);
  ProcessedFeedback const& feedback =
      broker()->ProcessFeedbackForPropertyAccess(source, access_mode, name);
  if (BailoutOnUninitialized(feedback)) return;

  Hints new_accumulator_hints;
  switch (feedback.kind()) {
    case ProcessedFeedback::kNamedAccess:
      DCHECK(name.equals(feedback.AsNamedAccess().name()));
      ProcessNamedSuperAccess(receiver, feedback.AsNamedAccess(), access_mode,
                              &new_accumulator_hints);
      break;
    case ProcessedFeedback::kMinimorphicPropertyAccess:
      DCHECK(name.equals(feedback.AsMinimorphicPropertyAccess().name()));
      ProcessMinimorphicPropertyAccess(feedback.AsMinimorphicPropertyAccess(),
                                       source);
      break;
    case ProcessedFeedback::kInsufficient:
      break;
    default:
      UNREACHABLE();
  }

  if (access_mode == AccessMode::kLoad) {
    environment()->accumulator_hints() = new_accumulator_hints;
  }
}

void SerializerForBackgroundCompilation::ProcessNamedAccess(
    Hints* receiver, NamedAccessFeedback const& feedback,
    AccessMode access_mode, Hints* result_hints) {
  for (Handle<Map> map : feedback.maps()) {
    MapRef map_ref(broker(), map);
    TRACE_BROKER(broker(), "Propagating feedback map "
                               << map_ref << " to receiver hints.");
    receiver->AddMap(map, zone(), broker_, false);
  }

  for (Handle<Map> map :
       GetRelevantReceiverMaps(broker()->isolate(), receiver->maps())) {
    MapRef map_ref(broker(), map);
    ProcessMapForNamedPropertyAccess(receiver, map_ref, map_ref,
                                     feedback.name(), access_mode,
                                     base::nullopt, result_hints);
  }

  for (Handle<Object> hint : receiver->constants()) {
    ObjectRef object(broker(), hint);
    if (access_mode == AccessMode::kLoad && object.IsJSObject()) {
      MapRef map_ref = object.AsJSObject().map();
      ProcessMapForNamedPropertyAccess(receiver, map_ref, map_ref,
                                       feedback.name(), access_mode,
                                       object.AsJSObject(), result_hints);
    }
    // For JSNativeContextSpecialization::ReduceJSLoadNamed.
    if (access_mode == AccessMode::kLoad && object.IsJSFunction() &&
        feedback.name().equals(ObjectRef(
            broker(), broker()->isolate()->factory()->prototype_string()))) {
      JSFunctionRef function = object.AsJSFunction();
      function.Serialize();
      if (result_hints != nullptr && function.has_prototype()) {
        result_hints->AddConstant(function.prototype().object(), zone(),
                                  broker());
      }
    }
    // TODO(neis): Also record accumulator hint for string.length and maybe
    // more?
  }
}

void SerializerForBackgroundCompilation::ProcessNamedSuperAccess(
    Hints* receiver, NamedAccessFeedback const& feedback,
    AccessMode access_mode, Hints* result_hints) {
  MapHandles receiver_maps =
      GetRelevantReceiverMaps(broker()->isolate(), receiver->maps());
  for (Handle<Map> receiver_map : receiver_maps) {
    MapRef receiver_map_ref(broker(), receiver_map);
    for (Handle<Map> feedback_map : feedback.maps()) {
      MapRef feedback_map_ref(broker(), feedback_map);
      ProcessMapForNamedPropertyAccess(
          receiver, receiver_map_ref, feedback_map_ref, feedback.name(),
          access_mode, base::nullopt, result_hints);
    }
  }
  if (receiver_maps.empty()) {
    for (Handle<Map> feedback_map : feedback.maps()) {
      MapRef feedback_map_ref(broker(), feedback_map);
      ProcessMapForNamedPropertyAccess(
          receiver, base::nullopt, feedback_map_ref, feedback.name(),
          access_mode, base::nullopt, result_hints);
    }
  }
}

void SerializerForBackgroundCompilation::ProcessElementAccess(
    Hints const& receiver, Hints const& key,
    ElementAccessFeedback const& feedback, AccessMode access_mode) {
  for (auto const& group : feedback.transition_groups()) {
    for (Handle<Map> map_handle : group) {
      MapRef map(broker(), map_handle);
      switch (access_mode) {
        case AccessMode::kHas:
        case AccessMode::kLoad:
          map.SerializeForElementLoad();
          break;
        case AccessMode::kStore:
          map.SerializeForElementStore();
          break;
        case AccessMode::kStoreInLiteral:
          // This operation is fairly local and simple, nothing to serialize.
          break;
      }
    }
  }

  for (Handle<Object> hint : receiver.constants()) {
    ObjectRef receiver_ref(broker(), hint);

    // For JSNativeContextSpecialization::InferRootMap
    if (receiver_ref.IsHeapObject()) {
      receiver_ref.AsHeapObject().map().SerializeRootMap();
    }

    // For JSNativeContextSpecialization::ReduceElementAccess.
    if (receiver_ref.IsJSTypedArray()) {
      receiver_ref.AsJSTypedArray().Serialize();
    }

    // For JSNativeContextSpecialization::ReduceElementLoadFromHeapConstant.
    if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
      for (Handle<Object> hint : key.constants()) {
        ObjectRef key_ref(broker(), hint);
        // TODO(neis): Do this for integer-HeapNumbers too?
        if (key_ref.IsSmi() && key_ref.AsSmi() >= 0) {
          base::Optional<ObjectRef> element =
              receiver_ref.GetOwnConstantElement(
                  key_ref.AsSmi(), SerializationPolicy::kSerializeIfNeeded);
          if (!element.has_value() && receiver_ref.IsJSArray()) {
            // We didn't find a constant element, but if the receiver is a
            // cow-array we can exploit the fact that any future write to the
            // element will replace the whole elements storage.
            receiver_ref.AsJSArray().GetOwnCowElement(
                key_ref.AsSmi(), SerializationPolicy::kSerializeIfNeeded);
          }
        }
      }
    }
  }

  // For JSNativeContextSpecialization::InferRootMap
  for (Handle<Map> map : receiver.maps()) {
    MapRef map_ref(broker(), map);
    map_ref.SerializeRootMap();
  }
}

void SerializerForBackgroundCompilation::VisitLdaNamedProperty(
    BytecodeArrayIterator* iterator) {
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(0));
  NameRef name(broker(),
               iterator->GetConstantForIndexOperand(1, broker()->isolate()));
  FeedbackSlot slot = iterator->GetSlotOperand(2);
  ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kLoad);
}

void SerializerForBackgroundCompilation::VisitLdaNamedPropertyFromSuper(
    BytecodeArrayIterator* iterator) {
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(0));
  NameRef name(broker(),
               iterator->GetConstantForIndexOperand(1, broker()->isolate()));
  FeedbackSlot slot = iterator->GetSlotOperand(2);
  ProcessNamedSuperPropertyAccess(receiver, name, slot, AccessMode::kLoad);
}

// TODO(neis): Do feedback-independent serialization also for *NoFeedback
// bytecodes.
void SerializerForBackgroundCompilation::VisitLdaNamedPropertyNoFeedback(
    BytecodeArrayIterator* iterator) {
  NameRef(broker(),
          iterator->GetConstantForIndexOperand(1, broker()->isolate()));
}

void SerializerForBackgroundCompilation::VisitStaNamedProperty(
    BytecodeArrayIterator* iterator) {
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(0));
  NameRef name(broker(),
               iterator->GetConstantForIndexOperand(1, broker()->isolate()));
  FeedbackSlot slot = iterator->GetSlotOperand(2);
  ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kStore);
}

void SerializerForBackgroundCompilation::VisitStaNamedPropertyNoFeedback(
    BytecodeArrayIterator* iterator) {
  NameRef(broker(),
          iterator->GetConstantForIndexOperand(1, broker()->isolate()));
}

void SerializerForBackgroundCompilation::VisitStaNamedOwnProperty(
    BytecodeArrayIterator* iterator) {
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(0));
  NameRef name(broker(),
               iterator->GetConstantForIndexOperand(1, broker()->isolate()));
  FeedbackSlot slot = iterator->GetSlotOperand(2);
  ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kStoreInLiteral);
}

void SerializerForBackgroundCompilation::VisitTestIn(
    BytecodeArrayIterator* iterator) {
  Hints* receiver = &environment()->accumulator_hints();
  Hints const& key = register_hints(iterator->GetRegisterOperand(0));
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kHas, false);
}

// For JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance.
void SerializerForBackgroundCompilation::ProcessConstantForOrdinaryHasInstance(
    HeapObjectRef const& constructor, bool* walk_prototypes) {
  if (constructor.IsJSBoundFunction()) {
    constructor.AsJSBoundFunction().Serialize();
    ProcessConstantForInstanceOf(
        constructor.AsJSBoundFunction().bound_target_function(),
        walk_prototypes);
  } else if (constructor.IsJSFunction()) {
    constructor.AsJSFunction().Serialize();
    *walk_prototypes =
        *walk_prototypes ||
        (constructor.map().has_prototype_slot() &&
         constructor.AsJSFunction().has_prototype() &&
         !constructor.AsJSFunction().PrototypeRequiresRuntimeLookup());
  }
}

void SerializerForBackgroundCompilation::ProcessConstantForInstanceOf(
    ObjectRef const& constructor, bool* walk_prototypes) {
  if (!constructor.IsHeapObject()) return;
  HeapObjectRef constructor_heap_object = constructor.AsHeapObject();

  PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo(
      constructor_heap_object.map(),
      NameRef(broker(), broker()->isolate()->factory()->has_instance_symbol()),
      AccessMode::kLoad, dependencies(),
      SerializationPolicy::kSerializeIfNeeded);

  if (access_info.IsNotFound()) {
    ProcessConstantForOrdinaryHasInstance(constructor_heap_object,
                                          walk_prototypes);
  } else if (access_info.IsDataConstant()) {
    Handle<JSObject> holder;
    bool found_on_proto = access_info.holder().ToHandle(&holder);
    JSObjectRef holder_ref = found_on_proto ? JSObjectRef(broker(), holder)
                                            : constructor.AsJSObject();
    base::Optional<ObjectRef> constant = holder_ref.GetOwnDataProperty(
        access_info.field_representation(), access_info.field_index(),
        SerializationPolicy::kSerializeIfNeeded);
    CHECK(constant.has_value());
    if (constant->IsJSFunction()) {
      JSFunctionRef function = constant->AsJSFunction();
      function.Serialize();
      if (function.shared().HasBuiltinId() &&
          function.shared().builtin_id() ==
              Builtins::kFunctionPrototypeHasInstance) {
        // For JSCallReducer::ReduceFunctionPrototypeHasInstance.
        ProcessConstantForOrdinaryHasInstance(constructor_heap_object,
                                              walk_prototypes);
      }
    }
  }
}

void SerializerForBackgroundCompilation::VisitTestInstanceOf(
    BytecodeArrayIterator* iterator) {
  Hints const& lhs = register_hints(iterator->GetRegisterOperand(0));
  Hints rhs = environment()->accumulator_hints();
  FeedbackSlot slot = iterator->GetSlotOperand(1);

  if (slot.IsInvalid() || feedback_vector().is_null()) return;
  FeedbackSource source(feedback_vector(), slot);
  ProcessedFeedback const& feedback =
      broker()->ProcessFeedbackForInstanceOf(source);

  // Incorporate feedback (about rhs) into hints copy to simplify processing.
  // TODO(neis): Propagate into original hints?
  if (!feedback.IsInsufficient()) {
    InstanceOfFeedback const& rhs_feedback = feedback.AsInstanceOf();
    if (rhs_feedback.value().has_value()) {
      rhs = rhs.Copy(zone());
      Handle<JSObject> constructor = rhs_feedback.value()->object();
      rhs.AddConstant(constructor, zone(), broker());
    }
  }

  bool walk_prototypes = false;
  for (Handle<Object> constant : rhs.constants()) {
    ProcessConstantForInstanceOf(ObjectRef(broker(), constant),
                                 &walk_prototypes);
  }
  if (walk_prototypes) ProcessHintsForHasInPrototypeChain(lhs);

  environment()->accumulator_hints() = Hints();
}

void SerializerForBackgroundCompilation::VisitToNumeric(
    BytecodeArrayIterator* iterator) {
  FeedbackSlot slot = iterator->GetSlotOperand(0);
  ProcessUnaryOrBinaryOperation(slot, false);
}

void SerializerForBackgroundCompilation::VisitToNumber(
    BytecodeArrayIterator* iterator) {
  FeedbackSlot slot = iterator->GetSlotOperand(0);
  ProcessUnaryOrBinaryOperation(slot, false);
}

void SerializerForBackgroundCompilation::VisitThrowReferenceErrorIfHole(
    BytecodeArrayIterator* iterator) {
  ObjectRef(broker(),
            iterator->GetConstantForIndexOperand(0, broker()->isolate()));
}

void SerializerForBackgroundCompilation::VisitStaKeyedProperty(
    BytecodeArrayIterator* iterator) {
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(0));
  Hints const& key = register_hints(iterator->GetRegisterOperand(1));
  FeedbackSlot slot = iterator->GetSlotOperand(2);
  ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStore, true);
}

void SerializerForBackgroundCompilation::VisitStaInArrayLiteral(
    BytecodeArrayIterator* iterator) {
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(0));
  Hints const& key = register_hints(iterator->GetRegisterOperand(1));
  FeedbackSlot slot = iterator->GetSlotOperand(2);
  ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStoreInLiteral,
                             true);
}

void SerializerForBackgroundCompilation::VisitStaDataPropertyInLiteral(
    BytecodeArrayIterator* iterator) {
  Hints* receiver = &register_hints(iterator->GetRegisterOperand(0));
  Hints const& key = register_hints(iterator->GetRegisterOperand(1));
  FeedbackSlot slot = iterator->GetSlotOperand(3);
  ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStoreInLiteral,
                             false);
}

#define DEFINE_CLEAR_ACCUMULATOR(name, ...)             \
  void SerializerForBackgroundCompilation::Visit##name( \
      BytecodeArrayIterator* iterator) {                \
    environment()->accumulator_hints() = Hints();       \
  }
CLEAR_ACCUMULATOR_LIST(DEFINE_CLEAR_ACCUMULATOR)
#undef DEFINE_CLEAR_ACCUMULATOR

#define DEFINE_CONDITIONAL_JUMP(name, ...)              \
  void SerializerForBackgroundCompilation::Visit##name( \
      BytecodeArrayIterator* iterator) {                \
    ProcessJump(iterator);                              \
  }
CONDITIONAL_JUMPS_LIST(DEFINE_CONDITIONAL_JUMP)
#undef DEFINE_CONDITIONAL_JUMP

#define DEFINE_UNCONDITIONAL_JUMP(name, ...)            \
  void SerializerForBackgroundCompilation::Visit##name( \
      BytecodeArrayIterator* iterator) {                \
    ProcessJump(iterator);                              \
    environment()->Kill();                              \
  }
UNCONDITIONAL_JUMPS_LIST(DEFINE_UNCONDITIONAL_JUMP)
#undef DEFINE_UNCONDITIONAL_JUMP

#define DEFINE_IGNORE(name, ...)                        \
  void SerializerForBackgroundCompilation::Visit##name( \
      BytecodeArrayIterator* iterator) {}
IGNORED_BYTECODE_LIST(DEFINE_IGNORE)
#undef DEFINE_IGNORE

#define DEFINE_UNREACHABLE(name, ...)                   \
  void SerializerForBackgroundCompilation::Visit##name( \
      BytecodeArrayIterator* iterator) {                \
    UNREACHABLE();                                      \
  }
UNREACHABLE_BYTECODE_LIST(DEFINE_UNREACHABLE)
#undef DEFINE_UNREACHABLE

#define DEFINE_KILL(name, ...)                          \
  void SerializerForBackgroundCompilation::Visit##name( \
      BytecodeArrayIterator* iterator) {                \
    environment()->Kill();                              \
  }
KILL_ENVIRONMENT_LIST(DEFINE_KILL)
#undef DEFINE_KILL

#define DEFINE_BINARY_OP(name, ...)                     \
  void SerializerForBackgroundCompilation::Visit##name( \
      BytecodeArrayIterator* iterator) {                \
    FeedbackSlot slot = iterator->GetSlotOperand(1);    \
    ProcessUnaryOrBinaryOperation(slot, true);          \
  }
BINARY_OP_LIST(DEFINE_BINARY_OP)
#undef DEFINE_BINARY_OP

#define DEFINE_COMPARE_OP(name, ...)                    \
  void SerializerForBackgroundCompilation::Visit##name( \
      BytecodeArrayIterator* iterator) {                \
    FeedbackSlot slot = iterator->GetSlotOperand(1);    \
    ProcessCompareOperation(slot);                      \
  }
COMPARE_OP_LIST(DEFINE_COMPARE_OP)
#undef DEFINE_COMPARE_OP

#define DEFINE_UNARY_OP(name, ...)                      \
  void SerializerForBackgroundCompilation::Visit##name( \
      BytecodeArrayIterator* iterator) {                \
    FeedbackSlot slot = iterator->GetSlotOperand(0);    \
    ProcessUnaryOrBinaryOperation(slot, true);          \
  }
UNARY_OP_LIST(DEFINE_UNARY_OP)
#undef DEFINE_UNARY_OP

#undef BINARY_OP_LIST
#undef CLEAR_ACCUMULATOR_LIST
#undef COMPARE_OP_LIST
#undef CONDITIONAL_JUMPS_LIST
#undef IGNORED_BYTECODE_LIST
#undef KILL_ENVIRONMENT_LIST
#undef SUPPORTED_BYTECODE_LIST
#undef UNARY_OP_LIST
#undef UNCONDITIONAL_JUMPS_LIST
#undef UNREACHABLE_BYTECODE_LIST

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