3120 lines
97 KiB
C++
3120 lines
97 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* 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_SharedIC_h
|
|
#define jit_SharedIC_h
|
|
|
|
#include "jscntxt.h"
|
|
#include "jscompartment.h"
|
|
#include "jsgc.h"
|
|
|
|
#include "jit/BaselineICList.h"
|
|
#include "jit/BaselineJIT.h"
|
|
#include "jit/MacroAssembler.h"
|
|
#include "jit/SharedICList.h"
|
|
#include "jit/SharedICRegisters.h"
|
|
#include "vm/ReceiverGuard.h"
|
|
#include "vm/TypedArrayCommon.h"
|
|
|
|
namespace js {
|
|
namespace jit {
|
|
|
|
class AutoShapeVector;
|
|
|
|
//
|
|
// Baseline Inline Caches are polymorphic caches that aggressively
|
|
// share their stub code.
|
|
//
|
|
// Every polymorphic site contains a linked list of stubs which are
|
|
// specific to that site. These stubs are composed of a |StubData|
|
|
// structure that stores parametrization information (e.g.
|
|
// the shape pointer for a shape-check-and-property-get stub), any
|
|
// dynamic information (e.g. warm-up counters), a pointer to the stub code,
|
|
// and a pointer to the next stub state in the linked list.
|
|
//
|
|
// Every BaselineScript keeps an table of |CacheDescriptor| data
|
|
// structures, which store the following:
|
|
// A pointer to the first StubData in the cache.
|
|
// The bytecode PC of the relevant IC.
|
|
// The machine-code PC where the call to the stubcode returns.
|
|
//
|
|
// A diagram:
|
|
//
|
|
// Control flow Pointers
|
|
// =======# ----. .---->
|
|
// # | |
|
|
// #======> \-----/
|
|
//
|
|
//
|
|
// .---------------------------------------.
|
|
// | .-------------------------. |
|
|
// | | .----. | |
|
|
// Baseline | | | | | |
|
|
// JIT Code 0 ^ 1 ^ 2 ^ | | |
|
|
// +--------------+ .-->+-----+ +-----+ +-----+ | | |
|
|
// | | #=|==>| |==>| |==>| FB | | | |
|
|
// | | # | +-----+ +-----+ +-----+ | | |
|
|
// | | # | # # # | | |
|
|
// |==============|==# | # # # | | |
|
|
// |=== IC =======| | # # # | | |
|
|
// .->|==============|<===|======#=========#=========# | | |
|
|
// | | | | | | |
|
|
// | | | | | | |
|
|
// | | | | | | |
|
|
// | | | | v | |
|
|
// | | | | +---------+ | |
|
|
// | | | | | Fallback| | |
|
|
// | | | | | Stub | | |
|
|
// | | | | | Code | | |
|
|
// | | | | +---------+ | |
|
|
// | +--------------+ | | |
|
|
// | |_______ | +---------+ | |
|
|
// | | | | Stub |<---/ |
|
|
// | IC | \--. | Code | |
|
|
// | Descriptor | | +---------+ |
|
|
// | Table v | |
|
|
// | +-----------------+ | +---------+ |
|
|
// \--| Ins | PC | Stub |----/ | Stub |<-------/
|
|
// +-----------------+ | Code |
|
|
// | ... | +---------+
|
|
// +-----------------+
|
|
// Shared
|
|
// Stub Code
|
|
//
|
|
//
|
|
// Type ICs
|
|
// ========
|
|
//
|
|
// Type ICs are otherwise regular ICs that are actually nested within
|
|
// other IC chains. They serve to optimize locations in the code where the
|
|
// baseline compiler would have otherwise had to perform a type Monitor operation
|
|
// (e.g. the result of GetProp, GetElem, etc.), or locations where the baseline
|
|
// compiler would have had to modify a heap typeset using the type of an input
|
|
// value (e.g. SetProp, SetElem, etc.)
|
|
//
|
|
// There are two kinds of Type ICs: Monitor and Update.
|
|
//
|
|
// Note that type stub bodies are no-ops. The stubs only exist for their
|
|
// guards, and their existence simply signifies that the typeset (implicit)
|
|
// that is being checked already contains that type.
|
|
//
|
|
// TypeMonitor ICs
|
|
// ---------------
|
|
// Monitor ICs are shared between stubs in the general IC, and monitor the resulting
|
|
// types of getter operations (call returns, getprop outputs, etc.)
|
|
//
|
|
// +-----------+ +-----------+ +-----------+ +-----------+
|
|
// ---->| Stub 1 |---->| Stub 2 |---->| Stub 3 |---->| FB Stub |
|
|
// +-----------+ +-----------+ +-----------+ +-----------+
|
|
// | | | |
|
|
// |------------------/-----------------/ |
|
|
// v |
|
|
// +-----------+ +-----------+ +-----------+ |
|
|
// | Type 1 |---->| Type 2 |---->| Type FB | |
|
|
// +-----------+ +-----------+ +-----------+ |
|
|
// | | | |
|
|
// <----------/-----------------/------------------/------------------/
|
|
// r e t u r n p a t h
|
|
//
|
|
// After an optimized IC stub successfully executes, it passes control to the type stub
|
|
// chain to check the resulting type. If no type stub succeeds, and the monitor fallback
|
|
// stub is reached, the monitor fallback stub performs a manual monitor, and also adds the
|
|
// appropriate type stub to the chain.
|
|
//
|
|
// The IC's main fallback, in addition to generating new mainline stubs, also generates
|
|
// type stubs as reflected by its returned value.
|
|
//
|
|
// NOTE: The type IC chain returns directly to the mainline code, not back to the
|
|
// stub it was entered from. Thus, entering a type IC is a matter of a |jump|, not
|
|
// a |call|. This allows us to safely call a VM Monitor function from within the monitor IC's
|
|
// fallback chain, since the return address (needed for stack inspection) is preserved.
|
|
//
|
|
//
|
|
// TypeUpdate ICs
|
|
// --------------
|
|
// Update ICs update heap typesets and monitor the input types of setter operations
|
|
// (setelem, setprop inputs, etc.). Unlike monitor ICs, they are not shared
|
|
// between stubs on an IC, but instead are kept track of on a per-stub basis.
|
|
//
|
|
// This is because the main stubs for the operation will each identify a potentially
|
|
// different ObjectGroup to update. New input types must be tracked on a group-to-
|
|
// group basis.
|
|
//
|
|
// Type-update ICs cannot be called in tail position (they must return to the
|
|
// the stub that called them so that the stub may continue to perform its original
|
|
// purpose). This means that any VMCall to perform a manual type update from C++ must be
|
|
// done from within the main IC stub. This necessitates that the stub enter a
|
|
// "BaselineStub" frame before making the call.
|
|
//
|
|
// If the type-update IC chain could itself make the VMCall, then the BaselineStub frame
|
|
// must be entered before calling the type-update chain, and exited afterward. This
|
|
// is very expensive for a common case where we expect the type-update fallback to not
|
|
// be called. To avoid the cost of entering and exiting a BaselineStub frame when
|
|
// using the type-update IC chain, we design the chain to not perform any VM-calls
|
|
// in its fallback.
|
|
//
|
|
// Instead, the type-update IC chain is responsible for returning 1 or 0, depending
|
|
// on if a type is represented in the chain or not. The fallback stub simply returns
|
|
// 0, and all other optimized stubs return 1.
|
|
// If the chain returns 1, then the IC stub goes ahead and performs its operation.
|
|
// If the chain returns 0, then the IC stub performs a call to the fallback function
|
|
// inline (doing the requisite BaselineStub frame enter/exit).
|
|
// This allows us to avoid the expensive subfram enter/exit in the common case.
|
|
//
|
|
// r e t u r n p a t h
|
|
// <--------------.-----------------.-----------------.-----------------.
|
|
// | | | |
|
|
// +-----------+ +-----------+ +-----------+ +-----------+
|
|
// ---->| Stub 1 |---->| Stub 2 |---->| Stub 3 |---->| FB Stub |
|
|
// +-----------+ +-----------+ +-----------+ +-----------+
|
|
// | ^ | ^ | ^
|
|
// | | | | | |
|
|
// | | | | | |----------------.
|
|
// | | | | v |1 |0
|
|
// | | | | +-----------+ +-----------+
|
|
// | | | | | Type 3.1 |--->| FB 3 |
|
|
// | | | | +-----------+ +-----------+
|
|
// | | | |
|
|
// | | | \-------------.-----------------.
|
|
// | | | | | |
|
|
// | | v |1 |1 |0
|
|
// | | +-----------+ +-----------+ +-----------+
|
|
// | | | Type 2.1 |---->| Type 2.2 |---->| FB 2 |
|
|
// | | +-----------+ +-----------+ +-----------+
|
|
// | |
|
|
// | \-------------.-----------------.
|
|
// | | | |
|
|
// v |1 |1 |0
|
|
// +-----------+ +-----------+ +-----------+
|
|
// | Type 1.1 |---->| Type 1.2 |---->| FB 1 |
|
|
// +-----------+ +-----------+ +-----------+
|
|
//
|
|
|
|
class ICStub;
|
|
class ICFallbackStub;
|
|
|
|
#define FORWARD_DECLARE_STUBS(kindName) class IC##kindName;
|
|
IC_BASELINE_STUB_KIND_LIST(FORWARD_DECLARE_STUBS)
|
|
IC_SHARED_STUB_KIND_LIST(FORWARD_DECLARE_STUBS)
|
|
#undef FORWARD_DECLARE_STUBS
|
|
|
|
#ifdef JS_JITSPEW
|
|
void FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...)
|
|
MOZ_FORMAT_PRINTF(3, 4);
|
|
void TypeFallbackICSpew(JSContext* cx, ICTypeMonitor_Fallback* stub, const char* fmt, ...)
|
|
MOZ_FORMAT_PRINTF(3, 4);
|
|
#else
|
|
#define FallbackICSpew(...)
|
|
#define TypeFallbackICSpew(...)
|
|
#endif
|
|
|
|
//
|
|
// An entry in the JIT IC descriptor table.
|
|
//
|
|
class ICEntry
|
|
{
|
|
private:
|
|
// A pointer to the shared IC stub for this instruction.
|
|
ICStub* firstStub_;
|
|
|
|
// Offset from the start of the JIT code where the IC
|
|
// load and call instructions are.
|
|
uint32_t returnOffset_;
|
|
|
|
// The PC of this IC's bytecode op within the JSScript.
|
|
uint32_t pcOffset_ : 28;
|
|
|
|
public:
|
|
enum Kind {
|
|
// A for-op IC entry.
|
|
Kind_Op = 0,
|
|
|
|
// A non-op IC entry.
|
|
Kind_NonOp,
|
|
|
|
// A fake IC entry for returning from a callVM for an op.
|
|
Kind_CallVM,
|
|
|
|
// A fake IC entry for returning from a callVM not for an op (e.g., in
|
|
// the prologue).
|
|
Kind_NonOpCallVM,
|
|
|
|
// A fake IC entry for returning from a callVM to after the
|
|
// warmup counter.
|
|
Kind_WarmupCounter,
|
|
|
|
// A fake IC entry for returning from a callVM to the interrupt
|
|
// handler via the over-recursion check on function entry.
|
|
Kind_StackCheck,
|
|
|
|
// As above, but for the early check. See emitStackCheck.
|
|
Kind_EarlyStackCheck,
|
|
|
|
// A fake IC entry for returning from DebugTrapHandler.
|
|
Kind_DebugTrap,
|
|
|
|
// A fake IC entry for returning from a callVM to
|
|
// Debug{Prologue,Epilogue}.
|
|
Kind_DebugPrologue,
|
|
Kind_DebugEpilogue,
|
|
|
|
Kind_Invalid
|
|
};
|
|
|
|
private:
|
|
// What this IC is for.
|
|
Kind kind_ : 4;
|
|
|
|
// Set the kind and asserts that it's sane.
|
|
void setKind(Kind kind) {
|
|
MOZ_ASSERT(kind < Kind_Invalid);
|
|
kind_ = kind;
|
|
MOZ_ASSERT(this->kind() == kind);
|
|
}
|
|
|
|
public:
|
|
ICEntry(uint32_t pcOffset, Kind kind)
|
|
: firstStub_(nullptr), returnOffset_(), pcOffset_(pcOffset)
|
|
{
|
|
// The offset must fit in at least 28 bits, since we shave off 4 for
|
|
// the Kind enum.
|
|
MOZ_ASSERT(pcOffset_ == pcOffset);
|
|
JS_STATIC_ASSERT(BaselineScript::MAX_JSSCRIPT_LENGTH <= (1u << 28) - 1);
|
|
MOZ_ASSERT(pcOffset <= BaselineScript::MAX_JSSCRIPT_LENGTH);
|
|
setKind(kind);
|
|
}
|
|
|
|
CodeOffset returnOffset() const {
|
|
return CodeOffset(returnOffset_);
|
|
}
|
|
|
|
void setReturnOffset(CodeOffset offset) {
|
|
MOZ_ASSERT(offset.offset() <= (size_t) UINT32_MAX);
|
|
returnOffset_ = (uint32_t) offset.offset();
|
|
}
|
|
|
|
uint32_t pcOffset() const {
|
|
return pcOffset_;
|
|
}
|
|
|
|
jsbytecode* pc(JSScript* script) const {
|
|
return script->offsetToPC(pcOffset_);
|
|
}
|
|
|
|
Kind kind() const {
|
|
// MSVC compiles enums as signed.
|
|
return Kind(kind_ & 0xf);
|
|
}
|
|
bool isForOp() const {
|
|
return kind() == Kind_Op;
|
|
}
|
|
|
|
void setFakeKind(Kind kind) {
|
|
MOZ_ASSERT(kind != Kind_Op && kind != Kind_NonOp);
|
|
setKind(kind);
|
|
}
|
|
|
|
bool hasStub() const {
|
|
return firstStub_ != nullptr;
|
|
}
|
|
ICStub* firstStub() const {
|
|
MOZ_ASSERT(hasStub());
|
|
return firstStub_;
|
|
}
|
|
|
|
ICFallbackStub* fallbackStub() const;
|
|
|
|
void setFirstStub(ICStub* stub) {
|
|
firstStub_ = stub;
|
|
}
|
|
|
|
static inline size_t offsetOfFirstStub() {
|
|
return offsetof(ICEntry, firstStub_);
|
|
}
|
|
|
|
inline ICStub** addressOfFirstStub() {
|
|
return &firstStub_;
|
|
}
|
|
|
|
protected:
|
|
void traceEntry(JSTracer* trc);
|
|
};
|
|
|
|
class BaselineICEntry : public ICEntry
|
|
{
|
|
public:
|
|
BaselineICEntry(uint32_t pcOffset, Kind kind)
|
|
: ICEntry(pcOffset, kind)
|
|
{ }
|
|
|
|
void trace(JSTracer* trc);
|
|
};
|
|
|
|
class IonICEntry : public ICEntry
|
|
{
|
|
JSScript* script_;
|
|
|
|
public:
|
|
IonICEntry(uint32_t pcOffset, Kind kind, JSScript* script)
|
|
: ICEntry(pcOffset, kind),
|
|
script_(script)
|
|
{ }
|
|
|
|
JSScript* script() {
|
|
return script_;
|
|
}
|
|
|
|
void trace(JSTracer* trc);
|
|
};
|
|
|
|
class ICMonitoredStub;
|
|
class ICMonitoredFallbackStub;
|
|
class ICUpdatedStub;
|
|
|
|
// Constant iterator that traverses arbitrary chains of ICStubs.
|
|
// No requirements are made of the ICStub used to construct this
|
|
// iterator, aside from that the stub be part of a nullptr-terminated
|
|
// chain.
|
|
// The iterator is considered to be at its end once it has been
|
|
// incremented _past_ the last stub. Thus, if 'atEnd()' returns
|
|
// true, the '*' and '->' operations are not valid.
|
|
class ICStubConstIterator
|
|
{
|
|
friend class ICStub;
|
|
friend class ICFallbackStub;
|
|
|
|
private:
|
|
ICStub* currentStub_;
|
|
|
|
public:
|
|
explicit ICStubConstIterator(ICStub* currentStub) : currentStub_(currentStub) {}
|
|
|
|
static ICStubConstIterator StartingAt(ICStub* stub) {
|
|
return ICStubConstIterator(stub);
|
|
}
|
|
static ICStubConstIterator End(ICStub* stub) {
|
|
return ICStubConstIterator(nullptr);
|
|
}
|
|
|
|
bool operator ==(const ICStubConstIterator& other) const {
|
|
return currentStub_ == other.currentStub_;
|
|
}
|
|
bool operator !=(const ICStubConstIterator& other) const {
|
|
return !(*this == other);
|
|
}
|
|
|
|
ICStubConstIterator& operator++();
|
|
|
|
ICStubConstIterator operator++(int) {
|
|
ICStubConstIterator oldThis(*this);
|
|
++(*this);
|
|
return oldThis;
|
|
}
|
|
|
|
ICStub* operator*() const {
|
|
MOZ_ASSERT(currentStub_);
|
|
return currentStub_;
|
|
}
|
|
|
|
ICStub* operator ->() const {
|
|
MOZ_ASSERT(currentStub_);
|
|
return currentStub_;
|
|
}
|
|
|
|
bool atEnd() const {
|
|
return currentStub_ == nullptr;
|
|
}
|
|
};
|
|
|
|
// Iterator that traverses "regular" IC chains that start at an ICEntry
|
|
// and are terminated with an ICFallbackStub.
|
|
//
|
|
// The iterator is considered to be at its end once it is _at_ the
|
|
// fallback stub. Thus, unlike the ICStubConstIterator, operators
|
|
// '*' and '->' are valid even if 'atEnd()' returns true - they
|
|
// will act on the fallback stub.
|
|
//
|
|
// This iterator also allows unlinking of stubs being traversed.
|
|
// Note that 'unlink' does not implicitly advance the iterator -
|
|
// it must be advanced explicitly using '++'.
|
|
class ICStubIterator
|
|
{
|
|
friend class ICFallbackStub;
|
|
|
|
private:
|
|
ICEntry* icEntry_;
|
|
ICFallbackStub* fallbackStub_;
|
|
ICStub* previousStub_;
|
|
ICStub* currentStub_;
|
|
bool unlinked_;
|
|
|
|
explicit ICStubIterator(ICFallbackStub* fallbackStub, bool end=false);
|
|
public:
|
|
|
|
bool operator ==(const ICStubIterator& other) const {
|
|
// == should only ever be called on stubs from the same chain.
|
|
MOZ_ASSERT(icEntry_ == other.icEntry_);
|
|
MOZ_ASSERT(fallbackStub_ == other.fallbackStub_);
|
|
return currentStub_ == other.currentStub_;
|
|
}
|
|
bool operator !=(const ICStubIterator& other) const {
|
|
return !(*this == other);
|
|
}
|
|
|
|
ICStubIterator& operator++();
|
|
|
|
ICStubIterator operator++(int) {
|
|
ICStubIterator oldThis(*this);
|
|
++(*this);
|
|
return oldThis;
|
|
}
|
|
|
|
ICStub* operator*() const {
|
|
return currentStub_;
|
|
}
|
|
|
|
ICStub* operator ->() const {
|
|
return currentStub_;
|
|
}
|
|
|
|
bool atEnd() const {
|
|
return currentStub_ == (ICStub*) fallbackStub_;
|
|
}
|
|
|
|
void unlink(JSContext* cx);
|
|
};
|
|
|
|
//
|
|
// Base class for all IC stubs.
|
|
//
|
|
class ICStub
|
|
{
|
|
friend class ICFallbackStub;
|
|
|
|
public:
|
|
enum Kind {
|
|
INVALID = 0,
|
|
#define DEF_ENUM_KIND(kindName) kindName,
|
|
IC_BASELINE_STUB_KIND_LIST(DEF_ENUM_KIND)
|
|
IC_SHARED_STUB_KIND_LIST(DEF_ENUM_KIND)
|
|
#undef DEF_ENUM_KIND
|
|
LIMIT
|
|
};
|
|
|
|
static inline bool IsValidKind(Kind k) {
|
|
return (k > INVALID) && (k < LIMIT);
|
|
}
|
|
|
|
static const char* KindString(Kind k) {
|
|
switch(k) {
|
|
#define DEF_KIND_STR(kindName) case kindName: return #kindName;
|
|
IC_BASELINE_STUB_KIND_LIST(DEF_KIND_STR)
|
|
IC_SHARED_STUB_KIND_LIST(DEF_KIND_STR)
|
|
#undef DEF_KIND_STR
|
|
default:
|
|
MOZ_CRASH("Invalid kind.");
|
|
}
|
|
}
|
|
|
|
enum Trait {
|
|
Regular = 0x0,
|
|
Fallback = 0x1,
|
|
Monitored = 0x2,
|
|
MonitoredFallback = 0x3,
|
|
Updated = 0x4
|
|
};
|
|
|
|
void markCode(JSTracer* trc, const char* name);
|
|
void updateCode(JitCode* stubCode);
|
|
void trace(JSTracer* trc);
|
|
|
|
template <typename T, typename... Args>
|
|
static T* New(JSContext* cx, ICStubSpace* space, JitCode* code, Args&&... args) {
|
|
if (!code)
|
|
return nullptr;
|
|
T* result = space->allocate<T>(code, mozilla::Forward<Args>(args)...);
|
|
if (!result)
|
|
ReportOutOfMemory(cx);
|
|
return result;
|
|
}
|
|
|
|
protected:
|
|
// The raw jitcode to call for this stub.
|
|
uint8_t* stubCode_;
|
|
|
|
// Pointer to next IC stub. This is null for the last IC stub, which should
|
|
// either be a fallback or inert IC stub.
|
|
ICStub* next_;
|
|
|
|
// A 16-bit field usable by subtypes of ICStub for subtype-specific small-info
|
|
uint16_t extra_;
|
|
|
|
// The kind of the stub.
|
|
// High bit is 'isFallback' flag.
|
|
// Second high bit is 'isMonitored' flag.
|
|
Trait trait_ : 3;
|
|
Kind kind_ : 13;
|
|
|
|
inline ICStub(Kind kind, JitCode* stubCode)
|
|
: stubCode_(stubCode->raw()),
|
|
next_(nullptr),
|
|
extra_(0),
|
|
trait_(Regular),
|
|
kind_(kind)
|
|
{
|
|
MOZ_ASSERT(stubCode != nullptr);
|
|
}
|
|
|
|
inline ICStub(Kind kind, Trait trait, JitCode* stubCode)
|
|
: stubCode_(stubCode->raw()),
|
|
next_(nullptr),
|
|
extra_(0),
|
|
trait_(trait),
|
|
kind_(kind)
|
|
{
|
|
MOZ_ASSERT(stubCode != nullptr);
|
|
}
|
|
|
|
inline Trait trait() const {
|
|
// Workaround for MSVC reading trait_ as signed value.
|
|
return (Trait)(trait_ & 0x7);
|
|
}
|
|
|
|
public:
|
|
|
|
inline Kind kind() const {
|
|
return static_cast<Kind>(kind_);
|
|
}
|
|
|
|
inline bool isFallback() const {
|
|
return trait() == Fallback || trait() == MonitoredFallback;
|
|
}
|
|
|
|
inline bool isMonitored() const {
|
|
return trait() == Monitored;
|
|
}
|
|
|
|
inline bool isUpdated() const {
|
|
return trait() == Updated;
|
|
}
|
|
|
|
inline bool isMonitoredFallback() const {
|
|
return trait() == MonitoredFallback;
|
|
}
|
|
|
|
inline const ICFallbackStub* toFallbackStub() const {
|
|
MOZ_ASSERT(isFallback());
|
|
return reinterpret_cast<const ICFallbackStub*>(this);
|
|
}
|
|
|
|
inline ICFallbackStub* toFallbackStub() {
|
|
MOZ_ASSERT(isFallback());
|
|
return reinterpret_cast<ICFallbackStub*>(this);
|
|
}
|
|
|
|
inline const ICMonitoredStub* toMonitoredStub() const {
|
|
MOZ_ASSERT(isMonitored());
|
|
return reinterpret_cast<const ICMonitoredStub*>(this);
|
|
}
|
|
|
|
inline ICMonitoredStub* toMonitoredStub() {
|
|
MOZ_ASSERT(isMonitored());
|
|
return reinterpret_cast<ICMonitoredStub*>(this);
|
|
}
|
|
|
|
inline const ICMonitoredFallbackStub* toMonitoredFallbackStub() const {
|
|
MOZ_ASSERT(isMonitoredFallback());
|
|
return reinterpret_cast<const ICMonitoredFallbackStub*>(this);
|
|
}
|
|
|
|
inline ICMonitoredFallbackStub* toMonitoredFallbackStub() {
|
|
MOZ_ASSERT(isMonitoredFallback());
|
|
return reinterpret_cast<ICMonitoredFallbackStub*>(this);
|
|
}
|
|
|
|
inline const ICUpdatedStub* toUpdatedStub() const {
|
|
MOZ_ASSERT(isUpdated());
|
|
return reinterpret_cast<const ICUpdatedStub*>(this);
|
|
}
|
|
|
|
inline ICUpdatedStub* toUpdatedStub() {
|
|
MOZ_ASSERT(isUpdated());
|
|
return reinterpret_cast<ICUpdatedStub*>(this);
|
|
}
|
|
|
|
#define KIND_METHODS(kindName) \
|
|
inline bool is##kindName() const { return kind() == kindName; } \
|
|
inline const IC##kindName* to##kindName() const { \
|
|
MOZ_ASSERT(is##kindName()); \
|
|
return reinterpret_cast<const IC##kindName*>(this); \
|
|
} \
|
|
inline IC##kindName* to##kindName() { \
|
|
MOZ_ASSERT(is##kindName()); \
|
|
return reinterpret_cast<IC##kindName*>(this); \
|
|
}
|
|
IC_BASELINE_STUB_KIND_LIST(KIND_METHODS)
|
|
IC_SHARED_STUB_KIND_LIST(KIND_METHODS)
|
|
#undef KIND_METHODS
|
|
|
|
inline ICStub* next() const {
|
|
return next_;
|
|
}
|
|
|
|
inline bool hasNext() const {
|
|
return next_ != nullptr;
|
|
}
|
|
|
|
inline void setNext(ICStub* stub) {
|
|
// Note: next_ only needs to be changed under the compilation lock for
|
|
// non-type-monitor/update ICs.
|
|
next_ = stub;
|
|
}
|
|
|
|
inline ICStub** addressOfNext() {
|
|
return &next_;
|
|
}
|
|
|
|
inline JitCode* jitCode() {
|
|
return JitCode::FromExecutable(stubCode_);
|
|
}
|
|
|
|
inline uint8_t* rawStubCode() const {
|
|
return stubCode_;
|
|
}
|
|
|
|
// This method is not valid on TypeUpdate stub chains!
|
|
inline ICFallbackStub* getChainFallback() {
|
|
ICStub* lastStub = this;
|
|
while (lastStub->next_)
|
|
lastStub = lastStub->next_;
|
|
MOZ_ASSERT(lastStub->isFallback());
|
|
return lastStub->toFallbackStub();
|
|
}
|
|
|
|
inline ICStubConstIterator beginHere() {
|
|
return ICStubConstIterator::StartingAt(this);
|
|
}
|
|
|
|
static inline size_t offsetOfNext() {
|
|
return offsetof(ICStub, next_);
|
|
}
|
|
|
|
static inline size_t offsetOfStubCode() {
|
|
return offsetof(ICStub, stubCode_);
|
|
}
|
|
|
|
static inline size_t offsetOfExtra() {
|
|
return offsetof(ICStub, extra_);
|
|
}
|
|
|
|
static bool CanMakeCalls(ICStub::Kind kind) {
|
|
MOZ_ASSERT(IsValidKind(kind));
|
|
switch (kind) {
|
|
case Call_Fallback:
|
|
case Call_Scripted:
|
|
case Call_AnyScripted:
|
|
case Call_Native:
|
|
case Call_ClassHook:
|
|
case Call_ScriptedApplyArray:
|
|
case Call_ScriptedApplyArguments:
|
|
case Call_ScriptedFunCall:
|
|
case Call_StringSplit:
|
|
case WarmUpCounter_Fallback:
|
|
case GetElem_NativeSlotName:
|
|
case GetElem_NativeSlotSymbol:
|
|
case GetElem_NativePrototypeSlotName:
|
|
case GetElem_NativePrototypeSlotSymbol:
|
|
case GetElem_NativePrototypeCallNativeName:
|
|
case GetElem_NativePrototypeCallNativeSymbol:
|
|
case GetElem_NativePrototypeCallScriptedName:
|
|
case GetElem_NativePrototypeCallScriptedSymbol:
|
|
case GetElem_UnboxedPropertyName:
|
|
case GetProp_CallScripted:
|
|
case GetProp_CallNative:
|
|
case GetProp_CallNativeGlobal:
|
|
case GetProp_CallDOMProxyNative:
|
|
case GetProp_CallDOMProxyWithGenerationNative:
|
|
case GetProp_DOMProxyShadowed:
|
|
case GetProp_Generic:
|
|
case SetProp_CallScripted:
|
|
case SetProp_CallNative:
|
|
case RetSub_Fallback:
|
|
// These two fallback stubs don't actually make non-tail calls,
|
|
// but the fallback code for the bailout path needs to pop the stub frame
|
|
// pushed during the bailout.
|
|
case GetProp_Fallback:
|
|
case SetProp_Fallback:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Optimized stubs get purged on GC. But some stubs can be active on the
|
|
// stack during GC - specifically the ones that can make calls. To ensure
|
|
// that these do not get purged, all stubs that can make calls are allocated
|
|
// in the fallback stub space.
|
|
bool allocatedInFallbackSpace() const {
|
|
MOZ_ASSERT(next());
|
|
return CanMakeCalls(kind());
|
|
}
|
|
};
|
|
|
|
class ICFallbackStub : public ICStub
|
|
{
|
|
friend class ICStubConstIterator;
|
|
protected:
|
|
// Fallback stubs need these fields to easily add new stubs to
|
|
// the linked list of stubs for an IC.
|
|
|
|
// The IC entry for this linked list of stubs.
|
|
ICEntry* icEntry_;
|
|
|
|
// The number of stubs kept in the IC entry.
|
|
uint32_t numOptimizedStubs_ : 31;
|
|
uint32_t invalid_ : 1;
|
|
|
|
// A pointer to the location stub pointer that needs to be
|
|
// changed to add a new "last" stub immediately before the fallback
|
|
// stub. This'll start out pointing to the icEntry's "firstStub_"
|
|
// field, and as new stubs are added, it'll point to the current
|
|
// last stub's "next_" field.
|
|
ICStub** lastStubPtrAddr_;
|
|
|
|
ICFallbackStub(Kind kind, JitCode* stubCode)
|
|
: ICStub(kind, ICStub::Fallback, stubCode),
|
|
icEntry_(nullptr),
|
|
numOptimizedStubs_(0),
|
|
invalid_(false),
|
|
lastStubPtrAddr_(nullptr) {}
|
|
|
|
ICFallbackStub(Kind kind, Trait trait, JitCode* stubCode)
|
|
: ICStub(kind, trait, stubCode),
|
|
icEntry_(nullptr),
|
|
numOptimizedStubs_(0),
|
|
invalid_(false),
|
|
lastStubPtrAddr_(nullptr)
|
|
{
|
|
MOZ_ASSERT(trait == ICStub::Fallback ||
|
|
trait == ICStub::MonitoredFallback);
|
|
}
|
|
|
|
public:
|
|
inline ICEntry* icEntry() const {
|
|
return icEntry_;
|
|
}
|
|
|
|
inline size_t numOptimizedStubs() const {
|
|
return (size_t) numOptimizedStubs_;
|
|
}
|
|
|
|
void setInvalid() {
|
|
invalid_ = 1;
|
|
}
|
|
|
|
bool invalid() const {
|
|
return invalid_;
|
|
}
|
|
|
|
// The icEntry and lastStubPtrAddr_ fields can't be initialized when the stub is
|
|
// created since the stub is created at compile time, and we won't know the IC entry
|
|
// address until after compile when the JitScript is created. This method
|
|
// allows these fields to be fixed up at that point.
|
|
void fixupICEntry(ICEntry* icEntry) {
|
|
MOZ_ASSERT(icEntry_ == nullptr);
|
|
MOZ_ASSERT(lastStubPtrAddr_ == nullptr);
|
|
icEntry_ = icEntry;
|
|
lastStubPtrAddr_ = icEntry_->addressOfFirstStub();
|
|
}
|
|
|
|
// Add a new stub to the IC chain terminated by this fallback stub.
|
|
void addNewStub(ICStub* stub) {
|
|
MOZ_ASSERT(!invalid());
|
|
MOZ_ASSERT(*lastStubPtrAddr_ == this);
|
|
MOZ_ASSERT(stub->next() == nullptr);
|
|
stub->setNext(this);
|
|
*lastStubPtrAddr_ = stub;
|
|
lastStubPtrAddr_ = stub->addressOfNext();
|
|
numOptimizedStubs_++;
|
|
}
|
|
|
|
ICStubConstIterator beginChainConst() const {
|
|
return ICStubConstIterator(icEntry_->firstStub());
|
|
}
|
|
|
|
ICStubIterator beginChain() {
|
|
return ICStubIterator(this);
|
|
}
|
|
|
|
bool hasStub(ICStub::Kind kind) const {
|
|
for (ICStubConstIterator iter = beginChainConst(); !iter.atEnd(); iter++) {
|
|
if (iter->kind() == kind)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
unsigned numStubsWithKind(ICStub::Kind kind) const {
|
|
unsigned count = 0;
|
|
for (ICStubConstIterator iter = beginChainConst(); !iter.atEnd(); iter++) {
|
|
if (iter->kind() == kind)
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void unlinkStub(Zone* zone, ICStub* prev, ICStub* stub);
|
|
void unlinkStubsWithKind(JSContext* cx, ICStub::Kind kind);
|
|
};
|
|
|
|
// Monitored stubs are IC stubs that feed a single resulting value out to a
|
|
// type monitor operation.
|
|
class ICMonitoredStub : public ICStub
|
|
{
|
|
protected:
|
|
// Pointer to the start of the type monitoring stub chain.
|
|
ICStub* firstMonitorStub_;
|
|
|
|
ICMonitoredStub(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub);
|
|
|
|
public:
|
|
inline void updateFirstMonitorStub(ICStub* monitorStub) {
|
|
// This should only be called once: when the first optimized monitor stub
|
|
// is added to the type monitor IC chain.
|
|
MOZ_ASSERT(firstMonitorStub_ && firstMonitorStub_->isTypeMonitor_Fallback());
|
|
firstMonitorStub_ = monitorStub;
|
|
}
|
|
inline void resetFirstMonitorStub(ICStub* monitorFallback) {
|
|
MOZ_ASSERT(monitorFallback->isTypeMonitor_Fallback());
|
|
firstMonitorStub_ = monitorFallback;
|
|
}
|
|
inline ICStub* firstMonitorStub() const {
|
|
return firstMonitorStub_;
|
|
}
|
|
|
|
static inline size_t offsetOfFirstMonitorStub() {
|
|
return offsetof(ICMonitoredStub, firstMonitorStub_);
|
|
}
|
|
};
|
|
|
|
class ICCacheIR_Monitored : public ICMonitoredStub
|
|
{
|
|
CacheIRStubInfo* stubInfo_;
|
|
|
|
public:
|
|
ICCacheIR_Monitored(JitCode* stubCode, ICStub* firstMonitorStub, CacheIRStubInfo* stubInfo)
|
|
: ICMonitoredStub(ICStub::CacheIR_Monitored, stubCode, firstMonitorStub),
|
|
stubInfo_(stubInfo)
|
|
{}
|
|
|
|
void notePreliminaryObject() {
|
|
extra_ = 1;
|
|
}
|
|
bool hasPreliminaryObject() const {
|
|
return extra_;
|
|
}
|
|
|
|
const CacheIRStubInfo* stubInfo() const {
|
|
return stubInfo_;
|
|
}
|
|
};
|
|
|
|
// Updated stubs are IC stubs that use a TypeUpdate IC to track
|
|
// the status of heap typesets that need to be updated.
|
|
class ICUpdatedStub : public ICStub
|
|
{
|
|
protected:
|
|
// Pointer to the start of the type updating stub chain.
|
|
ICStub* firstUpdateStub_;
|
|
|
|
static const uint32_t MAX_OPTIMIZED_STUBS = 8;
|
|
uint32_t numOptimizedStubs_;
|
|
|
|
ICUpdatedStub(Kind kind, JitCode* stubCode)
|
|
: ICStub(kind, ICStub::Updated, stubCode),
|
|
firstUpdateStub_(nullptr),
|
|
numOptimizedStubs_(0)
|
|
{}
|
|
|
|
public:
|
|
MOZ_MUST_USE bool initUpdatingChain(JSContext* cx, ICStubSpace* space);
|
|
|
|
MOZ_MUST_USE bool addUpdateStubForValue(JSContext* cx, HandleScript script, HandleObject obj,
|
|
HandleId id, HandleValue val);
|
|
|
|
void addOptimizedUpdateStub(ICStub* stub) {
|
|
if (firstUpdateStub_->isTypeUpdate_Fallback()) {
|
|
stub->setNext(firstUpdateStub_);
|
|
firstUpdateStub_ = stub;
|
|
} else {
|
|
ICStub* iter = firstUpdateStub_;
|
|
MOZ_ASSERT(iter->next() != nullptr);
|
|
while (!iter->next()->isTypeUpdate_Fallback())
|
|
iter = iter->next();
|
|
MOZ_ASSERT(iter->next()->next() == nullptr);
|
|
stub->setNext(iter->next());
|
|
iter->setNext(stub);
|
|
}
|
|
|
|
numOptimizedStubs_++;
|
|
}
|
|
|
|
inline ICStub* firstUpdateStub() const {
|
|
return firstUpdateStub_;
|
|
}
|
|
|
|
bool hasTypeUpdateStub(ICStub::Kind kind) {
|
|
ICStub* stub = firstUpdateStub_;
|
|
do {
|
|
if (stub->kind() == kind)
|
|
return true;
|
|
|
|
stub = stub->next();
|
|
} while (stub);
|
|
|
|
return false;
|
|
}
|
|
|
|
inline uint32_t numOptimizedStubs() const {
|
|
return numOptimizedStubs_;
|
|
}
|
|
|
|
static inline size_t offsetOfFirstUpdateStub() {
|
|
return offsetof(ICUpdatedStub, firstUpdateStub_);
|
|
}
|
|
};
|
|
|
|
// Base class for stubcode compilers.
|
|
class ICStubCompiler
|
|
{
|
|
// Prevent GC in the middle of stub compilation.
|
|
js::gc::AutoSuppressGC suppressGC;
|
|
|
|
public:
|
|
enum class Engine {
|
|
Baseline = 0,
|
|
IonMonkey
|
|
};
|
|
|
|
protected:
|
|
JSContext* cx;
|
|
ICStub::Kind kind;
|
|
Engine engine_;
|
|
bool inStubFrame_;
|
|
|
|
#ifdef DEBUG
|
|
bool entersStubFrame_;
|
|
uint32_t framePushedAtEnterStubFrame_;
|
|
#endif
|
|
|
|
// By default the stubcode key is just the kind.
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1);
|
|
}
|
|
|
|
virtual MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) = 0;
|
|
virtual void postGenerateStubCode(MacroAssembler& masm, Handle<JitCode*> genCode) {}
|
|
|
|
JitCode* getStubCode();
|
|
|
|
ICStubCompiler(JSContext* cx, ICStub::Kind kind, Engine engine)
|
|
: suppressGC(cx), cx(cx), kind(kind), engine_(engine), inStubFrame_(false)
|
|
#ifdef DEBUG
|
|
, entersStubFrame_(false), framePushedAtEnterStubFrame_(0)
|
|
#endif
|
|
{}
|
|
|
|
// Push a payload specialized per compiler needed to execute stubs.
|
|
void PushStubPayload(MacroAssembler& masm, Register scratch);
|
|
void pushStubPayload(MacroAssembler& masm, Register scratch);
|
|
|
|
// Emits a tail call to a VMFunction wrapper.
|
|
MOZ_MUST_USE bool tailCallVM(const VMFunction& fun, MacroAssembler& masm);
|
|
|
|
// Emits a normal (non-tail) call to a VMFunction wrapper.
|
|
MOZ_MUST_USE bool callVM(const VMFunction& fun, MacroAssembler& masm);
|
|
|
|
// Emits a call to a type-update IC, assuming that the value to be
|
|
// checked is already in R0.
|
|
MOZ_MUST_USE bool callTypeUpdateIC(MacroAssembler& masm, uint32_t objectOffset);
|
|
|
|
// A stub frame is used when a stub wants to call into the VM without
|
|
// performing a tail call. This is required for the return address
|
|
// to pc mapping to work.
|
|
void enterStubFrame(MacroAssembler& masm, Register scratch);
|
|
void leaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false);
|
|
|
|
// Some stubs need to emit SPS profiler updates. This emits the guarding
|
|
// jitcode for those stubs. If profiling is not enabled, jumps to the
|
|
// given label.
|
|
void guardProfilingEnabled(MacroAssembler& masm, Register scratch, Label* skip);
|
|
|
|
public:
|
|
static inline AllocatableGeneralRegisterSet availableGeneralRegs(size_t numInputs) {
|
|
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
|
#if defined(JS_CODEGEN_ARM)
|
|
MOZ_ASSERT(!regs.has(BaselineStackReg));
|
|
MOZ_ASSERT(!regs.has(ICTailCallReg));
|
|
regs.take(BaselineSecondScratchReg);
|
|
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
|
MOZ_ASSERT(!regs.has(BaselineStackReg));
|
|
MOZ_ASSERT(!regs.has(ICTailCallReg));
|
|
MOZ_ASSERT(!regs.has(BaselineSecondScratchReg));
|
|
#elif defined(JS_CODEGEN_ARM64)
|
|
MOZ_ASSERT(!regs.has(PseudoStackPointer));
|
|
MOZ_ASSERT(!regs.has(RealStackPointer));
|
|
MOZ_ASSERT(!regs.has(ICTailCallReg));
|
|
#else
|
|
MOZ_ASSERT(!regs.has(BaselineStackReg));
|
|
#endif
|
|
regs.take(BaselineFrameReg);
|
|
regs.take(ICStubReg);
|
|
#ifdef JS_CODEGEN_X64
|
|
regs.take(ExtractTemp0);
|
|
regs.take(ExtractTemp1);
|
|
#endif
|
|
|
|
switch (numInputs) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
regs.take(R0);
|
|
break;
|
|
case 2:
|
|
regs.take(R0);
|
|
regs.take(R1);
|
|
break;
|
|
default:
|
|
MOZ_CRASH("Invalid numInputs");
|
|
}
|
|
|
|
return regs;
|
|
}
|
|
|
|
protected:
|
|
void emitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val,
|
|
Register scratch, LiveGeneralRegisterSet saveRegs);
|
|
|
|
template <typename T, typename... Args>
|
|
T* newStub(Args&&... args) {
|
|
return ICStub::New<T>(cx, mozilla::Forward<Args>(args)...);
|
|
}
|
|
|
|
public:
|
|
virtual ICStub* getStub(ICStubSpace* space) = 0;
|
|
|
|
static ICStubSpace* StubSpaceForKind(ICStub::Kind kind, JSScript* outerScript, Engine engine) {
|
|
if (ICStub::CanMakeCalls(kind)) {
|
|
if (engine == ICStubCompiler::Engine::Baseline)
|
|
return outerScript->baselineScript()->fallbackStubSpace();
|
|
return outerScript->ionScript()->fallbackStubSpace();
|
|
}
|
|
return outerScript->zone()->jitZone()->optimizedStubSpace();
|
|
}
|
|
|
|
ICStubSpace* getStubSpace(JSScript* outerScript) {
|
|
return StubSpaceForKind(kind, outerScript, engine_);
|
|
}
|
|
};
|
|
|
|
class SharedStubInfo
|
|
{
|
|
BaselineFrame* maybeFrame_;
|
|
RootedScript outerScript_;
|
|
RootedScript innerScript_;
|
|
ICEntry* icEntry_;
|
|
|
|
public:
|
|
SharedStubInfo(JSContext* cx, void* payload, ICEntry* entry);
|
|
|
|
ICStubCompiler::Engine engine() const {
|
|
return maybeFrame_ ? ICStubCompiler::Engine::Baseline : ICStubCompiler::Engine::IonMonkey;
|
|
}
|
|
|
|
HandleScript script() const {
|
|
MOZ_ASSERT(innerScript_);
|
|
return innerScript_;
|
|
}
|
|
|
|
HandleScript innerScript() const {
|
|
MOZ_ASSERT(innerScript_);
|
|
return innerScript_;
|
|
}
|
|
|
|
HandleScript outerScript(JSContext* cx);
|
|
|
|
jsbytecode* pc() const {
|
|
return icEntry()->pc(innerScript());
|
|
}
|
|
|
|
uint32_t pcOffset() const {
|
|
return script()->pcToOffset(pc());
|
|
}
|
|
|
|
BaselineFrame* frame() const {
|
|
MOZ_ASSERT(maybeFrame_);
|
|
return maybeFrame_;
|
|
}
|
|
|
|
BaselineFrame* maybeFrame() const {
|
|
return maybeFrame_;
|
|
}
|
|
|
|
ICEntry* icEntry() const {
|
|
return icEntry_;
|
|
}
|
|
};
|
|
|
|
// Monitored fallback stubs - as the name implies.
|
|
class ICMonitoredFallbackStub : public ICFallbackStub
|
|
{
|
|
protected:
|
|
// Pointer to the fallback monitor stub.
|
|
ICTypeMonitor_Fallback* fallbackMonitorStub_;
|
|
|
|
ICMonitoredFallbackStub(Kind kind, JitCode* stubCode)
|
|
: ICFallbackStub(kind, ICStub::MonitoredFallback, stubCode),
|
|
fallbackMonitorStub_(nullptr) {}
|
|
|
|
public:
|
|
MOZ_MUST_USE bool initMonitoringChain(JSContext* cx, ICStubSpace* space,
|
|
ICStubCompiler::Engine engine);
|
|
MOZ_MUST_USE bool addMonitorStubForValue(JSContext* cx, SharedStubInfo* info, HandleValue val);
|
|
|
|
inline ICTypeMonitor_Fallback* fallbackMonitorStub() const {
|
|
return fallbackMonitorStub_;
|
|
}
|
|
|
|
static inline size_t offsetOfFallbackMonitorStub() {
|
|
return offsetof(ICMonitoredFallbackStub, fallbackMonitorStub_);
|
|
}
|
|
};
|
|
|
|
|
|
// Base class for stub compilers that can generate multiple stubcodes.
|
|
// These compilers need access to the JSOp they are compiling for.
|
|
class ICMultiStubCompiler : public ICStubCompiler
|
|
{
|
|
protected:
|
|
JSOp op;
|
|
|
|
// Stub keys for multi-stub kinds are composed of both the kind
|
|
// and the op they are compiled for.
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(static_cast<int32_t>(op) << 17);
|
|
}
|
|
|
|
ICMultiStubCompiler(JSContext* cx, ICStub::Kind kind, JSOp op, Engine engine)
|
|
: ICStubCompiler(cx, kind, engine), op(op) {}
|
|
};
|
|
|
|
// TypeCheckPrimitiveSetStub
|
|
// Base class for IC stubs (TypeUpdate or TypeMonitor) that check that a given
|
|
// value's type falls within a set of primitive types.
|
|
|
|
class TypeCheckPrimitiveSetStub : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
protected:
|
|
inline static uint16_t TypeToFlag(JSValueType type) {
|
|
return 1u << static_cast<unsigned>(type);
|
|
}
|
|
|
|
inline static uint16_t ValidFlags() {
|
|
return ((TypeToFlag(JSVAL_TYPE_OBJECT) << 1) - 1) & ~TypeToFlag(JSVAL_TYPE_MAGIC);
|
|
}
|
|
|
|
TypeCheckPrimitiveSetStub(Kind kind, JitCode* stubCode, uint16_t flags)
|
|
: ICStub(kind, stubCode)
|
|
{
|
|
MOZ_ASSERT(kind == TypeMonitor_PrimitiveSet || kind == TypeUpdate_PrimitiveSet);
|
|
MOZ_ASSERT(flags && !(flags & ~ValidFlags()));
|
|
extra_ = flags;
|
|
}
|
|
|
|
TypeCheckPrimitiveSetStub* updateTypesAndCode(uint16_t flags, JitCode* code) {
|
|
MOZ_ASSERT(flags && !(flags & ~ValidFlags()));
|
|
if (!code)
|
|
return nullptr;
|
|
extra_ = flags;
|
|
updateCode(code);
|
|
return this;
|
|
}
|
|
|
|
public:
|
|
uint16_t typeFlags() const {
|
|
return extra_;
|
|
}
|
|
|
|
bool containsType(JSValueType type) const {
|
|
MOZ_ASSERT(type <= JSVAL_TYPE_OBJECT);
|
|
MOZ_ASSERT(type != JSVAL_TYPE_MAGIC);
|
|
return extra_ & TypeToFlag(type);
|
|
}
|
|
|
|
ICTypeMonitor_PrimitiveSet* toMonitorStub() {
|
|
return toTypeMonitor_PrimitiveSet();
|
|
}
|
|
|
|
ICTypeUpdate_PrimitiveSet* toUpdateStub() {
|
|
return toTypeUpdate_PrimitiveSet();
|
|
}
|
|
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
TypeCheckPrimitiveSetStub* existingStub_;
|
|
uint16_t flags_;
|
|
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(static_cast<int32_t>(flags_) << 17);
|
|
}
|
|
|
|
public:
|
|
Compiler(JSContext* cx, Kind kind, Engine engine_, TypeCheckPrimitiveSetStub* existingStub,
|
|
JSValueType type)
|
|
: ICStubCompiler(cx, kind, engine_),
|
|
existingStub_(existingStub),
|
|
flags_((existingStub ? existingStub->typeFlags() : 0) | TypeToFlag(type))
|
|
{
|
|
MOZ_ASSERT_IF(existingStub_, flags_ != existingStub_->typeFlags());
|
|
}
|
|
|
|
TypeCheckPrimitiveSetStub* updateStub() {
|
|
MOZ_ASSERT(existingStub_);
|
|
return existingStub_->updateTypesAndCode(flags_, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
// TypeMonitor
|
|
|
|
// The TypeMonitor fallback stub is not always a regular fallback stub. When
|
|
// used for monitoring the values pushed by a bytecode it doesn't hold a
|
|
// pointer to the IC entry, but rather back to the main fallback stub for the
|
|
// IC (from which a pointer to the IC entry can be retrieved). When monitoring
|
|
// the types of 'this', arguments or other values with no associated IC, there
|
|
// is no main fallback stub, and the IC entry is referenced directly.
|
|
class ICTypeMonitor_Fallback : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
static const uint32_t MAX_OPTIMIZED_STUBS = 8;
|
|
|
|
// Pointer to the main fallback stub for the IC or to the main IC entry,
|
|
// depending on hasFallbackStub.
|
|
union {
|
|
ICMonitoredFallbackStub* mainFallbackStub_;
|
|
ICEntry* icEntry_;
|
|
};
|
|
|
|
// Pointer to the first monitor stub.
|
|
ICStub* firstMonitorStub_;
|
|
|
|
// Address of the last monitor stub's field pointing to this
|
|
// fallback monitor stub. This will get updated when new
|
|
// monitor stubs are created and added.
|
|
ICStub** lastMonitorStubPtrAddr_;
|
|
|
|
// Count of optimized type monitor stubs in this chain.
|
|
uint32_t numOptimizedMonitorStubs_ : 7;
|
|
|
|
uint32_t invalid_ : 1;
|
|
|
|
// Whether this has a fallback stub referring to the IC entry.
|
|
bool hasFallbackStub_ : 1;
|
|
|
|
// Index of 'this' or argument which is being monitored, or BYTECODE_INDEX
|
|
// if this is monitoring the types of values pushed at some bytecode.
|
|
uint32_t argumentIndex_ : 23;
|
|
|
|
static const uint32_t BYTECODE_INDEX = (1 << 23) - 1;
|
|
|
|
ICTypeMonitor_Fallback(JitCode* stubCode, ICMonitoredFallbackStub* mainFallbackStub,
|
|
uint32_t argumentIndex)
|
|
: ICStub(ICStub::TypeMonitor_Fallback, stubCode),
|
|
mainFallbackStub_(mainFallbackStub),
|
|
firstMonitorStub_(thisFromCtor()),
|
|
lastMonitorStubPtrAddr_(nullptr),
|
|
numOptimizedMonitorStubs_(0),
|
|
invalid_(false),
|
|
hasFallbackStub_(mainFallbackStub != nullptr),
|
|
argumentIndex_(argumentIndex)
|
|
{ }
|
|
|
|
ICTypeMonitor_Fallback* thisFromCtor() {
|
|
return this;
|
|
}
|
|
|
|
void addOptimizedMonitorStub(ICStub* stub) {
|
|
MOZ_ASSERT(!invalid());
|
|
stub->setNext(this);
|
|
|
|
MOZ_ASSERT((lastMonitorStubPtrAddr_ != nullptr) ==
|
|
(numOptimizedMonitorStubs_ || !hasFallbackStub_));
|
|
|
|
if (lastMonitorStubPtrAddr_)
|
|
*lastMonitorStubPtrAddr_ = stub;
|
|
|
|
if (numOptimizedMonitorStubs_ == 0) {
|
|
MOZ_ASSERT(firstMonitorStub_ == this);
|
|
firstMonitorStub_ = stub;
|
|
} else {
|
|
MOZ_ASSERT(firstMonitorStub_ != nullptr);
|
|
}
|
|
|
|
lastMonitorStubPtrAddr_ = stub->addressOfNext();
|
|
numOptimizedMonitorStubs_++;
|
|
}
|
|
|
|
public:
|
|
bool hasStub(ICStub::Kind kind) {
|
|
ICStub* stub = firstMonitorStub_;
|
|
do {
|
|
if (stub->kind() == kind)
|
|
return true;
|
|
|
|
stub = stub->next();
|
|
} while (stub);
|
|
|
|
return false;
|
|
}
|
|
|
|
inline ICFallbackStub* mainFallbackStub() const {
|
|
MOZ_ASSERT(hasFallbackStub_);
|
|
return mainFallbackStub_;
|
|
}
|
|
|
|
inline ICEntry* icEntry() const {
|
|
return hasFallbackStub_ ? mainFallbackStub()->icEntry() : icEntry_;
|
|
}
|
|
|
|
inline ICStub* firstMonitorStub() const {
|
|
return firstMonitorStub_;
|
|
}
|
|
|
|
static inline size_t offsetOfFirstMonitorStub() {
|
|
return offsetof(ICTypeMonitor_Fallback, firstMonitorStub_);
|
|
}
|
|
|
|
inline uint32_t numOptimizedMonitorStubs() const {
|
|
return numOptimizedMonitorStubs_;
|
|
}
|
|
|
|
void setInvalid() {
|
|
invalid_ = 1;
|
|
}
|
|
|
|
bool invalid() const {
|
|
return invalid_;
|
|
}
|
|
|
|
inline bool monitorsThis() const {
|
|
return argumentIndex_ == 0;
|
|
}
|
|
|
|
inline bool monitorsArgument(uint32_t* pargument) const {
|
|
if (argumentIndex_ > 0 && argumentIndex_ < BYTECODE_INDEX) {
|
|
*pargument = argumentIndex_ - 1;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inline bool monitorsBytecode() const {
|
|
return argumentIndex_ == BYTECODE_INDEX;
|
|
}
|
|
|
|
// Fixup the IC entry as for a normal fallback stub, for this/arguments.
|
|
void fixupICEntry(ICEntry* icEntry) {
|
|
MOZ_ASSERT(!hasFallbackStub_);
|
|
MOZ_ASSERT(icEntry_ == nullptr);
|
|
MOZ_ASSERT(lastMonitorStubPtrAddr_ == nullptr);
|
|
icEntry_ = icEntry;
|
|
lastMonitorStubPtrAddr_ = icEntry_->addressOfFirstStub();
|
|
}
|
|
|
|
// Create a new monitor stub for the type of the given value, and
|
|
// add it to this chain.
|
|
MOZ_MUST_USE bool addMonitorStubForValue(JSContext* cx, SharedStubInfo* info, HandleValue val);
|
|
|
|
void resetMonitorStubChain(Zone* zone);
|
|
|
|
// Compiler for this stub kind.
|
|
class Compiler : public ICStubCompiler {
|
|
ICMonitoredFallbackStub* mainFallbackStub_;
|
|
uint32_t argumentIndex_;
|
|
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, Engine engine, ICMonitoredFallbackStub* mainFallbackStub)
|
|
: ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, engine),
|
|
mainFallbackStub_(mainFallbackStub),
|
|
argumentIndex_(BYTECODE_INDEX)
|
|
{ }
|
|
|
|
Compiler(JSContext* cx, Engine engine, uint32_t argumentIndex)
|
|
: ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, engine),
|
|
mainFallbackStub_(nullptr),
|
|
argumentIndex_(argumentIndex)
|
|
{ }
|
|
|
|
ICTypeMonitor_Fallback* getStub(ICStubSpace* space) {
|
|
return newStub<ICTypeMonitor_Fallback>(space, getStubCode(), mainFallbackStub_,
|
|
argumentIndex_);
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICTypeMonitor_PrimitiveSet : public TypeCheckPrimitiveSetStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
ICTypeMonitor_PrimitiveSet(JitCode* stubCode, uint16_t flags)
|
|
: TypeCheckPrimitiveSetStub(TypeMonitor_PrimitiveSet, stubCode, flags)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public TypeCheckPrimitiveSetStub::Compiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, Engine engine, ICTypeMonitor_PrimitiveSet* existingStub,
|
|
JSValueType type)
|
|
: TypeCheckPrimitiveSetStub::Compiler(cx, TypeMonitor_PrimitiveSet, engine, existingStub,
|
|
type)
|
|
{}
|
|
|
|
ICTypeMonitor_PrimitiveSet* updateStub() {
|
|
TypeCheckPrimitiveSetStub* stub =
|
|
this->TypeCheckPrimitiveSetStub::Compiler::updateStub();
|
|
if (!stub)
|
|
return nullptr;
|
|
return stub->toMonitorStub();
|
|
}
|
|
|
|
ICTypeMonitor_PrimitiveSet* getStub(ICStubSpace* space) {
|
|
MOZ_ASSERT(!existingStub_);
|
|
return newStub<ICTypeMonitor_PrimitiveSet>(space, getStubCode(), flags_);
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICTypeMonitor_SingleObject : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
GCPtrObject obj_;
|
|
|
|
ICTypeMonitor_SingleObject(JitCode* stubCode, JSObject* obj);
|
|
|
|
public:
|
|
GCPtrObject& object() {
|
|
return obj_;
|
|
}
|
|
|
|
static size_t offsetOfObject() {
|
|
return offsetof(ICTypeMonitor_SingleObject, obj_);
|
|
}
|
|
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
HandleObject obj_;
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, HandleObject obj)
|
|
: ICStubCompiler(cx, TypeMonitor_SingleObject, Engine::Baseline),
|
|
obj_(obj)
|
|
{ }
|
|
|
|
ICTypeMonitor_SingleObject* getStub(ICStubSpace* space) {
|
|
return newStub<ICTypeMonitor_SingleObject>(space, getStubCode(), obj_);
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICTypeMonitor_ObjectGroup : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
GCPtrObjectGroup group_;
|
|
|
|
ICTypeMonitor_ObjectGroup(JitCode* stubCode, ObjectGroup* group);
|
|
|
|
public:
|
|
GCPtrObjectGroup& group() {
|
|
return group_;
|
|
}
|
|
|
|
static size_t offsetOfGroup() {
|
|
return offsetof(ICTypeMonitor_ObjectGroup, group_);
|
|
}
|
|
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
HandleObjectGroup group_;
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, HandleObjectGroup group)
|
|
: ICStubCompiler(cx, TypeMonitor_ObjectGroup, Engine::Baseline),
|
|
group_(group)
|
|
{ }
|
|
|
|
ICTypeMonitor_ObjectGroup* getStub(ICStubSpace* space) {
|
|
return newStub<ICTypeMonitor_ObjectGroup>(space, getStubCode(), group_);
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
// BinaryArith
|
|
// JSOP_ADD, JSOP_SUB, JSOP_MUL, JOP_DIV, JSOP_MOD
|
|
// JSOP_BITAND, JSOP_BITXOR, JSOP_BITOR
|
|
// JSOP_LSH, JSOP_RSH, JSOP_URSH
|
|
|
|
class ICBinaryArith_Fallback : public ICFallbackStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICBinaryArith_Fallback(JitCode* stubCode)
|
|
: ICFallbackStub(BinaryArith_Fallback, stubCode)
|
|
{
|
|
extra_ = 0;
|
|
}
|
|
|
|
static const uint16_t SAW_DOUBLE_RESULT_BIT = 0x1;
|
|
static const uint16_t UNOPTIMIZABLE_OPERANDS_BIT = 0x2;
|
|
|
|
public:
|
|
static const uint32_t MAX_OPTIMIZED_STUBS = 8;
|
|
|
|
bool sawDoubleResult() const {
|
|
return extra_ & SAW_DOUBLE_RESULT_BIT;
|
|
}
|
|
void setSawDoubleResult() {
|
|
extra_ |= SAW_DOUBLE_RESULT_BIT;
|
|
}
|
|
bool hadUnoptimizableOperands() const {
|
|
return extra_ & UNOPTIMIZABLE_OPERANDS_BIT;
|
|
}
|
|
void noteUnoptimizableOperands() {
|
|
extra_ |= UNOPTIMIZABLE_OPERANDS_BIT;
|
|
}
|
|
|
|
// Compiler for this stub kind.
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
explicit Compiler(JSContext* cx, Engine engine)
|
|
: ICStubCompiler(cx, ICStub::BinaryArith_Fallback, engine) {}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICBinaryArith_Fallback>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICBinaryArith_Int32 : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
ICBinaryArith_Int32(JitCode* stubCode, bool allowDouble)
|
|
: ICStub(BinaryArith_Int32, stubCode)
|
|
{
|
|
extra_ = allowDouble;
|
|
}
|
|
|
|
public:
|
|
bool allowDouble() const {
|
|
return extra_;
|
|
}
|
|
|
|
// Compiler for this stub kind.
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
JSOp op_;
|
|
bool allowDouble_;
|
|
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
// Stub keys shift-stubs need to encode the kind, the JSOp and if we allow doubles.
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(static_cast<int32_t>(op_) << 17) |
|
|
(static_cast<int32_t>(allowDouble_) << 25);
|
|
}
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine, bool allowDouble)
|
|
: ICStubCompiler(cx, ICStub::BinaryArith_Int32, engine),
|
|
op_(op), allowDouble_(allowDouble) {}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICBinaryArith_Int32>(space, getStubCode(), allowDouble_);
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICBinaryArith_StringConcat : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICBinaryArith_StringConcat(JitCode* stubCode)
|
|
: ICStub(BinaryArith_StringConcat, stubCode)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
explicit Compiler(JSContext* cx, Engine engine)
|
|
: ICStubCompiler(cx, ICStub::BinaryArith_StringConcat, engine)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICBinaryArith_StringConcat>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICBinaryArith_StringObjectConcat : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
ICBinaryArith_StringObjectConcat(JitCode* stubCode, bool lhsIsString)
|
|
: ICStub(BinaryArith_StringObjectConcat, stubCode)
|
|
{
|
|
extra_ = lhsIsString;
|
|
}
|
|
|
|
public:
|
|
bool lhsIsString() const {
|
|
return extra_;
|
|
}
|
|
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
bool lhsIsString_;
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(static_cast<int32_t>(lhsIsString_) << 17);
|
|
}
|
|
|
|
public:
|
|
Compiler(JSContext* cx, Engine engine, bool lhsIsString)
|
|
: ICStubCompiler(cx, ICStub::BinaryArith_StringObjectConcat, engine),
|
|
lhsIsString_(lhsIsString)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICBinaryArith_StringObjectConcat>(space, getStubCode(),
|
|
lhsIsString_);
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICBinaryArith_Double : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICBinaryArith_Double(JitCode* stubCode)
|
|
: ICStub(BinaryArith_Double, stubCode)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICMultiStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
: ICMultiStubCompiler(cx, ICStub::BinaryArith_Double, op, engine)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICBinaryArith_Double>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICBinaryArith_BooleanWithInt32 : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
ICBinaryArith_BooleanWithInt32(JitCode* stubCode, bool lhsIsBool, bool rhsIsBool)
|
|
: ICStub(BinaryArith_BooleanWithInt32, stubCode)
|
|
{
|
|
MOZ_ASSERT(lhsIsBool || rhsIsBool);
|
|
extra_ = 0;
|
|
if (lhsIsBool)
|
|
extra_ |= 1;
|
|
if (rhsIsBool)
|
|
extra_ |= 2;
|
|
}
|
|
|
|
public:
|
|
bool lhsIsBoolean() const {
|
|
return extra_ & 1;
|
|
}
|
|
|
|
bool rhsIsBoolean() const {
|
|
return extra_ & 2;
|
|
}
|
|
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
JSOp op_;
|
|
bool lhsIsBool_;
|
|
bool rhsIsBool_;
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(static_cast<int32_t>(op_) << 17) |
|
|
(static_cast<int32_t>(lhsIsBool_) << 25) |
|
|
(static_cast<int32_t>(rhsIsBool_) << 26);
|
|
}
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsBool, bool rhsIsBool)
|
|
: ICStubCompiler(cx, ICStub::BinaryArith_BooleanWithInt32, engine),
|
|
op_(op), lhsIsBool_(lhsIsBool), rhsIsBool_(rhsIsBool)
|
|
{
|
|
MOZ_ASSERT(op_ == JSOP_ADD || op_ == JSOP_SUB || op_ == JSOP_BITOR ||
|
|
op_ == JSOP_BITAND || op_ == JSOP_BITXOR);
|
|
MOZ_ASSERT(lhsIsBool_ || rhsIsBool_);
|
|
}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICBinaryArith_BooleanWithInt32>(space, getStubCode(),
|
|
lhsIsBool_, rhsIsBool_);
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICBinaryArith_DoubleWithInt32 : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
ICBinaryArith_DoubleWithInt32(JitCode* stubCode, bool lhsIsDouble)
|
|
: ICStub(BinaryArith_DoubleWithInt32, stubCode)
|
|
{
|
|
extra_ = lhsIsDouble;
|
|
}
|
|
|
|
public:
|
|
bool lhsIsDouble() const {
|
|
return extra_;
|
|
}
|
|
|
|
class Compiler : public ICMultiStubCompiler {
|
|
protected:
|
|
bool lhsIsDouble_;
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(static_cast<int32_t>(op) << 17) |
|
|
(static_cast<int32_t>(lhsIsDouble_) << 25);
|
|
}
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsDouble)
|
|
: ICMultiStubCompiler(cx, ICStub::BinaryArith_DoubleWithInt32, op, engine),
|
|
lhsIsDouble_(lhsIsDouble)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICBinaryArith_DoubleWithInt32>(space, getStubCode(),
|
|
lhsIsDouble_);
|
|
}
|
|
};
|
|
};
|
|
|
|
// UnaryArith
|
|
// JSOP_BITNOT
|
|
// JSOP_NEG
|
|
|
|
class ICUnaryArith_Fallback : public ICFallbackStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICUnaryArith_Fallback(JitCode* stubCode)
|
|
: ICFallbackStub(UnaryArith_Fallback, stubCode)
|
|
{
|
|
extra_ = 0;
|
|
}
|
|
|
|
public:
|
|
static const uint32_t MAX_OPTIMIZED_STUBS = 8;
|
|
|
|
bool sawDoubleResult() {
|
|
return extra_;
|
|
}
|
|
void setSawDoubleResult() {
|
|
extra_ = 1;
|
|
}
|
|
|
|
// Compiler for this stub kind.
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
explicit Compiler(JSContext* cx, Engine engine)
|
|
: ICStubCompiler(cx, ICStub::UnaryArith_Fallback, engine)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICUnaryArith_Fallback>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICUnaryArith_Int32 : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICUnaryArith_Int32(JitCode* stubCode)
|
|
: ICStub(UnaryArith_Int32, stubCode)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICMultiStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
: ICMultiStubCompiler(cx, ICStub::UnaryArith_Int32, op, engine)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICUnaryArith_Int32>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICUnaryArith_Double : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICUnaryArith_Double(JitCode* stubCode)
|
|
: ICStub(UnaryArith_Double, stubCode)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICMultiStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
: ICMultiStubCompiler(cx, ICStub::UnaryArith_Double, op, engine)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICUnaryArith_Double>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
// Compare
|
|
// JSOP_LT
|
|
// JSOP_LE
|
|
// JSOP_GT
|
|
// JSOP_GE
|
|
// JSOP_EQ
|
|
// JSOP_NE
|
|
// JSOP_STRICTEQ
|
|
// JSOP_STRICTNE
|
|
|
|
class ICCompare_Fallback : public ICFallbackStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICCompare_Fallback(JitCode* stubCode)
|
|
: ICFallbackStub(ICStub::Compare_Fallback, stubCode) {}
|
|
|
|
public:
|
|
static const uint32_t MAX_OPTIMIZED_STUBS = 8;
|
|
|
|
static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
|
|
void noteUnoptimizableAccess() {
|
|
extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT);
|
|
}
|
|
bool hadUnoptimizableAccess() const {
|
|
return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT);
|
|
}
|
|
|
|
// Compiler for this stub kind.
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
explicit Compiler(JSContext* cx, Engine engine)
|
|
: ICStubCompiler(cx, ICStub::Compare_Fallback, engine) {}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICCompare_Fallback>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICCompare_Int32 : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICCompare_Int32(JitCode* stubCode)
|
|
: ICStub(ICStub::Compare_Int32, stubCode) {}
|
|
|
|
public:
|
|
// Compiler for this stub kind.
|
|
class Compiler : public ICMultiStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
: ICMultiStubCompiler(cx, ICStub::Compare_Int32, op, engine) {}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICCompare_Int32>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICCompare_Double : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICCompare_Double(JitCode* stubCode)
|
|
: ICStub(ICStub::Compare_Double, stubCode)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICMultiStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
: ICMultiStubCompiler(cx, ICStub::Compare_Double, op, engine)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICCompare_Double>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICCompare_NumberWithUndefined : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
ICCompare_NumberWithUndefined(JitCode* stubCode, bool lhsIsUndefined)
|
|
: ICStub(ICStub::Compare_NumberWithUndefined, stubCode)
|
|
{
|
|
extra_ = lhsIsUndefined;
|
|
}
|
|
|
|
public:
|
|
bool lhsIsUndefined() {
|
|
return extra_;
|
|
}
|
|
|
|
class Compiler : public ICMultiStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
bool lhsIsUndefined;
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined)
|
|
: ICMultiStubCompiler(cx, ICStub::Compare_NumberWithUndefined, op, engine),
|
|
lhsIsUndefined(lhsIsUndefined)
|
|
{}
|
|
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(static_cast<int32_t>(op) << 17) |
|
|
(static_cast<int32_t>(lhsIsUndefined) << 25);
|
|
}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICCompare_NumberWithUndefined>(space, getStubCode(),
|
|
lhsIsUndefined);
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICCompare_String : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICCompare_String(JitCode* stubCode)
|
|
: ICStub(ICStub::Compare_String, stubCode)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICMultiStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
: ICMultiStubCompiler(cx, ICStub::Compare_String, op, engine)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICCompare_String>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICCompare_Boolean : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICCompare_Boolean(JitCode* stubCode)
|
|
: ICStub(ICStub::Compare_Boolean, stubCode)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICMultiStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
: ICMultiStubCompiler(cx, ICStub::Compare_Boolean, op, engine)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICCompare_Boolean>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICCompare_Object : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICCompare_Object(JitCode* stubCode)
|
|
: ICStub(ICStub::Compare_Object, stubCode)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICMultiStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine)
|
|
: ICMultiStubCompiler(cx, ICStub::Compare_Object, op, engine)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICCompare_Object>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICCompare_ObjectWithUndefined : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICCompare_ObjectWithUndefined(JitCode* stubCode)
|
|
: ICStub(ICStub::Compare_ObjectWithUndefined, stubCode)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICMultiStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
bool lhsIsUndefined;
|
|
bool compareWithNull;
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsUndefined, bool compareWithNull)
|
|
: ICMultiStubCompiler(cx, ICStub::Compare_ObjectWithUndefined, op, engine),
|
|
lhsIsUndefined(lhsIsUndefined),
|
|
compareWithNull(compareWithNull)
|
|
{}
|
|
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(static_cast<int32_t>(op) << 17) |
|
|
(static_cast<int32_t>(lhsIsUndefined) << 25) |
|
|
(static_cast<int32_t>(compareWithNull) << 26);
|
|
}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICCompare_ObjectWithUndefined>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICCompare_Int32WithBoolean : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
ICCompare_Int32WithBoolean(JitCode* stubCode, bool lhsIsInt32)
|
|
: ICStub(ICStub::Compare_Int32WithBoolean, stubCode)
|
|
{
|
|
extra_ = lhsIsInt32;
|
|
}
|
|
|
|
public:
|
|
bool lhsIsInt32() const {
|
|
return extra_;
|
|
}
|
|
|
|
// Compiler for this stub kind.
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
JSOp op_;
|
|
bool lhsIsInt32_;
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(static_cast<int32_t>(op_) << 17) |
|
|
(static_cast<int32_t>(lhsIsInt32_) << 25);
|
|
}
|
|
|
|
public:
|
|
Compiler(JSContext* cx, JSOp op, Engine engine, bool lhsIsInt32)
|
|
: ICStubCompiler(cx, ICStub::Compare_Int32WithBoolean, engine),
|
|
op_(op),
|
|
lhsIsInt32_(lhsIsInt32)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICCompare_Int32WithBoolean>(space, getStubCode(), lhsIsInt32_);
|
|
}
|
|
};
|
|
};
|
|
|
|
// Enum for stubs handling a combination of typed arrays and typed objects.
|
|
enum TypedThingLayout {
|
|
Layout_TypedArray,
|
|
Layout_OutlineTypedObject,
|
|
Layout_InlineTypedObject
|
|
};
|
|
|
|
static inline TypedThingLayout
|
|
GetTypedThingLayout(const Class* clasp)
|
|
{
|
|
if (IsTypedArrayClass(clasp))
|
|
return Layout_TypedArray;
|
|
if (IsOutlineTypedObjectClass(clasp))
|
|
return Layout_OutlineTypedObject;
|
|
if (IsInlineTypedObjectClass(clasp))
|
|
return Layout_InlineTypedObject;
|
|
MOZ_CRASH("Bad object class");
|
|
}
|
|
|
|
bool
|
|
IsPreliminaryObject(JSObject* obj);
|
|
|
|
void
|
|
StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub);
|
|
|
|
MOZ_MUST_USE bool
|
|
EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandleId name,
|
|
MutableHandleObject holder, MutableHandleShape shape,
|
|
bool* checkDOMProxy=nullptr,
|
|
DOMProxyShadowsResult* shadowsResult=nullptr,
|
|
bool* domProxyHasGeneration=nullptr);
|
|
|
|
JSObject*
|
|
GetDOMProxyProto(JSObject* obj);
|
|
|
|
bool
|
|
IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy=false);
|
|
|
|
bool
|
|
IsCacheableGetPropReadSlot(JSObject* obj, JSObject* holder, Shape* shape, bool isDOMProxy=false);
|
|
|
|
void
|
|
GetFixedOrDynamicSlotOffset(Shape* shape, bool* isFixed, uint32_t* offset);
|
|
|
|
MOZ_MUST_USE bool
|
|
IsCacheableGetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape,
|
|
bool* isScripted, bool* isTemporarilyUnoptimizable, bool isDOMProxy=false);
|
|
|
|
MOZ_MUST_USE bool
|
|
UpdateExistingGetPropCallStubs(ICFallbackStub* fallbackStub,
|
|
ICStub::Kind kind,
|
|
HandleNativeObject holder,
|
|
HandleObject receiver,
|
|
HandleFunction getter);
|
|
MOZ_MUST_USE bool
|
|
CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, PropertyName* name,
|
|
JSObject** lastProto = nullptr, size_t* protoChainDepthOut = nullptr);
|
|
|
|
void
|
|
GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard,
|
|
Register object, Register scratch,
|
|
size_t receiverGuardOffset, Label* failure);
|
|
|
|
MOZ_MUST_USE bool
|
|
GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle<ShapeVector> shapes);
|
|
|
|
void
|
|
CheckDOMProxyExpandoDoesNotShadow(JSContext* cx, MacroAssembler& masm, Register object,
|
|
const Address& checkExpandoShapeAddr,
|
|
Address* expandoAndGenerationAddr,
|
|
Address* generationAddr,
|
|
Register scratch,
|
|
AllocatableGeneralRegisterSet& domProxyRegSet,
|
|
Label* checkFailed);
|
|
|
|
void
|
|
CheckForTypedObjectWithDetachedStorage(JSContext* cx, MacroAssembler& masm, Label* failure);
|
|
|
|
MOZ_MUST_USE bool
|
|
DoCallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj,
|
|
MutableHandleValue result);
|
|
|
|
void
|
|
LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result);
|
|
|
|
class ICGetProp_Fallback : public ICMonitoredFallbackStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICGetProp_Fallback(JitCode* stubCode)
|
|
: ICMonitoredFallbackStub(ICStub::GetProp_Fallback, stubCode)
|
|
{ }
|
|
|
|
public:
|
|
static const uint32_t MAX_OPTIMIZED_STUBS = 16;
|
|
static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
|
|
static const size_t ACCESSED_GETTER_BIT = 1;
|
|
|
|
void noteUnoptimizableAccess() {
|
|
extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT);
|
|
}
|
|
bool hadUnoptimizableAccess() const {
|
|
return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT);
|
|
}
|
|
|
|
void noteAccessedGetter() {
|
|
extra_ |= (1u << ACCESSED_GETTER_BIT);
|
|
}
|
|
bool hasAccessedGetter() const {
|
|
return extra_ & (1u << ACCESSED_GETTER_BIT);
|
|
}
|
|
|
|
class Compiler : public ICStubCompiler {
|
|
public:
|
|
static const int32_t BASELINE_KEY =
|
|
(static_cast<int32_t>(Engine::Baseline)) |
|
|
(static_cast<int32_t>(ICStub::GetProp_Fallback) << 1);
|
|
|
|
protected:
|
|
uint32_t returnOffset_;
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
void postGenerateStubCode(MacroAssembler& masm, Handle<JitCode*> code);
|
|
|
|
public:
|
|
explicit Compiler(JSContext* cx, Engine engine)
|
|
: ICStubCompiler(cx, ICStub::GetProp_Fallback, engine)
|
|
{ }
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
ICGetProp_Fallback* stub = newStub<ICGetProp_Fallback>(space, getStubCode());
|
|
if (!stub || !stub->initMonitoringChain(cx, space, engine_))
|
|
return nullptr;
|
|
return stub;
|
|
}
|
|
};
|
|
};
|
|
|
|
// Stub for sites, which are too polymorphic (i.e. MAX_OPTIMIZED_STUBS was reached)
|
|
class ICGetProp_Generic : public ICMonitoredStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
protected:
|
|
explicit ICGetProp_Generic(JitCode* stubCode, ICStub* firstMonitorStub)
|
|
: ICMonitoredStub(ICStub::GetProp_Generic, stubCode, firstMonitorStub) {}
|
|
|
|
public:
|
|
static ICGetProp_Generic* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
|
|
ICGetProp_Generic& other);
|
|
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
ICStub* firstMonitorStub_;
|
|
public:
|
|
explicit Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub)
|
|
: ICStubCompiler(cx, ICStub::GetProp_Generic, engine),
|
|
firstMonitorStub_(firstMonitorStub)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICGetProp_Generic>(space, getStubCode(), firstMonitorStub_);
|
|
}
|
|
};
|
|
};
|
|
|
|
// Stub for accessing a string's length.
|
|
class ICGetProp_StringLength : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICGetProp_StringLength(JitCode* stubCode)
|
|
: ICStub(GetProp_StringLength, stubCode)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICStubCompiler {
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
explicit Compiler(JSContext* cx, Engine engine)
|
|
: ICStubCompiler(cx, ICStub::GetProp_StringLength, engine)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICGetProp_StringLength>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
// Base class for native GetProp stubs.
|
|
class ICGetPropNativeStub : public ICMonitoredStub
|
|
{
|
|
// Object shape/group.
|
|
HeapReceiverGuard receiverGuard_;
|
|
|
|
// Fixed or dynamic slot offset.
|
|
uint32_t offset_;
|
|
|
|
protected:
|
|
ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
|
|
ReceiverGuard guard, uint32_t offset);
|
|
|
|
public:
|
|
HeapReceiverGuard& receiverGuard() {
|
|
return receiverGuard_;
|
|
}
|
|
uint32_t offset() const {
|
|
return offset_;
|
|
}
|
|
|
|
void notePreliminaryObject() {
|
|
extra_ = 1;
|
|
}
|
|
bool hasPreliminaryObject() const {
|
|
return extra_;
|
|
}
|
|
|
|
static size_t offsetOfReceiverGuard() {
|
|
return offsetof(ICGetPropNativeStub, receiverGuard_);
|
|
}
|
|
static size_t offsetOfOffset() {
|
|
return offsetof(ICGetPropNativeStub, offset_);
|
|
}
|
|
};
|
|
|
|
class ICGetPropNativePrototypeStub : public ICGetPropNativeStub
|
|
{
|
|
// Holder and its shape.
|
|
GCPtrObject holder_;
|
|
GCPtrShape holderShape_;
|
|
|
|
protected:
|
|
ICGetPropNativePrototypeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
|
|
ReceiverGuard guard, uint32_t offset, JSObject* holder,
|
|
Shape* holderShape);
|
|
|
|
public:
|
|
GCPtrObject& holder() {
|
|
return holder_;
|
|
}
|
|
GCPtrShape& holderShape() {
|
|
return holderShape_;
|
|
}
|
|
static size_t offsetOfHolder() {
|
|
return offsetof(ICGetPropNativePrototypeStub, holder_);
|
|
}
|
|
static size_t offsetOfHolderShape() {
|
|
return offsetof(ICGetPropNativePrototypeStub, holderShape_);
|
|
}
|
|
};
|
|
|
|
// Stub for accessing a non-lexical global name. Semantically, it is really a
|
|
// getprop: the name is either on the GlobalObject or its prototype chain. We
|
|
// teleport to the object that has the name, but we also need to guard on the
|
|
// shape of the global object.
|
|
//
|
|
// The receiver object is the global lexical scope.
|
|
class ICGetName_Global : public ICGetPropNativePrototypeStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
protected:
|
|
GCPtrShape globalShape_;
|
|
|
|
ICGetName_Global(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard,
|
|
uint32_t slot, JSObject* holder, Shape* holderShape, Shape* globalShape);
|
|
|
|
public:
|
|
static ICGetName_Global* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
|
|
ICGetName_Global& other);
|
|
|
|
GCPtrShape& globalShape() {
|
|
return globalShape_;
|
|
}
|
|
static size_t offsetOfGlobalShape() {
|
|
return offsetof(ICGetName_Global, globalShape_);
|
|
}
|
|
};
|
|
|
|
// Compiler for native GetProp stubs.
|
|
class ICGetPropNativeCompiler : public ICStubCompiler
|
|
{
|
|
ICStub* firstMonitorStub_;
|
|
HandleObject obj_;
|
|
HandleObject holder_;
|
|
HandlePropertyName propName_;
|
|
bool isFixedSlot_;
|
|
uint32_t offset_;
|
|
bool inputDefinitelyObject_;
|
|
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
protected:
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(static_cast<int32_t>(isFixedSlot_) << 17) |
|
|
(static_cast<int32_t>(inputDefinitelyObject_) << 18) |
|
|
(HeapReceiverGuard::keyBits(obj_) << 19);
|
|
}
|
|
|
|
public:
|
|
ICGetPropNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStubCompiler::Engine engine,
|
|
ICStub* firstMonitorStub, HandleObject obj, HandleObject holder,
|
|
HandlePropertyName propName, bool isFixedSlot, uint32_t offset,
|
|
bool inputDefinitelyObject = false)
|
|
: ICStubCompiler(cx, kind, engine),
|
|
firstMonitorStub_(firstMonitorStub),
|
|
obj_(obj),
|
|
holder_(holder),
|
|
propName_(propName),
|
|
isFixedSlot_(isFixedSlot),
|
|
offset_(offset),
|
|
inputDefinitelyObject_(inputDefinitelyObject)
|
|
{}
|
|
|
|
ICGetPropNativeStub* getStub(ICStubSpace* space);
|
|
};
|
|
|
|
static uint32_t
|
|
SimpleTypeDescrKey(SimpleTypeDescr* descr)
|
|
{
|
|
if (descr->is<ScalarTypeDescr>())
|
|
return uint32_t(descr->as<ScalarTypeDescr>().type()) << 1;
|
|
return (uint32_t(descr->as<ReferenceTypeDescr>().type()) << 1) | 1;
|
|
}
|
|
|
|
inline bool
|
|
SimpleTypeDescrKeyIsScalar(uint32_t key)
|
|
{
|
|
return !(key & 1);
|
|
}
|
|
|
|
inline ScalarTypeDescr::Type
|
|
ScalarTypeFromSimpleTypeDescrKey(uint32_t key)
|
|
{
|
|
MOZ_ASSERT(SimpleTypeDescrKeyIsScalar(key));
|
|
return ScalarTypeDescr::Type(key >> 1);
|
|
}
|
|
|
|
inline ReferenceTypeDescr::Type
|
|
ReferenceTypeFromSimpleTypeDescrKey(uint32_t key)
|
|
{
|
|
MOZ_ASSERT(!SimpleTypeDescrKeyIsScalar(key));
|
|
return ReferenceTypeDescr::Type(key >> 1);
|
|
}
|
|
|
|
class ICGetPropCallGetter : public ICMonitoredStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
protected:
|
|
// Shape/group of receiver object. Used for both own and proto getters.
|
|
// In the GetPropCallDOMProxyNative case, the receiver guard enforces
|
|
// the proxy handler, because Shape implies Class.
|
|
HeapReceiverGuard receiverGuard_;
|
|
|
|
// Holder and holder shape. For own getters, guarding on receiverGuard_ is
|
|
// sufficient, although Ion may use holder_ and holderShape_ even for own
|
|
// getters. In this case holderShape_ == receiverGuard_.shape_ (isOwnGetter
|
|
// below relies on this).
|
|
GCPtrObject holder_;
|
|
|
|
GCPtrShape holderShape_;
|
|
|
|
// Function to call.
|
|
GCPtrFunction getter_;
|
|
|
|
// PC offset of call
|
|
uint32_t pcOffset_;
|
|
|
|
ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
|
|
ReceiverGuard receiverGuard, JSObject* holder,
|
|
Shape* holderShape, JSFunction* getter, uint32_t pcOffset);
|
|
|
|
public:
|
|
GCPtrObject& holder() {
|
|
return holder_;
|
|
}
|
|
GCPtrShape& holderShape() {
|
|
return holderShape_;
|
|
}
|
|
GCPtrFunction& getter() {
|
|
return getter_;
|
|
}
|
|
HeapReceiverGuard& receiverGuard() {
|
|
return receiverGuard_;
|
|
}
|
|
|
|
bool isOwnGetter() const {
|
|
MOZ_ASSERT(holder_->isNative());
|
|
MOZ_ASSERT(holderShape_);
|
|
return receiverGuard_.shape() == holderShape_;
|
|
}
|
|
|
|
static size_t offsetOfHolder() {
|
|
return offsetof(ICGetPropCallGetter, holder_);
|
|
}
|
|
static size_t offsetOfHolderShape() {
|
|
return offsetof(ICGetPropCallGetter, holderShape_);
|
|
}
|
|
static size_t offsetOfGetter() {
|
|
return offsetof(ICGetPropCallGetter, getter_);
|
|
}
|
|
static size_t offsetOfPCOffset() {
|
|
return offsetof(ICGetPropCallGetter, pcOffset_);
|
|
}
|
|
static size_t offsetOfReceiverGuard() {
|
|
return offsetof(ICGetPropCallGetter, receiverGuard_);
|
|
}
|
|
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
ICStub* firstMonitorStub_;
|
|
RootedObject receiver_;
|
|
RootedObject holder_;
|
|
RootedFunction getter_;
|
|
uint32_t pcOffset_;
|
|
const Class* outerClass_;
|
|
|
|
virtual int32_t getKey() const {
|
|
// ICGetPropCallNativeCompiler::getKey adds more bits to our
|
|
// return value, so be careful when making changes here.
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(HeapReceiverGuard::keyBits(receiver_) << 17) |
|
|
(static_cast<int32_t>(!!outerClass_) << 19) |
|
|
(static_cast<int32_t>(receiver_ != holder_) << 20);
|
|
}
|
|
|
|
public:
|
|
Compiler(JSContext* cx, ICStub::Kind kind, Engine engine, ICStub* firstMonitorStub,
|
|
HandleObject receiver, HandleObject holder, HandleFunction getter,
|
|
uint32_t pcOffset, const Class* outerClass)
|
|
: ICStubCompiler(cx, kind, engine),
|
|
firstMonitorStub_(firstMonitorStub),
|
|
receiver_(cx, receiver),
|
|
holder_(cx, holder),
|
|
getter_(cx, getter),
|
|
pcOffset_(pcOffset),
|
|
outerClass_(outerClass)
|
|
{
|
|
MOZ_ASSERT(kind == ICStub::GetProp_CallScripted ||
|
|
kind == ICStub::GetProp_CallNative ||
|
|
kind == ICStub::GetProp_CallNativeGlobal);
|
|
}
|
|
};
|
|
};
|
|
|
|
// Stub for calling a scripted getter on a native object when the getter is kept on the
|
|
// proto-chain.
|
|
class ICGetProp_CallScripted : public ICGetPropCallGetter
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
protected:
|
|
ICGetProp_CallScripted(JitCode* stubCode, ICStub* firstMonitorStub,
|
|
ReceiverGuard receiverGuard,
|
|
JSObject* holder, Shape* holderShape,
|
|
JSFunction* getter, uint32_t pcOffset)
|
|
: ICGetPropCallGetter(GetProp_CallScripted, stubCode, firstMonitorStub,
|
|
receiverGuard, holder, holderShape, getter, pcOffset)
|
|
{}
|
|
|
|
public:
|
|
static ICGetProp_CallScripted* Clone(JSContext* cx, ICStubSpace* space,
|
|
ICStub* firstMonitorStub, ICGetProp_CallScripted& other);
|
|
|
|
class Compiler : public ICGetPropCallGetter::Compiler {
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, ICStub* firstMonitorStub, HandleObject obj,
|
|
HandleObject holder, HandleFunction getter, uint32_t pcOffset)
|
|
: ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallScripted, Engine::Baseline,
|
|
firstMonitorStub, obj, holder,
|
|
getter, pcOffset, /* outerClass = */ nullptr)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
ReceiverGuard guard(receiver_);
|
|
Shape* holderShape = holder_->as<NativeObject>().lastProperty();
|
|
return newStub<ICGetProp_CallScripted>(space, getStubCode(), firstMonitorStub_,
|
|
guard, holder_, holderShape, getter_,
|
|
pcOffset_);
|
|
}
|
|
};
|
|
};
|
|
|
|
// Stub for calling a native getter on a native object.
|
|
class ICGetProp_CallNative : public ICGetPropCallGetter
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
protected:
|
|
|
|
ICGetProp_CallNative(JitCode* stubCode, ICStub* firstMonitorStub,
|
|
ReceiverGuard receiverGuard,
|
|
JSObject* holder, Shape* holderShape,
|
|
JSFunction* getter, uint32_t pcOffset)
|
|
: ICGetPropCallGetter(GetProp_CallNative, stubCode, firstMonitorStub,
|
|
receiverGuard, holder, holderShape, getter, pcOffset)
|
|
{}
|
|
|
|
public:
|
|
static ICGetProp_CallNative* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
|
|
ICGetProp_CallNative& other);
|
|
|
|
};
|
|
|
|
// Stub for calling a native getter on the GlobalObject.
|
|
class ICGetProp_CallNativeGlobal : public ICGetPropCallGetter
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
protected:
|
|
GCPtrShape globalShape_;
|
|
|
|
ICGetProp_CallNativeGlobal(JitCode* stubCode, ICStub* firstMonitorStub,
|
|
ReceiverGuard receiverGuard,
|
|
JSObject* holder, Shape* holderShape, Shape* globalShape,
|
|
JSFunction* getter, uint32_t pcOffset)
|
|
: ICGetPropCallGetter(GetProp_CallNativeGlobal, stubCode, firstMonitorStub,
|
|
receiverGuard, holder, holderShape, getter, pcOffset),
|
|
globalShape_(globalShape)
|
|
{ }
|
|
|
|
public:
|
|
static ICGetProp_CallNativeGlobal* Clone(JSContext* cx, ICStubSpace* space,
|
|
ICStub* firstMonitorStub,
|
|
ICGetProp_CallNativeGlobal& other);
|
|
|
|
GCPtrShape& globalShape() {
|
|
return globalShape_;
|
|
}
|
|
static size_t offsetOfGlobalShape() {
|
|
return offsetof(ICGetProp_CallNativeGlobal, globalShape_);
|
|
}
|
|
};
|
|
|
|
class ICGetPropCallNativeCompiler : public ICGetPropCallGetter::Compiler
|
|
{
|
|
bool inputDefinitelyObject_;
|
|
protected:
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
virtual int32_t getKey() const {
|
|
int32_t baseKey = ICGetPropCallGetter::Compiler::getKey();
|
|
MOZ_ASSERT((baseKey >> 21) == 0);
|
|
return baseKey | (static_cast<int32_t>(inputDefinitelyObject_) << 21);
|
|
}
|
|
|
|
public:
|
|
ICGetPropCallNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStubCompiler::Engine engine,
|
|
ICStub* firstMonitorStub, HandleObject receiver,
|
|
HandleObject holder, HandleFunction getter, uint32_t pcOffset,
|
|
const Class* outerClass, bool inputDefinitelyObject = false)
|
|
: ICGetPropCallGetter::Compiler(cx, kind, engine, firstMonitorStub, receiver, holder,
|
|
getter, pcOffset, outerClass),
|
|
inputDefinitelyObject_(inputDefinitelyObject)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space);
|
|
};
|
|
|
|
class ICGetPropCallDOMProxyNativeStub : public ICGetPropCallGetter
|
|
{
|
|
friend class ICStubSpace;
|
|
protected:
|
|
// Object shape of expected expando object. (nullptr if no expando object should be there)
|
|
GCPtrShape expandoShape_;
|
|
|
|
ICGetPropCallDOMProxyNativeStub(ICStub::Kind kind, JitCode* stubCode,
|
|
ICStub* firstMonitorStub, Shape* shape,
|
|
Shape* expandoShape,
|
|
JSObject* holder, Shape* holderShape,
|
|
JSFunction* getter, uint32_t pcOffset);
|
|
|
|
public:
|
|
GCPtrShape& expandoShape() {
|
|
return expandoShape_;
|
|
}
|
|
static size_t offsetOfExpandoShape() {
|
|
return offsetof(ICGetPropCallDOMProxyNativeStub, expandoShape_);
|
|
}
|
|
};
|
|
|
|
class ICGetProp_CallDOMProxyNative : public ICGetPropCallDOMProxyNativeStub
|
|
{
|
|
friend class ICStubSpace;
|
|
ICGetProp_CallDOMProxyNative(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape,
|
|
Shape* expandoShape,
|
|
JSObject* holder, Shape* holderShape,
|
|
JSFunction* getter, uint32_t pcOffset)
|
|
: ICGetPropCallDOMProxyNativeStub(ICStub::GetProp_CallDOMProxyNative, stubCode,
|
|
firstMonitorStub, shape, expandoShape,
|
|
holder, holderShape, getter, pcOffset)
|
|
{}
|
|
|
|
public:
|
|
static ICGetProp_CallDOMProxyNative* Clone(JSContext* cx,
|
|
ICStubSpace* space,
|
|
ICStub* firstMonitorStub,
|
|
ICGetProp_CallDOMProxyNative& other);
|
|
};
|
|
|
|
class ICGetProp_CallDOMProxyWithGenerationNative : public ICGetPropCallDOMProxyNativeStub
|
|
{
|
|
protected:
|
|
ExpandoAndGeneration* expandoAndGeneration_;
|
|
uint64_t generation_;
|
|
|
|
public:
|
|
ICGetProp_CallDOMProxyWithGenerationNative(JitCode* stubCode, ICStub* firstMonitorStub,
|
|
Shape* shape,
|
|
ExpandoAndGeneration* expandoAndGeneration,
|
|
uint64_t generation, Shape* expandoShape,
|
|
JSObject* holder, Shape* holderShape,
|
|
JSFunction* getter, uint32_t pcOffset)
|
|
: ICGetPropCallDOMProxyNativeStub(ICStub::GetProp_CallDOMProxyWithGenerationNative,
|
|
stubCode, firstMonitorStub, shape,
|
|
expandoShape, holder, holderShape, getter, pcOffset),
|
|
expandoAndGeneration_(expandoAndGeneration),
|
|
generation_(generation)
|
|
{
|
|
}
|
|
|
|
static ICGetProp_CallDOMProxyWithGenerationNative*
|
|
Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
|
|
ICGetProp_CallDOMProxyWithGenerationNative& other);
|
|
|
|
void* expandoAndGeneration() const {
|
|
return expandoAndGeneration_;
|
|
}
|
|
uint64_t generation() const {
|
|
return generation_;
|
|
}
|
|
|
|
void setGeneration(uint64_t value) {
|
|
generation_ = value;
|
|
}
|
|
|
|
static size_t offsetOfInternalStruct() {
|
|
return offsetof(ICGetProp_CallDOMProxyWithGenerationNative, expandoAndGeneration_);
|
|
}
|
|
static size_t offsetOfGeneration() {
|
|
return offsetof(ICGetProp_CallDOMProxyWithGenerationNative, generation_);
|
|
}
|
|
};
|
|
|
|
class ICGetPropCallDOMProxyNativeCompiler : public ICStubCompiler {
|
|
ICStub* firstMonitorStub_;
|
|
Rooted<ProxyObject*> proxy_;
|
|
RootedObject holder_;
|
|
RootedFunction getter_;
|
|
uint32_t pcOffset_;
|
|
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm, Address* internalStructAddr,
|
|
Address* generationAddr);
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
ICGetPropCallDOMProxyNativeCompiler(JSContext* cx, ICStub::Kind kind,
|
|
ICStubCompiler::Engine engine,
|
|
ICStub* firstMonitorStub, Handle<ProxyObject*> proxy,
|
|
HandleObject holder, HandleFunction getter,
|
|
uint32_t pcOffset);
|
|
|
|
ICStub* getStub(ICStubSpace* space);
|
|
};
|
|
|
|
class ICGetProp_DOMProxyShadowed : public ICMonitoredStub
|
|
{
|
|
friend class ICStubSpace;
|
|
protected:
|
|
GCPtrShape shape_;
|
|
const BaseProxyHandler* proxyHandler_;
|
|
GCPtrPropertyName name_;
|
|
uint32_t pcOffset_;
|
|
|
|
ICGetProp_DOMProxyShadowed(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape,
|
|
const BaseProxyHandler* proxyHandler, PropertyName* name,
|
|
uint32_t pcOffset);
|
|
|
|
public:
|
|
static ICGetProp_DOMProxyShadowed* Clone(JSContext* cx, ICStubSpace* space,
|
|
ICStub* firstMonitorStub,
|
|
ICGetProp_DOMProxyShadowed& other);
|
|
|
|
GCPtrShape& shape() {
|
|
return shape_;
|
|
}
|
|
GCPtrPropertyName& name() {
|
|
return name_;
|
|
}
|
|
|
|
static size_t offsetOfShape() {
|
|
return offsetof(ICGetProp_DOMProxyShadowed, shape_);
|
|
}
|
|
static size_t offsetOfProxyHandler() {
|
|
return offsetof(ICGetProp_DOMProxyShadowed, proxyHandler_);
|
|
}
|
|
static size_t offsetOfName() {
|
|
return offsetof(ICGetProp_DOMProxyShadowed, name_);
|
|
}
|
|
static size_t offsetOfPCOffset() {
|
|
return offsetof(ICGetProp_DOMProxyShadowed, pcOffset_);
|
|
}
|
|
|
|
class Compiler : public ICStubCompiler {
|
|
ICStub* firstMonitorStub_;
|
|
Rooted<ProxyObject*> proxy_;
|
|
RootedPropertyName name_;
|
|
uint32_t pcOffset_;
|
|
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub, Handle<ProxyObject*> proxy,
|
|
HandlePropertyName name, uint32_t pcOffset)
|
|
: ICStubCompiler(cx, ICStub::GetProp_CallNative, engine),
|
|
firstMonitorStub_(firstMonitorStub),
|
|
proxy_(cx, proxy),
|
|
name_(cx, name),
|
|
pcOffset_(pcOffset)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space);
|
|
};
|
|
};
|
|
|
|
class ICGetProp_ArgumentsLength : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
public:
|
|
enum Which { Magic };
|
|
|
|
protected:
|
|
explicit ICGetProp_ArgumentsLength(JitCode* stubCode)
|
|
: ICStub(ICStub::GetProp_ArgumentsLength, stubCode)
|
|
{ }
|
|
|
|
public:
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
Which which_;
|
|
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
virtual int32_t getKey() const {
|
|
return static_cast<int32_t>(engine_) |
|
|
(static_cast<int32_t>(kind) << 1) |
|
|
(static_cast<int32_t>(which_) << 17);
|
|
}
|
|
|
|
public:
|
|
Compiler(JSContext* cx, Engine engine, Which which)
|
|
: ICStubCompiler(cx, ICStub::GetProp_ArgumentsLength, engine),
|
|
which_(which)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICGetProp_ArgumentsLength>(space, getStubCode());
|
|
}
|
|
};
|
|
};
|
|
|
|
class ICGetProp_ArgumentsCallee : public ICMonitoredStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
protected:
|
|
ICGetProp_ArgumentsCallee(JitCode* stubCode, ICStub* firstMonitorStub);
|
|
|
|
public:
|
|
class Compiler : public ICStubCompiler {
|
|
protected:
|
|
ICStub* firstMonitorStub_;
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub)
|
|
: ICStubCompiler(cx, ICStub::GetProp_ArgumentsCallee, engine),
|
|
firstMonitorStub_(firstMonitorStub)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICGetProp_ArgumentsCallee>(space, getStubCode(), firstMonitorStub_);
|
|
}
|
|
};
|
|
};
|
|
|
|
// JSOP_NEWARRAY
|
|
// JSOP_NEWINIT
|
|
|
|
class ICNewArray_Fallback : public ICFallbackStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
GCPtrObject templateObject_;
|
|
|
|
// The group used for objects created here is always available, even if the
|
|
// template object itself is not.
|
|
GCPtrObjectGroup templateGroup_;
|
|
|
|
ICNewArray_Fallback(JitCode* stubCode, ObjectGroup* templateGroup)
|
|
: ICFallbackStub(ICStub::NewArray_Fallback, stubCode),
|
|
templateObject_(nullptr), templateGroup_(templateGroup)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICStubCompiler {
|
|
RootedObjectGroup templateGroup;
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
Compiler(JSContext* cx, ObjectGroup* templateGroup, Engine engine)
|
|
: ICStubCompiler(cx, ICStub::NewArray_Fallback, engine),
|
|
templateGroup(cx, templateGroup)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICNewArray_Fallback>(space, getStubCode(), templateGroup);
|
|
}
|
|
};
|
|
|
|
GCPtrObject& templateObject() {
|
|
return templateObject_;
|
|
}
|
|
|
|
void setTemplateObject(JSObject* obj) {
|
|
MOZ_ASSERT(obj->group() == templateGroup());
|
|
templateObject_ = obj;
|
|
}
|
|
|
|
GCPtrObjectGroup& templateGroup() {
|
|
return templateGroup_;
|
|
}
|
|
|
|
void setTemplateGroup(ObjectGroup* group) {
|
|
templateObject_ = nullptr;
|
|
templateGroup_ = group;
|
|
}
|
|
};
|
|
|
|
// JSOP_NEWOBJECT
|
|
|
|
class ICNewObject_Fallback : public ICFallbackStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
GCPtrObject templateObject_;
|
|
|
|
explicit ICNewObject_Fallback(JitCode* stubCode)
|
|
: ICFallbackStub(ICStub::NewObject_Fallback, stubCode), templateObject_(nullptr)
|
|
{}
|
|
|
|
public:
|
|
class Compiler : public ICStubCompiler {
|
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
|
|
|
public:
|
|
explicit Compiler(JSContext* cx, Engine engine)
|
|
: ICStubCompiler(cx, ICStub::NewObject_Fallback, engine)
|
|
{}
|
|
|
|
ICStub* getStub(ICStubSpace* space) {
|
|
return newStub<ICNewObject_Fallback>(space, getStubCode());
|
|
}
|
|
};
|
|
|
|
GCPtrObject& templateObject() {
|
|
return templateObject_;
|
|
}
|
|
|
|
void setTemplateObject(JSObject* obj) {
|
|
templateObject_ = obj;
|
|
}
|
|
};
|
|
|
|
class ICNewObject_WithTemplate : public ICStub
|
|
{
|
|
friend class ICStubSpace;
|
|
|
|
explicit ICNewObject_WithTemplate(JitCode* stubCode)
|
|
: ICStub(ICStub::NewObject_WithTemplate, stubCode)
|
|
{}
|
|
};
|
|
|
|
} // namespace jit
|
|
} // namespace js
|
|
|
|
#endif /* jit_SharedIC_h */
|