// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/compiler/serializer-for-background-compilation.h" #include <sstream> #include "src/base/optional.h" #include "src/compiler/access-info.h" #include "src/compiler/bytecode-analysis.h" #include "src/compiler/compilation-dependencies.h" #include "src/compiler/js-heap-broker.h" #include "src/compiler/serializer-hints.h" #include "src/compiler/zone-stats.h" #include "src/handles/handles-inl.h" #include "src/ic/call-optimization.h" #include "src/interpreter/bytecode-array-iterator.h" #include "src/objects/code.h" #include "src/objects/js-array-inl.h" #include "src/objects/js-regexp-inl.h" #include "src/objects/literal-objects-inl.h" #include "src/objects/shared-function-info-inl.h" #include "src/zone/zone-containers.h" #include "src/zone/zone.h" namespace v8 { namespace internal { namespace compiler { #define KILL_ENVIRONMENT_LIST(V) \ V(Abort) \ V(ReThrow) \ V(Throw) #define CLEAR_ACCUMULATOR_LIST(V) \ V(CallRuntime) \ V(CloneObject) \ V(CreateArrayFromIterable) \ V(CreateEmptyObjectLiteral) \ V(CreateMappedArguments) \ V(CreateRestParameter) \ V(CreateUnmappedArguments) \ V(DeletePropertySloppy) \ V(DeletePropertyStrict) \ V(ForInContinue) \ V(ForInEnumerate) \ V(ForInStep) \ V(LogicalNot) \ V(SetPendingMessage) \ V(TestNull) \ V(TestReferenceEqual) \ V(TestTypeOf) \ V(TestUndefined) \ V(TestUndetectable) \ V(ToBooleanLogicalNot) \ V(ToName) \ V(ToString) \ V(TypeOf) #define UNCONDITIONAL_JUMPS_LIST(V) \ V(Jump) \ V(JumpConstant) \ V(JumpLoop) #define CONDITIONAL_JUMPS_LIST(V) \ V(JumpIfFalse) \ V(JumpIfFalseConstant) \ V(JumpIfJSReceiver) \ V(JumpIfJSReceiverConstant) \ V(JumpIfNotNull) \ V(JumpIfNotNullConstant) \ V(JumpIfNotUndefined) \ V(JumpIfNotUndefinedConstant) \ V(JumpIfNull) \ V(JumpIfNullConstant) \ V(JumpIfToBooleanFalse) \ V(JumpIfToBooleanFalseConstant) \ V(JumpIfToBooleanTrue) \ V(JumpIfToBooleanTrueConstant) \ V(JumpIfTrue) \ V(JumpIfTrueConstant) \ V(JumpIfUndefined) \ V(JumpIfUndefinedConstant) \ V(JumpIfUndefinedOrNull) \ V(JumpIfUndefinedOrNullConstant) #define IGNORED_BYTECODE_LIST(V) \ V(CallRuntimeForPair) \ V(CollectTypeProfile) \ V(DebugBreak0) \ V(DebugBreak1) \ V(DebugBreak2) \ V(DebugBreak3) \ V(DebugBreak4) \ V(DebugBreak5) \ V(DebugBreak6) \ V(DebugBreakExtraWide) \ V(DebugBreakWide) \ V(Debugger) \ V(IncBlockCounter) \ V(ResumeGenerator) \ V(SuspendGenerator) \ V(ThrowIfNotSuperConstructor) \ V(ThrowSuperAlreadyCalledIfNotHole) \ V(ThrowSuperNotCalledIfHole) \ V(ToObject) #define UNREACHABLE_BYTECODE_LIST(V) \ V(ExtraWide) \ V(Illegal) \ V(Wide) #define BINARY_OP_LIST(V) \ V(Add) \ V(AddSmi) \ V(BitwiseAnd) \ V(BitwiseAndSmi) \ V(BitwiseOr) \ V(BitwiseOrSmi) \ V(BitwiseXor) \ V(BitwiseXorSmi) \ V(Div) \ V(DivSmi) \ V(Exp) \ V(ExpSmi) \ V(Mod) \ V(ModSmi) \ V(Mul) \ V(MulSmi) \ V(ShiftLeft) \ V(ShiftLeftSmi) \ V(ShiftRight) \ V(ShiftRightSmi) \ V(ShiftRightLogical) \ V(ShiftRightLogicalSmi) \ V(Sub) \ V(SubSmi) #define UNARY_OP_LIST(V) \ V(BitwiseNot) \ V(Dec) \ V(Inc) \ V(Negate) #define COMPARE_OP_LIST(V) \ V(TestEqual) \ V(TestEqualStrict) \ V(TestGreaterThan) \ V(TestGreaterThanOrEqual) \ V(TestLessThan) \ V(TestLessThanOrEqual) #define SUPPORTED_BYTECODE_LIST(V) \ V(CallAnyReceiver) \ V(CallJSRuntime) \ V(CallNoFeedback) \ V(CallProperty) \ V(CallProperty0) \ V(CallProperty1) \ V(CallProperty2) \ V(CallUndefinedReceiver) \ V(CallUndefinedReceiver0) \ V(CallUndefinedReceiver1) \ V(CallUndefinedReceiver2) \ V(CallWithSpread) \ V(Construct) \ V(ConstructWithSpread) \ V(CreateArrayLiteral) \ V(CreateBlockContext) \ V(CreateCatchContext) \ V(CreateClosure) \ V(CreateEmptyArrayLiteral) \ V(CreateEvalContext) \ V(CreateFunctionContext) \ V(CreateObjectLiteral) \ V(CreateRegExpLiteral) \ V(CreateWithContext) \ V(ForInNext) \ V(ForInPrepare) \ V(GetIterator) \ V(GetSuperConstructor) \ V(GetTemplateObject) \ V(InvokeIntrinsic) \ V(LdaConstant) \ V(LdaContextSlot) \ V(LdaCurrentContextSlot) \ V(LdaImmutableContextSlot) \ V(LdaImmutableCurrentContextSlot) \ V(LdaModuleVariable) \ V(LdaFalse) \ V(LdaGlobal) \ V(LdaGlobalInsideTypeof) \ V(LdaKeyedProperty) \ V(LdaLookupContextSlot) \ V(LdaLookupContextSlotInsideTypeof) \ V(LdaLookupGlobalSlot) \ V(LdaLookupGlobalSlotInsideTypeof) \ V(LdaLookupSlot) \ V(LdaLookupSlotInsideTypeof) \ V(LdaNamedProperty) \ V(LdaNamedPropertyFromSuper) \ V(LdaNamedPropertyNoFeedback) \ V(LdaNull) \ V(Ldar) \ V(LdaSmi) \ V(LdaTheHole) \ V(LdaTrue) \ V(LdaUndefined) \ V(LdaZero) \ V(Mov) \ V(PopContext) \ V(PushContext) \ V(Return) \ V(StaContextSlot) \ V(StaCurrentContextSlot) \ V(StaDataPropertyInLiteral) \ V(StaGlobal) \ V(StaInArrayLiteral) \ V(StaKeyedProperty) \ V(StaLookupSlot) \ V(StaModuleVariable) \ V(StaNamedOwnProperty) \ V(StaNamedProperty) \ V(StaNamedPropertyNoFeedback) \ V(Star) \ V(SwitchOnGeneratorState) \ V(SwitchOnSmiNoFeedback) \ V(TestIn) \ V(TestInstanceOf) \ V(ThrowReferenceErrorIfHole) \ V(ToNumber) \ V(ToNumeric) \ BINARY_OP_LIST(V) \ COMPARE_OP_LIST(V) \ CLEAR_ACCUMULATOR_LIST(V) \ CONDITIONAL_JUMPS_LIST(V) \ IGNORED_BYTECODE_LIST(V) \ KILL_ENVIRONMENT_LIST(V) \ UNARY_OP_LIST(V) \ UNCONDITIONAL_JUMPS_LIST(V) \ UNREACHABLE_BYTECODE_LIST(V) struct HintsImpl : public ZoneObject { explicit HintsImpl(Zone* zone) : zone_(zone) {} ConstantsSet constants_; MapsSet maps_; VirtualClosuresSet virtual_closures_; VirtualContextsSet virtual_contexts_; VirtualBoundFunctionsSet virtual_bound_functions_; Zone* const zone_; }; void Hints::EnsureAllocated(Zone* zone, bool check_zone_equality) { if (IsAllocated()) { if (check_zone_equality) CHECK_EQ(zone, impl_->zone_); // ... else {zone} lives no longer than {impl_->zone_} but we have no way of // checking that. } else { impl_ = zone->New<HintsImpl>(zone); } DCHECK(IsAllocated()); } struct VirtualBoundFunction { Hints const bound_target; HintsVector const bound_arguments; VirtualBoundFunction(Hints const& target, const HintsVector& arguments) : bound_target(target), bound_arguments(arguments) {} bool operator==(const VirtualBoundFunction& other) const { if (bound_arguments.size() != other.bound_arguments.size()) return false; if (bound_target != other.bound_target) return false; for (size_t i = 0; i < bound_arguments.size(); ++i) { if (bound_arguments[i] != other.bound_arguments[i]) return false; } return true; } }; // A VirtualClosure is a SharedFunctionInfo and a FeedbackVector, plus // Hints about the context in which a closure will be created from them. class VirtualClosure { public: VirtualClosure(Handle<JSFunction> function, Isolate* isolate, Zone* zone); VirtualClosure(Handle<SharedFunctionInfo> shared, Handle<FeedbackVector> feedback_vector, Hints const& context_hints); Handle<SharedFunctionInfo> shared() const { return shared_; } Handle<FeedbackVector> feedback_vector() const { return feedback_vector_; } Hints const& context_hints() const { return context_hints_; } bool operator==(const VirtualClosure& other) const { // A feedback vector is never used for more than one SFI. There might, // however, be two virtual closures with the same SFI and vector, but // different context hints. crbug.com/1024282 has a link to a document // describing why the context_hints_ might be different in that case. DCHECK_IMPLIES(feedback_vector_.equals(other.feedback_vector_), shared_.equals(other.shared_)); return feedback_vector_.equals(other.feedback_vector_) && context_hints_ == other.context_hints_; } private: Handle<SharedFunctionInfo> const shared_; Handle<FeedbackVector> const feedback_vector_; Hints const context_hints_; }; // A CompilationSubject is a VirtualClosure, optionally with a matching // concrete closure. class CompilationSubject { public: explicit CompilationSubject(VirtualClosure virtual_closure) : virtual_closure_(virtual_closure), closure_() {} // The zone parameter is to correctly initialize the virtual closure, // which contains zone-allocated context information. CompilationSubject(Handle<JSFunction> closure, Isolate* isolate, Zone* zone); const VirtualClosure& virtual_closure() const { return virtual_closure_; } MaybeHandle<JSFunction> closure() const { return closure_; } private: VirtualClosure const virtual_closure_; MaybeHandle<JSFunction> const closure_; }; // A Callee is either a JSFunction (which may not have a feedback vector), or a // VirtualClosure. Note that this is different from CompilationSubject, which // always has a VirtualClosure. class Callee { public: explicit Callee(Handle<JSFunction> jsfunction) : jsfunction_(jsfunction), virtual_closure_() {} explicit Callee(VirtualClosure const& virtual_closure) : jsfunction_(), virtual_closure_(virtual_closure) {} Handle<SharedFunctionInfo> shared(Isolate* isolate) const { return virtual_closure_.has_value() ? virtual_closure_->shared() : handle(jsfunction_.ToHandleChecked()->shared(), isolate); } bool HasFeedbackVector() const { Handle<JSFunction> function; return virtual_closure_.has_value() || jsfunction_.ToHandleChecked()->has_feedback_vector(); } CompilationSubject ToCompilationSubject(Isolate* isolate, Zone* zone) const { CHECK(HasFeedbackVector()); return virtual_closure_.has_value() ? CompilationSubject(*virtual_closure_) : CompilationSubject(jsfunction_.ToHandleChecked(), isolate, zone); } private: MaybeHandle<JSFunction> const jsfunction_; base::Optional<VirtualClosure> const virtual_closure_; }; // If a list of arguments (hints) is shorter than the function's parameter // count, this enum expresses what we know about the missing arguments. enum MissingArgumentsPolicy { kMissingArgumentsAreUndefined, // ... as in the JS undefined value kMissingArgumentsAreUnknown, }; // The SerializerForBackgroundCompilation makes sure that the relevant function // data such as bytecode, SharedFunctionInfo and FeedbackVector, used by later // optimizations in the compiler, is copied to the heap broker. class SerializerForBackgroundCompilation { public: SerializerForBackgroundCompilation( ZoneStats* zone_stats, JSHeapBroker* broker, CompilationDependencies* dependencies, Handle<JSFunction> closure, SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset); Hints Run(); // NOTE: Returns empty for an // already-serialized function. class Environment; private: SerializerForBackgroundCompilation( ZoneStats* zone_stats, JSHeapBroker* broker, CompilationDependencies* dependencies, CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments, MissingArgumentsPolicy padding, SerializerForBackgroundCompilationFlags flags, int nesting_level); bool BailoutOnUninitialized(ProcessedFeedback const& feedback); void TraverseBytecode(); #define DECLARE_VISIT_BYTECODE(name, ...) \ void Visit##name(interpreter::BytecodeArrayIterator* iterator); SUPPORTED_BYTECODE_LIST(DECLARE_VISIT_BYTECODE) #undef DECLARE_VISIT_BYTECODE Hints& register_hints(interpreter::Register reg); // Return a vector containing the hints for the given register range (in // order). Also prepare these hints for feedback backpropagation by allocating // any that aren't yet allocated. HintsVector PrepareArgumentsHints(interpreter::Register first, size_t count); // Like above except that the hints have to be given directly. template <typename... MoreHints> HintsVector PrepareArgumentsHints(Hints* hints, MoreHints... more); void ProcessCalleeForCallOrConstruct(Callee const& callee, base::Optional<Hints> new_target, const HintsVector& arguments, SpeculationMode speculation_mode, MissingArgumentsPolicy padding, Hints* result_hints); void ProcessCalleeForCallOrConstruct(Handle<Object> callee, base::Optional<Hints> new_target, const HintsVector& arguments, SpeculationMode speculation_mode, MissingArgumentsPolicy padding, Hints* result_hints); void ProcessCallOrConstruct(Hints callee, base::Optional<Hints> new_target, HintsVector* arguments, FeedbackSlot slot, MissingArgumentsPolicy padding); void ProcessCallOrConstructRecursive(Hints const& callee, base::Optional<Hints> new_target, const HintsVector& arguments, SpeculationMode speculation_mode, MissingArgumentsPolicy padding, Hints* result_hints); void ProcessNewTargetForConstruct(Hints const& new_target, Hints* result_hints); void ProcessCallVarArgs( ConvertReceiverMode receiver_mode, Hints const& callee, interpreter::Register first_reg, int reg_count, FeedbackSlot slot, MissingArgumentsPolicy padding = kMissingArgumentsAreUndefined); void ProcessApiCall(Handle<SharedFunctionInfo> target, const HintsVector& arguments); void ProcessReceiverMapForApiCall(FunctionTemplateInfoRef target, Handle<Map> receiver); void ProcessBuiltinCall(Handle<SharedFunctionInfo> target, base::Optional<Hints> new_target, const HintsVector& arguments, SpeculationMode speculation_mode, MissingArgumentsPolicy padding, Hints* result_hints); void ProcessJump(interpreter::BytecodeArrayIterator* iterator); void ProcessKeyedPropertyAccess(Hints* receiver, Hints const& key, FeedbackSlot slot, AccessMode access_mode, bool honor_bailout_on_uninitialized); void ProcessNamedPropertyAccess(Hints* receiver, NameRef const& name, FeedbackSlot slot, AccessMode access_mode); void ProcessNamedSuperPropertyAccess(Hints* receiver, NameRef const& name, FeedbackSlot slot, AccessMode access_mode); void ProcessNamedAccess(Hints* receiver, NamedAccessFeedback const& feedback, AccessMode access_mode, Hints* result_hints); void ProcessNamedSuperAccess(Hints* receiver, NamedAccessFeedback const& feedback, AccessMode access_mode, Hints* result_hints); void ProcessElementAccess(Hints const& receiver, Hints const& key, ElementAccessFeedback const& feedback, AccessMode access_mode); void ProcessMinimorphicPropertyAccess( MinimorphicLoadPropertyAccessFeedback const& feedback, FeedbackSource const& source); void ProcessModuleVariableAccess( interpreter::BytecodeArrayIterator* iterator); void ProcessHintsForObjectCreate(Hints const& prototype); void ProcessMapHintsForPromises(Hints const& receiver_hints); void ProcessHintsForPromiseResolve(Hints const& resolution_hints); void ProcessHintsForHasInPrototypeChain(Hints const& instance_hints); void ProcessHintsForRegExpTest(Hints const& regexp_hints); PropertyAccessInfo ProcessMapForRegExpTest(MapRef map); void ProcessHintsForFunctionBind(Hints const& receiver_hints); void ProcessHintsForObjectGetPrototype(Hints const& object_hints); void ProcessConstantForOrdinaryHasInstance(HeapObjectRef const& constructor, bool* walk_prototypes); void ProcessConstantForInstanceOf(ObjectRef const& constant, bool* walk_prototypes); void ProcessHintsForOrdinaryHasInstance(Hints const& constructor_hints, Hints const& instance_hints); void ProcessGlobalAccess(FeedbackSlot slot, bool is_load); void ProcessCompareOperation(FeedbackSlot slot); void ProcessForIn(FeedbackSlot slot); void ProcessUnaryOrBinaryOperation(FeedbackSlot slot, bool honor_bailout_on_uninitialized); PropertyAccessInfo ProcessMapForNamedPropertyAccess( Hints* receiver, base::Optional<MapRef> receiver_map, MapRef lookup_start_object_map, NameRef const& name, AccessMode access_mode, base::Optional<JSObjectRef> concrete_receiver, Hints* result_hints); void ProcessCreateContext(interpreter::BytecodeArrayIterator* iterator, int scopeinfo_operand_index); enum ContextProcessingMode { kIgnoreSlot, kSerializeSlot, }; void ProcessContextAccess(Hints const& context_hints, int slot, int depth, ContextProcessingMode mode, Hints* result_hints = nullptr); void ProcessImmutableLoad(ContextRef const& context, int slot, ContextProcessingMode mode, Hints* new_accumulator_hints); void ProcessLdaLookupGlobalSlot(interpreter::BytecodeArrayIterator* iterator); void ProcessLdaLookupContextSlot( interpreter::BytecodeArrayIterator* iterator); // Performs extension lookups for [0, depth) like // BytecodeGraphBuilder::CheckContextExtensions(). void ProcessCheckContextExtensions(int depth); Hints RunChildSerializer(CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments, MissingArgumentsPolicy padding); // When (forward-)branching bytecodes are encountered, e.g. a conditional // jump, we call ContributeToJumpTargetEnvironment to "remember" the current // environment, associated with the jump target offset. When serialization // eventually reaches that offset, we call IncorporateJumpTargetEnvironment to // merge that environment back into whatever is the current environment then. // Note: Since there may be multiple jumps to the same target, // ContributeToJumpTargetEnvironment may actually do a merge as well. void ContributeToJumpTargetEnvironment(int target_offset); void IncorporateJumpTargetEnvironment(int target_offset); VirtualClosure function() const { return function_; } Hints& return_value_hints() { return return_value_hints_; } Handle<FeedbackVector> feedback_vector() const; Handle<BytecodeArray> bytecode_array() const; BytecodeAnalysis const& GetBytecodeAnalysis( SerializationPolicy policy = SerializationPolicy::kAssumeSerialized); JSHeapBroker* broker() const { return broker_; } CompilationDependencies* dependencies() const { return dependencies_; } Zone* zone() { return zone_scope_.zone(); } Environment* environment() const { return environment_; } SerializerForBackgroundCompilationFlags flags() const { return flags_; } BailoutId osr_offset() const { return osr_offset_; } JSHeapBroker* const broker_; CompilationDependencies* const dependencies_; ZoneStats::Scope zone_scope_; SerializerForBackgroundCompilationFlags const flags_; // Instead of storing the virtual_closure here, we could extract it from the // {closure_hints_} but that would be cumbersome. VirtualClosure const function_; BailoutId const osr_offset_; ZoneUnorderedMap<int, Environment*> jump_target_environments_; Environment* const environment_; HintsVector const arguments_; Hints return_value_hints_; Hints closure_hints_; int nesting_level_ = 0; }; void RunSerializerForBackgroundCompilation( ZoneStats* zone_stats, JSHeapBroker* broker, CompilationDependencies* dependencies, Handle<JSFunction> closure, SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset) { SerializerForBackgroundCompilation serializer( zone_stats, broker, dependencies, closure, flags, osr_offset); serializer.Run(); } using BytecodeArrayIterator = interpreter::BytecodeArrayIterator; VirtualClosure::VirtualClosure(Handle<SharedFunctionInfo> shared, Handle<FeedbackVector> feedback_vector, Hints const& context_hints) : shared_(shared), feedback_vector_(feedback_vector), context_hints_(context_hints) { // The checked invariant rules out recursion and thus avoids complexity. CHECK(context_hints_.virtual_closures().IsEmpty()); } VirtualClosure::VirtualClosure(Handle<JSFunction> function, Isolate* isolate, Zone* zone) : shared_(handle(function->shared(), isolate)), feedback_vector_(function->feedback_vector(), isolate), context_hints_( Hints::SingleConstant(handle(function->context(), isolate), zone)) { // The checked invariant rules out recursion and thus avoids complexity. CHECK(context_hints_.virtual_closures().IsEmpty()); } CompilationSubject::CompilationSubject(Handle<JSFunction> closure, Isolate* isolate, Zone* zone) : virtual_closure_(closure, isolate, zone), closure_(closure) { CHECK(closure->has_feedback_vector()); } Hints Hints::Copy(Zone* zone) const { if (!IsAllocated()) return *this; Hints result; result.EnsureAllocated(zone); result.impl_->constants_ = impl_->constants_; result.impl_->maps_ = impl_->maps_; result.impl_->virtual_contexts_ = impl_->virtual_contexts_; result.impl_->virtual_closures_ = impl_->virtual_closures_; result.impl_->virtual_bound_functions_ = impl_->virtual_bound_functions_; return result; } bool Hints::operator==(Hints const& other) const { if (impl_ == other.impl_) return true; if (IsEmpty() && other.IsEmpty()) return true; return IsAllocated() && other.IsAllocated() && constants() == other.constants() && virtual_closures() == other.virtual_closures() && maps() == other.maps() && virtual_contexts() == other.virtual_contexts() && virtual_bound_functions() == other.virtual_bound_functions(); } bool Hints::operator!=(Hints const& other) const { return !(*this == other); } #ifdef ENABLE_SLOW_DCHECKS bool Hints::Includes(Hints const& other) const { if (impl_ == other.impl_ || other.IsEmpty()) return true; return IsAllocated() && constants().Includes(other.constants()) && virtual_closures().Includes(other.virtual_closures()) && maps().Includes(other.maps()); } #endif Hints Hints::SingleConstant(Handle<Object> constant, Zone* zone) { Hints result; result.AddConstant(constant, zone, nullptr); return result; } Hints Hints::SingleMap(Handle<Map> map, Zone* zone) { Hints result; result.AddMap(map, zone, nullptr); return result; } ConstantsSet Hints::constants() const { return IsAllocated() ? impl_->constants_ : ConstantsSet(); } MapsSet Hints::maps() const { return IsAllocated() ? impl_->maps_ : MapsSet(); } VirtualClosuresSet Hints::virtual_closures() const { return IsAllocated() ? impl_->virtual_closures_ : VirtualClosuresSet(); } VirtualContextsSet Hints::virtual_contexts() const { return IsAllocated() ? impl_->virtual_contexts_ : VirtualContextsSet(); } VirtualBoundFunctionsSet Hints::virtual_bound_functions() const { return IsAllocated() ? impl_->virtual_bound_functions_ : VirtualBoundFunctionsSet(); } void Hints::AddVirtualContext(VirtualContext const& virtual_context, Zone* zone, JSHeapBroker* broker) { EnsureAllocated(zone); if (impl_->virtual_contexts_.Size() >= kMaxHintsSize) { TRACE_BROKER_MISSING(broker, "opportunity - limit for virtual contexts reached."); return; } impl_->virtual_contexts_.Add(virtual_context, impl_->zone_); } void Hints::AddConstant(Handle<Object> constant, Zone* zone, JSHeapBroker* broker) { EnsureAllocated(zone); if (impl_->constants_.Size() >= kMaxHintsSize) { TRACE_BROKER_MISSING(broker, "opportunity - limit for constants reached."); return; } impl_->constants_.Add(constant, impl_->zone_); } void Hints::AddMap(Handle<Map> map, Zone* zone, JSHeapBroker* broker, bool check_zone_equality) { EnsureAllocated(zone, check_zone_equality); if (impl_->maps_.Size() >= kMaxHintsSize) { TRACE_BROKER_MISSING(broker, "opportunity - limit for maps reached."); return; } impl_->maps_.Add(map, impl_->zone_); } void Hints::AddVirtualClosure(VirtualClosure const& virtual_closure, Zone* zone, JSHeapBroker* broker) { EnsureAllocated(zone); if (impl_->virtual_closures_.Size() >= kMaxHintsSize) { TRACE_BROKER_MISSING(broker, "opportunity - limit for virtual closures reached."); return; } impl_->virtual_closures_.Add(virtual_closure, impl_->zone_); } void Hints::AddVirtualBoundFunction(VirtualBoundFunction const& bound_function, Zone* zone, JSHeapBroker* broker) { EnsureAllocated(zone); if (impl_->virtual_bound_functions_.Size() >= kMaxHintsSize) { TRACE_BROKER_MISSING( broker, "opportunity - limit for virtual bound functions reached."); return; } // TODO(mslekova): Consider filtering the hints in the added bound function, // for example: a) Remove any non-JS(Bound)Function constants, b) Truncate the // argument vector the formal parameter count. impl_->virtual_bound_functions_.Add(bound_function, impl_->zone_); } void Hints::Add(Hints const& other, Zone* zone, JSHeapBroker* broker) { if (impl_ == other.impl_ || other.IsEmpty()) return; EnsureAllocated(zone); if (!Union(other)) { TRACE_BROKER_MISSING(broker, "opportunity - hints limit reached."); } } Hints Hints::CopyToParentZone(Zone* zone, JSHeapBroker* broker) const { if (!IsAllocated()) return *this; Hints result; for (auto const& x : constants()) result.AddConstant(x, zone, broker); for (auto const& x : maps()) result.AddMap(x, zone, broker); for (auto const& x : virtual_contexts()) result.AddVirtualContext(x, zone, broker); // Adding hints from a child serializer run means copying data out from // a zone that's being destroyed. VirtualClosures and VirtualBoundFunction // have zone allocated data, so we've got to make a deep copy to eliminate // traces of the dying zone. for (auto const& x : virtual_closures()) { VirtualClosure new_virtual_closure( x.shared(), x.feedback_vector(), x.context_hints().CopyToParentZone(zone, broker)); result.AddVirtualClosure(new_virtual_closure, zone, broker); } for (auto const& x : virtual_bound_functions()) { HintsVector new_arguments_hints(zone); for (auto hint : x.bound_arguments) { new_arguments_hints.push_back(hint.CopyToParentZone(zone, broker)); } VirtualBoundFunction new_bound_function( x.bound_target.CopyToParentZone(zone, broker), new_arguments_hints); result.AddVirtualBoundFunction(new_bound_function, zone, broker); } return result; } bool Hints::IsEmpty() const { if (!IsAllocated()) return true; return constants().IsEmpty() && maps().IsEmpty() && virtual_closures().IsEmpty() && virtual_contexts().IsEmpty() && virtual_bound_functions().IsEmpty(); } std::ostream& operator<<(std::ostream& out, const VirtualContext& virtual_context) { out << "Distance " << virtual_context.distance << " from " << Brief(*virtual_context.context) << std::endl; return out; } std::ostream& operator<<(std::ostream& out, const Hints& hints); std::ostream& operator<<(std::ostream& out, const VirtualClosure& virtual_closure) { out << Brief(*virtual_closure.shared()) << std::endl; out << Brief(*virtual_closure.feedback_vector()) << std::endl; !virtual_closure.context_hints().IsEmpty() && out << virtual_closure.context_hints() << "):" << std::endl; return out; } std::ostream& operator<<(std::ostream& out, const VirtualBoundFunction& virtual_bound_function) { out << std::endl << " Target: " << virtual_bound_function.bound_target; out << " Arguments:" << std::endl; for (auto hint : virtual_bound_function.bound_arguments) { out << " " << hint; } return out; } std::ostream& operator<<(std::ostream& out, const Hints& hints) { out << "(impl_ = " << hints.impl_ << ")\n"; for (Handle<Object> constant : hints.constants()) { out << " constant " << Brief(*constant) << std::endl; } for (Handle<Map> map : hints.maps()) { out << " map " << Brief(*map) << std::endl; } for (VirtualClosure const& virtual_closure : hints.virtual_closures()) { out << " virtual closure " << virtual_closure << std::endl; } for (VirtualContext const& virtual_context : hints.virtual_contexts()) { out << " virtual context " << virtual_context << std::endl; } for (VirtualBoundFunction const& virtual_bound_function : hints.virtual_bound_functions()) { out << " virtual bound function " << virtual_bound_function << std::endl; } return out; } void Hints::Reset(Hints* other, Zone* zone) { other->EnsureShareable(zone); *this = *other; DCHECK(IsAllocated()); } class SerializerForBackgroundCompilation::Environment : public ZoneObject { public: Environment(Zone* zone, CompilationSubject function); Environment(Zone* zone, Isolate* isolate, CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments, MissingArgumentsPolicy padding); bool IsDead() const { return !alive_; } void Kill() { DCHECK(!IsDead()); alive_ = false; DCHECK(IsDead()); } void Resurrect() { DCHECK(IsDead()); alive_ = true; DCHECK(!IsDead()); } // Merge {other} into {this} environment (leaving {other} unmodified). void Merge(Environment* other, Zone* zone, JSHeapBroker* broker); Hints const& current_context_hints() const { return current_context_hints_; } Hints const& accumulator_hints() const { return accumulator_hints_; } Hints& current_context_hints() { return current_context_hints_; } Hints& accumulator_hints() { return accumulator_hints_; } Hints& register_hints(interpreter::Register reg); private: friend std::ostream& operator<<(std::ostream& out, const Environment& env); Hints current_context_hints_; Hints accumulator_hints_; HintsVector parameters_hints_; // First parameter is the receiver. HintsVector locals_hints_; bool alive_ = true; }; SerializerForBackgroundCompilation::Environment::Environment( Zone* zone, CompilationSubject function) : parameters_hints_(function.virtual_closure() .shared() ->GetBytecodeArray() .parameter_count(), Hints(), zone), locals_hints_(function.virtual_closure() .shared() ->GetBytecodeArray() .register_count(), Hints(), zone) { // Consume the virtual_closure's context hint information. current_context_hints_ = function.virtual_closure().context_hints(); } SerializerForBackgroundCompilation::Environment::Environment( Zone* zone, Isolate* isolate, CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments, MissingArgumentsPolicy padding) : Environment(zone, function) { // Set the hints for the actually passed arguments, at most up to // the parameter_count. for (size_t i = 0; i < std::min(arguments.size(), parameters_hints_.size()); ++i) { parameters_hints_[i] = arguments[i]; } if (padding == kMissingArgumentsAreUndefined) { Hints const undefined_hint = Hints::SingleConstant(isolate->factory()->undefined_value(), zone); for (size_t i = arguments.size(); i < parameters_hints_.size(); ++i) { parameters_hints_[i] = undefined_hint; } } else { DCHECK_EQ(padding, kMissingArgumentsAreUnknown); } // Set hints for new_target. interpreter::Register new_target_reg = function.virtual_closure() .shared() ->GetBytecodeArray() .incoming_new_target_or_generator_register(); if (new_target_reg.is_valid()) { Hints& hints = register_hints(new_target_reg); CHECK(hints.IsEmpty()); if (new_target.has_value()) hints = *new_target; } } Hints& SerializerForBackgroundCompilation::register_hints( interpreter::Register reg) { if (reg.is_function_closure()) return closure_hints_; return environment()->register_hints(reg); } Hints& SerializerForBackgroundCompilation::Environment::register_hints( interpreter::Register reg) { if (reg.is_current_context()) return current_context_hints_; if (reg.is_parameter()) { return parameters_hints_[reg.ToParameterIndex( static_cast<int>(parameters_hints_.size()))]; } DCHECK(!reg.is_function_closure()); CHECK_LT(reg.index(), locals_hints_.size()); return locals_hints_[reg.index()]; } void SerializerForBackgroundCompilation::Environment::Merge( Environment* other, Zone* zone, JSHeapBroker* broker) { // {other} is guaranteed to have the same layout because it comes from an // earlier bytecode in the same function. DCHECK_EQ(parameters_hints_.size(), other->parameters_hints_.size()); DCHECK_EQ(locals_hints_.size(), other->locals_hints_.size()); if (IsDead()) { parameters_hints_ = other->parameters_hints_; locals_hints_ = other->locals_hints_; current_context_hints_ = other->current_context_hints_; accumulator_hints_ = other->accumulator_hints_; Resurrect(); } else { for (size_t i = 0; i < parameters_hints_.size(); ++i) { parameters_hints_[i].Merge(other->parameters_hints_[i], zone, broker); } for (size_t i = 0; i < locals_hints_.size(); ++i) { locals_hints_[i].Merge(other->locals_hints_[i], zone, broker); } current_context_hints_.Merge(other->current_context_hints_, zone, broker); accumulator_hints_.Merge(other->accumulator_hints_, zone, broker); } CHECK(!IsDead()); } bool Hints::Union(Hints const& other) { CHECK(IsAllocated()); if (impl_->constants_.Size() + other.constants().Size() > kMaxHintsSize || impl_->maps_.Size() + other.maps().Size() > kMaxHintsSize || impl_->virtual_closures_.Size() + other.virtual_closures().Size() > kMaxHintsSize || impl_->virtual_contexts_.Size() + other.virtual_contexts().Size() > kMaxHintsSize || impl_->virtual_bound_functions_.Size() + other.virtual_bound_functions().Size() > kMaxHintsSize) { return false; } Zone* zone = impl_->zone_; impl_->constants_.Union(other.constants(), zone); impl_->maps_.Union(other.maps(), zone); impl_->virtual_closures_.Union(other.virtual_closures(), zone); impl_->virtual_contexts_.Union(other.virtual_contexts(), zone); impl_->virtual_bound_functions_.Union(other.virtual_bound_functions(), zone); return true; } void Hints::Merge(Hints const& other, Zone* zone, JSHeapBroker* broker) { if (impl_ == other.impl_) { return; } if (!IsAllocated()) { *this = other.Copy(zone); DCHECK(IsAllocated()); return; } *this = this->Copy(zone); if (!Union(other)) { TRACE_BROKER_MISSING(broker, "opportunity - hints limit reached."); } DCHECK(IsAllocated()); } std::ostream& operator<<( std::ostream& out, const SerializerForBackgroundCompilation::Environment& env) { std::ostringstream output_stream; if (env.IsDead()) { output_stream << "dead\n"; } else { output_stream << "alive\n"; for (size_t i = 0; i < env.parameters_hints_.size(); ++i) { Hints const& hints = env.parameters_hints_[i]; if (!hints.IsEmpty()) { if (i == 0) { output_stream << "Hints for <this>: "; } else { output_stream << "Hints for a" << i - 1 << ": "; } output_stream << hints; } } for (size_t i = 0; i < env.locals_hints_.size(); ++i) { Hints const& hints = env.locals_hints_[i]; if (!hints.IsEmpty()) { output_stream << "Hints for r" << i << ": " << hints; } } } if (!env.current_context_hints().IsEmpty()) { output_stream << "Hints for <context>: " << env.current_context_hints(); } if (!env.accumulator_hints().IsEmpty()) { output_stream << "Hints for <accumulator>: " << env.accumulator_hints(); } out << output_stream.str(); return out; } SerializerForBackgroundCompilation::SerializerForBackgroundCompilation( ZoneStats* zone_stats, JSHeapBroker* broker, CompilationDependencies* dependencies, Handle<JSFunction> closure, SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset) : broker_(broker), dependencies_(dependencies), zone_scope_(zone_stats, ZONE_NAME), flags_(flags), function_(closure, broker->isolate(), zone()), osr_offset_(osr_offset), jump_target_environments_(zone()), environment_(zone()->New<Environment>( zone(), CompilationSubject(closure, broker_->isolate(), zone()))), arguments_(zone()) { closure_hints_.AddConstant(closure, zone(), broker_); JSFunctionRef(broker, closure).Serialize(); TRACE_BROKER(broker_, "Hints for <closure>: " << closure_hints_); TRACE_BROKER(broker_, "Initial environment:\n" << *environment_); } SerializerForBackgroundCompilation::SerializerForBackgroundCompilation( ZoneStats* zone_stats, JSHeapBroker* broker, CompilationDependencies* dependencies, CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments, MissingArgumentsPolicy padding, SerializerForBackgroundCompilationFlags flags, int nesting_level) : broker_(broker), dependencies_(dependencies), zone_scope_(zone_stats, ZONE_NAME), flags_(flags), function_(function.virtual_closure()), osr_offset_(BailoutId::None()), jump_target_environments_(zone()), environment_(zone()->New<Environment>(zone(), broker_->isolate(), function, new_target, arguments, padding)), arguments_(arguments), nesting_level_(nesting_level) { Handle<JSFunction> closure; if (function.closure().ToHandle(&closure)) { closure_hints_.AddConstant(closure, zone(), broker); JSFunctionRef(broker, closure).Serialize(); } else { closure_hints_.AddVirtualClosure(function.virtual_closure(), zone(), broker); } TRACE_BROKER(broker_, "Hints for <closure>: " << closure_hints_); TRACE_BROKER(broker_, "Initial environment:\n" << *environment_); } bool SerializerForBackgroundCompilation::BailoutOnUninitialized( ProcessedFeedback const& feedback) { DCHECK(!environment()->IsDead()); if (!(flags() & SerializerForBackgroundCompilationFlag::kBailoutOnUninitialized)) { return false; } if (!osr_offset().IsNone()) { // Exclude OSR from this optimization because we might end up skipping the // OSR entry point. TODO(neis): Support OSR? return false; } if (broker()->is_turboprop() && feedback.slot_kind() == FeedbackSlotKind::kCall) { return false; } if (feedback.IsInsufficient()) { environment()->Kill(); return true; } return false; } Hints SerializerForBackgroundCompilation::Run() { TraceScope tracer(broker(), this, "SerializerForBackgroundCompilation::Run"); if (nesting_level_ >= FLAG_max_serializer_nesting) { TRACE_BROKER_MISSING( broker(), "opportunity - Reached max nesting level for " "SerializerForBackgroundCompilation::Run, bailing out.\n"); return Hints(); } TRACE_BROKER_MEMORY(broker(), "[serializer start] Broker zone usage: " << broker()->zone()->allocation_size()); SharedFunctionInfoRef shared(broker(), function().shared()); FeedbackVectorRef feedback_vector_ref(broker(), feedback_vector()); if (!broker()->ShouldBeSerializedForCompilation(shared, feedback_vector_ref, arguments_)) { TRACE_BROKER(broker(), "opportunity - Already ran serializer for SharedFunctionInfo " << Brief(*shared.object()) << ", bailing out.\n"); return Hints(); } { HintsVector arguments_copy_in_broker_zone(broker()->zone()); for (auto const& hints : arguments_) { arguments_copy_in_broker_zone.push_back( hints.CopyToParentZone(broker()->zone(), broker())); } broker()->SetSerializedForCompilation(shared, feedback_vector_ref, arguments_copy_in_broker_zone); } // We eagerly call the {EnsureSourcePositionsAvailable} for all serialized // SFIs while still on the main thread. Source positions will later be used // by JSInliner::ReduceJSCall. if (flags() & SerializerForBackgroundCompilationFlag::kCollectSourcePositions) { SharedFunctionInfo::EnsureSourcePositionsAvailable(broker()->isolate(), shared.object()); } feedback_vector_ref.Serialize(); TraverseBytecode(); if (return_value_hints().IsEmpty()) { TRACE_BROKER(broker(), "Return value hints: none"); } else { TRACE_BROKER(broker(), "Return value hints: " << return_value_hints()); } TRACE_BROKER_MEMORY(broker(), "[serializer end] Broker zone usage: " << broker()->zone()->allocation_size()); return return_value_hints(); } class HandlerRangeMatcher { public: HandlerRangeMatcher(BytecodeArrayIterator const& bytecode_iterator, Handle<BytecodeArray> bytecode_array) : bytecode_iterator_(bytecode_iterator) { HandlerTable table(*bytecode_array); for (int i = 0, n = table.NumberOfRangeEntries(); i < n; ++i) { ranges_.insert({table.GetRangeStart(i), table.GetRangeEnd(i), table.GetRangeHandler(i)}); } ranges_iterator_ = ranges_.cbegin(); } using OffsetReporter = std::function<void(int handler_offset)>; void HandlerOffsetForCurrentPosition(const OffsetReporter& offset_reporter) { CHECK(!bytecode_iterator_.done()); const int current_offset = bytecode_iterator_.current_offset(); // Remove outdated try ranges from the stack. while (!stack_.empty()) { const int end = stack_.top().end; if (end < current_offset) { stack_.pop(); } else { break; } } // Advance the iterator and maintain the stack. while (ranges_iterator_ != ranges_.cend() && ranges_iterator_->start <= current_offset) { if (ranges_iterator_->end >= current_offset) { stack_.push(*ranges_iterator_); if (ranges_iterator_->start == current_offset) { offset_reporter(ranges_iterator_->handler); } } ranges_iterator_++; } if (!stack_.empty() && stack_.top().start < current_offset) { offset_reporter(stack_.top().handler); } } private: BytecodeArrayIterator const& bytecode_iterator_; struct Range { int start; int end; int handler; friend bool operator<(const Range& a, const Range& b) { if (a.start < b.start) return true; if (a.start == b.start) { if (a.end < b.end) return true; CHECK_GT(a.end, b.end); } return false; } }; std::set<Range> ranges_; std::set<Range>::const_iterator ranges_iterator_; std::stack<Range> stack_; }; Handle<FeedbackVector> SerializerForBackgroundCompilation::feedback_vector() const { return function().feedback_vector(); } Handle<BytecodeArray> SerializerForBackgroundCompilation::bytecode_array() const { return handle(function().shared()->GetBytecodeArray(), broker()->isolate()); } BytecodeAnalysis const& SerializerForBackgroundCompilation::GetBytecodeAnalysis( SerializationPolicy policy) { return broker()->GetBytecodeAnalysis( bytecode_array(), osr_offset(), flags() & SerializerForBackgroundCompilationFlag::kAnalyzeEnvironmentLiveness, policy); } void SerializerForBackgroundCompilation::TraverseBytecode() { BytecodeAnalysis const& bytecode_analysis = GetBytecodeAnalysis(SerializationPolicy::kSerializeIfNeeded); BytecodeArrayRef(broker(), bytecode_array()).SerializeForCompilation(); BytecodeArrayIterator iterator(bytecode_array()); HandlerRangeMatcher try_start_matcher(iterator, bytecode_array()); bool has_one_shot_bytecode = false; for (; !iterator.done(); iterator.Advance()) { has_one_shot_bytecode = has_one_shot_bytecode || interpreter::Bytecodes::IsOneShotBytecode(iterator.current_bytecode()); int const current_offset = iterator.current_offset(); // TODO(mvstanton): we might want to ignore the current environment if we // are at the start of a catch handler. IncorporateJumpTargetEnvironment(current_offset); TRACE_BROKER(broker(), "Handling bytecode: " << current_offset << " " << iterator.current_bytecode()); TRACE_BROKER(broker(), "Current environment: " << *environment()); if (environment()->IsDead()) { continue; // Skip this bytecode since TF won't generate code for it. } auto save_handler_environments = [&](int handler_offset) { auto it = jump_target_environments_.find(handler_offset); if (it == jump_target_environments_.end()) { ContributeToJumpTargetEnvironment(handler_offset); TRACE_BROKER(broker(), "Handler offset for current pos: " << handler_offset); } }; try_start_matcher.HandlerOffsetForCurrentPosition( save_handler_environments); if (bytecode_analysis.IsLoopHeader(current_offset)) { // Graph builder might insert jumps to resume targets in the loop body. LoopInfo const& loop_info = bytecode_analysis.GetLoopInfoFor(current_offset); for (const auto& target : loop_info.resume_jump_targets()) { ContributeToJumpTargetEnvironment(target.target_offset()); } } switch (iterator.current_bytecode()) { #define DEFINE_BYTECODE_CASE(name) \ case interpreter::Bytecode::k##name: \ Visit##name(&iterator); \ break; SUPPORTED_BYTECODE_LIST(DEFINE_BYTECODE_CASE) #undef DEFINE_BYTECODE_CASE } } if (has_one_shot_bytecode) { broker()->isolate()->CountUsage( v8::Isolate::UseCounterFeature::kOptimizedFunctionWithOneShotBytecode); } } void SerializerForBackgroundCompilation::VisitGetIterator( BytecodeArrayIterator* iterator) { Hints* receiver = ®ister_hints(iterator->GetRegisterOperand(0)); FeedbackSlot load_slot = iterator->GetSlotOperand(1); FeedbackSlot call_slot = iterator->GetSlotOperand(2); Handle<Name> name = broker()->isolate()->factory()->iterator_symbol(); ProcessNamedPropertyAccess(receiver, NameRef(broker(), name), load_slot, AccessMode::kLoad); if (environment()->IsDead()) return; Hints callee; HintsVector args = PrepareArgumentsHints(receiver); ProcessCallOrConstruct(callee, base::nullopt, &args, call_slot, kMissingArgumentsAreUndefined); } void SerializerForBackgroundCompilation::VisitGetSuperConstructor( BytecodeArrayIterator* iterator) { interpreter::Register dst = iterator->GetRegisterOperand(0); Hints result_hints; for (auto constant : environment()->accumulator_hints().constants()) { // For JSNativeContextSpecialization::ReduceJSGetSuperConstructor. if (!constant->IsJSFunction()) continue; MapRef map(broker(), handle(HeapObject::cast(*constant).map(), broker()->isolate())); map.SerializePrototype(); ObjectRef proto = map.prototype(); if (proto.IsHeapObject() && proto.AsHeapObject().map().is_constructor()) { result_hints.AddConstant(proto.object(), zone(), broker()); } } register_hints(dst) = result_hints; } void SerializerForBackgroundCompilation::VisitGetTemplateObject( BytecodeArrayIterator* iterator) { TemplateObjectDescriptionRef description( broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(1); FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForTemplateObject(source); if (feedback.IsInsufficient()) { environment()->accumulator_hints() = Hints(); } else { JSArrayRef template_object = feedback.AsTemplateObject().value(); environment()->accumulator_hints() = Hints::SingleConstant(template_object.object(), zone()); } } void SerializerForBackgroundCompilation::VisitLdaTrue( BytecodeArrayIterator* iterator) { environment()->accumulator_hints() = Hints::SingleConstant( broker()->isolate()->factory()->true_value(), zone()); } void SerializerForBackgroundCompilation::VisitLdaFalse( BytecodeArrayIterator* iterator) { environment()->accumulator_hints() = Hints::SingleConstant( broker()->isolate()->factory()->false_value(), zone()); } void SerializerForBackgroundCompilation::VisitLdaTheHole( BytecodeArrayIterator* iterator) { environment()->accumulator_hints() = Hints::SingleConstant( broker()->isolate()->factory()->the_hole_value(), zone()); } void SerializerForBackgroundCompilation::VisitLdaUndefined( BytecodeArrayIterator* iterator) { environment()->accumulator_hints() = Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone()); } void SerializerForBackgroundCompilation::VisitLdaNull( BytecodeArrayIterator* iterator) { environment()->accumulator_hints() = Hints::SingleConstant( broker()->isolate()->factory()->null_value(), zone()); } void SerializerForBackgroundCompilation::VisitLdaZero( BytecodeArrayIterator* iterator) { environment()->accumulator_hints() = Hints::SingleConstant( handle(Smi::FromInt(0), broker()->isolate()), zone()); } void SerializerForBackgroundCompilation::VisitLdaSmi( BytecodeArrayIterator* iterator) { Handle<Smi> smi(Smi::FromInt(iterator->GetImmediateOperand(0)), broker()->isolate()); environment()->accumulator_hints() = Hints::SingleConstant(smi, zone()); } void SerializerForBackgroundCompilation::VisitInvokeIntrinsic( BytecodeArrayIterator* iterator) { Runtime::FunctionId functionId = iterator->GetIntrinsicIdOperand(0); // For JSNativeContextSpecialization::ReduceJSAsyncFunctionResolve and // JSNativeContextSpecialization::ReduceJSResolvePromise. switch (functionId) { case Runtime::kInlineAsyncFunctionResolve: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncFunctionResolve)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); size_t reg_count = iterator->GetRegisterCountOperand(2); CHECK_EQ(reg_count, 3); HintsVector args = PrepareArgumentsHints(first_reg, reg_count); Hints const& resolution_hints = args[1]; // The resolution object. ProcessHintsForPromiseResolve(resolution_hints); return; } case Runtime::kInlineAsyncGeneratorReject: case Runtime::kAsyncGeneratorReject: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncGeneratorReject)); break; } case Runtime::kInlineAsyncGeneratorResolve: case Runtime::kAsyncGeneratorResolve: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncGeneratorResolve)); break; } case Runtime::kInlineAsyncGeneratorYield: case Runtime::kAsyncGeneratorYield: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncGeneratorYield)); break; } case Runtime::kInlineAsyncGeneratorAwaitUncaught: case Runtime::kAsyncGeneratorAwaitUncaught: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncGeneratorAwaitUncaught)); break; } case Runtime::kInlineAsyncGeneratorAwaitCaught: case Runtime::kAsyncGeneratorAwaitCaught: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncGeneratorAwaitCaught)); break; } case Runtime::kInlineAsyncFunctionAwaitUncaught: case Runtime::kAsyncFunctionAwaitUncaught: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncFunctionAwaitUncaught)); break; } case Runtime::kInlineAsyncFunctionAwaitCaught: case Runtime::kAsyncFunctionAwaitCaught: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncFunctionAwaitCaught)); break; } case Runtime::kInlineAsyncFunctionReject: case Runtime::kAsyncFunctionReject: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncFunctionReject)); break; } case Runtime::kAsyncFunctionResolve: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncFunctionResolve)); break; } case Runtime::kInlineCopyDataProperties: case Runtime::kCopyDataProperties: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kCopyDataProperties)); break; } case Runtime::kInlineGetImportMetaObject: { Hints const& context_hints = environment()->current_context_hints(); for (auto x : context_hints.constants()) { ContextRef(broker(), x) .GetModule(SerializationPolicy::kSerializeIfNeeded) .Serialize(); } for (auto x : context_hints.virtual_contexts()) { ContextRef(broker(), x.context) .GetModule(SerializationPolicy::kSerializeIfNeeded) .Serialize(); } break; } default: { break; } } environment()->accumulator_hints() = Hints(); } void SerializerForBackgroundCompilation::VisitLdaConstant( BytecodeArrayIterator* iterator) { ObjectRef object( broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); environment()->accumulator_hints() = Hints::SingleConstant(object.object(), zone()); } void SerializerForBackgroundCompilation::VisitPushContext( BytecodeArrayIterator* iterator) { register_hints(iterator->GetRegisterOperand(0)) .Reset(&environment()->current_context_hints(), zone()); environment()->current_context_hints().Reset( &environment()->accumulator_hints(), zone()); } void SerializerForBackgroundCompilation::VisitPopContext( BytecodeArrayIterator* iterator) { environment()->current_context_hints().Reset( ®ister_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( ®ister_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(®ister_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, ¶meters, 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 = ®ister_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 = ®ister_hints(iterator->GetRegisterOperand(1)); Hints* arg1 = ®ister_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 = ®ister_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 = ®ister_hints(iterator->GetRegisterOperand(1)); Hints* arg0 = ®ister_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 = ®ister_hints(iterator->GetRegisterOperand(1)); Hints* arg0 = ®ister_hints(iterator->GetRegisterOperand(2)); Hints* arg1 = ®ister_hints(iterator->GetRegisterOperand(3)); FeedbackSlot slot = iterator->GetSlotOperand(4); HintsVector args = PrepareArgumentsHints(receiver, arg0, arg1); ProcessCallOrConstruct(callee, base::nullopt, &args, slot, kMissingArgumentsAreUndefined); } void SerializerForBackgroundCompilation::VisitCallWithSpread( BytecodeArrayIterator* iterator) { Hints const& callee = register_hints(iterator->GetRegisterOperand(0)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2)); FeedbackSlot slot = iterator->GetSlotOperand(3); ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count, slot, kMissingArgumentsAreUnknown); } void SerializerForBackgroundCompilation::VisitCallJSRuntime( BytecodeArrayIterator* iterator) { const int runtime_index = iterator->GetNativeContextIndexOperand(0); ObjectRef constant = broker() ->target_native_context() .get(runtime_index, SerializationPolicy::kSerializeIfNeeded) .value(); Hints const callee = Hints::SingleConstant(constant.object(), zone()); interpreter::Register first_reg = iterator->GetRegisterOperand(1); int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2)); ProcessCallVarArgs(ConvertReceiverMode::kNullOrUndefined, callee, first_reg, reg_count, FeedbackSlot::Invalid()); } Hints SerializerForBackgroundCompilation::RunChildSerializer( CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments, MissingArgumentsPolicy padding) { SerializerForBackgroundCompilation child_serializer( zone_scope_.zone_stats(), broker(), dependencies(), function, new_target, arguments, padding, flags(), nesting_level_ + 1); Hints result = child_serializer.Run(); // The Hints returned by the call to Run are allocated in the zone // created by the child serializer. Adding those hints to a hints // object created in our zone will preserve the information. return result.CopyToParentZone(zone(), broker()); } void SerializerForBackgroundCompilation::ProcessCalleeForCallOrConstruct( Callee const& callee, base::Optional<Hints> new_target, const HintsVector& arguments, SpeculationMode speculation_mode, MissingArgumentsPolicy padding, Hints* result_hints) { Handle<SharedFunctionInfo> shared = callee.shared(broker()->isolate()); if (shared->IsApiFunction()) { ProcessApiCall(shared, arguments); DCHECK_NE(shared->GetInlineability(), SharedFunctionInfo::kIsInlineable); } else if (shared->HasBuiltinId()) { ProcessBuiltinCall(shared, new_target, arguments, speculation_mode, padding, result_hints); DCHECK_NE(shared->GetInlineability(), SharedFunctionInfo::kIsInlineable); } else if ((flags() & SerializerForBackgroundCompilationFlag::kEnableTurboInlining) && shared->GetInlineability() == SharedFunctionInfo::kIsInlineable && callee.HasFeedbackVector()) { CompilationSubject subject = callee.ToCompilationSubject(broker()->isolate(), zone()); result_hints->Add( RunChildSerializer(subject, new_target, arguments, padding), zone(), broker()); } } namespace { // Returns the innermost bound target and inserts all bound arguments and // {original_arguments} into {expanded_arguments} in the appropriate order. JSReceiverRef UnrollBoundFunction(JSBoundFunctionRef const& bound_function, JSHeapBroker* broker, const HintsVector& original_arguments, HintsVector* expanded_arguments, Zone* zone) { DCHECK(expanded_arguments->empty()); JSReceiverRef target = bound_function.AsJSReceiver(); HintsVector reversed_bound_arguments(zone); for (; target.IsJSBoundFunction(); target = target.AsJSBoundFunction().bound_target_function()) { for (int i = target.AsJSBoundFunction().bound_arguments().length() - 1; i >= 0; --i) { Hints const arg = Hints::SingleConstant( target.AsJSBoundFunction().bound_arguments().get(i).object(), zone); reversed_bound_arguments.push_back(arg); } Hints const arg = Hints::SingleConstant( target.AsJSBoundFunction().bound_this().object(), zone); reversed_bound_arguments.push_back(arg); } expanded_arguments->insert(expanded_arguments->end(), reversed_bound_arguments.rbegin(), reversed_bound_arguments.rend()); expanded_arguments->insert(expanded_arguments->end(), original_arguments.begin(), original_arguments.end()); return target; } } // namespace void SerializerForBackgroundCompilation::ProcessCalleeForCallOrConstruct( Handle<Object> callee, base::Optional<Hints> new_target, const HintsVector& arguments, SpeculationMode speculation_mode, MissingArgumentsPolicy padding, Hints* result_hints) { const HintsVector* actual_arguments = &arguments; HintsVector expanded_arguments(zone()); if (callee->IsJSBoundFunction()) { JSBoundFunctionRef bound_function(broker(), Handle<JSBoundFunction>::cast(callee)); if (!bound_function.Serialize()) return; callee = UnrollBoundFunction(bound_function, broker(), arguments, &expanded_arguments, zone()) .object(); actual_arguments = &expanded_arguments; } if (!callee->IsJSFunction()) return; JSFunctionRef function(broker(), Handle<JSFunction>::cast(callee)); function.Serialize(); Callee new_callee(function.object()); ProcessCalleeForCallOrConstruct(new_callee, new_target, *actual_arguments, speculation_mode, padding, result_hints); } void SerializerForBackgroundCompilation::ProcessCallOrConstruct( Hints callee, base::Optional<Hints> new_target, HintsVector* arguments, FeedbackSlot slot, MissingArgumentsPolicy padding) { SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation; if (!slot.IsInvalid()) { FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForCall(source); if (BailoutOnUninitialized(feedback)) return; if (!feedback.IsInsufficient()) { // Incorporate feedback into hints copy to simplify processing. // TODO(neis): Modify the original hints instead? speculation_mode = feedback.AsCall().speculation_mode(); // Incorporate target feedback into hints copy to simplify processing. base::Optional<HeapObjectRef> target = feedback.AsCall().target(); if (target.has_value() && (target->map().is_callable() || target->IsFeedbackCell())) { callee = callee.Copy(zone()); // TODO(mvstanton): if the map isn't callable then we have an allocation // site, and it may make sense to add the Array JSFunction constant. if (new_target.has_value()) { // Construct; feedback is new_target, which often is also the callee. new_target = new_target->Copy(zone()); new_target->AddConstant(target->object(), zone(), broker()); callee.AddConstant(target->object(), zone(), broker()); } else { // Call; target is feedback cell or callee. if (target->IsFeedbackCell() && target->AsFeedbackCell().value().IsFeedbackVector()) { FeedbackVectorRef vector = target->AsFeedbackCell().value().AsFeedbackVector(); vector.Serialize(); VirtualClosure virtual_closure( vector.shared_function_info().object(), vector.object(), Hints()); callee.AddVirtualClosure(virtual_closure, zone(), broker()); } else { callee.AddConstant(target->object(), zone(), broker()); } } } } } Hints result_hints_from_new_target; if (new_target.has_value()) { ProcessNewTargetForConstruct(*new_target, &result_hints_from_new_target); // These hints are a good guess at the resulting object, so they are useful // for both the accumulator and the constructor call's receiver. The latter // is still missing completely in {arguments} so add it now. arguments->insert(arguments->begin(), result_hints_from_new_target); } // For JSNativeContextSpecialization::InferRootMap Hints new_accumulator_hints = result_hints_from_new_target.Copy(zone()); ProcessCallOrConstructRecursive(callee, new_target, *arguments, speculation_mode, padding, &new_accumulator_hints); environment()->accumulator_hints() = new_accumulator_hints; } void SerializerForBackgroundCompilation::ProcessCallOrConstructRecursive( Hints const& callee, base::Optional<Hints> new_target, const HintsVector& arguments, SpeculationMode speculation_mode, MissingArgumentsPolicy padding, Hints* result_hints) { // For JSCallReducer::ReduceJSCall and JSCallReducer::ReduceJSConstruct. for (auto constant : callee.constants()) { ProcessCalleeForCallOrConstruct(constant, new_target, arguments, speculation_mode, padding, result_hints); } // For JSCallReducer::ReduceJSCall and JSCallReducer::ReduceJSConstruct. for (auto hint : callee.virtual_closures()) { ProcessCalleeForCallOrConstruct(Callee(hint), new_target, arguments, speculation_mode, padding, result_hints); } for (auto hint : callee.virtual_bound_functions()) { HintsVector new_arguments = hint.bound_arguments; new_arguments.insert(new_arguments.end(), arguments.begin(), arguments.end()); ProcessCallOrConstructRecursive(hint.bound_target, new_target, new_arguments, speculation_mode, padding, result_hints); } } void SerializerForBackgroundCompilation::ProcessNewTargetForConstruct( Hints const& new_target_hints, Hints* result_hints) { for (Handle<Object> target : new_target_hints.constants()) { if (target->IsJSBoundFunction()) { // Unroll the bound function. while (target->IsJSBoundFunction()) { target = handle( Handle<JSBoundFunction>::cast(target)->bound_target_function(), broker()->isolate()); } } if (target->IsJSFunction()) { Handle<JSFunction> new_target(Handle<JSFunction>::cast(target)); if (new_target->has_prototype_slot(broker()->isolate()) && new_target->has_initial_map()) { result_hints->AddMap( handle(new_target->initial_map(), broker()->isolate()), zone(), broker()); } } } for (auto const& virtual_bound_function : new_target_hints.virtual_bound_functions()) { ProcessNewTargetForConstruct(virtual_bound_function.bound_target, result_hints); } } void SerializerForBackgroundCompilation::ProcessCallVarArgs( ConvertReceiverMode receiver_mode, Hints const& callee, interpreter::Register first_reg, int reg_count, FeedbackSlot slot, MissingArgumentsPolicy padding) { HintsVector args = PrepareArgumentsHints(first_reg, reg_count); // The receiver is either given in the first register or it is implicitly // the {undefined} value. if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { args.insert(args.begin(), Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone())); } ProcessCallOrConstruct(callee, base::nullopt, &args, slot, padding); } void SerializerForBackgroundCompilation::ProcessApiCall( Handle<SharedFunctionInfo> target, const HintsVector& arguments) { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kCallFunctionTemplate_CheckAccess)); ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kCallFunctionTemplate_CheckCompatibleReceiver)); ObjectRef( broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kCallFunctionTemplate_CheckAccessAndCompatibleReceiver)); FunctionTemplateInfoRef target_template_info( broker(), handle(target->function_data(kAcquireLoad), broker()->isolate())); if (!target_template_info.has_call_code()) return; target_template_info.SerializeCallCode(); SharedFunctionInfoRef target_ref(broker(), target); target_ref.SerializeFunctionTemplateInfo(); if (target_template_info.accept_any_receiver() && target_template_info.is_signature_undefined()) { return; } if (arguments.empty()) return; Hints const& receiver_hints = arguments[0]; for (auto hint : receiver_hints.constants()) { if (hint->IsUndefined()) { // The receiver is the global proxy. Handle<JSGlobalProxy> global_proxy = broker()->target_native_context().global_proxy_object().object(); ProcessReceiverMapForApiCall( target_template_info, handle(global_proxy->map(), broker()->isolate())); continue; } if (!hint->IsJSReceiver()) continue; Handle<JSReceiver> receiver(Handle<JSReceiver>::cast(hint)); ProcessReceiverMapForApiCall(target_template_info, handle(receiver->map(), broker()->isolate())); } for (auto receiver_map : receiver_hints.maps()) { ProcessReceiverMapForApiCall(target_template_info, receiver_map); } } void SerializerForBackgroundCompilation::ProcessReceiverMapForApiCall( FunctionTemplateInfoRef target, Handle<Map> receiver) { if (!receiver->is_access_check_needed()) { MapRef receiver_map(broker(), receiver); TRACE_BROKER(broker(), "Serializing holder for target: " << target); target.LookupHolderOfExpectedType(receiver_map, SerializationPolicy::kSerializeIfNeeded); } } void SerializerForBackgroundCompilation::ProcessHintsForObjectCreate( Hints const& prototype) { for (Handle<Object> constant_handle : prototype.constants()) { ObjectRef constant(broker(), constant_handle); if (constant.IsJSObject()) constant.AsJSObject().SerializeObjectCreateMap(); } } void SerializerForBackgroundCompilation::ProcessBuiltinCall( Handle<SharedFunctionInfo> target, base::Optional<Hints> new_target, const HintsVector& arguments, SpeculationMode speculation_mode, MissingArgumentsPolicy padding, Hints* result_hints) { DCHECK(target->HasBuiltinId()); const int builtin_id = target->builtin_id(); const char* name = Builtins::name(builtin_id); TRACE_BROKER(broker(), "Serializing for call to builtin " << name); switch (builtin_id) { case Builtins::kObjectCreate: { if (arguments.size() >= 2) { ProcessHintsForObjectCreate(arguments[1]); } else { ProcessHintsForObjectCreate(Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone())); } break; } case Builtins::kPromisePrototypeCatch: { // For JSCallReducer::ReducePromisePrototypeCatch. if (speculation_mode != SpeculationMode::kDisallowSpeculation) { if (arguments.size() >= 1) { ProcessMapHintsForPromises(arguments[0]); } } break; } case Builtins::kPromisePrototypeFinally: { // For JSCallReducer::ReducePromisePrototypeFinally. if (speculation_mode != SpeculationMode::kDisallowSpeculation) { if (arguments.size() >= 1) { ProcessMapHintsForPromises(arguments[0]); } SharedFunctionInfoRef( broker(), broker()->isolate()->factory()->promise_catch_finally_shared_fun()); SharedFunctionInfoRef( broker(), broker()->isolate()->factory()->promise_then_finally_shared_fun()); } break; } case Builtins::kPromisePrototypeThen: { // For JSCallReducer::ReducePromisePrototypeThen. if (speculation_mode != SpeculationMode::kDisallowSpeculation) { if (arguments.size() >= 1) { ProcessMapHintsForPromises(arguments[0]); } } break; } case Builtins::kPromiseResolveTrampoline: // For JSCallReducer::ReducePromiseInternalResolve and // JSNativeContextSpecialization::ReduceJSResolvePromise. if (arguments.size() >= 1) { Hints const resolution_hints = arguments.size() >= 2 ? arguments[1] : Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone()); ProcessHintsForPromiseResolve(resolution_hints); } break; case Builtins::kRegExpPrototypeTest: case Builtins::kRegExpPrototypeTestFast: // For JSCallReducer::ReduceRegExpPrototypeTest. if (arguments.size() >= 1 && speculation_mode != SpeculationMode::kDisallowSpeculation) { Hints const& regexp_hints = arguments[0]; ProcessHintsForRegExpTest(regexp_hints); } break; case Builtins::kArrayEvery: case Builtins::kArrayFilter: case Builtins::kArrayForEach: case Builtins::kArrayPrototypeFind: case Builtins::kArrayPrototypeFindIndex: case Builtins::kArrayMap: case Builtins::kArraySome: if (arguments.size() >= 2 && speculation_mode != SpeculationMode::kDisallowSpeculation) { Hints const& callback = arguments[1]; // "Call(callbackfn, T, « kValue, k, O »)" HintsVector new_arguments(zone()); new_arguments.push_back( arguments.size() < 3 ? Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone()) : arguments[2]); // T new_arguments.push_back(Hints()); // kValue new_arguments.push_back(Hints()); // k new_arguments.push_back(arguments[0]); // O for (auto constant : callback.constants()) { ProcessCalleeForCallOrConstruct( constant, base::nullopt, new_arguments, speculation_mode, kMissingArgumentsAreUndefined, result_hints); } for (auto virtual_closure : callback.virtual_closures()) { ProcessCalleeForCallOrConstruct( Callee(virtual_closure), base::nullopt, new_arguments, speculation_mode, kMissingArgumentsAreUndefined, result_hints); } } break; case Builtins::kArrayReduce: case Builtins::kArrayReduceRight: if (arguments.size() >= 2 && speculation_mode != SpeculationMode::kDisallowSpeculation) { Hints const& callback = arguments[1]; // "Call(callbackfn, undefined, « accumulator, kValue, k, O »)" HintsVector new_arguments(zone()); new_arguments.push_back(Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone())); new_arguments.push_back(Hints()); // accumulator new_arguments.push_back(Hints()); // kValue new_arguments.push_back(Hints()); // k new_arguments.push_back(arguments[0]); // O for (auto constant : callback.constants()) { ProcessCalleeForCallOrConstruct( constant, base::nullopt, new_arguments, speculation_mode, kMissingArgumentsAreUndefined, result_hints); } for (auto virtual_closure : callback.virtual_closures()) { ProcessCalleeForCallOrConstruct( Callee(virtual_closure), base::nullopt, new_arguments, speculation_mode, kMissingArgumentsAreUndefined, result_hints); } } break; case Builtins::kFunctionPrototypeApply: if (arguments.size() >= 1) { // Drop hints for all arguments except the user-given receiver. Hints const new_receiver = arguments.size() >= 2 ? arguments[1] : Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone()); HintsVector new_arguments({new_receiver}, zone()); for (auto constant : arguments[0].constants()) { ProcessCalleeForCallOrConstruct( constant, base::nullopt, new_arguments, speculation_mode, kMissingArgumentsAreUnknown, result_hints); } for (auto const& virtual_closure : arguments[0].virtual_closures()) { ProcessCalleeForCallOrConstruct( Callee(virtual_closure), base::nullopt, new_arguments, speculation_mode, kMissingArgumentsAreUnknown, result_hints); } } break; case Builtins::kPromiseConstructor: if (arguments.size() >= 1) { // "Call(executor, undefined, « resolvingFunctions.[[Resolve]], // resolvingFunctions.[[Reject]] »)" HintsVector new_arguments( {Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone())}, zone()); for (auto constant : arguments[0].constants()) { ProcessCalleeForCallOrConstruct( constant, base::nullopt, new_arguments, SpeculationMode::kDisallowSpeculation, kMissingArgumentsAreUnknown, result_hints); } for (auto const& virtual_closure : arguments[0].virtual_closures()) { ProcessCalleeForCallOrConstruct( Callee(virtual_closure), base::nullopt, new_arguments, SpeculationMode::kDisallowSpeculation, kMissingArgumentsAreUnknown, result_hints); } } SharedFunctionInfoRef( broker(), broker() ->isolate() ->factory() ->promise_capability_default_reject_shared_fun()); SharedFunctionInfoRef( broker(), broker() ->isolate() ->factory() ->promise_capability_default_resolve_shared_fun()); break; case Builtins::kFunctionPrototypeCall: if (arguments.size() >= 1) { HintsVector new_arguments(arguments.begin() + 1, arguments.end(), zone()); for (auto constant : arguments[0].constants()) { ProcessCalleeForCallOrConstruct(constant, base::nullopt, new_arguments, speculation_mode, padding, result_hints); } for (auto const& virtual_closure : arguments[0].virtual_closures()) { ProcessCalleeForCallOrConstruct( Callee(virtual_closure), base::nullopt, new_arguments, speculation_mode, padding, result_hints); } } break; case Builtins::kReflectApply: case Builtins::kReflectConstruct: if (arguments.size() >= 2) { for (auto constant : arguments[1].constants()) { if (constant->IsJSFunction()) { JSFunctionRef(broker(), constant).Serialize(); } } } break; case Builtins::kObjectPrototypeIsPrototypeOf: if (arguments.size() >= 2) { ProcessHintsForHasInPrototypeChain(arguments[1]); } break; case Builtins::kFunctionPrototypeHasInstance: // For JSCallReducer::ReduceFunctionPrototypeHasInstance. if (arguments.size() >= 2) { ProcessHintsForOrdinaryHasInstance(arguments[0], arguments[1]); } break; case Builtins::kFastFunctionPrototypeBind: if (arguments.size() >= 1 && speculation_mode != SpeculationMode::kDisallowSpeculation) { Hints const& bound_target = arguments[0]; ProcessHintsForFunctionBind(bound_target); HintsVector new_arguments(arguments.begin() + 1, arguments.end(), zone()); result_hints->AddVirtualBoundFunction( VirtualBoundFunction(bound_target, new_arguments), zone(), broker()); } break; case Builtins::kObjectGetPrototypeOf: case Builtins::kReflectGetPrototypeOf: if (arguments.size() >= 2) { ProcessHintsForObjectGetPrototype(arguments[1]); } else { Hints const undefined_hint = Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone()); ProcessHintsForObjectGetPrototype(undefined_hint); } break; case Builtins::kObjectPrototypeGetProto: if (arguments.size() >= 1) { ProcessHintsForObjectGetPrototype(arguments[0]); } break; case Builtins::kMapIteratorPrototypeNext: ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kOrderedHashTableHealIndex)); ObjectRef(broker(), broker()->isolate()->factory()->empty_ordered_hash_map()); break; case Builtins::kSetIteratorPrototypeNext: ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kOrderedHashTableHealIndex)); ObjectRef(broker(), broker()->isolate()->factory()->empty_ordered_hash_set()); break; default: break; } } void SerializerForBackgroundCompilation::ProcessHintsForOrdinaryHasInstance( Hints const& constructor_hints, Hints const& instance_hints) { bool walk_prototypes = false; for (Handle<Object> constructor : constructor_hints.constants()) { // For JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance. if (constructor->IsHeapObject()) { ProcessConstantForOrdinaryHasInstance( HeapObjectRef(broker(), constructor), &walk_prototypes); } } // For JSNativeContextSpecialization::ReduceJSHasInPrototypeChain. if (walk_prototypes) ProcessHintsForHasInPrototypeChain(instance_hints); } void SerializerForBackgroundCompilation::ProcessHintsForHasInPrototypeChain( Hints const& instance_hints) { auto processMap = [&](Handle<Map> map_handle) { MapRef map(broker(), map_handle); while (map.IsJSObjectMap()) { map.SerializePrototype(); map = map.prototype().map(); } }; for (auto hint : instance_hints.constants()) { if (!hint->IsHeapObject()) continue; Handle<HeapObject> object(Handle<HeapObject>::cast(hint)); processMap(handle(object->map(), broker()->isolate())); } for (auto map_hint : instance_hints.maps()) { processMap(map_hint); } } void SerializerForBackgroundCompilation::ProcessHintsForPromiseResolve( Hints const& resolution_hints) { auto processMap = [&](Handle<Map> map) { broker()->GetPropertyAccessInfo( MapRef(broker(), map), NameRef(broker(), broker()->isolate()->factory()->then_string()), AccessMode::kLoad, dependencies(), SerializationPolicy::kSerializeIfNeeded); }; for (auto hint : resolution_hints.constants()) { if (!hint->IsHeapObject()) continue; Handle<HeapObject> resolution(Handle<HeapObject>::cast(hint)); processMap(handle(resolution->map(), broker()->isolate())); } for (auto map_hint : resolution_hints.maps()) { processMap(map_hint); } } void SerializerForBackgroundCompilation::ProcessMapHintsForPromises( Hints const& receiver_hints) { // We need to serialize the prototypes on each receiver map. for (auto constant : receiver_hints.constants()) { if (!constant->IsJSPromise()) continue; Handle<Map> map(Handle<HeapObject>::cast(constant)->map(), broker()->isolate()); MapRef(broker(), map).SerializePrototype(); } for (auto map : receiver_hints.maps()) { if (!map->IsJSPromiseMap()) continue; MapRef(broker(), map).SerializePrototype(); } } PropertyAccessInfo SerializerForBackgroundCompilation::ProcessMapForRegExpTest( MapRef map) { PropertyAccessInfo ai_exec = broker()->GetPropertyAccessInfo( map, NameRef(broker(), broker()->isolate()->factory()->exec_string()), AccessMode::kLoad, dependencies(), SerializationPolicy::kSerializeIfNeeded); Handle<JSObject> holder; if (ai_exec.IsDataConstant() && ai_exec.holder().ToHandle(&holder)) { // The property is on the prototype chain. JSObjectRef holder_ref(broker(), holder); holder_ref.GetOwnDataProperty(ai_exec.field_representation(), ai_exec.field_index(), SerializationPolicy::kSerializeIfNeeded); } return ai_exec; } void SerializerForBackgroundCompilation::ProcessHintsForRegExpTest( Hints const& regexp_hints) { for (auto hint : regexp_hints.constants()) { if (!hint->IsJSRegExp()) continue; Handle<JSRegExp> regexp(Handle<JSRegExp>::cast(hint)); Handle<Map> regexp_map(regexp->map(), broker()->isolate()); PropertyAccessInfo ai_exec = ProcessMapForRegExpTest(MapRef(broker(), regexp_map)); Handle<JSObject> holder; if (ai_exec.IsDataConstant() && !ai_exec.holder().ToHandle(&holder)) { // The property is on the object itself. JSObjectRef holder_ref(broker(), regexp); holder_ref.GetOwnDataProperty(ai_exec.field_representation(), ai_exec.field_index(), SerializationPolicy::kSerializeIfNeeded); } } for (auto map : regexp_hints.maps()) { if (!map->IsJSRegExpMap()) continue; ProcessMapForRegExpTest(MapRef(broker(), map)); } } namespace { void ProcessMapForFunctionBind(MapRef map) { map.SerializePrototype(); int min_nof_descriptors = std::max({JSFunction::kLengthDescriptorIndex, JSFunction::kNameDescriptorIndex}) + 1; if (map.NumberOfOwnDescriptors() >= min_nof_descriptors) { map.SerializeOwnDescriptor( InternalIndex(JSFunction::kLengthDescriptorIndex)); map.SerializeOwnDescriptor(InternalIndex(JSFunction::kNameDescriptorIndex)); } } } // namespace void SerializerForBackgroundCompilation::ProcessHintsForFunctionBind( Hints const& receiver_hints) { for (auto constant : receiver_hints.constants()) { if (!constant->IsJSFunction()) continue; JSFunctionRef function(broker(), constant); function.Serialize(); ProcessMapForFunctionBind(function.map()); } for (auto map : receiver_hints.maps()) { if (!map->IsJSFunctionMap()) continue; MapRef map_ref(broker(), map); ProcessMapForFunctionBind(map_ref); } } void SerializerForBackgroundCompilation::ProcessHintsForObjectGetPrototype( Hints const& object_hints) { for (auto constant : object_hints.constants()) { if (!constant->IsHeapObject()) continue; HeapObjectRef object(broker(), constant); object.map().SerializePrototype(); } for (auto map : object_hints.maps()) { MapRef map_ref(broker(), map); map_ref.SerializePrototype(); } } void SerializerForBackgroundCompilation::ContributeToJumpTargetEnvironment( int target_offset) { auto it = jump_target_environments_.find(target_offset); if (it == jump_target_environments_.end()) { jump_target_environments_[target_offset] = zone()->New<Environment>(*environment()); } else { it->second->Merge(environment(), zone(), broker()); } } void SerializerForBackgroundCompilation::IncorporateJumpTargetEnvironment( int target_offset) { auto it = jump_target_environments_.find(target_offset); if (it != jump_target_environments_.end()) { environment()->Merge(it->second, zone(), broker()); jump_target_environments_.erase(it); } } void SerializerForBackgroundCompilation::ProcessJump( interpreter::BytecodeArrayIterator* iterator) { int jump_target = iterator->GetJumpTargetOffset(); if (iterator->current_offset() < jump_target) { ContributeToJumpTargetEnvironment(jump_target); } } void SerializerForBackgroundCompilation::VisitReturn( BytecodeArrayIterator* iterator) { return_value_hints().Add(environment()->accumulator_hints(), zone(), broker()); environment()->Kill(); } void SerializerForBackgroundCompilation::VisitSwitchOnSmiNoFeedback( interpreter::BytecodeArrayIterator* iterator) { interpreter::JumpTableTargetOffsets targets = iterator->GetJumpTableTargetOffsets(); for (const auto& target : targets) { ContributeToJumpTargetEnvironment(target.target_offset); } } void SerializerForBackgroundCompilation::VisitSwitchOnGeneratorState( interpreter::BytecodeArrayIterator* iterator) { for (const auto& target : GetBytecodeAnalysis().resume_jump_targets()) { ContributeToJumpTargetEnvironment(target.target_offset()); } } void SerializerForBackgroundCompilation::VisitConstruct( BytecodeArrayIterator* iterator) { Hints& new_target = environment()->accumulator_hints(); Hints const& callee = register_hints(iterator->GetRegisterOperand(0)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); size_t reg_count = iterator->GetRegisterCountOperand(2); FeedbackSlot slot = iterator->GetSlotOperand(3); HintsVector args = PrepareArgumentsHints(first_reg, reg_count); ProcessCallOrConstruct(callee, new_target, &args, slot, kMissingArgumentsAreUndefined); } void SerializerForBackgroundCompilation::VisitConstructWithSpread( BytecodeArrayIterator* iterator) { Hints const& new_target = environment()->accumulator_hints(); Hints const& callee = register_hints(iterator->GetRegisterOperand(0)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); size_t reg_count = iterator->GetRegisterCountOperand(2); FeedbackSlot slot = iterator->GetSlotOperand(3); DCHECK_GT(reg_count, 0); reg_count--; // Pop the spread element. HintsVector args = PrepareArgumentsHints(first_reg, reg_count); ProcessCallOrConstruct(callee, new_target, &args, slot, kMissingArgumentsAreUnknown); } void SerializerForBackgroundCompilation::ProcessGlobalAccess(FeedbackSlot slot, bool is_load) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForGlobalAccess(source); if (is_load) { Hints result_hints; if (feedback.kind() == ProcessedFeedback::kGlobalAccess) { // We may be able to contribute to accumulator constant hints. base::Optional<ObjectRef> value = feedback.AsGlobalAccess().GetConstantHint(); if (value.has_value()) { result_hints.AddConstant(value->object(), zone(), broker()); } } else { DCHECK(feedback.IsInsufficient()); } environment()->accumulator_hints() = result_hints; } } void SerializerForBackgroundCompilation::VisitLdaGlobal( BytecodeArrayIterator* iterator) { NameRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(1); ProcessGlobalAccess(slot, true); } void SerializerForBackgroundCompilation::VisitLdaGlobalInsideTypeof( BytecodeArrayIterator* iterator) { VisitLdaGlobal(iterator); } void SerializerForBackgroundCompilation::VisitLdaLookupSlot( BytecodeArrayIterator* iterator) { ObjectRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); environment()->accumulator_hints() = Hints(); } void SerializerForBackgroundCompilation::VisitLdaLookupSlotInsideTypeof( BytecodeArrayIterator* iterator) { ObjectRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); environment()->accumulator_hints() = Hints(); } void SerializerForBackgroundCompilation::ProcessCheckContextExtensions( int depth) { // for BytecodeGraphBuilder::CheckContextExtensions. Hints const& context_hints = environment()->current_context_hints(); for (int i = 0; i < depth; i++) { ProcessContextAccess(context_hints, Context::EXTENSION_INDEX, i, kSerializeSlot); } SharedFunctionInfoRef shared(broker(), function().shared()); shared.SerializeScopeInfoChain(); } void SerializerForBackgroundCompilation::ProcessLdaLookupGlobalSlot( BytecodeArrayIterator* iterator) { ProcessCheckContextExtensions(iterator->GetUnsignedImmediateOperand(2)); // TODO(neis): BytecodeGraphBilder may insert a JSLoadGlobal. VisitLdaGlobal(iterator); } void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlot( BytecodeArrayIterator* iterator) { ProcessLdaLookupGlobalSlot(iterator); } void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlotInsideTypeof( BytecodeArrayIterator* iterator) { ProcessLdaLookupGlobalSlot(iterator); } void SerializerForBackgroundCompilation::VisitStaGlobal( BytecodeArrayIterator* iterator) { NameRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(1); ProcessGlobalAccess(slot, false); } void SerializerForBackgroundCompilation::ProcessLdaLookupContextSlot( BytecodeArrayIterator* iterator) { const int slot_index = iterator->GetIndexOperand(1); const int depth = iterator->GetUnsignedImmediateOperand(2); NameRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); ProcessCheckContextExtensions(depth); environment()->accumulator_hints() = Hints(); ProcessContextAccess(environment()->current_context_hints(), slot_index, depth, kIgnoreSlot); } void SerializerForBackgroundCompilation::VisitLdaLookupContextSlot( BytecodeArrayIterator* iterator) { ProcessLdaLookupContextSlot(iterator); } void SerializerForBackgroundCompilation::VisitLdaLookupContextSlotInsideTypeof( BytecodeArrayIterator* iterator) { ProcessLdaLookupContextSlot(iterator); } // TODO(neis): Avoid duplicating this. namespace { template <class MapContainer> MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) { MapHandles result; for (Handle<Map> map : maps) { if (Map::TryUpdate(isolate, map).ToHandle(&map) && !map->is_abandoned_prototype_map()) { DCHECK(!map->is_deprecated()); result.push_back(map); } } return result; } } // namespace void SerializerForBackgroundCompilation::ProcessCompareOperation( FeedbackSlot slot) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(function().feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForCompareOperation(source); if (BailoutOnUninitialized(feedback)) return; environment()->accumulator_hints() = Hints(); } void SerializerForBackgroundCompilation::ProcessForIn(FeedbackSlot slot) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForForIn(source); if (BailoutOnUninitialized(feedback)) return; environment()->accumulator_hints() = Hints(); } void SerializerForBackgroundCompilation::ProcessUnaryOrBinaryOperation( FeedbackSlot slot, bool honor_bailout_on_uninitialized) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); // Internally V8 uses binary op feedback also for unary ops. ProcessedFeedback const& feedback = broker()->ProcessFeedbackForBinaryOperation(source); if (honor_bailout_on_uninitialized && BailoutOnUninitialized(feedback)) { return; } environment()->accumulator_hints() = Hints(); } PropertyAccessInfo SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess( Hints* receiver, base::Optional<MapRef> receiver_map, MapRef lookup_start_object_map, NameRef const& name, AccessMode access_mode, base::Optional<JSObjectRef> concrete_receiver, Hints* result_hints) { DCHECK_IMPLIES(concrete_receiver.has_value(), receiver_map.has_value()); // For JSNativeContextSpecialization::InferRootMap lookup_start_object_map.SerializeRootMap(); // For JSNativeContextSpecialization::ReduceNamedAccess. JSGlobalProxyRef global_proxy = broker()->target_native_context().global_proxy_object(); JSGlobalObjectRef global_object = broker()->target_native_context().global_object(); if (lookup_start_object_map.equals(global_proxy.map())) { base::Optional<PropertyCellRef> cell = global_object.GetPropertyCell( name, SerializationPolicy::kSerializeIfNeeded); if (access_mode == AccessMode::kLoad && cell.has_value()) { result_hints->AddConstant(cell->value().object(), zone(), broker()); } } PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo( lookup_start_object_map, name, access_mode, dependencies(), SerializationPolicy::kSerializeIfNeeded); // For JSNativeContextSpecialization::InlinePropertySetterCall // and InlinePropertyGetterCall. if (access_info.IsAccessorConstant() && !access_info.constant().is_null()) { if (access_info.constant()->IsJSFunction()) { JSFunctionRef function(broker(), access_info.constant()); if (receiver_map.has_value()) { // For JSCallReducer and JSInlining(Heuristic). HintsVector arguments( {Hints::SingleMap(receiver_map->object(), zone())}, zone()); // In the case of a setter any added result hints won't make sense, but // they will be ignored anyways by Process*PropertyAccess due to the // access mode not being kLoad. ProcessCalleeForCallOrConstruct( function.object(), base::nullopt, arguments, SpeculationMode::kDisallowSpeculation, kMissingArgumentsAreUndefined, result_hints); // For JSCallReducer::ReduceCallApiFunction. Handle<SharedFunctionInfo> sfi = function.shared().object(); if (sfi->IsApiFunction()) { FunctionTemplateInfoRef fti_ref( broker(), handle(sfi->get_api_func_data(), broker()->isolate())); if (fti_ref.has_call_code()) { fti_ref.SerializeCallCode(); ProcessReceiverMapForApiCall(fti_ref, receiver_map->object()); } } } } else if (access_info.constant()->IsJSBoundFunction()) { JSBoundFunctionRef function(broker(), access_info.constant()); // For JSCallReducer::ReduceJSCall. function.Serialize(); } else { FunctionTemplateInfoRef fti(broker(), access_info.constant()); if (fti.has_call_code()) fti.SerializeCallCode(); } } else if (access_info.IsModuleExport()) { // For JSNativeContextSpecialization::BuildPropertyLoad DCHECK(!access_info.constant().is_null()); CellRef(broker(), access_info.constant()); } switch (access_mode) { case AccessMode::kLoad: // For PropertyAccessBuilder::TryBuildLoadConstantDataField if (access_info.IsDataConstant()) { base::Optional<JSObjectRef> holder; Handle<JSObject> prototype; if (access_info.holder().ToHandle(&prototype)) { holder = JSObjectRef(broker(), prototype); } else { CHECK_IMPLIES(concrete_receiver.has_value(), concrete_receiver->map().equals(*receiver_map)); holder = concrete_receiver; } if (holder.has_value()) { base::Optional<ObjectRef> constant(holder->GetOwnDataProperty( access_info.field_representation(), access_info.field_index(), SerializationPolicy::kSerializeIfNeeded)); if (constant.has_value()) { result_hints->AddConstant(constant->object(), zone(), broker()); } } } break; case AccessMode::kStore: case AccessMode::kStoreInLiteral: // For MapInference (StoreField case). if (access_info.IsDataField() || access_info.IsDataConstant()) { Handle<Map> transition_map; if (access_info.transition_map().ToHandle(&transition_map)) { MapRef map_ref(broker(), transition_map); TRACE_BROKER(broker(), "Propagating transition map " << map_ref << " to receiver hints."); receiver->AddMap(transition_map, zone(), broker_, false); } } break; case AccessMode::kHas: break; } return access_info; } void SerializerForBackgroundCompilation::ProcessMinimorphicPropertyAccess( MinimorphicLoadPropertyAccessFeedback const& feedback, FeedbackSource const& source) { broker()->GetPropertyAccessInfo(feedback, source, SerializationPolicy::kSerializeIfNeeded); } void SerializerForBackgroundCompilation::VisitLdaKeyedProperty( BytecodeArrayIterator* iterator) { Hints const& key = environment()->accumulator_hints(); Hints* receiver = ®ister_hints(iterator->GetRegisterOperand(0)); FeedbackSlot slot = iterator->GetSlotOperand(1); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kLoad, true); } void SerializerForBackgroundCompilation::ProcessKeyedPropertyAccess( Hints* receiver, Hints const& key, FeedbackSlot slot, AccessMode access_mode, bool honor_bailout_on_uninitialized) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForPropertyAccess(source, access_mode, base::nullopt); if (honor_bailout_on_uninitialized && BailoutOnUninitialized(feedback)) { return; } Hints new_accumulator_hints; switch (feedback.kind()) { case ProcessedFeedback::kElementAccess: ProcessElementAccess(*receiver, key, feedback.AsElementAccess(), access_mode); break; case ProcessedFeedback::kNamedAccess: ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode, &new_accumulator_hints); break; case ProcessedFeedback::kInsufficient: break; default: UNREACHABLE(); } if (access_mode == AccessMode::kLoad) { environment()->accumulator_hints() = new_accumulator_hints; } } void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess( Hints* receiver, NameRef const& name, FeedbackSlot slot, AccessMode access_mode) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForPropertyAccess(source, access_mode, name); if (BailoutOnUninitialized(feedback)) return; Hints new_accumulator_hints; switch (feedback.kind()) { case ProcessedFeedback::kNamedAccess: DCHECK(name.equals(feedback.AsNamedAccess().name())); ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode, &new_accumulator_hints); break; case ProcessedFeedback::kMinimorphicPropertyAccess: DCHECK(name.equals(feedback.AsMinimorphicPropertyAccess().name())); ProcessMinimorphicPropertyAccess(feedback.AsMinimorphicPropertyAccess(), source); break; case ProcessedFeedback::kInsufficient: break; default: UNREACHABLE(); } if (access_mode == AccessMode::kLoad) { environment()->accumulator_hints() = new_accumulator_hints; } } void SerializerForBackgroundCompilation::ProcessNamedSuperPropertyAccess( Hints* receiver, NameRef const& name, FeedbackSlot slot, AccessMode access_mode) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForPropertyAccess(source, access_mode, name); if (BailoutOnUninitialized(feedback)) return; Hints new_accumulator_hints; switch (feedback.kind()) { case ProcessedFeedback::kNamedAccess: DCHECK(name.equals(feedback.AsNamedAccess().name())); ProcessNamedSuperAccess(receiver, feedback.AsNamedAccess(), access_mode, &new_accumulator_hints); break; case ProcessedFeedback::kMinimorphicPropertyAccess: DCHECK(name.equals(feedback.AsMinimorphicPropertyAccess().name())); ProcessMinimorphicPropertyAccess(feedback.AsMinimorphicPropertyAccess(), source); break; case ProcessedFeedback::kInsufficient: break; default: UNREACHABLE(); } if (access_mode == AccessMode::kLoad) { environment()->accumulator_hints() = new_accumulator_hints; } } void SerializerForBackgroundCompilation::ProcessNamedAccess( Hints* receiver, NamedAccessFeedback const& feedback, AccessMode access_mode, Hints* result_hints) { for (Handle<Map> map : feedback.maps()) { MapRef map_ref(broker(), map); TRACE_BROKER(broker(), "Propagating feedback map " << map_ref << " to receiver hints."); receiver->AddMap(map, zone(), broker_, false); } for (Handle<Map> map : GetRelevantReceiverMaps(broker()->isolate(), receiver->maps())) { MapRef map_ref(broker(), map); ProcessMapForNamedPropertyAccess(receiver, map_ref, map_ref, feedback.name(), access_mode, base::nullopt, result_hints); } for (Handle<Object> hint : receiver->constants()) { ObjectRef object(broker(), hint); if (access_mode == AccessMode::kLoad && object.IsJSObject()) { MapRef map_ref = object.AsJSObject().map(); ProcessMapForNamedPropertyAccess(receiver, map_ref, map_ref, feedback.name(), access_mode, object.AsJSObject(), result_hints); } // For JSNativeContextSpecialization::ReduceJSLoadNamed. if (access_mode == AccessMode::kLoad && object.IsJSFunction() && feedback.name().equals(ObjectRef( broker(), broker()->isolate()->factory()->prototype_string()))) { JSFunctionRef function = object.AsJSFunction(); function.Serialize(); if (result_hints != nullptr && function.has_prototype()) { result_hints->AddConstant(function.prototype().object(), zone(), broker()); } } // TODO(neis): Also record accumulator hint for string.length and maybe // more? } } void SerializerForBackgroundCompilation::ProcessNamedSuperAccess( Hints* receiver, NamedAccessFeedback const& feedback, AccessMode access_mode, Hints* result_hints) { MapHandles receiver_maps = GetRelevantReceiverMaps(broker()->isolate(), receiver->maps()); for (Handle<Map> receiver_map : receiver_maps) { MapRef receiver_map_ref(broker(), receiver_map); for (Handle<Map> feedback_map : feedback.maps()) { MapRef feedback_map_ref(broker(), feedback_map); ProcessMapForNamedPropertyAccess( receiver, receiver_map_ref, feedback_map_ref, feedback.name(), access_mode, base::nullopt, result_hints); } } if (receiver_maps.empty()) { for (Handle<Map> feedback_map : feedback.maps()) { MapRef feedback_map_ref(broker(), feedback_map); ProcessMapForNamedPropertyAccess( receiver, base::nullopt, feedback_map_ref, feedback.name(), access_mode, base::nullopt, result_hints); } } } void SerializerForBackgroundCompilation::ProcessElementAccess( Hints const& receiver, Hints const& key, ElementAccessFeedback const& feedback, AccessMode access_mode) { for (auto const& group : feedback.transition_groups()) { for (Handle<Map> map_handle : group) { MapRef map(broker(), map_handle); switch (access_mode) { case AccessMode::kHas: case AccessMode::kLoad: map.SerializeForElementLoad(); break; case AccessMode::kStore: map.SerializeForElementStore(); break; case AccessMode::kStoreInLiteral: // This operation is fairly local and simple, nothing to serialize. break; } } } for (Handle<Object> hint : receiver.constants()) { ObjectRef receiver_ref(broker(), hint); // For JSNativeContextSpecialization::InferRootMap if (receiver_ref.IsHeapObject()) { receiver_ref.AsHeapObject().map().SerializeRootMap(); } // For JSNativeContextSpecialization::ReduceElementAccess. if (receiver_ref.IsJSTypedArray()) { receiver_ref.AsJSTypedArray().Serialize(); } // For JSNativeContextSpecialization::ReduceElementLoadFromHeapConstant. if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) { for (Handle<Object> hint : key.constants()) { ObjectRef key_ref(broker(), hint); // TODO(neis): Do this for integer-HeapNumbers too? if (key_ref.IsSmi() && key_ref.AsSmi() >= 0) { base::Optional<ObjectRef> element = receiver_ref.GetOwnConstantElement( key_ref.AsSmi(), SerializationPolicy::kSerializeIfNeeded); if (!element.has_value() && receiver_ref.IsJSArray()) { // We didn't find a constant element, but if the receiver is a // cow-array we can exploit the fact that any future write to the // element will replace the whole elements storage. receiver_ref.AsJSArray().GetOwnCowElement( key_ref.AsSmi(), SerializationPolicy::kSerializeIfNeeded); } } } } } // For JSNativeContextSpecialization::InferRootMap for (Handle<Map> map : receiver.maps()) { MapRef map_ref(broker(), map); map_ref.SerializeRootMap(); } } void SerializerForBackgroundCompilation::VisitLdaNamedProperty( BytecodeArrayIterator* iterator) { Hints* receiver = ®ister_hints(iterator->GetRegisterOperand(0)); NameRef name(broker(), iterator->GetConstantForIndexOperand(1, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(2); ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kLoad); } void SerializerForBackgroundCompilation::VisitLdaNamedPropertyFromSuper( BytecodeArrayIterator* iterator) { Hints* receiver = ®ister_hints(iterator->GetRegisterOperand(0)); NameRef name(broker(), iterator->GetConstantForIndexOperand(1, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(2); ProcessNamedSuperPropertyAccess(receiver, name, slot, AccessMode::kLoad); } // TODO(neis): Do feedback-independent serialization also for *NoFeedback // bytecodes. void SerializerForBackgroundCompilation::VisitLdaNamedPropertyNoFeedback( BytecodeArrayIterator* iterator) { NameRef(broker(), iterator->GetConstantForIndexOperand(1, broker()->isolate())); } void SerializerForBackgroundCompilation::VisitStaNamedProperty( BytecodeArrayIterator* iterator) { Hints* receiver = ®ister_hints(iterator->GetRegisterOperand(0)); NameRef name(broker(), iterator->GetConstantForIndexOperand(1, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(2); ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kStore); } void SerializerForBackgroundCompilation::VisitStaNamedPropertyNoFeedback( BytecodeArrayIterator* iterator) { NameRef(broker(), iterator->GetConstantForIndexOperand(1, broker()->isolate())); } void SerializerForBackgroundCompilation::VisitStaNamedOwnProperty( BytecodeArrayIterator* iterator) { Hints* receiver = ®ister_hints(iterator->GetRegisterOperand(0)); NameRef name(broker(), iterator->GetConstantForIndexOperand(1, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(2); ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kStoreInLiteral); } void SerializerForBackgroundCompilation::VisitTestIn( BytecodeArrayIterator* iterator) { Hints* receiver = &environment()->accumulator_hints(); Hints const& key = register_hints(iterator->GetRegisterOperand(0)); FeedbackSlot slot = iterator->GetSlotOperand(1); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kHas, false); } // For JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance. void SerializerForBackgroundCompilation::ProcessConstantForOrdinaryHasInstance( HeapObjectRef const& constructor, bool* walk_prototypes) { if (constructor.IsJSBoundFunction()) { constructor.AsJSBoundFunction().Serialize(); ProcessConstantForInstanceOf( constructor.AsJSBoundFunction().bound_target_function(), walk_prototypes); } else if (constructor.IsJSFunction()) { constructor.AsJSFunction().Serialize(); *walk_prototypes = *walk_prototypes || (constructor.map().has_prototype_slot() && constructor.AsJSFunction().has_prototype() && !constructor.AsJSFunction().PrototypeRequiresRuntimeLookup()); } } void SerializerForBackgroundCompilation::ProcessConstantForInstanceOf( ObjectRef const& constructor, bool* walk_prototypes) { if (!constructor.IsHeapObject()) return; HeapObjectRef constructor_heap_object = constructor.AsHeapObject(); PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo( constructor_heap_object.map(), NameRef(broker(), broker()->isolate()->factory()->has_instance_symbol()), AccessMode::kLoad, dependencies(), SerializationPolicy::kSerializeIfNeeded); if (access_info.IsNotFound()) { ProcessConstantForOrdinaryHasInstance(constructor_heap_object, walk_prototypes); } else if (access_info.IsDataConstant()) { Handle<JSObject> holder; bool found_on_proto = access_info.holder().ToHandle(&holder); JSObjectRef holder_ref = found_on_proto ? JSObjectRef(broker(), holder) : constructor.AsJSObject(); base::Optional<ObjectRef> constant = holder_ref.GetOwnDataProperty( access_info.field_representation(), access_info.field_index(), SerializationPolicy::kSerializeIfNeeded); CHECK(constant.has_value()); if (constant->IsJSFunction()) { JSFunctionRef function = constant->AsJSFunction(); function.Serialize(); if (function.shared().HasBuiltinId() && function.shared().builtin_id() == Builtins::kFunctionPrototypeHasInstance) { // For JSCallReducer::ReduceFunctionPrototypeHasInstance. ProcessConstantForOrdinaryHasInstance(constructor_heap_object, walk_prototypes); } } } } void SerializerForBackgroundCompilation::VisitTestInstanceOf( BytecodeArrayIterator* iterator) { Hints const& lhs = register_hints(iterator->GetRegisterOperand(0)); Hints rhs = environment()->accumulator_hints(); FeedbackSlot slot = iterator->GetSlotOperand(1); if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForInstanceOf(source); // Incorporate feedback (about rhs) into hints copy to simplify processing. // TODO(neis): Propagate into original hints? if (!feedback.IsInsufficient()) { InstanceOfFeedback const& rhs_feedback = feedback.AsInstanceOf(); if (rhs_feedback.value().has_value()) { rhs = rhs.Copy(zone()); Handle<JSObject> constructor = rhs_feedback.value()->object(); rhs.AddConstant(constructor, zone(), broker()); } } bool walk_prototypes = false; for (Handle<Object> constant : rhs.constants()) { ProcessConstantForInstanceOf(ObjectRef(broker(), constant), &walk_prototypes); } if (walk_prototypes) ProcessHintsForHasInPrototypeChain(lhs); environment()->accumulator_hints() = Hints(); } void SerializerForBackgroundCompilation::VisitToNumeric( BytecodeArrayIterator* iterator) { FeedbackSlot slot = iterator->GetSlotOperand(0); ProcessUnaryOrBinaryOperation(slot, false); } void SerializerForBackgroundCompilation::VisitToNumber( BytecodeArrayIterator* iterator) { FeedbackSlot slot = iterator->GetSlotOperand(0); ProcessUnaryOrBinaryOperation(slot, false); } void SerializerForBackgroundCompilation::VisitThrowReferenceErrorIfHole( BytecodeArrayIterator* iterator) { ObjectRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); } void SerializerForBackgroundCompilation::VisitStaKeyedProperty( BytecodeArrayIterator* iterator) { Hints* receiver = ®ister_hints(iterator->GetRegisterOperand(0)); Hints const& key = register_hints(iterator->GetRegisterOperand(1)); FeedbackSlot slot = iterator->GetSlotOperand(2); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStore, true); } void SerializerForBackgroundCompilation::VisitStaInArrayLiteral( BytecodeArrayIterator* iterator) { Hints* receiver = ®ister_hints(iterator->GetRegisterOperand(0)); Hints const& key = register_hints(iterator->GetRegisterOperand(1)); FeedbackSlot slot = iterator->GetSlotOperand(2); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStoreInLiteral, true); } void SerializerForBackgroundCompilation::VisitStaDataPropertyInLiteral( BytecodeArrayIterator* iterator) { Hints* receiver = ®ister_hints(iterator->GetRegisterOperand(0)); Hints const& key = register_hints(iterator->GetRegisterOperand(1)); FeedbackSlot slot = iterator->GetSlotOperand(3); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStoreInLiteral, false); } #define DEFINE_CLEAR_ACCUMULATOR(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ environment()->accumulator_hints() = Hints(); \ } CLEAR_ACCUMULATOR_LIST(DEFINE_CLEAR_ACCUMULATOR) #undef DEFINE_CLEAR_ACCUMULATOR #define DEFINE_CONDITIONAL_JUMP(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ ProcessJump(iterator); \ } CONDITIONAL_JUMPS_LIST(DEFINE_CONDITIONAL_JUMP) #undef DEFINE_CONDITIONAL_JUMP #define DEFINE_UNCONDITIONAL_JUMP(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ ProcessJump(iterator); \ environment()->Kill(); \ } UNCONDITIONAL_JUMPS_LIST(DEFINE_UNCONDITIONAL_JUMP) #undef DEFINE_UNCONDITIONAL_JUMP #define DEFINE_IGNORE(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) {} IGNORED_BYTECODE_LIST(DEFINE_IGNORE) #undef DEFINE_IGNORE #define DEFINE_UNREACHABLE(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ UNREACHABLE(); \ } UNREACHABLE_BYTECODE_LIST(DEFINE_UNREACHABLE) #undef DEFINE_UNREACHABLE #define DEFINE_KILL(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ environment()->Kill(); \ } KILL_ENVIRONMENT_LIST(DEFINE_KILL) #undef DEFINE_KILL #define DEFINE_BINARY_OP(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ FeedbackSlot slot = iterator->GetSlotOperand(1); \ ProcessUnaryOrBinaryOperation(slot, true); \ } BINARY_OP_LIST(DEFINE_BINARY_OP) #undef DEFINE_BINARY_OP #define DEFINE_COMPARE_OP(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ FeedbackSlot slot = iterator->GetSlotOperand(1); \ ProcessCompareOperation(slot); \ } COMPARE_OP_LIST(DEFINE_COMPARE_OP) #undef DEFINE_COMPARE_OP #define DEFINE_UNARY_OP(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ FeedbackSlot slot = iterator->GetSlotOperand(0); \ ProcessUnaryOrBinaryOperation(slot, true); \ } UNARY_OP_LIST(DEFINE_UNARY_OP) #undef DEFINE_UNARY_OP #undef BINARY_OP_LIST #undef CLEAR_ACCUMULATOR_LIST #undef COMPARE_OP_LIST #undef CONDITIONAL_JUMPS_LIST #undef IGNORED_BYTECODE_LIST #undef KILL_ENVIRONMENT_LIST #undef SUPPORTED_BYTECODE_LIST #undef UNARY_OP_LIST #undef UNCONDITIONAL_JUMPS_LIST #undef UNREACHABLE_BYTECODE_LIST } // namespace compiler } // namespace internal } // namespace v8