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