gecko/js/src/jstracer.cpp

2330 lines
54 KiB
C++
Raw Normal View History

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 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):
* Andreas Gal <gal@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 ***** */
#include <math.h>
#include "nanojit/avmplus.h"
#include "nanojit/nanojit.h"
#include "jsarray.h"
#include "jsbool.h"
#include "jstracer.h"
#include "jscntxt.h"
#include "jsscript.h"
#include "jsprf.h"
#include "jsinterp.h"
#include "jsscope.h"
using namespace avmplus;
using namespace nanojit;
static GC gc = GC();
static avmplus::AvmCore* core = new (&gc) avmplus::AvmCore();
template<class T>
Tracker<T>::Tracker()
{
pagelist = 0;
}
template<class T>
Tracker<T>::~Tracker()
{
clear();
}
template<class T> jsuword
Tracker<T>::getPageBase(const void* v) const
{
return jsuword(v) & ~jsuword(NJ_PAGE_SIZE-1);
2008-06-04 00:09:57 -07:00
}
2008-06-11 11:29:35 -07:00
template<class T> struct Tracker<T>::Page*
Tracker<T>::findPage(const void* v) const
2008-06-11 11:29:35 -07:00
{
jsuword base = getPageBase(v);
struct Tracker<T>::Page* p = pagelist;
while (p) {
if (p->base == base) {
return p;
}
p = p->next;
}
return 0;
}
template <class T> struct Tracker<T>::Page*
Tracker<T>::addPage(const void* v) {
jsuword base = getPageBase(v);
2008-06-26 20:44:23 -07:00
struct Tracker::Page* p = (struct Tracker::Page*)
GC::Alloc(sizeof(struct Tracker::Page) + (NJ_PAGE_SIZE >> 2) * sizeof(LInsp));
p->base = base;
p->next = pagelist;
pagelist = p;
return p;
2008-06-11 11:29:35 -07:00
}
template <class T> void
Tracker<T>::clear()
2008-06-11 11:29:35 -07:00
{
while (pagelist) {
Page* p = pagelist;
pagelist = pagelist->next;
GC::Free(p);
}
2008-06-11 11:29:35 -07:00
}
template <class T> T
Tracker<T>::get(const void* v) const
{
struct Tracker<T>::Page* p = findPage(v);
JS_ASSERT(p != 0); /* we must have a page for the slot we are looking for */
T i = p->map[(jsuword(v) & 0xfff) >> 2];
JS_ASSERT(i != 0);
return i;
}
template <class T> void
Tracker<T>::set(const void* v, T ins)
{
struct Tracker<T>::Page* p = findPage(v);
2008-06-22 23:42:54 -07:00
if (!p)
p = addPage(v);
p->map[(jsuword(v) & 0xfff) >> 2] = ins;
}
#define LO ARGSIZE_LO
#define F ARGSIZE_F
#define Q ARGSIZE_Q
#ifdef DEBUG
#define NAME(op) ,#op
#else
#define NAME(op)
#endif
#define BUILTIN1(op, at0, atr, tr, t0, cse, fold) \
{ (intptr_t)&builtin_##op, (at0 << 2) | atr, cse, fold NAME(op) },
#define BUILTIN2(op, at0, at1, atr, tr, t0, t1, cse, fold) \
{ (intptr_t)&builtin_##op, (at0 << 4) | (at1 << 2) | atr, cse, fold NAME(op) },
#define BUILTIN3(op, at0, at1, at2, atr, tr, t0, t1, t2, cse, fold) \
{ (intptr_t)&builtin_##op, (at0 << 6) | (at1 << 4) | (at2 << 2) | atr, cse, fold NAME(op) },
static struct CallInfo builtins[] = {
#include "builtins.tbl"
2008-06-22 23:42:54 -07:00
};
2008-06-22 23:42:54 -07:00
#undef NAME
#undef BUILTIN1
#undef BUILTIN2
2008-06-22 23:42:54 -07:00
#undef BUILTIN3
/* Return the tag of a jsval. Doubles are checked whether they actually
represent an int, in which case we treat them as JSVAL_INT. */
static inline int getType(jsval v)
{
if (JSVAL_IS_INT(v))
return JSVAL_INT;
jsint i;
if (JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i))
return JSVAL_INT;
return JSVAL_TAG(v);
}
static inline bool isInt(jsval v)
{
return getType(v) == JSVAL_INT;
}
static inline bool isDouble(jsval v)
{
return getType(v) == JSVAL_DOUBLE;
}
static inline bool isNumber(jsval v)
{
return JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v);
}
static inline bool isTrueOrFalse(jsval v)
{
return v == JSVAL_TRUE || v == JSVAL_FALSE;
}
static inline jsint asInt(jsval v)
{
JS_ASSERT(isInt(v));
if (JSVAL_IS_DOUBLE(v))
return js_DoubleToECMAInt32(*JSVAL_TO_DOUBLE(v));
return JSVAL_TO_INT(v);
}
static inline jsdouble asNumber(jsval v)
{
JS_ASSERT(isNumber(v));
if (JSVAL_IS_DOUBLE(v))
return *JSVAL_TO_DOUBLE(v);
return (jsdouble)JSVAL_TO_INT(v);
}
static LIns* demote(LirWriter *out, LInsp i)
{
if (i->isCall())
return callArgN(i,0);
if (i->isop(LIR_i2f) || i->isop(LIR_u2f))
return i->oprnd1();
AvmAssert(i->isconstq());
double cf = i->constvalf();
int32_t ci = cf > 0x7fffffff ? uint32_t(cf) : int32_t(cf);
return out->insImm(ci);
}
static bool isPromoteInt(LIns *i)
{
return i->isop(LIR_i2f);
}
static bool isPromoteUint(LIns *i)
{
return i->isop(LIR_u2f);
}
static bool isPromote(LIns *i)
{
return isPromoteInt(i) || isPromoteUint(i);;
}
class FuncFilter: public LirWriter
{
public:
FuncFilter(LirWriter *out):
LirWriter(out)
{
}
LInsp ins2(LOpcode v, LInsp s1, LInsp s0)
{
if (s0 == s1 && v == LIR_feq) {
if (isPromote(s0)) {
// double(int) and double(uint) cannot be nan
return insImm(1);
}
if (s0->isop(LIR_fmul) || s0->isop(LIR_fsub) || s0->isop(LIR_fadd)) {
LInsp lhs = s0->oprnd1();
LInsp rhs = s0->oprnd2();
if (isPromote(lhs) && isPromote(rhs)) {
// add/sub/mul promoted ints can't be nan
return insImm(1);
}
}
} else if (v >= LIR_feq && v <= LIR_fge) {
if (isPromoteInt(s0) && isPromoteInt(s1)) {
// demote fcmp to cmp
v = LOpcode(v + (LIR_eq - LIR_feq));
return out->ins2(v, demote(out, s1), demote(out, s0));
} else if (isPromoteUint(s0) && isPromoteUint(s1)) {
// uint compare
v = LOpcode(v + (LIR_eq - LIR_feq));
if (v != LIR_eq)
v = LOpcode(v + (LIR_ult - LIR_lt)); // cmp -> ucmp
return out->ins2(v, demote(out, s1), demote(out, s0));
}
}
return out->ins2(v, s1, s0);
}
LInsp insCall(int32_t fid, LInsp args[])
{
if (fid == F_doubleToInt32) {
LInsp s0 = args[0];
if (s0->isop(LIR_fadd) || s0->isop(LIR_fsub) || s0->isop(LIR_fmul)) {
LInsp lhs = s0->oprnd1();
LInsp rhs = s0->oprnd2();
if (isPromote(lhs) && isPromote(rhs)) {
LOpcode op = LOpcode(s0->opcode() & ~LIR64);
return out->ins2(op, demote(out, lhs), demote(out, rhs));
}
}
}
return out->insCall(fid, args);
}
};
class ExitFilter: public LirWriter
{
TraceRecorder& recorder;
public:
ExitFilter(LirWriter *out, TraceRecorder& _recorder):
LirWriter(out), recorder(_recorder)
{
}
/* Determine the type of a store by looking at the current type of the actual value the
interpreter is using. For numbers we have to check what kind of store we used last
(integer or double) to figure out what the side exit show reflect in its typemap. */
int getStoreType(jsval& v) {
if (isNumber(v))
return recorder.get(&v)->isQuad() ? JSVAL_DOUBLE : JSVAL_INT;
return JSVAL_TAG(v);
}
/* Write out a type map for the current scopes and all outer scopes,
up until the entry scope. */
void
buildTypeMap(JSStackFrame* fp, JSFrameRegs& regs, char* m)
{
if (fp != recorder.getEntryFrame())
buildTypeMap(fp->down, *fp->down->regs, m);
for (unsigned n = 0; n < fp->argc; ++n)
*m++ = getStoreType(fp->argv[n]);
for (unsigned n = 0; n < fp->nvars; ++n)
*m++ = getStoreType(fp->vars[n]);
for (jsval* sp = fp->spbase; sp < regs.sp; ++sp)
*m++ = getStoreType(*sp);
}
virtual LInsp insGuard(LOpcode v, LIns *c, SideExit *x) {
VMSideExitInfo* i = (VMSideExitInfo*)x->vmprivate;
buildTypeMap(recorder.getFp(), recorder.getRegs(), i->typeMap);
return out->insGuard(v, c, x);
}
};
static void
buildTypeMap(JSStackFrame* entryFrame, JSStackFrame* fp, JSFrameRegs& regs, char* m);
TraceRecorder::TraceRecorder(JSContext* cx, Fragmento* fragmento, Fragment* _fragment)
{
this->cx = cx;
this->fragment = _fragment;
entryFrame = cx->fp;
entryRegs.pc = entryFrame->regs->pc;
entryRegs.sp = entryFrame->regs->sp;
#ifdef DEBUG
printf("entryRegs.pc=%p opcode=%d\n", entryRegs.pc, *entryRegs.pc);
#endif
fragment->calldepth = 0;
lirbuf = new (&gc) LirBuffer(fragmento, builtins);
fragment->lirbuf = lirbuf;
lir = lir_buf_writer = new (&gc) LirBufWriter(lirbuf);
#ifdef DEBUG
lirbuf->names = new (&gc) LirNameMap(&gc, builtins, fragmento->labels);
lir = verbose_filter = new (&gc) VerboseWriter(&gc, lir, lirbuf->names);
#endif
lir = cse_filter = new (&gc) CseFilter(lir, &gc);
lir = expr_filter = new (&gc) ExprFilter(lir);
lir = exit_filter = new (&gc) ExitFilter(lir, *this);
lir = func_filter = new (&gc) FuncFilter(lir);
lir->ins0(LIR_trace);
/* generate the entry map and stash it in the trace */
entryNativeFrameSlots = nativeFrameSlots(entryFrame, entryRegs);
maxNativeFrameSlots = entryNativeFrameSlots;
2008-07-05 14:05:25 -07:00
LIns* data = lir_buf_writer->skip(sizeof(VMFragmentInfo) +
entryNativeFrameSlots * sizeof(char));
2008-07-04 13:23:42 -07:00
fragmentInfo = (VMFragmentInfo*)data->payload();
buildTypeMap(entryFrame, entryFrame, entryRegs, fragmentInfo->typeMap);
fragmentInfo->nativeStackBase = nativeFrameOffset(&cx->fp->spbase[0]);
fragment->vmprivate = fragmentInfo;
fragment->param0 = lir->insImm8(LIR_param, Assembler::argRegs[0], 0);
fragment->param1 = lir->insImm8(LIR_param, Assembler::argRegs[1], 0);
fragment->sp = lir->insLoadi(fragment->param0, offsetof(InterpState, sp));
cx_ins = lir->insLoadi(fragment->param0, offsetof(InterpState, cx));
#ifdef DEBUG
lirbuf->names->addName(fragment->param0, "state");
lirbuf->names->addName(fragment->sp, "sp");
lirbuf->names->addName(cx_ins, "cx");
#endif
JSStackFrame* fp = cx->fp;
unsigned n;
for (n = 0; n < fp->argc; ++n)
import(&fp->argv[n], "arg", n);
for (n = 0; n < fp->nvars; ++n)
import(&fp->vars[n], "var", n);
for (n = 0; n < unsigned(fp->regs->sp - fp->spbase); ++n)
import(&fp->spbase[n], "stack", n);
}
TraceRecorder::~TraceRecorder()
{
#ifdef DEBUG
delete lirbuf->names;
delete verbose_filter;
#endif
delete cse_filter;
delete expr_filter;
delete exit_filter;
delete func_filter;
delete lir_buf_writer;
}
/* Determine the current call depth (starting with the entry frame.) */
unsigned
TraceRecorder::getCallDepth() const
{
JSStackFrame* fp = cx->fp;
unsigned depth = 0;
while (fp != entryFrame) {
++depth;
fp = fp->down;
}
return depth;
}
/* Find the frame that this address belongs to (if any). */
JSStackFrame*
TraceRecorder::findFrame(void* p) const
{
jsval* vp = (jsval*) p;
JSStackFrame* fp = cx->fp;
for (;;) {
// FIXME: fixing bug 441686 collapses the last two tests here
if (size_t(vp - fp->argv) < fp->argc ||
size_t(vp - fp->vars) < fp->nvars ||
size_t(vp - fp->spbase) < fp->script->depth) {
return fp;
}
if (fp == entryFrame)
return NULL;
fp = fp->down;
}
JS_NOT_REACHED("findFrame");
}
/* Determine whether an address is part of a currently active frame. */
bool
TraceRecorder::onFrame(void* p) const
{
return findFrame(p) != NULL;
}
/* Calculate the total number of native frame slots we need from this frame
all the way back to the entry frame, including the current stack usage. */
unsigned
TraceRecorder::nativeFrameSlots(JSStackFrame* fp, JSFrameRegs& regs) const
{
unsigned slots = 0;
for (;;) {
slots += fp->argc + fp->nvars + (regs.sp - fp->spbase);
if (fp == entryFrame)
return slots;
fp = fp->down;
}
JS_NOT_REACHED("nativeFrameSlots");
}
/* Determine the offset in the native frame (marshal) for an address
that is part of a currently active frame. */
size_t
TraceRecorder::nativeFrameOffset(void* p) const
{
jsval* vp = (jsval*) p;
JSStackFrame* fp = findFrame(p);
JS_ASSERT(fp != NULL); // must be on the frame somewhere
size_t offset = size_t(vp - fp->argv);
if (offset >= fp->argc) {
// FIXME: fixing bug 441686 collapses the vars and spbase cases
offset = size_t(vp - fp->vars);
if (offset >= fp->nvars) {
JS_ASSERT(size_t(vp - fp->spbase) < fp->script->depth);
offset = fp->nvars + size_t(vp - fp->spbase);
}
offset += fp->argc;
}
if (fp != entryFrame)
offset += nativeFrameSlots(fp->down, *fp->regs);
return offset * sizeof(double);
}
/* Track the maximum number of native frame slots we need during
execution. */
void
TraceRecorder::trackNativeFrameUse(unsigned slots)
{
if (slots > maxNativeFrameSlots)
maxNativeFrameSlots = slots;
}
2008-06-28 18:19:21 -07:00
/* Write out a type map for the current scopes and all outer scopes,
up until the entry scope. */
static void
buildTypeMap(JSStackFrame* entryFrame, JSStackFrame* fp, JSFrameRegs& regs, char* m)
2008-06-28 18:19:21 -07:00
{
if (fp != entryFrame)
buildTypeMap(entryFrame, fp->down, *fp->down->regs, m);
2008-06-28 18:19:21 -07:00
for (unsigned n = 0; n < fp->argc; ++n)
*m++ = getType(fp->argv[n]);
2008-06-28 18:19:21 -07:00
for (unsigned n = 0; n < fp->nvars; ++n)
*m++ = getType(fp->vars[n]);
for (jsval* sp = fp->spbase; sp < regs.sp; ++sp)
*m++ = getType(*sp);
2008-06-28 18:19:21 -07:00
}
/* Unbox a jsval into a slot. Slots are wide enough to hold double values
directly (instead of storing a pointer to them). */
static bool
unbox_jsval(jsval v, int t, double* slot)
{
if (t != getType(v))
return false;
switch (t) {
case JSVAL_BOOLEAN:
*(bool*)slot = JSVAL_TO_BOOLEAN(v);
break;
case JSVAL_INT:
*(jsint*)slot = asInt(v);
break;
case JSVAL_DOUBLE:
*(jsdouble*)slot = *JSVAL_TO_DOUBLE(v);
break;
case JSVAL_STRING:
*(JSString**)slot = JSVAL_TO_STRING(v);
break;
default:
JS_ASSERT(JSVAL_IS_GCTHING(v));
*(void**)slot = JSVAL_TO_GCTHING(v);
}
return true;
}
/* Box a value from the native stack back into the jsval format. Integers
that are too large to fit into a jsval are automatically boxed into
heap-allocated doubles. */
static bool
box_jsval(JSContext* cx, jsval* vp, int t, double* slot)
{
switch (t) {
case JSVAL_BOOLEAN:
*vp = BOOLEAN_TO_JSVAL(*(bool*)slot);
break;
case JSVAL_INT:
jsint i = *(jsint*)slot;
if (INT_FITS_IN_JSVAL(i))
*vp = INT_TO_JSVAL(i);
else
return js_NewDoubleInRootedValue(cx, (jsdouble)i, vp);
break;
case JSVAL_DOUBLE:
return js_NewDoubleInRootedValue(cx, *slot, vp);
case JSVAL_STRING:
*vp = STRING_TO_JSVAL(*(JSString**)slot);
break;
default:
JS_ASSERT(t == JSVAL_OBJECT);
*vp = OBJECT_TO_JSVAL(*(JSObject**)slot);
break;
}
return true;
}
/* Attempt to unbox the given JS frame into a native frame, checking
along the way that the supplied typemap holds. */
static bool
unbox(JSStackFrame* fp, JSFrameRegs& regs, char* m, double* native)
{
jsval* vp;
for (vp = fp->argv; vp < fp->argv + fp->argc; ++vp)
if (!unbox_jsval(*vp, (JSType)*m++, native++))
return false;
for (vp = fp->vars; vp < fp->vars + fp->nvars; ++vp)
if (!unbox_jsval(*vp, (JSType)*m++, native++))
return false;
for (vp = fp->spbase; vp < regs.sp; ++vp)
if (!unbox_jsval(*vp, (JSType)*m++, native++))
return false;
return true;
}
/* Attempt to unbox the given JS frame into a native frame, checking
along the way that the supplied typemap holds. */
static bool
box(JSContext* cx, JSStackFrame* fp, JSFrameRegs& regs, char* m, double* native)
{
jsval* vp;
for (vp = fp->argv; vp < fp->argv + fp->argc; ++vp)
if (!box_jsval(cx, vp, (JSType)*m++, native++))
return false;
for (vp = fp->vars; vp < fp->vars + fp->nvars; ++vp)
if (!box_jsval(cx, vp, (JSType)*m++, native++))
return false;
for (vp = fp->spbase; vp < regs.sp; ++vp)
if (!box_jsval(cx, vp, (JSType)*m++, native++))
return false;
return true;
}
/* Emit load instructions onto the trace that read the initial stack state. */
void
TraceRecorder::import(jsval* p, char *prefix, int index)
{
JS_ASSERT(onFrame(p));
LIns *ins = lir->insLoad(isDouble(*p) ? LIR_ldq : LIR_ld,
2008-07-04 13:23:42 -07:00
fragment->sp, -fragmentInfo->nativeStackBase + nativeFrameOffset(p) + 8);
tracker.set(p, ins);
#ifdef DEBUG
if (prefix) {
char name[16];
JS_ASSERT(strlen(prefix) < 10);
2008-07-01 15:58:47 -07:00
JS_snprintf(name, sizeof name, "$%s%d", prefix, index);
lirbuf->names->addName(ins, name);
}
#endif
}
/* Update the tracker. If the value is part of any argv/vars/stack of any
currently active frame (onFrame), then issue a write back store. */
void
TraceRecorder::set(void* p, LIns* i)
{
tracker.set(p, i);
if (onFrame(p))
2008-07-04 13:23:42 -07:00
lir->insStorei(i, fragment->sp, -fragmentInfo->nativeStackBase + nativeFrameOffset(p) + 8);
}
LIns*
TraceRecorder::get(void* p)
{
return tracker.get(p);
}
JSStackFrame*
TraceRecorder::getEntryFrame() const
{
return entryFrame;
}
JSStackFrame*
TraceRecorder::getFp() const
{
return cx->fp;
}
JSFrameRegs&
TraceRecorder::getRegs() const
{
return *cx->fp->regs;
}
SideExit*
TraceRecorder::snapshot()
{
/* generate the entry map and stash it in the trace */
unsigned slots = nativeFrameSlots(cx->fp, *cx->fp->regs);
trackNativeFrameUse(slots);
/* reserve space for the type map, ExitFilter will write it out for us */
LIns* data = lir_buf_writer->skip(sizeof(VMSideExitInfo) + slots * sizeof(char));
VMSideExitInfo* si = (VMSideExitInfo*)data->payload();
/* setup side exit structure */
memset(&exit, 0, sizeof(exit));
#ifdef DEBUG
exit.from = fragment;
#endif
exit.calldepth = getCallDepth();
2008-07-04 13:23:42 -07:00
exit.sp_adj = (cx->fp->regs->sp - entryRegs.sp) * sizeof(double);
exit.ip_adj = cx->fp->regs->pc - entryRegs.pc;
exit.vmprivate = si;
return &exit;
}
void
2008-07-04 13:23:42 -07:00
TraceRecorder::guard(bool expected, LIns* cond)
{
lir->insGuard(expected ? LIR_xf : LIR_xt,
cond,
snapshot());
}
/* See if the type of a loop variable matches its loop entry type, or whether we can at least
make it match by promoting it. */
bool
TraceRecorder::adjustType(jsval& v, int type)
{
/* if the type is still the same, we are done */
if (getType(v) == type)
return true;
printf("getType(v): %d type: %d\n", getType(v), type);
/* if its an integer now, but we want a double at entry, make it so */
if (getType(v) == JSVAL_INT && type == JSVAL_DOUBLE) {
set(&v, i2f(get(&v)));
return true;
}
/* fail, incompatible types */
return false;
}
/* Make sure that all loop-carrying values have a stable type along the loop edge. */
bool
TraceRecorder::verifyTypeStability(JSStackFrame* fp, JSFrameRegs& regs, char* m)
{
if (fp != entryFrame)
verifyTypeStability(fp->down, *fp->down->regs, m);
for (unsigned n = 0; n < fp->argc; ++n, ++m)
if (!adjustType(fp->argv[n], *m))
return false;
for (unsigned n = 0; n < fp->nvars; ++n, ++m)
if (!adjustType(fp->vars[n], *m))
return false;
for (jsval* sp = fp->spbase; sp < regs.sp; ++sp, ++m)
if (!adjustType(*sp, *m))
return false;
return true;
}
void
TraceRecorder::closeLoop(Fragmento* fragmento)
{
if (!verifyTypeStability(entryFrame, entryRegs, fragmentInfo->typeMap)) {
#ifdef DEBUG
printf("Trace rejected: unstable loop variables.\n");
#endif
return;
}
fragment->lastIns = lir->ins0(LIR_loop);
((VMFragmentInfo*)fragment->vmprivate)->maxNativeFrameSlots = maxNativeFrameSlots;
compile(fragmento->assm(), fragment);
}
bool
TraceRecorder::loopEdge()
{
if (cx->fp->regs->pc == entryRegs.pc) {
closeLoop(JS_TRACE_MONITOR(cx).fragmento);
return false; /* done recording */
}
return false; /* abort recording */
}
void
TraceRecorder::stop()
{
fragment->blacklist();
}
void
js_DeleteRecorder(JSContext* cx)
{
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
delete tm->recorder;
tm->recorder = NULL;
}
#define HOTLOOP1 10
#define HOTLOOP2 13
#define HOTLOOP3 37
bool
js_LoopEdge(JSContext* cx)
{
2008-06-22 23:42:54 -07:00
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
/* is the recorder currently active? */
if (tm->recorder) {
if (tm->recorder->loopEdge())
return true; /* keep recording */
js_DeleteRecorder(cx);
return false; /* done recording */
}
InterpState state;
state.ip = (FOpcodep)cx->fp->regs->pc;
Fragment* f = tm->fragmento->getLoop(state);
if (!f->code()) {
int hits = ++f->hits();
if (!f->isBlacklisted() && hits >= HOTLOOP1) {
if (hits == HOTLOOP1 || hits == HOTLOOP2 || hits == HOTLOOP3) {
tm->recorder = new (&gc) TraceRecorder(cx, tm->fragmento, f);
return true; /* start recording */
}
if (hits > HOTLOOP3)
f->blacklist();
}
return false;
}
/* execute previously recorded race */
VMFragmentInfo* fi = (VMFragmentInfo*)f->vmprivate;
double native[fi->maxNativeFrameSlots+1];
#ifdef DEBUG
*(uint64*)&native[fi->maxNativeFrameSlots] = 0xdeadbeefdeadbeefLL;
#endif
unbox(cx->fp, *cx->fp->regs, fi->typeMap, native);
2008-07-05 14:05:25 -07:00
double* entry_sp = &native[fi->nativeStackBase/sizeof(double) +
(cx->fp->regs->sp - cx->fp->spbase - 1)];
2008-07-04 13:23:42 -07:00
state.sp = (void*)entry_sp;
state.rp = NULL;
state.f = NULL;
state.cx = cx;
union { NIns *code; GuardRecord* (FASTCALL *func)(InterpState*, Fragment*); } u;
u.code = f->code();
#ifdef DEBUG
printf("entering trace, pc=%p, sp=%p\n", state.ip, state.sp);
uint64 start = rdtsc();
#endif
GuardRecord* lr = u.func(&state, NULL);
#ifdef DEBUG
printf("leaving trace, pc=%p, sp=%p, cycles=%llu\n", state.ip, state.sp,
(rdtsc() - start));
#endif
cx->fp->regs->sp += (((double*)state.sp - entry_sp));
cx->fp->regs->pc = (jsbytecode*)state.ip;
box(cx, cx->fp, *cx->fp->regs, ((VMSideExitInfo*)lr->vmprivate)->typeMap, native);
#ifdef DEBUG
JS_ASSERT(*(uint64*)&native[fi->maxNativeFrameSlots] == 0xdeadbeefdeadbeefLL);
#endif
return false; /* continue with regular interpreter */
}
2008-06-28 18:19:21 -07:00
void
js_AbortRecording(JSContext* cx, const char* reason)
2008-06-28 18:19:21 -07:00
{
#ifdef DEBUG
printf("Abort recording: %s.\n", reason);
#endif
JS_TRACE_MONITOR(cx).recorder->stop();
js_DeleteRecorder(cx);
2008-06-28 18:19:21 -07:00
}
extern void
js_InitJIT(JSContext* cx)
{
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
if (!tm->fragmento) {
Fragmento* fragmento = new (&gc) Fragmento(core);
#ifdef DEBUG
fragmento->labels = new (&gc) LabelMap(core, NULL);
#endif
fragmento->assm()->setCallTable(builtins);
tm->fragmento = fragmento;
}
}
jsval&
TraceRecorder::argval(unsigned n) const
{
JS_ASSERT((n >= 0) && (n <= cx->fp->argc));
return cx->fp->argv[n];
}
jsval&
TraceRecorder::varval(unsigned n) const
{
JS_ASSERT((n >= 0) && (n <= cx->fp->nvars));
return cx->fp->vars[n];
}
jsval&
TraceRecorder::stackval(int n) const
{
2008-07-05 14:05:25 -07:00
JS_ASSERT((cx->fp->regs->sp + n < cx->fp->spbase + cx->fp->script->depth) &&
(cx->fp->regs->sp + n >= cx->fp->spbase));
return cx->fp->regs->sp[n];
}
LIns*
TraceRecorder::arg(unsigned n)
{
return get(&argval(n));
}
void
TraceRecorder::arg(unsigned n, LIns* i)
{
set(&argval(n), i);
}
LIns*
TraceRecorder::var(unsigned n)
{
return get(&varval(n));
}
void
TraceRecorder::var(unsigned n, LIns* i)
{
set(&varval(n), i);
}
LIns*
TraceRecorder::stack(int n)
{
return get(&stackval(n));
}
void
TraceRecorder::stack(int n, LIns* i)
{
set(&stackval(n), i);
}
LIns*
TraceRecorder::i2f(LIns* i)
{
return lir->ins1(LIR_i2f, i);
}
LIns*
TraceRecorder::u2f(LIns* u)
{
return lir->ins1(LIR_u2f, u);
}
LIns*
TraceRecorder::f2i(LIns* f)
{
return lir->insCall(F_doubleToInt32, &f);
}
2008-07-05 22:23:34 -07:00
bool TraceRecorder::ifop(bool sense)
{
jsval& v = stackval(-1);
LIns* cond_ins;
bool cond;
if (isTrueOrFalse(v)) {
cond_ins = lir->ins_eq0(jsval_to_boolean(get(&v)));
cond = JSVAL_TO_BOOLEAN(v);
} else {
return false;
}
if (!sense) {
cond = !cond;
cond_ins = lir->ins_eq0(cond_ins);
}
guard(cond, cond_ins);
return true;
}
bool
TraceRecorder::inc(jsval& v, jsint incr, bool pre)
{
if (isInt(v)) {
LIns* before = get(&v);
LIns* after = lir->ins2i(LIR_add, before, incr);
2008-07-04 13:23:42 -07:00
guard(false, lir->ins1(LIR_ov, after));
set(&v, after);
stack(0, pre ? after : before);
return true;
}
return false;
}
bool
TraceRecorder::cmp(LOpcode op, bool negate)
{
jsval& r = stackval(-1);
jsval& l = stackval(-2);
if (isInt(l) && isInt(r)) {
LIns* x = lir->ins2(op, get(&l), get(&r));
if (negate)
x = lir->ins2i(LIR_eq, x, 0);
bool cond;
switch (op) {
case LIR_lt:
cond = asInt(l) < asInt(r);
break;
case LIR_gt:
cond = asInt(l) > asInt(r);
break;
case LIR_le:
cond = asInt(l) <= asInt(r);
break;
case LIR_ge:
cond = asInt(l) >= asInt(r);
break;
default:
JS_ASSERT(cond == LIR_eq);
cond = asInt(l) == asInt(r);
break;
}
2008-07-04 13:23:42 -07:00
/* The interpreter fuses comparisons and the following branch,
so we have to do that here as well. */
if (cx->fp->regs->pc[1] == ::JSOP_IFEQ)
2008-07-04 13:23:42 -07:00
guard(!cond, x);
else if (cx->fp->regs->pc[1] == ::JSOP_IFNE)
2008-07-04 13:23:42 -07:00
guard(cond, x);
/* We update the stack after the guard. This is safe since
the guard bails out at the comparison and the interpreter
will this re-execute the comparison. This way the
value of the condition doesn't have to be calculated and
saved on the stack in most cases. */
set(&l, x);
return true;
}
return false;
}
bool
TraceRecorder::iunary(LOpcode op)
{
jsval& v = stackval(-1);
if (isNumber(v)) {
set(&v, lir->ins1(op, get(&v)));
return true;
}
return false;
}
bool
TraceRecorder::ibinary(LOpcode op)
{
jsval& r = stackval(-1);
jsval& l = stackval(-2);
if (isInt(l) && isInt(r)) {
set(&l, lir->ins2(op, get(&l), get(&r)));
return true;
}
return false;
}
bool
TraceRecorder::bbinary(LOpcode op)
{
jsval& r = stackval(-1);
jsval& l = stackval(-2);
if (isTrueOrFalse(l) && isTrueOrFalse(r)) {
LIns* result = lir->ins2(op, get(&l), get(&r));
set(&l, result);
return true;
}
return false;
}
bool
TraceRecorder::dbinary(LOpcode op)
{
jsval& r = stackval(-1);
jsval& l = stackval(-2);
if (isNumber(l) && isNumber(r)) {
/* if we store our operands currently as int, we have to cast them to float */
LIns* l_ins = get(&l);
if (isInt(l))
l_ins = i2f(l_ins);
LIns* r_ins = get(&r);
if (isInt(r))
r_ins = i2f(r_ins);
set(&l, lir->ins2(op, l_ins, r_ins));
return true;
}
return false;
}
void
TraceRecorder::demote(jsval& v, jsdouble result)
{
jsint i;
if (JSDOUBLE_IS_INT(result, i)) {
LIns* v_ins = get(&v);
set(&v, lir->insCall(F_doubleToInt32, &v_ins));
}
}
bool
TraceRecorder::map_is_native(JSObjectMap* map, LIns* map_ins)
{
LIns* ops = lir->insLoadi(map_ins, offsetof(JSObjectMap, ops));
if (map->ops == &js_ObjectOps) {
2008-07-05 13:44:48 -07:00
guard(true, lir->ins2(LIR_eq, ops, lir->insImmPtr(&js_ObjectOps)));
return true;
}
LIns* n = lir->insLoadi(ops, offsetof(JSObjectOps, newObjectMap));
if (map->ops->newObjectMap == js_ObjectOps.newObjectMap) {
2008-07-05 13:44:48 -07:00
guard(true, lir->ins2(LIR_eq, n, lir->insImmPtr(&js_ObjectOps.newObjectMap)));
return true;
}
return false;
}
bool
TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2,
JSPropCacheEntry*& entry)
{
LIns* map_ins = lir->insLoadi(obj_ins, offsetof(JSObject, map));
if (!map_is_native(obj->map, map_ins))
return false;
JSAtom* atom;
PROPERTY_CACHE_TEST(cx, cx->fp->regs->pc, obj, obj2, entry, atom);
if (atom)
return false;
if (PCVCAP_TAG(entry->vcap == 1))
return false; // need to look in the prototype, NYI
if (OBJ_SCOPE(obj)->object != obj)
return false; // need to normalize to the owner of the shared scope, NYI
LIns* shape_ins = lir->insLoadi(map_ins, offsetof(JSScope, shape));
#ifdef DEBUG
lirbuf->names->addName(shape_ins, "shape");
#endif
guard(true, lir->ins2i(LIR_eq, shape_ins, OBJ_SCOPE(obj)->shape));
return true;
}
2008-07-05 13:44:48 -07:00
void
TraceRecorder::stobj_set_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins, LIns* v_ins)
{
if (slot < JS_INITIAL_NSLOTS)
lir->insStorei(v_ins,
obj_ins,
offsetof(JSObject, fslots) + slot * sizeof(jsval));
2008-07-05 13:44:48 -07:00
else {
if (!dslots_ins)
dslots_ins = lir->insLoadi(obj_ins, offsetof(JSObject, dslots));
lir->insStorei(v_ins,
2008-07-05 13:44:48 -07:00
dslots_ins,
(slot - JS_INITIAL_NSLOTS) * sizeof(jsval));
2008-07-05 13:44:48 -07:00
}
}
LIns*
TraceRecorder::stobj_get_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins)
{
if (slot < JS_INITIAL_NSLOTS) {
return lir->insLoadi(obj_ins,
offsetof(JSObject, fslots) + slot * sizeof(jsval));
2008-07-05 13:44:48 -07:00
}
if (!dslots_ins)
dslots_ins = lir->insLoadi(obj_ins, offsetof(JSObject, dslots));
return lir->insLoadi(dslots_ins, (slot - JS_INITIAL_NSLOTS) * sizeof(jsval));
}
bool
2008-07-05 13:44:48 -07:00
TraceRecorder::native_set(LIns* obj_ins, JSScopeProperty* sprop, LIns*& dslots_ins, LIns* v_ins)
{
if (SPROP_HAS_STUB_SETTER(sprop) && sprop->slot != SPROP_INVALID_SLOT) {
2008-07-05 13:44:48 -07:00
stobj_set_slot(obj_ins, sprop->slot, dslots_ins, v_ins);
return true;
}
return false;
}
bool
2008-07-05 13:44:48 -07:00
TraceRecorder::native_get(LIns* obj_ins, LIns* pobj_ins, JSScopeProperty* sprop,
LIns*& dslots_ins, LIns*& v_ins)
{
if (!SPROP_HAS_STUB_GETTER(sprop))
return false;
if (sprop->slot != SPROP_INVALID_SLOT)
v_ins = stobj_get_slot(pobj_ins, sprop->slot, dslots_ins);
else
v_ins = lir->insImm(JSVAL_VOID);
return true;
}
void
TraceRecorder::guard_jsval_tag(LIns* v_ins, jsuint tag)
{
guard(true, lir->ins2i(LIR_eq,
lir->ins2(LIR_and, v_ins, lir->insImmPtr((void*)JSVAL_TAGMASK)),
tag));
}
2008-07-05 13:44:48 -07:00
LIns*
TraceRecorder::int32_to_jsval(LIns* i_ins)
{
2008-07-05 13:44:48 -07:00
LIns* args[] = { cx_ins, i_ins };
LIns* ret = lir->insCall(F_BoxInt32, args);
guard(false, lir->ins2(LIR_eq, ret, lir->insImmPtr((void*)JSVAL_ERROR_COOKIE)));
2008-07-05 13:44:48 -07:00
return ret;
}
LIns*
TraceRecorder::double_to_jsval(LIns* d_ins)
{
LIns* args[] = { cx_ins, d_ins };
LIns* ret = lir->insCall(F_BoxDouble, args);
guard(false, lir->ins2(LIR_eq, ret, lir->insImmPtr((void*)JSVAL_ERROR_COOKIE)));
return ret;
}
2008-07-05 18:34:41 -07:00
LIns*
TraceRecorder::boolean_to_jsval(LIns* b_ins)
{
return lir->ins2i(LIR_or, lir->ins2i(LIR_lsh, b_ins, JSVAL_TAGBITS), JSVAL_BOOLEAN);
}
JS_STATIC_ASSERT(JSVAL_OBJECT == 0);
LIns*
TraceRecorder::object_to_jsval(LIns* b_ins)
{
return lir->ins2i(LIR_lsh, b_ins, JSVAL_TAGBITS);
}
LIns*
TraceRecorder::jsval_to_int32(LIns* v_ins)
2008-07-05 13:44:48 -07:00
{
LIns* ret = lir->insCall(F_UnboxInt32, &v_ins);
guard(false, lir->ins2i(LIR_eq, ret, INT32_ERROR_COOKIE));
2008-07-05 13:44:48 -07:00
return ret;
}
LIns*
TraceRecorder::jsval_to_double(LIns* v_ins)
{
guard_jsval_tag(v_ins, JSVAL_DOUBLE);
2008-07-05 13:44:48 -07:00
return lir->insLoadi(lir->ins2(LIR_and, v_ins, lir->insImmPtr((void*)~JSVAL_TAGMASK)), 0);
}
LIns*
2008-07-05 18:34:41 -07:00
TraceRecorder::jsval_to_boolean(LIns* v_ins)
2008-07-05 13:44:48 -07:00
{
guard(true, lir->ins2i(LIR_eq, lir->ins2(LIR_and, v_ins, lir->insImmPtr((void*)~JSVAL_TRUE)),
JSVAL_BOOLEAN));
2008-07-05 18:34:41 -07:00
return lir->ins2i(LIR_ush, v_ins, JSVAL_TAGBITS);
2008-07-05 13:44:48 -07:00
}
LIns*
TraceRecorder::jsval_to_object(LIns* v_ins)
{
guard_jsval_tag(v_ins, JSVAL_OBJECT);
return lir->ins2(LIR_and, v_ins, lir->insImmPtr((void*)~JSVAL_TAGMASK));
}
bool
TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
{
if (isInt(v))
v_ins = jsval_to_int32(v_ins);
else if (isDouble(v))
v_ins = jsval_to_double(v_ins);
else if (isTrueOrFalse(v))
v_ins = jsval_to_boolean(v_ins);
else
return false; /* we don't know how to convert that type */
return true;
}
2008-07-05 13:44:48 -07:00
bool TraceRecorder::guardThatObjectIsDenseArray(JSObject* obj, LIns* obj_ins, LIns*& dslots_ins)
{
if (!OBJ_IS_DENSE_ARRAY(cx, obj))
return false;
// guard(OBJ_GET_CLASS(obj) == &js_ArrayClass);
LIns* class_ins = stobj_get_slot(obj_ins, JSSLOT_CLASS, dslots_ins);
2008-07-05 13:44:48 -07:00
class_ins = lir->ins2(LIR_and, class_ins, lir->insImmPtr((void*)~3));
guard(true, lir->ins2(LIR_eq, class_ins, lir->insImmPtr(&js_ArrayClass)));
return true;
}
bool TraceRecorder::guardDenseArrayIndexWithinBounds(JSObject* obj, jsint idx,
LIns* obj_ins, LIns*& dslots_ins, LIns* idx_ins)
{
jsuint length = ARRAY_DENSE_LENGTH(obj);
if (!((jsuint)idx < length && idx < obj->fslots[JSSLOT_ARRAY_LENGTH]))
return false;
if (!dslots_ins)
dslots_ins = lir->insLoadi(obj_ins, offsetof(JSObject, dslots));
LIns* length_ins = stobj_get_slot(obj_ins, JSSLOT_ARRAY_LENGTH, dslots_ins);
2008-07-05 13:44:48 -07:00
// guard(index < length)
guard(true, lir->ins2(LIR_lt, idx_ins, length_ins));
// guard(index < capacity)
guard(false, lir->ins_eq0(dslots_ins));
guard(true, lir->ins2(LIR_lt, idx_ins,
lir->insLoadi(dslots_ins, -sizeof(jsval))));
return true;
}
bool TraceRecorder::JSOP_INTERRUPT()
{
return false;
}
bool TraceRecorder::JSOP_PUSH()
{
2008-07-06 06:52:50 -07:00
stack(0, lir->insImm(JSVAL_VOID));
return true;
}
bool TraceRecorder::JSOP_POPV()
{
return false;
}
bool TraceRecorder::JSOP_ENTERWITH()
{
return false;
}
bool TraceRecorder::JSOP_LEAVEWITH()
{
return false;
}
bool TraceRecorder::JSOP_RETURN()
{
return false;
}
bool TraceRecorder::JSOP_GOTO()
{
return false;
}
bool TraceRecorder::JSOP_IFEQ()
{
2008-07-05 22:23:34 -07:00
return ifop(true);
}
bool TraceRecorder::JSOP_IFNE()
{
2008-07-05 22:23:34 -07:00
return ifop(false);
}
bool TraceRecorder::JSOP_ARGUMENTS()
{
return false;
}
bool TraceRecorder::JSOP_FORARG()
{
return false;
}
bool TraceRecorder::JSOP_FORVAR()
{
return false;
}
bool TraceRecorder::JSOP_DUP()
{
stack(0, get(&stackval(-1)));
return true;
}
bool TraceRecorder::JSOP_DUP2()
{
stack(0, get(&stackval(-2)));
stack(1, get(&stackval(-1)));
return true;
}
bool TraceRecorder::JSOP_SETCONST()
{
return false;
}
bool TraceRecorder::JSOP_BITOR()
{
return ibinary(LIR_or);
}
bool TraceRecorder::JSOP_BITXOR()
{
return ibinary(LIR_xor);
}
bool TraceRecorder::JSOP_BITAND()
{
return ibinary(LIR_and);
}
bool TraceRecorder::JSOP_EQ()
{
return cmp(LIR_eq);
}
bool TraceRecorder::JSOP_NE()
{
return cmp(LIR_eq, true);
}
bool TraceRecorder::JSOP_LT()
{
return cmp(LIR_lt);
}
bool TraceRecorder::JSOP_LE()
{
return cmp(LIR_le);
}
bool TraceRecorder::JSOP_GT()
{
return cmp(LIR_gt);
}
bool TraceRecorder::JSOP_GE()
{
return cmp(LIR_ge);
}
bool TraceRecorder::JSOP_LSH()
{
return ibinary(LIR_lsh);
}
bool TraceRecorder::JSOP_RSH()
{
return ibinary(LIR_rsh);
}
bool TraceRecorder::JSOP_URSH()
{
return ibinary(LIR_ush);
}
bool TraceRecorder::JSOP_ADD()
{
if (dbinary(LIR_fadd)) {
demote(stackval(-2), asNumber(stackval(-2)) + asNumber(stackval(-1)));
return true;
}
return false;
}
bool TraceRecorder::JSOP_SUB()
{
if (dbinary(LIR_fsub)) {
demote(stackval(-2), asNumber(stackval(-2)) - asNumber(stackval(-1)));
return true;
}
return false;
}
bool TraceRecorder::JSOP_MUL()
{
if (dbinary(LIR_fmul)) {
demote(stackval(-2), asNumber(stackval(-2)) - asNumber(stackval(-1)));
return true;
}
return false;
}
bool TraceRecorder::JSOP_DIV()
{
if (dbinary(LIR_fdiv)) {
demote(stackval(-2), asNumber(stackval(-2)) - asNumber(stackval(-1)));
return true;
}
return false;
}
bool TraceRecorder::JSOP_MOD()
{
return false;
}
bool TraceRecorder::JSOP_NOT()
{
jsval& v = stackval(-1);
if (isTrueOrFalse(v)) {
set(&v, lir->ins_eq0(get(&v)));
return true;
}
return false;
}
bool TraceRecorder::JSOP_BITNOT()
{
return iunary(LIR_not);
}
bool TraceRecorder::JSOP_NEG()
{
return false;
}
bool TraceRecorder::JSOP_NEW()
{
return false;
}
bool TraceRecorder::JSOP_DELNAME()
{
return false;
}
bool TraceRecorder::JSOP_DELPROP()
{
return false;
}
bool TraceRecorder::JSOP_DELELEM()
{
return false;
}
bool TraceRecorder::JSOP_TYPEOF()
{
return false;
}
bool TraceRecorder::JSOP_VOID()
{
return false;
}
bool TraceRecorder::JSOP_INCNAME()
{
return false;
}
bool TraceRecorder::JSOP_INCPROP()
{
return false;
}
bool TraceRecorder::JSOP_INCELEM()
{
return false;
}
bool TraceRecorder::JSOP_DECNAME()
{
return false;
}
bool TraceRecorder::JSOP_DECPROP()
{
return false;
}
bool TraceRecorder::JSOP_DECELEM()
{
return false;
}
bool TraceRecorder::JSOP_NAMEINC()
{
return false;
}
bool TraceRecorder::JSOP_PROPINC()
{
return false;
}
bool TraceRecorder::JSOP_ELEMINC()
{
return false;
}
bool TraceRecorder::JSOP_NAMEDEC()
{
return false;
}
bool TraceRecorder::JSOP_PROPDEC()
{
return false;
}
bool TraceRecorder::JSOP_ELEMDEC()
{
return false;
}
bool TraceRecorder::JSOP_GETPROP()
{
return false;
}
bool TraceRecorder::JSOP_SETPROP()
{
return false;
}
bool TraceRecorder::JSOP_GETELEM()
{
jsval& r = stackval(-1);
jsval& l = stackval(-2);
2008-07-05 13:44:48 -07:00
/* no guards for type checks, trace specialized this already */
if (!isInt(r) || JSVAL_IS_PRIMITIVE(l))
return false;
2008-07-05 13:44:48 -07:00
JSObject* obj = JSVAL_TO_OBJECT(l);
LIns* obj_ins = get(&l);
/* make sure the object is actually a dense array */
LIns* dslots_ins = lir->insLoadi(obj_ins, offsetof(JSObject, dslots));
if (!guardThatObjectIsDenseArray(obj, obj_ins, dslots_ins))
return false;
/* check that the index is within bounds */
jsint idx = asInt(r);
LIns* idx_ins = get(&r);
if (!guardDenseArrayIndexWithinBounds(obj, idx, obj_ins, dslots_ins, idx_ins))
return false;
jsval v = obj->dslots[idx];
/* ok, we can trace this case since we now have the value and thus know the type */
LIns* addr = lir->ins2(LIR_add, dslots_ins,
lir->ins2i(LIR_lsh, idx_ins, sizeof(jsval) == 4 ? 2 : 3));
/* load the value, check the type (need to check JSVAL_HOLE only for booleans) */
LIns* v_ins = lir->insLoadi(addr, 0);
if (!unbox_jsval(v, v_ins))
return false;
2008-07-05 13:44:48 -07:00
set(&l, v_ins);
return true;
}
bool TraceRecorder::JSOP_SETELEM()
{
jsval& v = stackval(-1);
jsval& r = stackval(-2);
jsval& l = stackval(-3);
/* no guards for type checks, trace specialized this already */
if (!isInt(r) || JSVAL_IS_PRIMITIVE(l))
return false;
JSObject* obj = JSVAL_TO_OBJECT(l);
LIns* obj_ins = get(&l);
/* make sure the object is actually a dense array */
LIns* dslots_ins = lir->insLoadi(obj_ins, offsetof(JSObject, dslots));
if (!guardThatObjectIsDenseArray(obj, obj_ins, dslots_ins))
return false;
/* check that the index is within bounds */
jsint idx = asInt(r);
LIns* idx_ins = get(&r);
if (!guardDenseArrayIndexWithinBounds(obj, idx, obj_ins, dslots_ins, idx_ins))
return false;
/* get us the address of the array slot */
LIns* addr = lir->ins2(LIR_add, dslots_ins,
lir->ins2i(LIR_lsh, idx_ins, JS_BYTES_PER_WORD_LOG2));
LIns* oldval = lir->insLoad(LIR_ld, addr, 0);
LIns* isHole = lir->ins2(LIR_eq, oldval, lir->insImmPtr((void*)JSVAL_HOLE));
LIns* count = lir->insLoadi(obj_ins,
offsetof(JSObject, fslots[JSSLOT_ARRAY_COUNT]));
lir->insStorei(lir->ins2(LIR_add, count, isHole), obj_ins,
offsetof(JSObject, fslots[JSSLOT_ARRAY_COUNT]));
/* ok, box the value we are storing, store it and we are done */
LIns* v_ins = get(&v);
LIns* boxed_ins;
if (isInt(v))
boxed_ins = int32_to_jsval(v_ins);
else if (isDouble(v))
boxed_ins = double_to_jsval(v_ins);
else if (isTrueOrFalse(v))
boxed_ins = boolean_to_jsval(v_ins);
else
return false; /* don't know how to box this type */
lir->insStorei(boxed_ins, addr, 0);
set(&l, v_ins);
return true;
}
bool TraceRecorder::JSOP_CALLNAME()
{
return false;
}
bool TraceRecorder::JSOP_CALL()
{
return false;
}
bool TraceRecorder::JSOP_NAME()
{
JSObject* obj;
JSObject* obj2;
JSPropCacheEntry* entry;
LIns* obj_ins = lir->insLoadi(lir->insLoadi(cx_ins, offsetof(JSContext, fp)),
offsetof(JSStackFrame, scopeChain));
obj = cx->fp->scopeChain;
if (!test_property_cache(obj, obj_ins, obj2, entry))
return false;
if (!PCVAL_IS_SLOT(entry->vword))
return false;
LIns* dslots_ins = NULL;
uint32 slot = PCVAL_TO_SLOT(entry->vword);
LIns* v_ins = stobj_get_slot(obj_ins, slot, dslots_ins);
if (!unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins))
return false;
stack(0, v_ins);
return true;
}
bool TraceRecorder::JSOP_DOUBLE()
{
jsval v = (jsval)cx->fp->script->atomMap.vector[GET_INDEX(cx->fp->regs->pc)];
if (isInt(v))
stack(0, lir->insImm(asInt(v)));
else
stack(0, lir->insImmq(*(uint64_t*)JSVAL_TO_DOUBLE(v)));
return true;
}
bool TraceRecorder::JSOP_STRING()
{
return false;
}
bool TraceRecorder::JSOP_ZERO()
{
2008-07-05 06:15:33 -07:00
stack(0, lir->insImm(0));
return true;
}
bool TraceRecorder::JSOP_ONE()
{
2008-07-05 06:15:33 -07:00
stack(0, lir->insImm(1));
return true;
}
bool TraceRecorder::JSOP_NULL()
{
stack(0, lir->insImmPtr(NULL));
2008-07-05 06:15:33 -07:00
return true;
}
bool TraceRecorder::JSOP_THIS()
{
return false;
}
bool TraceRecorder::JSOP_FALSE()
{
2008-07-05 06:15:33 -07:00
stack(0, lir->insImm(0));
return true;
}
bool TraceRecorder::JSOP_TRUE()
{
2008-07-05 06:15:33 -07:00
stack(0, lir->insImm(1));
return true;
}
bool TraceRecorder::JSOP_OR()
{
return bbinary(LIR_or);
}
bool TraceRecorder::JSOP_AND()
{
return bbinary(LIR_and);
}
bool TraceRecorder::JSOP_TABLESWITCH()
{
return false;
}
bool TraceRecorder::JSOP_LOOKUPSWITCH()
{
return false;
}
bool TraceRecorder::JSOP_STRICTEQ()
{
return false;
}
bool TraceRecorder::JSOP_STRICTNE()
{
return false;
}
bool TraceRecorder::JSOP_CLOSURE()
{
return false;
}
bool TraceRecorder::JSOP_EXPORTALL()
{
return false;
}
bool TraceRecorder::JSOP_EXPORTNAME()
{
return false;
}
bool TraceRecorder::JSOP_IMPORTALL()
{
return false;
}
bool TraceRecorder::JSOP_IMPORTPROP()
{
return false;
}
bool TraceRecorder::JSOP_IMPORTELEM()
{
return false;
}
bool TraceRecorder::JSOP_OBJECT()
{
return false;
}
bool TraceRecorder::JSOP_POP()
{
2008-07-06 06:52:50 -07:00
return true;
}
bool TraceRecorder::JSOP_POS()
{
return false;
}
bool TraceRecorder::JSOP_TRAP()
{
return false;
}
bool TraceRecorder::JSOP_GETARG()
{
stack(0, arg(GET_ARGNO(cx->fp->regs->pc)));
return true;
}
bool TraceRecorder::JSOP_SETARG()
{
arg(GET_ARGNO(cx->fp->regs->pc), stack(-1));
return true;
}
bool TraceRecorder::JSOP_GETVAR()
{
stack(0, var(GET_VARNO(cx->fp->regs->pc)));
return true;
}
bool TraceRecorder::JSOP_SETVAR()
{
var(GET_VARNO(cx->fp->regs->pc), stack(-1));
return true;
}
bool TraceRecorder::JSOP_UINT16()
{
stack(0, lir->insImm((jsint) GET_UINT16(cx->fp->regs->pc)));
return true;
}
bool TraceRecorder::JSOP_NEWINIT()
{
return false;
}
bool TraceRecorder::JSOP_ENDINIT()
{
return false;
}
bool TraceRecorder::JSOP_INITPROP()
{
return false;
}
bool TraceRecorder::JSOP_INITELEM()
{
return false;
}
bool TraceRecorder::JSOP_DEFSHARP()
{
return false;
}
bool TraceRecorder::JSOP_USESHARP()
{
return false;
}
bool TraceRecorder::JSOP_INCARG()
{
return inc(argval(GET_ARGNO(cx->fp->regs->pc)), 1, true);
}
bool TraceRecorder::JSOP_INCVAR()
{
return inc(varval(GET_VARNO(cx->fp->regs->pc)), 1, true);
}
bool TraceRecorder::JSOP_DECARG()
{
return inc(argval(GET_ARGNO(cx->fp->regs->pc)), -1, true);
}
bool TraceRecorder::JSOP_DECVAR()
{
return inc(varval(GET_VARNO(cx->fp->regs->pc)), -1, true);
}
bool TraceRecorder::JSOP_ARGINC()
{
return inc(argval(GET_ARGNO(cx->fp->regs->pc)), 1, false);
}
bool TraceRecorder::JSOP_VARINC()
{
return inc(varval(GET_VARNO(cx->fp->regs->pc)), 1, false);
}
bool TraceRecorder::JSOP_ARGDEC()
{
return inc(argval(GET_ARGNO(cx->fp->regs->pc)), -1, false);
}
bool TraceRecorder::JSOP_VARDEC()
{
return inc(varval(GET_VARNO(cx->fp->regs->pc)), -1, false);
}
bool TraceRecorder::JSOP_ITER()
{
return false;
}
bool TraceRecorder::JSOP_FORNAME()
{
return false;
}
bool TraceRecorder::JSOP_FORPROP()
{
return false;
}
bool TraceRecorder::JSOP_FORELEM()
{
return false;
}
bool TraceRecorder::JSOP_POPN()
{
2008-07-06 06:52:50 -07:00
return true;
}
bool TraceRecorder::JSOP_BINDNAME()
{
/* BINDNAME is a no-op for the recorder. We wait until we hit the
SETNAME/SETPROP that uses it. This is safe because the
interpreter calculates here the scope we will use and we
will use that value to guard against in SETNAME/SETPROP. */
return true;
}
bool TraceRecorder::JSOP_SETNAME()
{
return false;
}
bool TraceRecorder::JSOP_THROW()
{
return false;
}
bool TraceRecorder::JSOP_IN()
{
return false;
}
bool TraceRecorder::JSOP_INSTANCEOF()
{
return false;
}
bool TraceRecorder::JSOP_DEBUGGER()
{
return false;
}
bool TraceRecorder::JSOP_GOSUB()
{
return false;
}
bool TraceRecorder::JSOP_RETSUB()
{
return false;
}
bool TraceRecorder::JSOP_EXCEPTION()
{
return false;
}
bool TraceRecorder::JSOP_LINENO()
{
2008-07-06 06:52:50 -07:00
return true;
}
bool TraceRecorder::JSOP_CONDSWITCH()
{
2008-07-06 06:52:50 -07:00
return true;
}
bool TraceRecorder::JSOP_CASE()
{
return false;
}
bool TraceRecorder::JSOP_DEFAULT()
{
return false;
}
bool TraceRecorder::JSOP_EVAL()
{
return false;
}
bool TraceRecorder::JSOP_ENUMELEM()
{
return false;
}
bool TraceRecorder::JSOP_GETTER()
{
return false;
}
bool TraceRecorder::JSOP_SETTER()
{
return false;
}
bool TraceRecorder::JSOP_DEFFUN()
{
return false;
}
bool TraceRecorder::JSOP_DEFCONST()
{
return false;
}
bool TraceRecorder::JSOP_DEFVAR()
{
return false;
}
bool TraceRecorder::JSOP_ANONFUNOBJ()
{
return false;
}
bool TraceRecorder::JSOP_NAMEDFUNOBJ()
{
return false;
}
bool TraceRecorder::JSOP_SETLOCALPOP()
{
return false;
}
bool TraceRecorder::JSOP_GROUP()
{
2008-07-05 06:15:33 -07:00
return true; // no-op
}
bool TraceRecorder::JSOP_SETCALL()
{
return false;
}
bool TraceRecorder::JSOP_TRY()
{
2008-07-06 06:52:50 -07:00
return true;
}
bool TraceRecorder::JSOP_FINALLY()
{
2008-07-06 06:52:50 -07:00
return true;
}
bool TraceRecorder::JSOP_NOP()
{
2008-07-06 06:52:50 -07:00
return true;
}
bool TraceRecorder::JSOP_ARGSUB()
{
return false;
}
bool TraceRecorder::JSOP_ARGCNT()
{
return false;
}
bool TraceRecorder::JSOP_DEFLOCALFUN()
{
return false;
}
bool TraceRecorder::JSOP_GOTOX()
{
return false;
}
bool TraceRecorder::JSOP_IFEQX()
{
2008-07-06 06:52:50 -07:00
return JSOP_IFEQ();
}
bool TraceRecorder::JSOP_IFNEX()
{
2008-07-06 06:52:50 -07:00
return JSOP_IFNE();
}
bool TraceRecorder::JSOP_ORX()
{
2008-07-06 06:52:50 -07:00
return JSOP_OR();
}
bool TraceRecorder::JSOP_ANDX()
{
2008-07-06 06:52:50 -07:00
return JSOP_AND();
}
bool TraceRecorder::JSOP_GOSUBX()
{
2008-07-06 06:52:50 -07:00
return JSOP_GOSUB();
}
bool TraceRecorder::JSOP_CASEX()
{
2008-07-06 06:52:50 -07:00
return JSOP_CASE();
}
bool TraceRecorder::JSOP_DEFAULTX()
{
2008-07-06 06:52:50 -07:00
return JSOP_DEFAULT();
}
bool TraceRecorder::JSOP_TABLESWITCHX()
{
2008-07-06 06:52:50 -07:00
return JSOP_TABLESWITCH();
}
bool TraceRecorder::JSOP_LOOKUPSWITCHX()
{
2008-07-06 06:52:50 -07:00
return JSOP_LOOKUPSWITCH();
}
bool TraceRecorder::JSOP_BACKPATCH()
{
2008-07-06 06:52:50 -07:00
return true;
}
bool TraceRecorder::JSOP_BACKPATCH_POP()
{
2008-07-06 06:52:50 -07:00
return true;
}
bool TraceRecorder::JSOP_THROWING()
{
return false;
}
bool TraceRecorder::JSOP_SETRVAL()
{
return false;
}
bool TraceRecorder::JSOP_RETRVAL()
{
return false;
}
bool TraceRecorder::JSOP_GETGVAR()
{
return false;
}
bool TraceRecorder::JSOP_SETGVAR()
{
return false;
}
bool TraceRecorder::JSOP_INCGVAR()
{
return false;
}
bool TraceRecorder::JSOP_DECGVAR()
{
return false;
}
bool TraceRecorder::JSOP_GVARINC()
{
return false;
}
bool TraceRecorder::JSOP_GVARDEC()
{
return false;
}
bool TraceRecorder::JSOP_REGEXP()
{
return false;
}
bool TraceRecorder::JSOP_DEFXMLNS()
{
return false;
}
bool TraceRecorder::JSOP_ANYNAME()
{
return false;
}
bool TraceRecorder::JSOP_QNAMEPART()
{
return false;
}
bool TraceRecorder::JSOP_QNAMECONST()
{
return false;
}
bool TraceRecorder::JSOP_QNAME()
{
return false;
}
bool TraceRecorder::JSOP_TOATTRNAME()
{
return false;
}
bool TraceRecorder::JSOP_TOATTRVAL()
{
return false;
}
bool TraceRecorder::JSOP_ADDATTRNAME()
{
return false;
}
bool TraceRecorder::JSOP_ADDATTRVAL()
{
return false;
}
bool TraceRecorder::JSOP_BINDXMLNAME()
{
return false;
}
bool TraceRecorder::JSOP_SETXMLNAME()
{
return false;
}
bool TraceRecorder::JSOP_XMLNAME()
{
return false;
}
bool TraceRecorder::JSOP_DESCENDANTS()
{
return false;
}
bool TraceRecorder::JSOP_FILTER()
{
return false;
}
bool TraceRecorder::JSOP_ENDFILTER()
{
return false;
}
bool TraceRecorder::JSOP_TOXML()
{
return false;
}
bool TraceRecorder::JSOP_TOXMLLIST()
{
return false;
}
bool TraceRecorder::JSOP_XMLTAGEXPR()
{
return false;
}
bool TraceRecorder::JSOP_XMLELTEXPR()
{
return false;
}
bool TraceRecorder::JSOP_XMLOBJECT()
{
return false;
}
bool TraceRecorder::JSOP_XMLCDATA()
{
return false;
}
bool TraceRecorder::JSOP_XMLCOMMENT()
{
return false;
}
bool TraceRecorder::JSOP_XMLPI()
{
return false;
}
bool TraceRecorder::JSOP_CALLPROP()
{
return false;
}
bool TraceRecorder::JSOP_GETFUNNS()
{
return false;
}
bool TraceRecorder::JSOP_UNUSED186()
{
return false;
}
bool TraceRecorder::JSOP_DELDESC()
{
return false;
}
bool TraceRecorder::JSOP_UINT24()
{
stack(0, lir->insImm((jsint) GET_UINT24(cx->fp->regs->pc)));
return true;
}
bool TraceRecorder::JSOP_INDEXBASE()
{
return false;
}
bool TraceRecorder::JSOP_RESETBASE()
{
return false;
}
bool TraceRecorder::JSOP_RESETBASE0()
{
return false;
}
bool TraceRecorder::JSOP_STARTXML()
{
return false;
}
bool TraceRecorder::JSOP_STARTXMLEXPR()
{
return false;
}
bool TraceRecorder::JSOP_CALLELEM()
{
return false;
}
bool TraceRecorder::JSOP_STOP()
{
2008-07-06 06:52:50 -07:00
return true;
}
bool TraceRecorder::JSOP_GETXPROP()
{
return false;
}
bool TraceRecorder::JSOP_CALLXMLNAME()
{
return false;
}
bool TraceRecorder::JSOP_TYPEOFEXPR()
{
return false;
}
bool TraceRecorder::JSOP_ENTERBLOCK()
{
return false;
}
bool TraceRecorder::JSOP_LEAVEBLOCK()
{
return false;
}
bool TraceRecorder::JSOP_GETLOCAL()
{
return false;
}
bool TraceRecorder::JSOP_SETLOCAL()
{
return false;
}
bool TraceRecorder::JSOP_INCLOCAL()
{
return false;
}
bool TraceRecorder::JSOP_DECLOCAL()
{
return false;
}
bool TraceRecorder::JSOP_LOCALINC()
{
return false;
}
bool TraceRecorder::JSOP_LOCALDEC()
{
return false;
}
bool TraceRecorder::JSOP_FORLOCAL()
{
return false;
}
bool TraceRecorder::JSOP_FORCONST()
{
return false;
}
bool TraceRecorder::JSOP_ENDITER()
{
return false;
}
bool TraceRecorder::JSOP_GENERATOR()
{
return false;
}
bool TraceRecorder::JSOP_YIELD()
{
return false;
}
bool TraceRecorder::JSOP_ARRAYPUSH()
{
return false;
}
bool TraceRecorder::JSOP_UNUSED213()
{
return false;
}
bool TraceRecorder::JSOP_ENUMCONSTELEM()
{
return false;
}
bool TraceRecorder::JSOP_LEAVEBLOCKEXPR()
{
return false;
}
bool TraceRecorder::JSOP_GETTHISPROP()
{
return false;
}
bool TraceRecorder::JSOP_GETARGPROP()
{
return false;
}
bool TraceRecorder::JSOP_GETVARPROP()
{
return false;
}
bool TraceRecorder::JSOP_GETLOCALPROP()
{
return false;
}
bool TraceRecorder::JSOP_INDEXBASE1()
{
return false;
}
bool TraceRecorder::JSOP_INDEXBASE2()
{
return false;
}
bool TraceRecorder::JSOP_INDEXBASE3()
{
return false;
}
bool TraceRecorder::JSOP_CALLGVAR()
{
return false;
}
bool TraceRecorder::JSOP_CALLVAR()
{
return false;
}
bool TraceRecorder::JSOP_CALLARG()
{
return false;
}
bool TraceRecorder::JSOP_CALLLOCAL()
{
return false;
}
bool TraceRecorder::JSOP_INT8()
{
stack(0, lir->insImm(GET_INT8(cx->fp->regs->pc)));
return true;
}
bool TraceRecorder::JSOP_INT32()
{
stack(0, lir->insImm(GET_INT32(cx->fp->regs->pc)));
return true;
}
bool TraceRecorder::JSOP_LENGTH()
{
return false;
}
bool TraceRecorder::JSOP_NEWARRAY()
{
return false;
}
bool TraceRecorder::JSOP_HOLE()
{
2008-07-06 06:52:50 -07:00
stack(0, lir->insImm(JSVAL_HOLE));
return true;
}