// 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/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 CLEAR_ENVIRONMENT_LIST(V) \
  V(CallRuntimeForPair)           \
  V(Debugger)                     \
  V(ResumeGenerator)              \
  V(SuspendGenerator)

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

#define CLEAR_ACCUMULATOR_LIST(V) \
  V(CallRuntime)                  \
  V(CloneObject)                  \
  V(CreateArrayFromIterable)      \
  V(CreateEmptyArrayLiteral)      \
  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(IncBlockCounter)                  \
  V(StackCheck)                       \
  V(ThrowSuperAlreadyCalledIfNotHole) \
  V(ThrowSuperNotCalledIfHole)

#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(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(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)           \
  CLEAR_ENVIRONMENT_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)

template <typename T>
struct HandleComparator {
  bool operator()(const Handle<T>& lhs, const Handle<T>& rhs) const {
    return lhs.address() < rhs.address();
  }
};

struct VirtualContext {
  unsigned int distance;
  Handle<Context> context;

  VirtualContext(unsigned int distance_in, Handle<Context> context_in)
      : distance(distance_in), context(context_in) {
    CHECK_GT(distance, 0);
  }
  bool operator<(const VirtualContext& other) const {
    return HandleComparator<Context>()(context, other.context) &&
           distance < other.distance;
  }
};

class FunctionBlueprint;
using ConstantsSet = ZoneSet<Handle<Object>, HandleComparator<Object>>;
using VirtualContextsSet = ZoneSet<VirtualContext>;
using MapsSet = ZoneSet<Handle<Map>, HandleComparator<Map>>;
using BlueprintsSet = ZoneSet<FunctionBlueprint>;

class Hints {
 public:
  explicit Hints(Zone* zone);

  static Hints SingleConstant(Handle<Object> constant, Zone* zone);

  const ConstantsSet& constants() const;
  const MapsSet& maps() const;
  const BlueprintsSet& function_blueprints() const;
  const VirtualContextsSet& virtual_contexts() const;

  void AddConstant(Handle<Object> constant);
  void AddMap(Handle<Map> map);
  void AddFunctionBlueprint(FunctionBlueprint function_blueprint);
  void AddVirtualContext(VirtualContext virtual_context);

  void Add(const Hints& other);

  void Clear();
  bool IsEmpty() const;

#ifdef ENABLE_SLOW_DCHECKS
  bool Includes(Hints const& other) const;
  bool Equals(Hints const& other) const;
#endif

 private:
  VirtualContextsSet virtual_contexts_;
  ConstantsSet constants_;
  MapsSet maps_;
  BlueprintsSet function_blueprints_;
};

using HintsVector = ZoneVector<Hints>;

class FunctionBlueprint {
 public:
  FunctionBlueprint(Handle<JSFunction> function, Isolate* isolate, Zone* zone);

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

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

  bool operator<(const FunctionBlueprint& other) const {
    // A feedback vector is never used for more than one SFI, so it can
    // be used for strict ordering of blueprints.
    DCHECK_IMPLIES(feedback_vector_.equals(other.feedback_vector_),
                   shared_.equals(other.shared_));
    return HandleComparator<FeedbackVector>()(feedback_vector_,
                                              other.feedback_vector_);
  }

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

class CompilationSubject {
 public:
  explicit CompilationSubject(FunctionBlueprint blueprint)
      : blueprint_(blueprint) {}

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

  const FunctionBlueprint& blueprint() const { return blueprint_; }
  MaybeHandle<JSFunction> closure() const { return closure_; }

 private:
  FunctionBlueprint blueprint_;
  MaybeHandle<JSFunction> closure_;
};

// 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(
      JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone,
      Handle<JSFunction> closure, SerializerForBackgroundCompilationFlags flags,
      BailoutId osr_offset);
  Hints Run();  // NOTE: Returns empty for an already-serialized function.

  class Environment;

 private:
  SerializerForBackgroundCompilation(
      JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone,
      CompilationSubject function, base::Optional<Hints> new_target,
      const HintsVector& arguments,
      SerializerForBackgroundCompilationFlags flags);

  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

  // Returns whether the callee with the given SFI should be processed further,
  // i.e. whether it's inlineable.
  bool ProcessSFIForCallOrConstruct(Handle<SharedFunctionInfo> shared,
                                    const HintsVector& arguments,
                                    SpeculationMode speculation_mode);
  // Returns whether {function} should be serialized for compilation.
  bool ProcessCalleeForCallOrConstruct(Handle<JSFunction> function,
                                       const HintsVector& arguments,
                                       SpeculationMode speculation_mode);
  void ProcessCallOrConstruct(Hints callee, base::Optional<Hints> new_target,
                              const HintsVector& arguments, FeedbackSlot slot,
                              bool with_spread = false);
  void ProcessCallVarArgs(ConvertReceiverMode receiver_mode,
                          Hints const& callee, interpreter::Register first_reg,
                          int reg_count, FeedbackSlot slot,
                          bool with_spread = false);
  void ProcessApiCall(Handle<SharedFunctionInfo> target,
                      const HintsVector& arguments);
  void ProcessReceiverMapForApiCall(FunctionTemplateInfoRef target,
                                    Handle<Map> receiver);
  void ProcessBuiltinCall(Handle<SharedFunctionInfo> target,
                          const HintsVector& arguments,
                          SpeculationMode speculation_mode);

  void ProcessJump(interpreter::BytecodeArrayIterator* iterator);

  void ProcessKeyedPropertyAccess(Hints const& 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 ProcessNamedAccess(Hints receiver, NamedAccessFeedback const& feedback,
                          AccessMode access_mode, Hints* new_accumulator_hints);
  void ProcessElementAccess(Hints receiver, Hints key,
                            ElementAccessFeedback const& feedback,
                            AccessMode access_mode);

  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 ProcessHintsForFunctionCall(Hints const& target_hints);
  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(
      MapRef receiver_map, NameRef const& name, AccessMode access_mode,
      base::Optional<JSObjectRef> receiver, Hints* new_accumulator_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, bool with_spread);

  // 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);

  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() const { return zone_; }
  Environment* environment() const { return environment_; }
  SerializerForBackgroundCompilationFlags flags() const { return flags_; }
  BailoutId osr_offset() const { return osr_offset_; }

  JSHeapBroker* const broker_;
  CompilationDependencies* const dependencies_;
  Zone* const zone_;
  Environment* const environment_;
  ZoneUnorderedMap<int, Environment*> jump_target_environments_;
  SerializerForBackgroundCompilationFlags const flags_;
  BailoutId const osr_offset_;
};

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

