mirror of
https://github.com/UOX3DevTeam/UOX3
synced 2026-08-13 12:27:04 -04:00
561 lines
18 KiB
C++
561 lines
18 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=4 sw=4 et tw=99:
|
|
*
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
|
|
* May 28, 2008.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Brendan Eich <brendan@mozilla.org>
|
|
*
|
|
* Contributor(s):
|
|
* David Mandelin <dmandelin@mozilla.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#if !defined jsjaeger_poly_ic_h__ && defined JS_METHODJIT
|
|
#define jsjaeger_poly_ic_h__
|
|
|
|
#include "jscntxt.h"
|
|
#include "jstl.h"
|
|
#include "jsvector.h"
|
|
#include "assembler/assembler/MacroAssembler.h"
|
|
#include "assembler/assembler/CodeLocation.h"
|
|
#include "methodjit/MethodJIT.h"
|
|
#include "methodjit/ICRepatcher.h"
|
|
#include "BaseAssembler.h"
|
|
#include "RematInfo.h"
|
|
#include "BaseCompiler.h"
|
|
#include "methodjit/ICLabels.h"
|
|
#include "assembler/moco/MocoStubs.h"
|
|
|
|
namespace js {
|
|
namespace mjit {
|
|
namespace ic {
|
|
|
|
/* Maximum number of stubs for a given callsite. */
|
|
static const uint32 MAX_PIC_STUBS = 16;
|
|
static const uint32 MAX_GETELEM_IC_STUBS = 17;
|
|
|
|
void PurgePICs(JSContext *cx);
|
|
|
|
enum LookupStatus {
|
|
Lookup_Error = 0,
|
|
Lookup_Uncacheable,
|
|
Lookup_Cacheable
|
|
};
|
|
|
|
struct BaseIC : public MacroAssemblerTypedefs {
|
|
BaseIC() { }
|
|
|
|
// Address of inline fast-path.
|
|
CodeLocationLabel fastPathStart;
|
|
|
|
// Address to rejoin to the fast-path.
|
|
CodeLocationLabel fastPathRejoin;
|
|
|
|
// Start of the slow path.
|
|
CodeLocationLabel slowPathStart;
|
|
|
|
// Slow path stub call.
|
|
CodeLocationCall slowPathCall;
|
|
|
|
// Whether or not the callsite has been hit at least once.
|
|
bool hit : 1;
|
|
bool slowCallPatched : 1;
|
|
|
|
// Number of stubs generated.
|
|
uint32 stubsGenerated : 5;
|
|
|
|
// Offset from start of stub to jump target of second shape guard as Nitro
|
|
// asm data location. This is 0 if there is only one shape guard in the
|
|
// last stub.
|
|
int secondShapeGuard : 11;
|
|
|
|
// Opcode this was compiled for.
|
|
JSOp op : 9;
|
|
|
|
void reset() {
|
|
hit = false;
|
|
slowCallPatched = false;
|
|
stubsGenerated = 0;
|
|
secondShapeGuard = 0;
|
|
}
|
|
bool shouldUpdate(JSContext *cx);
|
|
void spew(JSContext *cx, const char *event, const char *reason);
|
|
LookupStatus disable(JSContext *cx, const char *reason, void *stub);
|
|
bool isCallOp();
|
|
};
|
|
|
|
class BasePolyIC : public BaseIC {
|
|
typedef Vector<JSC::ExecutablePool *, 2, SystemAllocPolicy> ExecPoolVector;
|
|
|
|
// ExecutablePools that IC stubs were generated into. Very commonly (eg.
|
|
// 99.5% of BasePolyICs) there are 0 or 1, and there are lots of
|
|
// BasePolyICs, so we space-optimize for that case. If the bottom bit of
|
|
// the pointer is 0, execPool should be used, and it will be NULL (for 0
|
|
// pools) or non-NULL (for 1 pool). If the bottom bit of the
|
|
// pointer is 1, taggedExecPools should be used, but only after de-tagging
|
|
// (for 2 or more pools).
|
|
union {
|
|
JSC::ExecutablePool *execPool; // valid when bottom bit is a 0
|
|
ExecPoolVector *taggedExecPools; // valid when bottom bit is a 1
|
|
} u;
|
|
|
|
static bool isTagged(void *p) {
|
|
return !!(intptr_t(p) & 1);
|
|
}
|
|
|
|
static ExecPoolVector *tag(ExecPoolVector *p) {
|
|
JS_ASSERT(!isTagged(p));
|
|
return (ExecPoolVector *)(intptr_t(p) | 1);
|
|
}
|
|
|
|
static ExecPoolVector *detag(ExecPoolVector *p) {
|
|
JS_ASSERT(isTagged(p));
|
|
return (ExecPoolVector *)(intptr_t(p) & ~1);
|
|
}
|
|
|
|
bool areZeroPools() { return !u.execPool; }
|
|
bool isOnePool() { return u.execPool && !isTagged(u.execPool); }
|
|
bool areMultiplePools() { return isTagged(u.taggedExecPools); }
|
|
|
|
ExecPoolVector *multiplePools() {
|
|
JS_ASSERT(areMultiplePools());
|
|
return detag(u.taggedExecPools);
|
|
}
|
|
|
|
public:
|
|
BasePolyIC() {
|
|
u.execPool = NULL;
|
|
}
|
|
|
|
~BasePolyIC() {
|
|
releasePools();
|
|
if (areMultiplePools())
|
|
js_delete(multiplePools());
|
|
}
|
|
|
|
void reset() {
|
|
BaseIC::reset();
|
|
releasePools();
|
|
if (areZeroPools()) {
|
|
// Common case: do nothing.
|
|
} else if (isOnePool()) {
|
|
u.execPool = NULL;
|
|
} else {
|
|
multiplePools()->clear();
|
|
}
|
|
}
|
|
|
|
void releasePools() {
|
|
if (areZeroPools()) {
|
|
// Common case: do nothing.
|
|
} else if (isOnePool()) {
|
|
u.execPool->release();
|
|
} else {
|
|
ExecPoolVector *execPools = multiplePools();
|
|
for (size_t i = 0; i < execPools->length(); i++)
|
|
(*execPools)[i]->release();
|
|
}
|
|
}
|
|
|
|
bool addPool(JSContext *cx, JSC::ExecutablePool *pool) {
|
|
if (areZeroPools()) {
|
|
u.execPool = pool;
|
|
return true;
|
|
}
|
|
if (isOnePool()) {
|
|
JSC::ExecutablePool *oldPool = u.execPool;
|
|
JS_ASSERT(!isTagged(oldPool));
|
|
ExecPoolVector *execPools = js_new<ExecPoolVector>(SystemAllocPolicy());
|
|
if (!execPools)
|
|
return false;
|
|
if (!execPools->append(oldPool) || !execPools->append(pool)) {
|
|
js_delete(execPools);
|
|
return false;
|
|
}
|
|
u.taggedExecPools = tag(execPools);
|
|
return true;
|
|
}
|
|
return multiplePools()->append(pool);
|
|
}
|
|
};
|
|
|
|
struct GetElementIC : public BasePolyIC {
|
|
GetElementIC() { reset(); }
|
|
|
|
// On stub entry:
|
|
// If hasInlineTypeCheck() is true, and inlineTypeCheckPatched is false,
|
|
// - typeReg contains the type of the |id| parameter.
|
|
// If hasInlineTypeCheck() is true, and inlineTypeCheckPatched is true,
|
|
// - typeReg contains the shape of |objReg| iff typeRegHasBaseShape
|
|
// is true.
|
|
// Otherwise, typeReg is garbage.
|
|
//
|
|
// On stub exit, typeReg must contain the type of the result value.
|
|
RegisterID typeReg : 5;
|
|
|
|
// On stub entry, objReg contains the object pointer for the |obj| parameter.
|
|
// On stub exit, objReg must contain the payload of the result value.
|
|
RegisterID objReg : 5;
|
|
|
|
// Offset from the fast path to the inline type check.
|
|
// This is only set if hasInlineTypeCheck() is true.
|
|
unsigned inlineTypeGuard : 8;
|
|
|
|
// Offset from the fast path to the inline clasp guard. This is always
|
|
// set; if |id| is known to not be int32, then it's an unconditional
|
|
// jump to the slow path.
|
|
unsigned inlineClaspGuard : 8;
|
|
|
|
// This is usable if hasInlineTypeGuard() returns true, which implies
|
|
// that a dense array fast path exists. The inline type guard serves as
|
|
// the head of the chain of all string-based element stubs.
|
|
bool inlineTypeGuardPatched : 1;
|
|
|
|
// This is always usable, and specifies whether the inline clasp guard
|
|
// has been patched. If hasInlineTypeGuard() is true, it guards against
|
|
// a dense array, and guarantees the inline type guard has passed.
|
|
// Otherwise, there is no inline type guard, and the clasp guard is just
|
|
// an unconditional jump.
|
|
bool inlineClaspGuardPatched : 1;
|
|
|
|
////////////////////////////////////////////
|
|
// State for string-based property stubs. //
|
|
////////////////////////////////////////////
|
|
|
|
// True if typeReg is guaranteed to have the shape of objReg.
|
|
bool typeRegHasBaseShape : 1;
|
|
|
|
// These offsets are used for string-key dependent stubs, such as named
|
|
// property accesses. They are separated from the int-key dependent stubs,
|
|
// in order to guarantee that the id type needs only one guard per type.
|
|
int atomGuard : 8; // optional, non-zero if present
|
|
int firstShapeGuard : 8; // always set
|
|
int secondShapeGuard : 8; // optional, non-zero if present
|
|
|
|
bool hasLastStringStub : 1;
|
|
JITCode lastStringStub;
|
|
|
|
// A limited ValueRemat instance. It may contains either:
|
|
// 1) A constant, or
|
|
// 2) A known type and data reg, or
|
|
// 3) A data reg.
|
|
// The sync bits are not set, and the type reg is never set and should not
|
|
// be used, as it is encapsulated more accurately in |typeReg|. Also, note
|
|
// carefully that the data reg is immutable.
|
|
ValueRemat idRemat;
|
|
|
|
bool hasInlineTypeGuard() const {
|
|
return !idRemat.isTypeKnown();
|
|
}
|
|
bool shouldPatchInlineTypeGuard() {
|
|
return hasInlineTypeGuard() && !inlineTypeGuardPatched;
|
|
}
|
|
bool shouldPatchUnconditionalClaspGuard() {
|
|
// The clasp guard is only unconditional if the type is known to not
|
|
// be an int32.
|
|
if (idRemat.isTypeKnown() && idRemat.knownType() != JSVAL_TYPE_INT32)
|
|
return !inlineClaspGuardPatched;
|
|
return false;
|
|
}
|
|
|
|
void reset() {
|
|
BasePolyIC::reset();
|
|
inlineTypeGuardPatched = false;
|
|
inlineClaspGuardPatched = false;
|
|
typeRegHasBaseShape = false;
|
|
hasLastStringStub = false;
|
|
}
|
|
void purge(Repatcher &repatcher);
|
|
LookupStatus update(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp);
|
|
LookupStatus attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid id,
|
|
Value *vp);
|
|
LookupStatus attachTypedArray(JSContext *cx, JSObject *obj, const Value &v, jsid id,
|
|
Value *vp);
|
|
LookupStatus disable(JSContext *cx, const char *reason);
|
|
LookupStatus error(JSContext *cx);
|
|
bool shouldUpdate(JSContext *cx);
|
|
};
|
|
|
|
struct SetElementIC : public BaseIC {
|
|
SetElementIC() : execPool(NULL) { reset(); }
|
|
~SetElementIC() {
|
|
if (execPool)
|
|
execPool->release();
|
|
}
|
|
|
|
// On stub entry:
|
|
// objReg contains the payload of the |obj| parameter.
|
|
// On stub exit:
|
|
// objReg may be clobbered.
|
|
RegisterID objReg : 5;
|
|
|
|
// Information on how to rematerialize |objReg|.
|
|
int32 objRemat : MIN_STATE_REMAT_BITS;
|
|
|
|
// Offset from the start of the fast path to the inline clasp guard.
|
|
unsigned inlineClaspGuard : 6;
|
|
|
|
// True if the clasp guard has been patched; false otherwise.
|
|
bool inlineClaspGuardPatched : 1;
|
|
|
|
// Offset from the start of the fast path to the inline hole guard.
|
|
unsigned inlineHoleGuard : 8;
|
|
|
|
// True if the capacity guard has been patched; false otherwise.
|
|
bool inlineHoleGuardPatched : 1;
|
|
|
|
// True if this is from a strict-mode script.
|
|
bool strictMode : 1;
|
|
|
|
// A bitmask of registers that are volatile and must be preserved across
|
|
// stub calls inside the IC.
|
|
uint32 volatileMask : 16;
|
|
|
|
// If true, then keyValue contains a constant index value >= 0. Otherwise,
|
|
// keyReg contains a dynamic integer index in any range.
|
|
bool hasConstantKey : 1;
|
|
union {
|
|
RegisterID keyReg;
|
|
int32 keyValue;
|
|
};
|
|
|
|
// Rematerialize information about the value being stored.
|
|
ValueRemat vr;
|
|
|
|
// Optional executable pool for the out-of-line hole stub.
|
|
JSC::ExecutablePool *execPool;
|
|
|
|
void reset() {
|
|
BaseIC::reset();
|
|
if (execPool != NULL)
|
|
execPool->release();
|
|
execPool = NULL;
|
|
inlineClaspGuardPatched = false;
|
|
inlineHoleGuardPatched = false;
|
|
}
|
|
void purge(Repatcher &repatcher);
|
|
LookupStatus attachTypedArray(JSContext *cx, JSObject *obj, int32 key);
|
|
LookupStatus attachHoleStub(JSContext *cx, JSObject *obj, int32 key);
|
|
LookupStatus update(JSContext *cx, const Value &objval, const Value &idval);
|
|
LookupStatus disable(JSContext *cx, const char *reason);
|
|
LookupStatus error(JSContext *cx);
|
|
};
|
|
|
|
struct PICInfo : public BasePolyIC {
|
|
PICInfo() { reset(); }
|
|
|
|
// Operation this is a PIC for.
|
|
enum Kind
|
|
#ifdef _MSC_VER
|
|
: uint8_t
|
|
#endif
|
|
{
|
|
GET, // JSOP_GETPROP
|
|
CALL, // JSOP_CALLPROP
|
|
SET, // JSOP_SETPROP, JSOP_SETNAME
|
|
SETMETHOD, // JSOP_SETMETHOD
|
|
NAME, // JSOP_NAME
|
|
BIND, // JSOP_BINDNAME
|
|
XNAME // JSOP_GETXPROP
|
|
};
|
|
|
|
union {
|
|
struct {
|
|
RegisterID typeReg : 5; // reg used for checking type
|
|
bool hasTypeCheck : 1; // type check and reg are present
|
|
|
|
// Reverse offset from slowPathStart to the type check slow path.
|
|
int32 typeCheckOffset;
|
|
} get;
|
|
ValueRemat vr;
|
|
} u;
|
|
|
|
// Address of the start of the last generated stub, if any. Note that this
|
|
// does not correctly overlay with the allocated memory; it does however
|
|
// overlay the portion that may need to be patched, which is good enough.
|
|
JITCode lastStubStart;
|
|
|
|
// Return the start address of the last path in this PIC, which is the
|
|
// inline path if no stubs have been generated yet.
|
|
CodeLocationLabel lastPathStart() {
|
|
if (!stubsGenerated)
|
|
return fastPathStart;
|
|
return CodeLocationLabel(lastStubStart.start());
|
|
}
|
|
|
|
CodeLocationLabel getFastShapeGuard() {
|
|
return fastPathStart.labelAtOffset(shapeGuard);
|
|
}
|
|
|
|
CodeLocationLabel getSlowTypeCheck() {
|
|
JS_ASSERT(isGet());
|
|
return slowPathStart.labelAtOffset(u.get.typeCheckOffset);
|
|
}
|
|
|
|
// Return a JITCode block corresponding to the code memory to attach a
|
|
// new stub to.
|
|
JITCode lastCodeBlock(JITScript *jit) {
|
|
if (!stubsGenerated)
|
|
return JITCode(jit->code.m_code.executableAddress(), jit->code.m_size);
|
|
return lastStubStart;
|
|
}
|
|
|
|
void updateLastPath(LinkerHelper &linker, Label label) {
|
|
CodeLocationLabel loc = linker.locationOf(label);
|
|
lastStubStart = JITCode(loc.executableAddress(), linker.size());
|
|
}
|
|
|
|
Kind kind : 3;
|
|
|
|
// True if register R holds the base object shape along exits from the
|
|
// last stub.
|
|
bool shapeRegHasBaseShape : 1;
|
|
|
|
// True if can use the property cache.
|
|
bool usePropCache : 1;
|
|
|
|
// State flags.
|
|
bool inlinePathPatched : 1; // inline path has been patched
|
|
|
|
RegisterID shapeReg : 5; // also the out type reg
|
|
RegisterID objReg : 5; // also the out data reg
|
|
|
|
// Offset from start of fast path to initial shape guard.
|
|
uint32 shapeGuard;
|
|
|
|
inline bool isSet() const {
|
|
return kind == SET || kind == SETMETHOD;
|
|
}
|
|
inline bool isGet() const {
|
|
return kind == GET || kind == CALL;
|
|
}
|
|
inline bool isBind() const {
|
|
return kind == BIND;
|
|
}
|
|
inline bool isScopeName() const {
|
|
return kind == NAME || kind == XNAME;
|
|
}
|
|
inline RegisterID typeReg() {
|
|
JS_ASSERT(isGet());
|
|
return u.get.typeReg;
|
|
}
|
|
inline bool hasTypeCheck() {
|
|
JS_ASSERT(isGet());
|
|
return u.get.hasTypeCheck;
|
|
}
|
|
inline bool shapeNeedsRemat() {
|
|
return !shapeRegHasBaseShape;
|
|
}
|
|
inline bool isFastCall() {
|
|
JS_ASSERT(kind == CALL);
|
|
return !hasTypeCheck();
|
|
}
|
|
|
|
#if !defined JS_HAS_IC_LABELS
|
|
static GetPropLabels getPropLabels_;
|
|
static SetPropLabels setPropLabels_;
|
|
static BindNameLabels bindNameLabels_;
|
|
static ScopeNameLabels scopeNameLabels_;
|
|
#else
|
|
union {
|
|
GetPropLabels getPropLabels_;
|
|
SetPropLabels setPropLabels_;
|
|
BindNameLabels bindNameLabels_;
|
|
ScopeNameLabels scopeNameLabels_;
|
|
};
|
|
void setLabels(const ic::GetPropLabels &labels) {
|
|
JS_ASSERT(isGet());
|
|
getPropLabels_ = labels;
|
|
}
|
|
void setLabels(const ic::SetPropLabels &labels) {
|
|
JS_ASSERT(isSet());
|
|
setPropLabels_ = labels;
|
|
}
|
|
void setLabels(const ic::BindNameLabels &labels) {
|
|
JS_ASSERT(kind == BIND);
|
|
bindNameLabels_ = labels;
|
|
}
|
|
void setLabels(const ic::ScopeNameLabels &labels) {
|
|
JS_ASSERT(kind == NAME || kind == XNAME);
|
|
scopeNameLabels_ = labels;
|
|
}
|
|
#endif
|
|
|
|
GetPropLabels &getPropLabels() {
|
|
JS_ASSERT(isGet());
|
|
return getPropLabels_;
|
|
}
|
|
SetPropLabels &setPropLabels() {
|
|
JS_ASSERT(isSet());
|
|
return setPropLabels_;
|
|
}
|
|
BindNameLabels &bindNameLabels() {
|
|
JS_ASSERT(kind == BIND);
|
|
return bindNameLabels_;
|
|
}
|
|
ScopeNameLabels &scopeNameLabels() {
|
|
JS_ASSERT(kind == NAME || kind == XNAME);
|
|
return scopeNameLabels_;
|
|
}
|
|
|
|
// Where in the script did we generate this PIC?
|
|
jsbytecode *pc;
|
|
|
|
// Index into the script's atom table.
|
|
JSAtom *atom;
|
|
|
|
// Reset the data members to the state of a fresh PIC before any patching
|
|
// or stub generation was done.
|
|
void reset() {
|
|
BasePolyIC::reset();
|
|
inlinePathPatched = false;
|
|
shapeRegHasBaseShape = true;
|
|
}
|
|
};
|
|
|
|
#ifdef JS_POLYIC
|
|
void PurgePICs(JSContext *cx, JSScript *script);
|
|
void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *);
|
|
void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *);
|
|
void JS_FASTCALL CallProp(VMFrame &f, ic::PICInfo *);
|
|
void JS_FASTCALL Name(VMFrame &f, ic::PICInfo *);
|
|
void JS_FASTCALL XName(VMFrame &f, ic::PICInfo *);
|
|
void JS_FASTCALL BindName(VMFrame &f, ic::PICInfo *);
|
|
void JS_FASTCALL GetElement(VMFrame &f, ic::GetElementIC *);
|
|
void JS_FASTCALL CallElement(VMFrame &f, ic::GetElementIC *);
|
|
template <JSBool strict> void JS_FASTCALL SetElement(VMFrame &f, ic::SetElementIC *);
|
|
#endif
|
|
|
|
} /* namespace ic */
|
|
} /* namespace mjit */
|
|
} /* namespace js */
|
|
|
|
#endif /* jsjaeger_poly_ic_h__ */
|
|
|