// 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_SRC_IC_ACCESSOR_ASSEMBLER_H_ #define V8_SRC_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: typedef compiler::Node Node; explicit AccessorAssembler(compiler::CodeAssemblerState* state) : CodeStubAssembler(state) {} void GenerateLoadIC(); void GenerateLoadIC_Noninlined(); void GenerateLoadIC_Uninitialized(); void GenerateLoadField(); void GenerateLoadICTrampoline(); void GenerateKeyedLoadIC(); void GenerateKeyedLoadICTrampoline(); void GenerateKeyedLoadIC_Megamorphic(); void GenerateStoreIC(LanguageMode language_mode); void GenerateStoreICTrampoline(LanguageMode language_mode); void GenerateLoadICProtoArray(bool throw_reference_error_if_nonexistent); void GenerateLoadGlobalIC(TypeofMode typeof_mode); void GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode); void GenerateKeyedStoreIC(LanguageMode language_mode); void GenerateKeyedStoreICTrampoline(LanguageMode language_mode); void TryProbeStubCache(StubCache* stub_cache, Node* receiver, Node* name, Label* if_handler, Variable* 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) : context(context), receiver(receiver), name(name), slot(slot), vector(vector) {} Node* context; Node* receiver; Node* name; Node* slot; Node* vector; }; void LoadGlobalIC_TryPropertyCellCase( Node* vector, Node* slot, ExitPoint* exit_point, Label* try_handler, Label* miss, ParameterMode slot_mode = SMI_PARAMETERS); void LoadGlobalIC_TryHandlerCase(const LoadICParameters* p, TypeofMode typeof_mode, ExitPoint* exit_point, Label* miss); void LoadGlobalIC_MissCase(const LoadICParameters* p, ExitPoint* exit_point); // Specialized LoadIC for inlined bytecode handler, hand-tuned to omit frame // construction on common paths. void LoadIC_BytecodeHandler(const LoadICParameters* p, ExitPoint* exit_point); protected: struct StoreICParameters : public LoadICParameters { StoreICParameters(Node* context, Node* receiver, Node* name, Node* value, Node* slot, Node* vector) : LoadICParameters(context, receiver, name, slot, vector), value(value) {} Node* value; }; enum ElementSupport { kOnlyProperties, kSupportElements }; void HandleStoreICHandlerCase( const StoreICParameters* p, Node* handler, Label* miss, ElementSupport support_elements = kOnlyProperties); void JumpIfDataProperty(Node* details, Label* writable, Label* readonly); 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, Node* feedback, Variable* var_handler, Label* if_handler, Label* miss, ExitPoint* exit_point); void LoadIC_Uninitialized(const LoadICParameters* p); void LoadICProtoArray(const LoadICParameters* p, Node* handler, bool throw_reference_error_if_nonexistent); void LoadGlobalIC(const LoadICParameters* p, TypeofMode typeof_mode); void KeyedLoadIC(const LoadICParameters* p); void KeyedLoadICGeneric(const LoadICParameters* p); void StoreIC(const StoreICParameters* p, LanguageMode language_mode); void KeyedStoreIC(const StoreICParameters* p, LanguageMode language_mode); // IC dispatcher behavior. // Checks monomorphic case. Returns {feedback} entry of the vector. Node* TryMonomorphicCase(Node* slot, Node* vector, Node* receiver_map, Label* if_handler, Variable* var_handler, Label* if_miss); void HandlePolymorphicCase(Node* receiver_map, Node* feedback, Label* if_handler, Variable* var_handler, Label* if_miss, int min_feedback_capacity); // LoadIC implementation. void HandleLoadICHandlerCase( const LoadICParameters* p, Node* handler, Label* miss, ExitPoint* exit_point, ElementSupport support_elements = kOnlyProperties); void HandleLoadICSmiHandlerCase(const LoadICParameters* p, Node* holder, Node* smi_handler, Label* miss, ExitPoint* exit_point, bool throw_reference_error_if_nonexistent, ElementSupport support_elements); void HandleLoadICProtoHandlerCase(const LoadICParameters* p, Node* handler, Variable* var_holder, Variable* var_smi_handler, Label* if_smi_handler, Label* miss, ExitPoint* exit_point, bool throw_reference_error_if_nonexistent); void HandleLoadField(Node* holder, Node* handler_word, Variable* var_double_value, Label* rebox_double, ExitPoint* exit_point); Node* EmitLoadICProtoArrayCheck(const LoadICParameters* p, Node* handler, Node* handler_length, Node* handler_flags, Label* miss); // LoadGlobalIC implementation. void HandleLoadGlobalICHandlerCase(const LoadICParameters* p, Node* handler, Label* miss, ExitPoint* exit_point, bool throw_reference_error_if_nonexistent); // StoreIC implementation. void HandleStoreICElementHandlerCase(const StoreICParameters* p, Node* handler, Label* miss); void HandleStoreICProtoHandler(const StoreICParameters* p, Node* handler, Label* miss, ElementSupport support_elements); // If |transition| is nullptr then the normal field store is generated or // transitioning store otherwise. void HandleStoreICSmiHandlerCase(Node* handler_word, Node* holder, Node* value, Node* transition, Label* miss); // If |transition| is nullptr then the normal field store is generated or // transitioning store otherwise. void HandleStoreFieldAndReturn(Node* handler_word, Node* holder, Representation representation, Node* value, Node* transition, Label* miss); // KeyedLoadIC_Generic implementation. void GenericElementLoad(Node* receiver, Node* receiver_map, Node* instance_type, Node* index, Label* slow); enum UseStubCache { kUseStubCache, kDontUseStubCache }; void GenericPropertyLoad(Node* receiver, Node* receiver_map, Node* instance_type, Node* key, const LoadICParameters* p, Label* slow, UseStubCache use_stub_cache = kUseStubCache); // Low-level helpers. Node* PrepareValueForStore(Node* handler_word, Node* holder, Representation representation, Node* transition, Node* value, Label* bailout); // Extends properties backing store by JSObject::kFieldsAdded elements. void ExtendPropertiesBackingStore(Node* object); void StoreNamedField(Node* handler_word, Node* object, bool is_inobject, Representation representation, Node* value, bool transition_to_field, 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, Node* 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 CheckPrototype(Node* prototype_cell, Node* name, Label* miss); void NameDictionaryNegativeLookup(Node* object, Node* name, Label* miss); // 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, Variable* 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: explicit ExitPoint(CodeStubAssembler* assembler) : ExitPoint(assembler, nullptr, nullptr) {} ExitPoint(CodeStubAssembler* assembler, CodeAssemblerLabel* out, CodeAssemblerVariable* var_result) : out_(out), var_result_(var_result), asm_(assembler) { DCHECK_EQ(out != nullptr, var_result != nullptr); } template void ReturnCallRuntime(Runtime::FunctionId function, Node* context, TArgs... args) { if (IsDirect()) { asm_->TailCallRuntime(function, context, args...); } else { IndirectReturn(asm_->CallRuntime(function, context, args...)); } } template void ReturnCallStub(Callable const& callable, Node* context, TArgs... args) { if (IsDirect()) { asm_->TailCallStub(callable, context, args...); } else { IndirectReturn(asm_->CallStub(callable, context, args...)); } } template void ReturnCallStub(const CallInterfaceDescriptor& descriptor, Node* target, Node* context, TArgs... args) { if (IsDirect()) { asm_->TailCallStub(descriptor, target, context, args...); } else { IndirectReturn(asm_->CallStub(descriptor, target, context, args...)); } } void Return(Node* const result) { if (IsDirect()) { asm_->Return(result); } else { IndirectReturn(result); } } bool IsDirect() const { return out_ == nullptr; } private: void IndirectReturn(Node* const result) { var_result_->Bind(result); asm_->Goto(out_); } CodeAssemblerLabel* const out_; CodeAssemblerVariable* const var_result_; CodeStubAssembler* const asm_; }; } // namespace internal } // namespace v8 #endif // V8_SRC_IC_ACCESSOR_ASSEMBLER_H_