using BytecodeArrayIterator = interpreter::BytecodeArrayIterator;

FunctionBlueprint::FunctionBlueprint(Handle<SharedFunctionInfo> shared,
                                     Handle<FeedbackVector> feedback_vector,
                                     const Hints& context_hints)
    : shared_(shared),
      feedback_vector_(feedback_vector),
      context_hints_(context_hints) {}

FunctionBlueprint::FunctionBlueprint(Handle<JSFunction> function,
                                     Isolate* isolate, Zone* zone)
    : shared_(handle(function->shared(), isolate)),
      feedback_vector_(handle(function->feedback_vector(), isolate)),
      context_hints_(zone) {
  context_hints_.AddConstant(handle(function->context(), isolate));
}

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

Hints::Hints(Zone* zone)
    : virtual_contexts_(zone),
      constants_(zone),
      maps_(zone),
      function_blueprints_(zone) {}

#ifdef ENABLE_SLOW_DCHECKS
namespace {
template <typename K, typename Compare>
bool SetIncludes(ZoneSet<K, Compare> const& lhs,
                 ZoneSet<K, Compare> const& rhs) {
  return std::all_of(rhs.cbegin(), rhs.cend(),
                     [&](K const& x) { return lhs.find(x) != lhs.cend(); });
}
}  // namespace
bool Hints::Includes(Hints const& other) const {
  return SetIncludes(constants(), other.constants()) &&
         SetIncludes(function_blueprints(), other.function_blueprints()) &&
         SetIncludes(maps(), other.maps());
}
bool Hints::Equals(Hints const& other) const {
  return this->Includes(other) && other.Includes(*this);
}
#endif

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

const ConstantsSet& Hints::constants() const { return constants_; }

const MapsSet& Hints::maps() const { return maps_; }

const BlueprintsSet& Hints::function_blueprints() const {
  return function_blueprints_;
}

const VirtualContextsSet& Hints::virtual_contexts() const {
  return virtual_contexts_;
}

void Hints::AddVirtualContext(VirtualContext virtual_context) {
  virtual_contexts_.insert(virtual_context);
}

void Hints::AddConstant(Handle<Object> constant) {
  constants_.insert(constant);
}

void Hints::AddMap(Handle<Map> map) { maps_.insert(map); }

void Hints::AddFunctionBlueprint(FunctionBlueprint function_blueprint) {
  function_blueprints_.insert(function_blueprint);
}

void Hints::Add(const Hints& other) {
  for (auto x : other.constants()) AddConstant(x);
  for (auto x : other.maps()) AddMap(x);
  for (auto x : other.function_blueprints()) AddFunctionBlueprint(x);
  for (auto x : other.virtual_contexts()) AddVirtualContext(x);
}

bool Hints::IsEmpty() const {
  return constants().empty() && maps().empty() &&
         function_blueprints().empty() && virtual_contexts().empty();
}

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 FunctionBlueprint& blueprint) {
  out << Brief(*blueprint.shared()) << std::endl;
  out << Brief(*blueprint.feedback_vector()) << std::endl;
  !blueprint.context_hints().IsEmpty() && out << blueprint.context_hints()
                                              << "):" << std::endl;
  return out;
}

std::ostream& operator<<(std::ostream& out, const Hints& hints) {
  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 (FunctionBlueprint const& blueprint : hints.function_blueprints()) {
    out << "  blueprint " << blueprint << std::endl;
  }
  for (VirtualContext const& virtual_context : hints.virtual_contexts()) {
    out << "  virtual context " << virtual_context << std::endl;
  }
  return out;
}

void Hints::Clear() {
  virtual_contexts_.clear();
  constants_.clear();
  maps_.clear();
  function_blueprints_.clear();
  DCHECK(IsEmpty());
}

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);

  bool IsDead() const { return ephemeral_hints_.empty(); }

  void Kill() {
    DCHECK(!IsDead());
    ephemeral_hints_.clear();
    DCHECK(IsDead());
  }

  void Revive() {
    DCHECK(IsDead());
    ephemeral_hints_.resize(ephemeral_hints_size(), Hints(zone()));
    DCHECK(!IsDead());
  }

  // Merge {other} into {this} environment (leaving {other} unmodified).
  void Merge(Environment* other);

  FunctionBlueprint function() const { return function_; }

  Hints const& closure_hints() const { return closure_hints_; }
  Hints const& current_context_hints() const { return current_context_hints_; }
  Hints& current_context_hints() { return current_context_hints_; }
  Hints const& return_value_hints() const { return return_value_hints_; }
  Hints& return_value_hints() { return return_value_hints_; }

  Hints& accumulator_hints() {
    CHECK_LT(accumulator_index(), ephemeral_hints_.size());
    return ephemeral_hints_[accumulator_index()];
  }

  Hints& register_hints(interpreter::Register reg) {
    if (reg.is_function_closure()) return closure_hints_;
    if (reg.is_current_context()) return current_context_hints_;
    int local_index = RegisterToLocalIndex(reg);
    CHECK_LT(local_index, ephemeral_hints_.size());
    return ephemeral_hints_[local_index];
  }

  // Clears all hints except those for the context, return value, and the
  // closure.
  void ClearEphemeralHints() {
    for (auto& hints : ephemeral_hints_) hints.Clear();
  }

  // Appends the hints for the given register range to {dst} (in order).
  void ExportRegisterHints(interpreter::Register first, size_t count,
                           HintsVector* dst);

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

  int RegisterToLocalIndex(interpreter::Register reg) const;

  Zone* zone() const { return zone_; }
  int parameter_count() const { return parameter_count_; }
  int register_count() const { return register_count_; }

  Zone* const zone_;
  // Instead of storing the blueprint here, we could extract it from the
  // (closure) hints but that would be cumbersome.
  FunctionBlueprint const function_;
  int const parameter_count_;
  int const register_count_;

  Hints closure_hints_;
  Hints current_context_hints_;
  Hints return_value_hints_;

  // ephemeral_hints_ contains hints for the contents of the registers,
  // the accumulator and the parameters. The layout is as follows:
  // [ parameters | registers | accumulator ]
  // The first parameter is the receiver.
  HintsVector ephemeral_hints_;
  int accumulator_index() const { return parameter_count() + register_count(); }
  int ephemeral_hints_size() const { return accumulator_index() + 1; }
};

