// 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/code-stub-assembler.h" namespace v8 { namespace internal { namespace compiler { class CodeAssemblerState; } class ExitPoint; class AccessorAssembler : public CodeStubAssembler { public: using Node = compiler::Node; template <class T> using TNode = compiler::TNode<T>; template <class T> using SloppyTNode = compiler::SloppyTNode<T>; explicit AccessorAssembler(compiler::CodeAssemblerState* state) : CodeStubAssembler(state) {} void GenerateLoadIC(); void GenerateLoadIC_Megamorphic(); void GenerateLoadIC_Noninlined(); void GenerateLoadIC_Uninitialized(); void GenerateLoadICTrampoline(); void GenerateLoadICTrampoline_Megamorphic(); 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 GenerateLoadGlobalIC(TypeofMode typeof_mode); void GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode); void GenerateKeyedStoreIC(); void GenerateKeyedStoreICTrampoline(); void GenerateStoreInArrayLiteralIC(); void TryProbeStubCache(StubCache* stub_cache, Node* receiver, Node* name, Label* if_handler, TVariable<MaybeObject>* var_handler, Label* if_miss); Node* StubCachePrimaryOffsetForTesting(Node* name, Node* map) { return StubCachePrimaryOffset(name, map); } Node* StubCacheSecondaryOffsetForTesting(Node* name, Node* map) { return StubCacheSecondaryOffset(name, map); } struct LoadICParameters { LoadICParameters(Node* context, Node* receiver, Node* name, Node* slot, Node* vector, Node* holder = nullptr) : context(context), receiver(receiver), name(name), slot(slot), vector(vector), holder(holder ? holder : receiver) {} Node* context; Node* receiver; Node* name; Node* slot; Node* vector; Node* holder; }; void LoadGlobalIC(TNode<FeedbackVector> vector, Node* slot, const LazyNode<Context>& lazy_context, const LazyNode<Name>& lazy_name, TypeofMode typeof_mode, ExitPoint* exit_point, ParameterMode slot_mode = SMI_PARAMETERS); // Specialized LoadIC for inlined bytecode handler, hand-tuned to omit frame // construction on common paths. void LoadIC_BytecodeHandler(const LoadICParameters* p, ExitPoint* exit_point); // Loads dataX field from the DataHandler object. TNode<MaybeObject> LoadHandlerDataField(SloppyTNode<DataHandler> handler, int data_index); protected: struct StoreICParameters : public LoadICParameters { StoreICParameters(Node* context, Node* receiver, Node* name, SloppyTNode<Object> value, Node* slot, Node* vector) : LoadICParameters(context, receiver, name, slot, vector), value(value) {} SloppyTNode<Object> value; }; 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(Node* details, Label* writable, Label* readonly); void InvalidateValidityCellIfPrototype(Node* map, Node* bitfield2 = nullptr); void OverwriteExistingFastDataProperty(Node* object, Node* object_map, Node* descriptors, Node* descriptor_name_index, Node* details, Node* value, Label* slow, bool do_transitioning_store); void CheckFieldType(TNode<DescriptorArray> descriptors, Node* name_index, Node* representation, Node* 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); void LoadIC_Noninlined(const LoadICParameters* p, Node* receiver_map, TNode<HeapObject> feedback, TVariable<MaybeObject>* var_handler, Label* if_handler, Label* miss, ExitPoint* exit_point); TNode<Object> LoadDescriptorValue(TNode<Map> map, TNode<IntPtrT> descriptor_entry); TNode<MaybeObject> LoadDescriptorValueOrFieldType( TNode<Map> map, TNode<IntPtrT> descriptor_entry); void LoadIC_Uninitialized(const LoadICParameters* p); void KeyedLoadIC(const LoadICParameters* p); void KeyedLoadICGeneric(const LoadICParameters* p); void KeyedLoadICPolymorphicName(const LoadICParameters* p); void StoreIC(const StoreICParameters* p); void StoreGlobalIC(const StoreICParameters* p); void StoreGlobalIC_PropertyCellCase(Node* property_cell, Node* 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(Node* slot, Node* vector, Node* receiver_map, Label* if_handler, TVariable<MaybeObject>* var_handler, Label* if_miss); void HandlePolymorphicCase(Node* receiver_map, TNode<WeakFixedArray> feedback, Label* if_handler, TVariable<MaybeObject>* var_handler, Label* if_miss); // LoadIC implementation. void HandleLoadICHandlerCase( const LoadICParameters* p, TNode<Object> handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode = ICMode::kNonGlobalIC, OnNonExistent on_nonexistent = OnNonExistent::kReturnUndefined, ElementSupport support_elements = kOnlyProperties); void HandleLoadICSmiHandlerCase(const LoadICParameters* p, Node* holder, SloppyTNode<Smi> smi_handler, SloppyTNode<Object> handler, Label* miss, ExitPoint* exit_point, OnNonExistent on_nonexistent, ElementSupport support_elements); void HandleLoadICProtoHandler(const LoadICParameters* p, Node* handler, Variable* var_holder, Variable* var_smi_handler, Label* if_smi_handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode); void HandleLoadCallbackProperty(const LoadICParameters* p, TNode<JSObject> holder, TNode<WordT> handler_word, ExitPoint* exit_point); void HandleLoadAccessor(const LoadICParameters* p, TNode<CallHandlerInfo> call_handler_info, TNode<WordT> handler_word, TNode<DataHandler> handler, TNode<IntPtrT> handler_kind, ExitPoint* exit_point); void HandleLoadField(Node* holder, Node* handler_word, Variable* var_double_value, Label* rebox_double, ExitPoint* exit_point); void EmitAccessCheck(Node* expected_native_context, Node* context, Node* receiver, Label* can_access, Label* miss); // LoadGlobalIC implementation. void LoadGlobalIC_TryPropertyCellCase( TNode<FeedbackVector> vector, Node* slot, const LazyNode<Context>& lazy_context, ExitPoint* exit_point, Label* try_handler, Label* miss, ParameterMode slot_mode = SMI_PARAMETERS); void LoadGlobalIC_TryHandlerCase(TNode<FeedbackVector> vector, Node* slot, const LazyNode<Context>& lazy_context, const LazyNode<Name>& lazy_name, TypeofMode typeof_mode, ExitPoint* exit_point, Label* miss, ParameterMode slot_mode); // StoreIC implementation. void HandleStoreICProtoHandler(const StoreICParameters* p, TNode<StoreHandler> handler, Label* miss, ICMode ic_mode, ElementSupport support_elements); void HandleStoreICSmiHandlerCase(Node* handler_word, Node* holder, Node* value, Label* miss); void HandleStoreFieldAndReturn(Node* handler_word, Node* holder, Representation representation, Node* value, Label* miss); void CheckPrototypeValidityCell(Node* maybe_validity_cell, Label* miss); void HandleStoreICNativeDataProperty(const StoreICParameters* p, Node* holder, Node* handler_word); void HandleStoreToProxy(const StoreICParameters* p, Node* proxy, Label* miss, ElementSupport support_elements); void HandleStoreAccessor(const StoreICParameters* p, Node* holder, Node* handler_word); // KeyedLoadIC_Generic implementation. void GenericElementLoad(Node* receiver, Node* receiver_map, SloppyTNode<Int32T> instance_type, Node* index, Label* slow); enum UseStubCache { kUseStubCache, kDontUseStubCache }; void GenericPropertyLoad(Node* receiver, Node* receiver_map, SloppyTNode<Int32T> instance_type, const LoadICParameters* p, Label* slow, UseStubCache use_stub_cache = kUseStubCache); // Low-level helpers. typedef std::function<void(Node* code_handler)> OnCodeHandler; typedef std::function<void(Node* properties, Node* name_index)> OnFoundOnReceiver; template <typename ICHandler, typename ICParameters> Node* HandleProtoHandler(const ICParameters* p, Node* handler, const OnCodeHandler& on_code_handler, const OnFoundOnReceiver& on_found_on_receiver, Label* miss, ICMode ic_mode); Node* PrepareValueForStore(Node* handler_word, Node* holder, Representation representation, Node* value, Label* bailout); // Extends properties backing store by JSObject::kFieldsAdded elements, // returns updated properties backing store. Node* ExtendPropertiesBackingStore(Node* object, Node* index); void StoreNamedField(Node* handler_word, Node* object, bool is_inobject, Representation representation, Node* value, Label* bailout); void EmitFastElementsBoundsCheck(Node* object, Node* elements, Node* intptr_index, Node* is_jsarray_condition, Label* miss); void EmitElementLoad(Node* object, Node* elements, Node* elements_kind, SloppyTNode<IntPtrT> key, Node* is_jsarray_condition, Label* if_hole, Label* rebox_double, Variable* var_double_value, Label* unimplemented_elements_kind, Label* out_of_bounds, Label* miss, ExitPoint* exit_point); void NameDictionaryNegativeLookup(Node* object, SloppyTNode<Name> name, Label* miss); TNode<BoolT> IsPropertyDetailsConst(Node* 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; Node* StubCachePrimaryOffset(Node* name, Node* map); Node* StubCacheSecondaryOffset(Node* name, Node* seed); void TryProbeStubCacheTable(StubCache* stub_cache, StubCacheTable table_id, Node* entry_offset, Node* name, Node* map, Label* if_handler, TVariable<MaybeObject>* var_handler, Label* if_miss); }; // 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: typedef compiler::Node Node; typedef compiler::CodeAssemblerLabel CodeAssemblerLabel; typedef compiler::CodeAssemblerVariable CodeAssemblerVariable; public: typedef std::function<void(Node* result)> IndirectReturnHandler; 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, CodeAssemblerVariable* var_result) : ExitPoint(assembler, [=](Node* result) { var_result->Bind(result); assembler->Goto(out); }) { DCHECK_EQ(out != nullptr, var_result != nullptr); } template <class... TArgs> void ReturnCallRuntime(Runtime::FunctionId function, Node* 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, Node* 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, Node* target, Node* context, TArgs... args) { if (IsDirect()) { asm_->TailCallStub(descriptor, target, context, args...); } else { indirect_return_handler_( asm_->CallStub(descriptor, target, context, args...)); } } void Return(Node* const 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_