| // 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: |
| using Node = compiler::Node; |
| |
| explicit AccessorAssembler(compiler::CodeAssemblerState* state) |
| : CodeStubAssembler(state) {} |
| |
| void GenerateLoadIC(); |
| void GenerateLoadIC_Megamorphic(); |
| void GenerateLoadIC_Noninlined(); |
| void GenerateLoadIC_NoFeedback(); |
| void GenerateLoadGlobalIC_NoFeedback(); |
| void GenerateLoadICTrampoline(); |
| void GenerateLoadICTrampoline_Megamorphic(); |
| void GenerateLoadSuperIC(); |
| void GenerateKeyedLoadIC(); |
| void GenerateKeyedLoadIC_Megamorphic(); |
| void GenerateKeyedLoadIC_PolymorphicName(); |
| void GenerateKeyedLoadICTrampoline(); |
| void GenerateKeyedLoadICTrampoline_Megamorphic(); |
| void GenerateStoreIC(); |
| void GenerateStoreICTrampoline(); |
| void GenerateStoreGlobalIC(); |
| void GenerateStoreGlobalICTrampoline(); |
| void GenerateCloneObjectIC(); |
| void GenerateCloneObjectIC_Slow(); |
| void GenerateKeyedHasIC(); |
| void GenerateKeyedHasIC_Megamorphic(); |
| void GenerateKeyedHasIC_PolymorphicName(); |
| |
| void GenerateLoadGlobalIC(TypeofMode typeof_mode); |
| void GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode); |
| |
| void GenerateKeyedStoreIC(); |
| void GenerateKeyedStoreICTrampoline(); |
| |
| void GenerateStoreInArrayLiteralIC(); |
| |
| void TryProbeStubCache(StubCache* stub_cache, |
| TNode<Object> lookup_start_object, TNode<Name> name, |
| Label* if_handler, TVariable<MaybeObject>* var_handler, |
| Label* if_miss); |
| |
| TNode<IntPtrT> StubCachePrimaryOffsetForTesting(TNode<Name> name, |
| TNode<Map> map) { |
| return StubCachePrimaryOffset(name, map); |
| } |
| TNode<IntPtrT> StubCacheSecondaryOffsetForTesting(TNode<Name> name, |
| TNode<IntPtrT> seed) { |
| return StubCacheSecondaryOffset(name, seed); |
| } |
| |
| struct LoadICParameters { |
| LoadICParameters( |
| TNode<Context> context, TNode<Object> receiver, TNode<Object> name, |
| TNode<TaggedIndex> slot, TNode<HeapObject> vector, |
| base::Optional<TNode<Object>> 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<Object> 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> context() const { return context_; } |
| TNode<Object> receiver() const { return receiver_; } |
| TNode<Object> name() const { return name_; } |
| TNode<TaggedIndex> slot() const { return slot_; } |
| TNode<HeapObject> vector() const { return vector_; } |
| TNode<Object> 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<Object> receiver_and_lookup_start_object() const { |
| DCHECK_EQ(receiver_, lookup_start_object_); |
| return receiver_; |
| } |
| |
| private: |
| TNode<Context> context_; |
| TNode<Object> receiver_; |
| TNode<Object> name_; |
| TNode<TaggedIndex> slot_; |
| TNode<HeapObject> vector_; |
| base::Optional<TNode<Object>> lookup_start_object_; |
| }; |
| |
| struct LazyLoadICParameters { |
| LazyLoadICParameters( |
| LazyNode<Context> context, TNode<Object> receiver, |
| LazyNode<Object> name, LazyNode<TaggedIndex> slot, |
| TNode<HeapObject> vector, |
| base::Optional<TNode<Object>> 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> context() const { return context_(); } |
| TNode<Object> receiver() const { return receiver_; } |
| TNode<Object> name() const { return name_(); } |
| TNode<TaggedIndex> slot() const { return slot_(); } |
| TNode<HeapObject> vector() const { return vector_; } |
| TNode<Object> 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<Object> receiver_and_lookup_start_object() const { |
| DCHECK_EQ(receiver_, lookup_start_object_); |
| return receiver_; |
| } |
| |
| private: |
| LazyNode<Context> context_; |
| TNode<Object> receiver_; |
| LazyNode<Object> name_; |
| LazyNode<TaggedIndex> slot_; |
| TNode<HeapObject> vector_; |
| TNode<Object> lookup_start_object_; |
| }; |
| |
| void LoadGlobalIC(TNode<HeapObject> maybe_feedback_vector, |
| const LazyNode<TaggedIndex>& lazy_slot, |
| const LazyNode<Context>& lazy_context, |
| const LazyNode<Name>& 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<MaybeObject> LoadHandlerDataField(TNode<DataHandler> handler, |
| int data_index); |
| |
| protected: |
| struct StoreICParameters { |
| StoreICParameters(TNode<Context> context, |
| base::Optional<TNode<Object>> receiver, |
| TNode<Object> name, TNode<Object> value, |
| TNode<TaggedIndex> slot, TNode<HeapObject> vector) |
| : context_(context), |
| receiver_(receiver), |
| name_(name), |
| value_(value), |
| slot_(slot), |
| vector_(vector) {} |
| |
| TNode<Context> context() const { return context_; } |
| TNode<Object> receiver() const { return receiver_.value(); } |
| TNode<Object> name() const { return name_; } |
| TNode<Object> value() const { return value_; } |
| TNode<TaggedIndex> slot() const { return slot_; } |
| TNode<HeapObject> vector() const { return vector_; } |
| |
| TNode<Object> lookup_start_object() const { return receiver(); } |
| |
| bool receiver_is_null() const { return !receiver_.has_value(); } |
| |
| private: |
| TNode<Context> context_; |
| base::Optional<TNode<Object>> receiver_; |
| TNode<Object> name_; |
| TNode<Object> value_; |
| TNode<TaggedIndex> slot_; |
| TNode<HeapObject> vector_; |
| }; |
| |
| enum class LoadAccessMode { kLoad, kHas }; |
| enum class ICMode { kNonGlobalIC, kGlobalIC }; |
| enum ElementSupport { kOnlyProperties, kSupportElements }; |
| void HandleStoreICHandlerCase( |
| const StoreICParameters* p, TNode<MaybeObject> handler, Label* miss, |
| ICMode ic_mode, ElementSupport support_elements = kOnlyProperties); |
| enum StoreTransitionMapFlags { |
| kCheckPrototypeValidity = 1 << 0, |
| kValidateTransitionHandler = 1 << 1, |
| kStoreTransitionMapFlagsMask = |
| kCheckPrototypeValidity | kValidateTransitionHandler, |
| }; |
| void HandleStoreICTransitionMapHandlerCase(const StoreICParameters* p, |
| TNode<Map> transition_map, |
| Label* miss, |
| StoreTransitionMapFlags flags); |
| |
| void JumpIfDataProperty(TNode<Uint32T> details, Label* writable, |
| Label* readonly); |
| |
| void InvalidateValidityCellIfPrototype( |
| TNode<Map> map, base::Optional<TNode<Uint32T>> bitfield3 = base::nullopt); |
| |
| void OverwriteExistingFastDataProperty(TNode<HeapObject> object, |
| TNode<Map> object_map, |
| TNode<DescriptorArray> descriptors, |
| TNode<IntPtrT> descriptor_name_index, |
| TNode<Uint32T> details, |
| TNode<Object> value, Label* slow, |
| bool do_transitioning_store); |
| |
| void CheckFieldType(TNode<DescriptorArray> descriptors, |
| TNode<IntPtrT> name_index, TNode<Word32T> representation, |
| TNode<Object> 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<Map> lookup_start_object_map, |
| TNode<HeapObject> feedback, |
| TVariable<MaybeObject>* var_handler, Label* if_handler, |
| Label* miss, ExitPoint* exit_point); |
| |
| void LoadSuperIC(const LoadICParameters* p); |
| |
| TNode<Object> LoadDescriptorValue(TNode<Map> map, |
| TNode<IntPtrT> descriptor_entry); |
| TNode<MaybeObject> LoadDescriptorValueOrFieldType( |
| TNode<Map> map, TNode<IntPtrT> descriptor_entry); |
| |
| void LoadIC_NoFeedback(const LoadICParameters* p, TNode<Smi> smi_typeof_mode); |
| void LoadSuperIC_NoFeedback(const LoadICParameters* p); |
| void LoadGlobalIC_NoFeedback(TNode<Context> context, TNode<Object> name, |
| TNode<Smi> 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<PropertyCell> property_cell, |
| TNode<Object> value, |
| ExitPoint* exit_point, Label* miss); |
| void KeyedStoreIC(const StoreICParameters* p); |
| void StoreInArrayLiteralIC(const StoreICParameters* p); |
| |
| // IC dispatcher behavior. |
| |
| // Checks monomorphic case. Returns {feedback} entry of the vector. |
| TNode<MaybeObject> TryMonomorphicCase(TNode<TaggedIndex> slot, |
| TNode<FeedbackVector> vector, |
| TNode<Map> lookup_start_object_map, |
| Label* if_handler, |
| TVariable<MaybeObject>* var_handler, |
| Label* if_miss); |
| void HandlePolymorphicCase(TNode<Map> lookup_start_object_map, |
| TNode<WeakFixedArray> feedback, Label* if_handler, |
| TVariable<MaybeObject>* var_handler, |
| Label* if_miss); |
| |
| // LoadIC implementation. |
| void HandleLoadICHandlerCase( |
| const LazyLoadICParameters* p, TNode<Object> 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<Object> holder, TNode<Smi> smi_handler, |
| TNode<Object> handler, Label* miss, |
| ExitPoint* exit_point, ICMode ic_mode, |
| OnNonExistent on_nonexistent, |
| ElementSupport support_elements, |
| LoadAccessMode access_mode); |
| |
| void HandleLoadICProtoHandler(const LazyLoadICParameters* p, |
| TNode<DataHandler> handler, |
| TVariable<Object>* var_holder, |
| TVariable<Object>* var_smi_handler, |
| Label* if_smi_handler, Label* miss, |
| ExitPoint* exit_point, ICMode ic_mode, |
| LoadAccessMode access_mode); |
| |
| void HandleLoadCallbackProperty(const LazyLoadICParameters* p, |
| TNode<JSObject> holder, |
| TNode<WordT> handler_word, |
| ExitPoint* exit_point); |
| |
| void HandleLoadAccessor(const LazyLoadICParameters* p, |
| TNode<CallHandlerInfo> call_handler_info, |
| TNode<WordT> handler_word, TNode<DataHandler> handler, |
| TNode<IntPtrT> handler_kind, ExitPoint* exit_point); |
| |
| void HandleLoadField(TNode<JSObject> holder, TNode<WordT> handler_word, |
| TVariable<Float64T>* var_double_value, |
| Label* rebox_double, Label* miss, ExitPoint* exit_point); |
| |
| void EmitAccessCheck(TNode<Context> expected_native_context, |
| TNode<Context> context, TNode<Object> receiver, |
| Label* can_access, Label* miss); |
| |
| void HandleLoadICSmiHandlerLoadNamedCase( |
| const LazyLoadICParameters* p, TNode<Object> holder, |
| TNode<IntPtrT> handler_kind, TNode<WordT> handler_word, |
| Label* rebox_double, TVariable<Float64T>* var_double_value, |
| TNode<Object> handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode, |
| OnNonExistent on_nonexistent, ElementSupport support_elements); |
| |
| void HandleLoadICSmiHandlerHasNamedCase(const LazyLoadICParameters* p, |
| TNode<Object> holder, |
| TNode<IntPtrT> handler_kind, |
| Label* miss, ExitPoint* exit_point, |
| ICMode ic_mode); |
| |
| // LoadGlobalIC implementation. |
| |
| void LoadGlobalIC_TryPropertyCellCase(TNode<FeedbackVector> vector, |
| TNode<TaggedIndex> slot, |
| const LazyNode<Context>& lazy_context, |
| ExitPoint* exit_point, |
| Label* try_handler, Label* miss); |
| |
| void LoadGlobalIC_TryHandlerCase(TNode<FeedbackVector> vector, |
| TNode<TaggedIndex> slot, |
| const LazyNode<Context>& lazy_context, |
| const LazyNode<Name>& 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> name, |
| TNode<NativeContext> native_context, |
| Label* found_hole, Label* not_found); |
| |
| // StoreIC implementation. |
| |
| void HandleStoreICProtoHandler(const StoreICParameters* p, |
| TNode<StoreHandler> handler, Label* miss, |
| ICMode ic_mode, |
| ElementSupport support_elements); |
| void HandleStoreICSmiHandlerCase(TNode<Word32T> handler_word, |
| TNode<JSObject> holder, TNode<Object> value, |
| Label* miss); |
| void HandleStoreFieldAndReturn(TNode<Word32T> handler_word, |
| TNode<JSObject> holder, TNode<Object> value, |
| base::Optional<TNode<Float64T>> double_value, |
| Representation representation, Label* miss); |
| |
| void CheckPrototypeValidityCell(TNode<Object> maybe_validity_cell, |
| Label* miss); |
| void HandleStoreICNativeDataProperty(const StoreICParameters* p, |
| TNode<HeapObject> holder, |
| TNode<Word32T> handler_word); |
| |
| void HandleStoreToProxy(const StoreICParameters* p, TNode<JSProxy> proxy, |
| Label* miss, ElementSupport support_elements); |
| |
| void HandleStoreAccessor(const StoreICParameters* p, TNode<HeapObject> holder, |
| TNode<Word32T> handler_word); |
| |
| // KeyedLoadIC_Generic implementation. |
| |
| void GenericElementLoad(TNode<HeapObject> lookup_start_object, |
| TNode<Map> lookup_start_object_map, |
| TNode<Int32T> lookup_start_object_instance_type, |
| TNode<IntPtrT> index, Label* slow); |
| |
| enum UseStubCache { kUseStubCache, kDontUseStubCache }; |
| void GenericPropertyLoad(TNode<HeapObject> lookup_start_object, |
| TNode<Map> lookup_start_object_map, |
| TNode<Int32T> lookup_start_object_instance_type, |
| const LoadICParameters* p, Label* slow, |
| UseStubCache use_stub_cache = kUseStubCache); |
| |
| // Low-level helpers. |
| |
| using OnCodeHandler = std::function<void(TNode<Code> code_handler)>; |
| using OnFoundOnLookupStartObject = std::function<void( |
| TNode<NameDictionary> properties, TNode<IntPtrT> name_index)>; |
| |
| template <typename ICHandler, typename ICParameters> |
| TNode<Object> HandleProtoHandler( |
| const ICParameters* p, TNode<DataHandler> handler, |
| const OnCodeHandler& on_code_handler, |
| const OnFoundOnLookupStartObject& on_found_on_lookup_start_object, |
| Label* miss, ICMode ic_mode); |
| |
| void CheckHeapObjectTypeMatchesDescriptor(TNode<Word32T> handler_word, |
| TNode<JSObject> holder, |
| TNode<Object> 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<Word32T> handler_word, |
| TNode<JSObject> holder, |
| Label* bailout); |
| |
| // Extends properties backing store by JSObject::kFieldsAdded elements, |
| // returns updated properties backing store. |
| TNode<PropertyArray> ExtendPropertiesBackingStore(TNode<HeapObject> object, |
| TNode<IntPtrT> index); |
| |
| void EmitFastElementsBoundsCheck(TNode<JSObject> object, |
| TNode<FixedArrayBase> elements, |
| TNode<IntPtrT> intptr_index, |
| TNode<BoolT> is_jsarray_condition, |
| Label* miss); |
| void EmitElementLoad(TNode<HeapObject> object, TNode<Word32T> elements_kind, |
| TNode<IntPtrT> key, TNode<BoolT> is_jsarray_condition, |
| Label* if_hole, Label* rebox_double, |
| TVariable<Float64T>* var_double_value, |
| Label* unimplemented_elements_kind, Label* out_of_bounds, |
| Label* miss, ExitPoint* exit_point, |
| LoadAccessMode access_mode = LoadAccessMode::kLoad); |
| TNode<BoolT> IsPropertyDetailsConst(TNode<Uint32T> details); |
| |
| // 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<IntPtrT> StubCachePrimaryOffset(TNode<Name> name, TNode<Map> map); |
| TNode<IntPtrT> StubCacheSecondaryOffset(TNode<Name> name, |
| TNode<IntPtrT> seed); |
| |
| void TryProbeStubCacheTable(StubCache* stub_cache, StubCacheTable table_id, |
| TNode<IntPtrT> entry_offset, TNode<Object> name, |
| TNode<Map> map, Label* if_handler, |
| TVariable<MaybeObject>* var_handler, |
| Label* if_miss); |
| |
| void BranchIfPrototypesHaveNoElements(TNode<Map> 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<void(TNode<Object> 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<Object>* var_result) |
| : ExitPoint(assembler, [=](TNode<Object> result) { |
| *var_result = result; |
| assembler->Goto(out); |
| }) { |
| DCHECK_EQ(out != nullptr, var_result != nullptr); |
| } |
| |
| template <class... TArgs> |
| void ReturnCallRuntime(Runtime::FunctionId function, TNode<Context> context, |
| TArgs... args) { |
| if (IsDirect()) { |
| asm_->TailCallRuntime(function, context, args...); |
| } else { |
| indirect_return_handler_(asm_->CallRuntime(function, context, args...)); |
| } |
| } |
| |
| template <class... TArgs> |
| void ReturnCallStub(Callable const& callable, TNode<Context> context, |
| TArgs... args) { |
| if (IsDirect()) { |
| asm_->TailCallStub(callable, context, args...); |
| } else { |
| indirect_return_handler_(asm_->CallStub(callable, context, args...)); |
| } |
| } |
| |
| template <class... TArgs> |
| void ReturnCallStub(const CallInterfaceDescriptor& descriptor, |
| TNode<Code> target, TNode<Context> 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<Object> 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_ |