SerializerForBackgroundCompilation::Environment::Environment(
    Zone* zone, CompilationSubject function)
    : zone_(zone),
      function_(function.blueprint()),
      parameter_count_(
          function_.shared()->GetBytecodeArray().parameter_count()),
      register_count_(function_.shared()->GetBytecodeArray().register_count()),
      closure_hints_(zone),
      current_context_hints_(zone),
      return_value_hints_(zone),
      ephemeral_hints_(ephemeral_hints_size(), Hints(zone), zone) {
  Handle<JSFunction> closure;
  if (function.closure().ToHandle(&closure)) {
    closure_hints_.AddConstant(closure);
  } else {
    closure_hints_.AddFunctionBlueprint(function.blueprint());
  }

  // Consume blueprint context hint information.
  current_context_hints().Add(function.blueprint().context_hints());
}

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

  // Pad the rest with "undefined".
  Hints undefined_hint =
      Hints::SingleConstant(isolate->factory()->undefined_value(), zone);
  for (size_t i = arguments.size(); i < param_count; ++i) {
    ephemeral_hints_[i] = undefined_hint;
  }

  interpreter::Register new_target_reg =
      function_.shared()
          ->GetBytecodeArray()
          .incoming_new_target_or_generator_register();
  if (new_target_reg.is_valid()) {
    DCHECK(register_hints(new_target_reg).IsEmpty());
    if (new_target.has_value()) {
      register_hints(new_target_reg).Add(*new_target);
    }
  }
}

void SerializerForBackgroundCompilation::Environment::Merge(
    Environment* other) {
  // {other} is guaranteed to have the same layout because it comes from an
  // earlier bytecode in the same function.
  CHECK_EQ(parameter_count(), other->parameter_count());
  CHECK_EQ(register_count(), other->register_count());

  SLOW_DCHECK(closure_hints_.Equals(other->closure_hints_));

  if (IsDead()) {
    ephemeral_hints_ = other->ephemeral_hints_;
    SLOW_DCHECK(return_value_hints_.Includes(other->return_value_hints_));
    CHECK(!IsDead());
    return;
  }

  CHECK_EQ(ephemeral_hints_.size(), other->ephemeral_hints_.size());
  for (size_t i = 0; i < ephemeral_hints_.size(); ++i) {
    ephemeral_hints_[i].Add(other->ephemeral_hints_[i]);
  }

  return_value_hints_.Add(other->return_value_hints_);
}

std::ostream& operator<<(
    std::ostream& out,
    const SerializerForBackgroundCompilation::Environment& env) {
  std::ostringstream output_stream;
  output_stream << "Function ";
  env.function_.shared()->Name().Print(output_stream);

  if (env.IsDead()) {
    output_stream << "dead\n";
  } else {
    output_stream << "alive\n";
    for (int i = 0; i < static_cast<int>(env.ephemeral_hints_.size()); ++i) {
      Hints const& hints = env.ephemeral_hints_[i];
      if (!hints.IsEmpty()) {
        if (i < env.parameter_count()) {
          output_stream << "Hints for a" << i << ":\n";
        } else if (i < env.parameter_count() + env.register_count()) {
          int local_register = i - env.parameter_count();
          output_stream << "Hints for r" << local_register << ":\n";
        } else if (i == env.accumulator_index()) {
          output_stream << "Hints for <accumulator>:\n";
        } else {
          UNREACHABLE();
        }
        output_stream << hints;
      }
    }
  }

  if (!env.closure_hints().IsEmpty()) {
    output_stream << "Hints for <closure>:\n" << env.closure_hints();
  }
  if (!env.current_context_hints().IsEmpty()) {
    output_stream << "Hints for <context>:\n" << env.current_context_hints();
  }
  if (!env.return_value_hints().IsEmpty()) {
    output_stream << "Hints for {return value}:\n" << env.return_value_hints();
  }

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

int SerializerForBackgroundCompilation::Environment::RegisterToLocalIndex(
    interpreter::Register reg) const {
  if (reg.is_parameter()) {
    return reg.ToParameterIndex(parameter_count());
  } else {
    DCHECK(!reg.is_function_closure());
    return parameter_count() + reg.index();
  }
}

SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
    JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone,
    Handle<JSFunction> closure, SerializerForBackgroundCompilationFlags flags,
    BailoutId osr_offset)
    : broker_(broker),
      dependencies_(dependencies),
      zone_(zone),
      environment_(new (zone) Environment(
          zone, CompilationSubject(closure, broker_->isolate(), zone))),
      jump_target_environments_(zone),
      flags_(flags),
      osr_offset_(osr_offset) {
  JSFunctionRef(broker, closure).Serialize();
}

SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
    JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone,
    CompilationSubject function, base::Optional<Hints> new_target,
    const HintsVector& arguments, SerializerForBackgroundCompilationFlags flags)
    : broker_(broker),
      dependencies_(dependencies),
      zone_(zone),
      environment_(new (zone) Environment(zone, broker_->isolate(), function,
                                          new_target, arguments)),
      jump_target_environments_(zone),
      flags_(flags),
      osr_offset_(BailoutId::None()) {
  TraceScope tracer(
      broker_, this,
      "SerializerForBackgroundCompilation::SerializerForBackgroundCompilation");
  TRACE_BROKER(broker_, "Initial environment:\n" << *environment_);
  Handle<JSFunction> closure;
  if (function.closure().ToHandle(&closure)) {
    JSFunctionRef(broker, closure).Serialize();
  }
}

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 (feedback.IsInsufficient()) {
    environment()->Kill();
    return true;
  }
  return false;
}

