summaryrefslogtreecommitdiff
path: root/js/src/methodjit/PolyIC.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/methodjit/PolyIC.h')
-rw-r--r--js/src/methodjit/PolyIC.h561
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__ */
+