michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jit_IonCaches_h michael@0: #define jit_IonCaches_h michael@0: michael@0: #if defined(JS_CODEGEN_ARM) michael@0: # include "jit/arm/Assembler-arm.h" michael@0: #elif defined(JS_CODEGEN_MIPS) michael@0: # include "jit/mips/Assembler-mips.h" michael@0: #endif michael@0: #include "jit/Registers.h" michael@0: #include "jit/shared/Assembler-shared.h" michael@0: michael@0: namespace js { michael@0: michael@0: class LockedJSContext; michael@0: class TypedArrayObject; michael@0: michael@0: namespace jit { michael@0: michael@0: #define IONCACHE_KIND_LIST(_) \ michael@0: _(GetProperty) \ michael@0: _(SetProperty) \ michael@0: _(GetElement) \ michael@0: _(SetElement) \ michael@0: _(BindName) \ michael@0: _(Name) \ michael@0: _(CallsiteClone) \ michael@0: _(GetPropertyPar) \ michael@0: _(GetElementPar) \ michael@0: _(SetPropertyPar) \ michael@0: _(SetElementPar) michael@0: michael@0: // Forward declarations of Cache kinds. michael@0: #define FORWARD_DECLARE(kind) class kind##IC; michael@0: IONCACHE_KIND_LIST(FORWARD_DECLARE) michael@0: #undef FORWARD_DECLARE michael@0: michael@0: class IonCacheVisitor michael@0: { michael@0: public: michael@0: #define VISIT_INS(op) \ michael@0: virtual bool visit##op##IC(CodeGenerator *codegen) { \ michael@0: MOZ_ASSUME_UNREACHABLE("NYI: " #op "IC"); \ michael@0: } michael@0: michael@0: IONCACHE_KIND_LIST(VISIT_INS) michael@0: #undef VISIT_INS michael@0: }; michael@0: michael@0: // Common shared temporary state needed during codegen between the different michael@0: // kinds of caches. Used by OutOfLineUpdateCache. michael@0: struct AddCacheState michael@0: { michael@0: RepatchLabel repatchEntry; michael@0: Register dispatchScratch; michael@0: }; michael@0: michael@0: michael@0: // Common structure encoding the state of a polymorphic inline cache contained michael@0: // in the code for an IonScript. IonCaches are used for polymorphic operations michael@0: // where multiple implementations may be required. michael@0: // michael@0: // Roughly speaking, the cache initially jumps to an out of line fragment michael@0: // which invokes a cache function to perform the operation. The cache function michael@0: // may generate a stub to perform the operation in certain cases (e.g. a michael@0: // particular shape for an input object) and attach the stub to existing michael@0: // stubs, forming a daisy chain of tests for how to perform the operation in michael@0: // different circumstances. The details of how stubs are linked up as michael@0: // described in comments below for the classes RepatchIonCache and michael@0: // DispatchIonCache. michael@0: // michael@0: // Eventually, if too many stubs are generated the cache function may disable michael@0: // the cache, by generating a stub to make a call and perform the operation michael@0: // within the VM. michael@0: // michael@0: // While calls may be made to the cache function and other VM functions, the michael@0: // cache may still be treated as pure during optimization passes, such that michael@0: // LICM and GVN may be performed on operations around the cache as if the michael@0: // operation cannot reenter scripted code through an Invoke() or otherwise have michael@0: // unexpected behavior. This restricts the sorts of stubs which the cache can michael@0: // generate or the behaviors which called functions can have, and if a called michael@0: // function performs a possibly impure operation then the operation will be michael@0: // marked as such and the calling script will be recompiled. michael@0: // michael@0: // Similarly, despite the presence of functions and multiple stubs generated michael@0: // for a cache, the cache itself may be marked as idempotent and become hoisted michael@0: // or coalesced by LICM or GVN. This also constrains the stubs which can be michael@0: // generated for the cache. michael@0: // michael@0: // * IonCache usage michael@0: // michael@0: // IonCache is the base structure of an inline cache, which generates code stubs michael@0: // dynamically and attaches them to an IonScript. michael@0: // michael@0: // A cache must at least provide a static update function which will usualy have michael@0: // a JSContext*, followed by the cache index. The rest of the arguments of the michael@0: // update function are usualy corresponding to the register inputs of the cache, michael@0: // as it must perform the same operation as any of the stubs that it might michael@0: // produce. The update function call is handled by the visit function of michael@0: // CodeGenerator corresponding to this IC. michael@0: // michael@0: // The CodeGenerator visit function, as opposed to other visit functions, has michael@0: // two arguments. The first one is the OutOfLineUpdateCache which stores the LIR michael@0: // instruction. The second one is the IC object. This function would be called michael@0: // once the IC is registered with the addCache function of CodeGeneratorShared. michael@0: // michael@0: // To register a cache, you must call the addCache function as follow: michael@0: // michael@0: // MyCodeIC cache(inputReg1, inputValueReg2, outputReg); michael@0: // if (!addCache(lir, allocateCache(cache))) michael@0: // return false; michael@0: // michael@0: // Once the cache is allocated with the allocateCache function, any modification michael@0: // made to the cache would be ignored. michael@0: // michael@0: // The addCache function will produce a patchable jump at the location where michael@0: // it is called. This jump will execute generated stubs and fallback on the code michael@0: // of the visitMyCodeIC function if no stub match. michael@0: // michael@0: // Warning: As the addCache function fallback on a VMCall, calls to michael@0: // addCache should not be in the same path as another VMCall or in the same michael@0: // path of another addCache as this is not supported by the invalidation michael@0: // procedure. michael@0: class IonCache michael@0: { michael@0: public: michael@0: class StubAttacher; michael@0: michael@0: enum Kind { michael@0: # define DEFINE_CACHEKINDS(ickind) Cache_##ickind, michael@0: IONCACHE_KIND_LIST(DEFINE_CACHEKINDS) michael@0: # undef DEFINE_CACHEKINDS michael@0: Cache_Invalid michael@0: }; michael@0: michael@0: // Cache testing and cast. michael@0: # define CACHEKIND_CASTS(ickind) \ michael@0: bool is##ickind() const { \ michael@0: return kind() == Cache_##ickind; \ michael@0: } \ michael@0: inline ickind##IC &to##ickind(); \ michael@0: inline const ickind##IC &to##ickind() const; michael@0: IONCACHE_KIND_LIST(CACHEKIND_CASTS) michael@0: # undef CACHEKIND_CASTS michael@0: michael@0: virtual Kind kind() const = 0; michael@0: michael@0: virtual bool accept(CodeGenerator *codegen, IonCacheVisitor *visitor) = 0; michael@0: michael@0: public: michael@0: michael@0: static const char *CacheName(Kind kind); michael@0: michael@0: protected: michael@0: bool pure_ : 1; michael@0: bool idempotent_ : 1; michael@0: bool disabled_ : 1; michael@0: size_t stubCount_ : 5; michael@0: michael@0: CodeLocationLabel fallbackLabel_; michael@0: michael@0: // Location of this operation, nullptr for idempotent caches. michael@0: JSScript *script_; michael@0: jsbytecode *pc_; michael@0: michael@0: private: michael@0: static const size_t MAX_STUBS; michael@0: void incrementStubCount() { michael@0: // The IC should stop generating stubs before wrapping stubCount. michael@0: stubCount_++; michael@0: JS_ASSERT(stubCount_); michael@0: } michael@0: michael@0: public: michael@0: michael@0: IonCache() michael@0: : pure_(false), michael@0: idempotent_(false), michael@0: disabled_(false), michael@0: stubCount_(0), michael@0: fallbackLabel_(), michael@0: script_(nullptr), michael@0: pc_(nullptr) michael@0: { michael@0: } michael@0: michael@0: virtual void disable(); michael@0: inline bool isDisabled() const { michael@0: return disabled_; michael@0: } michael@0: michael@0: // Set the initial 'out-of-line' jump state of the cache. The fallbackLabel is michael@0: // the location of the out-of-line update (slow) path. This location will michael@0: // be set to the exitJump of the last generated stub. michael@0: void setFallbackLabel(CodeOffsetLabel fallbackLabel) { michael@0: fallbackLabel_ = fallbackLabel; michael@0: } michael@0: michael@0: virtual void emitInitialJump(MacroAssembler &masm, AddCacheState &addState) = 0; michael@0: virtual void bindInitialJump(MacroAssembler &masm, AddCacheState &addState) = 0; michael@0: virtual void updateBaseAddress(JitCode *code, MacroAssembler &masm); michael@0: michael@0: // Initialize the AddCacheState depending on the kind of cache, like michael@0: // setting a scratch register. Defaults to doing nothing. michael@0: virtual void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); michael@0: michael@0: // Reset the cache around garbage collection. michael@0: virtual void reset(); michael@0: michael@0: // Destroy any extra resources the cache uses upon IonScript finalization. michael@0: virtual void destroy(); michael@0: michael@0: bool canAttachStub() const { michael@0: return stubCount_ < MAX_STUBS; michael@0: } michael@0: bool empty() const { michael@0: return stubCount_ == 0; michael@0: } michael@0: michael@0: enum LinkStatus { michael@0: LINK_ERROR, michael@0: CACHE_FLUSHED, michael@0: LINK_GOOD michael@0: }; michael@0: michael@0: // Use the Linker to link the generated code and check if any michael@0: // monitoring/allocation caused an invalidation of the running ion script, michael@0: // this function returns CACHE_FLUSHED. In case of allocation issue this michael@0: // function returns LINK_ERROR. michael@0: LinkStatus linkCode(JSContext *cx, MacroAssembler &masm, IonScript *ion, JitCode **code); michael@0: // Fixup variables and update jumps in the list of stubs. Increment the michael@0: // number of attached stubs accordingly. michael@0: void attachStub(MacroAssembler &masm, StubAttacher &attacher, Handle code); michael@0: michael@0: // Combine both linkStub and attachStub into one function. In addition, it michael@0: // produces a spew augmented with the attachKind string. michael@0: bool linkAndAttachStub(JSContext *cx, MacroAssembler &masm, StubAttacher &attacher, michael@0: IonScript *ion, const char *attachKind); michael@0: michael@0: #ifdef DEBUG michael@0: bool isAllocated() { michael@0: return fallbackLabel_.isSet(); michael@0: } michael@0: #endif michael@0: michael@0: bool pure() const { michael@0: return pure_; michael@0: } michael@0: bool idempotent() const { michael@0: return idempotent_; michael@0: } michael@0: void setIdempotent() { michael@0: JS_ASSERT(!idempotent_); michael@0: JS_ASSERT(!script_); michael@0: JS_ASSERT(!pc_); michael@0: idempotent_ = true; michael@0: } michael@0: michael@0: void setScriptedLocation(JSScript *script, jsbytecode *pc) { michael@0: JS_ASSERT(!idempotent_); michael@0: script_ = script; michael@0: pc_ = pc; michael@0: } michael@0: michael@0: void getScriptedLocation(MutableHandleScript pscript, jsbytecode **ppc) const { michael@0: pscript.set(script_); michael@0: *ppc = pc_; michael@0: } michael@0: michael@0: jsbytecode *pc() const { michael@0: JS_ASSERT(pc_); michael@0: return pc_; michael@0: } michael@0: }; michael@0: michael@0: // michael@0: // Repatch caches initially generate a patchable jump to an out of line call michael@0: // to the cache function. Stubs are attached by appending: when attaching a michael@0: // new stub, we patch the any failure conditions in last generated stub to michael@0: // jump to the new stub. Failure conditions in the new stub jump to the cache michael@0: // function which may generate new stubs. michael@0: // michael@0: // Control flow Pointers michael@0: // =======# ----. .----> michael@0: // # | | michael@0: // #======> \-----/ michael@0: // michael@0: // Initial state: michael@0: // michael@0: // JIT Code michael@0: // +--------+ .---------------. michael@0: // | | | | michael@0: // |========| v +----------+ | michael@0: // |== IC ==|====>| Cache Fn | | michael@0: // |========| +----------+ | michael@0: // | |<=# # | michael@0: // | | #=======# | michael@0: // +--------+ Rejoin path | michael@0: // |________ | michael@0: // | | michael@0: // Repatch | | michael@0: // IC | | michael@0: // Entry | | michael@0: // +------------+ | michael@0: // | lastJump_ |---------------/ michael@0: // +------------+ michael@0: // | ... | michael@0: // +------------+ michael@0: // michael@0: // Attaching stubs: michael@0: // michael@0: // Patch the jump pointed to by lastJump_ to jump to the new stub. Update michael@0: // lastJump_ to be the new stub's failure jump. The failure jump of the new michael@0: // stub goes to the fallback label, which is the cache function. In this michael@0: // fashion, new stubs are _appended_ to the chain of stubs, as lastJump_ michael@0: // points to the _tail_ of the stub chain. michael@0: // michael@0: // JIT Code michael@0: // +--------+ #=======================# michael@0: // | | # v michael@0: // |========| # +----------+ +------+ michael@0: // |== IC ==|=# | Cache Fn |<====| Stub | michael@0: // |========| +----------+ ^ +------+ michael@0: // | |<=# # | # michael@0: // | | #======#=========|=====# michael@0: // +--------+ Rejoin path | michael@0: // |________ | michael@0: // | | michael@0: // Repatch | | michael@0: // IC | | michael@0: // Entry | | michael@0: // +------------+ | michael@0: // | lastJump_ |---------------/ michael@0: // +------------+ michael@0: // | ... | michael@0: // +------------+ michael@0: // michael@0: class RepatchIonCache : public IonCache michael@0: { michael@0: protected: michael@0: class RepatchStubAppender; michael@0: michael@0: CodeLocationJump initialJump_; michael@0: CodeLocationJump lastJump_; michael@0: michael@0: // Offset from the initial jump to the rejoin label. michael@0: #ifdef JS_CODEGEN_ARM michael@0: static const size_t REJOIN_LABEL_OFFSET = 4; michael@0: #elif defined(JS_CODEGEN_MIPS) michael@0: // The size of jump created by MacroAssemblerMIPSCompat::jumpWithPatch. michael@0: static const size_t REJOIN_LABEL_OFFSET = 4 * sizeof(void *); michael@0: #else michael@0: static const size_t REJOIN_LABEL_OFFSET = 0; michael@0: #endif michael@0: michael@0: CodeLocationLabel rejoinLabel() const { michael@0: uint8_t *ptr = initialJump_.raw(); michael@0: #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) michael@0: uint32_t i = 0; michael@0: while (i < REJOIN_LABEL_OFFSET) michael@0: ptr = Assembler::nextInstruction(ptr, &i); michael@0: #endif michael@0: return CodeLocationLabel(ptr); michael@0: } michael@0: michael@0: public: michael@0: RepatchIonCache() michael@0: : initialJump_(), michael@0: lastJump_() michael@0: { michael@0: } michael@0: michael@0: virtual void reset(); michael@0: michael@0: // Set the initial jump state of the cache. The initialJump is the inline michael@0: // jump that will point to out-of-line code (such as the slow path, or michael@0: // stubs), and the rejoinLabel is the position that all out-of-line paths michael@0: // will rejoin to. michael@0: void emitInitialJump(MacroAssembler &masm, AddCacheState &addState); michael@0: void bindInitialJump(MacroAssembler &masm, AddCacheState &addState); michael@0: michael@0: // Update the labels once the code is finalized. michael@0: void updateBaseAddress(JitCode *code, MacroAssembler &masm); michael@0: }; michael@0: michael@0: // michael@0: // Dispatch caches avoid patching already-running code. Instead, the jump to michael@0: // the stub chain is indirect by way of the firstStub_ pointer michael@0: // below. Initially the pointer points to the cache function which may attach michael@0: // new stubs. Stubs are attached by prepending: when attaching a new stub, we michael@0: // jump to the previous stub on failure conditions, then overwrite the michael@0: // firstStub_ pointer with the newly generated stub. michael@0: // michael@0: // This style does not patch the already executing instruction stream, does michael@0: // not need to worry about cache coherence of cached jump addresses, and does michael@0: // not have to worry about aligning the exit jumps to ensure atomic patching, michael@0: // at the expense of an extra memory read to load the very first stub. michael@0: // michael@0: // ICs that need to work in parallel execution need to be dispatch style. michael@0: // michael@0: // Control flow Pointers Memory load michael@0: // =======# ----. .----> ****** michael@0: // # | | * michael@0: // #======> \-----/ ******* michael@0: // michael@0: // Initial state: michael@0: // michael@0: // The first stub points to the cache function. michael@0: // michael@0: // JIT Code michael@0: // +--------+ .-------. michael@0: // | | v | michael@0: // |========| +---------------+ +----------+ | michael@0: // |== IC ==|====>| Load and jump |====>| Cache Fn | | michael@0: // |========| +---------------+ +----------+ | michael@0: // | |<=# * # | michael@0: // | | #===========*================# | michael@0: // +--------+ Rejoin * path | michael@0: // |________ * | michael@0: // | * | michael@0: // Dispatch | * | michael@0: // IC **|************ | michael@0: // Entry * | | michael@0: // +------------+ | michael@0: // | firstStub_ |-------------------------------------/ michael@0: // +------------+ michael@0: // | ... | michael@0: // +------------+ michael@0: // michael@0: // Attaching stubs: michael@0: // michael@0: // Assign the address of the new stub to firstStub_. The new stub jumps to michael@0: // the old address held in firstStub_ on failure. Note that there is no michael@0: // concept of a fallback label here, new stubs are _prepended_, as michael@0: // firstStub_ always points to the _head_ of the stub chain. michael@0: // michael@0: // JIT Code michael@0: // +--------+ #=====================# .-----. michael@0: // | | # v v | michael@0: // |========| +---------------+ # +----------+ +------+ | michael@0: // |== IC ==|====>| Load and jump |==# | Cache Fn |<====| Stub | | michael@0: // |========| +---------------+ +----------+ +------+ | michael@0: // | |<=# * # # | michael@0: // | | #===========*================#================# | michael@0: // +--------+ Rejoin * path | michael@0: // |________ * | michael@0: // | * | michael@0: // Dispatch | * | michael@0: // IC **|************ | michael@0: // Entry * | | michael@0: // +------------+ | michael@0: // | firstStub_ |----------------------------------------------------/ michael@0: // +------------+ michael@0: // | ... | michael@0: // +------------+ michael@0: // michael@0: class DispatchIonCache : public IonCache michael@0: { michael@0: protected: michael@0: class DispatchStubPrepender; michael@0: michael@0: uint8_t *firstStub_; michael@0: CodeLocationLabel rejoinLabel_; michael@0: CodeOffsetLabel dispatchLabel_; michael@0: michael@0: public: michael@0: DispatchIonCache() michael@0: : firstStub_(nullptr), michael@0: rejoinLabel_(), michael@0: dispatchLabel_() michael@0: { michael@0: } michael@0: michael@0: virtual void reset(); michael@0: virtual void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); michael@0: michael@0: void emitInitialJump(MacroAssembler &masm, AddCacheState &addState); michael@0: void bindInitialJump(MacroAssembler &masm, AddCacheState &addState); michael@0: michael@0: // Fix up the first stub pointer once the code is finalized. michael@0: void updateBaseAddress(JitCode *code, MacroAssembler &masm); michael@0: }; michael@0: michael@0: // Define the cache kind and pre-declare data structures used for calling inline michael@0: // caches. michael@0: #define CACHE_HEADER(ickind) \ michael@0: Kind kind() const { \ michael@0: return IonCache::Cache_##ickind; \ michael@0: } \ michael@0: \ michael@0: bool accept(CodeGenerator *codegen, IonCacheVisitor *visitor) { \ michael@0: return visitor->visit##ickind##IC(codegen); \ michael@0: } \ michael@0: \ michael@0: static const VMFunction UpdateInfo; michael@0: michael@0: // Subclasses of IonCache for the various kinds of caches. These do not define michael@0: // new data members; all caches must be of the same size. michael@0: michael@0: // Helper for idempotent GetPropertyIC location tracking. Declared externally michael@0: // to be forward declarable. michael@0: // michael@0: // Since all the scripts stored in CacheLocations are guaranteed to have been michael@0: // Ion compiled, and are kept alive by function objects in jitcode, and since michael@0: // the CacheLocations only have the lifespan of the jitcode, there is no need michael@0: // to trace or mark any of the scripts. Since JSScripts are always allocated michael@0: // tenured, and never moved, we can keep raw pointers, and there is no need michael@0: // for HeapPtrScripts here. michael@0: struct CacheLocation { michael@0: jsbytecode *pc; michael@0: JSScript *script; michael@0: michael@0: CacheLocation(jsbytecode *pcin, JSScript *scriptin) michael@0: : pc(pcin), script(scriptin) michael@0: { } michael@0: }; michael@0: michael@0: class GetPropertyIC : public RepatchIonCache michael@0: { michael@0: protected: michael@0: // Registers live after the cache, excluding output registers. The initial michael@0: // value of these registers must be preserved by the cache. michael@0: RegisterSet liveRegs_; michael@0: michael@0: Register object_; michael@0: PropertyName *name_; michael@0: TypedOrValueRegister output_; michael@0: michael@0: // Only valid if idempotent michael@0: size_t locationsIndex_; michael@0: size_t numLocations_; michael@0: michael@0: bool monitoredResult_ : 1; michael@0: bool hasTypedArrayLengthStub_ : 1; michael@0: bool hasStrictArgumentsLengthStub_ : 1; michael@0: bool hasNormalArgumentsLengthStub_ : 1; michael@0: bool hasGenericProxyStub_ : 1; michael@0: michael@0: public: michael@0: GetPropertyIC(RegisterSet liveRegs, michael@0: Register object, PropertyName *name, michael@0: TypedOrValueRegister output, michael@0: bool monitoredResult) michael@0: : liveRegs_(liveRegs), michael@0: object_(object), michael@0: name_(name), michael@0: output_(output), michael@0: locationsIndex_(0), michael@0: numLocations_(0), michael@0: monitoredResult_(monitoredResult), michael@0: hasTypedArrayLengthStub_(false), michael@0: hasStrictArgumentsLengthStub_(false), michael@0: hasNormalArgumentsLengthStub_(false), michael@0: hasGenericProxyStub_(false) michael@0: { michael@0: } michael@0: michael@0: CACHE_HEADER(GetProperty) michael@0: michael@0: void reset(); michael@0: michael@0: Register object() const { michael@0: return object_; michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: TypedOrValueRegister output() const { michael@0: return output_; michael@0: } michael@0: bool monitoredResult() const { michael@0: return monitoredResult_; michael@0: } michael@0: bool hasTypedArrayLengthStub() const { michael@0: return hasTypedArrayLengthStub_; michael@0: } michael@0: bool hasArgumentsLengthStub(bool strict) const { michael@0: return strict ? hasStrictArgumentsLengthStub_ : hasNormalArgumentsLengthStub_; michael@0: } michael@0: bool hasGenericProxyStub() const { michael@0: return hasGenericProxyStub_; michael@0: } michael@0: michael@0: void setLocationInfo(size_t locationsIndex, size_t numLocations) { michael@0: JS_ASSERT(idempotent()); michael@0: JS_ASSERT(!numLocations_); michael@0: JS_ASSERT(numLocations); michael@0: locationsIndex_ = locationsIndex; michael@0: numLocations_ = numLocations; michael@0: } michael@0: void getLocationInfo(uint32_t *index, uint32_t *num) const { michael@0: JS_ASSERT(idempotent()); michael@0: *index = locationsIndex_; michael@0: *num = numLocations_; michael@0: } michael@0: michael@0: enum NativeGetPropCacheability { michael@0: CanAttachNone, michael@0: CanAttachReadSlot, michael@0: CanAttachArrayLength, michael@0: CanAttachCallGetter michael@0: }; michael@0: michael@0: // Helpers for CanAttachNativeGetProp michael@0: typedef JSContext * Context; michael@0: bool allowArrayLength(Context cx, HandleObject obj) const; michael@0: bool allowGetters() const { michael@0: return monitoredResult() && !idempotent(); michael@0: } michael@0: michael@0: // Attach the proper stub, if possible michael@0: bool tryAttachStub(JSContext *cx, IonScript *ion, HandleObject obj, michael@0: HandlePropertyName name, void *returnAddr, bool *emitted); michael@0: bool tryAttachProxy(JSContext *cx, IonScript *ion, HandleObject obj, michael@0: HandlePropertyName name, void *returnAddr, bool *emitted); michael@0: bool tryAttachGenericProxy(JSContext *cx, IonScript *ion, HandleObject obj, michael@0: HandlePropertyName name, void *returnAddr, bool *emitted); michael@0: bool tryAttachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObject obj, michael@0: void *returnAddr, bool *emitted); michael@0: bool tryAttachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj, michael@0: HandlePropertyName name, bool resetNeeded, michael@0: void *returnAddr, bool *emitted); michael@0: bool tryAttachNative(JSContext *cx, IonScript *ion, HandleObject obj, michael@0: HandlePropertyName name, void *returnAddr, bool *emitted); michael@0: bool tryAttachTypedArrayLength(JSContext *cx, IonScript *ion, HandleObject obj, michael@0: HandlePropertyName name, bool *emitted); michael@0: michael@0: bool tryAttachArgumentsLength(JSContext *cx, IonScript *ion, HandleObject obj, michael@0: HandlePropertyName name, bool *emitted); michael@0: michael@0: static bool update(JSContext *cx, size_t cacheIndex, HandleObject obj, MutableHandleValue vp); michael@0: }; michael@0: michael@0: class SetPropertyIC : public RepatchIonCache michael@0: { michael@0: protected: michael@0: // Registers live after the cache, excluding output registers. The initial michael@0: // value of these registers must be preserved by the cache. michael@0: RegisterSet liveRegs_; michael@0: michael@0: Register object_; michael@0: PropertyName *name_; michael@0: ConstantOrRegister value_; michael@0: bool strict_; michael@0: bool needsTypeBarrier_; michael@0: michael@0: bool hasGenericProxyStub_; michael@0: michael@0: public: michael@0: SetPropertyIC(RegisterSet liveRegs, Register object, PropertyName *name, michael@0: ConstantOrRegister value, bool strict, bool needsTypeBarrier) michael@0: : liveRegs_(liveRegs), michael@0: object_(object), michael@0: name_(name), michael@0: value_(value), michael@0: strict_(strict), michael@0: needsTypeBarrier_(needsTypeBarrier), michael@0: hasGenericProxyStub_(false) michael@0: { michael@0: } michael@0: michael@0: CACHE_HEADER(SetProperty) michael@0: michael@0: void reset(); michael@0: michael@0: Register object() const { michael@0: return object_; michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: ConstantOrRegister value() const { michael@0: return value_; michael@0: } michael@0: bool strict() const { michael@0: return strict_; michael@0: } michael@0: bool needsTypeBarrier() const { michael@0: return needsTypeBarrier_; michael@0: } michael@0: bool hasGenericProxyStub() const { michael@0: return hasGenericProxyStub_; michael@0: } michael@0: michael@0: enum NativeSetPropCacheability { michael@0: CanAttachNone, michael@0: CanAttachSetSlot, michael@0: MaybeCanAttachAddSlot, michael@0: CanAttachCallSetter michael@0: }; michael@0: michael@0: bool attachSetSlot(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape, michael@0: bool checkTypeset); michael@0: bool attachCallSetter(JSContext *cx, IonScript *ion, HandleObject obj, michael@0: HandleObject holder, HandleShape shape, void *returnAddr); michael@0: bool attachAddSlot(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldShape, michael@0: bool checkTypeset); michael@0: bool attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAddr); michael@0: bool attachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObject obj, michael@0: void *returnAddr); michael@0: bool attachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj, michael@0: void *returnAddr); michael@0: michael@0: static bool michael@0: update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value); michael@0: }; michael@0: michael@0: class GetElementIC : public RepatchIonCache michael@0: { michael@0: protected: michael@0: RegisterSet liveRegs_; michael@0: michael@0: Register object_; michael@0: ConstantOrRegister index_; michael@0: TypedOrValueRegister output_; michael@0: michael@0: bool monitoredResult_ : 1; michael@0: bool allowDoubleResult_ : 1; michael@0: bool hasDenseStub_ : 1; michael@0: bool hasStrictArgumentsStub_ : 1; michael@0: bool hasNormalArgumentsStub_ : 1; michael@0: michael@0: size_t failedUpdates_; michael@0: michael@0: static const size_t MAX_FAILED_UPDATES; michael@0: michael@0: public: michael@0: GetElementIC(RegisterSet liveRegs, Register object, ConstantOrRegister index, michael@0: TypedOrValueRegister output, bool monitoredResult, bool allowDoubleResult) michael@0: : liveRegs_(liveRegs), michael@0: object_(object), michael@0: index_(index), michael@0: output_(output), michael@0: monitoredResult_(monitoredResult), michael@0: allowDoubleResult_(allowDoubleResult), michael@0: hasDenseStub_(false), michael@0: hasStrictArgumentsStub_(false), michael@0: hasNormalArgumentsStub_(false), michael@0: failedUpdates_(0) michael@0: { michael@0: } michael@0: michael@0: CACHE_HEADER(GetElement) michael@0: michael@0: void reset(); michael@0: michael@0: Register object() const { michael@0: return object_; michael@0: } michael@0: ConstantOrRegister index() const { michael@0: return index_; michael@0: } michael@0: TypedOrValueRegister output() const { michael@0: return output_; michael@0: } michael@0: bool monitoredResult() const { michael@0: return monitoredResult_; michael@0: } michael@0: bool allowDoubleResult() const { michael@0: return allowDoubleResult_; michael@0: } michael@0: bool hasDenseStub() const { michael@0: return hasDenseStub_; michael@0: } michael@0: bool hasArgumentsStub(bool strict) const { michael@0: return strict ? hasStrictArgumentsStub_ : hasNormalArgumentsStub_; michael@0: } michael@0: void setHasDenseStub() { michael@0: JS_ASSERT(!hasDenseStub()); michael@0: hasDenseStub_ = true; michael@0: } michael@0: michael@0: // Helpers for CanAttachNativeGetProp michael@0: typedef JSContext * Context; michael@0: bool allowGetters() const { JS_ASSERT(!idempotent()); return true; } michael@0: bool allowArrayLength(Context, HandleObject) const { return false; } michael@0: bool canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const { michael@0: return monitoredResult(); michael@0: } michael@0: michael@0: static bool canAttachGetProp(JSObject *obj, const Value &idval, jsid id); michael@0: static bool canAttachDenseElement(JSObject *obj, const Value &idval); michael@0: static bool canAttachTypedArrayElement(JSObject *obj, const Value &idval, michael@0: TypedOrValueRegister output); michael@0: michael@0: bool attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj, const Value &idval, michael@0: HandlePropertyName name, void *returnAddr); michael@0: bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval); michael@0: bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr, michael@0: const Value &idval); michael@0: bool attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *obj); michael@0: michael@0: static bool michael@0: update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, michael@0: MutableHandleValue vp); michael@0: michael@0: void incFailedUpdates() { michael@0: failedUpdates_++; michael@0: } michael@0: void resetFailedUpdates() { michael@0: failedUpdates_ = 0; michael@0: } michael@0: bool shouldDisable() const { michael@0: return !canAttachStub() || michael@0: (stubCount_ == 0 && failedUpdates_ > MAX_FAILED_UPDATES); michael@0: } michael@0: }; michael@0: michael@0: class SetElementIC : public RepatchIonCache michael@0: { michael@0: protected: michael@0: Register object_; michael@0: Register tempToUnboxIndex_; michael@0: Register temp_; michael@0: FloatRegister tempFloat_; michael@0: ValueOperand index_; michael@0: ConstantOrRegister value_; michael@0: bool strict_; michael@0: bool guardHoles_; michael@0: michael@0: bool hasDenseStub_ : 1; michael@0: michael@0: public: michael@0: SetElementIC(Register object, Register tempToUnboxIndex, Register temp, michael@0: FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value, michael@0: bool strict, bool guardHoles) michael@0: : object_(object), michael@0: tempToUnboxIndex_(tempToUnboxIndex), michael@0: temp_(temp), michael@0: tempFloat_(tempFloat), michael@0: index_(index), michael@0: value_(value), michael@0: strict_(strict), michael@0: guardHoles_(guardHoles), michael@0: hasDenseStub_(false) michael@0: { michael@0: } michael@0: michael@0: CACHE_HEADER(SetElement) michael@0: michael@0: void reset(); michael@0: michael@0: Register object() const { michael@0: return object_; michael@0: } michael@0: Register tempToUnboxIndex() const { michael@0: return tempToUnboxIndex_; michael@0: } michael@0: Register temp() const { michael@0: return temp_; michael@0: } michael@0: FloatRegister tempFloat() const { michael@0: return tempFloat_; michael@0: } michael@0: ValueOperand index() const { michael@0: return index_; michael@0: } michael@0: ConstantOrRegister value() const { michael@0: return value_; michael@0: } michael@0: bool strict() const { michael@0: return strict_; michael@0: } michael@0: bool guardHoles() const { michael@0: return guardHoles_; michael@0: } michael@0: michael@0: bool hasDenseStub() const { michael@0: return hasDenseStub_; michael@0: } michael@0: void setHasDenseStub() { michael@0: JS_ASSERT(!hasDenseStub()); michael@0: hasDenseStub_ = true; michael@0: } michael@0: michael@0: bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval); michael@0: bool attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr); michael@0: michael@0: static bool michael@0: update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, michael@0: HandleValue value); michael@0: }; michael@0: michael@0: class BindNameIC : public RepatchIonCache michael@0: { michael@0: protected: michael@0: Register scopeChain_; michael@0: PropertyName *name_; michael@0: Register output_; michael@0: michael@0: public: michael@0: BindNameIC(Register scopeChain, PropertyName *name, Register output) michael@0: : scopeChain_(scopeChain), michael@0: name_(name), michael@0: output_(output) michael@0: { michael@0: } michael@0: michael@0: CACHE_HEADER(BindName) michael@0: michael@0: Register scopeChainReg() const { michael@0: return scopeChain_; michael@0: } michael@0: HandlePropertyName name() const { michael@0: return HandlePropertyName::fromMarkedLocation(&name_); michael@0: } michael@0: Register outputReg() const { michael@0: return output_; michael@0: } michael@0: michael@0: bool attachGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain); michael@0: bool attachNonGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain, JSObject *holder); michael@0: michael@0: static JSObject * michael@0: update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain); michael@0: }; michael@0: michael@0: class NameIC : public RepatchIonCache michael@0: { michael@0: protected: michael@0: // Registers live after the cache, excluding output registers. The initial michael@0: // value of these registers must be preserved by the cache. michael@0: RegisterSet liveRegs_; michael@0: michael@0: bool typeOf_; michael@0: Register scopeChain_; michael@0: PropertyName *name_; michael@0: TypedOrValueRegister output_; michael@0: michael@0: public: michael@0: NameIC(RegisterSet liveRegs, bool typeOf, michael@0: Register scopeChain, PropertyName *name, michael@0: TypedOrValueRegister output) michael@0: : liveRegs_(liveRegs), michael@0: typeOf_(typeOf), michael@0: scopeChain_(scopeChain), michael@0: name_(name), michael@0: output_(output) michael@0: { michael@0: } michael@0: michael@0: CACHE_HEADER(Name) michael@0: michael@0: Register scopeChainReg() const { michael@0: return scopeChain_; michael@0: } michael@0: HandlePropertyName name() const { michael@0: return HandlePropertyName::fromMarkedLocation(&name_); michael@0: } michael@0: TypedOrValueRegister outputReg() const { michael@0: return output_; michael@0: } michael@0: bool isTypeOf() const { michael@0: return typeOf_; michael@0: } michael@0: michael@0: bool attachReadSlot(JSContext *cx, IonScript *ion, HandleObject scopeChain, michael@0: HandleObject holderBase, HandleObject holder, HandleShape shape); michael@0: bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, michael@0: HandleShape shape, void *returnAddr); michael@0: michael@0: static bool michael@0: update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain, MutableHandleValue vp); michael@0: }; michael@0: michael@0: class CallsiteCloneIC : public RepatchIonCache michael@0: { michael@0: protected: michael@0: Register callee_; michael@0: Register output_; michael@0: JSScript *callScript_; michael@0: jsbytecode *callPc_; michael@0: michael@0: public: michael@0: CallsiteCloneIC(Register callee, JSScript *callScript, jsbytecode *callPc, Register output) michael@0: : callee_(callee), michael@0: output_(output), michael@0: callScript_(callScript), michael@0: callPc_(callPc) michael@0: { michael@0: } michael@0: michael@0: CACHE_HEADER(CallsiteClone) michael@0: michael@0: Register calleeReg() const { michael@0: return callee_; michael@0: } michael@0: HandleScript callScript() const { michael@0: return HandleScript::fromMarkedLocation(&callScript_); michael@0: } michael@0: jsbytecode *callPc() const { michael@0: return callPc_; michael@0: } michael@0: Register outputReg() const { michael@0: return output_; michael@0: } michael@0: michael@0: bool attach(JSContext *cx, IonScript *ion, HandleFunction original, HandleFunction clone); michael@0: michael@0: static JSObject *update(JSContext *cx, size_t cacheIndex, HandleObject callee); michael@0: }; michael@0: michael@0: class ParallelIonCache : public DispatchIonCache michael@0: { michael@0: protected: michael@0: // A set of all objects that are stubbed. Used to detect duplicates in michael@0: // parallel execution. michael@0: ShapeSet *stubbedShapes_; michael@0: michael@0: ParallelIonCache() michael@0: : stubbedShapes_(nullptr) michael@0: { michael@0: } michael@0: michael@0: bool initStubbedShapes(JSContext *cx); michael@0: michael@0: public: michael@0: void reset(); michael@0: void destroy(); michael@0: michael@0: bool hasOrAddStubbedShape(LockedJSContext &cx, Shape *shape, bool *alreadyStubbed); michael@0: }; michael@0: michael@0: class GetPropertyParIC : public ParallelIonCache michael@0: { michael@0: protected: michael@0: Register object_; michael@0: PropertyName *name_; michael@0: TypedOrValueRegister output_; michael@0: bool hasTypedArrayLengthStub_ : 1; michael@0: michael@0: public: michael@0: GetPropertyParIC(Register object, PropertyName *name, TypedOrValueRegister output) michael@0: : object_(object), michael@0: name_(name), michael@0: output_(output), michael@0: hasTypedArrayLengthStub_(false) michael@0: { michael@0: } michael@0: michael@0: CACHE_HEADER(GetPropertyPar) michael@0: michael@0: #ifdef JS_CODEGEN_X86 michael@0: // x86 lacks a general purpose scratch register for dispatch caches and michael@0: // must be given one manually. michael@0: void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); michael@0: #endif michael@0: michael@0: void reset(); michael@0: michael@0: Register object() const { michael@0: return object_; michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: TypedOrValueRegister output() const { michael@0: return output_; michael@0: } michael@0: bool hasTypedArrayLengthStub() const { michael@0: return hasTypedArrayLengthStub_; michael@0: } michael@0: michael@0: // CanAttachNativeGetProp Helpers michael@0: typedef LockedJSContext & Context; michael@0: bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; } michael@0: bool allowGetters() const { return false; } michael@0: bool allowArrayLength(Context, HandleObject) const { return true; } michael@0: michael@0: bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, JSObject *holder, michael@0: Shape *shape); michael@0: bool attachArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj); michael@0: bool attachTypedArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj); michael@0: michael@0: static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, michael@0: MutableHandleValue vp); michael@0: }; michael@0: michael@0: class GetElementParIC : public ParallelIonCache michael@0: { michael@0: protected: michael@0: Register object_; michael@0: ConstantOrRegister index_; michael@0: TypedOrValueRegister output_; michael@0: michael@0: bool monitoredResult_ : 1; michael@0: bool allowDoubleResult_ : 1; michael@0: michael@0: public: michael@0: GetElementParIC(Register object, ConstantOrRegister index, michael@0: TypedOrValueRegister output, bool monitoredResult, bool allowDoubleResult) michael@0: : object_(object), michael@0: index_(index), michael@0: output_(output), michael@0: monitoredResult_(monitoredResult), michael@0: allowDoubleResult_(allowDoubleResult) michael@0: { michael@0: } michael@0: michael@0: CACHE_HEADER(GetElementPar) michael@0: michael@0: #ifdef JS_CODEGEN_X86 michael@0: // x86 lacks a general purpose scratch register for dispatch caches and michael@0: // must be given one manually. michael@0: void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); michael@0: #endif michael@0: michael@0: Register object() const { michael@0: return object_; michael@0: } michael@0: ConstantOrRegister index() const { michael@0: return index_; michael@0: } michael@0: TypedOrValueRegister output() const { michael@0: return output_; michael@0: } michael@0: bool monitoredResult() const { michael@0: return monitoredResult_; michael@0: } michael@0: bool allowDoubleResult() const { michael@0: return allowDoubleResult_; michael@0: } michael@0: michael@0: // CanAttachNativeGetProp Helpers michael@0: typedef LockedJSContext & Context; michael@0: bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; } michael@0: bool allowGetters() const { return false; } michael@0: bool allowArrayLength(Context, HandleObject) const { return false; } michael@0: michael@0: bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval, michael@0: PropertyName *name, JSObject *holder, Shape *shape); michael@0: bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval); michael@0: bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr, michael@0: const Value &idval); michael@0: michael@0: static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, michael@0: MutableHandleValue vp); michael@0: michael@0: }; michael@0: michael@0: class SetPropertyParIC : public ParallelIonCache michael@0: { michael@0: protected: michael@0: Register object_; michael@0: PropertyName *name_; michael@0: ConstantOrRegister value_; michael@0: bool strict_; michael@0: bool needsTypeBarrier_; michael@0: michael@0: public: michael@0: SetPropertyParIC(Register object, PropertyName *name, ConstantOrRegister value, michael@0: bool strict, bool needsTypeBarrier) michael@0: : object_(object), michael@0: name_(name), michael@0: value_(value), michael@0: strict_(strict), michael@0: needsTypeBarrier_(needsTypeBarrier) michael@0: { michael@0: } michael@0: michael@0: CACHE_HEADER(SetPropertyPar) michael@0: michael@0: #ifdef JS_CODEGEN_X86 michael@0: // x86 lacks a general purpose scratch register for dispatch caches and michael@0: // must be given one manually. michael@0: void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); michael@0: #endif michael@0: michael@0: Register object() const { michael@0: return object_; michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: ConstantOrRegister value() const { michael@0: return value_; michael@0: } michael@0: bool strict() const { michael@0: return strict_; michael@0: } michael@0: bool needsTypeBarrier() const { michael@0: return needsTypeBarrier_; michael@0: } michael@0: michael@0: bool attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape, michael@0: bool checkTypeset); michael@0: bool attachAddSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *oldShape, michael@0: bool checkTypeset); michael@0: michael@0: static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, michael@0: HandleValue value); michael@0: }; michael@0: michael@0: class SetElementParIC : public ParallelIonCache michael@0: { michael@0: protected: michael@0: Register object_; michael@0: Register tempToUnboxIndex_; michael@0: Register temp_; michael@0: FloatRegister tempFloat_; michael@0: ValueOperand index_; michael@0: ConstantOrRegister value_; michael@0: bool strict_; michael@0: bool guardHoles_; michael@0: michael@0: public: michael@0: SetElementParIC(Register object, Register tempToUnboxIndex, Register temp, michael@0: FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value, michael@0: bool strict, bool guardHoles) michael@0: : object_(object), michael@0: tempToUnboxIndex_(tempToUnboxIndex), michael@0: temp_(temp), michael@0: tempFloat_(tempFloat), michael@0: index_(index), michael@0: value_(value), michael@0: strict_(strict), michael@0: guardHoles_(guardHoles) michael@0: { michael@0: } michael@0: michael@0: CACHE_HEADER(SetElementPar) michael@0: michael@0: #ifdef JS_CODEGEN_X86 michael@0: // x86 lacks a general purpose scratch register for dispatch caches and michael@0: // must be given one manually. michael@0: void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); michael@0: #endif michael@0: michael@0: Register object() const { michael@0: return object_; michael@0: } michael@0: Register tempToUnboxIndex() const { michael@0: return tempToUnboxIndex_; michael@0: } michael@0: Register temp() const { michael@0: return temp_; michael@0: } michael@0: FloatRegister tempFloat() const { michael@0: return tempFloat_; michael@0: } michael@0: ValueOperand index() const { michael@0: return index_; michael@0: } michael@0: ConstantOrRegister value() const { michael@0: return value_; michael@0: } michael@0: bool strict() const { michael@0: return strict_; michael@0: } michael@0: bool guardHoles() const { michael@0: return guardHoles_; michael@0: } michael@0: michael@0: bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval); michael@0: bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr); michael@0: michael@0: static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, michael@0: HandleValue idval, HandleValue value); michael@0: }; michael@0: michael@0: #undef CACHE_HEADER michael@0: michael@0: // Implement cache casts now that the compiler can see the inheritance. michael@0: #define CACHE_CASTS(ickind) \ michael@0: ickind##IC &IonCache::to##ickind() \ michael@0: { \ michael@0: JS_ASSERT(is##ickind()); \ michael@0: return *static_cast(this); \ michael@0: } \ michael@0: const ickind##IC &IonCache::to##ickind() const \ michael@0: { \ michael@0: JS_ASSERT(is##ickind()); \ michael@0: return *static_cast(this); \ michael@0: } michael@0: IONCACHE_KIND_LIST(CACHE_CASTS) michael@0: #undef OPCODE_CASTS michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif /* jit_IonCaches_h */