blob: 557a17eeddcb26aeab43127a0678d884689d4479 [file] [log] [blame]
// 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.SerializeSlots();
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;
}
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) {
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