Hints SerializerForBackgroundCompilation::Run() {
  TraceScope tracer(broker(), this, "SerializerForBackgroundCompilation::Run");
  SharedFunctionInfoRef shared(broker(), environment()->function().shared());
  FeedbackVectorRef feedback_vector_ref(broker(), feedback_vector());
  if (shared.IsSerializedForCompilation(feedback_vector_ref)) {
    TRACE_BROKER(broker(), "Already ran serializer for SharedFunctionInfo "
                               << Brief(*shared.object())
                               << ", bailing out.\n");
    return Hints(zone());
  }
  shared.SetSerializedForCompilation(feedback_vector_ref);

  // 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();
  return environment()->return_value_hints();
}

class ExceptionHandlerMatcher {
 public:
  explicit ExceptionHandlerMatcher(
      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) {
      handlers_.insert(table.GetRangeHandler(i));
    }
    handlers_iterator_ = handlers_.cbegin();
  }

  bool CurrentBytecodeIsExceptionHandlerStart() {
    CHECK(!bytecode_iterator_.done());
    while (handlers_iterator_ != handlers_.cend() &&
           *handlers_iterator_ < bytecode_iterator_.current_offset()) {
      handlers_iterator_++;
    }
    return handlers_iterator_ != handlers_.cend() &&
           *handlers_iterator_ == bytecode_iterator_.current_offset();
  }

 private:
  BytecodeArrayIterator const& bytecode_iterator_;
  std::set<int> handlers_;
  std::set<int>::const_iterator handlers_iterator_;
};

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

Handle<BytecodeArray> SerializerForBackgroundCompilation::bytecode_array()
    const {
  return handle(environment()->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());
  ExceptionHandlerMatcher handler_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();
    IncorporateJumpTargetEnvironment(current_offset);

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

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

    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
      default: {
        environment()->ClearEphemeralHints();
        break;
      }
    }
  }

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

void SerializerForBackgroundCompilation::VisitGetIterator(
    BytecodeArrayIterator* iterator) {
  AccessMode mode = AccessMode::kLoad;
  Hints const& receiver =
      environment()->register_hints(iterator->GetRegisterOperand(0));
  Handle<Name> name = broker()->isolate()->factory()->iterator_symbol();
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  ProcessNamedPropertyAccess(receiver, NameRef(broker(), name), slot, mode);
}

void SerializerForBackgroundCompilation::VisitGetSuperConstructor(
    BytecodeArrayIterator* iterator) {
  interpreter::Register dst = iterator->GetRegisterOperand(0);
  environment()->register_hints(dst).Clear();

  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()) {
      environment()->register_hints(dst).AddConstant(proto.object());
    }
  }
}

void SerializerForBackgroundCompilation::VisitGetTemplateObject(
    BytecodeArrayIterator* iterator) {
  ObjectRef description(
      broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  FeedbackVectorRef feedback_vector_ref(broker(), feedback_vector());
  SharedFunctionInfoRef shared(broker(), environment()->function().shared());
  JSArrayRef template_object =
      shared.GetTemplateObject(description, feedback_vector_ref, slot,
                               SerializationPolicy::kSerializeIfNeeded);
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().AddConstant(template_object.object());
}

void SerializerForBackgroundCompilation::VisitLdaTrue(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().AddConstant(
      broker()->isolate()->factory()->true_value());
}

void SerializerForBackgroundCompilation::VisitLdaFalse(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().AddConstant(
      broker()->isolate()->factory()->false_value());
}

void SerializerForBackgroundCompilation::VisitLdaTheHole(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().AddConstant(
      broker()->isolate()->factory()->the_hole_value());
}

void SerializerForBackgroundCompilation::VisitLdaUndefined(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().AddConstant(
      broker()->isolate()->factory()->undefined_value());
}

void SerializerForBackgroundCompilation::VisitLdaNull(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().AddConstant(
      broker()->isolate()->factory()->null_value());
}

void SerializerForBackgroundCompilation::VisitLdaZero(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().AddConstant(
      handle(Smi::FromInt(0), broker()->isolate()));
}

void SerializerForBackgroundCompilation::VisitLdaSmi(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().AddConstant(handle(
      Smi::FromInt(iterator->GetImmediateOperand(0)), broker()->isolate()));
}

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 arguments(zone());
      environment()->ExportRegisterHints(first_reg, reg_count, &arguments);
      Hints const& resolution_hints = arguments[1];  // The resolution object.
      ProcessHintsForPromiseResolve(resolution_hints);
      environment()->accumulator_hints().Clear();
      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;
    }
    default: {
      break;
    }
  }
  environment()->ClearEphemeralHints();
}

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

void SerializerForBackgroundCompilation::VisitPushContext(
    BytecodeArrayIterator* iterator) {
  // Transfer current context hints to the destination register hints.
  Hints& current_context_hints = environment()->current_context_hints();
  Hints& saved_context_hints =
      environment()->register_hints(iterator->GetRegisterOperand(0));
  saved_context_hints.Clear();
  saved_context_hints.Add(current_context_hints);

  // New context is in the accumulator. Put those hints into the current context
  // register hints.
  current_context_hints.Clear();
  current_context_hints.Add(environment()->accumulator_hints());
}

void SerializerForBackgroundCompilation::VisitPopContext(
    BytecodeArrayIterator* iterator) {
  // Replace current context hints with hints given in the argument register.
  Hints& new_context_hints =
      environment()->register_hints(iterator->GetRegisterOperand(0));
  environment()->current_context_hints().Clear();
  environment()->current_context_hints().Add(new_context_hints);
}

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());
  }
}

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 =
      environment()->register_hints(iterator->GetRegisterOperand(0));
  const int slot = iterator->GetIndexOperand(1);
  const int depth = iterator->GetUnsignedImmediateOperand(2);
  Hints new_accumulator_hints(zone());
  ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot,
                       &new_accumulator_hints);
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().Add(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(zone());
  ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot,
                       &new_accumulator_hints);
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().Add(new_accumulator_hints);
}

