// Copyright 2012 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_CODE_STUBS_H_ #define V8_CODE_STUBS_H_ #include "src/interface-descriptors.h" #include "src/type-hints.h" namespace v8 { namespace internal { // Forward declarations. class CodeStubDescriptor; class Isolate; namespace compiler { class CodeAssemblerState; } // List of code stubs used on all platforms. #define CODE_STUB_LIST_ALL_PLATFORMS(V) \ /* --- PlatformCodeStubs --- */ \ V(CallApiCallback) \ V(CallApiGetter) \ V(JSEntry) \ V(ProfileEntryHook) \ /* --- TurboFanCodeStubs --- */ \ V(StoreSlowElement) \ V(StoreInArrayLiteralSlow) \ V(ElementsTransitionAndStore) \ V(KeyedLoadSloppyArguments) \ V(KeyedStoreSloppyArguments) \ V(StoreFastElement) \ V(StoreInterceptor) \ V(LoadIndexedInterceptor) // List of code stubs only used on ARM 32 bits platforms. #if V8_TARGET_ARCH_ARM #define CODE_STUB_LIST_ARM(V) V(DirectCEntry) #else #define CODE_STUB_LIST_ARM(V) #endif // List of code stubs only used on ARM 64 bits platforms. #if V8_TARGET_ARCH_ARM64 #define CODE_STUB_LIST_ARM64(V) V(DirectCEntry) #else #define CODE_STUB_LIST_ARM64(V) #endif // List of code stubs only used on PPC platforms. #ifdef V8_TARGET_ARCH_PPC #define CODE_STUB_LIST_PPC(V) V(DirectCEntry) #else #define CODE_STUB_LIST_PPC(V) #endif // List of code stubs only used on MIPS platforms. #if V8_TARGET_ARCH_MIPS #define CODE_STUB_LIST_MIPS(V) V(DirectCEntry) #elif V8_TARGET_ARCH_MIPS64 #define CODE_STUB_LIST_MIPS(V) V(DirectCEntry) #else #define CODE_STUB_LIST_MIPS(V) #endif // List of code stubs only used on S390 platforms. #ifdef V8_TARGET_ARCH_S390 #define CODE_STUB_LIST_S390(V) V(DirectCEntry) #else #define CODE_STUB_LIST_S390(V) #endif // Combined list of code stubs. #define CODE_STUB_LIST(V) \ CODE_STUB_LIST_ALL_PLATFORMS(V) \ CODE_STUB_LIST_ARM(V) \ CODE_STUB_LIST_ARM64(V) \ CODE_STUB_LIST_PPC(V) \ CODE_STUB_LIST_MIPS(V) \ CODE_STUB_LIST_S390(V) static const int kHasReturnedMinusZeroSentinel = 1; class CodeStub : public ZoneObject { public: enum Major { // TODO(mvstanton): eliminate the NoCache key by getting rid // of the non-monomorphic-cache. NoCache = 0, // marker for stubs that do custom caching] #define DEF_ENUM(name) name, CODE_STUB_LIST(DEF_ENUM) #undef DEF_ENUM NUMBER_OF_IDS }; // Retrieve the code for the stub. Generate the code if needed. Handle GetCode(); static Major MajorKeyFromKey(uint32_t key) { return static_cast(MajorKeyBits::decode(key)); } static uint32_t MinorKeyFromKey(uint32_t key) { return MinorKeyBits::decode(key); } // Gets the major key from a code object that is a code stub or binary op IC. static Major GetMajorKey(const Code* code_stub); static uint32_t NoCacheKey() { return MajorKeyBits::encode(NoCache); } static const char* MajorName(Major major_key); explicit CodeStub(Isolate* isolate) : minor_key_(0), isolate_(isolate) {} virtual ~CodeStub() = default; static void GenerateStubsAheadOfTime(Isolate* isolate); // Some stubs put untagged junk on the stack that cannot be scanned by the // GC. This means that we must be statically sure that no GC can occur while // they are running. If that is the case they should override this to return // true, which will cause an assertion if we try to call something that can // GC or if we try to put a stack frame on top of the junk, which would not // result in a traversable stack. virtual bool SometimesSetsUpAFrame() { return true; } // Lookup the code in the (possibly custom) cache. bool FindCodeInCache(Code** code_out); virtual CallInterfaceDescriptor GetCallInterfaceDescriptor() const = 0; virtual int GetStackParameterCount() const { return GetCallInterfaceDescriptor().GetStackParameterCount(); } static void InitializeDescriptor(Isolate* isolate, uint32_t key, CodeStubDescriptor* desc); static MaybeHandle GetCode(Isolate* isolate, uint32_t key); // Returns information for computing the number key. virtual Major MajorKey() const = 0; uint32_t MinorKey() const { return minor_key_; } friend std::ostream& operator<<(std::ostream& os, const CodeStub& s) { s.PrintName(os); return os; } Isolate* isolate() const { return isolate_; } void set_isolate(Isolate* isolate) { DCHECK_NOT_NULL(isolate); DCHECK(isolate_ == nullptr || isolate_ == isolate); isolate_ = isolate; } void DeleteStubFromCacheForTesting(); protected: CodeStub(uint32_t key, Isolate* isolate) : minor_key_(MinorKeyFromKey(key)), isolate_(isolate) {} // Generates the assembler code for the stub. virtual Handle GenerateCode() = 0; // Returns whether the code generated for this stub needs to be allocated as // a fixed (non-moveable) code object. // TODO(jgruber): Only required by DirectCEntryStub. Can be removed when/if // that is ported to a builtin. virtual Movability NeedsImmovableCode() { return kMovable; } virtual void PrintName(std::ostream& os) const; // NOLINT virtual void PrintBaseName(std::ostream& os) const; // NOLINT virtual void PrintState(std::ostream& os) const { ; } // NOLINT // Computes the key based on major and minor. uint32_t GetKey() { DCHECK(static_cast(MajorKey()) < NUMBER_OF_IDS); return MinorKeyBits::encode(MinorKey()) | MajorKeyBits::encode(MajorKey()); } uint32_t minor_key_; private: // Perform bookkeeping required after code generation when stub code is // initially generated. void RecordCodeGeneration(Handle code); // Activate newly generated stub. Is called after // registering stub in the stub cache. virtual void Activate(Code* code) { } // We use this dispatch to statically instantiate the correct code stub for // the given stub key and call the passed function with that code stub. typedef void (*DispatchedCall)(CodeStub* stub, void** value_out); static void Dispatch(Isolate* isolate, uint32_t key, void** value_out, DispatchedCall call); static void GetCodeDispatchCall(CodeStub* stub, void** value_out); STATIC_ASSERT(NUMBER_OF_IDS < (1 << kStubMajorKeyBits)); class MajorKeyBits: public BitField {}; class MinorKeyBits: public BitField {}; // NOLINT friend class BreakPointIterator; Isolate* isolate_; }; #define DEFINE_CODE_STUB_BASE(NAME, SUPER) \ public: \ NAME(uint32_t key, Isolate* isolate) : SUPER(key, isolate) {} \ \ private: \ DISALLOW_COPY_AND_ASSIGN(NAME) #define DEFINE_CODE_STUB(NAME, SUPER) \ public: \ inline Major MajorKey() const override { return NAME; }; \ \ DEFINE_CODE_STUB_BASE(NAME##Stub, SUPER) #define DEFINE_PLATFORM_CODE_STUB(NAME, SUPER) \ private: \ void Generate(MacroAssembler* masm) override; \ DEFINE_CODE_STUB(NAME, SUPER) #define DEFINE_TURBOFAN_CODE_STUB(NAME, SUPER) \ public: \ void GenerateAssembly(compiler::CodeAssemblerState* state) const override; \ DEFINE_CODE_STUB(NAME, SUPER) #define DEFINE_CALL_INTERFACE_DESCRIPTOR(NAME) \ public: \ typedef NAME##Descriptor Descriptor; \ CallInterfaceDescriptor GetCallInterfaceDescriptor() const override { \ return Descriptor(); \ } // There are some code stubs we just can't describe right now with a // CallInterfaceDescriptor. Isolate behavior for those cases with this macro. // An attempt to retrieve a descriptor will fail. #define DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR() \ public: \ CallInterfaceDescriptor GetCallInterfaceDescriptor() const override { \ UNREACHABLE(); \ return CallInterfaceDescriptor(); \ } class PlatformCodeStub : public CodeStub { public: // Retrieve the code for the stub. Generate the code if needed. Handle GenerateCode() override; protected: explicit PlatformCodeStub(Isolate* isolate) : CodeStub(isolate) {} // Generates the assembler code for the stub. virtual void Generate(MacroAssembler* masm) = 0; // Generates the exception handler table for the stub. virtual int GenerateHandlerTable(MacroAssembler* masm); DEFINE_CODE_STUB_BASE(PlatformCodeStub, CodeStub); }; enum StubFunctionMode { NOT_JS_FUNCTION_STUB_MODE, JS_FUNCTION_STUB_MODE }; class CodeStubDescriptor { public: explicit CodeStubDescriptor(CodeStub* stub); CodeStubDescriptor(Isolate* isolate, uint32_t stub_key); void Initialize(Address deoptimization_handler = kNullAddress, int hint_stack_parameter_count = -1, StubFunctionMode function_mode = NOT_JS_FUNCTION_STUB_MODE); void Initialize(Register stack_parameter_count, Address deoptimization_handler = kNullAddress, int hint_stack_parameter_count = -1, StubFunctionMode function_mode = NOT_JS_FUNCTION_STUB_MODE); void SetMissHandler(Runtime::FunctionId id) { miss_handler_id_ = id; miss_handler_ = ExternalReference::Create(Runtime::FunctionForId(id)); has_miss_handler_ = true; // Our miss handler infrastructure doesn't currently support // variable stack parameter counts. DCHECK(!stack_parameter_count_.is_valid()); } void set_call_descriptor(CallInterfaceDescriptor d) { call_descriptor_ = std::move(d); } CallInterfaceDescriptor call_descriptor() const { return call_descriptor_; } int GetRegisterParameterCount() const { return call_descriptor().GetRegisterParameterCount(); } int GetStackParameterCount() const { return call_descriptor().GetStackParameterCount(); } int GetParameterCount() const { return call_descriptor().GetParameterCount(); } Register GetRegisterParameter(int index) const { return call_descriptor().GetRegisterParameter(index); } MachineType GetParameterType(int index) const { return call_descriptor().GetParameterType(index); } ExternalReference miss_handler() const { DCHECK(has_miss_handler_); return miss_handler_; } Runtime::FunctionId miss_handler_id() const { DCHECK(has_miss_handler_); return miss_handler_id_; } bool has_miss_handler() const { return has_miss_handler_; } int GetHandlerParameterCount() const { int params = GetParameterCount(); if (PassesArgumentsToDeoptimizationHandler()) { params += 1; } return params; } int hint_stack_parameter_count() const { return hint_stack_parameter_count_; } Register stack_parameter_count() const { return stack_parameter_count_; } StubFunctionMode function_mode() const { return function_mode_; } Address deoptimization_handler() const { return deoptimization_handler_; } private: bool PassesArgumentsToDeoptimizationHandler() const { return stack_parameter_count_.is_valid(); } Isolate* isolate_; CallInterfaceDescriptor call_descriptor_; Register stack_parameter_count_; // If hint_stack_parameter_count_ > 0, the code stub can optimize the // return sequence. Default value is -1, which means it is ignored. int hint_stack_parameter_count_; StubFunctionMode function_mode_; Address deoptimization_handler_; ExternalReference miss_handler_; Runtime::FunctionId miss_handler_id_; bool has_miss_handler_; }; class TurboFanCodeStub : public CodeStub { public: // Retrieve the code for the stub. Generate the code if needed. Handle GenerateCode() override; int GetStackParameterCount() const override { return GetCallInterfaceDescriptor().GetStackParameterCount(); } protected: explicit TurboFanCodeStub(Isolate* isolate) : CodeStub(isolate) {} virtual void GenerateAssembly(compiler::CodeAssemblerState* state) const = 0; private: DEFINE_CODE_STUB_BASE(TurboFanCodeStub, CodeStub); }; } // namespace internal } // namespace v8 #if V8_TARGET_ARCH_IA32 #elif V8_TARGET_ARCH_X64 #elif V8_TARGET_ARCH_ARM64 #include "src/arm64/code-stubs-arm64.h" #elif V8_TARGET_ARCH_ARM #include "src/arm/code-stubs-arm.h" #elif V8_TARGET_ARCH_PPC #include "src/ppc/code-stubs-ppc.h" #elif V8_TARGET_ARCH_MIPS #include "src/mips/code-stubs-mips.h" #elif V8_TARGET_ARCH_MIPS64 #include "src/mips64/code-stubs-mips64.h" #elif V8_TARGET_ARCH_S390 #include "src/s390/code-stubs-s390.h" #else #error Unsupported target architecture. #endif namespace v8 { namespace internal { // TODO(jgruber): Convert this stub into a builtin. class StoreInterceptorStub : public TurboFanCodeStub { public: explicit StoreInterceptorStub(Isolate* isolate) : TurboFanCodeStub(isolate) {} DEFINE_CALL_INTERFACE_DESCRIPTOR(StoreWithVector); DEFINE_TURBOFAN_CODE_STUB(StoreInterceptor, TurboFanCodeStub); }; // TODO(jgruber): Convert this stub into a builtin. class LoadIndexedInterceptorStub : public TurboFanCodeStub { public: explicit LoadIndexedInterceptorStub(Isolate* isolate) : TurboFanCodeStub(isolate) {} DEFINE_CALL_INTERFACE_DESCRIPTOR(LoadWithVector); DEFINE_TURBOFAN_CODE_STUB(LoadIndexedInterceptor, TurboFanCodeStub); }; // TODO(jgruber): Convert this stub into a builtin. class KeyedLoadSloppyArgumentsStub : public TurboFanCodeStub { public: explicit KeyedLoadSloppyArgumentsStub(Isolate* isolate) : TurboFanCodeStub(isolate) {} protected: DEFINE_CALL_INTERFACE_DESCRIPTOR(LoadWithVector); DEFINE_TURBOFAN_CODE_STUB(KeyedLoadSloppyArguments, TurboFanCodeStub); }; class CommonStoreModeBits : public BitField {}; class KeyedStoreSloppyArgumentsStub : public TurboFanCodeStub { public: explicit KeyedStoreSloppyArgumentsStub(Isolate* isolate, KeyedAccessStoreMode mode) : TurboFanCodeStub(isolate) { minor_key_ = CommonStoreModeBits::encode(mode); } protected: DEFINE_CALL_INTERFACE_DESCRIPTOR(StoreWithVector); DEFINE_TURBOFAN_CODE_STUB(KeyedStoreSloppyArguments, TurboFanCodeStub); }; class CallApiCallbackStub : public PlatformCodeStub { public: static const int kArgBits = 7; static const int kArgMax = (1 << kArgBits) - 1; CallApiCallbackStub(Isolate* isolate, int argc) : PlatformCodeStub(isolate) { CHECK_LE(0, argc); // The argc in {0, 1} cases are covered by builtins. CHECK_LE(argc, kArgMax); minor_key_ = ArgumentBits::encode(argc); } private: int argc() const { return ArgumentBits::decode(minor_key_); } class ArgumentBits : public BitField {}; friend class Builtins; // For generating the related builtin. DEFINE_CALL_INTERFACE_DESCRIPTOR(ApiCallback); DEFINE_PLATFORM_CODE_STUB(CallApiCallback, PlatformCodeStub); }; // TODO(jgruber): This stub only exists to avoid code duplication between // code-stubs-.cc and builtins-.cc. If CallApiCallbackStub is ever // completely removed, CallApiGetterStub can also be deleted. class CallApiGetterStub : public PlatformCodeStub { private: // For generating the related builtin. explicit CallApiGetterStub(Isolate* isolate) : PlatformCodeStub(isolate) {} friend class Builtins; DEFINE_CALL_INTERFACE_DESCRIPTOR(ApiGetter); DEFINE_PLATFORM_CODE_STUB(CallApiGetter, PlatformCodeStub); }; class JSEntryStub : public PlatformCodeStub { public: enum class SpecialTarget { kNone, kRunMicrotasks }; JSEntryStub(Isolate* isolate, StackFrame::Type type) : PlatformCodeStub(isolate) { DCHECK(type == StackFrame::ENTRY || type == StackFrame::CONSTRUCT_ENTRY); minor_key_ = StackFrameTypeBits::encode(type) | SpecialTargetBits::encode(SpecialTarget::kNone); } JSEntryStub(Isolate* isolate, SpecialTarget target) : PlatformCodeStub(isolate) { minor_key_ = StackFrameTypeBits::encode(StackFrame::ENTRY) | SpecialTargetBits::encode(target); } private: int GenerateHandlerTable(MacroAssembler* masm) override; void PrintName(std::ostream& os) const override { // NOLINT os << (type() == StackFrame::ENTRY ? "JSEntryStub" : "JSConstructEntryStub"); } StackFrame::Type type() const { return StackFrameTypeBits::decode(minor_key_); } SpecialTarget special_target() const { return SpecialTargetBits::decode(minor_key_); } Handle EntryTrampoline() { switch (special_target()) { case SpecialTarget::kNone: return (type() == StackFrame::CONSTRUCT_ENTRY) ? BUILTIN_CODE(isolate(), JSConstructEntryTrampoline) : BUILTIN_CODE(isolate(), JSEntryTrampoline); case SpecialTarget::kRunMicrotasks: return BUILTIN_CODE(isolate(), RunMicrotasks); } UNREACHABLE(); return Handle(); } class StackFrameTypeBits : public BitField {}; class SpecialTargetBits : public BitField {}; int handler_offset_; DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); DEFINE_PLATFORM_CODE_STUB(JSEntry, PlatformCodeStub); }; class StoreFastElementStub : public TurboFanCodeStub { public: StoreFastElementStub(Isolate* isolate, bool is_js_array, ElementsKind elements_kind, KeyedAccessStoreMode mode) : TurboFanCodeStub(isolate) { minor_key_ = CommonStoreModeBits::encode(mode) | ElementsKindBits::encode(elements_kind) | IsJSArrayBits::encode(is_js_array); } static void GenerateAheadOfTime(Isolate* isolate); bool is_js_array() const { return IsJSArrayBits::decode(minor_key_); } ElementsKind elements_kind() const { return ElementsKindBits::decode(minor_key_); } KeyedAccessStoreMode store_mode() const { return CommonStoreModeBits::decode(minor_key_); } private: class ElementsKindBits : public BitField {}; class IsJSArrayBits : public BitField {}; DEFINE_CALL_INTERFACE_DESCRIPTOR(StoreWithVector); DEFINE_TURBOFAN_CODE_STUB(StoreFastElement, TurboFanCodeStub); }; class StoreSlowElementStub : public TurboFanCodeStub { public: StoreSlowElementStub(Isolate* isolate, KeyedAccessStoreMode mode) : TurboFanCodeStub(isolate) { minor_key_ = CommonStoreModeBits::encode(mode); } private: DEFINE_CALL_INTERFACE_DESCRIPTOR(StoreWithVector); DEFINE_TURBOFAN_CODE_STUB(StoreSlowElement, TurboFanCodeStub); }; class StoreInArrayLiteralSlowStub : public TurboFanCodeStub { public: StoreInArrayLiteralSlowStub(Isolate* isolate, KeyedAccessStoreMode mode) : TurboFanCodeStub(isolate) { minor_key_ = CommonStoreModeBits::encode(mode); } private: DEFINE_CALL_INTERFACE_DESCRIPTOR(StoreWithVector); DEFINE_TURBOFAN_CODE_STUB(StoreInArrayLiteralSlow, TurboFanCodeStub); }; class ElementsTransitionAndStoreStub : public TurboFanCodeStub { public: ElementsTransitionAndStoreStub(Isolate* isolate, ElementsKind from_kind, ElementsKind to_kind, bool is_jsarray, KeyedAccessStoreMode store_mode) : TurboFanCodeStub(isolate) { minor_key_ = CommonStoreModeBits::encode(store_mode) | FromBits::encode(from_kind) | ToBits::encode(to_kind) | IsJSArrayBits::encode(is_jsarray); } ElementsKind from_kind() const { return FromBits::decode(minor_key_); } ElementsKind to_kind() const { return ToBits::decode(minor_key_); } bool is_jsarray() const { return IsJSArrayBits::decode(minor_key_); } KeyedAccessStoreMode store_mode() const { return CommonStoreModeBits::decode(minor_key_); } private: class FromBits : public BitField {}; class ToBits : public BitField {}; class IsJSArrayBits : public BitField {}; DEFINE_CALL_INTERFACE_DESCRIPTOR(StoreTransition); DEFINE_TURBOFAN_CODE_STUB(ElementsTransitionAndStore, TurboFanCodeStub); }; // TODO(jgruber): Convert this stub into a builtin. class ProfileEntryHookStub : public PlatformCodeStub { public: explicit ProfileEntryHookStub(Isolate* isolate) : PlatformCodeStub(isolate) {} // The profile entry hook function is not allowed to cause a GC. bool SometimesSetsUpAFrame() override { return false; } // Generates a call to the entry hook if it's enabled. static void MaybeCallEntryHook(MacroAssembler* masm); static void MaybeCallEntryHookDelayed(TurboAssembler* tasm, Zone* zone); private: static void EntryHookTrampoline(intptr_t function, intptr_t stack_pointer, Isolate* isolate); // ProfileEntryHookStub is called at the start of a function, so it has the // same register set. DEFINE_CALL_INTERFACE_DESCRIPTOR(CallFunction) DEFINE_PLATFORM_CODE_STUB(ProfileEntryHook, PlatformCodeStub); }; #undef DEFINE_CALL_INTERFACE_DESCRIPTOR #undef DEFINE_PLATFORM_CODE_STUB #undef DEFINE_CODE_STUB #undef DEFINE_CODE_STUB_BASE } // namespace internal } // namespace v8 #endif // V8_CODE_STUBS_H_