// Copyright 2016 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. #ifndef V8_IC_ACCESSOR_ASSEMBLER_H_ #define V8_IC_ACCESSOR_ASSEMBLER_H_ #include "src/base/optional.h" #include "src/codegen/code-stub-assembler.h" #include "src/compiler/code-assembler.h" namespace v8 { namespace internal { namespace compiler { class CodeAssemblerState; } // namespace compiler class ExitPoint; class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler { public: explicit AccessorAssembler(compiler::CodeAssemblerState* state) : CodeStubAssembler(state) {} void GenerateLoadIC(); void GenerateLoadIC_Megamorphic(); void GenerateLoadIC_Noninlined(); void GenerateLoadIC_NoFeedback(); void GenerateLoadGlobalIC_NoFeedback(); void GenerateLoadICTrampoline(); void GenerateLoadICBaseline(); void GenerateLoadICTrampoline_Megamorphic(); void GenerateLoadSuperIC(); void GenerateLoadSuperICBaseline(); void GenerateKeyedLoadIC(); void GenerateKeyedLoadIC_Megamorphic(); void GenerateKeyedLoadIC_PolymorphicName(); void GenerateKeyedLoadICTrampoline(); void GenerateKeyedLoadICBaseline(); void GenerateKeyedLoadICTrampoline_Megamorphic(); void GenerateStoreIC(); void GenerateStoreICTrampoline(); void GenerateStoreICBaseline(); void GenerateDefineNamedOwnIC(); void GenerateDefineNamedOwnICTrampoline(); void GenerateDefineNamedOwnICBaseline(); void GenerateStoreGlobalIC(); void GenerateStoreGlobalICTrampoline(); void GenerateStoreGlobalICBaseline(); void GenerateCloneObjectIC(); void GenerateCloneObjectICBaseline(); void GenerateCloneObjectIC_Slow(); void GenerateKeyedHasIC(); void GenerateKeyedHasICBaseline(); void GenerateKeyedHasIC_Megamorphic(); void GenerateKeyedHasIC_PolymorphicName(); void GenerateLoadGlobalIC(TypeofMode typeof_mode); void GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode); void GenerateLoadGlobalICBaseline(TypeofMode typeof_mode); void GenerateLookupGlobalICBaseline(TypeofMode typeof_mode); void GenerateLookupContextBaseline(TypeofMode typeof_mode); void GenerateKeyedStoreIC(); void GenerateKeyedStoreICTrampoline(); void GenerateKeyedStoreICBaseline(); void GenerateDefineKeyedOwnIC(); void GenerateDefineKeyedOwnICTrampoline(); void GenerateDefineKeyedOwnICBaseline(); void GenerateStoreInArrayLiteralIC(); void GenerateStoreInArrayLiteralICBaseline(); void TryProbeStubCache(StubCache* stub_cache, TNode lookup_start_object, TNode name, Label* if_handler, TVariable* var_handler, Label* if_miss); TNode StubCachePrimaryOffsetForTesting(TNode name, TNode map) { return StubCachePrimaryOffset(name, map); } TNode StubCacheSecondaryOffsetForTesting(TNode name, TNode map) { return StubCacheSecondaryOffset(name, map); } struct LoadICParameters { LoadICParameters( TNode context, TNode receiver, TNode name, TNode slot, TNode vector, base::Optional> lookup_start_object = base::nullopt) : context_(context), receiver_(receiver), name_(name), slot_(slot), vector_(vector), lookup_start_object_(lookup_start_object ? lookup_start_object.value() : receiver) {} LoadICParameters(const LoadICParameters* p, TNode unique_name) : context_(p->context_), receiver_(p->receiver_), name_(unique_name), slot_(p->slot_), vector_(p->vector_), lookup_start_object_(p->lookup_start_object_) {} TNode context() const { return context_; } TNode receiver() const { return receiver_; } TNode name() const { return name_; } TNode slot() const { return slot_; } TNode vector() const { return vector_; } TNode lookup_start_object() const { return lookup_start_object_.value(); } // Usable in cases where the receiver and the lookup start object are // expected to be the same, i.e., when "receiver != lookup_start_object" // case is not supported or not expected by the surrounding code. TNode receiver_and_lookup_start_object() const { DCHECK_EQ(receiver_, lookup_start_object_); return receiver_; } private: TNode context_; TNode receiver_; TNode name_; TNode slot_; TNode vector_; base::Optional> lookup_start_object_; }; struct LazyLoadICParameters { LazyLoadICParameters( LazyNode context, TNode receiver, LazyNode name, LazyNode slot, TNode vector, base::Optional> lookup_start_object = base::nullopt) : context_(context), receiver_(receiver), name_(name), slot_(slot), vector_(vector), lookup_start_object_(lookup_start_object ? lookup_start_object.value() : receiver) {} explicit LazyLoadICParameters(const LoadICParameters* p) : receiver_(p->receiver()), vector_(p->vector()), lookup_start_object_(p->lookup_start_object()) { slot_ = [=] { return p->slot(); }; context_ = [=] { return p->context(); }; name_ = [=] { return p->name(); }; } TNode context() const { return context_(); } TNode receiver() const { return receiver_; } TNode name() const { return name_(); } TNode slot() const { return slot_(); } TNode vector() const { return vector_; } TNode lookup_start_object() const { return lookup_start_object_; } // Usable in cases where the receiver and the lookup start object are // expected to be the same, i.e., when "receiver != lookup_start_object" // case is not supported or not expected by the surrounding code. TNode receiver_and_lookup_start_object() const { DCHECK_EQ(receiver_, lookup_start_object_); return receiver_; } private: LazyNode context_; TNode receiver_; LazyNode name_; LazyNode slot_; TNode vector_; TNode lookup_start_object_; }; void LoadGlobalIC(TNode maybe_feedback_vector, const LazyNode& lazy_slot, const LazyNode& lazy_context, const LazyNode& lazy_name, TypeofMode typeof_mode, ExitPoint* exit_point); // Specialized LoadIC for inlined bytecode handler, hand-tuned to omit frame // construction on common paths. void LoadIC_BytecodeHandler(const LazyLoadICParameters* p, ExitPoint* exit_point); // Loads dataX field from the DataHandler object. TNode LoadHandlerDataField(TNode handler, int data_index); protected: enum class StoreICMode { // TODO(v8:12548): rename to kDefineKeyedOwnInLiteral kDefault, kDefineNamedOwn, kDefineKeyedOwn, }; struct StoreICParameters { StoreICParameters(TNode context, base::Optional> receiver, TNode name, TNode value, TNode slot, TNode vector, StoreICMode mode) : context_(context), receiver_(receiver), name_(name), value_(value), slot_(slot), vector_(vector), mode_(mode) {} TNode context() const { return context_; } TNode receiver() const { return receiver_.value(); } TNode name() const { return name_; } TNode value() const { return value_; } TNode slot() const { return slot_; } TNode vector() const { return vector_; } TNode lookup_start_object() const { return receiver(); } bool receiver_is_null() const { return !receiver_.has_value(); } bool IsDefineNamedOwn() const { return mode_ == StoreICMode::kDefineNamedOwn; } bool IsDefineKeyedOwn() const { return mode_ == StoreICMode::kDefineKeyedOwn; } bool IsAnyDefineOwn() const { return IsDefineNamedOwn() || IsDefineKeyedOwn(); } private: TNode context_; base::Optional> receiver_; TNode name_; TNode value_; TNode slot_; TNode vector_; StoreICMode mode_; }; enum class LoadAccessMode { kLoad, kHas }; enum class ICMode { kNonGlobalIC, kGlobalIC }; enum ElementSupport { kOnlyProperties, kSupportElements }; void HandleStoreICHandlerCase( const StoreICParameters* p, TNode handler, Label* miss, ICMode ic_mode, ElementSupport support_elements = kOnlyProperties); enum StoreTransitionMapFlags { kDontCheckPrototypeValidity = 0, kCheckPrototypeValidity = 1 << 0, kValidateTransitionHandler = 1 << 1, kStoreTransitionMapFlagsMask = kCheckPrototypeValidity | kValidateTransitionHandler, }; void HandleStoreICTransitionMapHandlerCase(const StoreICParameters* p, TNode transition_map, Label* miss, StoreTransitionMapFlags flags); void JumpIfDataProperty(TNode details, Label* writable, Label* readonly); void InvalidateValidityCellIfPrototype( TNode map, base::Optional> bitfield3 = base::nullopt); void OverwriteExistingFastDataProperty(TNode object, TNode object_map, TNode descriptors, TNode descriptor_name_index, TNode details, TNode value, Label* slow, bool do_transitioning_store); void StoreJSSharedStructField(TNode context, TNode shared_struct, TNode shared_struct_map, TNode descriptors, TNode descriptor_name_index, TNode details, TNode value); TNode IsPropertyDetailsConst(TNode details); void CheckFieldType(TNode descriptors, TNode name_index, TNode representation, TNode value, Label* bailout); private: // Stub generation entry points. // LoadIC contains the full LoadIC logic, while LoadIC_Noninlined contains // logic not inlined into Ignition bytecode handlers. void LoadIC(const LoadICParameters* p); // Can be used in the receiver != lookup_start_object case. void LoadIC_Noninlined(const LoadICParameters* p, TNode lookup_start_object_map, TNode feedback, TVariable* var_handler, Label* if_handler, Label* miss, ExitPoint* exit_point); void LoadSuperIC(const LoadICParameters* p); TNode LoadDescriptorValue(TNode map, TNode descriptor_entry); TNode LoadDescriptorValueOrFieldType( TNode map, TNode descriptor_entry); void LoadIC_NoFeedback(const LoadICParameters* p, TNode smi_typeof_mode); void LoadSuperIC_NoFeedback(const LoadICParameters* p); void LoadGlobalIC_NoFeedback(TNode context, TNode name, TNode smi_typeof_mode); void KeyedLoadIC(const LoadICParameters* p, LoadAccessMode access_mode); void KeyedLoadICGeneric(const LoadICParameters* p); void KeyedLoadICPolymorphicName(const LoadICParameters* p, LoadAccessMode access_mode); void StoreIC(const StoreICParameters* p); void StoreGlobalIC(const StoreICParameters* p); void StoreGlobalIC_PropertyCellCase(TNode property_cell, TNode value, ExitPoint* exit_point, Label* miss); void KeyedStoreIC(const StoreICParameters* p); void DefineKeyedOwnIC(const StoreICParameters* p); void StoreInArrayLiteralIC(const StoreICParameters* p); // IC dispatcher behavior. // Checks monomorphic case. Returns {feedback} entry of the vector. TNode TryMonomorphicCase(TNode slot, TNode vector, TNode lookup_start_object_map, Label* if_handler, TVariable* var_handler, Label* if_miss); void HandlePolymorphicCase(TNode lookup_start_object_map, TNode feedback, Label* if_handler, TVariable* var_handler, Label* if_miss); void TryMegaDOMCase(TNode lookup_start_object, TNode lookup_start_object_map, TVariable* var_handler, TNode vector, TNode slot, Label* miss, ExitPoint* exit_point); // LoadIC implementation. void HandleLoadICHandlerCase( const LazyLoadICParameters* p, TNode handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode = ICMode::kNonGlobalIC, OnNonExistent on_nonexistent = OnNonExistent::kReturnUndefined, ElementSupport support_elements = kOnlyProperties, LoadAccessMode access_mode = LoadAccessMode::kLoad); void HandleLoadICSmiHandlerCase(const LazyLoadICParameters* p, TNode holder, TNode smi_handler, TNode handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode, OnNonExistent on_nonexistent, ElementSupport support_elements, LoadAccessMode access_mode); void HandleLoadICProtoHandler(const LazyLoadICParameters* p, TNode handler, TVariable* var_holder, TVariable* var_smi_handler, Label* if_smi_handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode, LoadAccessMode access_mode); void HandleLoadCallbackProperty(const LazyLoadICParameters* p, TNode holder, TNode handler_word, ExitPoint* exit_point); void HandleLoadAccessor(const LazyLoadICParameters* p, TNode call_handler_info, TNode handler_word, TNode handler, TNode handler_kind, ExitPoint* exit_point); void HandleLoadField(TNode holder, TNode handler_word, TVariable* var_double_value, Label* rebox_double, Label* miss, ExitPoint* exit_point); #if V8_ENABLE_WEBASSEMBLY void HandleLoadWasmField(TNode holder, TNode wasm_value_type, TNode field_offset, TVariable* var_double_value, Label* rebox_double, ExitPoint* exit_point); void HandleLoadWasmField(TNode holder, TNode handler_word, TVariable* var_double_value, Label* rebox_double, ExitPoint* exit_point); #endif // V8_ENABLE_WEBASSEMBLY void EmitAccessCheck(TNode expected_native_context, TNode context, TNode receiver, Label* can_access, Label* miss); void HandleLoadICSmiHandlerLoadNamedCase( const LazyLoadICParameters* p, TNode holder, TNode handler_kind, TNode handler_word, Label* rebox_double, TVariable* var_double_value, TNode handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode, OnNonExistent on_nonexistent, ElementSupport support_elements); void HandleLoadICSmiHandlerHasNamedCase(const LazyLoadICParameters* p, TNode holder, TNode handler_kind, Label* miss, ExitPoint* exit_point, ICMode ic_mode); // LoadGlobalIC implementation. void LoadGlobalIC_TryPropertyCellCase(TNode vector, TNode slot, const LazyNode& lazy_context, ExitPoint* exit_point, Label* try_handler, Label* miss); void LoadGlobalIC_TryHandlerCase(TNode vector, TNode slot, const LazyNode& lazy_context, const LazyNode& lazy_name, TypeofMode typeof_mode, ExitPoint* exit_point, Label* miss); // This is a copy of ScriptContextTable::Lookup. They should be kept in sync. void ScriptContextTableLookup(TNode name, TNode native_context, Label* found_hole, Label* not_found); // StoreIC implementation. void HandleStoreICProtoHandler(const StoreICParameters* p, TNode handler, Label* miss, ICMode ic_mode, ElementSupport support_elements); void HandleStoreICSmiHandlerCase(TNode handler_word, TNode holder, TNode value, Label* miss); void HandleStoreICSmiHandlerJSSharedStructFieldCase( TNode context, TNode handler_word, TNode holder, TNode value); void HandleStoreFieldAndReturn(TNode handler_word, TNode holder, TNode value, base::Optional> double_value, Representation representation, Label* miss); void CheckPrototypeValidityCell(TNode maybe_validity_cell, Label* miss); void HandleStoreICNativeDataProperty(const StoreICParameters* p, TNode holder, TNode handler_word); void HandleStoreToProxy(const StoreICParameters* p, TNode proxy, Label* miss, ElementSupport support_elements); void HandleStoreAccessor(const StoreICParameters* p, TNode holder, TNode handler_word); // KeyedLoadIC_Generic implementation. void GenericElementLoad(TNode lookup_start_object, TNode lookup_start_object_map, TNode lookup_start_object_instance_type, TNode index, Label* slow); enum UseStubCache { kUseStubCache, kDontUseStubCache }; void GenericPropertyLoad(TNode lookup_start_object, TNode lookup_start_object_map, TNode lookup_start_object_instance_type, const LoadICParameters* p, Label* slow, UseStubCache use_stub_cache = kUseStubCache); // Low-level helpers. using OnCodeHandler = std::function code_handler)>; using OnFoundOnLookupStartObject = std::function properties, TNode name_index)>; template TNode HandleProtoHandler( const ICParameters* p, TNode handler, const OnCodeHandler& on_code_handler, const OnFoundOnLookupStartObject& on_found_on_lookup_start_object, Label* miss, ICMode ic_mode); void CheckHeapObjectTypeMatchesDescriptor(TNode handler_word, TNode holder, TNode value, Label* bailout); // Double fields store double values in a mutable box, where stores are // writes into this box rather than HeapNumber assignment. void CheckDescriptorConsidersNumbersMutable(TNode handler_word, TNode holder, Label* bailout); // Extends properties backing store by JSObject::kFieldsAdded elements, // returns updated properties backing store. TNode ExtendPropertiesBackingStore(TNode object, TNode index); void EmitFastElementsBoundsCheck(TNode object, TNode elements, TNode intptr_index, TNode is_jsarray_condition, Label* miss); void EmitElementLoad(TNode object, TNode elements_kind, TNode key, TNode is_jsarray_condition, Label* if_hole, Label* rebox_double, TVariable* var_double_value, Label* unimplemented_elements_kind, Label* out_of_bounds, Label* miss, ExitPoint* exit_point, LoadAccessMode access_mode = LoadAccessMode::kLoad); // Stub cache access helpers. // This enum is used here as a replacement for StubCache::Table to avoid // including stub cache header. enum StubCacheTable : int; TNode StubCachePrimaryOffset(TNode name, TNode map); TNode StubCacheSecondaryOffset(TNode name, TNode map); void TryProbeStubCacheTable(StubCache* stub_cache, StubCacheTable table_id, TNode entry_offset, TNode name, TNode map, Label* if_handler, TVariable* var_handler, Label* if_miss); void BranchIfPrototypesHaveNoElements(TNode receiver_map, Label* definitely_no_elements, Label* possibly_elements); }; // Abstraction over direct and indirect exit points. Direct exits correspond to // tailcalls and Return, while indirect exits store the result in a variable // and then jump to an exit label. class ExitPoint { private: using CodeAssemblerLabel = compiler::CodeAssemblerLabel; public: using IndirectReturnHandler = std::function result)>; explicit ExitPoint(CodeStubAssembler* assembler) : ExitPoint(assembler, nullptr) {} ExitPoint(CodeStubAssembler* assembler, const IndirectReturnHandler& indirect_return_handler) : asm_(assembler), indirect_return_handler_(indirect_return_handler) {} ExitPoint(CodeStubAssembler* assembler, CodeAssemblerLabel* out, compiler::CodeAssembler::TVariable* var_result) : ExitPoint(assembler, [=](TNode result) { *var_result = result; assembler->Goto(out); }) { DCHECK_EQ(out != nullptr, var_result != nullptr); } template void ReturnCallRuntime(Runtime::FunctionId function, TNode context, TArgs... args) { if (IsDirect()) { asm_->TailCallRuntime(function, context, args...); } else { indirect_return_handler_(asm_->CallRuntime(function, context, args...)); } } template void ReturnCallStub(Callable const& callable, TNode context, TArgs... args) { if (IsDirect()) { asm_->TailCallStub(callable, context, args...); } else { indirect_return_handler_(asm_->CallStub(callable, context, args...)); } } template void ReturnCallStub(const CallInterfaceDescriptor& descriptor, TNode target, TNode context, TArgs... args) { if (IsDirect()) { asm_->TailCallStub(descriptor, target, context, args...); } else { indirect_return_handler_( asm_->CallStub(descriptor, target, context, args...)); } } void Return(const TNode result) { if (IsDirect()) { asm_->Return(result); } else { indirect_return_handler_(result); } } bool IsDirect() const { return !indirect_return_handler_; } private: CodeStubAssembler* const asm_; IndirectReturnHandler indirect_return_handler_; }; } // namespace internal } // namespace v8 #endif // V8_IC_ACCESSOR_ASSEMBLER_H_