blob: 610f8da96bcff7d21757729c508d0282cd7d104a [file] [log] [blame]
/* -*- 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 */