void SerializerForBackgroundCompilation::VisitLdaImmutableContextSlot(
    BytecodeArrayIterator* iterator) {
  const int slot = iterator->GetIndexOperand(1);
  const int depth = iterator->GetUnsignedImmediateOperand(2);
  Hints const& context_hints =
      environment()->register_hints(iterator->GetRegisterOperand(0));
  Hints new_accumulator_hints(zone());
  ProcessContextAccess(context_hints, slot, depth, kSerializeSlot,
                       &new_accumulator_hints);
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().Add(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(zone());
  ProcessContextAccess(context_hints, slot, depth, kSerializeSlot,
                       &new_accumulator_hints);
  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().Add(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(zone());
  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().Clear();
}

void SerializerForBackgroundCompilation::VisitStaContextSlot(
    BytecodeArrayIterator* iterator) {
  const int slot = iterator->GetIndexOperand(1);
  const int depth = iterator->GetUnsignedImmediateOperand(2);
  Hints const& register_hints =
      environment()->register_hints(iterator->GetRegisterOperand(0));
  ProcessContextAccess(register_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().Clear();
  environment()->accumulator_hints().Add(
      environment()->register_hints(iterator->GetRegisterOperand(0)));
}

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

void SerializerForBackgroundCompilation::VisitMov(
    BytecodeArrayIterator* iterator) {
  interpreter::Register src = iterator->GetRegisterOperand(0);
  interpreter::Register dst = iterator->GetRegisterOperand(1);
  environment()->register_hints(dst).Clear();
  environment()->register_hints(dst).Add(environment()->register_hints(src));
}

void SerializerForBackgroundCompilation::VisitCreateRegExpLiteral(
    BytecodeArrayIterator* iterator) {
  Handle<String> constant_pattern = Handle<String>::cast(
      iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  StringRef description(broker(), constant_pattern);
  environment()->accumulator_hints().Clear();
}

void SerializerForBackgroundCompilation::VisitCreateArrayLiteral(
    BytecodeArrayIterator* iterator) {
  Handle<ArrayBoilerplateDescription> array_boilerplate_description =
      Handle<ArrayBoilerplateDescription>::cast(
          iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  ArrayBoilerplateDescriptionRef description(broker(),
                                             array_boilerplate_description);
  environment()->accumulator_hints().Clear();
}

void SerializerForBackgroundCompilation::VisitCreateObjectLiteral(
    BytecodeArrayIterator* iterator) {
  Handle<ObjectBoilerplateDescription> constant_properties =
      Handle<ObjectBoilerplateDescription>::cast(
          iterator->GetConstantForIndexOperand(0, broker()->isolate()));
  ObjectBoilerplateDescriptionRef description(broker(), constant_properties);
  environment()->accumulator_hints().Clear();
}

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);

  Hints const& current_context_hints = environment()->current_context_hints();
  Hints& accumulator_hints = environment()->accumulator_hints();
  accumulator_hints.Clear();

  // 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));
      accumulator_hints.AddVirtualContext(VirtualContext(1, as_context));
    }
  }

  // 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()) {
    accumulator_hints.AddVirtualContext(
        VirtualContext(x.distance + 1, x.context));
  }
}

void SerializerForBackgroundCompilation::VisitCreateClosure(
    BytecodeArrayIterator* iterator) {
  environment()->accumulator_hints().Clear();

  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);

  if (cell_value->IsFeedbackVector()) {
    FunctionBlueprint blueprint(shared,
                                Handle<FeedbackVector>::cast(cell_value),
                                environment()->current_context_hints());
    environment()->accumulator_hints().AddFunctionBlueprint(blueprint);
  }
}

void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver(
    BytecodeArrayIterator* iterator) {
  const Hints& callee =
      environment()->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) {
  const Hints& callee =
      environment()->register_hints(iterator->GetRegisterOperand(0));
  FeedbackSlot slot = iterator->GetSlotOperand(1);

  Hints receiver = Hints::SingleConstant(
      broker()->isolate()->factory()->undefined_value(), zone());
  HintsVector parameters({receiver}, zone());
  ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}

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

  Hints receiver = Hints::SingleConstant(
      broker()->isolate()->factory()->undefined_value(), zone());
  HintsVector parameters({receiver, arg0}, zone());
  ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}

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

  Hints receiver = Hints::SingleConstant(
      broker()->isolate()->factory()->undefined_value(), zone());
  HintsVector parameters({receiver, arg0, arg1}, zone());
  ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}

void SerializerForBackgroundCompilation::VisitCallAnyReceiver(
    BytecodeArrayIterator* iterator) {
  const Hints& callee =
      environment()->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) {
  const Hints& callee =
      environment()->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) {
  const Hints& callee =
      environment()->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) {
  const Hints& callee =
      environment()->register_hints(iterator->GetRegisterOperand(0));
  const Hints& receiver =
      environment()->register_hints(iterator->GetRegisterOperand(1));
  FeedbackSlot slot = iterator->GetSlotOperand(2);

  HintsVector parameters({receiver}, zone());
  ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}

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

  HintsVector parameters({receiver, arg0}, zone());
  ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}

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

  HintsVector parameters({receiver, arg0, arg1}, zone());
  ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}

void SerializerForBackgroundCompilation::VisitCallWithSpread(
    BytecodeArrayIterator* iterator) {
  const Hints& callee =
      environment()->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, true);
}

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 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, bool with_spread) {
  if (with_spread) {
    DCHECK_LT(0, arguments.size());
    // Pad the missing arguments in case we were called with spread operator.
    // Drop the last actually passed argument, which contains the spread.
    // We don't know what the spread element produces. Therefore we pretend
    // that the function is called with the maximal number of parameters and
    // that we have no information about the parameters that were not
    // explicitly provided.
    HintsVector padded = arguments;
    padded.pop_back();  // Remove the spread element.
    // Fill the rest with empty hints.
    padded.resize(
        function.blueprint().shared()->GetBytecodeArray().parameter_count(),
        Hints(zone()));
    return RunChildSerializer(function, new_target, padded, false);
  }

  SerializerForBackgroundCompilation child_serializer(
      broker(), dependencies(), zone(), function, new_target, arguments,
      flags());
  return child_serializer.Run();
}

