blob: a828010ee14c5fc69aaf45ca6ddd3be1516488b5 [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/compiler/serializer-hints.h"
#include "src/compiler/zone-stats.h"
#include "src/handles/handles-inl.h"
#include "src/ic/call-optimization.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/objects/code.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/shared-function-info-inl.h"
#include "src/zone/zone-containers.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace compiler {
#define KILL_ENVIRONMENT_LIST(V) \
V(Abort) \
V(ReThrow) \
V(Throw)
#define CLEAR_ACCUMULATOR_LIST(V) \
V(CallRuntime) \
V(CloneObject) \
V(CreateArrayFromIterable) \
V(CreateEmptyObjectLiteral) \
V(CreateMappedArguments) \
V(CreateRestParameter) \
V(CreateUnmappedArguments) \
V(DeletePropertySloppy) \
V(DeletePropertyStrict) \
V(ForInContinue) \
V(ForInEnumerate) \
V(ForInStep) \
V(LogicalNot) \
V(SetPendingMessage) \
V(TestNull) \
V(TestReferenceEqual) \
V(TestTypeOf) \
V(TestUndefined) \
V(TestUndetectable) \
V(ToBooleanLogicalNot) \
V(ToName) \
V(ToString) \
V(TypeOf)
#define UNCONDITIONAL_JUMPS_LIST(V) \
V(Jump) \
V(JumpConstant) \
V(JumpLoop)
#define CONDITIONAL_JUMPS_LIST(V) \
V(JumpIfFalse) \
V(JumpIfFalseConstant) \
V(JumpIfJSReceiver) \
V(JumpIfJSReceiverConstant) \
V(JumpIfNotNull) \
V(JumpIfNotNullConstant) \
V(JumpIfNotUndefined) \
V(JumpIfNotUndefinedConstant) \
V(JumpIfNull) \
V(JumpIfNullConstant) \
V(JumpIfToBooleanFalse) \
V(JumpIfToBooleanFalseConstant) \
V(JumpIfToBooleanTrue) \
V(JumpIfToBooleanTrueConstant) \
V(JumpIfTrue) \
V(JumpIfTrueConstant) \
V(JumpIfUndefined) \
V(JumpIfUndefinedConstant) \
V(JumpIfUndefinedOrNull) \
V(JumpIfUndefinedOrNullConstant)
#define IGNORED_BYTECODE_LIST(V) \
V(CallRuntimeForPair) \
V(CollectTypeProfile) \
V(DebugBreak0) \
V(DebugBreak1) \
V(DebugBreak2) \
V(DebugBreak3) \
V(DebugBreak4) \
V(DebugBreak5) \
V(DebugBreak6) \
V(DebugBreakExtraWide) \
V(DebugBreakWide) \
V(Debugger) \
V(IncBlockCounter) \
V(ResumeGenerator) \
V(SuspendGenerator) \
V(ThrowSuperAlreadyCalledIfNotHole) \
V(ThrowSuperNotCalledIfHole) \
V(ToObject)
#define UNREACHABLE_BYTECODE_LIST(V) \
V(ExtraWide) \
V(Illegal) \
V(Wide)
#define BINARY_OP_LIST(V) \
V(Add) \
V(AddSmi) \
V(BitwiseAnd) \
V(BitwiseAndSmi) \
V(BitwiseOr) \
V(BitwiseOrSmi) \
V(BitwiseXor) \
V(BitwiseXorSmi) \
V(Div) \
V(DivSmi) \
V(Exp) \
V(ExpSmi) \
V(Mod) \
V(ModSmi) \
V(Mul) \
V(MulSmi) \
V(ShiftLeft) \
V(ShiftLeftSmi) \
V(ShiftRight) \
V(ShiftRightSmi) \
V(ShiftRightLogical) \
V(ShiftRightLogicalSmi) \
V(Sub) \
V(SubSmi)
#define UNARY_OP_LIST(V) \
V(BitwiseNot) \
V(Dec) \
V(Inc) \
V(Negate)
#define COMPARE_OP_LIST(V) \
V(TestEqual) \
V(TestEqualStrict) \
V(TestGreaterThan) \
V(TestGreaterThanOrEqual) \
V(TestLessThan) \
V(TestLessThanOrEqual)
#define SUPPORTED_BYTECODE_LIST(V) \
V(CallAnyReceiver) \
V(CallJSRuntime) \
V(CallNoFeedback) \
V(CallProperty) \
V(CallProperty0) \
V(CallProperty1) \
V(CallProperty2) \
V(CallUndefinedReceiver) \
V(CallUndefinedReceiver0) \
V(CallUndefinedReceiver1) \
V(CallUndefinedReceiver2) \
V(CallWithSpread) \
V(Construct) \
V(ConstructWithSpread) \
V(CreateArrayLiteral) \
V(CreateBlockContext) \
V(CreateCatchContext) \
V(CreateClosure) \
V(CreateEmptyArrayLiteral) \
V(CreateEvalContext) \
V(CreateFunctionContext) \
V(CreateObjectLiteral) \
V(CreateRegExpLiteral) \
V(CreateWithContext) \
V(ForInNext) \
V(ForInPrepare) \
V(GetIterator) \
V(GetSuperConstructor) \
V(GetTemplateObject) \
V(InvokeIntrinsic) \
V(LdaConstant) \
V(LdaContextSlot) \
V(LdaCurrentContextSlot) \
V(LdaImmutableContextSlot) \
V(LdaImmutableCurrentContextSlot) \
V(LdaModuleVariable) \
V(LdaFalse) \
V(LdaGlobal) \
V(LdaGlobalInsideTypeof) \
V(LdaKeyedProperty) \
V(LdaLookupContextSlot) \
V(LdaLookupContextSlotInsideTypeof) \
V(LdaLookupGlobalSlot) \
V(LdaLookupGlobalSlotInsideTypeof) \
V(LdaLookupSlot) \
V(LdaLookupSlotInsideTypeof) \
V(LdaNamedProperty) \
V(LdaNamedPropertyNoFeedback) \
V(LdaNull) \
V(Ldar) \
V(LdaSmi) \
V(LdaTheHole) \
V(LdaTrue) \
V(LdaUndefined) \
V(LdaZero) \
V(Mov) \
V(PopContext) \
V(PushContext) \
V(Return) \
V(StaContextSlot) \
V(StaCurrentContextSlot) \
V(StaDataPropertyInLiteral) \
V(StaGlobal) \
V(StaInArrayLiteral) \
V(StaKeyedProperty) \
V(StaLookupSlot) \
V(StaModuleVariable) \
V(StaNamedOwnProperty) \
V(StaNamedProperty) \
V(StaNamedPropertyNoFeedback) \
V(Star) \
V(SwitchOnGeneratorState) \
V(SwitchOnSmiNoFeedback) \
V(TestIn) \
V(TestInstanceOf) \
V(ThrowReferenceErrorIfHole) \
V(ToNumber) \
V(ToNumeric) \
BINARY_OP_LIST(V) \
COMPARE_OP_LIST(V) \
CLEAR_ACCUMULATOR_LIST(V) \
CONDITIONAL_JUMPS_LIST(V) \
IGNORED_BYTECODE_LIST(V) \
KILL_ENVIRONMENT_LIST(V) \
UNARY_OP_LIST(V) \
UNCONDITIONAL_JUMPS_LIST(V) \
UNREACHABLE_BYTECODE_LIST(V)
struct HintsImpl : public ZoneObject {
explicit HintsImpl(Zone* zone) : zone_(zone) {}
ConstantsSet constants_;
MapsSet maps_;
VirtualClosuresSet virtual_closures_;
VirtualContextsSet virtual_contexts_;
VirtualBoundFunctionsSet virtual_bound_functions_;
Zone* const zone_;
};
void Hints::EnsureAllocated(Zone* zone, bool check_zone_equality) {
if (IsAllocated()) {
if (check_zone_equality) CHECK_EQ(zone, impl_->zone_);
// ... else {zone} lives no longer than {impl_->zone_} but we have no way of
// checking that.
} else {
impl_ = zone->New<HintsImpl>(zone);
}
DCHECK(IsAllocated());
}
struct VirtualBoundFunction {
Hints const bound_target;
HintsVector const bound_arguments;
VirtualBoundFunction(Hints const& target, const HintsVector& arguments)
: bound_target(target), bound_arguments(arguments) {}
bool operator==(const VirtualBoundFunction& other) const {
if (bound_arguments.size() != other.bound_arguments.size()) return false;
if (bound_target != other.bound_target) return false;
for (size_t i = 0; i < bound_arguments.size(); ++i) {
if (bound_arguments[i] != other.bound_arguments[i]) return false;
}
return true;
}
};
// A VirtualClosure is a SharedFunctionInfo and a FeedbackVector, plus
// Hints about the context in which a closure will be created from them.
class VirtualClosure {
public:
VirtualClosure(Handle<JSFunction> function, Isolate* isolate, Zone* zone);
VirtualClosure(Handle<SharedFunctionInfo> shared,
Handle<FeedbackVector> feedback_vector,
Hints const& context_hints);
Handle<SharedFunctionInfo> shared() const { return shared_; }
Handle<FeedbackVector> feedback_vector() const { return feedback_vector_; }
Hints const& context_hints() const { return context_hints_; }
bool operator==(const VirtualClosure& other) const {
// A feedback vector is never used for more than one SFI. There might,
// however, be two virtual closures with the same SFI and vector, but
// different context hints. crbug.com/1024282 has a link to a document
// describing why the context_hints_ might be different in that case.
DCHECK_IMPLIES(feedback_vector_.equals(other.feedback_vector_),
shared_.equals(other.shared_));
return feedback_vector_.equals(other.feedback_vector_) &&
context_hints_ == other.context_hints_;
}
private:
Handle<SharedFunctionInfo> const shared_;
Handle<FeedbackVector> const feedback_vector_;
Hints const context_hints_;
};
// A CompilationSubject is a VirtualClosure, optionally with a matching
// concrete closure.
class CompilationSubject {
public:
explicit CompilationSubject(VirtualClosure virtual_closure)
: virtual_closure_(virtual_closure), closure_() {}
// The zone parameter is to correctly initialize the virtual closure,
// which contains zone-allocated context information.
CompilationSubject(Handle<JSFunction> closure, Isolate* isolate, Zone* zone);
const VirtualClosure& virtual_closure() const { return virtual_closure_; }
MaybeHandle<JSFunction> closure() const { return closure_; }
private:
VirtualClosure const virtual_closure_;
MaybeHandle<JSFunction> const closure_;
};
// A Callee is either a JSFunction (which may not have a feedback vector), or a
// VirtualClosure. Note that this is different from CompilationSubject, which
// always has a VirtualClosure.
class Callee {
public:
explicit Callee(Handle<JSFunction> jsfunction)
: jsfunction_(jsfunction), virtual_closure_() {}
explicit Callee(VirtualClosure const& virtual_closure)
: jsfunction_(), virtual_closure_(virtual_closure) {}
Handle<SharedFunctionInfo> shared(Isolate* isolate) const {
return virtual_closure_.has_value()
? virtual_closure_->shared()
: handle(jsfunction_.ToHandleChecked()->shared(), isolate);
}
bool HasFeedbackVector() const {
Handle<JSFunction> function;
return virtual_closure_.has_value() ||
jsfunction_.ToHandleChecked()->has_feedback_vector();
}
CompilationSubject ToCompilationSubject(Isolate* isolate, Zone* zone) const {
CHECK(HasFeedbackVector());
return virtual_closure_.has_value()
? CompilationSubject(*virtual_closure_)
: CompilationSubject(jsfunction_.ToHandleChecked(), isolate,
zone);
}
private:
MaybeHandle<JSFunction> const jsfunction_;
base::Optional<VirtualClosure> const virtual_closure_;
};
// If a list of arguments (hints) is shorter than the function's parameter
// count, this enum expresses what we know about the missing arguments.
enum MissingArgumentsPolicy {
kMissingArgumentsAreUndefined, // ... as in the JS undefined value
kMissingArgumentsAreUnknown,
};
// The SerializerForBackgroundCompilation makes sure that the relevant function
// data such as bytecode, SharedFunctionInfo and FeedbackVector, used by later
// optimizations in the compiler, is copied to the heap broker.
class SerializerForBackgroundCompilation {
public:
SerializerForBackgroundCompilation(
ZoneStats* zone_stats, JSHeapBroker* broker,
CompilationDependencies* dependencies, Handle<JSFunction> closure,
SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset);
Hints Run(); // NOTE: Returns empty for an
// already-serialized function.
class Environment;
private:
SerializerForBackgroundCompilation(
ZoneStats* zone_stats, JSHeapBroker* broker,
CompilationDependencies* dependencies, CompilationSubject function,
base::Optional<Hints> new_target, const HintsVector& arguments,
MissingArgumentsPolicy padding,
SerializerForBackgroundCompilationFlags flags, int nesting_level);
bool BailoutOnUninitialized(ProcessedFeedback const& feedback);
void TraverseBytecode();
#define DECLARE_VISIT_BYTECODE(name, ...) \
void Visit##name(interpreter::BytecodeArrayIterator* iterator);
SUPPORTED_BYTECODE_LIST(DECLARE_VISIT_BYTECODE)
#undef DECLARE_VISIT_BYTECODE
Hints& register_hints(interpreter::Register reg);
// Return a vector containing the hints for the given register range (in
// order). Also prepare these hints for feedback backpropagation by allocating
// any that aren't yet allocated.
HintsVector PrepareArgumentsHints(interpreter::Register first, size_t count);
// Like above except that the hints have to be given directly.
template <typename... MoreHints>
HintsVector PrepareArgumentsHints(Hints* hints, MoreHints... more);
void ProcessCalleeForCallOrConstruct(Callee const& callee,
base::Optional<Hints> new_target,
const HintsVector& arguments,
SpeculationMode speculation_mode,
MissingArgumentsPolicy padding,
Hints* result_hints);
void ProcessCalleeForCallOrConstruct(Handle<Object> callee,
base::Optional<Hints> new_target,
const HintsVector& arguments,
SpeculationMode speculation_mode,
MissingArgumentsPolicy padding,
Hints* result_hints);
void ProcessCallOrConstruct(Hints callee, base::Optional<Hints> new_target,
HintsVector* arguments, FeedbackSlot slot,
MissingArgumentsPolicy padding);
void ProcessCallOrConstructRecursive(Hints const& callee,
base::Optional<Hints> new_target,
const HintsVector& arguments,
SpeculationMode speculation_mode,
MissingArgumentsPolicy padding,
Hints* result_hints);
void ProcessNewTargetForConstruct(Hints const& new_target,
Hints* result_hints);
void ProcessCallVarArgs(
ConvertReceiverMode receiver_mode, Hints const& callee,
interpreter::Register first_reg, int reg_count, FeedbackSlot slot,
MissingArgumentsPolicy padding = kMissingArgumentsAreUndefined);
void ProcessApiCall(Handle<SharedFunctionInfo> target,
const HintsVector& arguments);
void ProcessReceiverMapForApiCall(FunctionTemplateInfoRef target,
Handle<Map> receiver);
void ProcessBuiltinCall(Handle<SharedFunctionInfo> target,
base::Optional<Hints> new_target,
const HintsVector& arguments,
SpeculationMode speculation_mode,
MissingArgumentsPolicy padding, Hints* result_hints);
void ProcessJump(interpreter::BytecodeArrayIterator* iterator);
void ProcessKeyedPropertyAccess(Hints* receiver, Hints const& key,
FeedbackSlot slot, AccessMode access_mode,
bool honor_bailout_on_uninitialized);
void ProcessNamedPropertyAccess(Hints* receiver, NameRef const& name,
FeedbackSlot slot, AccessMode access_mode);
void ProcessNamedAccess(Hints* receiver, NamedAccessFeedback const& feedback,
AccessMode access_mode, Hints* result_hints);
void ProcessElementAccess(Hints const& receiver, Hints const& key,
ElementAccessFeedback const& feedback,
AccessMode access_mode);
void ProcessMinimorphicPropertyAccess(
MinimorphicLoadPropertyAccessFeedback const& feedback,
FeedbackSource const& source);
void ProcessModuleVariableAccess(
interpreter::BytecodeArrayIterator* iterator);
void ProcessHintsForObjectCreate(Hints const& prototype);
void ProcessMapHintsForPromises(Hints const& receiver_hints);
void ProcessHintsForPromiseResolve(Hints const& resolution_hints);
void ProcessHintsForHasInPrototypeChain(Hints const& instance_hints);
void ProcessHintsForRegExpTest(Hints const& regexp_hints);
PropertyAccessInfo ProcessMapForRegExpTest(MapRef map);
void ProcessHintsForFunctionBind(Hints const& receiver_hints);
void ProcessHintsForObjectGetPrototype(Hints const& object_hints);
void ProcessConstantForOrdinaryHasInstance(HeapObjectRef const& constructor,
bool* walk_prototypes);
void ProcessConstantForInstanceOf(ObjectRef const& constant,
bool* walk_prototypes);
void ProcessHintsForOrdinaryHasInstance(Hints const& constructor_hints,
Hints const& instance_hints);
void ProcessGlobalAccess(FeedbackSlot slot, bool is_load);
void ProcessCompareOperation(FeedbackSlot slot);
void ProcessForIn(FeedbackSlot slot);
void ProcessUnaryOrBinaryOperation(FeedbackSlot slot,
bool honor_bailout_on_uninitialized);
PropertyAccessInfo ProcessMapForNamedPropertyAccess(
Hints* receiver, MapRef receiver_map, NameRef const& name,
AccessMode access_mode, base::Optional<JSObjectRef> concrete_receiver,
Hints* result_hints);
void ProcessCreateContext(interpreter::BytecodeArrayIterator* iterator,
int scopeinfo_operand_index);
enum ContextProcessingMode {
kIgnoreSlot,
kSerializeSlot,
};
void ProcessContextAccess(Hints const& context_hints, int slot, int depth,
ContextProcessingMode mode,
Hints* result_hints = nullptr);
void ProcessImmutableLoad(ContextRef const& context, int slot,
ContextProcessingMode mode,
Hints* new_accumulator_hints);
void ProcessLdaLookupGlobalSlot(interpreter::BytecodeArrayIterator* iterator);
void ProcessLdaLookupContextSlot(
interpreter::BytecodeArrayIterator* iterator);
// Performs extension lookups for [0, depth) like
// BytecodeGraphBuilder::CheckContextExtensions().
void ProcessCheckContextExtensions(int depth);
Hints RunChildSerializer(CompilationSubject function,
base::Optional<Hints> new_target,
const HintsVector& arguments,
MissingArgumentsPolicy padding);
// When (forward-)branching bytecodes are encountered, e.g. a conditional
// jump, we call ContributeToJumpTargetEnvironment to "remember" the current
// environment, associated with the jump target offset. When serialization
// eventually reaches that offset, we call IncorporateJumpTargetEnvironment to
// merge that environment back into whatever is the current environment then.
// Note: Since there may be multiple jumps to the same target,
// ContributeToJumpTargetEnvironment may actually do a merge as well.
void ContributeToJumpTargetEnvironment(int target_offset);
void IncorporateJumpTargetEnvironment(int target_offset);
VirtualClosure function() const { return function_; }
Hints& return_value_hints() { return return_value_hints_; }
Handle<FeedbackVector> feedback_vector() const;
Handle<BytecodeArray> bytecode_array() const;
BytecodeAnalysis const& GetBytecodeAnalysis(
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
JSHeapBroker* broker() const { return broker_; }
CompilationDependencies* dependencies() const { return dependencies_; }
Zone* zone() { return zone_scope_.zone(); }
Environment* environment() const { return environment_; }
SerializerForBackgroundCompilationFlags flags() const { return flags_; }
BailoutId osr_offset() const { return osr_offset_; }
JSHeapBroker* const broker_;
CompilationDependencies* const dependencies_;
ZoneStats::Scope zone_scope_;
SerializerForBackgroundCompilationFlags const flags_;
// Instead of storing the virtual_closure here, we could extract it from the
// {closure_hints_} but that would be cumbersome.
VirtualClosure const function_;
BailoutId const osr_offset_;
ZoneUnorderedMap<int, Environment*> jump_target_environments_;
Environment* const environment_;
HintsVector const arguments_;
Hints return_value_hints_;
Hints closure_hints_;
int nesting_level_ = 0;
};
void RunSerializerForBackgroundCompilation(
ZoneStats* zone_stats, JSHeapBroker* broker,
CompilationDependencies* dependencies, Handle<JSFunction> closure,
SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset) {
SerializerForBackgroundCompilation serializer(
zone_stats, broker, dependencies, closure, flags, osr_offset);
serializer.Run();
}
using BytecodeArrayIterator = interpreter::BytecodeArrayIterator;
VirtualClosure::VirtualClosure(Handle<SharedFunctionInfo> shared,
Handle<FeedbackVector> feedback_vector,
Hints const& context_hints)
: shared_(shared),
feedback_vector_(feedback_vector),
context_hints_(context_hints) {
// The checked invariant rules out recursion and thus avoids complexity.
CHECK(context_hints_.virtual_closures().IsEmpty());
}
VirtualClosure::VirtualClosure(Handle<JSFunction> function, Isolate* isolate,
Zone* zone)
: shared_(handle(function->shared(), isolate)),
feedback_vector_(function->feedback_vector(), isolate),
context_hints_(
Hints::SingleConstant(handle(function->context(), isolate), zone)) {
// The checked invariant rules out recursion and thus avoids complexity.
CHECK(context_hints_.virtual_closures().IsEmpty());
}
CompilationSubject::CompilationSubject(Handle<JSFunction> closure,
Isolate* isolate, Zone* zone)
: virtual_closure_(closure, isolate, zone), closure_(closure) {
CHECK(closure->has_feedback_vector());
}
Hints Hints::Copy(Zone* zone) const {
if (!IsAllocated()) return *this;
Hints result;
result.EnsureAllocated(zone);
result.impl_->constants_ = impl_->constants_;
result.impl_->maps_ = impl_->maps_;
result.impl_->virtual_contexts_ = impl_->virtual_contexts_;
result.impl_->virtual_closures_ = impl_->virtual_closures_;
result.impl_->virtual_bound_functions_ = impl_->virtual_bound_functions_;
return result;
}
bool Hints::operator==(Hints const& other) const {
if (impl_ == other.impl_) return true;
if (IsEmpty() && other.IsEmpty()) return true;
return IsAllocated() && other.IsAllocated() &&
constants() == other.constants() &&
virtual_closures() == other.virtual_closures() &&
maps() == other.maps() &&
virtual_contexts() == other.virtual_contexts() &&
virtual_bound_functions() == other.virtual_bound_functions();
}
bool Hints::operator!=(Hints const& other) const { return !(*this == other); }
#ifdef ENABLE_SLOW_DCHECKS
bool Hints::Includes(Hints const& other) const {
if (impl_ == other.impl_ || other.IsEmpty()) return true;
return IsAllocated() && constants().Includes(other.constants()) &&
virtual_closures().Includes(other.virtual_closures()) &&
maps().Includes(other.maps());
}
#endif
Hints Hints::SingleConstant(Handle<Object> constant, Zone* zone) {
Hints result;
result.AddConstant(constant, zone, nullptr);
return result;
}
Hints Hints::SingleMap(Handle<Map> map, Zone* zone) {
Hints result;
result.AddMap(map, zone, nullptr);
return result;
}
ConstantsSet Hints::constants() const {
return IsAllocated() ? impl_->constants_ : ConstantsSet();
}
MapsSet Hints::maps() const { return IsAllocated() ? impl_->maps_ : MapsSet(); }
VirtualClosuresSet Hints::virtual_closures() const {
return IsAllocated() ? impl_->virtual_closures_ : VirtualClosuresSet();
}
VirtualContextsSet Hints::virtual_contexts() const {
return IsAllocated() ? impl_->virtual_contexts_ : VirtualContextsSet();
}
VirtualBoundFunctionsSet Hints::virtual_bound_functions() const {
return IsAllocated() ? impl_->virtual_bound_functions_
: VirtualBoundFunctionsSet();
}
void Hints::AddVirtualContext(VirtualContext const& virtual_context, Zone* zone,
JSHeapBroker* broker) {
EnsureAllocated(zone);
if (impl_->virtual_contexts_.Size() >= kMaxHintsSize) {
TRACE_BROKER_MISSING(broker,
"opportunity - limit for virtual contexts reached.");
return;
}
impl_->virtual_contexts_.Add(virtual_context, impl_->zone_);
}
void Hints::AddConstant(Handle<Object> constant, Zone* zone,
JSHeapBroker* broker) {
EnsureAllocated(zone);
if (impl_->constants_.Size() >= kMaxHintsSize) {
TRACE_BROKER_MISSING(broker, "opportunity - limit for constants reached.");
return;
}
impl_->constants_.Add(constant, impl_->zone_);
}
void Hints::AddMap(Handle<Map> map, Zone* zone, JSHeapBroker* broker,
bool check_zone_equality) {
EnsureAllocated(zone, check_zone_equality);
if (impl_->maps_.Size() >= kMaxHintsSize) {
TRACE_BROKER_MISSING(broker, "opportunity - limit for maps reached.");
return;
}
impl_->maps_.Add(map, impl_->zone_);
}
void Hints::AddVirtualClosure(VirtualClosure const& virtual_closure, Zone* zone,
JSHeapBroker* broker) {
EnsureAllocated(zone);
if (impl_->virtual_closures_.Size() >= kMaxHintsSize) {
TRACE_BROKER_MISSING(broker,
"opportunity - limit for virtual closures reached.");
return;
}
impl_->virtual_closures_.Add(virtual_closure, impl_->zone_);
}
void Hints::AddVirtualBoundFunction(VirtualBoundFunction const& bound_function,
Zone* zone, JSHeapBroker* broker) {
EnsureAllocated(zone);
if (impl_->virtual_bound_functions_.Size() >= kMaxHintsSize) {
TRACE_BROKER_MISSING(
broker, "opportunity - limit for virtual bound functions reached.");
return;
}
// TODO(mslekova): Consider filtering the hints in the added bound function,
// for example: a) Remove any non-JS(Bound)Function constants, b) Truncate the
// argument vector the formal parameter count.
impl_->virtual_bound_functions_.Add(bound_function, impl_->zone_);
}
void Hints::Add(Hints const& other, Zone* zone, JSHeapBroker* broker) {
if (impl_ == other.impl_ || other.IsEmpty()) return;
EnsureAllocated(zone);
if (!Union(other)) {
TRACE_BROKER_MISSING(broker, "opportunity - hints limit reached.");
}
}
Hints Hints::CopyToParentZone(Zone* zone, JSHeapBroker* broker) const {
if (!IsAllocated()) return *this;
Hints result;
for (auto const& x : constants()) result.AddConstant(x, zone, broker);
for (auto const& x : maps()) result.AddMap(x, zone, broker);
for (auto const& x : virtual_contexts())
result.AddVirtualContext(x, zone, broker);
// Adding hints from a child serializer run means copying data out from
// a zone that's being destroyed. VirtualClosures and VirtualBoundFunction
// have zone allocated data, so we've got to make a deep copy to eliminate
// traces of the dying zone.
for (auto const& x : virtual_closures()) {
VirtualClosure new_virtual_closure(
x.shared(), x.feedback_vector(),
x.context_hints().CopyToParentZone(zone, broker));
result.AddVirtualClosure(new_virtual_closure, zone, broker);
}
for (auto const& x : virtual_bound_functions()) {
HintsVector new_arguments_hints(zone);
for (auto hint : x.bound_arguments) {
new_arguments_hints.push_back(hint.CopyToParentZone(zone, broker));
}
VirtualBoundFunction new_bound_function(
x.bound_target.CopyToParentZone(zone, broker), new_arguments_hints);
result.AddVirtualBoundFunction(new_bound_function, zone, broker);
}
return result;
}
bool Hints::IsEmpty() const {
if (!IsAllocated()) return true;
return constants().IsEmpty() && maps().IsEmpty() &&
virtual_closures().IsEmpty() && virtual_contexts().IsEmpty() &&
virtual_bound_functions().IsEmpty();
}
std::ostream& operator<<(std::ostream& out,
const VirtualContext& virtual_context) {
out << "Distance " << virtual_context.distance << " from "
<< Brief(*virtual_context.context) << std::endl;
return out;
}
std::ostream& operator<<(std::ostream& out, const Hints& hints);
std::ostream& operator<<(std::ostream& out,
const VirtualClosure& virtual_closure) {
out << Brief(*virtual_closure.shared()) << std::endl;
out << Brief(*virtual_closure.feedback_vector()) << std::endl;
!virtual_closure.context_hints().IsEmpty() &&
out << virtual_closure.context_hints() << "):" << std::endl;
return out;
}
std::ostream& operator<<(std::ostream& out,
const VirtualBoundFunction& virtual_bound_function) {
out << std::endl << " Target: " << virtual_bound_function.bound_target;
out << " Arguments:" << std::endl;
for (auto hint : virtual_bound_function.bound_arguments) {
out << " " << hint;
}
return out;
}
std::ostream& operator<<(std::ostream& out, const Hints& hints) {
out << "(impl_ = " << hints.impl_ << ")\n";
for (Handle<Object> constant : hints.constants()) {
out << " constant " << Brief(*constant) << std::endl;
}
for (Handle<Map> map : hints.maps()) {
out << " map " << Brief(*map) << std::endl;
}
for (VirtualClosure const& virtual_closure : hints.virtual_closures()) {
out << " virtual closure " << virtual_closure << std::endl;
}
for (VirtualContext const& virtual_context : hints.virtual_contexts()) {
out << " virtual context " << virtual_context << std::endl;
}
for (VirtualBoundFunction const& virtual_bound_function :
hints.virtual_bound_functions()) {
out << " virtual bound function " << virtual_bound_function << std::endl;
}
return out;
}
void Hints::Reset(Hints* other, Zone* zone) {
other->EnsureShareable(zone);
*this = *other;
DCHECK(IsAllocated());
}
class SerializerForBackgroundCompilation::Environment : public ZoneObject {
public:
Environment(Zone* zone, CompilationSubject function);
Environment(Zone* zone, Isolate* isolate, CompilationSubject function,
base::Optional<Hints> new_target, const HintsVector& arguments,
MissingArgumentsPolicy padding);
bool IsDead() const { return !alive_; }
void Kill() {
DCHECK(!IsDead());
alive_ = false;
DCHECK(IsDead());
}
void Resurrect() {
DCHECK(IsDead());
alive_ = true;
DCHECK(!IsDead());
}
// Merge {other} into {this} environment (leaving {other} unmodified).
void Merge(Environment* other, Zone* zone, JSHeapBroker* broker);
Hints const& current_context_hints() const { return current_context_hints_; }
Hints const& accumulator_hints() const { return accumulator_hints_; }
Hints& current_context_hints() { return current_context_hints_; }
Hints& accumulator_hints() { return accumulator_hints_; }
Hints& register_hints(interpreter::Register reg);
private:
friend std::ostream& operator<<(std::ostream& out, const Environment& env);
Hints current_context_hints_;
Hints accumulator_hints_;
HintsVector parameters_hints_; // First parameter is the receiver.
HintsVector locals_hints_;
bool alive_ = true;
};
SerializerForBackgroundCompilation::Environment::Environment(
Zone* zone, CompilationSubject function)
: parameters_hints_(function.virtual_closure()
.shared()
->GetBytecodeArray()
.parameter_count(),
Hints(), zone),
locals_hints_(function.virtual_closure()
.shared()
->GetBytecodeArray()
.register_count(),
Hints(), zone) {
// Consume the virtual_closure's context hint information.
current_context_hints_ = function.virtual_closure().context_hints();
}
SerializerForBackgroundCompilation::Environment::Environment(
Zone* zone, Isolate* isolate, CompilationSubject function,
base::Optional<Hints> new_target, const HintsVector& arguments,
MissingArgumentsPolicy padding)
: Environment(zone, function) {
// Set the hints for the actually passed arguments, at most up to
// the parameter_count.
for (size_t i = 0; i < std::min(arguments.size(), parameters_hints_.size());
++i) {
parameters_hints_[i] = arguments[i];
}
if (padding == kMissingArgumentsAreUndefined) {
Hints const undefined_hint =
Hints::SingleConstant(isolate->factory()->undefined_value(), zone);
for (size_t i = arguments.size(); i < parameters_hints_.size(); ++i) {
parameters_hints_[i] = undefined_hint;
}
} else {
DCHECK_EQ(padding, kMissingArgumentsAreUnknown);
}
// Set hints for new_target.
interpreter::Register new_target_reg =
function.virtual_closure()
.shared()
->GetBytecodeArray()
.incoming_new_target_or_generator_register();
if (new_target_reg.is_valid()) {
Hints& hints = register_hints(new_target_reg);
CHECK(hints.IsEmpty());
if (new_target.has_value()) hints = *new_target;
}
}
Hints& SerializerForBackgroundCompilation::register_hints(
interpreter::Register reg) {
if (reg.is_function_closure()) return closure_hints_;
return environment()->register_hints(reg);
}
Hints& SerializerForBackgroundCompilation::Environment::register_hints(
interpreter::Register reg) {
if (reg.is_current_context()) return current_context_hints_;
if (reg.is_parameter()) {
return parameters_hints_[reg.ToParameterIndex(
static_cast<int>(parameters_hints_.size()))];
}
DCHECK(!reg.is_function_closure());
CHECK_LT(reg.index(), locals_hints_.size());
return locals_hints_[reg.index()];
}
void SerializerForBackgroundCompilation::Environment::Merge(
Environment* other, Zone* zone, JSHeapBroker* broker) {
// {other} is guaranteed to have the same layout because it comes from an
// earlier bytecode in the same function.
DCHECK_EQ(parameters_hints_.size(), other->parameters_hints_.size());
DCHECK_EQ(locals_hints_.size(), other->locals_hints_.size());
if (IsDead()) {
parameters_hints_ = other->parameters_hints_;
locals_hints_ = other->locals_hints_;
current_context_hints_ = other->current_context_hints_;
accumulator_hints_ = other->accumulator_hints_;
Resurrect();
} else {
for (size_t i = 0; i < parameters_hints_.size(); ++i) {
parameters_hints_[i].Merge(other->parameters_hints_[i], zone, broker);
}
for (size_t i = 0; i < locals_hints_.size(); ++i) {
locals_hints_[i].Merge(other->locals_hints_[i], zone, broker);
}
current_context_hints_.Merge(other->current_context_hints_, zone, broker);
accumulator_hints_.Merge(other->accumulator_hints_, zone, broker);
}
CHECK(!IsDead());
}
bool Hints::Union(Hints const& other) {
CHECK(IsAllocated());
if (impl_->constants_.Size() + other.constants().Size() > kMaxHintsSize ||
impl_->maps_.Size() + other.maps().Size() > kMaxHintsSize ||
impl_->virtual_closures_.Size() + other.virtual_closures().Size() >
kMaxHintsSize ||
impl_->virtual_contexts_.Size() + other.virtual_contexts().Size() >
kMaxHintsSize ||
impl_->virtual_bound_functions_.Size() +
other.virtual_bound_functions().Size() >
kMaxHintsSize) {
return false;
}
Zone* zone = impl_->zone_;
impl_->constants_.Union(other.constants(), zone);
impl_->maps_.Union(other.maps(), zone);
impl_->virtual_closures_.Union(other.virtual_closures(), zone);
impl_->virtual_contexts_.Union(other.virtual_contexts(), zone);
impl_->virtual_bound_functions_.Union(other.virtual_bound_functions(), zone);
return true;
}
void Hints::Merge(Hints const& other, Zone* zone, JSHeapBroker* broker) {
if (impl_ == other.impl_) {
return;
}
if (!IsAllocated()) {
*this = other.Copy(zone);
DCHECK(IsAllocated());
return;
}
*this = this->Copy(zone);
if (!Union(other)) {
TRACE_BROKER_MISSING(broker, "opportunity - hints limit reached.");
}
DCHECK(IsAllocated());
}
std::ostream& operator<<(
std::ostream& out,
const SerializerForBackgroundCompilation::Environment& env) {
std::ostringstream output_stream;
if (env.IsDead()) {
output_stream << "dead\n";
} else {
output_stream << "alive\n";
for (size_t i = 0; i < env.parameters_hints_.size(); ++i) {
Hints const& hints = env.parameters_hints_[i];
if (!hints.IsEmpty()) {
if (i == 0) {
output_stream << "Hints for <this>: ";
} else {
output_stream << "Hints for a" << i - 1 << ": ";
}
output_stream << hints;
}
}
for (size_t i = 0; i < env.locals_hints_.size(); ++i) {
Hints const& hints = env.locals_hints_[i];
if (!hints.IsEmpty()) {
output_stream << "Hints for r" << i << ": " << hints;
}
}
}
if (!env.current_context_hints().IsEmpty()) {
output_stream << "Hints for <context>: " << env.current_context_hints();
}
if (!env.accumulator_hints().IsEmpty()) {
output_stream << "Hints for <accumulator>: " << env.accumulator_hints();
}
out << output_stream.str();
return out;
}
SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
ZoneStats* zone_stats, JSHeapBroker* broker,
CompilationDependencies* dependencies, Handle<JSFunction> closure,
SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset)
: broker_(broker),
dependencies_(dependencies),
zone_scope_(zone_stats, ZONE_NAME),
flags_(flags),
function_(closure, broker->isolate(), zone()),
osr_offset_(osr_offset),
jump_target_environments_(zone()),
environment_(zone()->New<Environment>(
zone(), CompilationSubject(closure, broker_->isolate(), zone()))),
arguments_(zone()) {
closure_hints_.AddConstant(closure, zone(), broker_);
JSFunctionRef(broker, closure).Serialize();
TRACE_BROKER(broker_, "Hints for <closure>: " << closure_hints_);
TRACE_BROKER(broker_, "Initial environment:\n" << *environment_);
}
SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
ZoneStats* zone_stats, JSHeapBroker* broker,
CompilationDependencies* dependencies, CompilationSubject function,
base::Optional<Hints> new_target, const HintsVector& arguments,
MissingArgumentsPolicy padding,
SerializerForBackgroundCompilationFlags flags, int nesting_level)
: broker_(broker),
dependencies_(dependencies),
zone_scope_(zone_stats, ZONE_NAME),
flags_(flags),
function_(function.virtual_closure()),
osr_offset_(BailoutId::None()),
jump_target_environments_(zone()),
environment_(zone()->New<Environment>(zone(), broker_->isolate(),
function, new_target, arguments,
padding)),
arguments_(arguments),
nesting_level_(nesting_level) {
Handle<JSFunction> closure;
if (function.closure().ToHandle(&closure)) {
closure_hints_.AddConstant(closure, zone(), broker);
JSFunctionRef(broker, closure).Serialize();
} else {
closure_hints_.AddVirtualClosure(function.virtual_closure(), zone(),
broker);
}
TRACE_BROKER(broker_, "Hints for <closure>: " << closure_hints_);
TRACE_BROKER(broker_, "Initial environment:\n" << *environment_);
}
bool SerializerForBackgroundCompilation::BailoutOnUninitialized(
ProcessedFeedback const& feedback) {
DCHECK(!environment()->IsDead());
if (!(flags() &
SerializerForBackgroundCompilationFlag::kBailoutOnUninitialized)) {
return false;
}
if (!osr_offset().IsNone()) {
// Exclude OSR from this optimization because we might end up skipping the
// OSR entry point. TODO(neis): Support OSR?
return false;
}
if (FLAG_turboprop && feedback.slot_kind() == FeedbackSlotKind::kCall) {
return false;
}
if (feedback.IsInsufficient()) {
environment()->Kill();
return true;
}
return false;
}
Hints SerializerForBackgroundCompilation::Run() {
TraceScope tracer(broker(), this, "SerializerForBackgroundCompilation::Run");
if (nesting_level_ >= FLAG_max_serializer_nesting) {
TRACE_BROKER_MISSING(
broker(),
"opportunity - Reached max nesting level for "
"SerializerForBackgroundCompilation::Run, bailing out.\n");
return Hints();
}
TRACE_BROKER_MEMORY(broker(), "[serializer start] Broker zone usage: "
<< broker()->zone()->allocation_size());
SharedFunctionInfoRef shared(broker(), function().shared());
FeedbackVectorRef feedback_vector_ref(broker(), feedback_vector());
if (!broker()->ShouldBeSerializedForCompilation(shared, feedback_vector_ref,
arguments_)) {
TRACE_BROKER(broker(),
"opportunity - Already ran serializer for SharedFunctionInfo "
<< Brief(*shared.object()) << ", bailing out.\n");
return Hints();
}
{
HintsVector arguments_copy_in_broker_zone(broker()->zone());
for (auto const& hints : arguments_) {
arguments_copy_in_broker_zone.push_back(
hints.CopyToParentZone(broker()->zone(), broker()));
}
broker()->SetSerializedForCompilation(shared, feedback_vector_ref,
arguments_copy_in_broker_zone);
}
// We eagerly call the {EnsureSourcePositionsAvailable} for all serialized
// SFIs while still on the main thread. Source positions will later be used
// by JSInliner::ReduceJSCall.
if (flags() &
SerializerForBackgroundCompilationFlag::kCollectSourcePositions) {
SharedFunctionInfo::EnsureSourcePositionsAvailable(broker()->isolate(),
shared.object());
}
feedback_vector_ref.Serialize();
TraverseBytecode();
if (return_value_hints().IsEmpty()) {
TRACE_BROKER(broker(), "Return value hints: none");
} else {
TRACE_BROKER(broker(), "Return value hints: " << return_value_hints());
}
TRACE_BROKER_MEMORY(broker(), "[serializer end] Broker zone usage: "
<< broker()->zone()->allocation_size());
return return_value_hints();
}
class HandlerRangeMatcher {
public:
HandlerRangeMatcher(BytecodeArrayIterator const& bytecode_iterator,
Handle<BytecodeArray> bytecode_array)
: bytecode_iterator_(bytecode_iterator) {
HandlerTable table(*bytecode_array);
for (int i = 0, n = table.NumberOfRangeEntries(); i < n; ++i) {
ranges_.insert({table.GetRangeStart(i), table.GetRangeEnd(i),
table.GetRangeHandler(i)});
}
ranges_iterator_ = ranges_.cbegin();
}
using OffsetReporter = std::function<void(int handler_offset)>;
void HandlerOffsetForCurrentPosition(const OffsetReporter& offset_reporter) {
CHECK(!bytecode_iterator_.done());
const int current_offset = bytecode_iterator_.current_offset();
// Remove outdated try ranges from the stack.
while (!stack_.empty()) {
const int end = stack_.top().end;
if (end < current_offset) {
stack_.pop();
} else {
break;
}
}
// Advance the iterator and maintain the stack.
while (ranges_iterator_ != ranges_.cend() &&
ranges_iterator_->start <= current_offset) {
if (ranges_iterator_->end >= current_offset) {
stack_.push(*ranges_iterator_);
if (ranges_iterator_->start == current_offset) {
offset_reporter(ranges_iterator_->handler);
}
}
ranges_iterator_++;
}
if (!stack_.empty() && stack_.top().start < current_offset) {
offset_reporter(stack_.top().handler);
}
}
private:
BytecodeArrayIterator const& bytecode_iterator_;
struct Range {
int start;
int end;
int handler;
friend bool operator<(const Range& a, const Range& b) {
if (a.start < b.start) return true;
if (a.start == b.start) {
if (a.end < b.end) return true;
CHECK_GT(a.end, b.end);
}
return false;
}
};
std::set<Range> ranges_;
std::set<Range>::const_iterator ranges_iterator_;
std::stack<Range> stack_;
};
Handle<FeedbackVector> SerializerForBackgroundCompilation::feedback_vector()
const {
return function().feedback_vector();
}
Handle<BytecodeArray> SerializerForBackgroundCompilation::bytecode_array()
const {
return handle(function().shared()->GetBytecodeArray(), broker()->isolate());
}
BytecodeAnalysis const& SerializerForBackgroundCompilation::GetBytecodeAnalysis(
SerializationPolicy policy) {
return broker()->GetBytecodeAnalysis(
bytecode_array(), osr_offset(),
flags() &
SerializerForBackgroundCompilationFlag::kAnalyzeEnvironmentLiveness,
policy);
}
void SerializerForBackgroundCompilation::TraverseBytecode() {
BytecodeAnalysis const& bytecode_analysis =
GetBytecodeAnalysis(SerializationPolicy::kSerializeIfNeeded);
BytecodeArrayRef(broker(), bytecode_array()).SerializeForCompilation();
BytecodeArrayIterator iterator(bytecode_array());
HandlerRangeMatcher try_start_matcher(iterator, bytecode_array());
bool has_one_shot_bytecode = false;
for (; !iterator.done(); iterator.Advance()) {
has_one_shot_bytecode =
has_one_shot_bytecode ||
interpreter::Bytecodes::IsOneShotBytecode(iterator.current_bytecode());
int const current_offset = iterator.current_offset();
// TODO(mvstanton): we might want to ignore the current environment if we
// are at the start of a catch handler.
IncorporateJumpTargetEnvironment(current_offset);
TRACE_BROKER(broker(),
"Handling bytecode: " << current_offset << " "
<< iterator.current_bytecode());
TRACE_BROKER(broker(), "Current environment: " << *environment());
if (environment()->IsDead()) {
continue; // Skip this bytecode since TF won't generate code for it.
}
auto save_handler_environments = [&](int handler_offset) {
auto it = jump_target_environments_.find(handler_offset);
if (it == jump_target_environments_.end()) {
ContributeToJumpTargetEnvironment(handler_offset);
TRACE_BROKER(broker(),
"Handler offset for current pos: " << handler_offset);
}
};
try_start_matcher.HandlerOffsetForCurrentPosition(
save_handler_environments);
if (bytecode_analysis.IsLoopHeader(current_offset)) {
// Graph builder might insert jumps to resume targets in the loop body.
LoopInfo const& loop_info =
bytecode_analysis.GetLoopInfoFor(current_offset);
for (const auto& target : loop_info.resume_jump_targets()) {
ContributeToJumpTargetEnvironment(target.target_offset());
}
}
switch (iterator.current_bytecode()) {
#define DEFINE_BYTECODE_CASE(name) \
case interpreter::Bytecode::k##name: \
Visit##name(&iterator); \
break;
SUPPORTED_BYTECODE_LIST(DEFINE_BYTECODE_CASE)
#undef DEFINE_BYTECODE_CASE
}
}
if (has_one_shot_bytecode) {
broker()->isolate()->CountUsage(
v8::Isolate::UseCounterFeature::kOptimizedFunctionWithOneShotBytecode);
}
}
void SerializerForBackgroundCompilation::VisitGetIterator(
BytecodeArrayIterator* iterator) {
Hints* receiver = &register_hints(iterator->GetRegisterOperand(0));
FeedbackSlot load_slot = iterator->GetSlotOperand(1);
FeedbackSlot call_slot = iterator->GetSlotOperand(2);
Handle<Name> name = broker()->isolate()->factory()->iterator_symbol();
ProcessNamedPropertyAccess(receiver, NameRef(broker(), name), load_slot,
AccessMode::kLoad);
if (environment()->IsDead()) return;
Hints callee;
HintsVector args = PrepareArgumentsHints(receiver);
ProcessCallOrConstruct(callee, base::nullopt, &args, call_slot,
kMissingArgumentsAreUndefined);
}
void SerializerForBackgroundCompilation::VisitGetSuperConstructor(
BytecodeArrayIterator* iterator) {
interpreter::Register dst = iterator->GetRegisterOperand(0);
Hints result_hints;
for (auto constant : environment()->accumulator_hints().constants()) {
// For JSNativeContextSpecialization::ReduceJSGetSuperConstructor.
if (!constant->IsJSFunction()) continue;
MapRef map(broker(),
handle(HeapObject::cast(*constant).map(), broker()->isolate()));
map.SerializePrototype();
ObjectRef proto = map.prototype();
if (proto.IsHeapObject() && proto.AsHeapObject().map().is_constructor()) {
result_hints.AddConstant(proto.object(), zone(), broker());
}
}
register_hints(dst) = result_hints;
}
void SerializerForBackgroundCompilation::VisitGetTemplateObject(
BytecodeArrayIterator* iterator) {
TemplateObjectDescriptionRef description(
broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate()));
FeedbackSlot slot = iterator->GetSlotOperand(1);
FeedbackSource source(feedback_vector(), slot);
SharedFunctionInfoRef shared(broker(), function().shared());
JSArrayRef template_object = shared.GetTemplateObject(
description, source, SerializationPolicy::kSerializeIfNeeded);
environment()->accumulator_hints() =
Hints::SingleConstant(template_object.object(), zone());
}
void SerializerForBackgroundCompilation::VisitLdaTrue(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints() = Hints::SingleConstant(
broker()->isolate()->factory()->true_value(), zone());
}
void SerializerForBackgroundCompilation::VisitLdaFalse(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints() = Hints::SingleConstant(
broker()->isolate()->factory()->false_value(), zone());
}
void SerializerForBackgroundCompilation::VisitLdaTheHole(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints() = Hints::SingleConstant(
broker()->isolate()->factory()->the_hole_value(), zone());
}
void SerializerForBackgroundCompilation::VisitLdaUndefined(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints() = Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone());
}
void SerializerForBackgroundCompilation::VisitLdaNull(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints() = Hints::SingleConstant(
broker()->isolate()->factory()->null_value(), zone());
}
void SerializerForBackgroundCompilation::VisitLdaZero(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints() = Hints::SingleConstant(
handle(Smi::FromInt(0), broker()->isolate()), zone());
}
void SerializerForBackgroundCompilation::VisitLdaSmi(
BytecodeArrayIterator* iterator) {
Handle<Smi> smi(Smi::FromInt(iterator->GetImmediateOperand(0)),
broker()->isolate());
environment()->accumulator_hints() = Hints::SingleConstant(smi, zone());
}
void SerializerForBackgroundCompilation::VisitInvokeIntrinsic(
BytecodeArrayIterator* iterator) {
Runtime::FunctionId functionId = iterator->GetIntrinsicIdOperand(0);
// For JSNativeContextSpecialization::ReduceJSAsyncFunctionResolve and
// JSNativeContextSpecialization::ReduceJSResolvePromise.
switch (functionId) {
case Runtime::kInlineAsyncFunctionResolve: {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kAsyncFunctionResolve));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
size_t reg_count = iterator->GetRegisterCountOperand(2);
CHECK_EQ(reg_count, 3);
HintsVector args = PrepareArgumentsHints(first_reg, reg_count);
Hints const& resolution_hints = args[1]; // The resolution object.
ProcessHintsForPromiseResolve(resolution_hints);
return;
}
case Runtime::kInlineAsyncGeneratorReject:
case Runtime::kAsyncGeneratorReject: {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kAsyncGeneratorReject));
break;
}
case Runtime::kInlineAsyncGeneratorResolve:
case Runtime::kAsyncGeneratorResolve: {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kAsyncGeneratorResolve));
break;
}
case Runtime::kInlineAsyncGeneratorYield:
case Runtime::kAsyncGeneratorYield: {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kAsyncGeneratorYield));
break;
}
case Runtime::kInlineAsyncGeneratorAwaitUncaught:
case Runtime::kAsyncGeneratorAwaitUncaught: {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kAsyncGeneratorAwaitUncaught));
break;
}
case Runtime::kInlineAsyncGeneratorAwaitCaught:
case Runtime::kAsyncGeneratorAwaitCaught: {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kAsyncGeneratorAwaitCaught));
break;
}
case Runtime::kInlineAsyncFunctionAwaitUncaught:
case Runtime::kAsyncFunctionAwaitUncaught: {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kAsyncFunctionAwaitUncaught));
break;
}
case Runtime::kInlineAsyncFunctionAwaitCaught:
case Runtime::kAsyncFunctionAwaitCaught: {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kAsyncFunctionAwaitCaught));
break;
}
case Runtime::kInlineAsyncFunctionReject:
case Runtime::kAsyncFunctionReject: {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kAsyncFunctionReject));
break;
}
case Runtime::kAsyncFunctionResolve: {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kAsyncFunctionResolve));
break;
}
case Runtime::kInlineCopyDataProperties:
case Runtime::kCopyDataProperties: {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kCopyDataProperties));
break;
}
case Runtime::kInlineGetImportMetaObject: {
Hints const& context_hints = environment()->current_context_hints();
for (auto x : context_hints.constants()) {
ContextRef(broker(), x)
.GetModule(SerializationPolicy::kSerializeIfNeeded)
.Serialize();
}
for (auto x : context_hints.virtual_contexts()) {
ContextRef(broker(), x.context)
.GetModule(SerializationPolicy::kSerializeIfNeeded)
.Serialize();
}
break;
}
default: {
break;
}
}
environment()->accumulator_hints() = Hints();
}
void SerializerForBackgroundCompilation::VisitLdaConstant(
BytecodeArrayIterator* iterator) {
ObjectRef object(
broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate()));
environment()->accumulator_hints() =
Hints::SingleConstant(object.object(), zone());
}
void SerializerForBackgroundCompilation::VisitPushContext(
BytecodeArrayIterator* iterator) {
register_hints(iterator->GetRegisterOperand(0))
.Reset(&environment()->current_context_hints(), zone());
environment()->current_context_hints().Reset(
&environment()->accumulator_hints(), zone());
}
void SerializerForBackgroundCompilation::VisitPopContext(
BytecodeArrayIterator* iterator) {
environment()->current_context_hints().Reset(
&register_hints(iterator->GetRegisterOperand(0)), zone());
}
void SerializerForBackgroundCompilation::ProcessImmutableLoad(
ContextRef const& context_ref, int slot, ContextProcessingMode mode,
Hints* result_hints) {
DCHECK_EQ(mode, kSerializeSlot);
base::Optional<ObjectRef> slot_value =
context_ref.get(slot, SerializationPolicy::kSerializeIfNeeded);
// If requested, record the object as a hint for the result value.
if (result_hints != nullptr && slot_value.has_value()) {
result_hints->AddConstant(slot_value.value().object(), zone(), broker());
}
}
void SerializerForBackgroundCompilation::ProcessContextAccess(
Hints const& context_hints, int slot, int depth, ContextProcessingMode mode,
Hints* result_hints) {
// This function is for JSContextSpecialization::ReduceJSLoadContext and
// ReduceJSStoreContext. Those reductions attempt to eliminate as many
// loads as possible by making use of constant Context objects. In the
// case of an immutable load, ReduceJSLoadContext even attempts to load
// the value at {slot}, replacing the load with a constant.
for (auto x : context_hints.constants()) {
if (x->IsContext()) {
// Walk this context to the given depth and serialize the slot found.
ContextRef context_ref(broker(), x);
size_t remaining_depth = depth;
context_ref = context_ref.previous(
&remaining_depth, SerializationPolicy::kSerializeIfNeeded);
if (remaining_depth == 0 && mode != kIgnoreSlot) {
ProcessImmutableLoad(context_ref, slot, mode, result_hints);
}
}
}
for (auto x : context_hints.virtual_contexts()) {
if (x.distance <= static_cast<unsigned int>(depth)) {
ContextRef context_ref(broker(), x.context);
size_t remaining_depth = depth - x.distance;
context_ref = context_ref.previous(
&remaining_depth, SerializationPolicy::kSerializeIfNeeded);
if (remaining_depth == 0 && mode != kIgnoreSlot) {
ProcessImmutableLoad(context_ref, slot, mode, result_hints);
}
}
}
}
void SerializerForBackgroundCompilation::VisitLdaContextSlot(
BytecodeArrayIterator* iterator) {
Hints const& context_hints = register_hints(iterator->GetRegisterOperand(0));
const int slot = iterator->GetIndexOperand(1);
const int depth = iterator->GetUnsignedImmediateOperand(2);
Hints new_accumulator_hints;
ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot,
&new_accumulator_hints);
environment()->accumulator_hints() = new_accumulator_hints;
}
void SerializerForBackgroundCompilation::VisitLdaCurrentContextSlot(
BytecodeArrayIterator* iterator) {
const int slot = iterator->GetIndexOperand(0);
const int depth = 0;
Hints const& context_hints = environment()->current_context_hints();
Hints new_accumulator_hints;
ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot,
&new_accumulator_hints);
environment()->accumulator_hints() = new_accumulator_hints;
}
void SerializerForBackgroundCompilation::VisitLdaImmutableContextSlot(
BytecodeArrayIterator* iterator) {
const int slot = iterator->GetIndexOperand(1);
const int depth = iterator->GetUnsignedImmediateOperand(2);
Hints const& context_hints = register_hints(iterator->GetRegisterOperand(0));
Hints new_accumulator_hints;
ProcessContextAccess(context_hints, slot, depth, kSerializeSlot,
&new_accumulator_hints);
environment()->accumulator_hints() = new_accumulator_hints;
}
void SerializerForBackgroundCompilation::VisitLdaImmutableCurrentContextSlot(
BytecodeArrayIterator* iterator) {
const int slot = iterator->GetIndexOperand(0);
const int depth = 0;
Hints const& context_hints = environment()->current_context_hints();
Hints new_accumulator_hints;
ProcessContextAccess(context_hints, slot, depth, kSerializeSlot,
&new_accumulator_hints);
environment()->accumulator_hints() = new_accumulator_hints;
}
void SerializerForBackgroundCompilation::ProcessModuleVariableAccess(
BytecodeArrayIterator* iterator) {
const int slot = Context::EXTENSION_INDEX;
const int depth = iterator->GetUnsignedImmediateOperand(1);
Hints const& context_hints = environment()->current_context_hints();
Hints result_hints;
ProcessContextAccess(context_hints, slot, depth, kSerializeSlot,
&result_hints);
for (Handle<Object> constant : result_hints.constants()) {
ObjectRef object(broker(), constant);
// For JSTypedLowering::BuildGetModuleCell.
if (object.IsSourceTextModule()) object.AsSourceTextModule().Serialize();
}
}
void SerializerForBackgroundCompilation::VisitLdaModuleVariable(
BytecodeArrayIterator* iterator) {
ProcessModuleVariableAccess(iterator);
}
void SerializerForBackgroundCompilation::VisitStaModuleVariable(
BytecodeArrayIterator* iterator) {
ProcessModuleVariableAccess(iterator);
}
void SerializerForBackgroundCompilation::VisitStaLookupSlot(
BytecodeArrayIterator* iterator) {
ObjectRef(broker(),
iterator->GetConstantForIndexOperand(0, broker()->isolate()));
environment()->accumulator_hints() = Hints();
}
void SerializerForBackgroundCompilation::VisitStaContextSlot(
BytecodeArrayIterator* iterator) {
const int slot = iterator->GetIndexOperand(1);
const int depth = iterator->GetUnsignedImmediateOperand(2);
Hints const& hints = register_hints(iterator->GetRegisterOperand(0));
ProcessContextAccess(hints, slot, depth, kIgnoreSlot);
}
void SerializerForBackgroundCompilation::VisitStaCurrentContextSlot(
BytecodeArrayIterator* iterator) {
const int slot = iterator->GetIndexOperand(0);
const int depth = 0;
Hints const& context_hints = environment()->current_context_hints();
ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot);
}
void SerializerForBackgroundCompilation::VisitLdar(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints().Reset(
&register_hints(iterator->GetRegisterOperand(0)), zone());
}
void SerializerForBackgroundCompilation::VisitStar(
BytecodeArrayIterator* iterator) {
interpreter::Register reg = iterator->GetRegisterOperand(0);
register_hints(reg).Reset(&environment()->accumulator_hints(), zone());
}
void SerializerForBackgroundCompilation::VisitMov(
BytecodeArrayIterator* iterator) {
interpreter::Register src = iterator->GetRegisterOperand(0);
interpreter::Register dst = iterator->GetRegisterOperand(1);
register_hints(dst).Reset(&register_hints(src), zone());
}
void SerializerForBackgroundCompilation::VisitCreateRegExpLiteral(
BytecodeArrayIterator* iterator) {
Handle<String> constant_pattern = Handle<String>::cast(
iterator->GetConstantForIndexOperand(0, broker()->isolate()));
StringRef description(broker(), constant_pattern);
FeedbackSlot slot = iterator->GetSlotOperand(1);
FeedbackSource source(feedback_vector(), slot);
broker()->ProcessFeedbackForRegExpLiteral(source);
environment()->accumulator_hints() = Hints();
}
void SerializerForBackgroundCompilation::VisitCreateArrayLiteral(
BytecodeArrayIterator* iterator) {
Handle<ArrayBoilerplateDescription> array_boilerplate_description =
Handle<ArrayBoilerplateDescription>::cast(
iterator->GetConstantForIndexOperand(0, broker()->isolate()));
ArrayBoilerplateDescriptionRef description(broker(),
array_boilerplate_description);
FeedbackSlot slot = iterator->GetSlotOperand(1);
FeedbackSource source(feedback_vector(), slot);
broker()->ProcessFeedbackForArrayOrObjectLiteral(source);
environment()->accumulator_hints() = Hints();
}
void SerializerForBackgroundCompilation::VisitCreateEmptyArrayLiteral(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(0);
FeedbackSource source(feedback_vector(), slot);
broker()->ProcessFeedbackForArrayOrObjectLiteral(source);
environment()->accumulator_hints() = Hints();
}
void SerializerForBackgroundCompilation::VisitCreateObjectLiteral(
BytecodeArrayIterator* iterator) {
Handle<ObjectBoilerplateDescription> constant_properties =
Handle<ObjectBoilerplateDescription>::cast(
iterator->GetConstantForIndexOperand(0, broker()->isolate()));
ObjectBoilerplateDescriptionRef description(broker(), constant_properties);
FeedbackSlot slot = iterator->GetSlotOperand(1);
FeedbackSource source(feedback_vector(), slot);
broker()->ProcessFeedbackForArrayOrObjectLiteral(source);
environment()->accumulator_hints() = Hints();
}
void SerializerForBackgroundCompilation::VisitCreateFunctionContext(
BytecodeArrayIterator* iterator) {
ProcessCreateContext(iterator, 0);
}
void SerializerForBackgroundCompilation::VisitCreateBlockContext(
BytecodeArrayIterator* iterator) {
ProcessCreateContext(iterator, 0);
}
void SerializerForBackgroundCompilation::VisitCreateEvalContext(
BytecodeArrayIterator* iterator) {
ProcessCreateContext(iterator, 0);
}
void SerializerForBackgroundCompilation::VisitCreateWithContext(
BytecodeArrayIterator* iterator) {
ProcessCreateContext(iterator, 1);
}
void SerializerForBackgroundCompilation::VisitCreateCatchContext(
BytecodeArrayIterator* iterator) {
ProcessCreateContext(iterator, 1);
}
void SerializerForBackgroundCompilation::VisitForInNext(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(3);
ProcessForIn(slot);
}
void SerializerForBackgroundCompilation::VisitForInPrepare(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessForIn(slot);
}
void SerializerForBackgroundCompilation::ProcessCreateContext(
interpreter::BytecodeArrayIterator* iterator, int scopeinfo_operand_index) {
Handle<ScopeInfo> scope_info =
Handle<ScopeInfo>::cast(iterator->GetConstantForIndexOperand(
scopeinfo_operand_index, broker()->isolate()));
ScopeInfoRef scope_info_ref(broker(), scope_info);
scope_info_ref.SerializeScopeInfoChain();
Hints const& current_context_hints = environment()->current_context_hints();
Hints result_hints;
// For each constant context, we must create a virtual context from
// it of distance one.
for (auto x : current_context_hints.constants()) {
if (x->IsContext()) {
Handle<Context> as_context(Handle<Context>::cast(x));
result_hints.AddVirtualContext(VirtualContext(1, as_context), zone(),
broker());
}
}
// For each virtual context, we must create a virtual context from
// it of distance {existing distance} + 1.
for (auto x : current_context_hints.virtual_contexts()) {
result_hints.AddVirtualContext(VirtualContext(x.distance + 1, x.context),
zone(), broker());
}
environment()->accumulator_hints() = result_hints;
}
void SerializerForBackgroundCompilation::VisitCreateClosure(
BytecodeArrayIterator* iterator) {
Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(
iterator->GetConstantForIndexOperand(0, broker()->isolate()));
Handle<FeedbackCell> feedback_cell =
feedback_vector()->GetClosureFeedbackCell(iterator->GetIndexOperand(1));
FeedbackCellRef feedback_cell_ref(broker(), feedback_cell);
Handle<Object> cell_value(feedback_cell->value(), broker()->isolate());
ObjectRef cell_value_ref(broker(), cell_value);
Hints result_hints;
if (cell_value->IsFeedbackVector()) {
VirtualClosure virtual_closure(shared,
Handle<FeedbackVector>::cast(cell_value),
environment()->current_context_hints());
result_hints.AddVirtualClosure(virtual_closure, zone(), broker());
}
environment()->accumulator_hints() = result_hints;
}
void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver(
BytecodeArrayIterator* iterator) {
Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
ProcessCallVarArgs(ConvertReceiverMode::kNullOrUndefined, callee, first_reg,
reg_count, slot);
}
void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver0(
BytecodeArrayIterator* iterator) {
Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
FeedbackSlot slot = iterator->GetSlotOperand(1);
Hints const receiver = Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone());
HintsVector parameters({receiver}, zone());
ProcessCallOrConstruct(callee, base::nullopt, &parameters, slot,
kMissingArgumentsAreUndefined);
}
namespace {
void PrepareArgumentsHintsInternal(Zone* zone, HintsVector* args) {}
template <typename... MoreHints>
void PrepareArgumentsHintsInternal(Zone* zone, HintsVector* args, Hints* hints,
MoreHints... more) {
hints->EnsureShareable(zone);
args->push_back(*hints);
PrepareArgumentsHintsInternal(zone, args, more...);
}
} // namespace
template <typename... MoreHints>
HintsVector SerializerForBackgroundCompilation::PrepareArgumentsHints(
Hints* hints, MoreHints... more) {
HintsVector args(zone());
PrepareArgumentsHintsInternal(zone(), &args, hints, more...);
return args;
}
HintsVector SerializerForBackgroundCompilation::PrepareArgumentsHints(
interpreter::Register first, size_t count) {
HintsVector result(zone());
const int reg_base = first.index();
for (int i = 0; i < static_cast<int>(count); ++i) {
Hints& hints = register_hints(interpreter::Register(reg_base + i));
hints.EnsureShareable(zone());
result.push_back(hints);
}
return result;
}
void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver1(
BytecodeArrayIterator* iterator) {
Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
Hints* arg0 = &register_hints(iterator->GetRegisterOperand(1));
FeedbackSlot slot = iterator->GetSlotOperand(2);
Hints receiver = Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone());
HintsVector args = PrepareArgumentsHints(&receiver, arg0);
ProcessCallOrConstruct(callee, base::nullopt, &args, slot,
kMissingArgumentsAreUndefined);
}
void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver2(
BytecodeArrayIterator* iterator) {
Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
Hints* arg0 = &register_hints(iterator->GetRegisterOperand(1));
Hints* arg1 = &register_hints(iterator->GetRegisterOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
Hints receiver = Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone());
HintsVector args = PrepareArgumentsHints(&receiver, arg0, arg1);
ProcessCallOrConstruct(callee, base::nullopt, &args, slot,
kMissingArgumentsAreUndefined);
}
void SerializerForBackgroundCompilation::VisitCallAnyReceiver(
BytecodeArrayIterator* iterator) {
Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count,
slot);
}
void SerializerForBackgroundCompilation::VisitCallNoFeedback(
BytecodeArrayIterator* iterator) {
Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count,
FeedbackSlot::Invalid());
}
void SerializerForBackgroundCompilation::VisitCallProperty(
BytecodeArrayIterator* iterator) {
Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
ProcessCallVarArgs(ConvertReceiverMode::kNotNullOrUndefined, callee,
first_reg, reg_count, slot);
}
void SerializerForBackgroundCompilation::VisitCallProperty0(
BytecodeArrayIterator* iterator) {
Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
Hints* receiver = &register_hints(iterator->GetRegisterOperand(1));
FeedbackSlot slot = iterator->GetSlotOperand(2);
HintsVector args = PrepareArgumentsHints(receiver);
ProcessCallOrConstruct(callee, base::nullopt, &args, slot,
kMissingArgumentsAreUndefined);
}
void SerializerForBackgroundCompilation::VisitCallProperty1(
BytecodeArrayIterator* iterator) {
Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
Hints* receiver = &register_hints(iterator->GetRegisterOperand(1));
Hints* arg0 = &register_hints(iterator->GetRegisterOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
HintsVector args = PrepareArgumentsHints(receiver, arg0);
ProcessCallOrConstruct(callee, base::nullopt, &args, slot,
kMissingArgumentsAreUndefined);
}
void SerializerForBackgroundCompilation::VisitCallProperty2(
BytecodeArrayIterator* iterator) {
Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
Hints* receiver = &register_hints(iterator->GetRegisterOperand(1));
Hints* arg0 = &register_hints(iterator->GetRegisterOperand(2));
Hints* arg1 = &register_hints(iterator->GetRegisterOperand(3));
FeedbackSlot slot = iterator->GetSlotOperand(4);
HintsVector args = PrepareArgumentsHints(receiver, arg0, arg1);
ProcessCallOrConstruct(callee, base::nullopt, &args, slot,
kMissingArgumentsAreUndefined);
}
void SerializerForBackgroundCompilation::VisitCallWithSpread(
BytecodeArrayIterator* iterator) {
Hints const& callee = register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count,
slot, kMissingArgumentsAreUnknown);
}
void SerializerForBackgroundCompilation::VisitCallJSRuntime(
BytecodeArrayIterator* iterator) {
const int runtime_index = iterator->GetNativeContextIndexOperand(0);
ObjectRef constant =
broker()
->target_native_context()
.get(runtime_index, SerializationPolicy::kSerializeIfNeeded)
.value();
Hints const callee = Hints::SingleConstant(constant.object(), zone());
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
ProcessCallVarArgs(ConvertReceiverMode::kNullOrUndefined, callee, first_reg,
reg_count, FeedbackSlot::Invalid());
}
Hints SerializerForBackgroundCompilation::RunChildSerializer(
CompilationSubject function, base::Optional<Hints> new_target,
const HintsVector& arguments, MissingArgumentsPolicy padding) {
SerializerForBackgroundCompilation child_serializer(
zone_scope_.zone_stats(), broker(), dependencies(), function, new_target,
arguments, padding, flags(), nesting_level_ + 1);
Hints result = child_serializer.Run();
// The Hints returned by the call to Run are allocated in the zone
// created by the child serializer. Adding those hints to a hints
// object created in our zone will preserve the information.
return result.CopyToParentZone(zone(), broker());
}
void SerializerForBackgroundCompilation::ProcessCalleeForCallOrConstruct(
Callee const& callee, base::Optional<Hints> new_target,
const HintsVector& arguments, SpeculationMode speculation_mode,
MissingArgumentsPolicy padding, Hints* result_hints) {
Handle<SharedFunctionInfo> shared = callee.shared(broker()->isolate());
if (shared->IsApiFunction()) {
ProcessApiCall(shared, arguments);
DCHECK_NE(shared->GetInlineability(), SharedFunctionInfo::kIsInlineable);
} else if (shared->HasBuiltinId()) {
ProcessBuiltinCall(shared, new_target, arguments, speculation_mode, padding,
result_hints);
DCHECK_NE(shared->GetInlineability(), SharedFunctionInfo::kIsInlineable);
} else if ((flags() &
SerializerForBackgroundCompilationFlag::kEnableTurboInlining) &&
shared->GetInlineability() == SharedFunctionInfo::kIsInlineable &&
callee.HasFeedbackVector()) {
CompilationSubject subject =
callee.ToCompilationSubject(broker()->isolate(), zone());
result_hints->Add(
RunChildSerializer(subject, new_target, arguments, padding), zone(),
broker());
}
}
namespace {
// Returns the innermost bound target and inserts all bound arguments and
// {original_arguments} into {expanded_arguments} in the appropriate order.
JSReceiverRef UnrollBoundFunction(JSBoundFunctionRef const& bound_function,
JSHeapBroker* broker,
const HintsVector& original_arguments,
HintsVector* expanded_arguments, Zone* zone) {
DCHECK(expanded_arguments->empty());
JSReceiverRef target = bound_function.AsJSReceiver();
HintsVector reversed_bound_arguments(zone);
for (; target.IsJSBoundFunction();
target = target.AsJSBoundFunction().bound_target_function()) {
for (int i = target.AsJSBoundFunction().bound_arguments().length() - 1;
i >= 0; --i) {
Hints const arg = Hints::SingleConstant(
target.AsJSBoundFunction().bound_arguments().get(i).object(), zone);
reversed_bound_arguments.push_back(arg);
}
Hints const arg = Hints::SingleConstant(
target.AsJSBoundFunction().bound_this().object(), zone);
reversed_bound_arguments.push_back(arg);
}
expanded_arguments->insert(expanded_arguments->end(),
reversed_bound_arguments.rbegin(),
reversed_bound_arguments.rend());
expanded_arguments->insert(expanded_arguments->end(),
original_arguments.begin(),
original_arguments.end());
return target;
}
} // namespace
void SerializerForBackgroundCompilation::ProcessCalleeForCallOrConstruct(
Handle<Object> callee, base::Optional<Hints> new_target,
const HintsVector& arguments, SpeculationMode speculation_mode,
MissingArgumentsPolicy padding, Hints* result_hints) {
const HintsVector* actual_arguments = &arguments;
HintsVector expanded_arguments(zone());
if (callee->IsJSBoundFunction()) {
JSBoundFunctionRef bound_function(broker(),
Handle<JSBoundFunction>::cast(callee));
bound_function.Serialize();
callee = UnrollBoundFunction(bound_function, broker(), arguments,
&expanded_arguments, zone())
.object();
actual_arguments = &expanded_arguments;
}
if (!callee->IsJSFunction()) return;
JSFunctionRef function(broker(), Handle<JSFunction>::cast(callee));
function.Serialize();
Callee new_callee(function.object());
ProcessCalleeForCallOrConstruct(new_callee, new_target, *actual_arguments,
speculation_mode, padding, result_hints);
}
void SerializerForBackgroundCompilation::ProcessCallOrConstruct(
Hints callee, base::Optional<Hints> new_target, HintsVector* arguments,
FeedbackSlot slot, MissingArgumentsPolicy padding) {
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation;
if (!slot.IsInvalid()) {
FeedbackSource source(feedback_vector(), slot);
ProcessedFeedback const& feedback =
broker()->ProcessFeedbackForCall(source);
if (BailoutOnUninitialized(feedback)) return;
if (!feedback.IsInsufficient()) {
// Incorporate feedback into hints copy to simplify processing.
// TODO(neis): Modify the original hints instead?
speculation_mode = feedback.AsCall().speculation_mode();
// Incorporate target feedback into hints copy to simplify processing.
base::Optional<HeapObjectRef> target = feedback.AsCall().target();
if (target.has_value() &&
(target->map().is_callable() || target->IsFeedbackCell())) {
callee = callee.Copy(zone());
// TODO(mvstanton): if the map isn't callable then we have an allocation
// site, and it may make sense to add the Array JSFunction constant.
if (new_target.has_value()) {
// Construct; feedback is new_target, which often is also the callee.
new_target = new_target->Copy(zone());
new_target->AddConstant(target->object(), zone(), broker());
callee.AddConstant(target->object(), zone(), broker());
} else {
// Call; target is feedback cell or callee.
if (target->IsFeedbackCell() &&
target->AsFeedbackCell().value().IsFeedbackVector()) {
FeedbackVectorRef vector =
target->AsFeedbackCell().value().AsFeedbackVector();
vector.Serialize();
VirtualClosure virtual_closure(
vector.shared_function_info().object(), vector.object(),
Hints());
callee.AddVirtualClosure(virtual_closure, zone(), broker());
} else {
callee.AddConstant(target->object(), zone(), broker());
}
}
}
}
}
Hints result_hints_from_new_target;
if (new_target.has_value()) {
ProcessNewTargetForConstruct(*new_target, &result_hints_from_new_target);
// These hints are a good guess at the resulting object, so they are useful
// for both the accumulator and the constructor call's receiver. The latter
// is still missing completely in {arguments} so add it now.
arguments->insert(arguments->begin(), result_hints_from_new_target);
}
// For JSNativeContextSpecialization::InferReceiverRootMap
Hints new_accumulator_hints = result_hints_from_new_target.Copy(zone());
ProcessCallOrConstructRecursive(callee, new_target, *arguments,
speculation_mode, padding,
&new_accumulator_hints);
environment()->accumulator_hints() = new_accumulator_hints;
}
void SerializerForBackgroundCompilation::ProcessCallOrConstructRecursive(
Hints const& callee, base::Optional<Hints> new_target,
const HintsVector& arguments, SpeculationMode speculation_mode,
MissingArgumentsPolicy padding, Hints* result_hints) {
// For JSCallReducer::ReduceJSCall and JSCallReducer::ReduceJSConstruct.
for (auto constant : callee.constants()) {
ProcessCalleeForCallOrConstruct(constant, new_target, arguments,
speculation_mode, padding, result_hints);
}
// For JSCallReducer::ReduceJSCall and JSCallReducer::ReduceJSConstruct.
for (auto hint : callee.virtual_closures()) {
ProcessCalleeForCallOrConstruct(Callee(hint), new_target, arguments,
speculation_mode, padding, result_hints);
}
for (auto hint : callee.virtual_bound_functions()) {
HintsVector new_arguments = hint.bound_arguments;
new_arguments.insert(new_arguments.end(), arguments.begin(),
arguments.end());
ProcessCallOrConstructRecursive(hint.bound_target, new_target,
new_arguments, speculation_mode, padding,
result_hints);
}
}
void SerializerForBackgroundCompilation::ProcessNewTargetForConstruct(
Hints const& new_target_hints, Hints* result_hints) {
for (Handle<Object> target : new_target_hints.constants()) {
if (target->IsJSBoundFunction()) {
// Unroll the bound function.
while (target->IsJSBoundFunction()) {
target = handle(
Handle<JSBoundFunction>::cast(target)->bound_target_function(),
broker()->isolate());
}
}
if (target->IsJSFunction()) {
Handle<JSFunction> new_target(Handle<JSFunction>::cast(target));
if (new_target->has_prototype_slot(broker()->isolate()) &&
new_target->has_initial_map()) {
result_hints->AddMap(
handle(new_target->initial_map(), broker()->isolate()), zone(),
broker());
}
}
}
for (auto const& virtual_bound_function :
new_target_hints.virtual_bound_functions()) {
ProcessNewTargetForConstruct(virtual_bound_function.bound_target,
result_hints);
}
}
void SerializerForBackgroundCompilation::ProcessCallVarArgs(
ConvertReceiverMode receiver_mode, Hints const& callee,
interpreter::Register first_reg, int reg_count, FeedbackSlot slot,
MissingArgumentsPolicy padding) {
HintsVector args = PrepareArgumentsHints(first_reg, reg_count);
// The receiver is either given in the first register or it is implicitly
// the {undefined} value.
if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) {
args.insert(args.begin(),
Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone()));
}
ProcessCallOrConstruct(callee, base::nullopt, &args, slot, padding);
}
void SerializerForBackgroundCompilation::ProcessApiCall(
Handle<SharedFunctionInfo> target, const HintsVector& arguments) {
ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle(
Builtins::kCallFunctionTemplate_CheckAccess));
ObjectRef(broker(),
broker()->isolate()->builtins()->builtin_handle(
Builtins::kCallFunctionTemplate_CheckCompatibleReceiver));
ObjectRef(
broker(),
broker()->isolate()->builtins()->builtin_handle(
Builtins::kCallFunctionTemplate_CheckAccessAndCompatibleReceiver));
FunctionTemplateInfoRef target_template_info(
broker(), handle(target->function_data(), broker()->isolate()));
if (!target_template_info.has_call_code()) return;
target_template_info.SerializeCallCode();
SharedFunctionInfoRef target_ref(broker(), target);
target_ref.SerializeFunctionTemplateInfo();
if (target_template_info.accept_any_receiver() &&
target_template_info.is_signature_undefined()) {
return;
}
if (arguments.empty()) return;
Hints const& receiver_hints = arguments[0];
for (auto hint : receiver_hints.constants()) {
if (hint->IsUndefined()) {
// The receiver is the global proxy.
Handle<JSGlobalProxy> global_proxy =
broker()->target_native_context().global_proxy_object().object();
ProcessReceiverMapForApiCall(
target_template_info,
handle(global_proxy->map(), broker()->isolate()));
continue;
}
if (!hint->IsJSReceiver()) continue;
Handle<JSReceiver> receiver(Handle<JSReceiver>::cast(hint));
ProcessReceiverMapForApiCall(target_template_info,
handle(receiver->map(), broker()->isolate()));
}
for (auto receiver_map : receiver_hints.maps()) {
ProcessReceiverMapForApiCall(target_template_info, receiver_map);
}
}
void SerializerForBackgroundCompilation::ProcessReceiverMapForApiCall(
FunctionTemplateInfoRef target, Handle<Map> receiver) {
if (!receiver->is_access_check_needed()) {
MapRef receiver_map(broker(), receiver);
TRACE_BROKER(broker(), "Serializing holder for target: " << target);
target.LookupHolderOfExpectedType(receiver_map,
SerializationPolicy::kSerializeIfNeeded);
}
}
void SerializerForBackgroundCompilation::ProcessHintsForObjectCreate(
Hints const& prototype) {
for (Handle<Object> constant_handle : prototype.constants()) {
ObjectRef constant(broker(), constant_handle);
if (constant.IsJSObject()) constant.AsJSObject().SerializeObjectCreateMap();
}
}
void SerializerForBackgroundCompilation::ProcessBuiltinCall(
Handle<SharedFunctionInfo> target, base::Optional<Hints> new_target,
const HintsVector& arguments, SpeculationMode speculation_mode,
MissingArgumentsPolicy padding, Hints* result_hints) {
DCHECK(target->HasBuiltinId());
const int builtin_id = target->builtin_id();
const char* name = Builtins::name(builtin_id);
TRACE_BROKER(broker(), "Serializing for call to builtin " << name);
switch (builtin_id) {
case Builtins::kObjectCreate: {
if (arguments.size() >= 2) {
ProcessHintsForObjectCreate(arguments[1]);
} else {
ProcessHintsForObjectCreate(Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone()));
}
break;
}
case Builtins::kPromisePrototypeCatch: {
// For JSCallReducer::ReducePromisePrototypeCatch.
if (speculation_mode != SpeculationMode::kDisallowSpeculation) {
if (arguments.size() >= 1) {
ProcessMapHintsForPromises(arguments[0]);
}
}
break;
}
case Builtins::kPromisePrototypeFinally: {
// For JSCallReducer::ReducePromisePrototypeFinally.
if (speculation_mode != SpeculationMode::kDisallowSpeculation) {
if (arguments.size() >= 1) {
ProcessMapHintsForPromises(arguments[0]);
}
SharedFunctionInfoRef(
broker(),
broker()->isolate()->factory()->promise_catch_finally_shared_fun());
SharedFunctionInfoRef(
broker(),
broker()->isolate()->factory()->promise_then_finally_shared_fun());
}
break;
}
case Builtins::kPromisePrototypeThen: {
// For JSCallReducer::ReducePromisePrototypeThen.
if (speculation_mode != SpeculationMode::kDisallowSpeculation) {
if (arguments.size() >= 1) {
ProcessMapHintsForPromises(arguments[0]);
}
}
break;
}
case Builtins::kPromiseResolveTrampoline:
// For JSCallReducer::ReducePromiseInternalResolve and
// JSNativeContextSpecialization::ReduceJSResolvePromise.
if (arguments.size() >= 1) {
Hints const resolution_hints =
arguments.size() >= 2
? arguments[1]
: Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(),
zone());
ProcessHintsForPromiseResolve(resolution_hints);
}
break;
case Builtins::kRegExpPrototypeTest:
case Builtins::kRegExpPrototypeTestFast:
// For JSCallReducer::ReduceRegExpPrototypeTest.
if (arguments.size() >= 1 &&
speculation_mode != SpeculationMode::kDisallowSpeculation) {
Hints const& regexp_hints = arguments[0];
ProcessHintsForRegExpTest(regexp_hints);
}
break;
case Builtins::kArrayEvery:
case Builtins::kArrayFilter:
case Builtins::kArrayForEach:
case Builtins::kArrayPrototypeFind:
case Builtins::kArrayPrototypeFindIndex:
case Builtins::kArrayMap:
case Builtins::kArraySome:
if (arguments.size() >= 2 &&
speculation_mode != SpeculationMode::kDisallowSpeculation) {
Hints const& callback = arguments[1];
// "Call(callbackfn, T, « kValue, k, O »)"
HintsVector new_arguments(zone());
new_arguments.push_back(
arguments.size() < 3
? Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone())
: arguments[2]); // T
new_arguments.push_back(Hints()); // kValue
new_arguments.push_back(Hints()); // k
new_arguments.push_back(arguments[0]); // O
for (auto constant : callback.constants()) {
ProcessCalleeForCallOrConstruct(
constant, base::nullopt, new_arguments, speculation_mode,
kMissingArgumentsAreUndefined, result_hints);
}
for (auto virtual_closure : callback.virtual_closures()) {
ProcessCalleeForCallOrConstruct(
Callee(virtual_closure), base::nullopt, new_arguments,
speculation_mode, kMissingArgumentsAreUndefined, result_hints);
}
}
break;
case Builtins::kArrayReduce:
case Builtins::kArrayReduceRight:
if (arguments.size() >= 2 &&
speculation_mode != SpeculationMode::kDisallowSpeculation) {
Hints const& callback = arguments[1];
// "Call(callbackfn, undefined, « accumulator, kValue, k, O »)"
HintsVector new_arguments(zone());
new_arguments.push_back(Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone()));
new_arguments.push_back(Hints()); // accumulator
new_arguments.push_back(Hints()); // kValue
new_arguments.push_back(Hints()); // k
new_arguments.push_back(arguments[0]); // O
for (auto constant : callback.constants()) {
ProcessCalleeForCallOrConstruct(
constant, base::nullopt, new_arguments, speculation_mode,
kMissingArgumentsAreUndefined, result_hints);
}
for (auto virtual_closure : callback.virtual_closures()) {
ProcessCalleeForCallOrConstruct(
Callee(virtual_closure), base::nullopt, new_arguments,
speculation_mode, kMissingArgumentsAreUndefined, result_hints);
}
}
break;
case Builtins::kFunctionPrototypeApply:
if (arguments.size() >= 1) {
// Drop hints for all arguments except the user-given receiver.
Hints const new_receiver =
arguments.size() >= 2
? arguments[1]
: Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(),
zone());
HintsVector new_arguments({new_receiver}, zone());
for (auto constant : arguments[0].constants()) {
ProcessCalleeForCallOrConstruct(
constant, base::nullopt, new_arguments, speculation_mode,
kMissingArgumentsAreUnknown, result_hints);
}
for (auto const& virtual_closure : arguments[0].virtual_closures()) {
ProcessCalleeForCallOrConstruct(
Callee(virtual_closure), base::nullopt, new_arguments,
speculation_mode, kMissingArgumentsAreUnknown, result_hints);
}
}
break;
case Builtins::kPromiseConstructor:
if (arguments.size() >= 1) {