���� JFIF �� � ( %"1"%)+...383,7(-.-
![]() Server : Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.4.20 System : Linux st2.domain.com 3.10.0-1127.10.1.el7.x86_64 #1 SMP Wed Jun 3 14:28:03 UTC 2020 x86_64 User : apache ( 48) PHP Version : 7.4.20 Disable Function : NONE Directory : /home/real/node-v13.0.1/deps/v8/src/compiler/ |
// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/compiler/serializer-for-background-compilation.h" #include <sstream> #include "src/base/optional.h" #include "src/compiler/access-info.h" #include "src/compiler/bytecode-analysis.h" #include "src/compiler/compilation-dependencies.h" #include "src/compiler/js-heap-broker.h" #include "src/handles/handles-inl.h" #include "src/ic/call-optimization.h" #include "src/interpreter/bytecode-array-iterator.h" #include "src/objects/code.h" #include "src/objects/js-array-inl.h" #include "src/objects/js-regexp-inl.h" #include "src/objects/literal-objects-inl.h" #include "src/objects/shared-function-info-inl.h" #include "src/zone/zone-containers.h" #include "src/zone/zone.h" namespace v8 { namespace internal { namespace compiler { #define CLEAR_ENVIRONMENT_LIST(V) \ V(CallRuntimeForPair) \ V(Debugger) \ V(ResumeGenerator) \ V(SuspendGenerator) #define KILL_ENVIRONMENT_LIST(V) \ V(Abort) \ V(ReThrow) \ V(Throw) #define CLEAR_ACCUMULATOR_LIST(V) \ V(CallRuntime) \ V(CloneObject) \ V(CreateArrayFromIterable) \ V(CreateEmptyArrayLiteral) \ V(CreateEmptyObjectLiteral) \ V(CreateMappedArguments) \ V(CreateRestParameter) \ V(CreateUnmappedArguments) \ V(DeletePropertySloppy) \ V(DeletePropertyStrict) \ V(ForInContinue) \ V(ForInEnumerate) \ V(ForInStep) \ V(LogicalNot) \ V(SetPendingMessage) \ V(TestNull) \ V(TestReferenceEqual) \ V(TestTypeOf) \ V(TestUndefined) \ V(TestUndetectable) \ V(ToBooleanLogicalNot) \ V(ToName) \ V(ToString) \ V(TypeOf) #define UNCONDITIONAL_JUMPS_LIST(V) \ V(Jump) \ V(JumpConstant) \ V(JumpLoop) #define CONDITIONAL_JUMPS_LIST(V) \ V(JumpIfFalse) \ V(JumpIfFalseConstant) \ V(JumpIfJSReceiver) \ V(JumpIfJSReceiverConstant) \ V(JumpIfNotNull) \ V(JumpIfNotNullConstant) \ V(JumpIfNotUndefined) \ V(JumpIfNotUndefinedConstant) \ V(JumpIfNull) \ V(JumpIfNullConstant) \ V(JumpIfToBooleanFalse) \ V(JumpIfToBooleanFalseConstant) \ V(JumpIfToBooleanTrue) \ V(JumpIfToBooleanTrueConstant) \ V(JumpIfTrue) \ V(JumpIfTrueConstant) \ V(JumpIfUndefined) \ V(JumpIfUndefinedConstant) \ V(JumpIfUndefinedOrNull) \ V(JumpIfUndefinedOrNullConstant) #define IGNORED_BYTECODE_LIST(V) \ V(IncBlockCounter) \ V(StackCheck) \ V(ThrowSuperAlreadyCalledIfNotHole) \ V(ThrowSuperNotCalledIfHole) #define UNREACHABLE_BYTECODE_LIST(V) \ V(ExtraWide) \ V(Illegal) \ V(Wide) #define BINARY_OP_LIST(V) \ V(Add) \ V(AddSmi) \ V(BitwiseAnd) \ V(BitwiseAndSmi) \ V(BitwiseOr) \ V(BitwiseOrSmi) \ V(BitwiseXor) \ V(BitwiseXorSmi) \ V(Div) \ V(DivSmi) \ V(Exp) \ V(ExpSmi) \ V(Mod) \ V(ModSmi) \ V(Mul) \ V(MulSmi) \ V(ShiftLeft) \ V(ShiftLeftSmi) \ V(ShiftRight) \ V(ShiftRightSmi) \ V(ShiftRightLogical) \ V(ShiftRightLogicalSmi) \ V(Sub) \ V(SubSmi) #define UNARY_OP_LIST(V) \ V(BitwiseNot) \ V(Dec) \ V(Inc) \ V(Negate) #define COMPARE_OP_LIST(V) \ V(TestEqual) \ V(TestEqualStrict) \ V(TestGreaterThan) \ V(TestGreaterThanOrEqual) \ V(TestLessThan) \ V(TestLessThanOrEqual) #define SUPPORTED_BYTECODE_LIST(V) \ V(CallAnyReceiver) \ V(CallJSRuntime) \ V(CallNoFeedback) \ V(CallProperty) \ V(CallProperty0) \ V(CallProperty1) \ V(CallProperty2) \ V(CallUndefinedReceiver) \ V(CallUndefinedReceiver0) \ V(CallUndefinedReceiver1) \ V(CallUndefinedReceiver2) \ V(CallWithSpread) \ V(Construct) \ V(ConstructWithSpread) \ V(CreateArrayLiteral) \ V(CreateBlockContext) \ V(CreateCatchContext) \ V(CreateClosure) \ V(CreateEvalContext) \ V(CreateFunctionContext) \ V(CreateObjectLiteral) \ V(CreateRegExpLiteral) \ V(CreateWithContext) \ V(ForInNext) \ V(ForInPrepare) \ V(GetIterator) \ V(GetSuperConstructor) \ V(GetTemplateObject) \ V(InvokeIntrinsic) \ V(LdaConstant) \ V(LdaContextSlot) \ V(LdaCurrentContextSlot) \ V(LdaImmutableContextSlot) \ V(LdaImmutableCurrentContextSlot) \ V(LdaModuleVariable) \ V(LdaFalse) \ V(LdaGlobal) \ V(LdaGlobalInsideTypeof) \ V(LdaKeyedProperty) \ V(LdaLookupContextSlot) \ V(LdaLookupContextSlotInsideTypeof) \ V(LdaLookupGlobalSlot) \ V(LdaLookupGlobalSlotInsideTypeof) \ V(LdaLookupSlot) \ V(LdaLookupSlotInsideTypeof) \ V(LdaNamedProperty) \ V(LdaNamedPropertyNoFeedback) \ V(LdaNull) \ V(Ldar) \ V(LdaSmi) \ V(LdaTheHole) \ V(LdaTrue) \ V(LdaUndefined) \ V(LdaZero) \ V(Mov) \ V(PopContext) \ V(PushContext) \ V(Return) \ V(StaContextSlot) \ V(StaCurrentContextSlot) \ V(StaDataPropertyInLiteral) \ V(StaGlobal) \ V(StaInArrayLiteral) \ V(StaKeyedProperty) \ V(StaLookupSlot) \ V(StaModuleVariable) \ V(StaNamedOwnProperty) \ V(StaNamedProperty) \ V(StaNamedPropertyNoFeedback) \ V(Star) \ V(SwitchOnGeneratorState) \ V(SwitchOnSmiNoFeedback) \ V(TestIn) \ V(TestInstanceOf) \ V(ThrowReferenceErrorIfHole) \ V(ToNumber) \ V(ToNumeric) \ BINARY_OP_LIST(V) \ COMPARE_OP_LIST(V) \ CLEAR_ACCUMULATOR_LIST(V) \ CLEAR_ENVIRONMENT_LIST(V) \ CONDITIONAL_JUMPS_LIST(V) \ IGNORED_BYTECODE_LIST(V) \ KILL_ENVIRONMENT_LIST(V) \ UNARY_OP_LIST(V) \ UNCONDITIONAL_JUMPS_LIST(V) \ UNREACHABLE_BYTECODE_LIST(V) template <typename T> struct HandleComparator { bool operator()(const Handle<T>& lhs, const Handle<T>& rhs) const { return lhs.address() < rhs.address(); } }; struct VirtualContext { unsigned int distance; Handle<Context> context; VirtualContext(unsigned int distance_in, Handle<Context> context_in) : distance(distance_in), context(context_in) { CHECK_GT(distance, 0); } bool operator<(const VirtualContext& other) const { return HandleComparator<Context>()(context, other.context) && distance < other.distance; } }; class FunctionBlueprint; using ConstantsSet = ZoneSet<Handle<Object>, HandleComparator<Object>>; using VirtualContextsSet = ZoneSet<VirtualContext>; using MapsSet = ZoneSet<Handle<Map>, HandleComparator<Map>>; using BlueprintsSet = ZoneSet<FunctionBlueprint>; class Hints { public: explicit Hints(Zone* zone); static Hints SingleConstant(Handle<Object> constant, Zone* zone); const ConstantsSet& constants() const; const MapsSet& maps() const; const BlueprintsSet& function_blueprints() const; const VirtualContextsSet& virtual_contexts() const; void AddConstant(Handle<Object> constant); void AddMap(Handle<Map> map); void AddFunctionBlueprint(FunctionBlueprint function_blueprint); void AddVirtualContext(VirtualContext virtual_context); void Add(const Hints& other); void Clear(); bool IsEmpty() const; #ifdef ENABLE_SLOW_DCHECKS bool Includes(Hints const& other) const; bool Equals(Hints const& other) const; #endif private: VirtualContextsSet virtual_contexts_; ConstantsSet constants_; MapsSet maps_; BlueprintsSet function_blueprints_; }; using HintsVector = ZoneVector<Hints>; class FunctionBlueprint { public: FunctionBlueprint(Handle<JSFunction> function, Isolate* isolate, Zone* zone); FunctionBlueprint(Handle<SharedFunctionInfo> shared, Handle<FeedbackVector> feedback_vector, const Hints& context_hints); Handle<SharedFunctionInfo> shared() const { return shared_; } Handle<FeedbackVector> feedback_vector() const { return feedback_vector_; } const Hints& context_hints() const { return context_hints_; } bool operator<(const FunctionBlueprint& other) const { // A feedback vector is never used for more than one SFI, so it can // be used for strict ordering of blueprints. DCHECK_IMPLIES(feedback_vector_.equals(other.feedback_vector_), shared_.equals(other.shared_)); return HandleComparator<FeedbackVector>()(feedback_vector_, other.feedback_vector_); } private: Handle<SharedFunctionInfo> shared_; Handle<FeedbackVector> feedback_vector_; Hints context_hints_; }; class CompilationSubject { public: explicit CompilationSubject(FunctionBlueprint blueprint) : blueprint_(blueprint) {} // The zone parameter is to correctly initialize the blueprint, // which contains zone-allocated context information. CompilationSubject(Handle<JSFunction> closure, Isolate* isolate, Zone* zone); const FunctionBlueprint& blueprint() const { return blueprint_; } MaybeHandle<JSFunction> closure() const { return closure_; } private: FunctionBlueprint blueprint_; MaybeHandle<JSFunction> closure_; }; // The SerializerForBackgroundCompilation makes sure that the relevant function // data such as bytecode, SharedFunctionInfo and FeedbackVector, used by later // optimizations in the compiler, is copied to the heap broker. class SerializerForBackgroundCompilation { public: SerializerForBackgroundCompilation( JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone, Handle<JSFunction> closure, SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset); Hints Run(); // NOTE: Returns empty for an already-serialized function. class Environment; private: SerializerForBackgroundCompilation( JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone, CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments, SerializerForBackgroundCompilationFlags flags); bool BailoutOnUninitialized(ProcessedFeedback const& feedback); void TraverseBytecode(); #define DECLARE_VISIT_BYTECODE(name, ...) \ void Visit##name(interpreter::BytecodeArrayIterator* iterator); SUPPORTED_BYTECODE_LIST(DECLARE_VISIT_BYTECODE) #undef DECLARE_VISIT_BYTECODE // Returns whether the callee with the given SFI should be processed further, // i.e. whether it's inlineable. bool ProcessSFIForCallOrConstruct(Handle<SharedFunctionInfo> shared, const HintsVector& arguments, SpeculationMode speculation_mode); // Returns whether {function} should be serialized for compilation. bool ProcessCalleeForCallOrConstruct(Handle<JSFunction> function, const HintsVector& arguments, SpeculationMode speculation_mode); void ProcessCallOrConstruct(Hints callee, base::Optional<Hints> new_target, const HintsVector& arguments, FeedbackSlot slot, bool with_spread = false); void ProcessCallVarArgs(ConvertReceiverMode receiver_mode, Hints const& callee, interpreter::Register first_reg, int reg_count, FeedbackSlot slot, bool with_spread = false); void ProcessApiCall(Handle<SharedFunctionInfo> target, const HintsVector& arguments); void ProcessReceiverMapForApiCall(FunctionTemplateInfoRef target, Handle<Map> receiver); void ProcessBuiltinCall(Handle<SharedFunctionInfo> target, const HintsVector& arguments, SpeculationMode speculation_mode); void ProcessJump(interpreter::BytecodeArrayIterator* iterator); void ProcessKeyedPropertyAccess(Hints const& receiver, Hints const& key, FeedbackSlot slot, AccessMode access_mode, bool honor_bailout_on_uninitialized); void ProcessNamedPropertyAccess(Hints receiver, NameRef const& name, FeedbackSlot slot, AccessMode access_mode); void ProcessNamedAccess(Hints receiver, NamedAccessFeedback const& feedback, AccessMode access_mode, Hints* new_accumulator_hints); void ProcessElementAccess(Hints receiver, Hints key, ElementAccessFeedback const& feedback, AccessMode access_mode); void ProcessModuleVariableAccess( interpreter::BytecodeArrayIterator* iterator); void ProcessHintsForObjectCreate(Hints const& prototype); void ProcessMapHintsForPromises(Hints const& receiver_hints); void ProcessHintsForPromiseResolve(Hints const& resolution_hints); void ProcessHintsForHasInPrototypeChain(Hints const& instance_hints); void ProcessHintsForRegExpTest(Hints const& regexp_hints); PropertyAccessInfo ProcessMapForRegExpTest(MapRef map); void ProcessHintsForFunctionCall(Hints const& target_hints); void ProcessHintsForFunctionBind(Hints const& receiver_hints); void ProcessHintsForObjectGetPrototype(Hints const& object_hints); void ProcessConstantForOrdinaryHasInstance(HeapObjectRef const& constructor, bool* walk_prototypes); void ProcessConstantForInstanceOf(ObjectRef const& constant, bool* walk_prototypes); void ProcessHintsForOrdinaryHasInstance(Hints const& constructor_hints, Hints const& instance_hints); void ProcessGlobalAccess(FeedbackSlot slot, bool is_load); void ProcessCompareOperation(FeedbackSlot slot); void ProcessForIn(FeedbackSlot slot); void ProcessUnaryOrBinaryOperation(FeedbackSlot slot, bool honor_bailout_on_uninitialized); PropertyAccessInfo ProcessMapForNamedPropertyAccess( MapRef receiver_map, NameRef const& name, AccessMode access_mode, base::Optional<JSObjectRef> receiver, Hints* new_accumulator_hints); void ProcessCreateContext(interpreter::BytecodeArrayIterator* iterator, int scopeinfo_operand_index); enum ContextProcessingMode { kIgnoreSlot, kSerializeSlot, }; void ProcessContextAccess(Hints const& context_hints, int slot, int depth, ContextProcessingMode mode, Hints* result_hints = nullptr); void ProcessImmutableLoad(ContextRef const& context, int slot, ContextProcessingMode mode, Hints* new_accumulator_hints); void ProcessLdaLookupGlobalSlot(interpreter::BytecodeArrayIterator* iterator); void ProcessLdaLookupContextSlot( interpreter::BytecodeArrayIterator* iterator); // Performs extension lookups for [0, depth) like // BytecodeGraphBuilder::CheckContextExtensions(). void ProcessCheckContextExtensions(int depth); Hints RunChildSerializer(CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments, bool with_spread); // When (forward-)branching bytecodes are encountered, e.g. a conditional // jump, we call ContributeToJumpTargetEnvironment to "remember" the current // environment, associated with the jump target offset. When serialization // eventually reaches that offset, we call IncorporateJumpTargetEnvironment to // merge that environment back into whatever is the current environment then. // Note: Since there may be multiple jumps to the same target, // ContributeToJumpTargetEnvironment may actually do a merge as well. void ContributeToJumpTargetEnvironment(int target_offset); void IncorporateJumpTargetEnvironment(int target_offset); Handle<FeedbackVector> feedback_vector() const; Handle<BytecodeArray> bytecode_array() const; BytecodeAnalysis const& GetBytecodeAnalysis( SerializationPolicy policy = SerializationPolicy::kAssumeSerialized); JSHeapBroker* broker() const { return broker_; } CompilationDependencies* dependencies() const { return dependencies_; } Zone* zone() const { return zone_; } Environment* environment() const { return environment_; } SerializerForBackgroundCompilationFlags flags() const { return flags_; } BailoutId osr_offset() const { return osr_offset_; } JSHeapBroker* const broker_; CompilationDependencies* const dependencies_; Zone* const zone_; Environment* const environment_; ZoneUnorderedMap<int, Environment*> jump_target_environments_; SerializerForBackgroundCompilationFlags const flags_; BailoutId const osr_offset_; }; void RunSerializerForBackgroundCompilation( JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone, Handle<JSFunction> closure, SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset) { SerializerForBackgroundCompilation serializer(broker, dependencies, zone, closure, flags, osr_offset); serializer.Run(); } using BytecodeArrayIterator = interpreter::BytecodeArrayIterator; FunctionBlueprint::FunctionBlueprint(Handle<SharedFunctionInfo> shared, Handle<FeedbackVector> feedback_vector, const Hints& context_hints) : shared_(shared), feedback_vector_(feedback_vector), context_hints_(context_hints) {} FunctionBlueprint::FunctionBlueprint(Handle<JSFunction> function, Isolate* isolate, Zone* zone) : shared_(handle(function->shared(), isolate)), feedback_vector_(handle(function->feedback_vector(), isolate)), context_hints_(zone) { context_hints_.AddConstant(handle(function->context(), isolate)); } CompilationSubject::CompilationSubject(Handle<JSFunction> closure, Isolate* isolate, Zone* zone) : blueprint_(closure, isolate, zone), closure_(closure) { CHECK(closure->has_feedback_vector()); } Hints::Hints(Zone* zone) : virtual_contexts_(zone), constants_(zone), maps_(zone), function_blueprints_(zone) {} #ifdef ENABLE_SLOW_DCHECKS namespace { template <typename K, typename Compare> bool SetIncludes(ZoneSet<K, Compare> const& lhs, ZoneSet<K, Compare> const& rhs) { return std::all_of(rhs.cbegin(), rhs.cend(), [&](K const& x) { return lhs.find(x) != lhs.cend(); }); } } // namespace bool Hints::Includes(Hints const& other) const { return SetIncludes(constants(), other.constants()) && SetIncludes(function_blueprints(), other.function_blueprints()) && SetIncludes(maps(), other.maps()); } bool Hints::Equals(Hints const& other) const { return this->Includes(other) && other.Includes(*this); } #endif Hints Hints::SingleConstant(Handle<Object> constant, Zone* zone) { Hints result(zone); result.AddConstant(constant); return result; } const ConstantsSet& Hints::constants() const { return constants_; } const MapsSet& Hints::maps() const { return maps_; } const BlueprintsSet& Hints::function_blueprints() const { return function_blueprints_; } const VirtualContextsSet& Hints::virtual_contexts() const { return virtual_contexts_; } void Hints::AddVirtualContext(VirtualContext virtual_context) { virtual_contexts_.insert(virtual_context); } void Hints::AddConstant(Handle<Object> constant) { constants_.insert(constant); } void Hints::AddMap(Handle<Map> map) { maps_.insert(map); } void Hints::AddFunctionBlueprint(FunctionBlueprint function_blueprint) { function_blueprints_.insert(function_blueprint); } void Hints::Add(const Hints& other) { for (auto x : other.constants()) AddConstant(x); for (auto x : other.maps()) AddMap(x); for (auto x : other.function_blueprints()) AddFunctionBlueprint(x); for (auto x : other.virtual_contexts()) AddVirtualContext(x); } bool Hints::IsEmpty() const { return constants().empty() && maps().empty() && function_blueprints().empty() && virtual_contexts().empty(); } std::ostream& operator<<(std::ostream& out, const VirtualContext& virtual_context) { out << "Distance " << virtual_context.distance << " from " << Brief(*virtual_context.context) << std::endl; return out; } std::ostream& operator<<(std::ostream& out, const Hints& hints); std::ostream& operator<<(std::ostream& out, const FunctionBlueprint& blueprint) { out << Brief(*blueprint.shared()) << std::endl; out << Brief(*blueprint.feedback_vector()) << std::endl; !blueprint.context_hints().IsEmpty() && out << blueprint.context_hints() << "):" << std::endl; return out; } std::ostream& operator<<(std::ostream& out, const Hints& hints) { for (Handle<Object> constant : hints.constants()) { out << " constant " << Brief(*constant) << std::endl; } for (Handle<Map> map : hints.maps()) { out << " map " << Brief(*map) << std::endl; } for (FunctionBlueprint const& blueprint : hints.function_blueprints()) { out << " blueprint " << blueprint << std::endl; } for (VirtualContext const& virtual_context : hints.virtual_contexts()) { out << " virtual context " << virtual_context << std::endl; } return out; } void Hints::Clear() { virtual_contexts_.clear(); constants_.clear(); maps_.clear(); function_blueprints_.clear(); DCHECK(IsEmpty()); } class SerializerForBackgroundCompilation::Environment : public ZoneObject { public: Environment(Zone* zone, CompilationSubject function); Environment(Zone* zone, Isolate* isolate, CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments); bool IsDead() const { return ephemeral_hints_.empty(); } void Kill() { DCHECK(!IsDead()); ephemeral_hints_.clear(); DCHECK(IsDead()); } void Revive() { DCHECK(IsDead()); ephemeral_hints_.resize(ephemeral_hints_size(), Hints(zone())); DCHECK(!IsDead()); } // Merge {other} into {this} environment (leaving {other} unmodified). void Merge(Environment* other); FunctionBlueprint function() const { return function_; } Hints const& closure_hints() const { return closure_hints_; } Hints const& current_context_hints() const { return current_context_hints_; } Hints& current_context_hints() { return current_context_hints_; } Hints const& return_value_hints() const { return return_value_hints_; } Hints& return_value_hints() { return return_value_hints_; } Hints& accumulator_hints() { CHECK_LT(accumulator_index(), ephemeral_hints_.size()); return ephemeral_hints_[accumulator_index()]; } Hints& register_hints(interpreter::Register reg) { if (reg.is_function_closure()) return closure_hints_; if (reg.is_current_context()) return current_context_hints_; int local_index = RegisterToLocalIndex(reg); CHECK_LT(local_index, ephemeral_hints_.size()); return ephemeral_hints_[local_index]; } // Clears all hints except those for the context, return value, and the // closure. void ClearEphemeralHints() { for (auto& hints : ephemeral_hints_) hints.Clear(); } // Appends the hints for the given register range to {dst} (in order). void ExportRegisterHints(interpreter::Register first, size_t count, HintsVector* dst); private: friend std::ostream& operator<<(std::ostream& out, const Environment& env); int RegisterToLocalIndex(interpreter::Register reg) const; Zone* zone() const { return zone_; } int parameter_count() const { return parameter_count_; } int register_count() const { return register_count_; } Zone* const zone_; // Instead of storing the blueprint here, we could extract it from the // (closure) hints but that would be cumbersome. FunctionBlueprint const function_; int const parameter_count_; int const register_count_; Hints closure_hints_; Hints current_context_hints_; Hints return_value_hints_; // ephemeral_hints_ contains hints for the contents of the registers, // the accumulator and the parameters. The layout is as follows: // [ parameters | registers | accumulator ] // The first parameter is the receiver. HintsVector ephemeral_hints_; int accumulator_index() const { return parameter_count() + register_count(); } int ephemeral_hints_size() const { return accumulator_index() + 1; } }; SerializerForBackgroundCompilation::Environment::Environment( Zone* zone, CompilationSubject function) : zone_(zone), function_(function.blueprint()), parameter_count_( function_.shared()->GetBytecodeArray().parameter_count()), register_count_(function_.shared()->GetBytecodeArray().register_count()), closure_hints_(zone), current_context_hints_(zone), return_value_hints_(zone), ephemeral_hints_(ephemeral_hints_size(), Hints(zone), zone) { Handle<JSFunction> closure; if (function.closure().ToHandle(&closure)) { closure_hints_.AddConstant(closure); } else { closure_hints_.AddFunctionBlueprint(function.blueprint()); } // Consume blueprint context hint information. current_context_hints().Add(function.blueprint().context_hints()); } SerializerForBackgroundCompilation::Environment::Environment( Zone* zone, Isolate* isolate, CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments) : Environment(zone, function) { // Copy the hints for the actually passed arguments, at most up to // the parameter_count. size_t param_count = static_cast<size_t>(parameter_count()); for (size_t i = 0; i < std::min(arguments.size(), param_count); ++i) { ephemeral_hints_[i] = arguments[i]; } // Pad the rest with "undefined". Hints undefined_hint = Hints::SingleConstant(isolate->factory()->undefined_value(), zone); for (size_t i = arguments.size(); i < param_count; ++i) { ephemeral_hints_[i] = undefined_hint; } interpreter::Register new_target_reg = function_.shared() ->GetBytecodeArray() .incoming_new_target_or_generator_register(); if (new_target_reg.is_valid()) { DCHECK(register_hints(new_target_reg).IsEmpty()); if (new_target.has_value()) { register_hints(new_target_reg).Add(*new_target); } } } void SerializerForBackgroundCompilation::Environment::Merge( Environment* other) { // {other} is guaranteed to have the same layout because it comes from an // earlier bytecode in the same function. CHECK_EQ(parameter_count(), other->parameter_count()); CHECK_EQ(register_count(), other->register_count()); SLOW_DCHECK(closure_hints_.Equals(other->closure_hints_)); if (IsDead()) { ephemeral_hints_ = other->ephemeral_hints_; SLOW_DCHECK(return_value_hints_.Includes(other->return_value_hints_)); CHECK(!IsDead()); return; } CHECK_EQ(ephemeral_hints_.size(), other->ephemeral_hints_.size()); for (size_t i = 0; i < ephemeral_hints_.size(); ++i) { ephemeral_hints_[i].Add(other->ephemeral_hints_[i]); } return_value_hints_.Add(other->return_value_hints_); } std::ostream& operator<<( std::ostream& out, const SerializerForBackgroundCompilation::Environment& env) { std::ostringstream output_stream; output_stream << "Function "; env.function_.shared()->Name().Print(output_stream); if (env.IsDead()) { output_stream << "dead\n"; } else { output_stream << "alive\n"; for (int i = 0; i < static_cast<int>(env.ephemeral_hints_.size()); ++i) { Hints const& hints = env.ephemeral_hints_[i]; if (!hints.IsEmpty()) { if (i < env.parameter_count()) { output_stream << "Hints for a" << i << ":\n"; } else if (i < env.parameter_count() + env.register_count()) { int local_register = i - env.parameter_count(); output_stream << "Hints for r" << local_register << ":\n"; } else if (i == env.accumulator_index()) { output_stream << "Hints for <accumulator>:\n"; } else { UNREACHABLE(); } output_stream << hints; } } } if (!env.closure_hints().IsEmpty()) { output_stream << "Hints for <closure>:\n" << env.closure_hints(); } if (!env.current_context_hints().IsEmpty()) { output_stream << "Hints for <context>:\n" << env.current_context_hints(); } if (!env.return_value_hints().IsEmpty()) { output_stream << "Hints for {return value}:\n" << env.return_value_hints(); } out << output_stream.str(); return out; } int SerializerForBackgroundCompilation::Environment::RegisterToLocalIndex( interpreter::Register reg) const { if (reg.is_parameter()) { return reg.ToParameterIndex(parameter_count()); } else { DCHECK(!reg.is_function_closure()); return parameter_count() + reg.index(); } } SerializerForBackgroundCompilation::SerializerForBackgroundCompilation( JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone, Handle<JSFunction> closure, SerializerForBackgroundCompilationFlags flags, BailoutId osr_offset) : broker_(broker), dependencies_(dependencies), zone_(zone), environment_(new (zone) Environment( zone, CompilationSubject(closure, broker_->isolate(), zone))), jump_target_environments_(zone), flags_(flags), osr_offset_(osr_offset) { JSFunctionRef(broker, closure).Serialize(); } SerializerForBackgroundCompilation::SerializerForBackgroundCompilation( JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone, CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments, SerializerForBackgroundCompilationFlags flags) : broker_(broker), dependencies_(dependencies), zone_(zone), environment_(new (zone) Environment(zone, broker_->isolate(), function, new_target, arguments)), jump_target_environments_(zone), flags_(flags), osr_offset_(BailoutId::None()) { TraceScope tracer( broker_, this, "SerializerForBackgroundCompilation::SerializerForBackgroundCompilation"); TRACE_BROKER(broker_, "Initial environment:\n" << *environment_); Handle<JSFunction> closure; if (function.closure().ToHandle(&closure)) { JSFunctionRef(broker, closure).Serialize(); } } bool SerializerForBackgroundCompilation::BailoutOnUninitialized( ProcessedFeedback const& feedback) { DCHECK(!environment()->IsDead()); if (!(flags() & SerializerForBackgroundCompilationFlag::kBailoutOnUninitialized)) { return false; } if (!osr_offset().IsNone()) { // Exclude OSR from this optimization because we might end up skipping the // OSR entry point. TODO(neis): Support OSR? return false; } if (feedback.IsInsufficient()) { environment()->Kill(); return true; } return false; } Hints SerializerForBackgroundCompilation::Run() { TraceScope tracer(broker(), this, "SerializerForBackgroundCompilation::Run"); SharedFunctionInfoRef shared(broker(), environment()->function().shared()); FeedbackVectorRef feedback_vector_ref(broker(), feedback_vector()); if (shared.IsSerializedForCompilation(feedback_vector_ref)) { TRACE_BROKER(broker(), "Already ran serializer for SharedFunctionInfo " << Brief(*shared.object()) << ", bailing out.\n"); return Hints(zone()); } shared.SetSerializedForCompilation(feedback_vector_ref); // We eagerly call the {EnsureSourcePositionsAvailable} for all serialized // SFIs while still on the main thread. Source positions will later be used // by JSInliner::ReduceJSCall. if (flags() & SerializerForBackgroundCompilationFlag::kCollectSourcePositions) { SharedFunctionInfo::EnsureSourcePositionsAvailable(broker()->isolate(), shared.object()); } feedback_vector_ref.Serialize(); TraverseBytecode(); return environment()->return_value_hints(); } class ExceptionHandlerMatcher { public: explicit ExceptionHandlerMatcher( BytecodeArrayIterator const& bytecode_iterator, Handle<BytecodeArray> bytecode_array) : bytecode_iterator_(bytecode_iterator) { HandlerTable table(*bytecode_array); for (int i = 0, n = table.NumberOfRangeEntries(); i < n; ++i) { handlers_.insert(table.GetRangeHandler(i)); } handlers_iterator_ = handlers_.cbegin(); } bool CurrentBytecodeIsExceptionHandlerStart() { CHECK(!bytecode_iterator_.done()); while (handlers_iterator_ != handlers_.cend() && *handlers_iterator_ < bytecode_iterator_.current_offset()) { handlers_iterator_++; } return handlers_iterator_ != handlers_.cend() && *handlers_iterator_ == bytecode_iterator_.current_offset(); } private: BytecodeArrayIterator const& bytecode_iterator_; std::set<int> handlers_; std::set<int>::const_iterator handlers_iterator_; }; Handle<FeedbackVector> SerializerForBackgroundCompilation::feedback_vector() const { return environment()->function().feedback_vector(); } Handle<BytecodeArray> SerializerForBackgroundCompilation::bytecode_array() const { return handle(environment()->function().shared()->GetBytecodeArray(), broker()->isolate()); } BytecodeAnalysis const& SerializerForBackgroundCompilation::GetBytecodeAnalysis( SerializationPolicy policy) { return broker()->GetBytecodeAnalysis( bytecode_array(), osr_offset(), flags() & SerializerForBackgroundCompilationFlag::kAnalyzeEnvironmentLiveness, policy); } void SerializerForBackgroundCompilation::TraverseBytecode() { BytecodeAnalysis const& bytecode_analysis = GetBytecodeAnalysis(SerializationPolicy::kSerializeIfNeeded); BytecodeArrayRef(broker(), bytecode_array()).SerializeForCompilation(); BytecodeArrayIterator iterator(bytecode_array()); ExceptionHandlerMatcher handler_matcher(iterator, bytecode_array()); bool has_one_shot_bytecode = false; for (; !iterator.done(); iterator.Advance()) { has_one_shot_bytecode = has_one_shot_bytecode || interpreter::Bytecodes::IsOneShotBytecode(iterator.current_bytecode()); int const current_offset = iterator.current_offset(); IncorporateJumpTargetEnvironment(current_offset); TRACE_BROKER(broker(), "Handling bytecode: " << current_offset << " " << iterator.current_bytecode()); TRACE_BROKER(broker(), "Current environment: " << *environment()); if (environment()->IsDead()) { if (handler_matcher.CurrentBytecodeIsExceptionHandlerStart()) { environment()->Revive(); } else { continue; // Skip this bytecode since TF won't generate code for it. } } if (bytecode_analysis.IsLoopHeader(current_offset)) { // Graph builder might insert jumps to resume targets in the loop body. LoopInfo const& loop_info = bytecode_analysis.GetLoopInfoFor(current_offset); for (const auto& target : loop_info.resume_jump_targets()) { ContributeToJumpTargetEnvironment(target.target_offset()); } } switch (iterator.current_bytecode()) { #define DEFINE_BYTECODE_CASE(name) \ case interpreter::Bytecode::k##name: \ Visit##name(&iterator); \ break; SUPPORTED_BYTECODE_LIST(DEFINE_BYTECODE_CASE) #undef DEFINE_BYTECODE_CASE default: { environment()->ClearEphemeralHints(); break; } } } if (has_one_shot_bytecode) { broker()->isolate()->CountUsage( v8::Isolate::UseCounterFeature::kOptimizedFunctionWithOneShotBytecode); } } void SerializerForBackgroundCompilation::VisitGetIterator( BytecodeArrayIterator* iterator) { AccessMode mode = AccessMode::kLoad; Hints const& receiver = environment()->register_hints(iterator->GetRegisterOperand(0)); Handle<Name> name = broker()->isolate()->factory()->iterator_symbol(); FeedbackSlot slot = iterator->GetSlotOperand(1); ProcessNamedPropertyAccess(receiver, NameRef(broker(), name), slot, mode); } void SerializerForBackgroundCompilation::VisitGetSuperConstructor( BytecodeArrayIterator* iterator) { interpreter::Register dst = iterator->GetRegisterOperand(0); environment()->register_hints(dst).Clear(); for (auto constant : environment()->accumulator_hints().constants()) { // For JSNativeContextSpecialization::ReduceJSGetSuperConstructor. if (!constant->IsJSFunction()) continue; MapRef map(broker(), handle(HeapObject::cast(*constant).map(), broker()->isolate())); map.SerializePrototype(); ObjectRef proto = map.prototype(); if (proto.IsHeapObject() && proto.AsHeapObject().map().is_constructor()) { environment()->register_hints(dst).AddConstant(proto.object()); } } } void SerializerForBackgroundCompilation::VisitGetTemplateObject( BytecodeArrayIterator* iterator) { ObjectRef description( broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(1); FeedbackVectorRef feedback_vector_ref(broker(), feedback_vector()); SharedFunctionInfoRef shared(broker(), environment()->function().shared()); JSArrayRef template_object = shared.GetTemplateObject(description, feedback_vector_ref, slot, SerializationPolicy::kSerializeIfNeeded); environment()->accumulator_hints().Clear(); environment()->accumulator_hints().AddConstant(template_object.object()); } void SerializerForBackgroundCompilation::VisitLdaTrue( BytecodeArrayIterator* iterator) { environment()->accumulator_hints().Clear(); environment()->accumulator_hints().AddConstant( broker()->isolate()->factory()->true_value()); } void SerializerForBackgroundCompilation::VisitLdaFalse( BytecodeArrayIterator* iterator) { environment()->accumulator_hints().Clear(); environment()->accumulator_hints().AddConstant( broker()->isolate()->factory()->false_value()); } void SerializerForBackgroundCompilation::VisitLdaTheHole( BytecodeArrayIterator* iterator) { environment()->accumulator_hints().Clear(); environment()->accumulator_hints().AddConstant( broker()->isolate()->factory()->the_hole_value()); } void SerializerForBackgroundCompilation::VisitLdaUndefined( BytecodeArrayIterator* iterator) { environment()->accumulator_hints().Clear(); environment()->accumulator_hints().AddConstant( broker()->isolate()->factory()->undefined_value()); } void SerializerForBackgroundCompilation::VisitLdaNull( BytecodeArrayIterator* iterator) { environment()->accumulator_hints().Clear(); environment()->accumulator_hints().AddConstant( broker()->isolate()->factory()->null_value()); } void SerializerForBackgroundCompilation::VisitLdaZero( BytecodeArrayIterator* iterator) { environment()->accumulator_hints().Clear(); environment()->accumulator_hints().AddConstant( handle(Smi::FromInt(0), broker()->isolate())); } void SerializerForBackgroundCompilation::VisitLdaSmi( BytecodeArrayIterator* iterator) { environment()->accumulator_hints().Clear(); environment()->accumulator_hints().AddConstant(handle( Smi::FromInt(iterator->GetImmediateOperand(0)), broker()->isolate())); } void SerializerForBackgroundCompilation::VisitInvokeIntrinsic( BytecodeArrayIterator* iterator) { Runtime::FunctionId functionId = iterator->GetIntrinsicIdOperand(0); // For JSNativeContextSpecialization::ReduceJSAsyncFunctionResolve and // JSNativeContextSpecialization::ReduceJSResolvePromise. switch (functionId) { case Runtime::kInlineAsyncFunctionResolve: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncFunctionResolve)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); size_t reg_count = iterator->GetRegisterCountOperand(2); CHECK_EQ(reg_count, 3); HintsVector arguments(zone()); environment()->ExportRegisterHints(first_reg, reg_count, &arguments); Hints const& resolution_hints = arguments[1]; // The resolution object. ProcessHintsForPromiseResolve(resolution_hints); environment()->accumulator_hints().Clear(); return; } case Runtime::kInlineAsyncGeneratorReject: case Runtime::kAsyncGeneratorReject: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncGeneratorReject)); break; } case Runtime::kInlineAsyncGeneratorResolve: case Runtime::kAsyncGeneratorResolve: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncGeneratorResolve)); break; } case Runtime::kInlineAsyncGeneratorYield: case Runtime::kAsyncGeneratorYield: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncGeneratorYield)); break; } case Runtime::kInlineAsyncGeneratorAwaitUncaught: case Runtime::kAsyncGeneratorAwaitUncaught: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncGeneratorAwaitUncaught)); break; } case Runtime::kInlineAsyncGeneratorAwaitCaught: case Runtime::kAsyncGeneratorAwaitCaught: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncGeneratorAwaitCaught)); break; } case Runtime::kInlineAsyncFunctionAwaitUncaught: case Runtime::kAsyncFunctionAwaitUncaught: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncFunctionAwaitUncaught)); break; } case Runtime::kInlineAsyncFunctionAwaitCaught: case Runtime::kAsyncFunctionAwaitCaught: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncFunctionAwaitCaught)); break; } case Runtime::kInlineAsyncFunctionReject: case Runtime::kAsyncFunctionReject: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncFunctionReject)); break; } case Runtime::kAsyncFunctionResolve: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kAsyncFunctionResolve)); break; } case Runtime::kInlineCopyDataProperties: case Runtime::kCopyDataProperties: { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kCopyDataProperties)); break; } default: { break; } } environment()->ClearEphemeralHints(); } void SerializerForBackgroundCompilation::VisitLdaConstant( BytecodeArrayIterator* iterator) { ObjectRef object( broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); environment()->accumulator_hints().Clear(); environment()->accumulator_hints().AddConstant(object.object()); } void SerializerForBackgroundCompilation::VisitPushContext( BytecodeArrayIterator* iterator) { // Transfer current context hints to the destination register hints. Hints& current_context_hints = environment()->current_context_hints(); Hints& saved_context_hints = environment()->register_hints(iterator->GetRegisterOperand(0)); saved_context_hints.Clear(); saved_context_hints.Add(current_context_hints); // New context is in the accumulator. Put those hints into the current context // register hints. current_context_hints.Clear(); current_context_hints.Add(environment()->accumulator_hints()); } void SerializerForBackgroundCompilation::VisitPopContext( BytecodeArrayIterator* iterator) { // Replace current context hints with hints given in the argument register. Hints& new_context_hints = environment()->register_hints(iterator->GetRegisterOperand(0)); environment()->current_context_hints().Clear(); environment()->current_context_hints().Add(new_context_hints); } void SerializerForBackgroundCompilation::ProcessImmutableLoad( ContextRef const& context_ref, int slot, ContextProcessingMode mode, Hints* result_hints) { DCHECK_EQ(mode, kSerializeSlot); base::Optional<ObjectRef> slot_value = context_ref.get(slot, SerializationPolicy::kSerializeIfNeeded); // If requested, record the object as a hint for the result value. if (result_hints != nullptr && slot_value.has_value()) { result_hints->AddConstant(slot_value.value().object()); } } void SerializerForBackgroundCompilation::ProcessContextAccess( Hints const& context_hints, int slot, int depth, ContextProcessingMode mode, Hints* result_hints) { // This function is for JSContextSpecialization::ReduceJSLoadContext and // ReduceJSStoreContext. Those reductions attempt to eliminate as many // loads as possible by making use of constant Context objects. In the // case of an immutable load, ReduceJSLoadContext even attempts to load // the value at {slot}, replacing the load with a constant. for (auto x : context_hints.constants()) { if (x->IsContext()) { // Walk this context to the given depth and serialize the slot found. ContextRef context_ref(broker(), x); size_t remaining_depth = depth; context_ref = context_ref.previous( &remaining_depth, SerializationPolicy::kSerializeIfNeeded); if (remaining_depth == 0 && mode != kIgnoreSlot) { ProcessImmutableLoad(context_ref, slot, mode, result_hints); } } } for (auto x : context_hints.virtual_contexts()) { if (x.distance <= static_cast<unsigned int>(depth)) { ContextRef context_ref(broker(), x.context); size_t remaining_depth = depth - x.distance; context_ref = context_ref.previous( &remaining_depth, SerializationPolicy::kSerializeIfNeeded); if (remaining_depth == 0 && mode != kIgnoreSlot) { ProcessImmutableLoad(context_ref, slot, mode, result_hints); } } } } void SerializerForBackgroundCompilation::VisitLdaContextSlot( BytecodeArrayIterator* iterator) { Hints const& context_hints = environment()->register_hints(iterator->GetRegisterOperand(0)); const int slot = iterator->GetIndexOperand(1); const int depth = iterator->GetUnsignedImmediateOperand(2); Hints new_accumulator_hints(zone()); ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot, &new_accumulator_hints); environment()->accumulator_hints().Clear(); environment()->accumulator_hints().Add(new_accumulator_hints); } void SerializerForBackgroundCompilation::VisitLdaCurrentContextSlot( BytecodeArrayIterator* iterator) { const int slot = iterator->GetIndexOperand(0); const int depth = 0; Hints const& context_hints = environment()->current_context_hints(); Hints new_accumulator_hints(zone()); ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot, &new_accumulator_hints); environment()->accumulator_hints().Clear(); environment()->accumulator_hints().Add(new_accumulator_hints); } void SerializerForBackgroundCompilation::VisitLdaImmutableContextSlot( BytecodeArrayIterator* iterator) { const int slot = iterator->GetIndexOperand(1); const int depth = iterator->GetUnsignedImmediateOperand(2); Hints const& context_hints = environment()->register_hints(iterator->GetRegisterOperand(0)); Hints new_accumulator_hints(zone()); ProcessContextAccess(context_hints, slot, depth, kSerializeSlot, &new_accumulator_hints); environment()->accumulator_hints().Clear(); environment()->accumulator_hints().Add(new_accumulator_hints); } void SerializerForBackgroundCompilation::VisitLdaImmutableCurrentContextSlot( BytecodeArrayIterator* iterator) { const int slot = iterator->GetIndexOperand(0); const int depth = 0; Hints const& context_hints = environment()->current_context_hints(); Hints new_accumulator_hints(zone()); ProcessContextAccess(context_hints, slot, depth, kSerializeSlot, &new_accumulator_hints); environment()->accumulator_hints().Clear(); environment()->accumulator_hints().Add(new_accumulator_hints); } void SerializerForBackgroundCompilation::ProcessModuleVariableAccess( BytecodeArrayIterator* iterator) { const int slot = Context::EXTENSION_INDEX; const int depth = iterator->GetUnsignedImmediateOperand(1); Hints const& context_hints = environment()->current_context_hints(); Hints result_hints(zone()); ProcessContextAccess(context_hints, slot, depth, kSerializeSlot, &result_hints); for (Handle<Object> constant : result_hints.constants()) { ObjectRef object(broker(), constant); // For JSTypedLowering::BuildGetModuleCell. if (object.IsSourceTextModule()) object.AsSourceTextModule().Serialize(); } } void SerializerForBackgroundCompilation::VisitLdaModuleVariable( BytecodeArrayIterator* iterator) { ProcessModuleVariableAccess(iterator); } void SerializerForBackgroundCompilation::VisitStaModuleVariable( BytecodeArrayIterator* iterator) { ProcessModuleVariableAccess(iterator); } void SerializerForBackgroundCompilation::VisitStaLookupSlot( BytecodeArrayIterator* iterator) { ObjectRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); environment()->accumulator_hints().Clear(); } void SerializerForBackgroundCompilation::VisitStaContextSlot( BytecodeArrayIterator* iterator) { const int slot = iterator->GetIndexOperand(1); const int depth = iterator->GetUnsignedImmediateOperand(2); Hints const& register_hints = environment()->register_hints(iterator->GetRegisterOperand(0)); ProcessContextAccess(register_hints, slot, depth, kIgnoreSlot); } void SerializerForBackgroundCompilation::VisitStaCurrentContextSlot( BytecodeArrayIterator* iterator) { const int slot = iterator->GetIndexOperand(0); const int depth = 0; Hints const& context_hints = environment()->current_context_hints(); ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot); } void SerializerForBackgroundCompilation::VisitLdar( BytecodeArrayIterator* iterator) { environment()->accumulator_hints().Clear(); environment()->accumulator_hints().Add( environment()->register_hints(iterator->GetRegisterOperand(0))); } void SerializerForBackgroundCompilation::VisitStar( BytecodeArrayIterator* iterator) { interpreter::Register reg = iterator->GetRegisterOperand(0); environment()->register_hints(reg).Clear(); environment()->register_hints(reg).Add(environment()->accumulator_hints()); } void SerializerForBackgroundCompilation::VisitMov( BytecodeArrayIterator* iterator) { interpreter::Register src = iterator->GetRegisterOperand(0); interpreter::Register dst = iterator->GetRegisterOperand(1); environment()->register_hints(dst).Clear(); environment()->register_hints(dst).Add(environment()->register_hints(src)); } void SerializerForBackgroundCompilation::VisitCreateRegExpLiteral( BytecodeArrayIterator* iterator) { Handle<String> constant_pattern = Handle<String>::cast( iterator->GetConstantForIndexOperand(0, broker()->isolate())); StringRef description(broker(), constant_pattern); environment()->accumulator_hints().Clear(); } void SerializerForBackgroundCompilation::VisitCreateArrayLiteral( BytecodeArrayIterator* iterator) { Handle<ArrayBoilerplateDescription> array_boilerplate_description = Handle<ArrayBoilerplateDescription>::cast( iterator->GetConstantForIndexOperand(0, broker()->isolate())); ArrayBoilerplateDescriptionRef description(broker(), array_boilerplate_description); environment()->accumulator_hints().Clear(); } void SerializerForBackgroundCompilation::VisitCreateObjectLiteral( BytecodeArrayIterator* iterator) { Handle<ObjectBoilerplateDescription> constant_properties = Handle<ObjectBoilerplateDescription>::cast( iterator->GetConstantForIndexOperand(0, broker()->isolate())); ObjectBoilerplateDescriptionRef description(broker(), constant_properties); environment()->accumulator_hints().Clear(); } void SerializerForBackgroundCompilation::VisitCreateFunctionContext( BytecodeArrayIterator* iterator) { ProcessCreateContext(iterator, 0); } void SerializerForBackgroundCompilation::VisitCreateBlockContext( BytecodeArrayIterator* iterator) { ProcessCreateContext(iterator, 0); } void SerializerForBackgroundCompilation::VisitCreateEvalContext( BytecodeArrayIterator* iterator) { ProcessCreateContext(iterator, 0); } void SerializerForBackgroundCompilation::VisitCreateWithContext( BytecodeArrayIterator* iterator) { ProcessCreateContext(iterator, 1); } void SerializerForBackgroundCompilation::VisitCreateCatchContext( BytecodeArrayIterator* iterator) { ProcessCreateContext(iterator, 1); } void SerializerForBackgroundCompilation::VisitForInNext( BytecodeArrayIterator* iterator) { FeedbackSlot slot = iterator->GetSlotOperand(3); ProcessForIn(slot); } void SerializerForBackgroundCompilation::VisitForInPrepare( BytecodeArrayIterator* iterator) { FeedbackSlot slot = iterator->GetSlotOperand(1); ProcessForIn(slot); } void SerializerForBackgroundCompilation::ProcessCreateContext( interpreter::BytecodeArrayIterator* iterator, int scopeinfo_operand_index) { Handle<ScopeInfo> scope_info = Handle<ScopeInfo>::cast(iterator->GetConstantForIndexOperand( scopeinfo_operand_index, broker()->isolate())); ScopeInfoRef scope_info_ref(broker(), scope_info); Hints const& current_context_hints = environment()->current_context_hints(); Hints& accumulator_hints = environment()->accumulator_hints(); accumulator_hints.Clear(); // For each constant context, we must create a virtual context from // it of distance one. for (auto x : current_context_hints.constants()) { if (x->IsContext()) { Handle<Context> as_context(Handle<Context>::cast(x)); accumulator_hints.AddVirtualContext(VirtualContext(1, as_context)); } } // For each virtual context, we must create a virtual context from // it of distance {existing distance} + 1. for (auto x : current_context_hints.virtual_contexts()) { accumulator_hints.AddVirtualContext( VirtualContext(x.distance + 1, x.context)); } } void SerializerForBackgroundCompilation::VisitCreateClosure( BytecodeArrayIterator* iterator) { environment()->accumulator_hints().Clear(); Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast( iterator->GetConstantForIndexOperand(0, broker()->isolate())); Handle<FeedbackCell> feedback_cell = feedback_vector()->GetClosureFeedbackCell(iterator->GetIndexOperand(1)); FeedbackCellRef feedback_cell_ref(broker(), feedback_cell); Handle<Object> cell_value(feedback_cell->value(), broker()->isolate()); ObjectRef cell_value_ref(broker(), cell_value); if (cell_value->IsFeedbackVector()) { FunctionBlueprint blueprint(shared, Handle<FeedbackVector>::cast(cell_value), environment()->current_context_hints()); environment()->accumulator_hints().AddFunctionBlueprint(blueprint); } } void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver( BytecodeArrayIterator* iterator) { const Hints& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2)); FeedbackSlot slot = iterator->GetSlotOperand(3); ProcessCallVarArgs(ConvertReceiverMode::kNullOrUndefined, callee, first_reg, reg_count, slot); } void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver0( BytecodeArrayIterator* iterator) { const Hints& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); FeedbackSlot slot = iterator->GetSlotOperand(1); Hints receiver = Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone()); HintsVector parameters({receiver}, zone()); ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); } void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver1( BytecodeArrayIterator* iterator) { const Hints& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); const Hints& arg0 = environment()->register_hints(iterator->GetRegisterOperand(1)); FeedbackSlot slot = iterator->GetSlotOperand(2); Hints receiver = Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone()); HintsVector parameters({receiver, arg0}, zone()); ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); } void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver2( BytecodeArrayIterator* iterator) { const Hints& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); const Hints& arg0 = environment()->register_hints(iterator->GetRegisterOperand(1)); const Hints& arg1 = environment()->register_hints(iterator->GetRegisterOperand(2)); FeedbackSlot slot = iterator->GetSlotOperand(3); Hints receiver = Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone()); HintsVector parameters({receiver, arg0, arg1}, zone()); ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); } void SerializerForBackgroundCompilation::VisitCallAnyReceiver( BytecodeArrayIterator* iterator) { const Hints& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2)); FeedbackSlot slot = iterator->GetSlotOperand(3); ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count, slot); } void SerializerForBackgroundCompilation::VisitCallNoFeedback( BytecodeArrayIterator* iterator) { const Hints& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2)); ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count, FeedbackSlot::Invalid()); } void SerializerForBackgroundCompilation::VisitCallProperty( BytecodeArrayIterator* iterator) { const Hints& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2)); FeedbackSlot slot = iterator->GetSlotOperand(3); ProcessCallVarArgs(ConvertReceiverMode::kNotNullOrUndefined, callee, first_reg, reg_count, slot); } void SerializerForBackgroundCompilation::VisitCallProperty0( BytecodeArrayIterator* iterator) { const Hints& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); const Hints& receiver = environment()->register_hints(iterator->GetRegisterOperand(1)); FeedbackSlot slot = iterator->GetSlotOperand(2); HintsVector parameters({receiver}, zone()); ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); } void SerializerForBackgroundCompilation::VisitCallProperty1( BytecodeArrayIterator* iterator) { const Hints& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); const Hints& receiver = environment()->register_hints(iterator->GetRegisterOperand(1)); const Hints& arg0 = environment()->register_hints(iterator->GetRegisterOperand(2)); FeedbackSlot slot = iterator->GetSlotOperand(3); HintsVector parameters({receiver, arg0}, zone()); ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); } void SerializerForBackgroundCompilation::VisitCallProperty2( BytecodeArrayIterator* iterator) { const Hints& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); const Hints& receiver = environment()->register_hints(iterator->GetRegisterOperand(1)); const Hints& arg0 = environment()->register_hints(iterator->GetRegisterOperand(2)); const Hints& arg1 = environment()->register_hints(iterator->GetRegisterOperand(3)); FeedbackSlot slot = iterator->GetSlotOperand(4); HintsVector parameters({receiver, arg0, arg1}, zone()); ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); } void SerializerForBackgroundCompilation::VisitCallWithSpread( BytecodeArrayIterator* iterator) { const Hints& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2)); FeedbackSlot slot = iterator->GetSlotOperand(3); ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count, slot, true); } void SerializerForBackgroundCompilation::VisitCallJSRuntime( BytecodeArrayIterator* iterator) { const int runtime_index = iterator->GetNativeContextIndexOperand(0); ObjectRef constant = broker() ->target_native_context() .get(runtime_index, SerializationPolicy::kSerializeIfNeeded) .value(); Hints callee = Hints::SingleConstant(constant.object(), zone()); interpreter::Register first_reg = iterator->GetRegisterOperand(1); int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2)); ProcessCallVarArgs(ConvertReceiverMode::kNullOrUndefined, callee, first_reg, reg_count, FeedbackSlot::Invalid()); } Hints SerializerForBackgroundCompilation::RunChildSerializer( CompilationSubject function, base::Optional<Hints> new_target, const HintsVector& arguments, bool with_spread) { if (with_spread) { DCHECK_LT(0, arguments.size()); // Pad the missing arguments in case we were called with spread operator. // Drop the last actually passed argument, which contains the spread. // We don't know what the spread element produces. Therefore we pretend // that the function is called with the maximal number of parameters and // that we have no information about the parameters that were not // explicitly provided. HintsVector padded = arguments; padded.pop_back(); // Remove the spread element. // Fill the rest with empty hints. padded.resize( function.blueprint().shared()->GetBytecodeArray().parameter_count(), Hints(zone())); return RunChildSerializer(function, new_target, padded, false); } SerializerForBackgroundCompilation child_serializer( broker(), dependencies(), zone(), function, new_target, arguments, flags()); return child_serializer.Run(); } bool SerializerForBackgroundCompilation::ProcessSFIForCallOrConstruct( Handle<SharedFunctionInfo> shared, const HintsVector& arguments, SpeculationMode speculation_mode) { if (shared->IsApiFunction()) { ProcessApiCall(shared, arguments); DCHECK(!shared->IsInlineable()); } else if (shared->HasBuiltinId()) { ProcessBuiltinCall(shared, arguments, speculation_mode); DCHECK(!shared->IsInlineable()); } return shared->IsInlineable(); } bool SerializerForBackgroundCompilation::ProcessCalleeForCallOrConstruct( Handle<JSFunction> function, const HintsVector& arguments, SpeculationMode speculation_mode) { JSFunctionRef(broker(), function).Serialize(); Handle<SharedFunctionInfo> shared(function->shared(), broker()->isolate()); return ProcessSFIForCallOrConstruct(shared, arguments, speculation_mode) && function->has_feedback_vector(); } namespace { // Returns the innermost bound target, if it's a JSFunction and inserts // all bound arguments and {original_arguments} into {expanded_arguments} // in the appropriate order. MaybeHandle<JSFunction> UnrollBoundFunction( JSBoundFunctionRef const& bound_function, JSHeapBroker* broker, const HintsVector& original_arguments, HintsVector* expanded_arguments) { DCHECK(expanded_arguments->empty()); JSReceiverRef target = bound_function.AsJSReceiver(); HintsVector reversed_bound_arguments(broker->zone()); for (; target.IsJSBoundFunction(); target = target.AsJSBoundFunction().bound_target_function()) { for (int i = target.AsJSBoundFunction().bound_arguments().length() - 1; i >= 0; --i) { Hints arg = Hints::SingleConstant( target.AsJSBoundFunction().bound_arguments().get(i).object(), broker->zone()); reversed_bound_arguments.push_back(arg); } Hints arg = Hints::SingleConstant( target.AsJSBoundFunction().bound_this().object(), broker->zone()); reversed_bound_arguments.push_back(arg); } if (!target.IsJSFunction()) return MaybeHandle<JSFunction>(); expanded_arguments->insert(expanded_arguments->end(), reversed_bound_arguments.rbegin(), reversed_bound_arguments.rend()); expanded_arguments->insert(expanded_arguments->end(), original_arguments.begin(), original_arguments.end()); return target.AsJSFunction().object(); } } // namespace void SerializerForBackgroundCompilation::ProcessCallOrConstruct( Hints callee, base::Optional<Hints> new_target, const HintsVector& arguments, FeedbackSlot slot, bool with_spread) { SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation; if (!slot.IsInvalid()) { FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForCall(source); if (BailoutOnUninitialized(feedback)) return; // Incorporate feedback into hints copy to simplify processing. if (!feedback.IsInsufficient()) { speculation_mode = feedback.AsCall().speculation_mode(); base::Optional<HeapObjectRef> target = feedback.AsCall().target(); if (target.has_value() && target->map().is_callable()) { // TODO(mvstanton): if the map isn't callable then we have an allocation // site, and it may make sense to add the Array JSFunction constant. if (new_target.has_value()) { // Construct; feedback is new_target, which often is also the callee. new_target->AddConstant(target->object()); callee.AddConstant(target->object()); } else { // Call; target is callee. callee.AddConstant(target->object()); } } } } environment()->accumulator_hints().Clear(); // For JSCallReducer::ReduceJSCall and JSCallReducer::ReduceJSConstruct. for (auto hint : callee.constants()) { const HintsVector* actual_arguments = &arguments; Handle<JSFunction> function; HintsVector expanded_arguments(zone()); if (hint->IsJSBoundFunction()) { JSBoundFunctionRef bound_function(broker(), Handle<JSBoundFunction>::cast(hint)); bound_function.Serialize(); MaybeHandle<JSFunction> maybe_function = UnrollBoundFunction( bound_function, broker(), arguments, &expanded_arguments); if (maybe_function.is_null()) continue; function = maybe_function.ToHandleChecked(); actual_arguments = &expanded_arguments; } else if (hint->IsJSFunction()) { function = Handle<JSFunction>::cast(hint); } else { continue; } if (ProcessCalleeForCallOrConstruct(function, *actual_arguments, speculation_mode)) { environment()->accumulator_hints().Add(RunChildSerializer( CompilationSubject(function, broker()->isolate(), zone()), new_target, *actual_arguments, with_spread)); } } // For JSCallReducer::ReduceJSCall and JSCallReducer::ReduceJSConstruct. for (auto hint : callee.function_blueprints()) { Handle<SharedFunctionInfo> shared = hint.shared(); if (!ProcessSFIForCallOrConstruct(shared, arguments, speculation_mode)) { continue; } environment()->accumulator_hints().Add(RunChildSerializer( CompilationSubject(hint), new_target, arguments, with_spread)); } } void SerializerForBackgroundCompilation::ProcessCallVarArgs( ConvertReceiverMode receiver_mode, Hints const& callee, interpreter::Register first_reg, int reg_count, FeedbackSlot slot, bool with_spread) { HintsVector arguments(zone()); // The receiver is either given in the first register or it is implicitly // the {undefined} value. if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { arguments.push_back(Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone())); } environment()->ExportRegisterHints(first_reg, reg_count, &arguments); ProcessCallOrConstruct(callee, base::nullopt, arguments, slot); } void SerializerForBackgroundCompilation::ProcessApiCall( Handle<SharedFunctionInfo> target, const HintsVector& arguments) { ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kCallFunctionTemplate_CheckAccess)); ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kCallFunctionTemplate_CheckCompatibleReceiver)); ObjectRef( broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kCallFunctionTemplate_CheckAccessAndCompatibleReceiver)); FunctionTemplateInfoRef target_template_info( broker(), handle(target->function_data(), broker()->isolate())); if (!target_template_info.has_call_code()) return; target_template_info.SerializeCallCode(); SharedFunctionInfoRef target_ref(broker(), target); target_ref.SerializeFunctionTemplateInfo(); if (target_template_info.accept_any_receiver() && target_template_info.is_signature_undefined()) return; CHECK_GE(arguments.size(), 1); Hints const& receiver_hints = arguments[0]; for (auto hint : receiver_hints.constants()) { if (hint->IsUndefined()) { // The receiver is the global proxy. Handle<JSGlobalProxy> global_proxy = broker()->target_native_context().global_proxy_object().object(); ProcessReceiverMapForApiCall( target_template_info, handle(global_proxy->map(), broker()->isolate())); continue; } if (!hint->IsJSReceiver()) continue; Handle<JSReceiver> receiver(Handle<JSReceiver>::cast(hint)); ProcessReceiverMapForApiCall(target_template_info, handle(receiver->map(), broker()->isolate())); } for (auto receiver_map : receiver_hints.maps()) { ProcessReceiverMapForApiCall(target_template_info, receiver_map); } } void SerializerForBackgroundCompilation::ProcessReceiverMapForApiCall( FunctionTemplateInfoRef target, Handle<Map> receiver) { if (!receiver->is_access_check_needed()) { MapRef receiver_map(broker(), receiver); TRACE_BROKER(broker(), "Serializing holder for target:" << target); target.LookupHolderOfExpectedType(receiver_map, SerializationPolicy::kSerializeIfNeeded); } } void SerializerForBackgroundCompilation::ProcessHintsForObjectCreate( Hints const& prototype) { for (Handle<Object> constant_handle : prototype.constants()) { ObjectRef constant(broker(), constant_handle); if (constant.IsJSObject()) constant.AsJSObject().SerializeObjectCreateMap(); } } void SerializerForBackgroundCompilation::ProcessBuiltinCall( Handle<SharedFunctionInfo> target, const HintsVector& arguments, SpeculationMode speculation_mode) { DCHECK(target->HasBuiltinId()); const int builtin_id = target->builtin_id(); const char* name = Builtins::name(builtin_id); TRACE_BROKER(broker(), "Serializing for call to builtin " << name); switch (builtin_id) { case Builtins::kObjectCreate: { if (arguments.size() >= 2) { ProcessHintsForObjectCreate(arguments[1]); } else { ProcessHintsForObjectCreate(Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone())); } break; } case Builtins::kPromisePrototypeCatch: { // For JSCallReducer::ReducePromisePrototypeCatch. if (speculation_mode != SpeculationMode::kDisallowSpeculation) { CHECK_GE(arguments.size(), 1); ProcessMapHintsForPromises(arguments[0]); } break; } case Builtins::kPromisePrototypeFinally: { // For JSCallReducer::ReducePromisePrototypeFinally. if (speculation_mode != SpeculationMode::kDisallowSpeculation) { CHECK_GE(arguments.size(), 1); ProcessMapHintsForPromises(arguments[0]); } break; } case Builtins::kPromisePrototypeThen: { // For JSCallReducer::ReducePromisePrototypeThen. if (speculation_mode != SpeculationMode::kDisallowSpeculation) { CHECK_GE(arguments.size(), 1); ProcessMapHintsForPromises(arguments[0]); } break; } case Builtins::kPromiseResolveTrampoline: // For JSCallReducer::ReducePromiseInternalResolve and // JSNativeContextSpecialization::ReduceJSResolvePromise. if (arguments.size() >= 2) { Hints const& resolution_hints = arguments[1]; ProcessHintsForPromiseResolve(resolution_hints); } break; case Builtins::kPromiseInternalResolve: // For JSCallReducer::ReducePromiseInternalResolve and // JSNativeContextSpecialization::ReduceJSResolvePromise. if (arguments.size() >= 3) { Hints const& resolution_hints = arguments[2]; ProcessHintsForPromiseResolve(resolution_hints); } break; case Builtins::kRegExpPrototypeTest: // For JSCallReducer::ReduceRegExpPrototypeTest. if (arguments.size() >= 1 && speculation_mode != SpeculationMode::kDisallowSpeculation) { Hints const& regexp_hints = arguments[0]; ProcessHintsForRegExpTest(regexp_hints); } break; case Builtins::kArrayEvery: case Builtins::kArrayFilter: case Builtins::kArrayForEach: case Builtins::kArrayPrototypeFind: case Builtins::kArrayPrototypeFindIndex: case Builtins::kArrayMap: case Builtins::kArrayReduce: case Builtins::kArrayReduceRight: case Builtins::kArraySome: if (arguments.size() >= 2 && speculation_mode != SpeculationMode::kDisallowSpeculation) { Hints const& callback_hints = arguments[1]; ProcessHintsForFunctionCall(callback_hints); } break; case Builtins::kFunctionPrototypeApply: case Builtins::kFunctionPrototypeCall: case Builtins::kPromiseConstructor: // TODO(mslekova): Since the reducer for all these introduce a // JSCall/JSConstruct that will again get optimized by the JSCallReducer, // we basically might have to do all the serialization that we do for that // here as well. The only difference is that the new JSCall/JSConstruct // has speculation disabled, causing the JSCallReducer to do much less // work. To account for that, ProcessCallOrConstruct should have a way of // taking the speculation mode as an argument rather than getting that // from the feedback. (Also applies to Reflect.apply and // Reflect.construct.) if (arguments.size() >= 1) { ProcessHintsForFunctionCall(arguments[0]); } break; case Builtins::kReflectApply: case Builtins::kReflectConstruct: if (arguments.size() >= 2) { ProcessHintsForFunctionCall(arguments[1]); } break; case Builtins::kObjectPrototypeIsPrototypeOf: if (arguments.size() >= 2) { ProcessHintsForHasInPrototypeChain(arguments[1]); } break; case Builtins::kFunctionPrototypeHasInstance: // For JSCallReducer::ReduceFunctionPrototypeHasInstance. if (arguments.size() >= 2) { ProcessHintsForOrdinaryHasInstance(arguments[0], arguments[1]); } break; case Builtins::kFastFunctionPrototypeBind: if (arguments.size() >= 1 && speculation_mode != SpeculationMode::kDisallowSpeculation) { ProcessHintsForFunctionBind(arguments[0]); } break; case Builtins::kObjectGetPrototypeOf: case Builtins::kReflectGetPrototypeOf: if (arguments.size() >= 2) { ProcessHintsForObjectGetPrototype(arguments[1]); } else { Hints undefined_hint = Hints::SingleConstant( broker()->isolate()->factory()->undefined_value(), zone()); ProcessHintsForObjectGetPrototype(undefined_hint); } break; case Builtins::kObjectPrototypeGetProto: if (arguments.size() >= 1) { ProcessHintsForObjectGetPrototype(arguments[0]); } break; case Builtins::kMapIteratorPrototypeNext: ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kOrderedHashTableHealIndex)); ObjectRef(broker(), broker()->isolate()->factory()->empty_ordered_hash_map()); break; case Builtins::kSetIteratorPrototypeNext: ObjectRef(broker(), broker()->isolate()->builtins()->builtin_handle( Builtins::kOrderedHashTableHealIndex)); ObjectRef(broker(), broker()->isolate()->factory()->empty_ordered_hash_set()); break; default: break; } } void SerializerForBackgroundCompilation::ProcessHintsForOrdinaryHasInstance( Hints const& constructor_hints, Hints const& instance_hints) { bool walk_prototypes = false; for (Handle<Object> constructor : constructor_hints.constants()) { // For JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance. if (constructor->IsHeapObject()) { ProcessConstantForOrdinaryHasInstance( HeapObjectRef(broker(), constructor), &walk_prototypes); } } // For JSNativeContextSpecialization::ReduceJSHasInPrototypeChain. if (walk_prototypes) ProcessHintsForHasInPrototypeChain(instance_hints); } void SerializerForBackgroundCompilation::ProcessHintsForHasInPrototypeChain( Hints const& instance_hints) { auto processMap = [&](Handle<Map> map_handle) { MapRef map(broker(), map_handle); while (map.IsJSObjectMap()) { map.SerializePrototype(); map = map.prototype().map(); } }; for (auto hint : instance_hints.constants()) { if (!hint->IsHeapObject()) continue; Handle<HeapObject> object(Handle<HeapObject>::cast(hint)); processMap(handle(object->map(), broker()->isolate())); } for (auto map_hint : instance_hints.maps()) { processMap(map_hint); } } void SerializerForBackgroundCompilation::ProcessHintsForPromiseResolve( Hints const& resolution_hints) { auto processMap = [&](Handle<Map> map) { broker()->GetPropertyAccessInfo( MapRef(broker(), map), NameRef(broker(), broker()->isolate()->factory()->then_string()), AccessMode::kLoad, dependencies(), SerializationPolicy::kSerializeIfNeeded); }; for (auto hint : resolution_hints.constants()) { if (!hint->IsJSReceiver()) continue; Handle<JSReceiver> receiver(Handle<JSReceiver>::cast(hint)); processMap(handle(receiver->map(), broker()->isolate())); } for (auto map_hint : resolution_hints.maps()) { processMap(map_hint); } } void SerializerForBackgroundCompilation::ProcessMapHintsForPromises( Hints const& receiver_hints) { // We need to serialize the prototypes on each receiver map. for (auto constant : receiver_hints.constants()) { if (!constant->IsJSPromise()) continue; Handle<Map> map(Handle<HeapObject>::cast(constant)->map(), broker()->isolate()); MapRef(broker(), map).SerializePrototype(); } for (auto map : receiver_hints.maps()) { if (!map->IsJSPromiseMap()) continue; MapRef(broker(), map).SerializePrototype(); } } PropertyAccessInfo SerializerForBackgroundCompilation::ProcessMapForRegExpTest( MapRef map) { PropertyAccessInfo ai_exec = broker()->GetPropertyAccessInfo( map, NameRef(broker(), broker()->isolate()->factory()->exec_string()), AccessMode::kLoad, dependencies(), SerializationPolicy::kSerializeIfNeeded); Handle<JSObject> holder; if (ai_exec.IsDataConstant() && ai_exec.holder().ToHandle(&holder)) { // The property is on the prototype chain. JSObjectRef holder_ref(broker(), holder); holder_ref.GetOwnDataProperty(ai_exec.field_representation(), ai_exec.field_index(), SerializationPolicy::kSerializeIfNeeded); } return ai_exec; } void SerializerForBackgroundCompilation::ProcessHintsForRegExpTest( Hints const& regexp_hints) { for (auto hint : regexp_hints.constants()) { if (!hint->IsJSRegExp()) continue; Handle<JSRegExp> regexp(Handle<JSRegExp>::cast(hint)); Handle<Map> regexp_map(regexp->map(), broker()->isolate()); PropertyAccessInfo ai_exec = ProcessMapForRegExpTest(MapRef(broker(), regexp_map)); Handle<JSObject> holder; if (ai_exec.IsDataConstant() && !ai_exec.holder().ToHandle(&holder)) { // The property is on the object itself. JSObjectRef holder_ref(broker(), regexp); holder_ref.GetOwnDataProperty(ai_exec.field_representation(), ai_exec.field_index(), SerializationPolicy::kSerializeIfNeeded); } } for (auto map : regexp_hints.maps()) { if (!map->IsJSRegExpMap()) continue; ProcessMapForRegExpTest(MapRef(broker(), map)); } } void SerializerForBackgroundCompilation::ProcessHintsForFunctionCall( Hints const& target_hints) { for (auto constant : target_hints.constants()) { if (constant->IsJSFunction()) JSFunctionRef(broker(), constant).Serialize(); } } namespace { void ProcessMapForFunctionBind(MapRef map) { map.SerializePrototype(); int min_nof_descriptors = i::Max(JSFunction::kLengthDescriptorIndex, JSFunction::kNameDescriptorIndex) + 1; if (map.NumberOfOwnDescriptors() >= min_nof_descriptors) { map.SerializeOwnDescriptor(JSFunction::kLengthDescriptorIndex); map.SerializeOwnDescriptor(JSFunction::kNameDescriptorIndex); } } } // namespace void SerializerForBackgroundCompilation::ProcessHintsForFunctionBind( Hints const& receiver_hints) { for (auto constant : receiver_hints.constants()) { if (!constant->IsJSFunction()) continue; JSFunctionRef function(broker(), constant); function.Serialize(); ProcessMapForFunctionBind(function.map()); } for (auto map : receiver_hints.maps()) { if (!map->IsJSFunctionMap()) continue; MapRef map_ref(broker(), map); ProcessMapForFunctionBind(map_ref); } } void SerializerForBackgroundCompilation::ProcessHintsForObjectGetPrototype( Hints const& object_hints) { for (auto constant : object_hints.constants()) { if (!constant->IsHeapObject()) continue; HeapObjectRef object(broker(), constant); object.map().SerializePrototype(); } for (auto map : object_hints.maps()) { MapRef map_ref(broker(), map); map_ref.SerializePrototype(); } } void SerializerForBackgroundCompilation::ContributeToJumpTargetEnvironment( int target_offset) { auto it = jump_target_environments_.find(target_offset); if (it == jump_target_environments_.end()) { jump_target_environments_[target_offset] = new (zone()) Environment(*environment()); } else { it->second->Merge(environment()); } } void SerializerForBackgroundCompilation::IncorporateJumpTargetEnvironment( int target_offset) { auto it = jump_target_environments_.find(target_offset); if (it != jump_target_environments_.end()) { environment()->Merge(it->second); jump_target_environments_.erase(it); } } void SerializerForBackgroundCompilation::ProcessJump( interpreter::BytecodeArrayIterator* iterator) { int jump_target = iterator->GetJumpTargetOffset(); if (iterator->current_offset() < jump_target) { ContributeToJumpTargetEnvironment(jump_target); } } void SerializerForBackgroundCompilation::VisitReturn( BytecodeArrayIterator* iterator) { environment()->return_value_hints().Add(environment()->accumulator_hints()); environment()->ClearEphemeralHints(); } void SerializerForBackgroundCompilation::VisitSwitchOnSmiNoFeedback( interpreter::BytecodeArrayIterator* iterator) { interpreter::JumpTableTargetOffsets targets = iterator->GetJumpTableTargetOffsets(); for (const auto& target : targets) { ContributeToJumpTargetEnvironment(target.target_offset); } } void SerializerForBackgroundCompilation::VisitSwitchOnGeneratorState( interpreter::BytecodeArrayIterator* iterator) { for (const auto& target : GetBytecodeAnalysis().resume_jump_targets()) { ContributeToJumpTargetEnvironment(target.target_offset()); } } void SerializerForBackgroundCompilation::Environment::ExportRegisterHints( interpreter::Register first, size_t count, HintsVector* dst) { const int reg_base = first.index(); for (int i = 0; i < static_cast<int>(count); ++i) { dst->push_back(register_hints(interpreter::Register(reg_base + i))); } } void SerializerForBackgroundCompilation::VisitConstruct( BytecodeArrayIterator* iterator) { Hints const& new_target = environment()->accumulator_hints(); Hints const& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); size_t reg_count = iterator->GetRegisterCountOperand(2); FeedbackSlot slot = iterator->GetSlotOperand(3); HintsVector arguments(zone()); environment()->ExportRegisterHints(first_reg, reg_count, &arguments); ProcessCallOrConstruct(callee, new_target, arguments, slot); } void SerializerForBackgroundCompilation::VisitConstructWithSpread( BytecodeArrayIterator* iterator) { Hints const& new_target = environment()->accumulator_hints(); Hints const& callee = environment()->register_hints(iterator->GetRegisterOperand(0)); interpreter::Register first_reg = iterator->GetRegisterOperand(1); size_t reg_count = iterator->GetRegisterCountOperand(2); FeedbackSlot slot = iterator->GetSlotOperand(3); HintsVector arguments(zone()); environment()->ExportRegisterHints(first_reg, reg_count, &arguments); ProcessCallOrConstruct(callee, new_target, arguments, slot, true); } void SerializerForBackgroundCompilation::ProcessGlobalAccess(FeedbackSlot slot, bool is_load) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForGlobalAccess(source); if (is_load) { environment()->accumulator_hints().Clear(); if (feedback.kind() == ProcessedFeedback::kGlobalAccess) { // We may be able to contribute to accumulator constant hints. base::Optional<ObjectRef> value = feedback.AsGlobalAccess().GetConstantHint(); if (value.has_value()) { environment()->accumulator_hints().AddConstant(value->object()); } } else { DCHECK(feedback.IsInsufficient()); } } } void SerializerForBackgroundCompilation::VisitLdaGlobal( BytecodeArrayIterator* iterator) { NameRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(1); ProcessGlobalAccess(slot, true); } void SerializerForBackgroundCompilation::VisitLdaGlobalInsideTypeof( BytecodeArrayIterator* iterator) { VisitLdaGlobal(iterator); } void SerializerForBackgroundCompilation::VisitLdaLookupSlot( BytecodeArrayIterator* iterator) { ObjectRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); environment()->accumulator_hints().Clear(); } void SerializerForBackgroundCompilation::VisitLdaLookupSlotInsideTypeof( BytecodeArrayIterator* iterator) { ObjectRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); environment()->accumulator_hints().Clear(); } void SerializerForBackgroundCompilation::ProcessCheckContextExtensions( int depth) { // for BytecodeGraphBuilder::CheckContextExtensions. Hints& context_hints = environment()->current_context_hints(); for (int i = 0; i < depth; i++) { ProcessContextAccess(context_hints, Context::EXTENSION_INDEX, i, kSerializeSlot); } } void SerializerForBackgroundCompilation::ProcessLdaLookupGlobalSlot( BytecodeArrayIterator* iterator) { ProcessCheckContextExtensions(iterator->GetUnsignedImmediateOperand(2)); // TODO(neis): BytecodeGraphBilder may insert a JSLoadGlobal. VisitLdaGlobal(iterator); } void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlot( BytecodeArrayIterator* iterator) { ProcessLdaLookupGlobalSlot(iterator); } void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlotInsideTypeof( BytecodeArrayIterator* iterator) { ProcessLdaLookupGlobalSlot(iterator); } void SerializerForBackgroundCompilation::VisitStaGlobal( BytecodeArrayIterator* iterator) { NameRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(1); ProcessGlobalAccess(slot, false); } void SerializerForBackgroundCompilation::ProcessLdaLookupContextSlot( BytecodeArrayIterator* iterator) { const int slot_index = iterator->GetIndexOperand(1); const int depth = iterator->GetUnsignedImmediateOperand(2); NameRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); ProcessCheckContextExtensions(depth); environment()->accumulator_hints().Clear(); ProcessContextAccess(environment()->current_context_hints(), slot_index, depth, kIgnoreSlot); } void SerializerForBackgroundCompilation::VisitLdaLookupContextSlot( BytecodeArrayIterator* iterator) { ProcessLdaLookupContextSlot(iterator); } void SerializerForBackgroundCompilation::VisitLdaLookupContextSlotInsideTypeof( BytecodeArrayIterator* iterator) { ProcessLdaLookupContextSlot(iterator); } // TODO(neis): Avoid duplicating this. namespace { template <class MapContainer> MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) { MapHandles result; for (Handle<Map> map : maps) { if (Map::TryUpdate(isolate, map).ToHandle(&map) && !map->is_abandoned_prototype_map()) { DCHECK(!map->is_deprecated()); result.push_back(map); } } return result; } } // namespace void SerializerForBackgroundCompilation::ProcessCompareOperation( FeedbackSlot slot) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(environment()->function().feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForCompareOperation(source); if (BailoutOnUninitialized(feedback)) return; environment()->accumulator_hints().Clear(); } void SerializerForBackgroundCompilation::ProcessForIn(FeedbackSlot slot) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForForIn(source); if (BailoutOnUninitialized(feedback)) return; environment()->accumulator_hints().Clear(); } void SerializerForBackgroundCompilation::ProcessUnaryOrBinaryOperation( FeedbackSlot slot, bool honor_bailout_on_uninitialized) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); // Internally V8 uses binary op feedback also for unary ops. ProcessedFeedback const& feedback = broker()->ProcessFeedbackForBinaryOperation(source); if (honor_bailout_on_uninitialized && BailoutOnUninitialized(feedback)) { return; } environment()->accumulator_hints().Clear(); } PropertyAccessInfo SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess( MapRef receiver_map, NameRef const& name, AccessMode access_mode, base::Optional<JSObjectRef> receiver, Hints* new_accumulator_hints) { // For JSNativeContextSpecialization::InferReceiverRootMap receiver_map.SerializeRootMap(); // For JSNativeContextSpecialization::ReduceNamedAccess. if (receiver_map.IsMapOfTargetGlobalProxy()) { broker()->target_native_context().global_proxy_object().GetPropertyCell( name, SerializationPolicy::kSerializeIfNeeded); } PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo( receiver_map, name, access_mode, dependencies(), SerializationPolicy::kSerializeIfNeeded); // For JSNativeContextSpecialization::InlinePropertySetterCall // and InlinePropertyGetterCall. if (access_info.IsAccessorConstant() && !access_info.constant().is_null()) { if (access_info.constant()->IsJSFunction()) { JSFunctionRef function(broker(), access_info.constant()); // For JSCallReducer::ReduceJSCall. function.Serialize(); // For JSCallReducer::ReduceCallApiFunction. Handle<SharedFunctionInfo> sfi = function.shared().object(); if (sfi->IsApiFunction()) { FunctionTemplateInfoRef fti_ref( broker(), handle(sfi->get_api_func_data(), broker()->isolate())); if (fti_ref.has_call_code()) fti_ref.SerializeCallCode(); ProcessReceiverMapForApiCall(fti_ref, receiver_map.object()); } } else if (access_info.constant()->IsJSBoundFunction()) { JSBoundFunctionRef function(broker(), access_info.constant()); // For JSCallReducer::ReduceJSCall. function.Serialize(); } else { FunctionTemplateInfoRef fti(broker(), access_info.constant()); if (fti.has_call_code()) fti.SerializeCallCode(); } } // For PropertyAccessBuilder::TryBuildLoadConstantDataField if (access_mode == AccessMode::kLoad) { if (access_info.IsDataConstant()) { base::Optional<JSObjectRef> holder; Handle<JSObject> prototype; if (access_info.holder().ToHandle(&prototype)) { holder = JSObjectRef(broker(), prototype); } else { CHECK_IMPLIES(receiver.has_value(), receiver->map().equals(receiver_map)); holder = receiver; } if (holder.has_value()) { base::Optional<ObjectRef> constant(holder->GetOwnDataProperty( access_info.field_representation(), access_info.field_index(), SerializationPolicy::kSerializeIfNeeded)); if (constant.has_value()) { new_accumulator_hints->AddConstant(constant->object()); } } } } return access_info; } void SerializerForBackgroundCompilation::VisitLdaKeyedProperty( BytecodeArrayIterator* iterator) { Hints const& key = environment()->accumulator_hints(); Hints const& receiver = environment()->register_hints(iterator->GetRegisterOperand(0)); FeedbackSlot slot = iterator->GetSlotOperand(1); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kLoad, true); } void SerializerForBackgroundCompilation::ProcessKeyedPropertyAccess( Hints const& receiver, Hints const& key, FeedbackSlot slot, AccessMode access_mode, bool honor_bailout_on_uninitialized) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForPropertyAccess(source, access_mode, base::nullopt); if (honor_bailout_on_uninitialized && BailoutOnUninitialized(feedback)) { return; } Hints new_accumulator_hints(zone()); switch (feedback.kind()) { case ProcessedFeedback::kElementAccess: ProcessElementAccess(receiver, key, feedback.AsElementAccess(), access_mode); break; case ProcessedFeedback::kNamedAccess: ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode, &new_accumulator_hints); break; case ProcessedFeedback::kInsufficient: break; default: UNREACHABLE(); } if (access_mode == AccessMode::kLoad) { environment()->accumulator_hints().Clear(); environment()->accumulator_hints().Add(new_accumulator_hints); } else { DCHECK(new_accumulator_hints.IsEmpty()); } } void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess( Hints receiver, NameRef const& name, FeedbackSlot slot, AccessMode access_mode) { if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForPropertyAccess(source, access_mode, name); if (BailoutOnUninitialized(feedback)) return; Hints new_accumulator_hints(zone()); switch (feedback.kind()) { case ProcessedFeedback::kNamedAccess: DCHECK(name.equals(feedback.AsNamedAccess().name())); ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode, &new_accumulator_hints); break; case ProcessedFeedback::kInsufficient: break; default: UNREACHABLE(); } if (access_mode == AccessMode::kLoad) { environment()->accumulator_hints().Clear(); environment()->accumulator_hints().Add(new_accumulator_hints); } else { DCHECK(new_accumulator_hints.IsEmpty()); } } void SerializerForBackgroundCompilation::ProcessNamedAccess( Hints receiver, NamedAccessFeedback const& feedback, AccessMode access_mode, Hints* new_accumulator_hints) { for (Handle<Map> map : feedback.AsNamedAccess().maps()) { MapRef map_ref(broker(), map); ProcessMapForNamedPropertyAccess(map_ref, feedback.name(), access_mode, base::nullopt, new_accumulator_hints); } for (Handle<Map> map : GetRelevantReceiverMaps(broker()->isolate(), receiver.maps())) { MapRef map_ref(broker(), map); ProcessMapForNamedPropertyAccess(map_ref, feedback.name(), access_mode, base::nullopt, new_accumulator_hints); } JSGlobalProxyRef global_proxy = broker()->target_native_context().global_proxy_object(); for (Handle<Object> hint : receiver.constants()) { ObjectRef object(broker(), hint); if (access_mode == AccessMode::kLoad && object.IsJSObject()) { MapRef map_ref = object.AsJSObject().map(); ProcessMapForNamedPropertyAccess(map_ref, feedback.name(), access_mode, object.AsJSObject(), new_accumulator_hints); } // For JSNativeContextSpecialization::ReduceNamedAccessFromNexus. if (object.equals(global_proxy)) { // TODO(neis): Record accumulator hint? Also for string.length and maybe // more. global_proxy.GetPropertyCell(feedback.name(), SerializationPolicy::kSerializeIfNeeded); } // For JSNativeContextSpecialization::ReduceJSLoadNamed. if (access_mode == AccessMode::kLoad && object.IsJSFunction() && feedback.name().equals(ObjectRef( broker(), broker()->isolate()->factory()->prototype_string()))) { JSFunctionRef function = object.AsJSFunction(); function.Serialize(); if (new_accumulator_hints != nullptr && function.has_prototype()) { new_accumulator_hints->AddConstant(function.prototype().object()); } } } } void SerializerForBackgroundCompilation::ProcessElementAccess( Hints receiver, Hints key, ElementAccessFeedback const& feedback, AccessMode access_mode) { for (auto const& group : feedback.transition_groups()) { for (Handle<Map> map_handle : group) { MapRef map(broker(), map_handle); switch (access_mode) { case AccessMode::kHas: case AccessMode::kLoad: map.SerializeForElementLoad(); break; case AccessMode::kStore: map.SerializeForElementStore(); break; case AccessMode::kStoreInLiteral: // This operation is fairly local and simple, nothing to serialize. break; } } } for (Handle<Object> hint : receiver.constants()) { ObjectRef receiver_ref(broker(), hint); // For JSNativeContextSpecialization::InferReceiverRootMap if (receiver_ref.IsHeapObject()) { receiver_ref.AsHeapObject().map().SerializeRootMap(); } // For JSNativeContextSpecialization::ReduceElementAccess. if (receiver_ref.IsJSTypedArray()) { receiver_ref.AsJSTypedArray().Serialize(); } // For JSNativeContextSpecialization::ReduceElementLoadFromHeapConstant. if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) { for (Handle<Object> hint : key.constants()) { ObjectRef key_ref(broker(), hint); // TODO(neis): Do this for integer-HeapNumbers too? if (key_ref.IsSmi() && key_ref.AsSmi() >= 0) { base::Optional<ObjectRef> element = receiver_ref.GetOwnConstantElement( key_ref.AsSmi(), SerializationPolicy::kSerializeIfNeeded); if (!element.has_value() && receiver_ref.IsJSArray()) { // We didn't find a constant element, but if the receiver is a // cow-array we can exploit the fact that any future write to the // element will replace the whole elements storage. receiver_ref.AsJSArray().GetOwnCowElement( key_ref.AsSmi(), SerializationPolicy::kSerializeIfNeeded); } } } } } // For JSNativeContextSpecialization::InferReceiverRootMap for (Handle<Map> map : receiver.maps()) { MapRef map_ref(broker(), map); map_ref.SerializeRootMap(); } } void SerializerForBackgroundCompilation::VisitLdaNamedProperty( BytecodeArrayIterator* iterator) { Hints const& receiver = environment()->register_hints(iterator->GetRegisterOperand(0)); NameRef name(broker(), iterator->GetConstantForIndexOperand(1, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(2); ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kLoad); } // TODO(neis): Do feedback-independent serialization also for *NoFeedback // bytecodes. void SerializerForBackgroundCompilation::VisitLdaNamedPropertyNoFeedback( BytecodeArrayIterator* iterator) { NameRef(broker(), iterator->GetConstantForIndexOperand(1, broker()->isolate())); } void SerializerForBackgroundCompilation::VisitStaNamedProperty( BytecodeArrayIterator* iterator) { Hints const& receiver = environment()->register_hints(iterator->GetRegisterOperand(0)); NameRef name(broker(), iterator->GetConstantForIndexOperand(1, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(2); ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kStore); } void SerializerForBackgroundCompilation::VisitStaNamedPropertyNoFeedback( BytecodeArrayIterator* iterator) { NameRef(broker(), iterator->GetConstantForIndexOperand(1, broker()->isolate())); } void SerializerForBackgroundCompilation::VisitStaNamedOwnProperty( BytecodeArrayIterator* iterator) { Hints const& receiver = environment()->register_hints(iterator->GetRegisterOperand(0)); NameRef name(broker(), iterator->GetConstantForIndexOperand(1, broker()->isolate())); FeedbackSlot slot = iterator->GetSlotOperand(2); ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kStoreInLiteral); } void SerializerForBackgroundCompilation::VisitTestIn( BytecodeArrayIterator* iterator) { Hints const& receiver = environment()->accumulator_hints(); Hints const& key = environment()->register_hints(iterator->GetRegisterOperand(0)); FeedbackSlot slot = iterator->GetSlotOperand(1); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kHas, false); } // For JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance. void SerializerForBackgroundCompilation::ProcessConstantForOrdinaryHasInstance( HeapObjectRef const& constructor, bool* walk_prototypes) { if (constructor.IsJSBoundFunction()) { constructor.AsJSBoundFunction().Serialize(); ProcessConstantForInstanceOf( constructor.AsJSBoundFunction().bound_target_function(), walk_prototypes); } else if (constructor.IsJSFunction()) { constructor.AsJSFunction().Serialize(); *walk_prototypes = *walk_prototypes || (constructor.map().has_prototype_slot() && constructor.AsJSFunction().has_prototype() && !constructor.AsJSFunction().PrototypeRequiresRuntimeLookup()); } } void SerializerForBackgroundCompilation::ProcessConstantForInstanceOf( ObjectRef const& constructor, bool* walk_prototypes) { if (!constructor.IsHeapObject()) return; HeapObjectRef constructor_heap_object = constructor.AsHeapObject(); PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo( constructor_heap_object.map(), NameRef(broker(), broker()->isolate()->factory()->has_instance_symbol()), AccessMode::kLoad, dependencies(), SerializationPolicy::kSerializeIfNeeded); if (access_info.IsNotFound()) { ProcessConstantForOrdinaryHasInstance(constructor_heap_object, walk_prototypes); } else if (access_info.IsDataConstant()) { Handle<JSObject> holder; bool found_on_proto = access_info.holder().ToHandle(&holder); JSObjectRef holder_ref = found_on_proto ? JSObjectRef(broker(), holder) : constructor.AsJSObject(); base::Optional<ObjectRef> constant = holder_ref.GetOwnDataProperty( access_info.field_representation(), access_info.field_index(), SerializationPolicy::kSerializeIfNeeded); CHECK(constant.has_value()); if (constant->IsJSFunction()) { JSFunctionRef function = constant->AsJSFunction(); function.Serialize(); if (function.shared().HasBuiltinId() && function.shared().builtin_id() == Builtins::kFunctionPrototypeHasInstance) { // For JSCallReducer::ReduceFunctionPrototypeHasInstance. ProcessConstantForOrdinaryHasInstance(constructor_heap_object, walk_prototypes); } } } } void SerializerForBackgroundCompilation::VisitTestInstanceOf( BytecodeArrayIterator* iterator) { Hints const& lhs = environment()->register_hints(iterator->GetRegisterOperand(0)); Hints rhs = environment()->accumulator_hints(); FeedbackSlot slot = iterator->GetSlotOperand(1); Hints new_accumulator_hints(zone()); if (slot.IsInvalid() || feedback_vector().is_null()) return; FeedbackSource source(feedback_vector(), slot); ProcessedFeedback const& feedback = broker()->ProcessFeedbackForInstanceOf(source); // Incorporate feedback (about rhs) into hints copy to simplify processing. if (!feedback.IsInsufficient()) { InstanceOfFeedback const& rhs_feedback = feedback.AsInstanceOf(); if (rhs_feedback.value().has_value()) { Handle<JSObject> constructor = rhs_feedback.value()->object(); rhs.AddConstant(constructor); } } bool walk_prototypes = false; for (Handle<Object> constant : rhs.constants()) { ProcessConstantForInstanceOf(ObjectRef(broker(), constant), &walk_prototypes); } if (walk_prototypes) ProcessHintsForHasInPrototypeChain(lhs); environment()->accumulator_hints().Clear(); environment()->accumulator_hints().Add(new_accumulator_hints); } void SerializerForBackgroundCompilation::VisitToNumeric( BytecodeArrayIterator* iterator) { FeedbackSlot slot = iterator->GetSlotOperand(0); ProcessUnaryOrBinaryOperation(slot, false); } void SerializerForBackgroundCompilation::VisitToNumber( BytecodeArrayIterator* iterator) { FeedbackSlot slot = iterator->GetSlotOperand(0); ProcessUnaryOrBinaryOperation(slot, false); } void SerializerForBackgroundCompilation::VisitThrowReferenceErrorIfHole( BytecodeArrayIterator* iterator) { ObjectRef(broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate())); } void SerializerForBackgroundCompilation::VisitStaKeyedProperty( BytecodeArrayIterator* iterator) { Hints const& receiver = environment()->register_hints(iterator->GetRegisterOperand(0)); Hints const& key = environment()->register_hints(iterator->GetRegisterOperand(1)); FeedbackSlot slot = iterator->GetSlotOperand(2); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStore, true); } void SerializerForBackgroundCompilation::VisitStaInArrayLiteral( BytecodeArrayIterator* iterator) { Hints const& receiver = environment()->register_hints(iterator->GetRegisterOperand(0)); Hints const& key = environment()->register_hints(iterator->GetRegisterOperand(1)); FeedbackSlot slot = iterator->GetSlotOperand(2); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStoreInLiteral, true); } void SerializerForBackgroundCompilation::VisitStaDataPropertyInLiteral( BytecodeArrayIterator* iterator) { Hints const& receiver = environment()->register_hints(iterator->GetRegisterOperand(0)); Hints const& key = environment()->register_hints(iterator->GetRegisterOperand(1)); FeedbackSlot slot = iterator->GetSlotOperand(3); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStoreInLiteral, false); } #define DEFINE_CLEAR_ENVIRONMENT(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ environment()->ClearEphemeralHints(); \ } CLEAR_ENVIRONMENT_LIST(DEFINE_CLEAR_ENVIRONMENT) #undef DEFINE_CLEAR_ENVIRONMENT #define DEFINE_CLEAR_ACCUMULATOR(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ environment()->accumulator_hints().Clear(); \ } CLEAR_ACCUMULATOR_LIST(DEFINE_CLEAR_ACCUMULATOR) #undef DEFINE_CLEAR_ACCUMULATOR #define DEFINE_CONDITIONAL_JUMP(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ ProcessJump(iterator); \ } CONDITIONAL_JUMPS_LIST(DEFINE_CONDITIONAL_JUMP) #undef DEFINE_CONDITIONAL_JUMP #define DEFINE_UNCONDITIONAL_JUMP(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ ProcessJump(iterator); \ environment()->ClearEphemeralHints(); \ } UNCONDITIONAL_JUMPS_LIST(DEFINE_UNCONDITIONAL_JUMP) #undef DEFINE_UNCONDITIONAL_JUMP #define DEFINE_IGNORE(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) {} IGNORED_BYTECODE_LIST(DEFINE_IGNORE) #undef DEFINE_IGNORE #define DEFINE_UNREACHABLE(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ UNREACHABLE(); \ } UNREACHABLE_BYTECODE_LIST(DEFINE_UNREACHABLE) #undef DEFINE_UNREACHABLE #define DEFINE_KILL(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ environment()->Kill(); \ } KILL_ENVIRONMENT_LIST(DEFINE_KILL) #undef DEFINE_KILL #define DEFINE_BINARY_OP(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ FeedbackSlot slot = iterator->GetSlotOperand(1); \ ProcessUnaryOrBinaryOperation(slot, true); \ } BINARY_OP_LIST(DEFINE_BINARY_OP) #undef DEFINE_BINARY_OP #define DEFINE_COMPARE_OP(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ FeedbackSlot slot = iterator->GetSlotOperand(1); \ ProcessCompareOperation(slot); \ } COMPARE_OP_LIST(DEFINE_COMPARE_OP) #undef DEFINE_COMPARE_OP #define DEFINE_UNARY_OP(name, ...) \ void SerializerForBackgroundCompilation::Visit##name( \ BytecodeArrayIterator* iterator) { \ FeedbackSlot slot = iterator->GetSlotOperand(0); \ ProcessUnaryOrBinaryOperation(slot, true); \ } UNARY_OP_LIST(DEFINE_UNARY_OP) #undef DEFINE_UNARY_OP #undef BINARY_OP_LIST #undef CLEAR_ACCUMULATOR_LIST #undef CLEAR_ENVIRONMENT_LIST #undef COMPARE_OP_LIST #undef CONDITIONAL_JUMPS_LIST #undef IGNORED_BYTECODE_LIST #undef KILL_ENVIRONMENT_LIST #undef SUPPORTED_BYTECODE_LIST #undef UNARY_OP_LIST #undef UNCONDITIONAL_JUMPS_LIST #undef UNREACHABLE_BYTECODE_LIST } // namespace compiler } // namespace internal } // namespace v8