bool SerializerForBackgroundCompilation::ProcessSFIForCallOrConstruct(
    Handle<SharedFunctionInfo> shared, const HintsVector& arguments,
    SpeculationMode speculation_mode) {
  if (shared->IsApiFunction()) {
    ProcessApiCall(shared, arguments);
    DCHECK(!shared->IsInlineable());
  } else if (shared->HasBuiltinId()) {
    ProcessBuiltinCall(shared, arguments, speculation_mode);
    DCHECK(!shared->IsInlineable());
  }
  return shared->IsInlineable();
}

bool SerializerForBackgroundCompilation::ProcessCalleeForCallOrConstruct(
    Handle<JSFunction> function, const HintsVector& arguments,
    SpeculationMode speculation_mode) {
  JSFunctionRef(broker(), function).Serialize();

  Handle<SharedFunctionInfo> shared(function->shared(), broker()->isolate());

  return ProcessSFIForCallOrConstruct(shared, arguments, speculation_mode) &&
         function->has_feedback_vector();
}

namespace {
// Returns the innermost bound target, if it's a JSFunction and inserts
// all bound arguments and {original_arguments} into {expanded_arguments}
// in the appropriate order.
MaybeHandle<JSFunction> UnrollBoundFunction(
    JSBoundFunctionRef const& bound_function, JSHeapBroker* broker,
    const HintsVector& original_arguments, HintsVector* expanded_arguments) {
  DCHECK(expanded_arguments->empty());

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

  if (!target.IsJSFunction()) return MaybeHandle<JSFunction>();

  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.AsJSFunction().object();
}
}  // namespace

void SerializerForBackgroundCompilation::ProcessCallOrConstruct(
    Hints callee, base::Optional<Hints> new_target,
    const HintsVector& arguments, FeedbackSlot slot, bool with_spread) {
  SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation;
  if (!slot.IsInvalid()) {
    FeedbackSource source(feedback_vector(), slot);
    ProcessedFeedback const& feedback =
        broker()->ProcessFeedbackForCall(source);
    if (BailoutOnUninitialized(feedback)) return;

    // Incorporate feedback into hints copy to simplify processing.
    if (!feedback.IsInsufficient()) {
      speculation_mode = feedback.AsCall().speculation_mode();
      base::Optional<HeapObjectRef> target = feedback.AsCall().target();
      if (target.has_value() && target->map().is_callable()) {
        // 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->AddConstant(target->object());
          callee.AddConstant(target->object());
        } else {
          // Call; target is callee.
          callee.AddConstant(target->object());
        }
      }
    }
  }

  environment()->accumulator_hints().Clear();

  // For JSCallReducer::ReduceJSCall and JSCallReducer::ReduceJSConstruct.
  for (auto hint : callee.constants()) {
    const HintsVector* actual_arguments = &arguments;
    Handle<JSFunction> function;
    HintsVector expanded_arguments(zone());
    if (hint->IsJSBoundFunction()) {
      JSBoundFunctionRef bound_function(broker(),
                                        Handle<JSBoundFunction>::cast(hint));
      bound_function.Serialize();

      MaybeHandle<JSFunction> maybe_function = UnrollBoundFunction(
          bound_function, broker(), arguments, &expanded_arguments);
      if (maybe_function.is_null()) continue;
      function = maybe_function.ToHandleChecked();
      actual_arguments = &expanded_arguments;
    } else if (hint->IsJSFunction()) {
      function = Handle<JSFunction>::cast(hint);
    } else {
      continue;
    }

    if (ProcessCalleeForCallOrConstruct(function, *actual_arguments,
                                        speculation_mode)) {
      environment()->accumulator_hints().Add(RunChildSerializer(
          CompilationSubject(function, broker()->isolate(), zone()), new_target,
          *actual_arguments, with_spread));
    }
  }

  // For JSCallReducer::ReduceJSCall and JSCallReducer::ReduceJSConstruct.
  for (auto hint : callee.function_blueprints()) {
    Handle<SharedFunctionInfo> shared = hint.shared();
    if (!ProcessSFIForCallOrConstruct(shared, arguments, speculation_mode)) {
      continue;
    }

    environment()->accumulator_hints().Add(RunChildSerializer(
        CompilationSubject(hint), new_target, arguments, with_spread));
  }
}

