summaryrefslogtreecommitdiff
path: root/js/src/jstracer.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jstracer.h')
-rw-r--r--js/src/jstracer.h1895
1 files changed, 1895 insertions, 0 deletions
diff --git a/js/src/jstracer.h b/js/src/jstracer.h
new file mode 100644
index 0000000..76921cc
--- /dev/null
+++ b/js/src/jstracer.h
@@ -0,0 +1,1895 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=99 ft=cpp:
+ *
+ * ***** 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):
+ * Andreas Gal <gal@mozilla.com>
+ * Mike Shaver <shaver@mozilla.org>
+ * David Anderson <danderson@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 ***** */
+
+#ifndef jstracer_h___
+#define jstracer_h___
+
+#ifdef JS_TRACER
+
+#include "jstypes.h"
+#include "jsbuiltins.h"
+#include "jscntxt.h"
+#include "jsdhash.h"
+#include "jsinterp.h"
+#include "jslock.h"
+#include "jsnum.h"
+#include "jsvector.h"
+#include "jscompartment.h"
+#include "Writer.h"
+
+namespace js {
+
+template <typename T>
+class Queue {
+ T* _data;
+ unsigned _len;
+ unsigned _max;
+ nanojit::Allocator* alloc;
+
+public:
+ void ensure(unsigned size) {
+ if (_max > size)
+ return;
+ if (!_max)
+ _max = 8;
+ _max = JS_MAX(_max * 2, size);
+ if (alloc) {
+ T* tmp = new (*alloc) T[_max];
+ memcpy(tmp, _data, _len * sizeof(T));
+ _data = tmp;
+ } else {
+ _data = (T*)js_realloc(_data, _max * sizeof(T));
+ }
+#if defined(DEBUG)
+ memset(&_data[_len], 0xcd, _max - _len);
+#endif
+ }
+
+ Queue(nanojit::Allocator* alloc)
+ : alloc(alloc)
+ {
+ this->_max =
+ this->_len = 0;
+ this->_data = NULL;
+ }
+
+ ~Queue() {
+ if (!alloc)
+ js_free(_data);
+ }
+
+ bool contains(T a) {
+ for (unsigned n = 0; n < _len; ++n) {
+ if (_data[n] == a)
+ return true;
+ }
+ return false;
+ }
+
+ void add(T a) {
+ ensure(_len + 1);
+ JS_ASSERT(_len <= _max);
+ _data[_len++] = a;
+ }
+
+ void add(T* chunk, unsigned size) {
+ ensure(_len + size);
+ JS_ASSERT(_len <= _max);
+ memcpy(&_data[_len], chunk, size * sizeof(T));
+ _len += size;
+ }
+
+ void addUnique(T a) {
+ if (!contains(a))
+ add(a);
+ }
+
+ void setLength(unsigned len) {
+ ensure(len + 1);
+ _len = len;
+ }
+
+ void clear() {
+ _len = 0;
+ }
+
+ T & get(unsigned i) {
+ JS_ASSERT(i < length());
+ return _data[i];
+ }
+
+ const T & get(unsigned i) const {
+ JS_ASSERT(i < length());
+ return _data[i];
+ }
+
+ T & operator [](unsigned i) {
+ return get(i);
+ }
+
+ const T & operator [](unsigned i) const {
+ return get(i);
+ }
+
+ unsigned length() const {
+ return _len;
+ }
+
+ T* data() const {
+ return _data;
+ }
+
+ int offsetOf(T slot) {
+ T* p = _data;
+ unsigned n = 0;
+ for (n = 0; n < _len; ++n)
+ if (*p++ == slot)
+ return n;
+ return -1;
+ }
+
+};
+
+/*
+ * Tracker is used to keep track of values being manipulated by the interpreter
+ * during trace recording. It maps opaque, 4-byte aligned address to LIns pointers.
+ * pointers. To do this efficiently, we observe that the addresses of jsvals
+ * living in the interpreter tend to be aggregated close to each other -
+ * usually on the same page (where a tracker page doesn't have to be the same
+ * size as the OS page size, but it's typically similar). The Tracker
+ * consists of a linked-list of structures representing a memory page, which
+ * are created on-demand as memory locations are used.
+ *
+ * For every address, first we split it into two parts: upper bits which
+ * represent the "base", and lower bits which represent an offset against the
+ * base. For the offset, we then right-shift it by two because the bottom two
+ * bits of a 4-byte aligned address are always zero. The mapping then
+ * becomes:
+ *
+ * page = page in pagelist such that Base(address) == page->base,
+ * page->map[Offset(address)]
+ */
+class Tracker {
+ #define TRACKER_PAGE_SZB 4096
+ #define TRACKER_PAGE_ENTRIES (TRACKER_PAGE_SZB >> 2) // each slot is 4 bytes
+ #define TRACKER_PAGE_MASK jsuword(TRACKER_PAGE_SZB - 1)
+
+ struct TrackerPage {
+ struct TrackerPage* next;
+ jsuword base;
+ nanojit::LIns* map[TRACKER_PAGE_ENTRIES];
+ };
+ struct TrackerPage* pagelist;
+
+ jsuword getTrackerPageBase(const void* v) const;
+ jsuword getTrackerPageOffset(const void* v) const;
+ struct TrackerPage* findTrackerPage(const void* v) const;
+ struct TrackerPage* addTrackerPage(const void* v);
+public:
+ Tracker();
+ ~Tracker();
+
+ bool has(const void* v) const;
+ nanojit::LIns* get(const void* v) const;
+ void set(const void* v, nanojit::LIns* ins);
+ void clear();
+};
+
+class VMFragment : public nanojit::Fragment {
+public:
+ VMFragment(const void* _ip verbose_only(, uint32_t profFragID))
+ : Fragment(_ip verbose_only(, profFragID))
+ {}
+
+ /*
+ * If this is anchored off a TreeFragment, this points to that tree fragment.
+ * Otherwise, it is |this|.
+ */
+ TreeFragment* root;
+
+ TreeFragment* toTreeFragment();
+};
+
+#ifdef NJ_NO_VARIADIC_MACROS
+
+#define debug_only_stmt(action) /* */
+static void debug_only_printf(int mask, const char *fmt, ...) JS_BEGIN_MACRO JS_END_MACRO
+#define debug_only_print0(mask, str) JS_BEGIN_MACRO JS_END_MACRO
+
+#elif defined(JS_JIT_SPEW)
+
+// Top level logging controller object.
+extern nanojit::LogControl LogController;
+
+// Top level profiling hook, needed to harvest profile info from Fragments
+// whose logical lifetime is about to finish
+extern void FragProfiling_FragFinalizer(nanojit::Fragment* f, TraceMonitor*);
+
+#define debug_only_stmt(stmt) \
+ stmt
+
+#define debug_only_printf(mask, fmt, ...) \
+ JS_BEGIN_MACRO \
+ if ((LogController.lcbits & (mask)) > 0) { \
+ LogController.printf(fmt, __VA_ARGS__); \
+ fflush(stdout); \
+ } \
+ JS_END_MACRO
+
+#define debug_only_print0(mask, str) \
+ JS_BEGIN_MACRO \
+ if ((LogController.lcbits & (mask)) > 0) { \
+ LogController.printf("%s", str); \
+ fflush(stdout); \
+ } \
+ JS_END_MACRO
+
+#else
+
+#define debug_only_stmt(action) /* */
+#define debug_only_printf(mask, fmt, ...) JS_BEGIN_MACRO JS_END_MACRO
+#define debug_only_print0(mask, str) JS_BEGIN_MACRO JS_END_MACRO
+
+#endif
+
+/*
+ * The oracle keeps track of hit counts for program counter locations, as
+ * well as slots that should not be demoted to int because we know them to
+ * overflow or they result in type-unstable traces. We are using simple
+ * hash tables. Collisions lead to loss of optimization (demotable slots
+ * are not demoted, etc.) but have no correctness implications.
+ */
+#define ORACLE_SIZE 4096
+
+class Oracle {
+ avmplus::BitSet _stackDontDemote;
+ avmplus::BitSet _globalDontDemote;
+ avmplus::BitSet _pcDontDemote;
+ avmplus::BitSet _pcSlowZeroTest;
+public:
+ Oracle();
+
+ JS_REQUIRES_STACK void markGlobalSlotUndemotable(JSContext* cx, unsigned slot);
+ JS_REQUIRES_STACK bool isGlobalSlotUndemotable(JSContext* cx, unsigned slot) const;
+ JS_REQUIRES_STACK void markStackSlotUndemotable(JSContext* cx, unsigned slot);
+ JS_REQUIRES_STACK void markStackSlotUndemotable(JSContext* cx, unsigned slot, const void* pc);
+ JS_REQUIRES_STACK bool isStackSlotUndemotable(JSContext* cx, unsigned slot) const;
+ JS_REQUIRES_STACK bool isStackSlotUndemotable(JSContext* cx, unsigned slot, const void* pc) const;
+ void markInstructionUndemotable(jsbytecode* pc);
+ bool isInstructionUndemotable(jsbytecode* pc) const;
+ void markInstructionSlowZeroTest(jsbytecode* pc);
+ bool isInstructionSlowZeroTest(jsbytecode* pc) const;
+
+ void clearDemotability();
+ void clear() {
+ clearDemotability();
+ }
+};
+
+typedef Queue<uint16> SlotList;
+
+class TypeMap : public Queue<JSValueType> {
+ Oracle *oracle;
+public:
+ TypeMap(nanojit::Allocator* alloc, Oracle *oracle)
+ : Queue<JSValueType>(alloc),
+ oracle(oracle)
+ {}
+ void set(unsigned stackSlots, unsigned ngslots,
+ const JSValueType* stackTypeMap, const JSValueType* globalTypeMap);
+ JS_REQUIRES_STACK void captureTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, unsigned callDepth,
+ bool speculate);
+ JS_REQUIRES_STACK void captureMissingGlobalTypes(JSContext* cx, JSObject* globalObj, SlotList& slots,
+ unsigned stackSlots, bool speculate);
+ bool matches(TypeMap& other) const;
+ void fromRaw(JSValueType* other, unsigned numSlots);
+};
+
+#define JS_TM_EXITCODES(_) \
+ /* \
+ * An exit at a possible branch-point in the trace at which to attach a \
+ * future secondary trace. Therefore the recorder must generate different \
+ * code to handle the other outcome of the branch condition from the \
+ * primary trace's outcome. \
+ */ \
+ _(BRANCH) \
+ /* \
+ * Exit at a tableswitch via a numbered case. \
+ */ \
+ _(CASE) \
+ /* \
+ * Exit at a tableswitch via the default case. \
+ */ \
+ _(DEFAULT) \
+ _(LOOP) \
+ _(NESTED) \
+ /* \
+ * An exit from a trace because a condition relied upon at recording time \
+ * no longer holds, where the alternate path of execution is so rare or \
+ * difficult to address in native code that it is not traced at all, e.g. \
+ * negative array index accesses, which differ from positive indexes in \
+ * that they require a string-based property lookup rather than a simple \
+ * memory access. \
+ */ \
+ _(MISMATCH) \
+ /* \
+ * A specialization of MISMATCH_EXIT to handle allocation failures. \
+ */ \
+ _(OOM) \
+ _(OVERFLOW) \
+ _(MUL_ZERO) \
+ _(UNSTABLE_LOOP) \
+ _(TIMEOUT) \
+ _(DEEP_BAIL) \
+ _(STATUS)
+
+enum ExitType {
+ #define MAKE_EXIT_CODE(x) x##_EXIT,
+ JS_TM_EXITCODES(MAKE_EXIT_CODE)
+ #undef MAKE_EXIT_CODE
+ TOTAL_EXIT_TYPES
+};
+
+struct FrameInfo;
+
+struct VMSideExit : public nanojit::SideExit
+{
+ jsbytecode* pc;
+ jsbytecode* imacpc;
+ intptr_t sp_adj;
+ intptr_t rp_adj;
+ int32_t calldepth;
+ uint32 numGlobalSlots;
+ uint32 numStackSlots;
+ uint32 numStackSlotsBelowCurrentFrame;
+ ExitType exitType;
+ uintN lookupFlags;
+ unsigned hitcount;
+
+ inline JSValueType* stackTypeMap() {
+ return (JSValueType*)(this + 1);
+ }
+
+ inline JSValueType& stackType(unsigned i) {
+ JS_ASSERT(i < numStackSlots);
+ return stackTypeMap()[i];
+ }
+
+ inline JSValueType* globalTypeMap() {
+ return (JSValueType*)(this + 1) + this->numStackSlots;
+ }
+
+ inline JSValueType* fullTypeMap() {
+ return stackTypeMap();
+ }
+
+ inline VMFragment* fromFrag() {
+ return (VMFragment*)from;
+ }
+
+ inline TreeFragment* root() {
+ return fromFrag()->root;
+ }
+};
+
+class VMAllocator : public nanojit::Allocator
+{
+
+public:
+ VMAllocator(char* reserve, size_t reserveSize)
+ : mOutOfMemory(false), mSize(0), mReserve(reserve),
+ mReserveCurr(uintptr_t(reserve)), mReserveLimit(uintptr_t(reserve + reserveSize))
+ {}
+
+ ~VMAllocator() {
+ js_free(mReserve);
+ }
+
+ size_t size() {
+ return mSize;
+ }
+
+ bool outOfMemory() {
+ return mOutOfMemory;
+ }
+
+ struct Mark
+ {
+ VMAllocator& vma;
+ bool committed;
+ nanojit::Allocator::Chunk* saved_chunk;
+ char* saved_top;
+ char* saved_limit;
+ size_t saved_size;
+
+ Mark(VMAllocator& vma) :
+ vma(vma),
+ committed(false),
+ saved_chunk(vma.current_chunk),
+ saved_top(vma.current_top),
+ saved_limit(vma.current_limit),
+ saved_size(vma.mSize)
+ {}
+
+ ~Mark()
+ {
+ if (!committed)
+ vma.rewind(*this);
+ }
+
+ void commit() { committed = true; }
+ };
+
+ void rewind(const Mark& m) {
+ while (current_chunk != m.saved_chunk) {
+ Chunk *prev = current_chunk->prev;
+ freeChunk(current_chunk);
+ current_chunk = prev;
+ }
+ current_top = m.saved_top;
+ current_limit = m.saved_limit;
+ mSize = m.saved_size;
+ memset(current_top, 0, current_limit - current_top);
+ }
+
+ bool mOutOfMemory;
+ size_t mSize;
+
+ /* See nanojit::Allocator::allocChunk() for details on these. */
+ char* mReserve;
+ uintptr_t mReserveCurr;
+ uintptr_t mReserveLimit;
+};
+
+struct FrameInfo {
+ JSObject* block; // caller block chain head
+ jsbytecode* pc; // caller fp->regs->pc
+ jsbytecode* imacpc; // caller fp->imacpc
+ uint32 spdist; // distance from fp->slots to fp->regs->sp at JSOP_CALL
+
+ /*
+ * Bit 15 (0x8000) is a flag that is set if constructing (called through new).
+ * Bits 0-14 are the actual argument count. This may be less than fun->nargs.
+ * NB: This is argc for the callee, not the caller.
+ */
+ uint32 argc;
+
+ /*
+ * Number of stack slots in the caller, not counting slots pushed when
+ * invoking the callee. That is, slots after JSOP_CALL completes but
+ * without the return value. This is also equal to the number of slots
+ * between fp->prev->argv[-2] (calleR fp->callee) and fp->argv[-2]
+ * (calleE fp->callee).
+ */
+ uint32 callerHeight;
+
+ /* argc of the caller */
+ uint32 callerArgc;
+
+ // Safer accessors for argc.
+ enum { CONSTRUCTING_FLAG = 0x10000 };
+ void set_argc(uint16 argc, bool constructing) {
+ this->argc = uint32(argc) | (constructing ? CONSTRUCTING_FLAG: 0);
+ }
+ uint16 get_argc() const { return uint16(argc & ~CONSTRUCTING_FLAG); }
+ bool is_constructing() const { return (argc & CONSTRUCTING_FLAG) != 0; }
+
+ // The typemap just before the callee is called.
+ JSValueType* get_typemap() { return (JSValueType*) (this+1); }
+ const JSValueType* get_typemap() const { return (JSValueType*) (this+1); }
+};
+
+struct UnstableExit
+{
+ VMFragment* fragment;
+ VMSideExit* exit;
+ UnstableExit* next;
+};
+
+struct LinkableFragment : public VMFragment
+{
+ LinkableFragment(const void* _ip, nanojit::Allocator* alloc, Oracle *oracle
+ verbose_only(, uint32_t profFragID))
+ : VMFragment(_ip verbose_only(, profFragID)), typeMap(alloc, oracle), nStackTypes(0)
+ { }
+
+ uint32 branchCount;
+ TypeMap typeMap;
+ unsigned nStackTypes;
+ unsigned spOffsetAtEntry;
+ SlotList* globalSlots;
+};
+
+/*
+ * argc is cx->fp->argc at the trace loop header, i.e., the number of arguments
+ * pushed for the innermost JS frame. This is required as part of the fragment
+ * key because the fragment will write those arguments back to the interpreter
+ * stack when it exits, using its typemap, which implicitly incorporates a
+ * given value of argc. Without this feature, a fragment could be called as an
+ * inner tree with two different values of argc, and entry type checking or
+ * exit frame synthesis could crash.
+ */
+struct TreeFragment : public LinkableFragment
+{
+ TreeFragment(const void* _ip, nanojit::Allocator* alloc, Oracle *oracle, JSObject* _globalObj,
+ uint32 _globalShape, uint32 _argc verbose_only(, uint32_t profFragID)):
+ LinkableFragment(_ip, alloc, oracle verbose_only(, profFragID)),
+ first(NULL),
+ next(NULL),
+ peer(NULL),
+ globalObj(_globalObj),
+ globalShape(_globalShape),
+ argc(_argc),
+ dependentTrees(alloc),
+ linkedTrees(alloc),
+ sideExits(alloc),
+ gcthings(alloc),
+ shapes(alloc)
+ { }
+
+ TreeFragment* first;
+ TreeFragment* next;
+ TreeFragment* peer;
+ JSObject* globalObj;
+ uint32 globalShape;
+ uint32 argc;
+ /* Dependent trees must be trashed if this tree dies, and updated on missing global types */
+ Queue<TreeFragment*> dependentTrees;
+ /* Linked trees must be updated on missing global types, but are not dependent */
+ Queue<TreeFragment*> linkedTrees;
+#ifdef DEBUG
+ const char* treeFileName;
+ uintN treeLineNumber;
+ uintN treePCOffset;
+#endif
+ JSScript* script;
+ UnstableExit* unstableExits;
+ Queue<VMSideExit*> sideExits;
+ ptrdiff_t nativeStackBase;
+ unsigned maxCallDepth;
+ /* All embedded GC things are registered here so the GC can scan them. */
+ Queue<Value> gcthings;
+ Queue<const js::Shape*> shapes;
+ unsigned maxNativeStackSlots;
+ /* Gives the number of times we have entered this trace. */
+ uintN execs;
+ /* Gives the total number of iterations executed by the trace (up to a limit). */
+ uintN iters;
+
+ inline unsigned nGlobalTypes() {
+ return typeMap.length() - nStackTypes;
+ }
+ inline JSValueType* globalTypeMap() {
+ return typeMap.data() + nStackTypes;
+ }
+ inline JSValueType* stackTypeMap() {
+ return typeMap.data();
+ }
+
+ JS_REQUIRES_STACK void initialize(JSContext* cx, SlotList *globalSlots, bool speculate);
+ UnstableExit* removeUnstableExit(VMSideExit* exit);
+};
+
+inline TreeFragment*
+VMFragment::toTreeFragment()
+{
+ JS_ASSERT(root == this);
+ return static_cast<TreeFragment*>(this);
+}
+
+enum MonitorResult {
+ MONITOR_RECORDING,
+ MONITOR_NOT_RECORDING,
+ MONITOR_ERROR
+};
+
+const uintN PROFILE_MAX_INNER_LOOPS = 8;
+const uintN PROFILE_MAX_STACK = 6;
+
+/*
+ * A loop profile keeps track of the instruction mix of a hot loop. We use this
+ * information to predict whether tracing would be beneficial for the loop.
+ */
+class LoopProfile
+{
+public:
+ /* Instructions are divided into a few categories. */
+ enum OpKind {
+ OP_FLOAT, // Floating point arithmetic
+ OP_INT, // Integer arithmetic
+ OP_BIT, // Bit operations
+ OP_EQ, // == and !=
+ OP_EVAL, // Calls to eval()
+ OP_CALL, // JSOP_CALL instructions
+ OP_FWDJUMP, // Jumps with positive delta
+ OP_NEW, // JSOP_NEW instructions
+ OP_RECURSIVE, // Recursive calls
+ OP_ARRAY_READ, // Reads from dense arrays
+ OP_TYPED_ARRAY, // Accesses to typed arrays
+ OP_LIMIT
+ };
+
+ /* The TraceMonitor for which we're profiling. */
+ TraceMonitor *traceMonitor;
+
+ /* The script in which the loop header lives. */
+ JSScript *entryScript;
+
+ /* The stack frame where we started profiling. Only valid while profiling! */
+ JSStackFrame *entryfp;
+
+ /* The bytecode locations of the loop header and the back edge. */
+ jsbytecode *top, *bottom;
+
+ /* Number of times we have seen this loop executed; used to decide when to profile. */
+ uintN hits;
+
+ /* Whether we have run a complete profile of the loop. */
+ bool profiled;
+
+ /* Sometimes we can't decide in one profile run whether to trace, so we set undecided. */
+ bool undecided;
+
+ /* If we have profiled the loop, this saves the decision of whether to trace it. */
+ bool traceOK;
+
+ /* Memoized value of isCompilationUnprofitable. */
+ bool unprofitable;
+
+ /*
+ * Sometimes loops are not good tracing opportunities, but they are nested inside
+ * loops that we want to trace. In that case, we set their traceOK flag to true,
+ * but we set execOK to false. That way, the loop is traced so that it can be
+ * integrated into the outer trace. But we never execute the trace on its only.
+ */
+ bool execOK;
+
+ /* Instruction mix for the loop and total number of instructions. */
+ uintN allOps[OP_LIMIT];
+ uintN numAllOps;
+
+ /* Instruction mix and total for the loop, excluding nested inner loops. */
+ uintN selfOps[OP_LIMIT];
+ uintN numSelfOps;
+
+ /*
+ * A prediction of the number of instructions we would have to compile
+ * for the loop. This takes into account the fact that a branch may cause us to
+ * compile every instruction after it twice. Polymorphic calls are
+ * treated as n-way branches.
+ */
+ double numSelfOpsMult;
+
+ /*
+ * This keeps track of the number of times that every succeeding instruction
+ * in the trace will have to be compiled. Every time we hit a branch, we
+ * double this number. Polymorphic calls multiply it by n (for n-way
+ * polymorphism).
+ */
+ double branchMultiplier;
+
+ /* Set to true if the loop is short (i.e., has fewer than 8 iterations). */
+ bool shortLoop;
+
+ /* Set to true if the loop may be short (has few iterations at profiling time). */
+ bool maybeShortLoop;
+
+ /*
+ * When we hit a nested loop while profiling, we record where it occurs
+ * and how many iterations we execute it.
+ */
+ struct InnerLoop {
+ JSStackFrame *entryfp;
+ jsbytecode *top, *bottom;
+ uintN iters;
+
+ InnerLoop() {}
+ InnerLoop(JSStackFrame *entryfp, jsbytecode *top, jsbytecode *bottom)
+ : entryfp(entryfp), top(top), bottom(bottom), iters(0) {}
+ };
+
+ /* These two variables track all the inner loops seen while profiling (up to a limit). */
+ InnerLoop innerLoops[PROFILE_MAX_INNER_LOOPS];
+ uintN numInnerLoops;
+
+ /*
+ * These two variables track the loops that we are currently nested
+ * inside while profiling. Loops get popped off here when they exit.
+ */
+ InnerLoop loopStack[PROFILE_MAX_INNER_LOOPS];
+ uintN loopStackDepth;
+
+ /*
+ * These fields keep track of values on the JS stack. If the stack grows larger
+ * than PROFILE_MAX_STACK, we continue to track sp, but we return conservative results
+ * for stackTop().
+ */
+ struct StackValue {
+ bool isConst;
+ bool hasValue;
+ int value;
+
+ StackValue() : isConst(false), hasValue(false) {}
+ StackValue(bool isConst) : isConst(isConst), hasValue(false) {}
+ StackValue(bool isConst, int value) : isConst(isConst), hasValue(true), value(value) {}
+ };
+ StackValue stack[PROFILE_MAX_STACK];
+ uintN sp;
+
+ inline void stackClear() { sp = 0; }
+
+ inline void stackPush(const StackValue &v) {
+ if (sp < PROFILE_MAX_STACK)
+ stack[sp++] = v;
+ else
+ stackClear();
+ }
+
+ inline void stackPop() { if (sp > 0) sp--; }
+
+ inline StackValue stackAt(int pos) {
+ pos += sp;
+ if (pos >= 0 && uintN(pos) < PROFILE_MAX_STACK)
+ return stack[pos];
+ else
+ return StackValue(false);
+ }
+
+ LoopProfile(TraceMonitor *tm, JSStackFrame *entryfp, jsbytecode *top, jsbytecode *bottom);
+
+ void reset();
+
+ enum ProfileAction {
+ ProfContinue,
+ ProfComplete
+ };
+
+ /* These two functions track the instruction mix. */
+ inline void increment(OpKind kind)
+ {
+ allOps[kind]++;
+ if (loopStackDepth == 0)
+ selfOps[kind]++;
+ }
+
+ inline uintN count(OpKind kind) { return allOps[kind]; }
+
+ /* Called for every back edge being profiled. */
+ MonitorResult profileLoopEdge(JSContext* cx, uintN& inlineCallCount);
+
+ /* Called for every instruction being profiled. */
+ ProfileAction profileOperation(JSContext *cx, JSOp op);
+
+ /* Once a loop's profile is done, these decide whether it should be traced. */
+ bool isCompilationExpensive(JSContext *cx, uintN depth);
+ bool isCompilationUnprofitable(JSContext *cx, uintN goodOps);
+ void decide(JSContext *cx);
+
+ void stopProfiling(JSContext *cx);
+};
+
+/*
+ * BUILTIN_NO_FIXUP_NEEDED indicates that after the initial LeaveTree of a deep
+ * bail, the builtin call needs no further fixup when the trace exits and calls
+ * LeaveTree the second time.
+ */
+typedef enum BuiltinStatus {
+ BUILTIN_BAILED = 1,
+ BUILTIN_ERROR = 2
+} BuiltinStatus;
+
+static JS_INLINE void
+SetBuiltinError(TraceMonitor *tm)
+{
+ tm->tracerState->builtinStatus |= BUILTIN_ERROR;
+}
+
+static JS_INLINE bool
+WasBuiltinSuccessful(TraceMonitor *tm)
+{
+ return tm->tracerState->builtinStatus == 0;
+}
+
+#ifdef DEBUG_RECORDING_STATUS_NOT_BOOL
+/* #define DEBUG_RECORDING_STATUS_NOT_BOOL to detect misuses of RecordingStatus */
+struct RecordingStatus {
+ int code;
+ bool operator==(RecordingStatus &s) { return this->code == s.code; };
+ bool operator!=(RecordingStatus &s) { return this->code != s.code; };
+};
+enum RecordingStatusCodes {
+ RECORD_ERROR_code = 0,
+ RECORD_STOP_code = 1,
+
+ RECORD_CONTINUE_code = 3,
+ RECORD_IMACRO_code = 4
+};
+RecordingStatus RECORD_CONTINUE = { RECORD_CONTINUE_code };
+RecordingStatus RECORD_STOP = { RECORD_STOP_code };
+RecordingStatus RECORD_IMACRO = { RECORD_IMACRO_code };
+RecordingStatus RECORD_ERROR = { RECORD_ERROR_code };
+
+struct AbortableRecordingStatus {
+ int code;
+ bool operator==(AbortableRecordingStatus &s) { return this->code == s.code; };
+ bool operator!=(AbortableRecordingStatus &s) { return this->code != s.code; };
+};
+enum AbortableRecordingStatusCodes {
+ ARECORD_ERROR_code = 0,
+ ARECORD_STOP_code = 1,
+ ARECORD_ABORTED_code = 2,
+ ARECORD_CONTINUE_code = 3,
+ ARECORD_IMACRO_code = 4,
+ ARECORD_IMACRO_ABORTED_code = 5,
+ ARECORD_COMPLETED_code = 6
+};
+AbortableRecordingStatus ARECORD_ERROR = { ARECORD_ERROR_code };
+AbortableRecordingStatus ARECORD_STOP = { ARECORD_STOP_code };
+AbortableRecordingStatus ARECORD_CONTINUE = { ARECORD_CONTINUE_code };
+AbortableRecordingStatus ARECORD_IMACRO = { ARECORD_IMACRO_code };
+AbortableRecordingStatus ARECORD_IMACRO_ABORTED = { ARECORD_IMACRO_ABORTED_code };
+AbortableRecordingStatus ARECORD_ABORTED = { ARECORD_ABORTED_code };
+AbortableRecordingStatus ARECORD_COMPLETED = { ARECORD_COMPLETED_code };
+
+static inline AbortableRecordingStatus
+InjectStatus(RecordingStatus rs)
+{
+ AbortableRecordingStatus ars = { rs.code };
+ return ars;
+}
+static inline AbortableRecordingStatus
+InjectStatus(AbortableRecordingStatus ars)
+{
+ return ars;
+}
+
+static inline bool
+StatusAbortsRecorderIfActive(AbortableRecordingStatus ars)
+{
+ return ars == ARECORD_ERROR || ars == ARECORD_STOP;
+}
+#else
+
+/*
+ * Normally, during recording, when the recorder cannot continue, it returns
+ * ARECORD_STOP to indicate that recording should be aborted by the top-level
+ * recording function. However, if the recorder reenters the interpreter (e.g.,
+ * when executing an inner loop), there will be an immediate abort. This
+ * condition must be carefully detected and propagated out of all nested
+ * recorder calls lest the now-invalid TraceRecorder object be accessed
+ * accidentally. This condition is indicated by the ARECORD_ABORTED value.
+ *
+ * The AbortableRecordingStatus enumeration represents the general set of
+ * possible results of calling a recorder function. Functions that cannot
+ * possibly return ARECORD_ABORTED may statically guarantee this to the caller
+ * using the RecordingStatus enumeration. Ideally, C++ would allow subtyping
+ * of enumerations, but it doesn't. To simulate subtype conversion manually,
+ * code should call InjectStatus to inject a value of the restricted set into a
+ * value of the general set.
+ */
+
+enum RecordingStatus {
+ RECORD_STOP = 0, // Recording should be aborted at the top-level
+ // call to the recorder.
+ RECORD_ERROR = 1, // Recording should be aborted at the top-level
+ // call to the recorder and the interpreter should
+ // goto error
+ RECORD_CONTINUE = 2, // Continue recording.
+ RECORD_IMACRO = 3 // Entered imacro; continue recording.
+ // Only JSOP_IS_IMACOP opcodes may return this.
+};
+
+enum AbortableRecordingStatus {
+ ARECORD_STOP = 0, // see RECORD_STOP
+ ARECORD_ERROR = 1, // Recording may or may not have been aborted.
+ // Recording should be aborted at the top-level
+ // if it has not already been and the interpreter
+ // should goto error
+ ARECORD_CONTINUE = 2, // see RECORD_CONTINUE
+ ARECORD_IMACRO = 3, // see RECORD_IMACRO
+ ARECORD_IMACRO_ABORTED = 4, // see comment in TR::monitorRecording.
+ ARECORD_ABORTED = 5, // Recording has already been aborted; the
+ // interpreter should continue executing
+ ARECORD_COMPLETED = 6 // Recording completed successfully, the
+ // trace recorder has been deleted
+};
+
+static JS_ALWAYS_INLINE AbortableRecordingStatus
+InjectStatus(RecordingStatus rs)
+{
+ return static_cast<AbortableRecordingStatus>(rs);
+}
+
+static JS_ALWAYS_INLINE AbortableRecordingStatus
+InjectStatus(AbortableRecordingStatus ars)
+{
+ return ars;
+}
+
+/*
+ * Return whether the recording status requires the current recording session
+ * to be deleted. ERROR means the recording session should be deleted if it
+ * hasn't already. ABORTED and COMPLETED indicate the recording session is
+ * already deleted, so they return 'false'.
+ */
+static JS_ALWAYS_INLINE bool
+StatusAbortsRecorderIfActive(AbortableRecordingStatus ars)
+{
+ return ars <= ARECORD_ERROR;
+}
+#endif
+
+class SlotMap;
+class SlurpInfo;
+
+/* Results of trying to compare two typemaps together */
+enum TypeConsensus
+{
+ TypeConsensus_Okay, /* Two typemaps are compatible */
+ TypeConsensus_Undemotes, /* Not compatible now, but would be with pending undemotes. */
+ TypeConsensus_Bad /* Typemaps are not compatible */
+};
+
+enum TracePointAction {
+ TPA_Nothing,
+ TPA_RanStuff,
+ TPA_Recorded,
+ TPA_Error
+};
+
+typedef HashMap<nanojit::LIns*, JSObject*> GuardedShapeTable;
+
+#ifdef DEBUG
+# define AbortRecording(cx, reason) AbortRecordingImpl(cx, reason)
+#else
+# define AbortRecording(cx, reason) AbortRecordingImpl(cx)
+#endif
+
+void
+AbortProfiling(JSContext *cx);
+
+class TraceRecorder
+{
+ /*************************************************************** Recording session constants */
+
+ /* The context in which recording started. */
+ JSContext* const cx;
+
+ /* Cached value of JS_TRACE_MONITOR(cx). */
+ TraceMonitor* const traceMonitor;
+
+ /* Cached oracle keeps track of hit counts for program counter locations */
+ Oracle* oracle;
+
+ /* The Fragment being recorded by this recording session. */
+ VMFragment* const fragment;
+
+ /* The root fragment representing the tree. */
+ TreeFragment* const tree;
+
+ /* The global object from the start of recording until now. */
+ JSObject* const globalObj;
+
+ /* If non-null, the script of outer loop aborted to start recording this loop. */
+ JSScript* const outerScript;
+
+ /* If non-null, the pc of the outer loop aborted to start recording this loop. */
+ jsbytecode* const outerPC;
+
+ /* If |outerPC|, the argc to use when looking up |outerPC| in the fragments table. */
+ uint32 const outerArgc;
+
+ /* If non-null, the side exit from which we are growing. */
+ VMSideExit* const anchor;
+
+ /* Instructions yielding the corresponding trace-const members of TracerState. */
+ nanojit::LIns* const cx_ins;
+ nanojit::LIns* const eos_ins;
+ nanojit::LIns* const eor_ins;
+ nanojit::LIns* const loopLabel;
+
+ /* Lazy slot import state. */
+ unsigned importStackSlots;
+ unsigned importGlobalSlots;
+ TypeMap importTypeMap;
+
+ /*
+ * The LirBuffer used to supply memory to our LirWriter pipeline. Also contains the most recent
+ * instruction for {sp, rp, state}. Also contains names for debug JIT spew. Should be split.
+ */
+ nanojit::LirBuffer* const lirbuf;
+
+ /*
+ * Remembers traceAlloc state before recording started; automatically rewinds when mark is
+ * destroyed on a failed compilation.
+ */
+ VMAllocator::Mark mark;
+
+ /* Remembers the number of sideExits in treeInfo before recording started. */
+ const unsigned numSideExitsBefore;
+
+ /*********************************************************** Recording session mutable state */
+
+ /* Maps interpreter stack values to the instruction generating that value. */
+ Tracker tracker;
+
+ /* Maps interpreter stack values to the instruction writing back to the native stack. */
+ Tracker nativeFrameTracker;
+
+ /* The start of the global object's slots we assume for the trackers. */
+ Value* global_slots;
+
+ /* The number of interpreted calls entered (and not yet left) since recording began. */
+ unsigned callDepth;
+
+ /* The current atom table, mirroring the interpreter loop's variable of the same name. */
+ JSAtom** atoms;
+ Value* consts;
+
+ /* An instruction yielding the current script's strict mode code flag. */
+ nanojit::LIns* strictModeCode_ins;
+
+ /* FIXME: Dead, but soon to be used for something or other. */
+ Queue<jsbytecode*> cfgMerges;
+
+ /* Indicates whether the current tree should be trashed when the recording session ends. */
+ bool trashSelf;
+
+ /* A list of trees to trash at the end of the recording session. */
+ Queue<TreeFragment*> whichTreesToTrash;
+
+ /* The set of objects whose shapes already have been guarded. */
+ GuardedShapeTable guardedShapeTable;
+
+ /* Current initializer depth, and whether any of the initializers are unoptimized NEWINIT. */
+ int initDepth;
+ bool hadNewInit;
+
+#ifdef DEBUG
+ /*
+ * If we are expecting a record_AddProperty callback for this instruction,
+ * the shape of the object before adding the data property. Else NULL.
+ */
+ const js::Shape* addPropShapeBefore;
+#endif
+
+ /***************************************** Temporal state hoisted into the recording session */
+
+ /* Carry the return value from a STOP/RETURN to the subsequent record_LeaveFrame. */
+ nanojit::LIns* rval_ins;
+
+ /* Carry the return value from a native call to the record_NativeCallComplete. */
+ nanojit::LIns* native_rval_ins;
+
+ /* Carry the return value of js_CreateThis to record_NativeCallComplete. */
+ nanojit::LIns* newobj_ins;
+
+ /* Carry the JSSpecializedNative used to generate a call to record_NativeCallComplete. */
+ JSSpecializedNative* pendingSpecializedNative;
+
+ /* Carry whether this is a jsval on the native stack from finishGetProp to monitorRecording. */
+ Value* pendingUnboxSlot;
+
+ /* Carry a guard condition to the beginning of the next monitorRecording. */
+ nanojit::LIns* pendingGuardCondition;
+
+ /* See AbortRecordingIfUnexpectedGlobalWrite. */
+ js::Vector<unsigned> pendingGlobalSlotsToSet;
+
+ /* Carry whether we have an always-exit from emitIf to checkTraceEnd. */
+ bool pendingLoop;
+
+ /* Temporary JSSpecializedNative used to describe non-specialized fast natives. */
+ JSSpecializedNative generatedSpecializedNative;
+
+ /* Temporary JSValueType array used to construct temporary typemaps. */
+ js::Vector<JSValueType, 256> tempTypeMap;
+
+ /* Used to generate LIR. Has a short name because it's used a lot. */
+ tjit::Writer w;
+
+ /************************************************************* 10 bajillion member functions */
+
+ /*
+ * These would be in Writer if they didn't modify TraceRecorder state.
+ * They are invoked the via macros below that make them look like they are
+ * part of Writer (hence the "w_" prefix, which looks like "w.").
+ */
+ nanojit::LIns* w_immpObjGC(JSObject* obj);
+ nanojit::LIns* w_immpFunGC(JSFunction* fun);
+ nanojit::LIns* w_immpStrGC(JSString* str);
+ nanojit::LIns* w_immpShapeGC(const js::Shape* shape);
+ nanojit::LIns* w_immpIdGC(jsid id);
+
+ #define immpObjGC(obj) name(w_immpObjGC(obj), #obj)
+ #define immpFunGC(fun) name(w_immpFunGC(fun), #fun)
+ #define immpStrGC(str) name(w_immpStrGC(str), #str)
+ #define immpAtomGC(atom) name(w_immpStrGC(ATOM_TO_STRING(atom)), "ATOM_TO_STRING(" #atom ")")
+ #define immpShapeGC(shape) name(w_immpShapeGC(shape), #shape)
+ #define immpIdGC(id) name(w_immpIdGC(id), #id)
+
+ /*
+ * Examines current interpreter state to record information suitable for returning to the
+ * interpreter through a side exit of the given type.
+ */
+ JS_REQUIRES_STACK VMSideExit* snapshot(ExitType exitType);
+
+ /*
+ * Creates a separate but identical copy of the given side exit, allowing the guards associated
+ * with each to be entirely separate even after subsequent patching.
+ */
+ JS_REQUIRES_STACK VMSideExit* copy(VMSideExit* exit);
+
+ /*
+ * Creates an instruction whose payload is a GuardRecord for the given exit. The instruction
+ * is suitable for use as the final argument of a single call to LirBuffer::insGuard; do not
+ * reuse the returned value.
+ */
+ JS_REQUIRES_STACK nanojit::GuardRecord* createGuardRecord(VMSideExit* exit);
+
+ JS_REQUIRES_STACK JS_INLINE void markSlotUndemotable(LinkableFragment* f, unsigned slot);
+
+ JS_REQUIRES_STACK JS_INLINE void markSlotUndemotable(LinkableFragment* f, unsigned slot, const void* pc);
+
+ JS_REQUIRES_STACK unsigned findUndemotesInTypemaps(const TypeMap& typeMap, LinkableFragment* f,
+ Queue<unsigned>& undemotes);
+
+ JS_REQUIRES_STACK void assertDownFrameIsConsistent(VMSideExit* anchor, FrameInfo* fi);
+
+ JS_REQUIRES_STACK void captureStackTypes(unsigned callDepth, JSValueType* typeMap);
+
+ bool isVoidPtrGlobal(const void* p) const;
+ bool isGlobal(const Value* p) const;
+ ptrdiff_t nativeGlobalSlot(const Value *p) const;
+ ptrdiff_t nativeGlobalOffset(const Value* p) const;
+ JS_REQUIRES_STACK ptrdiff_t nativeStackOffsetImpl(const void* p) const;
+ JS_REQUIRES_STACK ptrdiff_t nativeStackOffset(const Value* p) const;
+ JS_REQUIRES_STACK ptrdiff_t nativeStackSlotImpl(const void* p) const;
+ JS_REQUIRES_STACK ptrdiff_t nativeStackSlot(const Value* p) const;
+ JS_REQUIRES_STACK ptrdiff_t nativespOffsetImpl(const void* p) const;
+ JS_REQUIRES_STACK ptrdiff_t nativespOffset(const Value* p) const;
+ JS_REQUIRES_STACK void importImpl(tjit::Address addr, const void* p, JSValueType t,
+ const char *prefix, uintN index, JSStackFrame *fp);
+ JS_REQUIRES_STACK void import(tjit::Address addr, const Value* p, JSValueType t,
+ const char *prefix, uintN index, JSStackFrame *fp);
+ JS_REQUIRES_STACK void import(TreeFragment* tree, nanojit::LIns* sp, unsigned stackSlots,
+ unsigned callDepth, unsigned ngslots, JSValueType* typeMap);
+ void trackNativeStackUse(unsigned slots);
+
+ JS_REQUIRES_STACK bool isValidSlot(JSObject *obj, const js::Shape* shape);
+ JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot);
+ JS_REQUIRES_STACK void importGlobalSlot(unsigned slot);
+
+ void ensureCond(nanojit::LIns** ins, bool* cond);
+
+ JS_REQUIRES_STACK RecordingStatus guard(bool expected, nanojit::LIns* cond, ExitType exitType,
+ bool abortIfAlwaysExits = false);
+ JS_REQUIRES_STACK RecordingStatus guard(bool expected, nanojit::LIns* cond, VMSideExit* exit,
+ bool abortIfAlwaysExits = false);
+ JS_REQUIRES_STACK nanojit::LIns* guard_xov(nanojit::LOpcode op, nanojit::LIns* d0,
+ nanojit::LIns* d1, VMSideExit* exit);
+
+ nanojit::LIns* writeBack(nanojit::LIns* i, nanojit::LIns* base, ptrdiff_t offset,
+ bool shouldDemoteToInt32);
+
+#ifdef DEBUG
+ bool isValidFrameObjPtr(void *obj);
+#endif
+ void assertInsideLoop();
+
+ JS_REQUIRES_STACK void setImpl(void* p, nanojit::LIns* l, bool shouldDemoteToInt32 = true);
+ JS_REQUIRES_STACK void set(Value* p, nanojit::LIns* l, bool shouldDemoteToInt32 = true);
+ JS_REQUIRES_STACK void setFrameObjPtr(void* p, nanojit::LIns* l,
+ bool shouldDemoteToInt32 = true);
+ nanojit::LIns* getFromTrackerImpl(const void *p);
+ nanojit::LIns* getFromTracker(const Value* p);
+ JS_REQUIRES_STACK nanojit::LIns* getImpl(const void* p);
+ JS_REQUIRES_STACK nanojit::LIns* get(const Value* p);
+ JS_REQUIRES_STACK nanojit::LIns* getFrameObjPtr(void* p);
+ JS_REQUIRES_STACK nanojit::LIns* attemptImport(const Value* p);
+ JS_REQUIRES_STACK nanojit::LIns* addr(Value* p);
+
+ JS_REQUIRES_STACK bool knownImpl(const void* p);
+ JS_REQUIRES_STACK bool known(const Value* p);
+ JS_REQUIRES_STACK bool known(JSObject** p);
+ /*
+ * The slots of the global object are sometimes reallocated by the
+ * interpreter. This function checks for that condition and re-maps the
+ * entries of the tracker accordingly.
+ */
+ JS_REQUIRES_STACK void checkForGlobalObjectReallocation() {
+ if (global_slots != globalObj->getSlots())
+ checkForGlobalObjectReallocationHelper();
+ }
+ JS_REQUIRES_STACK void checkForGlobalObjectReallocationHelper();
+
+ JS_REQUIRES_STACK TypeConsensus selfTypeStability(SlotMap& smap);
+ JS_REQUIRES_STACK TypeConsensus peerTypeStability(SlotMap& smap, const void* ip,
+ TreeFragment** peer);
+
+ JS_REQUIRES_STACK Value& argval(unsigned n) const;
+ JS_REQUIRES_STACK Value& varval(unsigned n) const;
+ JS_REQUIRES_STACK Value& stackval(int n) const;
+
+ JS_REQUIRES_STACK void updateAtoms();
+ JS_REQUIRES_STACK void updateAtoms(JSScript *script);
+
+ struct NameResult {
+ // |tracked| is true iff the result of the name lookup is a variable that
+ // is already in the tracker. The rest of the fields are set only if
+ // |tracked| is false.
+ bool tracked;
+ Value v; // current property value
+ JSObject *obj; // Call object where name was found
+ nanojit::LIns *obj_ins; // LIR value for obj
+ js::Shape *shape; // shape name was resolved to
+ };
+
+ JS_REQUIRES_STACK nanojit::LIns* scopeChain();
+ JS_REQUIRES_STACK nanojit::LIns* entryScopeChain() const;
+ JS_REQUIRES_STACK nanojit::LIns* entryFrameIns() const;
+ JS_REQUIRES_STACK JSStackFrame* frameIfInRange(JSObject* obj, unsigned* depthp = NULL) const;
+ JS_REQUIRES_STACK RecordingStatus traverseScopeChain(JSObject *obj, nanojit::LIns *obj_ins, JSObject *obj2, nanojit::LIns *&obj2_ins);
+ JS_REQUIRES_STACK AbortableRecordingStatus scopeChainProp(JSObject* obj, Value*& vp, nanojit::LIns*& ins, NameResult& nr, JSObject **scopeObjp = NULL);
+ JS_REQUIRES_STACK RecordingStatus callProp(JSObject* obj, JSProperty* shape, jsid id, Value*& vp, nanojit::LIns*& ins, NameResult& nr);
+
+ JS_REQUIRES_STACK nanojit::LIns* arg(unsigned n);
+ JS_REQUIRES_STACK void arg(unsigned n, nanojit::LIns* i);
+ JS_REQUIRES_STACK nanojit::LIns* var(unsigned n);
+ JS_REQUIRES_STACK void var(unsigned n, nanojit::LIns* i);
+ JS_REQUIRES_STACK nanojit::LIns* upvar(JSScript* script, JSUpvarArray* uva, uintN index, Value& v);
+ nanojit::LIns* stackLoad(tjit::Address addr, uint8 type);
+ JS_REQUIRES_STACK nanojit::LIns* stack(int n);
+ JS_REQUIRES_STACK void stack(int n, nanojit::LIns* i);
+
+ JS_REQUIRES_STACK void guardNonNeg(nanojit::LIns* d0, nanojit::LIns* d1, VMSideExit* exit);
+ JS_REQUIRES_STACK nanojit::LIns* alu(nanojit::LOpcode op, jsdouble v0, jsdouble v1,
+ nanojit::LIns* s0, nanojit::LIns* s1);
+
+ nanojit::LIns* d2i(nanojit::LIns* f, bool resultCanBeImpreciseIfFractional = false);
+ nanojit::LIns* d2u(nanojit::LIns* d);
+ JS_REQUIRES_STACK RecordingStatus makeNumberInt32(nanojit::LIns* d, nanojit::LIns** num_ins);
+ JS_REQUIRES_STACK RecordingStatus makeNumberUint32(nanojit::LIns* d, nanojit::LIns** num_ins);
+ JS_REQUIRES_STACK nanojit::LIns* stringify(const Value& v);
+
+ JS_REQUIRES_STACK nanojit::LIns* newArguments(nanojit::LIns* callee_ins);
+
+ JS_REQUIRES_STACK bool canCallImacro() const;
+ JS_REQUIRES_STACK RecordingStatus callImacro(jsbytecode* imacro);
+ JS_REQUIRES_STACK RecordingStatus callImacroInfallibly(jsbytecode* imacro);
+
+ JS_REQUIRES_STACK AbortableRecordingStatus ifop();
+ JS_REQUIRES_STACK RecordingStatus switchop();
+#ifdef NANOJIT_IA32
+ JS_REQUIRES_STACK AbortableRecordingStatus tableswitch();
+#endif
+ JS_REQUIRES_STACK RecordingStatus inc(Value& v, jsint incr, bool pre = true);
+ JS_REQUIRES_STACK RecordingStatus inc(const Value &v, nanojit::LIns*& v_ins,
+ Value &v_out, jsint incr,
+ bool pre = true);
+ JS_REQUIRES_STACK RecordingStatus incHelper(const Value &v, nanojit::LIns*& v_ins,
+ Value &v_after,
+ nanojit::LIns*& v_ins_after,
+ jsint incr);
+ JS_REQUIRES_STACK AbortableRecordingStatus incProp(jsint incr, bool pre = true);
+ JS_REQUIRES_STACK RecordingStatus incElem(jsint incr, bool pre = true);
+ JS_REQUIRES_STACK AbortableRecordingStatus incName(jsint incr, bool pre = true);
+
+ JS_REQUIRES_STACK RecordingStatus strictEquality(bool equal, bool cmpCase);
+ JS_REQUIRES_STACK AbortableRecordingStatus equality(bool negate, bool tryBranchAfterCond);
+ JS_REQUIRES_STACK AbortableRecordingStatus equalityHelper(Value& l, Value& r,
+ nanojit::LIns* l_ins, nanojit::LIns* r_ins,
+ bool negate, bool tryBranchAfterCond,
+ Value& rval);
+ JS_REQUIRES_STACK AbortableRecordingStatus relational(nanojit::LOpcode op, bool tryBranchAfterCond);
+
+ JS_REQUIRES_STACK RecordingStatus unary(nanojit::LOpcode op);
+ JS_REQUIRES_STACK RecordingStatus binary(nanojit::LOpcode op);
+
+ JS_REQUIRES_STACK RecordingStatus guardShape(nanojit::LIns* obj_ins, JSObject* obj,
+ uint32 shape, const char* name, VMSideExit* exit);
+
+#if defined DEBUG_notme && defined XP_UNIX
+ void dumpGuardedShapes(const char* prefix);
+#endif
+
+ void forgetGuardedShapes();
+
+ JS_REQUIRES_STACK AbortableRecordingStatus test_property_cache(JSObject* obj, nanojit::LIns* obj_ins,
+ JSObject*& obj2, PCVal& pcval);
+ JS_REQUIRES_STACK RecordingStatus guardPropertyCacheHit(nanojit::LIns* obj_ins,
+ JSObject* aobj,
+ JSObject* obj2,
+ PropertyCacheEntry* entry,
+ PCVal& pcval);
+
+ void stobj_set_fslot(nanojit::LIns *obj_ins, unsigned slot, const Value &v,
+ nanojit::LIns* v_ins);
+ void stobj_set_dslot(nanojit::LIns *obj_ins, unsigned slot,
+ nanojit::LIns*& slots_ins, const Value &v, nanojit::LIns* v_ins);
+ void stobj_set_slot(JSObject *obj, nanojit::LIns* obj_ins, unsigned slot,
+ nanojit::LIns*& slots_ins, const Value &v, nanojit::LIns* v_ins);
+
+ nanojit::LIns* unbox_slot(JSObject *obj, nanojit::LIns *obj_ins, uint32 slot,
+ VMSideExit *exit);
+
+ JS_REQUIRES_STACK AbortableRecordingStatus name(Value*& vp, nanojit::LIns*& ins, NameResult& nr);
+ JS_REQUIRES_STACK AbortableRecordingStatus prop(JSObject* obj, nanojit::LIns* obj_ins,
+ uint32 *slotp, nanojit::LIns** v_insp,
+ Value* outp);
+ JS_REQUIRES_STACK RecordingStatus propTail(JSObject* obj, nanojit::LIns* obj_ins,
+ JSObject* obj2, PCVal pcval,
+ uint32 *slotp, nanojit::LIns** v_insp,
+ Value* outp);
+ JS_REQUIRES_STACK RecordingStatus denseArrayElement(Value& oval, Value& idx, Value*& vp,
+ nanojit::LIns*& v_ins,
+ nanojit::LIns*& addr_ins,
+ VMSideExit* exit);
+ JS_REQUIRES_STACK nanojit::LIns *canonicalizeNaNs(nanojit::LIns *dval_ins);
+ JS_REQUIRES_STACK AbortableRecordingStatus typedArrayElement(Value& oval, Value& idx, Value*& vp,
+ nanojit::LIns*& v_ins);
+ JS_REQUIRES_STACK AbortableRecordingStatus getProp(JSObject* obj, nanojit::LIns* obj_ins);
+ JS_REQUIRES_STACK AbortableRecordingStatus getProp(Value& v);
+ JS_REQUIRES_STACK RecordingStatus getThis(nanojit::LIns*& this_ins);
+
+ JS_REQUIRES_STACK void storeMagic(JSWhyMagic why, tjit::Address addr);
+ JS_REQUIRES_STACK AbortableRecordingStatus unboxNextValue(nanojit::LIns* &v_ins);
+
+ JS_REQUIRES_STACK VMSideExit* enterDeepBailCall();
+ JS_REQUIRES_STACK void leaveDeepBailCall();
+
+ JS_REQUIRES_STACK RecordingStatus primitiveToStringInPlace(Value* vp);
+ JS_REQUIRES_STACK void finishGetProp(nanojit::LIns* obj_ins, nanojit::LIns* vp_ins,
+ nanojit::LIns* ok_ins, Value* outp);
+ JS_REQUIRES_STACK RecordingStatus getPropertyByName(nanojit::LIns* obj_ins, Value* idvalp,
+ Value* outp);
+ JS_REQUIRES_STACK RecordingStatus getPropertyByIndex(nanojit::LIns* obj_ins,
+ nanojit::LIns* index_ins, Value* outp);
+ JS_REQUIRES_STACK RecordingStatus getPropertyById(nanojit::LIns* obj_ins, Value* outp);
+ JS_REQUIRES_STACK RecordingStatus getPropertyWithNativeGetter(nanojit::LIns* obj_ins,
+ const js::Shape* shape,
+ Value* outp);
+ JS_REQUIRES_STACK RecordingStatus getPropertyWithScriptGetter(JSObject *obj,
+ nanojit::LIns* obj_ins,
+ const js::Shape* shape);
+
+ JS_REQUIRES_STACK RecordingStatus getCharCodeAt(JSString *str,
+ nanojit::LIns* str_ins, nanojit::LIns* idx_ins,
+ nanojit::LIns** out_ins);
+ JS_REQUIRES_STACK nanojit::LIns* getUnitString(nanojit::LIns* str_ins, nanojit::LIns* idx_ins);
+ JS_REQUIRES_STACK RecordingStatus getCharAt(JSString *str,
+ nanojit::LIns* str_ins, nanojit::LIns* idx_ins,
+ JSOp mode, nanojit::LIns** out_ins);
+
+ JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByName(nanojit::LIns* obj_ins,
+ Value* idvalp, Value* rvalp,
+ bool init);
+ JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByIndex(nanojit::LIns* obj_ins,
+ nanojit::LIns* index_ins,
+ Value* rvalp, bool init);
+ JS_REQUIRES_STACK AbortableRecordingStatus setElem(int lval_spindex, int idx_spindex,
+ int v_spindex);
+
+ JS_REQUIRES_STACK RecordingStatus lookupForSetPropertyOp(JSObject* obj, nanojit::LIns* obj_ins,
+ jsid id, bool* safep,
+ JSObject** pobjp,
+ const js::Shape** shapep);
+ JS_REQUIRES_STACK RecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins,
+ const js::Shape* shape,
+ const Value& v, nanojit::LIns* v_ins);
+ JS_REQUIRES_STACK RecordingStatus addDataProperty(JSObject* obj);
+ JS_REQUIRES_STACK RecordingStatus setCallProp(JSObject* callobj, nanojit::LIns* callobj_ins,
+ const js::Shape* shape, nanojit::LIns* v_ins,
+ const Value& v);
+ JS_REQUIRES_STACK RecordingStatus setProperty(JSObject* obj, nanojit::LIns* obj_ins,
+ const Value& v, nanojit::LIns* v_ins,
+ bool* deferredp);
+ JS_REQUIRES_STACK RecordingStatus recordSetPropertyOp();
+ JS_REQUIRES_STACK RecordingStatus recordInitPropertyOp(jsbytecode op);
+
+ void box_undefined_into(tjit::Address addr);
+#if JS_BITS_PER_WORD == 32
+ void box_null_into(tjit::Address addr);
+ nanojit::LIns* unbox_number_as_double(tjit::Address addr, nanojit::LIns* tag_ins,
+ VMSideExit* exit);
+ nanojit::LIns* unbox_object(tjit::Address addr, nanojit::LIns* tag_ins, JSValueType type,
+ VMSideExit* exit);
+ nanojit::LIns* unbox_non_double_object(tjit::Address addr, nanojit::LIns* tag_ins,
+ JSValueType type, VMSideExit* exit);
+#elif JS_BITS_PER_WORD == 64
+ nanojit::LIns* non_double_object_value_has_type(nanojit::LIns* v_ins, JSValueType type);
+ nanojit::LIns* unpack_ptr(nanojit::LIns* v_ins);
+ nanojit::LIns* unbox_number_as_double(nanojit::LIns* v_ins, VMSideExit* exit);
+ nanojit::LIns* unbox_object(nanojit::LIns* v_ins, JSValueType type, VMSideExit* exit);
+ nanojit::LIns* unbox_non_double_object(nanojit::LIns* v_ins, JSValueType type, VMSideExit* exit);
+#endif
+
+ nanojit::LIns* unbox_value(const Value& v, tjit::Address addr, VMSideExit* exit,
+ bool force_double=false);
+ void unbox_any_object(tjit::Address addr, nanojit::LIns** obj_ins, nanojit::LIns** is_obj_ins);
+ nanojit::LIns* is_boxed_true(tjit::Address addr);
+ nanojit::LIns* is_boxed_magic(tjit::Address addr, JSWhyMagic why);
+
+ nanojit::LIns* is_string_id(nanojit::LIns* id_ins);
+ nanojit::LIns* unbox_string_id(nanojit::LIns* id_ins);
+ nanojit::LIns* unbox_int_id(nanojit::LIns* id_ins);
+
+ /* Box a slot on trace into the given address at the given offset. */
+ void box_value_into(const Value& v, nanojit::LIns* v_ins, tjit::Address addr);
+
+ /*
+ * Box a slot so that it may be passed with value semantics to a native. On
+ * 32-bit, this currently means boxing the value into insAlloc'd memory and
+ * returning the address which is passed as a Value*. On 64-bit, this
+ * currently means returning the boxed value which is passed as a jsval.
+ */
+ nanojit::LIns* box_value_for_native_call(const Value& v, nanojit::LIns* v_ins);
+
+ /* Box a slot into insAlloc'd memory. */
+ nanojit::LIns* box_value_into_alloc(const Value& v, nanojit::LIns* v_ins);
+
+ JS_REQUIRES_STACK void guardClassHelper(bool cond, nanojit::LIns* obj_ins, Class* clasp,
+ VMSideExit* exit, nanojit::LoadQual loadQual);
+ JS_REQUIRES_STACK void guardClass(nanojit::LIns* obj_ins, Class* clasp,
+ VMSideExit* exit, nanojit::LoadQual loadQual);
+ JS_REQUIRES_STACK void guardNotClass(nanojit::LIns* obj_ins, Class* clasp,
+ VMSideExit* exit, nanojit::LoadQual loadQual);
+ JS_REQUIRES_STACK void guardDenseArray(nanojit::LIns* obj_ins, ExitType exitType);
+ JS_REQUIRES_STACK void guardDenseArray(nanojit::LIns* obj_ins, VMSideExit* exit);
+ JS_REQUIRES_STACK bool guardHasPrototype(JSObject* obj, nanojit::LIns* obj_ins,
+ JSObject** pobj, nanojit::LIns** pobj_ins,
+ VMSideExit* exit);
+ JS_REQUIRES_STACK RecordingStatus guardPrototypeHasNoIndexedProperties(JSObject* obj,
+ nanojit::LIns* obj_ins,
+ VMSideExit* exit);
+ JS_REQUIRES_STACK RecordingStatus guardNativeConversion(Value& v);
+ JS_REQUIRES_STACK void clearReturningFrameFromNativeTracker();
+ JS_REQUIRES_STACK AbortableRecordingStatus putActivationObjects();
+ JS_REQUIRES_STACK RecordingStatus createThis(JSObject& ctor, nanojit::LIns* ctor_ins,
+ nanojit::LIns** thisobj_insp);
+ JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee);
+ JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins,
+ unsigned *depthp);
+ JS_REQUIRES_STACK nanojit::LIns* guardArgsLengthNotAssigned(nanojit::LIns* argsobj_ins);
+ JS_REQUIRES_STACK void guardNotHole(nanojit::LIns* argsobj_ins, nanojit::LIns* ids_ins);
+ JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSObject* ctor,
+ nanojit::LIns*& proto_ins);
+ JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSProtoKey key,
+ nanojit::LIns*& proto_ins);
+ JS_REQUIRES_STACK RecordingStatus newArray(JSObject* ctor, uint32 argc, Value* argv,
+ Value* rval);
+ JS_REQUIRES_STACK RecordingStatus newString(JSObject* ctor, uint32 argc, Value* argv,
+ Value* rval);
+ JS_REQUIRES_STACK RecordingStatus interpretedFunctionCall(Value& fval, JSFunction* fun,
+ uintN argc, bool constructing);
+ JS_REQUIRES_STACK void propagateFailureToBuiltinStatus(nanojit::LIns *ok_ins,
+ nanojit::LIns *&status_ins);
+ JS_REQUIRES_STACK RecordingStatus emitNativeCall(JSSpecializedNative* sn, uintN argc,
+ nanojit::LIns* args[], bool rooted);
+ JS_REQUIRES_STACK void emitNativePropertyOp(const js::Shape* shape,
+ nanojit::LIns* obj_ins,
+ bool setflag,
+ nanojit::LIns* addr_boxed_val_ins);
+ JS_REQUIRES_STACK RecordingStatus callSpecializedNative(JSNativeTraceInfo* trcinfo, uintN argc,
+ bool constructing);
+ JS_REQUIRES_STACK RecordingStatus callNative(uintN argc, JSOp mode);
+ JS_REQUIRES_STACK RecordingStatus callFloatReturningInt(uintN argc,
+ const nanojit::CallInfo *ci);
+ JS_REQUIRES_STACK RecordingStatus functionCall(uintN argc, JSOp mode);
+
+ JS_REQUIRES_STACK void trackCfgMerges(jsbytecode* pc);
+ JS_REQUIRES_STACK void emitIf(jsbytecode* pc, bool cond, nanojit::LIns* x);
+ JS_REQUIRES_STACK void fuseIf(jsbytecode* pc, bool cond, nanojit::LIns* x);
+ JS_REQUIRES_STACK AbortableRecordingStatus checkTraceEnd(jsbytecode* pc);
+
+ AbortableRecordingStatus hasMethod(JSObject* obj, jsid id, bool& found);
+ JS_REQUIRES_STACK AbortableRecordingStatus hasIteratorMethod(JSObject* obj, bool& found);
+
+ JS_REQUIRES_STACK jsatomid getFullIndex(ptrdiff_t pcoff = 0);
+
+ JS_REQUIRES_STACK JSValueType determineSlotType(Value* vp);
+
+ JS_REQUIRES_STACK RecordingStatus setUpwardTrackedVar(Value* stackVp, const Value& v,
+ nanojit::LIns* v_ins);
+
+ JS_REQUIRES_STACK AbortableRecordingStatus compile();
+ JS_REQUIRES_STACK AbortableRecordingStatus closeLoop();
+ JS_REQUIRES_STACK AbortableRecordingStatus endLoop();
+ JS_REQUIRES_STACK AbortableRecordingStatus endLoop(VMSideExit* exit);
+ JS_REQUIRES_STACK void joinEdgesToEntry(TreeFragment* peer_root);
+ JS_REQUIRES_STACK void adjustCallerTypes(TreeFragment* f);
+ JS_REQUIRES_STACK void prepareTreeCall(TreeFragment* inner);
+ JS_REQUIRES_STACK void emitTreeCall(TreeFragment* inner, VMSideExit* exit);
+ JS_REQUIRES_STACK void determineGlobalTypes(JSValueType* typeMap);
+ JS_REQUIRES_STACK VMSideExit* downSnapshot(FrameInfo* downFrame);
+ JS_REQUIRES_STACK TreeFragment* findNestedCompatiblePeer(TreeFragment* f);
+ JS_REQUIRES_STACK AbortableRecordingStatus attemptTreeCall(TreeFragment* inner,
+ uintN& inlineCallCount);
+
+ static JS_REQUIRES_STACK MonitorResult recordLoopEdge(JSContext* cx, TraceRecorder* r,
+ uintN& inlineCallCount);
+
+ /* Allocators associated with this recording session. */
+ VMAllocator& tempAlloc() const { return *traceMonitor->tempAlloc; }
+ VMAllocator& traceAlloc() const { return *traceMonitor->traceAlloc; }
+ VMAllocator& dataAlloc() const { return *traceMonitor->dataAlloc; }
+
+ /* Member declarations for each opcode, to be called before interpreting the opcode. */
+#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
+ JS_REQUIRES_STACK AbortableRecordingStatus record_##op();
+# include "jsopcode.tbl"
+#undef OPDEF
+
+ JS_REQUIRES_STACK
+ TraceRecorder(JSContext* cx, TraceMonitor *tm, VMSideExit*, VMFragment*,
+ unsigned stackSlots, unsigned ngslots, JSValueType* typeMap,
+ VMSideExit* expectedInnerExit, JSScript* outerScript, jsbytecode* outerPC,
+ uint32 outerArgc, bool speculate);
+
+ /* The destructor should only be called through finish*, not directly. */
+ ~TraceRecorder();
+ JS_REQUIRES_STACK AbortableRecordingStatus finishSuccessfully();
+
+ enum AbortResult { NORMAL_ABORT, JIT_RESET };
+ JS_REQUIRES_STACK AbortResult finishAbort(const char* reason);
+
+ friend class ImportBoxedStackSlotVisitor;
+ friend class ImportUnboxedStackSlotVisitor;
+ friend class ImportGlobalSlotVisitor;
+ friend class AdjustCallerGlobalTypesVisitor;
+ friend class AdjustCallerStackTypesVisitor;
+ friend class TypeCompatibilityVisitor;
+ friend class ImportFrameSlotsVisitor;
+ friend class SlotMap;
+ friend class DefaultSlotMap;
+ friend class DetermineTypesVisitor;
+ friend class RecursiveSlotMap;
+ friend class UpRecursiveSlotMap;
+ friend MonitorResult RecordLoopEdge(JSContext*, TraceMonitor*, uintN&);
+ friend TracePointAction RecordTracePoint(JSContext*, TraceMonitor*, uintN &inlineCallCount,
+ bool *blacklist);
+ friend AbortResult AbortRecording(JSContext*, const char*);
+ friend class BoxArg;
+ friend void TraceMonitor::sweep(JSContext *cx);
+
+ public:
+ static bool JS_REQUIRES_STACK
+ startRecorder(JSContext*, TraceMonitor *, VMSideExit*, VMFragment*,
+ unsigned stackSlots, unsigned ngslots, JSValueType* typeMap,
+ VMSideExit* expectedInnerExit, JSScript* outerScript, jsbytecode* outerPC,
+ uint32 outerArgc, bool speculate);
+
+ /* Accessors. */
+ VMFragment* getFragment() const { return fragment; }
+ TreeFragment* getTree() const { return tree; }
+ bool outOfMemory() const { return traceMonitor->outOfMemory(); }
+ Oracle* getOracle() const { return oracle; }
+ JSObject* getGlobal() const { return globalObj; }
+
+ /* Entry points / callbacks from the interpreter. */
+ JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op);
+ JS_REQUIRES_STACK AbortableRecordingStatus record_EnterFrame();
+ JS_REQUIRES_STACK AbortableRecordingStatus record_LeaveFrame();
+ JS_REQUIRES_STACK AbortableRecordingStatus record_AddProperty(JSObject *obj);
+ JS_REQUIRES_STACK AbortableRecordingStatus record_DefLocalFunSetSlot(uint32 slot,
+ JSObject* obj);
+ JS_REQUIRES_STACK AbortableRecordingStatus record_NativeCallComplete();
+ void forgetGuardedShapesForObject(JSObject* obj);
+
+ bool globalSetExpected(unsigned slot) {
+ unsigned *pi = Find(pendingGlobalSlotsToSet, slot);
+ if (pi == pendingGlobalSlotsToSet.end()) {
+ /*
+ * Do slot arithmetic manually to avoid getSlotRef assertions which
+ * do not need to be satisfied for this purpose.
+ */
+ Value *vp = globalObj->getSlots() + slot;
+
+ /* If this global is definitely being tracked, then the write is unexpected. */
+ if (tracker.has(vp))
+ return false;
+
+ /*
+ * Otherwise, only abort if the global is not present in the
+ * import typemap. Just deep aborting false here is not acceptable,
+ * because the recorder does not guard on every operation that
+ * could lazily resolve. Since resolving adds properties to
+ * reserved slots, the tracer will never have imported them.
+ */
+ return tree->globalSlots->offsetOf((uint16)nativeGlobalSlot(vp)) == -1;
+ }
+ pendingGlobalSlotsToSet.erase(pi);
+ return true;
+ }
+
+#ifdef DEBUG
+ /* Debug printing functionality to emit printf() on trace. */
+ JS_REQUIRES_STACK void tprint(const char *format, int count, nanojit::LIns *insa[]);
+ JS_REQUIRES_STACK void tprint(const char *format);
+ JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins);
+ JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1,
+ nanojit::LIns *ins2);
+ JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1,
+ nanojit::LIns *ins2, nanojit::LIns *ins3);
+ JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1,
+ nanojit::LIns *ins2, nanojit::LIns *ins3,
+ nanojit::LIns *ins4);
+ JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1,
+ nanojit::LIns *ins2, nanojit::LIns *ins3,
+ nanojit::LIns *ins4, nanojit::LIns *ins5);
+ JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1,
+ nanojit::LIns *ins2, nanojit::LIns *ins3,
+ nanojit::LIns *ins4, nanojit::LIns *ins5,
+ nanojit::LIns *ins6);
+#endif
+};
+
+#define TRACING_ENABLED(cx) ((cx)->traceJitEnabled)
+#define REGEX_JIT_ENABLED(cx) ((cx)->traceJitEnabled || (cx)->methodJitEnabled)
+
+#define JSOP_IN_RANGE(op,lo,hi) (uintN((op) - (lo)) <= uintN((hi) - (lo)))
+#define JSOP_IS_BINARY(op) JSOP_IN_RANGE(op, JSOP_BITOR, JSOP_MOD)
+#define JSOP_IS_UNARY(op) JSOP_IN_RANGE(op, JSOP_NEG, JSOP_POS)
+#define JSOP_IS_EQUALITY(op) JSOP_IN_RANGE(op, JSOP_EQ, JSOP_NE)
+
+#define TRACE_ARGS_(x,args) \
+ JS_BEGIN_MACRO \
+ if (TraceRecorder* tr_ = TRACE_RECORDER(cx)) { \
+ AbortableRecordingStatus status = tr_->record_##x args; \
+ if (StatusAbortsRecorderIfActive(status)) { \
+ if (TRACE_RECORDER(cx)) { \
+ JS_ASSERT(TRACE_RECORDER(cx) == tr_); \
+ AbortRecording(cx, #x); \
+ } \
+ if (status == ARECORD_ERROR) \
+ goto error; \
+ } \
+ JS_ASSERT(status != ARECORD_IMACRO); \
+ } \
+ JS_END_MACRO
+
+#define TRACE_ARGS(x,args) TRACE_ARGS_(x, args)
+#define TRACE_0(x) TRACE_ARGS(x, ())
+#define TRACE_1(x,a) TRACE_ARGS(x, (a))
+#define TRACE_2(x,a,b) TRACE_ARGS(x, (a, b))
+
+extern JS_REQUIRES_STACK MonitorResult
+MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, JSInterpMode interpMode);
+
+extern JS_REQUIRES_STACK TracePointAction
+RecordTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist);
+
+extern JS_REQUIRES_STACK TracePointAction
+MonitorTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist,
+ void** traceData, uintN *traceEpoch, uint32 *loopCounter, uint32 hits);
+
+extern JS_REQUIRES_STACK TraceRecorder::AbortResult
+AbortRecording(JSContext* cx, const char* reason);
+
+extern bool
+InitJIT(TraceMonitor *tm);
+
+extern void
+FinishJIT(TraceMonitor *tm);
+
+extern void
+PurgeScriptFragments(TraceMonitor* tm, JSScript* script);
+
+extern bool
+OverfullJITCache(JSContext *cx, TraceMonitor* tm);
+
+extern void
+FlushJITCache(JSContext* cx, TraceMonitor* tm);
+
+extern JSObject *
+GetBuiltinFunction(JSContext *cx, uintN index);
+
+extern void
+SetMaxCodeCacheBytes(JSContext* cx, uint32 bytes);
+
+extern void
+ExternNativeToValue(JSContext* cx, Value& v, JSValueType type, double* slot);
+
+#ifdef MOZ_TRACEVIS
+
+extern JS_FRIEND_API(bool)
+StartTraceVis(const char* filename);
+
+extern JS_FRIEND_API(JSBool)
+StartTraceVisNative(JSContext *cx, uintN argc, jsval *vp);
+
+extern JS_FRIEND_API(bool)
+StopTraceVis();
+
+extern JS_FRIEND_API(JSBool)
+StopTraceVisNative(JSContext *cx, uintN argc, jsval *vp);
+
+/* Must contain no more than 16 items. */
+enum TraceVisState {
+ // Special: means we returned from current activity to last
+ S_EXITLAST,
+ // Activities
+ S_INTERP,
+ S_MONITOR,
+ S_RECORD,
+ S_COMPILE,
+ S_EXECUTE,
+ S_NATIVE,
+ // Events: these all have (bit 3) == 1.
+ S_RESET = 8
+};
+
+/* Reason for an exit to the interpreter. */
+enum TraceVisExitReason {
+ R_NONE,
+ R_ABORT,
+ /* Reasons in MonitorLoopEdge */
+ R_INNER_SIDE_EXIT,
+ R_DOUBLES,
+ R_CALLBACK_PENDING,
+ R_OOM_GETANCHOR,
+ R_BACKED_OFF,
+ R_COLD,
+ R_FAIL_RECORD_TREE,
+ R_MAX_PEERS,
+ R_FAIL_EXECUTE_TREE,
+ R_FAIL_STABILIZE,
+ R_FAIL_EXTEND_FLUSH,
+ R_FAIL_EXTEND_MAX_BRANCHES,
+ R_FAIL_EXTEND_START,
+ R_FAIL_EXTEND_COLD,
+ R_FAIL_SCOPE_CHAIN_CHECK,
+ R_NO_EXTEND_OUTER,
+ R_MISMATCH_EXIT,
+ R_OOM_EXIT,
+ R_TIMEOUT_EXIT,
+ R_DEEP_BAIL_EXIT,
+ R_STATUS_EXIT,
+ R_OTHER_EXIT
+};
+
+enum TraceVisFlushReason {
+ FR_DEEP_BAIL,
+ FR_OOM,
+ FR_GLOBAL_SHAPE_MISMATCH,
+ FR_GLOBALS_FULL
+};
+
+const unsigned long long MS64_MASK = 0xfull << 60;
+const unsigned long long MR64_MASK = 0x1full << 55;
+const unsigned long long MT64_MASK = ~(MS64_MASK | MR64_MASK);
+
+extern FILE* traceVisLogFile;
+extern JSHashTable *traceVisScriptTable;
+
+extern JS_FRIEND_API(void)
+StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r);
+
+static inline void
+LogTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
+{
+ if (traceVisLogFile) {
+ unsigned long long sllu = s;
+ unsigned long long rllu = r;
+ unsigned long long d = (sllu << 60) | (rllu << 55) | (rdtsc() & MT64_MASK);
+ fwrite(&d, sizeof(d), 1, traceVisLogFile);
+ }
+ if (traceVisScriptTable) {
+ StoreTraceVisState(cx, s, r);
+ }
+}
+
+/*
+ * Although this runs the same code as LogTraceVisState, it is a separate
+ * function because the meaning of the log entry is different. Also, the entry
+ * formats may diverge someday.
+ */
+static inline void
+LogTraceVisEvent(JSContext *cx, TraceVisState s, TraceVisFlushReason r)
+{
+ LogTraceVisState(cx, s, (TraceVisExitReason) r);
+}
+
+static inline void
+EnterTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
+{
+ LogTraceVisState(cx, s, r);
+}
+
+static inline void
+ExitTraceVisState(JSContext *cx, TraceVisExitReason r)
+{
+ LogTraceVisState(cx, S_EXITLAST, r);
+}
+
+struct TraceVisStateObj {
+ TraceVisExitReason r;
+ JSContext *mCx;
+
+ inline TraceVisStateObj(JSContext *cx, TraceVisState s) : r(R_NONE)
+ {
+ EnterTraceVisState(cx, s, R_NONE);
+ mCx = cx;
+ }
+ inline ~TraceVisStateObj()
+ {
+ ExitTraceVisState(mCx, r);
+ }
+};
+
+#endif /* MOZ_TRACEVIS */
+
+} /* namespace js */
+
+#else /* !JS_TRACER */
+
+#define TRACE_0(x) ((void)0)
+#define TRACE_1(x,a) ((void)0)
+#define TRACE_2(x,a,b) ((void)0)
+
+#endif /* !JS_TRACER */
+
+namespace js {
+
+/*
+ * While recording, the slots of the global object may change payload or type.
+ * This is fine as long as the recorder expects this change (and therefore has
+ * generated the corresponding LIR, snapshots, etc). The recorder indicates
+ * that it expects a write to a global slot by setting pendingGlobalSlotsToSet
+ * in the recorder, before the write is made by the interpreter, and clearing
+ * pendingGlobalSlotsToSet before recording the next op. Any global slot write
+ * that has not been whitelisted in this manner is therefore unexpected and, if
+ * the global slot is actually being tracked, recording must be aborted.
+ */
+static JS_INLINE void
+AbortRecordingIfUnexpectedGlobalWrite(JSContext *cx, JSObject *obj, unsigned slot)
+{
+#ifdef JS_TRACER
+ if (TraceRecorder *tr = TRACE_RECORDER(cx)) {
+ if (obj == tr->getGlobal() && !tr->globalSetExpected(slot))
+ AbortRecording(cx, "Global slot written outside tracer supervision");
+ }
+#endif
+}
+
+} /* namespace js */
+
+#endif /* jstracer_h___ */