diff options
Diffstat (limited to 'js/src/jit/IonCaches.h')
-rw-r--r-- | js/src/jit/IonCaches.h | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/js/src/jit/IonCaches.h b/js/src/jit/IonCaches.h new file mode 100644 index 000000000..173e06c6b --- /dev/null +++ b/js/src/jit/IonCaches.h @@ -0,0 +1,848 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_IonCaches_h +#define jit_IonCaches_h + +#if defined(JS_CODEGEN_ARM) +# include "jit/arm/Assembler-arm.h" +#elif defined(JS_CODEGEN_ARM64) +# include "jit/arm64/Assembler-arm64.h" +#elif defined(JS_CODEGEN_MIPS32) +# include "jit/mips32/Assembler-mips32.h" +#elif defined(JS_CODEGEN_MIPS64) +# include "jit/mips64/Assembler-mips64.h" +#endif +#include "jit/JitCompartment.h" +#include "jit/Registers.h" +#include "jit/shared/Assembler-shared.h" +#include "js/TrackedOptimizationInfo.h" + +#include "vm/TypedArrayCommon.h" + +namespace js { +namespace jit { + +class LInstruction; + +#define IONCACHE_KIND_LIST(_) \ + _(GetProperty) \ + _(SetProperty) \ + _(BindName) \ + _(Name) + +// Forward declarations of Cache kinds. +#define FORWARD_DECLARE(kind) class kind##IC; +IONCACHE_KIND_LIST(FORWARD_DECLARE) +#undef FORWARD_DECLARE + +class IonCacheVisitor +{ + public: +#define VISIT_INS(op) \ + virtual void visit##op##IC(CodeGenerator* codegen) { \ + MOZ_CRASH("NYI: " #op "IC"); \ + } + + IONCACHE_KIND_LIST(VISIT_INS) +#undef VISIT_INS +}; + +// Common structure encoding the state of a polymorphic inline cache contained +// in the code for an IonScript. IonCaches are used for polymorphic operations +// where multiple implementations may be required. +// +// Roughly speaking, the cache initially jumps to an out of line fragment +// which invokes a cache function to perform the operation. The cache function +// may generate a stub to perform the operation in certain cases (e.g. a +// particular shape for an input object) and attach the stub to existing +// stubs, forming a daisy chain of tests for how to perform the operation in +// different circumstances. +// +// Eventually, if too many stubs are generated the cache function may disable +// the cache, by generating a stub to make a call and perform the operation +// within the VM. +// +// The caches initially generate a patchable jump to an out of line call +// to the cache function. Stubs are attached by appending: when attaching a +// new stub, we patch the any failure conditions in last generated stub to +// jump to the new stub. Failure conditions in the new stub jump to the cache +// function which may generate new stubs. +// +// Control flow Pointers +// =======# ----. .----> +// # | | +// #======> \-----/ +// +// Initial state: +// +// JIT Code +// +--------+ .---------------. +// | | | | +// |========| v +----------+ | +// |== IC ==|====>| Cache Fn | | +// |========| +----------+ | +// | |<=# # | +// | | #=======# | +// +--------+ Rejoin path | +// |________ | +// | | +// IC | | +// Entry | | +// +------------+ | +// | lastJump_ |---------------/ +// +------------+ +// | ... | +// +------------+ +// +// Attaching stubs: +// +// Patch the jump pointed to by lastJump_ to jump to the new stub. Update +// lastJump_ to be the new stub's failure jump. The failure jump of the new +// stub goes to the fallback label, which is the cache function. In this +// fashion, new stubs are _appended_ to the chain of stubs, as lastJump_ +// points to the _tail_ of the stub chain. +// +// JIT Code +// +--------+ #=======================# +// | | # v +// |========| # +----------+ +------+ +// |== IC ==|=# | Cache Fn |<====| Stub | +// |========| +----------+ ^ +------+ +// | |<=# # | # +// | | #======#=========|=====# +// +--------+ Rejoin path | +// |________ | +// | | +// IC | | +// Entry | | +// +------------+ | +// | lastJump_ |---------------/ +// +------------+ +// | ... | +// +------------+ +// +// While calls may be made to the cache function and other VM functions, the +// cache may still be treated as pure during optimization passes, such that +// LICM and GVN may be performed on operations around the cache as if the +// operation cannot reenter scripted code through an Invoke() or otherwise have +// unexpected behavior. This restricts the sorts of stubs which the cache can +// generate or the behaviors which called functions can have, and if a called +// function performs a possibly impure operation then the operation will be +// marked as such and the calling script will be recompiled. +// +// Similarly, despite the presence of functions and multiple stubs generated +// for a cache, the cache itself may be marked as idempotent and become hoisted +// or coalesced by LICM or GVN. This also constrains the stubs which can be +// generated for the cache. +// +// * IonCache usage +// +// IonCache is the base structure of an inline cache, which generates code stubs +// dynamically and attaches them to an IonScript. +// +// A cache must at least provide a static update function which will usualy have +// a JSContext*, followed by the cache index. The rest of the arguments of the +// update function are usualy corresponding to the register inputs of the cache, +// as it must perform the same operation as any of the stubs that it might +// produce. The update function call is handled by the visit function of +// CodeGenerator corresponding to this IC. +// +// The CodeGenerator visit function, as opposed to other visit functions, has +// two arguments. The first one is the OutOfLineUpdateCache which stores the LIR +// instruction. The second one is the IC object. This function would be called +// once the IC is registered with the addCache function of CodeGeneratorShared. +// +// To register a cache, you must call the addCache function as follow: +// +// MyCodeIC cache(inputReg1, inputValueReg2, outputReg); +// if (!addCache(lir, allocateCache(cache))) +// return false; +// +// Once the cache is allocated with the allocateCache function, any modification +// made to the cache would be ignored. +// +// The addCache function will produce a patchable jump at the location where +// it is called. This jump will execute generated stubs and fallback on the code +// of the visitMyCodeIC function if no stub match. +// +// Warning: As the addCache function fallback on a VMCall, calls to +// addCache should not be in the same path as another VMCall or in the same +// path of another addCache as this is not supported by the invalidation +// procedure. +class IonCache +{ + public: + class StubAttacher; + + enum Kind { +# define DEFINE_CACHEKINDS(ickind) Cache_##ickind, + IONCACHE_KIND_LIST(DEFINE_CACHEKINDS) +# undef DEFINE_CACHEKINDS + Cache_Invalid + }; + + // Cache testing and cast. +# define CACHEKIND_CASTS(ickind) \ + bool is##ickind() const { \ + return kind() == Cache_##ickind; \ + } \ + inline ickind##IC& to##ickind(); \ + inline const ickind##IC& to##ickind() const; + IONCACHE_KIND_LIST(CACHEKIND_CASTS) +# undef CACHEKIND_CASTS + + virtual Kind kind() const = 0; + + virtual void accept(CodeGenerator* codegen, IonCacheVisitor* visitor) = 0; + + public: + + static const char* CacheName(Kind kind); + + protected: + bool pure_ : 1; + bool idempotent_ : 1; + bool disabled_ : 1; + size_t stubCount_ : 5; + + CodeLocationLabel fallbackLabel_; + + // Location of this operation, nullptr for idempotent caches. + JSScript* script_; + jsbytecode* pc_; + + // Location to use when updating profiler pseudostack when leaving this + // IC code to enter a callee. + jsbytecode* profilerLeavePc_; + + CodeLocationJump initialJump_; + CodeLocationJump lastJump_; + CodeLocationLabel rejoinLabel_; + + private: + static const size_t MAX_STUBS; + void incrementStubCount() { + // The IC should stop generating stubs before wrapping stubCount. + stubCount_++; + MOZ_ASSERT(stubCount_); + } + + public: + + IonCache() + : pure_(false), + idempotent_(false), + disabled_(false), + stubCount_(0), + fallbackLabel_(), + script_(nullptr), + pc_(nullptr), + profilerLeavePc_(nullptr), + initialJump_(), + lastJump_(), + rejoinLabel_() + { + } + + void disable(); + inline bool isDisabled() const { + return disabled_; + } + + // Set the initial 'out-of-line' jump state of the cache. The fallbackLabel is + // the location of the out-of-line update (slow) path. This location will + // be set to the exitJump of the last generated stub. + void setFallbackLabel(CodeOffset fallbackLabel) { + fallbackLabel_ = fallbackLabel; + } + + void setProfilerLeavePC(jsbytecode* pc) { + MOZ_ASSERT(pc != nullptr); + profilerLeavePc_ = pc; + } + + // Get the address at which IC rejoins the mainline jitcode. + void* rejoinAddress() const { + return rejoinLabel_.raw(); + } + + void emitInitialJump(MacroAssembler& masm, RepatchLabel& entry); + void updateBaseAddress(JitCode* code, MacroAssembler& masm); + + // Reset the cache around garbage collection. + virtual void reset(ReprotectCode reprotect); + + bool canAttachStub() const { + return stubCount_ < MAX_STUBS; + } + bool empty() const { + return stubCount_ == 0; + } + + enum LinkStatus { + LINK_ERROR, + CACHE_FLUSHED, + LINK_GOOD + }; + + // Use the Linker to link the generated code and check if any + // monitoring/allocation caused an invalidation of the running ion script, + // this function returns CACHE_FLUSHED. In case of allocation issue this + // function returns LINK_ERROR. + LinkStatus linkCode(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher, IonScript* ion, + JitCode** code); + + // Fixup variables and update jumps in the list of stubs. Increment the + // number of attached stubs accordingly. + void attachStub(MacroAssembler& masm, StubAttacher& attacher, CodeLocationJump lastJump, + Handle<JitCode*> code); + + // Combine both linkStub and attachStub into one function. In addition, it + // produces a spew augmented with the attachKind string. + MOZ_MUST_USE bool linkAndAttachStub(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher, + IonScript* ion, const char* attachKind, + JS::TrackedOutcome = JS::TrackedOutcome::ICOptStub_GenericSuccess); + +#ifdef DEBUG + bool isAllocated() { + return fallbackLabel_.isSet(); + } +#endif + + bool pure() const { + return pure_; + } + bool idempotent() const { + return idempotent_; + } + void setIdempotent() { + MOZ_ASSERT(!idempotent_); + MOZ_ASSERT(!script_); + MOZ_ASSERT(!pc_); + idempotent_ = true; + } + + void setScriptedLocation(JSScript* script, jsbytecode* pc) { + MOZ_ASSERT(!idempotent_); + script_ = script; + pc_ = pc; + } + + void getScriptedLocation(MutableHandleScript pscript, jsbytecode** ppc) const { + pscript.set(script_); + *ppc = pc_; + } + + jsbytecode* pc() const { + MOZ_ASSERT(pc_); + return pc_; + } + + void trace(JSTracer* trc); +}; + +// Define the cache kind and pre-declare data structures used for calling inline +// caches. +#define CACHE_HEADER(ickind) \ + Kind kind() const { \ + return IonCache::Cache_##ickind; \ + } \ + \ + void accept(CodeGenerator* codegen, IonCacheVisitor* visitor) { \ + visitor->visit##ickind##IC(codegen); \ + } \ + \ + static const VMFunction UpdateInfo; + +// Subclasses of IonCache for the various kinds of caches. These do not define +// new data members; all caches must be of the same size. + +// Helper for idempotent GetPropertyIC location tracking. Declared externally +// to be forward declarable. +// +// Since all the scripts stored in CacheLocations are guaranteed to have been +// Ion compiled, and are kept alive by function objects in jitcode, and since +// the CacheLocations only have the lifespan of the jitcode, there is no need +// to trace or mark any of the scripts. Since JSScripts are always allocated +// tenured, and never moved, we can keep raw pointers, and there is no need +// for GCPtrScripts here. +struct CacheLocation { + jsbytecode* pc; + JSScript* script; + + CacheLocation(jsbytecode* pcin, JSScript* scriptin) + : pc(pcin), script(scriptin) + { } +}; + +class GetPropertyIC : public IonCache +{ + protected: + // Registers live after the cache, excluding output registers. The initial + // value of these registers must be preserved by the cache. + LiveRegisterSet liveRegs_; + + Register object_; + ConstantOrRegister id_; + TypedOrValueRegister output_; + + // Only valid if idempotent + size_t locationsIndex_; + size_t numLocations_; + + static const size_t MAX_FAILED_UPDATES = 16; + uint16_t failedUpdates_; + + bool monitoredResult_ : 1; + bool allowDoubleResult_ : 1; + bool hasTypedArrayLengthStub_ : 1; + bool hasMappedArgumentsLengthStub_ : 1; + bool hasUnmappedArgumentsLengthStub_ : 1; + bool hasMappedArgumentsElementStub_ : 1; + bool hasUnmappedArgumentsElementStub_ : 1; + bool hasGenericProxyStub_ : 1; + bool hasDenseStub_ : 1; + + void emitIdGuard(MacroAssembler& masm, jsid id, Label* fail); + + public: + GetPropertyIC(LiveRegisterSet liveRegs, + Register object, const ConstantOrRegister& id, + TypedOrValueRegister output, + bool monitoredResult, bool allowDoubleResult) + : liveRegs_(liveRegs), + object_(object), + id_(id), + output_(output), + locationsIndex_(0), + numLocations_(0), + failedUpdates_(0), + monitoredResult_(monitoredResult), + allowDoubleResult_(allowDoubleResult), + hasTypedArrayLengthStub_(false), + hasMappedArgumentsLengthStub_(false), + hasUnmappedArgumentsLengthStub_(false), + hasMappedArgumentsElementStub_(false), + hasUnmappedArgumentsElementStub_(false), + hasGenericProxyStub_(false), + hasDenseStub_(false) + { + } + + CACHE_HEADER(GetProperty) + + void reset(ReprotectCode reprotect); + + Register object() const { + return object_; + } + ConstantOrRegister id() const { + return id_; + } + TypedOrValueRegister output() const { + return output_; + } + bool monitoredResult() const { + return monitoredResult_; + } + bool hasTypedArrayLengthStub(HandleObject obj) const { + return hasTypedArrayLengthStub_; + } + bool hasArgumentsLengthStub(bool mapped) const { + return mapped ? hasMappedArgumentsLengthStub_ : hasUnmappedArgumentsLengthStub_; + } + bool hasArgumentsElementStub(bool mapped) const { + return mapped ? hasMappedArgumentsElementStub_ : hasUnmappedArgumentsElementStub_; + } + bool hasGenericProxyStub() const { + return hasGenericProxyStub_; + } + + bool hasDenseStub() const { + return hasDenseStub_; + } + void setHasDenseStub() { + MOZ_ASSERT(!hasDenseStub()); + hasDenseStub_ = true; + } + + void setHasTypedArrayLengthStub(HandleObject obj) { + MOZ_ASSERT(obj->is<TypedArrayObject>()); + MOZ_ASSERT(!hasTypedArrayLengthStub_); + hasTypedArrayLengthStub_ = true; + } + + void setLocationInfo(size_t locationsIndex, size_t numLocations) { + MOZ_ASSERT(idempotent()); + MOZ_ASSERT(!numLocations_); + MOZ_ASSERT(numLocations); + locationsIndex_ = locationsIndex; + numLocations_ = numLocations; + } + void getLocationInfo(uint32_t* index, uint32_t* num) const { + MOZ_ASSERT(idempotent()); + *index = locationsIndex_; + *num = numLocations_; + } + + enum NativeGetPropCacheability { + CanAttachNone, + CanAttachReadSlot, + CanAttachArrayLength, + CanAttachCallGetter + }; + + // Helpers for CanAttachNativeGetProp + bool allowArrayLength(JSContext* cx) const; + bool allowGetters() const { + return monitoredResult() && !idempotent(); + } + + void maybeDisable(bool emitted); + + // Attach the proper stub, if possible + MOZ_MUST_USE bool tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleValue idval, bool* emitted); + + MOZ_MUST_USE bool tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, void* returnAddr, + bool* emitted); + + MOZ_MUST_USE bool tryAttachGenericProxy(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, void* returnAddr, + bool* emitted); + + MOZ_MUST_USE bool tryAttachDOMProxyShadowed(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, HandleId id, + void* returnAddr, bool* emitted); + + MOZ_MUST_USE bool tryAttachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, HandleId id, + bool resetNeeded, void* returnAddr, + bool* emitted); + + MOZ_MUST_USE bool tryAttachNative(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, void* returnAddr, + bool* emitted); + + MOZ_MUST_USE bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, void* returnAddr, + bool* emitted); + + MOZ_MUST_USE bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, HandleId id, + void* returnAddr, bool* emitted); + + MOZ_MUST_USE bool tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, HandleId id, + void* returnAddr, bool* emitted); + + MOZ_MUST_USE bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, HandleId id, + bool* emitted); + + MOZ_MUST_USE bool tryAttachArgumentsLength(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, HandleId id, + bool* emitted); + + MOZ_MUST_USE bool tryAttachArgumentsElement(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, HandleValue idval, + bool* emitted); + + MOZ_MUST_USE bool tryAttachDenseElement(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleValue idval, bool* emitted); + + static bool canAttachDenseElementHole(JSObject* obj, HandleValue idval, + TypedOrValueRegister output); + MOZ_MUST_USE bool tryAttachDenseElementHole(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, + HandleValue idval, bool* emitted); + + static bool canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval, + TypedOrValueRegister output); + + MOZ_MUST_USE bool tryAttachTypedOrUnboxedArrayElement(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, + HandleValue idval, bool* emitted); + + MOZ_MUST_USE bool tryAttachModuleNamespace(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, HandleId id, + void* returnAddr, bool* emitted); + + static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, + HandleObject obj, HandleValue id, MutableHandleValue vp); +}; + +class SetPropertyIC : public IonCache +{ + protected: + // Registers live after the cache, excluding output registers. The initial + // value of these registers must be preserved by the cache. + LiveRegisterSet liveRegs_; + + Register object_; + Register temp_; + Register tempToUnboxIndex_; + FloatRegister tempDouble_; + FloatRegister tempFloat32_; + ConstantOrRegister id_; + ConstantOrRegister value_; + bool strict_ : 1; + bool needsTypeBarrier_ : 1; + bool guardHoles_ : 1; + + bool hasGenericProxyStub_ : 1; + bool hasDenseStub_ : 1; + + void emitIdGuard(MacroAssembler& masm, jsid id, Label* fail); + + public: + SetPropertyIC(LiveRegisterSet liveRegs, Register object, Register temp, Register tempToUnboxIndex, + FloatRegister tempDouble, FloatRegister tempFloat32, + const ConstantOrRegister& id, const ConstantOrRegister& value, + bool strict, bool needsTypeBarrier, bool guardHoles) + : liveRegs_(liveRegs), + object_(object), + temp_(temp), + tempToUnboxIndex_(tempToUnboxIndex), + tempDouble_(tempDouble), + tempFloat32_(tempFloat32), + id_(id), + value_(value), + strict_(strict), + needsTypeBarrier_(needsTypeBarrier), + guardHoles_(guardHoles), + hasGenericProxyStub_(false), + hasDenseStub_(false) + { + } + + CACHE_HEADER(SetProperty) + + void reset(ReprotectCode reprotect); + + Register object() const { + return object_; + } + Register temp() const { + return temp_; + } + Register tempToUnboxIndex() const { + return tempToUnboxIndex_; + } + FloatRegister tempDouble() const { + return tempDouble_; + } + FloatRegister tempFloat32() const { + return tempFloat32_; + } + ConstantOrRegister id() const { + return id_; + } + ConstantOrRegister value() const { + return value_; + } + bool strict() const { + return strict_; + } + bool needsTypeBarrier() const { + return needsTypeBarrier_; + } + bool guardHoles() const { + return guardHoles_; + } + bool hasGenericProxyStub() const { + return hasGenericProxyStub_; + } + + bool hasDenseStub() const { + return hasDenseStub_; + } + void setHasDenseStub() { + MOZ_ASSERT(!hasDenseStub()); + hasDenseStub_ = true; + } + + enum NativeSetPropCacheability { + CanAttachNone, + CanAttachSetSlot, + MaybeCanAttachAddSlot, + CanAttachCallSetter + }; + + MOZ_MUST_USE bool attachSetSlot(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleShape shape, bool checkTypeset); + + MOZ_MUST_USE bool attachCallSetter(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleObject holder, HandleShape shape, + void* returnAddr); + + MOZ_MUST_USE bool attachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, HandleShape oldShape, + HandleObjectGroup oldGroup, bool checkTypeset); + + MOZ_MUST_USE bool attachGenericProxy(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleId id, void* returnAddr); + + MOZ_MUST_USE bool attachDOMProxyShadowed(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, HandleId id, + void* returnAddr); + + MOZ_MUST_USE bool attachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, HandleId id, + void* returnAddr); + + static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, + HandleObject obj, HandleValue idval, HandleValue value); + + MOZ_MUST_USE bool tryAttachNative(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, bool* emitted, + bool* tryNativeAddSlot); + + MOZ_MUST_USE bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, bool* emitted); + + MOZ_MUST_USE bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, HandleId id, + bool* emitted); + + MOZ_MUST_USE bool tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, bool* emitted); + + MOZ_MUST_USE bool tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleValue idval, HandleValue value, + MutableHandleId id, bool* emitted, bool* tryNativeAddSlot); + + MOZ_MUST_USE bool tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, HandleId id, HandleObjectGroup oldGroup, + HandleShape oldShape, bool tryNativeAddSlot, bool* emitted); + + MOZ_MUST_USE bool tryAttachDenseElement(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject obj, const Value& idval, bool* emitted); + + MOZ_MUST_USE bool tryAttachTypedArrayElement(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject obj, + HandleValue idval, HandleValue val, bool* emitted); +}; + +class BindNameIC : public IonCache +{ + protected: + Register environmentChain_; + PropertyName* name_; + Register output_; + + public: + BindNameIC(Register envChain, PropertyName* name, Register output) + : environmentChain_(envChain), + name_(name), + output_(output) + { + } + + CACHE_HEADER(BindName) + + Register environmentChainReg() const { + return environmentChain_; + } + HandlePropertyName name() const { + return HandlePropertyName::fromMarkedLocation(&name_); + } + Register outputReg() const { + return output_; + } + + MOZ_MUST_USE bool attachGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject envChain); + + MOZ_MUST_USE bool attachNonGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject envChain, HandleObject holder); + + static JSObject* + update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain); +}; + +class NameIC : public IonCache +{ + protected: + // Registers live after the cache, excluding output registers. The initial + // value of these registers must be preserved by the cache. + LiveRegisterSet liveRegs_; + + bool typeOf_; + Register environmentChain_; + PropertyName* name_; + TypedOrValueRegister output_; + + public: + NameIC(LiveRegisterSet liveRegs, bool typeOf, + Register envChain, PropertyName* name, + TypedOrValueRegister output) + : liveRegs_(liveRegs), + typeOf_(typeOf), + environmentChain_(envChain), + name_(name), + output_(output) + { + } + + CACHE_HEADER(Name) + + Register environmentChainReg() const { + return environmentChain_; + } + HandlePropertyName name() const { + return HandlePropertyName::fromMarkedLocation(&name_); + } + TypedOrValueRegister outputReg() const { + return output_; + } + bool isTypeOf() const { + return typeOf_; + } + + MOZ_MUST_USE bool attachReadSlot(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject envChain, HandleObject holderBase, + HandleNativeObject holder, HandleShape shape); + + MOZ_MUST_USE bool attachCallGetter(JSContext* cx, HandleScript outerScript, IonScript* ion, + HandleObject envChain, HandleObject obj, + HandleObject holder, HandleShape shape, + void* returnAddr); + + MOZ_MUST_USE bool attachTypeOfNoProperty(JSContext* cx, HandleScript outerScript, + IonScript* ion, HandleObject envChain); + + static MOZ_MUST_USE bool + update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain, + MutableHandleValue vp); +}; + +#undef CACHE_HEADER + +// Implement cache casts now that the compiler can see the inheritance. +#define CACHE_CASTS(ickind) \ + ickind##IC& IonCache::to##ickind() \ + { \ + MOZ_ASSERT(is##ickind()); \ + return *static_cast<ickind##IC*>(this); \ + } \ + const ickind##IC& IonCache::to##ickind() const \ + { \ + MOZ_ASSERT(is##ickind()); \ + return *static_cast<const ickind##IC*>(this); \ + } +IONCACHE_KIND_LIST(CACHE_CASTS) +#undef OPCODE_CASTS + +bool IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder); +bool IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder, Shape* shape); + +} // namespace jit +} // namespace js + +#endif /* jit_IonCaches_h */ |