void SerializerForBackgroundCompilation::ProcessCallVarArgs(
    ConvertReceiverMode receiver_mode, Hints const& callee,
    interpreter::Register first_reg, int reg_count, FeedbackSlot slot,
    bool with_spread) {
  HintsVector arguments(zone());
  // The receiver is either given in the first register or it is implicitly
  // the {undefined} value.
  if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) {
    arguments.push_back(Hints::SingleConstant(
        broker()->isolate()->factory()->undefined_value(), zone()));
  }
  environment()->ExportRegisterHints(first_reg, reg_count, &arguments);

  ProcessCallOrConstruct(callee, base::nullopt, arguments, slot);
}

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(), 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;

  CHECK_GE(arguments.size(), 1);
  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, const HintsVector& arguments,
    SpeculationMode speculation_mode) {
  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) {
        CHECK_GE(arguments.size(), 1);
        ProcessMapHintsForPromises(arguments[0]);
      }
      break;
    }
    case Builtins::kPromisePrototypeFinally: {
      // For JSCallReducer::ReducePromisePrototypeFinally.
      if (speculation_mode != SpeculationMode::kDisallowSpeculation) {
        CHECK_GE(arguments.size(), 1);
        ProcessMapHintsForPromises(arguments[0]);
      }
      break;
    }
    case Builtins::kPromisePrototypeThen: {
      // For JSCallReducer::ReducePromisePrototypeThen.
      if (speculation_mode != SpeculationMode::kDisallowSpeculation) {
        CHECK_GE(arguments.size(), 1);
        ProcessMapHintsForPromises(arguments[0]);
      }
      break;
    }
    case Builtins::kPromiseResolveTrampoline:
      // For JSCallReducer::ReducePromiseInternalResolve and
      // JSNativeContextSpecialization::ReduceJSResolvePromise.
      if (arguments.size() >= 2) {
        Hints const& resolution_hints = arguments[1];
        ProcessHintsForPromiseResolve(resolution_hints);
      }
      break;
    case Builtins::kPromiseInternalResolve:
      // For JSCallReducer::ReducePromiseInternalResolve and
      // JSNativeContextSpecialization::ReduceJSResolvePromise.
      if (arguments.size() >= 3) {
        Hints const& resolution_hints = arguments[2];
        ProcessHintsForPromiseResolve(resolution_hints);
      }
      break;
    case Builtins::kRegExpPrototypeTest:
      // 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::kArrayReduce:
    case Builtins::kArrayReduceRight:
    case Builtins::kArraySome:
      if (arguments.size() >= 2 &&
          speculation_mode != SpeculationMode::kDisallowSpeculation) {
        Hints const& callback_hints = arguments[1];
        ProcessHintsForFunctionCall(callback_hints);
      }
      break;
    case Builtins::kFunctionPrototypeApply:
    case Builtins::kFunctionPrototypeCall:
    case Builtins::kPromiseConstructor:
      // TODO(mslekova): Since the reducer for all these introduce a
      // JSCall/JSConstruct that will again get optimized by the JSCallReducer,
      // we basically might have to do all the serialization that we do for that
      // here as well.  The only difference is that the new JSCall/JSConstruct
      // has speculation disabled, causing the JSCallReducer to do much less
      // work. To account for that, ProcessCallOrConstruct should have a way of
      // taking the speculation mode as an argument rather than getting that
      // from the feedback. (Also applies to Reflect.apply and
      // Reflect.construct.)
      if (arguments.size() >= 1) {
        ProcessHintsForFunctionCall(arguments[0]);
      }
      break;
    case Builtins::kReflectApply:
    case Builtins::kReflectConstruct:
      if (arguments.size() >= 2) {
        ProcessHintsForFunctionCall(arguments[1]);
      }
      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) {
        ProcessHintsForFunctionBind(arguments[0]);
      }
      break;
    case Builtins::kObjectGetPrototypeOf:
    case Builtins::kReflectGetPrototypeOf:
      if (arguments.size() >= 2) {
        ProcessHintsForObjectGetPrototype(arguments[1]);
      } else {
        Hints 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->IsJSReceiver()) continue;
    Handle<JSReceiver> receiver(Handle<JSReceiver>::cast(hint));
    processMap(handle(receiver->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));
  }
}

void SerializerForBackgroundCompilation::ProcessHintsForFunctionCall(
    Hints const& target_hints) {
  for (auto constant : target_hints.constants()) {
    if (constant->IsJSFunction()) JSFunctionRef(broker(), constant).Serialize();
  }
}

namespace {
void ProcessMapForFunctionBind(MapRef map) {
  map.SerializePrototype();
  int min_nof_descriptors = i::Max(JSFunction::kLengthDescriptorIndex,
                                   JSFunction::kNameDescriptorIndex) +
                            1;
  if (map.NumberOfOwnDescriptors() >= min_nof_descriptors) {
    map.SerializeOwnDescriptor(JSFunction::kLengthDescriptorIndex);
    map.SerializeOwnDescriptor(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] =
        new (zone()) Environment(*environment());
  } else {
    it->second->Merge(environment());
  }
}

void SerializerForBackgroundCompilation::IncorporateJumpTargetEnvironment(
    int target_offset) {
  auto it = jump_target_environments_.find(target_offset);
  if (it != jump_target_environments_.end()) {
    environment()->Merge(it->second);
    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) {
  environment()->return_value_hints().Add(environment()->accumulator_hints());
  environment()->ClearEphemeralHints();
}

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::Environment::ExportRegisterHints(
    interpreter::Register first, size_t count, HintsVector* dst) {
  const int reg_base = first.index();
  for (int i = 0; i < static_cast<int>(count); ++i) {
    dst->push_back(register_hints(interpreter::Register(reg_base + i)));
  }
}

void SerializerForBackgroundCompilation::VisitConstruct(
    BytecodeArrayIterator* iterator) {
  Hints const& new_target = environment()->accumulator_hints();
  Hints const& callee =
      environment()->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 arguments(zone());
  environment()->ExportRegisterHints(first_reg, reg_count, &arguments);

  ProcessCallOrConstruct(callee, new_target, arguments, slot);
}

void SerializerForBackgroundCompilation::VisitConstructWithSpread(
    BytecodeArrayIterator* iterator) {
  Hints const& new_target = environment()->accumulator_hints();
  Hints const& callee =
      environment()->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 arguments(zone());
  environment()->ExportRegisterHints(first_reg, reg_count, &arguments);

  ProcessCallOrConstruct(callee, new_target, arguments, slot, true);
}

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) {
    environment()->accumulator_hints().Clear();
    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()) {
        environment()->accumulator_hints().AddConstant(value->object());
      }
    } else {
      DCHECK(feedback.IsInsufficient());
    }
  }
}

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().Clear();
}

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

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

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().Clear();
  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(environment()->function().feedback_vector(), slot);
  ProcessedFeedback const& feedback =
      broker()->ProcessFeedbackForCompareOperation(source);
  if (BailoutOnUninitialized(feedback)) return;
  environment()->accumulator_hints().Clear();
}

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().Clear();
}

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().Clear();
}

