diff options
Diffstat (limited to 'js/src/methodjit/PolyIC.h')
-rw-r--r-- | js/src/methodjit/PolyIC.h | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/js/src/methodjit/PolyIC.h b/js/src/methodjit/PolyIC.h new file mode 100644 index 0000000..14e53e3 --- /dev/null +++ b/js/src/methodjit/PolyIC.h @@ -0,0 +1,561 @@ +/* -*- 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__ */ + |