diff options
Diffstat (limited to 'deps/v8/src/code-stubs.h')
-rw-r--r-- | deps/v8/src/code-stubs.h | 670 |
1 files changed, 0 insertions, 670 deletions
diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h deleted file mode 100644 index 4630fe7639..0000000000 --- a/deps/v8/src/code-stubs.h +++ /dev/null @@ -1,670 +0,0 @@ -// 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<Code> GetCode(); - - static Major MajorKeyFromKey(uint32_t key) { - return static_cast<Major>(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<Code> 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<Code> 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<int>(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> 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<uint32_t, 0, kStubMajorKeyBits> {}; - class MinorKeyBits: public BitField<uint32_t, - kStubMajorKeyBits, kStubMinorKeyBits> {}; // 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<Code> 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<Code> 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<KeyedAccessStoreMode, 0, 3> {}; - -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<int, 0, kArgBits> {}; - - 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-<arch>.cc and builtins-<arch>.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<Code> 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<Code>(); - } - - class StackFrameTypeBits : public BitField<StackFrame::Type, 0, 5> {}; - class SpecialTargetBits - : public BitField<SpecialTarget, StackFrameTypeBits::kNext, 1> {}; - - 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<ElementsKind, CommonStoreModeBits::kNext, 8> {}; - class IsJSArrayBits : public BitField<bool, ElementsKindBits::kNext, 1> {}; - - 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<ElementsKind, CommonStoreModeBits::kNext, 8> {}; - class ToBits : public BitField<ElementsKind, 11, 8> {}; - class IsJSArrayBits : public BitField<bool, 19, 1> {}; - - 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_ |