| /* -*- 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 |
| |
| #include "IonCode.h" |
| #include "Registers.h" |
| |
| #if defined(JS_CPU_MIPS) |
| #include "jit/mips/Assembler-mips.h" |
| #include "jit/shared/Assembler-shared.h" |
| #endif // defined(JS_CPU_MIPS) |
| |
| #include "vm/ForkJoin.h" |
| |
| class JSFunction; |
| class JSScript; |
| |
| namespace js { |
| namespace jit { |
| |
| #define IONCACHE_KIND_LIST(_) \ |
| _(GetProperty) \ |
| _(SetProperty) \ |
| _(GetElement) \ |
| _(SetElement) \ |
| _(BindName) \ |
| _(Name) \ |
| _(CallsiteClone) \ |
| _(ParallelGetProperty) |
| |
| // 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 bool visit##op##IC(CodeGenerator *codegen, op##IC *) { \ |
| JS_NOT_REACHED("NYI: " #op "IC"); \ |
| return false; \ |
| } |
| |
| IONCACHE_KIND_LIST(VISIT_INS) |
| #undef VISIT_INS |
| }; |
| |
| // Common shared temporary state needed during codegen between the different |
| // kinds of caches. Used by OutOfLineUpdateCache. |
| struct AddCacheState |
| { |
| RepatchLabel repatchEntry; |
| Register dispatchScratch; |
| }; |
| |
| |
| // 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. The details of how stubs are linked up as |
| // described in comments below for the classes RepatchIonCache and |
| // DispatchIonCache. |
| // |
| // 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. |
| // |
| // 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(); |
| |
| IONCACHE_KIND_LIST(CACHEKIND_CASTS) |
| # undef CACHEKIND_CASTS |
| |
| virtual Kind kind() const = 0; |
| |
| virtual bool 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, NULL for idempotent caches. |
| JSScript *script; |
| jsbytecode *pc; |
| |
| private: |
| static const size_t MAX_STUBS; |
| void incrementStubCount() { |
| // The IC should stop generating stubs before wrapping stubCount. |
| stubCount_++; |
| JS_ASSERT(stubCount_); |
| } |
| |
| public: |
| |
| IonCache() |
| : pure_(false), |
| idempotent_(false), |
| disabled_(false), |
| stubCount_(0), |
| fallbackLabel_(), |
| script(NULL), |
| pc(NULL) |
| { |
| } |
| |
| virtual 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(CodeOffsetLabel fallbackLabel) { |
| fallbackLabel_ = fallbackLabel; |
| } |
| |
| virtual void emitInitialJump(MacroAssembler &masm, AddCacheState &addState) = 0; |
| virtual void bindInitialJump(MacroAssembler &masm, AddCacheState &addState) = 0; |
| virtual void updateBaseAddress(IonCode *code, MacroAssembler &masm); |
| |
| // Initialize the AddCacheState depending on the kind of cache, like |
| // setting a scratch register. Defaults to doing nothing. |
| virtual void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); |
| |
| // Reset the cache around garbage collection. |
| virtual void reset(); |
| |
| // Destroy any extra resources the cache uses upon IonScript finalization. |
| virtual void destroy(); |
| |
| 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, IonScript *ion, IonCode **code); |
| // Fixup variables and update jumps in the list of stubs. Increment the |
| // number of attached stubs accordingly. |
| void attachStub(MacroAssembler &masm, StubAttacher &attacher, Handle<IonCode *> code); |
| |
| // Combine both linkStub and attachStub into one function. In addition, it |
| // produces a spew augmented with the attachKind string. |
| bool linkAndAttachStub(JSContext *cx, MacroAssembler &masm, StubAttacher &attacher, |
| IonScript *ion, const char *attachKind); |
| |
| bool isAllocated() { |
| return fallbackLabel_.isSet(); |
| } |
| bool pure() { |
| return pure_; |
| } |
| bool idempotent() { |
| return idempotent_; |
| } |
| void setIdempotent() { |
| JS_ASSERT(!idempotent_); |
| JS_ASSERT(!script); |
| JS_ASSERT(!pc); |
| idempotent_ = true; |
| } |
| |
| void setScriptedLocation(JSScript *script, jsbytecode *pc) { |
| JS_ASSERT(!idempotent_); |
| this->script = script; |
| this->pc = pc; |
| } |
| |
| void getScriptedLocation(MutableHandleScript pscript, jsbytecode **ppc) { |
| pscript.set(script); |
| *ppc = pc; |
| } |
| }; |
| |
| // |
| // Repatch 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 | |
| // |________ | |
| // | | |
| // Repatch | | |
| // 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 | |
| // |________ | |
| // | | |
| // Repatch | | |
| // IC | | |
| // Entry | | |
| // +------------+ | |
| // | lastJump_ |---------------/ |
| // +------------+ |
| // | ... | |
| // +------------+ |
| // |
| class RepatchIonCache : public IonCache |
| { |
| protected: |
| class RepatchStubAppender; |
| |
| CodeLocationJump initialJump_; |
| CodeLocationJump lastJump_; |
| |
| // Offset from the initial jump to the rejoin label. |
| #ifdef JS_CPU_ARM |
| static const size_t REJOIN_LABEL_OFFSET = 4; |
| #elif defined(JS_CPU_MIPS) |
| // The size of jump created by MacroAssemblerMIPSCompat::jumpWithPatch. |
| static const size_t REJOIN_LABEL_OFFSET = 4 * sizeof(void *); |
| #else |
| static const size_t REJOIN_LABEL_OFFSET = 0; |
| #endif |
| |
| CodeLocationLabel rejoinLabel() const { |
| uint8_t *ptr = initialJump_.raw(); |
| #if defined(JS_CPU_ARM) || defined(JS_CPU_MIPS) |
| uint32_t i = 0; |
| while (i < REJOIN_LABEL_OFFSET) |
| ptr = Assembler::nextInstruction(ptr, &i); |
| #endif |
| return CodeLocationLabel(ptr); |
| } |
| |
| public: |
| RepatchIonCache() |
| : initialJump_(), |
| lastJump_() |
| { |
| } |
| |
| virtual void reset(); |
| |
| // Set the initial jump state of the cache. The initialJump is the inline |
| // jump that will point to out-of-line code (such as the slow path, or |
| // stubs), and the rejoinLabel is the position that all out-of-line paths |
| // will rejoin to. |
| void emitInitialJump(MacroAssembler &masm, AddCacheState &addState); |
| void bindInitialJump(MacroAssembler &masm, AddCacheState &addState); |
| |
| // Update the labels once the code is finalized. |
| void updateBaseAddress(IonCode *code, MacroAssembler &masm); |
| }; |
| |
| // |
| // Dispatch caches avoid patching already-running code. Instead, the jump to |
| // the stub chain is indirect by way of the firstStub_ pointer |
| // below. Initially the pointer points to the cache function which may attach |
| // new stubs. Stubs are attached by prepending: when attaching a new stub, we |
| // jump to the previous stub on failure conditions, then overwrite the |
| // firstStub_ pointer with the newly generated stub. |
| // |
| // This style does not patch the already executing instruction stream, does |
| // not need to worry about cache coherence of cached jump addresses, and does |
| // not have to worry about aligning the exit jumps to ensure atomic patching, |
| // at the expense of an extra memory read to load the very first stub. |
| // |
| // ICs that need to work in parallel execution need to be dispatch style. |
| // |
| // Control flow Pointers Memory load |
| // =======# ----. .----> ****** |
| // # | | * |
| // #======> \-----/ ******* |
| // |
| // Initial state: |
| // |
| // The first stub points to the cache function. |
| // |
| // JIT Code |
| // +--------+ .-------. |
| // | | v | |
| // |========| +---------------+ +----------+ | |
| // |== IC ==|====>| Load and jump |====>| Cache Fn | | |
| // |========| +---------------+ +----------+ | |
| // | |<=# * # | |
| // | | #===========*================# | |
| // +--------+ Rejoin * path | |
| // |________ * | |
| // | * | |
| // Dispatch | * | |
| // IC **|************ | |
| // Entry * | | |
| // +------------+ | |
| // | firstStub_ |-------------------------------------/ |
| // +------------+ |
| // | ... | |
| // +------------+ |
| // |
| // Attaching stubs: |
| // |
| // Assign the address of the new stub to firstStub_. The new stub jumps to |
| // the old address held in firstStub_ on failure. Note that there is no |
| // concept of a fallback label here, new stubs are _prepended_, as |
| // firstStub_ always points to the _head_ of the stub chain. |
| // |
| // JIT Code |
| // +--------+ #=====================# .-----. |
| // | | # v v | |
| // |========| +---------------+ # +----------+ +------+ | |
| // |== IC ==|====>| Load and jump |==# | Cache Fn |<====| Stub | | |
| // |========| +---------------+ +----------+ +------+ | |
| // | |<=# * # # | |
| // | | #===========*================#================# | |
| // +--------+ Rejoin * path | |
| // |________ * | |
| // | * | |
| // Dispatch | * | |
| // IC **|************ | |
| // Entry * | | |
| // +------------+ | |
| // | firstStub_ |----------------------------------------------------/ |
| // +------------+ |
| // | ... | |
| // +------------+ |
| // |
| class DispatchIonCache : public IonCache |
| { |
| protected: |
| class DispatchStubPrepender; |
| |
| uint8_t *firstStub_; |
| CodeLocationLabel rejoinLabel_; |
| CodeOffsetLabel dispatchLabel_; |
| |
| public: |
| DispatchIonCache() |
| : firstStub_(NULL), |
| rejoinLabel_(), |
| dispatchLabel_() |
| { |
| } |
| |
| virtual void reset(); |
| |
| void emitInitialJump(MacroAssembler &masm, AddCacheState &addState); |
| void bindInitialJump(MacroAssembler &masm, AddCacheState &addState); |
| |
| // Fix up the first stub pointer once the code is finalized. |
| void updateBaseAddress(IonCode *code, MacroAssembler &masm); |
| }; |
| |
| // 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; \ |
| } \ |
| \ |
| bool accept(CodeGenerator *codegen, IonCacheVisitor *visitor) { \ |
| return visitor->visit##ickind##IC(codegen, this); \ |
| } \ |
| \ |
| 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. |
| |
| class GetPropertyIC : public RepatchIonCache |
| { |
| protected: |
| // Registers live after the cache, excluding output registers. The initial |
| // value of these registers must be preserved by the cache. |
| RegisterSet liveRegs_; |
| |
| Register object_; |
| PropertyName *name_; |
| TypedOrValueRegister output_; |
| bool allowGetters_ : 1; |
| bool hasArrayLengthStub_ : 1; |
| bool hasTypedArrayLengthStub_ : 1; |
| bool hasStrictArgumentsLengthStub_ : 1; |
| bool hasNormalArgumentsLengthStub_ : 1; |
| |
| public: |
| GetPropertyIC(RegisterSet liveRegs, |
| Register object, PropertyName *name, |
| TypedOrValueRegister output, |
| bool allowGetters) |
| : liveRegs_(liveRegs), |
| object_(object), |
| name_(name), |
| output_(output), |
| allowGetters_(allowGetters), |
| hasArrayLengthStub_(false), |
| hasTypedArrayLengthStub_(false), |
| hasStrictArgumentsLengthStub_(false), |
| hasNormalArgumentsLengthStub_(false) |
| { |
| } |
| |
| CACHE_HEADER(GetProperty) |
| |
| void reset(); |
| |
| Register object() const { |
| return object_; |
| } |
| PropertyName *name() const { |
| return name_; |
| } |
| TypedOrValueRegister output() const { |
| return output_; |
| } |
| bool allowGetters() const { |
| return allowGetters_; |
| } |
| bool hasArrayLengthStub() const { |
| return hasArrayLengthStub_; |
| } |
| bool hasTypedArrayLengthStub() const { |
| return hasTypedArrayLengthStub_; |
| } |
| bool hasArgumentsLengthStub(bool strict) const { |
| return strict ? hasStrictArgumentsLengthStub_ : hasNormalArgumentsLengthStub_; |
| } |
| |
| bool attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, |
| HandleShape shape); |
| bool attachDOMProxyShadowed(JSContext *cx, IonScript *ion, JSObject *obj, void *returnAddr); |
| bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, |
| HandleShape shape, |
| const SafepointIndex *safepointIndex, void *returnAddr); |
| bool attachArrayLength(JSContext *cx, IonScript *ion, JSObject *obj); |
| bool attachTypedArrayLength(JSContext *cx, IonScript *ion, JSObject *obj); |
| bool attachArgumentsLength(JSContext *cx, IonScript *ion, JSObject *obj); |
| |
| static bool update(JSContext *cx, size_t cacheIndex, HandleObject obj, MutableHandleValue vp); |
| }; |
| |
| class SetPropertyIC : public RepatchIonCache |
| { |
| protected: |
| // Registers live after the cache, excluding output registers. The initial |
| // value of these registers must be preserved by the cache. |
| RegisterSet liveRegs_; |
| |
| Register object_; |
| PropertyName *name_; |
| ConstantOrRegister value_; |
| bool isSetName_; |
| bool strict_; |
| |
| public: |
| SetPropertyIC(RegisterSet liveRegs, Register object, PropertyName *name, |
| ConstantOrRegister value, bool isSetName, bool strict) |
| : liveRegs_(liveRegs), |
| object_(object), |
| name_(name), |
| value_(value), |
| isSetName_(isSetName), |
| strict_(strict) |
| { |
| } |
| |
| CACHE_HEADER(SetProperty) |
| |
| Register object() const { |
| return object_; |
| } |
| PropertyName *name() const { |
| return name_; |
| } |
| ConstantOrRegister value() const { |
| return value_; |
| } |
| bool isSetName() const { |
| return isSetName_; |
| } |
| bool strict() const { |
| return strict_; |
| } |
| |
| bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape); |
| bool attachSetterCall(JSContext *cx, IonScript *ion, HandleObject obj, |
| HandleObject holder, HandleShape shape, void *returnAddr); |
| bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldshape, |
| HandleShape newshape, HandleShape propshape); |
| |
| static bool |
| update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value); |
| }; |
| |
| class GetElementIC : public RepatchIonCache |
| { |
| protected: |
| Register object_; |
| ConstantOrRegister index_; |
| TypedOrValueRegister output_; |
| |
| bool monitoredResult_ : 1; |
| bool hasDenseStub_ : 1; |
| bool hasStrictArgumentsStub_ : 1; |
| bool hasNormalArgumentsStub_ : 1; |
| |
| size_t failedUpdates_; |
| |
| static const size_t MAX_FAILED_UPDATES; |
| |
| public: |
| GetElementIC(Register object, ConstantOrRegister index, |
| TypedOrValueRegister output, bool monitoredResult) |
| : object_(object), |
| index_(index), |
| output_(output), |
| monitoredResult_(monitoredResult), |
| hasDenseStub_(false), |
| hasStrictArgumentsStub_(false), |
| hasNormalArgumentsStub_(false), |
| failedUpdates_(0) |
| { |
| } |
| |
| CACHE_HEADER(GetElement) |
| |
| void reset(); |
| |
| Register object() const { |
| return object_; |
| } |
| ConstantOrRegister index() const { |
| return index_; |
| } |
| TypedOrValueRegister output() const { |
| return output_; |
| } |
| bool monitoredResult() const { |
| return monitoredResult_; |
| } |
| bool hasDenseStub() const { |
| return hasDenseStub_; |
| } |
| bool hasArgumentsStub(bool strict) const { |
| return strict ? hasStrictArgumentsStub_ : hasNormalArgumentsStub_; |
| } |
| void setHasDenseStub() { |
| JS_ASSERT(!hasDenseStub()); |
| hasDenseStub_ = true; |
| } |
| |
| bool attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj, const Value &idval, HandlePropertyName name); |
| bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval); |
| bool attachTypedArrayElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval); |
| bool attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *obj); |
| |
| static bool |
| update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, |
| MutableHandleValue vp); |
| |
| void incFailedUpdates() { |
| failedUpdates_++; |
| } |
| void resetFailedUpdates() { |
| failedUpdates_ = 0; |
| } |
| bool shouldDisable() const { |
| return !canAttachStub() || |
| (stubCount_ == 0 && failedUpdates_ > MAX_FAILED_UPDATES); |
| } |
| }; |
| |
| class SetElementIC : public RepatchIonCache |
| { |
| protected: |
| Register object_; |
| Register tempToUnboxIndex_; |
| Register temp_; |
| ValueOperand index_; |
| ConstantOrRegister value_; |
| bool strict_; |
| |
| bool hasDenseStub_ : 1; |
| |
| public: |
| SetElementIC(Register object, Register tempToUnboxIndex, Register temp, |
| ValueOperand index, ConstantOrRegister value, |
| bool strict) |
| : object_(object), |
| tempToUnboxIndex_(tempToUnboxIndex), |
| temp_(temp), |
| index_(index), |
| value_(value), |
| strict_(strict), |
| hasDenseStub_(false) |
| { |
| } |
| |
| CACHE_HEADER(SetElement) |
| |
| void reset(); |
| |
| Register object() const { |
| return object_; |
| } |
| Register tempToUnboxIndex() const { |
| return tempToUnboxIndex_; |
| } |
| Register temp() const { |
| return temp_; |
| } |
| ValueOperand index() const { |
| return index_; |
| } |
| ConstantOrRegister value() const { |
| return value_; |
| } |
| bool strict() const { |
| return strict_; |
| } |
| |
| bool hasDenseStub() const { |
| return hasDenseStub_; |
| } |
| void setHasDenseStub() { |
| JS_ASSERT(!hasDenseStub()); |
| hasDenseStub_ = true; |
| } |
| |
| bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval); |
| |
| static bool |
| update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, |
| HandleValue value); |
| }; |
| |
| class BindNameIC : public RepatchIonCache |
| { |
| protected: |
| Register scopeChain_; |
| PropertyName *name_; |
| Register output_; |
| |
| public: |
| BindNameIC(Register scopeChain, PropertyName *name, Register output) |
| : scopeChain_(scopeChain), |
| name_(name), |
| output_(output) |
| { |
| } |
| |
| CACHE_HEADER(BindName) |
| |
| Register scopeChainReg() const { |
| return scopeChain_; |
| } |
| HandlePropertyName name() const { |
| return HandlePropertyName::fromMarkedLocation(&name_); |
| } |
| Register outputReg() const { |
| return output_; |
| } |
| |
| bool attachGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain); |
| bool attachNonGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain, JSObject *holder); |
| |
| static JSObject * |
| update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain); |
| }; |
| |
| class NameIC : public RepatchIonCache |
| { |
| protected: |
| // Registers live after the cache, excluding output registers. The initial |
| // value of these registers must be preserved by the cache. |
| RegisterSet liveRegs_; |
| |
| bool typeOf_; |
| Register scopeChain_; |
| PropertyName *name_; |
| TypedOrValueRegister output_; |
| |
| public: |
| NameIC(RegisterSet liveRegs, bool typeOf, |
| Register scopeChain, PropertyName *name, |
| TypedOrValueRegister output) |
| : liveRegs_(liveRegs), |
| typeOf_(typeOf), |
| scopeChain_(scopeChain), |
| name_(name), |
| output_(output) |
| { |
| } |
| |
| CACHE_HEADER(Name) |
| |
| Register scopeChainReg() const { |
| return scopeChain_; |
| } |
| HandlePropertyName name() const { |
| return HandlePropertyName::fromMarkedLocation(&name_); |
| } |
| TypedOrValueRegister outputReg() const { |
| return output_; |
| } |
| bool isTypeOf() const { |
| return typeOf_; |
| } |
| |
| bool attachReadSlot(JSContext *cx, IonScript *ion, HandleObject scopeChain, HandleObject obj, |
| HandleShape shape); |
| bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, |
| HandleShape shape, const SafepointIndex *safepointIndex, |
| void *returnAddr); |
| |
| static bool |
| update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain, MutableHandleValue vp); |
| }; |
| |
| class CallsiteCloneIC : public RepatchIonCache |
| { |
| protected: |
| Register callee_; |
| Register output_; |
| JSScript *callScript_; |
| jsbytecode *callPc_; |
| |
| public: |
| CallsiteCloneIC(Register callee, JSScript *callScript, jsbytecode *callPc, Register output) |
| : callee_(callee), |
| output_(output), |
| callScript_(callScript), |
| callPc_(callPc) |
| { |
| } |
| |
| CACHE_HEADER(CallsiteClone) |
| |
| Register calleeReg() const { |
| return callee_; |
| } |
| HandleScript callScript() const { |
| return HandleScript::fromMarkedLocation(&callScript_); |
| } |
| jsbytecode *callPc() const { |
| return callPc_; |
| } |
| Register outputReg() const { |
| return output_; |
| } |
| |
| bool attach(JSContext *cx, IonScript *ion, HandleFunction original, HandleFunction clone); |
| |
| static JSObject *update(JSContext *cx, size_t cacheIndex, HandleObject callee); |
| }; |
| |
| class ParallelGetPropertyIC : public DispatchIonCache |
| { |
| protected: |
| Register object_; |
| PropertyName *name_; |
| TypedOrValueRegister output_; |
| |
| // A set of all objects that are stubbed. Used to detect duplicates in |
| // parallel execution. |
| ShapeSet *stubbedShapes_; |
| |
| public: |
| ParallelGetPropertyIC(Register object, PropertyName *name, TypedOrValueRegister output) |
| : object_(object), |
| name_(name), |
| output_(output), |
| stubbedShapes_(NULL) |
| { |
| } |
| |
| CACHE_HEADER(ParallelGetProperty) |
| |
| void reset(); |
| void destroy(); |
| void initializeAddCacheState(LInstruction *ins, AddCacheState *addState); |
| |
| Register object() const { |
| return object_; |
| } |
| PropertyName *name() const { |
| return name_; |
| } |
| TypedOrValueRegister output() const { |
| return output_; |
| } |
| |
| bool initStubbedShapes(JSContext *cx); |
| ShapeSet *stubbedShapes() const { |
| JS_ASSERT_IF(stubbedShapes_, stubbedShapes_->initialized()); |
| return stubbedShapes_; |
| } |
| |
| bool canAttachReadSlot(LockedJSContext &cx, JSObject *obj, MutableHandleObject holder, |
| MutableHandleShape shape); |
| bool attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, bool *attachedStub); |
| |
| static ParallelResult update(ForkJoinSlice *slice, size_t cacheIndex, HandleObject obj, |
| 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() \ |
| { \ |
| JS_ASSERT(is##ickind()); \ |
| return *static_cast<ickind##IC *>(this); \ |
| } |
| IONCACHE_KIND_LIST(CACHE_CASTS) |
| #undef OPCODE_CASTS |
| |
| } // namespace jit |
| } // namespace js |
| |
| #endif /* jit_IonCaches_h */ |