PropertyAccessInfo
SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
    MapRef receiver_map, NameRef const& name, AccessMode access_mode,
    base::Optional<JSObjectRef> receiver, Hints* new_accumulator_hints) {
  // For JSNativeContextSpecialization::InferReceiverRootMap
  receiver_map.SerializeRootMap();

  // For JSNativeContextSpecialization::ReduceNamedAccess.
  if (receiver_map.IsMapOfTargetGlobalProxy()) {
    broker()->target_native_context().global_proxy_object().GetPropertyCell(
        name, SerializationPolicy::kSerializeIfNeeded);
  }

  PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo(
      receiver_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());

      // For JSCallReducer::ReduceJSCall.
      function.Serialize();

      // 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();
    }
  }

  // For PropertyAccessBuilder::TryBuildLoadConstantDataField
  if (access_mode == AccessMode::kLoad) {
    if (access_info.IsDataConstant()) {
      base::Optional<JSObjectRef> holder;
      Handle<JSObject> prototype;
      if (access_info.holder().ToHandle(&prototype)) {
        holder = JSObjectRef(broker(), prototype);
      } else {
        CHECK_IMPLIES(receiver.has_value(),
                      receiver->map().equals(receiver_map));
        holder = 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()) {
          new_accumulator_hints->AddConstant(constant->object());
        }
      }
    }
  }

  return access_info;
}

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

void SerializerForBackgroundCompilation::ProcessKeyedPropertyAccess(
    Hints const& 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(zone());
  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().Clear();
    environment()->accumulator_hints().Add(new_accumulator_hints);
  } else {
    DCHECK(new_accumulator_hints.IsEmpty());
  }
}

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(zone());
  switch (feedback.kind()) {
    case ProcessedFeedback::kNamedAccess:
      DCHECK(name.equals(feedback.AsNamedAccess().name()));
      ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode,
                         &new_accumulator_hints);
      break;
    case ProcessedFeedback::kInsufficient:
      break;
    default:
      UNREACHABLE();
  }

  if (access_mode == AccessMode::kLoad) {
    environment()->accumulator_hints().Clear();
    environment()->accumulator_hints().Add(new_accumulator_hints);
  } else {
    DCHECK(new_accumulator_hints.IsEmpty());
  }
}

void SerializerForBackgroundCompilation::ProcessNamedAccess(
    Hints receiver, NamedAccessFeedback const& feedback, AccessMode access_mode,
    Hints* new_accumulator_hints) {
  for (Handle<Map> map : feedback.AsNamedAccess().maps()) {
    MapRef map_ref(broker(), map);
    ProcessMapForNamedPropertyAccess(map_ref, feedback.name(), access_mode,
                                     base::nullopt, new_accumulator_hints);
  }

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

  JSGlobalProxyRef global_proxy =
      broker()->target_native_context().global_proxy_object();
  for (Handle<Object> hint : receiver.constants()) {
    ObjectRef object(broker(), hint);
    if (access_mode == AccessMode::kLoad && object.IsJSObject()) {
      MapRef map_ref = object.AsJSObject().map();
      ProcessMapForNamedPropertyAccess(map_ref, feedback.name(), access_mode,
                                       object.AsJSObject(),
                                       new_accumulator_hints);
    }
    // For JSNativeContextSpecialization::ReduceNamedAccessFromNexus.
    if (object.equals(global_proxy)) {
      // TODO(neis): Record accumulator hint? Also for string.length and maybe
      // more.
      global_proxy.GetPropertyCell(feedback.name(),
                                   SerializationPolicy::kSerializeIfNeeded);
    }
    // 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 (new_accumulator_hints != nullptr && function.has_prototype()) {
        new_accumulator_hints->AddConstant(function.prototype().object());
      }
    }
  }
}

void SerializerForBackgroundCompilation::ProcessElementAccess(
    Hints receiver, Hints 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::InferReceiverRootMap
    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::InferReceiverRootMap
  for (Handle<Map> map : receiver.maps()) {
    MapRef map_ref(broker(), map);
    map_ref.SerializeRootMap();
  }
}

void SerializerForBackgroundCompilation::VisitLdaNamedProperty(
    BytecodeArrayIterator* iterator) {
  Hints const& receiver =
      environment()->register_hints(iterator->GetRegisterOperand(0));
  NameRef name(broker(),
               iterator->GetConstantForIndexOperand(1, broker()->isolate()));
  FeedbackSlot slot = iterator->GetSlotOperand(2);
  ProcessNamedPropertyAccess(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 const& receiver =
      environment()->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 const& receiver =
      environment()->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 const& receiver = environment()->accumulator_hints();
  Hints const& key =
      environment()->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 =
      environment()->register_hints(iterator->GetRegisterOperand(0));
  Hints rhs = environment()->accumulator_hints();
  FeedbackSlot slot = iterator->GetSlotOperand(1);
  Hints new_accumulator_hints(zone());

  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.
  if (!feedback.IsInsufficient()) {
    InstanceOfFeedback const& rhs_feedback = feedback.AsInstanceOf();
    if (rhs_feedback.value().has_value()) {
      Handle<JSObject> constructor = rhs_feedback.value()->object();
      rhs.AddConstant(constructor);
    }
  }

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

  environment()->accumulator_hints().Clear();
  environment()->accumulator_hints().Add(new_accumulator_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 const& receiver =
      environment()->register_hints(iterator->GetRegisterOperand(0));
  Hints const& key =
      environment()->register_hints(iterator->GetRegisterOperand(1));
  FeedbackSlot slot = iterator->GetSlotOperand(2);
  ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStore, true);
}

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

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

#define DEFINE_CLEAR_ENVIRONMENT(name, ...)             \
  void SerializerForBackgroundCompilation::Visit##name( \
      BytecodeArrayIterator* iterator) {                \
    environment()->ClearEphemeralHints();               \
  }
CLEAR_ENVIRONMENT_LIST(DEFINE_CLEAR_ENVIRONMENT)
#undef DEFINE_CLEAR_ENVIRONMENT

#define DEFINE_CLEAR_ACCUMULATOR(name, ...)             \
  void SerializerForBackgroundCompilation::Visit##name( \
      BytecodeArrayIterator* iterator) {                \
    environment()->accumulator_hints().Clear();         \
  }
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()->ClearEphemeralHints();               \
  }
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 CLEAR_ENVIRONMENT_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
