2008-09-02 22:29:23 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2008-08-11 16:01:21 -07:00
|
|
|
* vim: set ts=4 sw=4 et tw=99:
|
2008-05-28 19:07:25 -07:00
|
|
|
*
|
|
|
|
* ***** 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
|
2008-06-28 09:58:15 -07:00
|
|
|
* Brendan Eich <brendan@mozilla.org>
|
2008-05-28 19:07:25 -07:00
|
|
|
*
|
|
|
|
* Contributor(s):
|
2008-06-28 09:58:15 -07:00
|
|
|
* Andreas Gal <gal@mozilla.com>
|
2008-07-15 15:05:16 -07:00
|
|
|
* Mike Shaver <shaver@mozilla.org>
|
|
|
|
* David Anderson <danderson@mozilla.com>
|
2008-05-28 19:07:25 -07:00
|
|
|
*
|
|
|
|
* 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 ***** */
|
|
|
|
|
2008-07-15 10:26:15 -07:00
|
|
|
#include "jsstddef.h" // always first
|
2008-08-01 08:26:32 -07:00
|
|
|
#include "jsbit.h" // low-level (NSPR-based) headers next
|
|
|
|
#include "jsprf.h"
|
2008-07-15 10:26:15 -07:00
|
|
|
#include <math.h> // standard headers next
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#include <malloc.h>
|
|
|
|
#define alloca _alloca
|
|
|
|
#endif
|
2008-09-05 16:56:03 -07:00
|
|
|
#ifdef SOLARIS
|
|
|
|
#include <alloca.h>
|
|
|
|
#endif
|
2008-05-28 19:07:25 -07:00
|
|
|
|
2008-10-08 15:08:33 -07:00
|
|
|
#include "nanojit.h"
|
2008-07-15 10:26:15 -07:00
|
|
|
#include "jsarray.h" // higher-level library and API headers
|
2008-07-05 06:47:59 -07:00
|
|
|
#include "jsbool.h"
|
2008-07-03 22:08:13 -07:00
|
|
|
#include "jscntxt.h"
|
2008-08-03 22:55:28 -07:00
|
|
|
#include "jsdbgapi.h"
|
|
|
|
#include "jsemit.h"
|
2008-07-11 17:59:10 -07:00
|
|
|
#include "jsfun.h"
|
2008-07-04 23:53:29 -07:00
|
|
|
#include "jsinterp.h"
|
2008-07-30 16:32:33 -07:00
|
|
|
#include "jsiter.h"
|
2008-07-15 10:26:15 -07:00
|
|
|
#include "jsobj.h"
|
2008-07-28 23:47:20 -07:00
|
|
|
#include "jsopcode.h"
|
2008-08-12 18:52:28 -07:00
|
|
|
#include "jsregexp.h"
|
2008-07-04 23:53:29 -07:00
|
|
|
#include "jsscope.h"
|
2008-08-03 22:55:28 -07:00
|
|
|
#include "jsscript.h"
|
2008-09-26 11:37:50 -07:00
|
|
|
#include "jsdate.h"
|
2008-07-11 17:59:10 -07:00
|
|
|
#include "jstracer.h"
|
2008-06-21 14:55:32 -07:00
|
|
|
|
2008-07-15 10:26:15 -07:00
|
|
|
#include "jsautooplen.h" // generated headers last
|
2008-07-10 18:37:49 -07:00
|
|
|
|
2008-09-26 19:20:52 -07:00
|
|
|
/* Never use JSVAL_IS_BOOLEAN because it restricts the value (true, false) and
|
|
|
|
the type. What you want to use is JSVAL_TAG(x) == JSVAL_BOOLEAN and then
|
|
|
|
handle the undefined case properly (bug 457363). */
|
|
|
|
#undef JSVAL_IS_BOOLEAN
|
|
|
|
#define JSVAL_IS_BOOLEAN(x) JS_STATIC_ASSERT(0)
|
|
|
|
|
2008-10-06 16:22:57 -07:00
|
|
|
/* Use a fake tag to represent boxed values, borrowing from the integer tag
|
|
|
|
range since we only use JSVAL_INT to indicate integers. */
|
|
|
|
#define JSVAL_BOXED 3
|
|
|
|
|
|
|
|
/* Map to translate a type tag into a printable representation. */
|
|
|
|
static const char typeChar[] = "OIDVS?B?";
|
|
|
|
|
2008-09-19 18:58:00 -07:00
|
|
|
/* Number of iterations of a loop where we start tracing. That is, we don't
|
2008-09-19 19:47:58 -07:00
|
|
|
start tracing until the beginning of the HOTLOOP-th iteration. */
|
2008-07-24 16:25:18 -07:00
|
|
|
#define HOTLOOP 2
|
|
|
|
|
|
|
|
/* Number of times we wait to exit on a side exit before we try to extend the tree. */
|
2008-08-20 23:35:00 -07:00
|
|
|
#define HOTEXIT 1
|
2008-07-24 16:25:18 -07:00
|
|
|
|
2008-08-11 15:16:34 -07:00
|
|
|
/* Max call depths for inlining. */
|
2008-09-05 18:57:32 -07:00
|
|
|
#define MAX_CALLDEPTH 10
|
2008-07-27 16:28:09 -07:00
|
|
|
|
2008-08-11 15:16:34 -07:00
|
|
|
/* Max number of type mismatchs before we trash the tree. */
|
|
|
|
#define MAX_MISMATCH 5
|
|
|
|
|
2008-08-12 21:39:44 -07:00
|
|
|
/* Max native stack size. */
|
|
|
|
#define MAX_NATIVE_STACK_SLOTS 1024
|
|
|
|
|
2008-08-13 13:51:59 -07:00
|
|
|
/* Max call stack size. */
|
|
|
|
#define MAX_CALL_STACK_ENTRIES 64
|
|
|
|
|
2008-09-02 23:52:11 -07:00
|
|
|
/* Max number of branches per tree. */
|
|
|
|
#define MAX_BRANCHES 16
|
|
|
|
|
2008-07-11 17:59:10 -07:00
|
|
|
#ifdef DEBUG
|
2008-08-20 10:15:42 -07:00
|
|
|
#define ABORT_TRACE(msg) do { debug_only_v(fprintf(stdout, "abort: %d: %s\n", __LINE__, msg);) return false; } while (0)
|
2008-07-11 17:59:10 -07:00
|
|
|
#else
|
|
|
|
#define ABORT_TRACE(msg) return false
|
|
|
|
#endif
|
|
|
|
|
2008-07-17 10:22:40 -07:00
|
|
|
#ifdef DEBUG
|
2008-09-19 19:47:58 -07:00
|
|
|
struct __jitstats {
|
|
|
|
#define JITSTAT(x) uint64 x;
|
|
|
|
#include "jitstats.tbl"
|
|
|
|
#undef JITSTAT
|
2008-09-23 14:35:29 -07:00
|
|
|
} jitstats = { 0LL, };
|
2008-09-19 19:47:58 -07:00
|
|
|
|
2008-09-23 14:35:29 -07:00
|
|
|
JS_STATIC_ASSERT(sizeof(jitstats) % sizeof(uint64) == 0);
|
2008-09-19 19:47:58 -07:00
|
|
|
|
|
|
|
enum jitstat_ids {
|
|
|
|
#define JITSTAT(x) STAT ## x ## ID,
|
|
|
|
#include "jitstats.tbl"
|
|
|
|
#undef JITSTAT
|
2008-09-22 11:23:22 -07:00
|
|
|
STAT_IDS_TOTAL
|
2008-09-19 19:47:58 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static JSPropertySpec jitstats_props[] = {
|
|
|
|
#define JITSTAT(x) { #x, STAT ## x ## ID, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT },
|
|
|
|
#include "jitstats.tbl"
|
|
|
|
#undef JITSTAT
|
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
jitstats_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|
|
|
{
|
|
|
|
int index = -1;
|
|
|
|
|
|
|
|
if (JSVAL_IS_STRING(id)) {
|
|
|
|
JSString* str = JSVAL_TO_STRING(id);
|
|
|
|
if (strcmp(JS_GetStringBytes(str), "HOTLOOP") == 0) {
|
|
|
|
*vp = INT_TO_JSVAL(HOTLOOP);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (JSVAL_IS_INT(id))
|
|
|
|
index = JSVAL_TO_INT(id);
|
|
|
|
|
|
|
|
uint64 result = 0;
|
|
|
|
switch (index) {
|
2008-09-23 14:35:29 -07:00
|
|
|
#define JITSTAT(x) case STAT ## x ## ID: result = jitstats.x; break;
|
2008-09-19 19:47:58 -07:00
|
|
|
#include "jitstats.tbl"
|
|
|
|
#undef JITSTAT
|
|
|
|
default:
|
|
|
|
*vp = JSVAL_VOID;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result < JSVAL_INT_MAX) {
|
|
|
|
*vp = INT_TO_JSVAL(result);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
char retstr[64];
|
2008-09-23 00:58:11 -07:00
|
|
|
JS_snprintf(retstr, sizeof retstr, "%llu", result);
|
2008-09-19 19:47:58 -07:00
|
|
|
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, retstr));
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSClass jitstats_class = {
|
|
|
|
"jitstats",
|
|
|
|
JSCLASS_HAS_PRIVATE,
|
|
|
|
JS_PropertyStub, JS_PropertyStub,
|
|
|
|
jitstats_getProperty, JS_PropertyStub,
|
|
|
|
JS_EnumerateStub, JS_ResolveStub,
|
|
|
|
JS_ConvertStub, JS_FinalizeStub,
|
|
|
|
JSCLASS_NO_OPTIONAL_MEMBERS
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
js_InitJITStatsClass(JSContext *cx, JSObject *glob)
|
|
|
|
{
|
|
|
|
JS_InitClass(cx, glob, NULL, &jitstats_class, NULL, 0, jitstats_props, NULL, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
2008-09-23 14:35:29 -07:00
|
|
|
#define AUDIT(x) (jitstats.x++)
|
2008-07-17 10:22:40 -07:00
|
|
|
#else
|
|
|
|
#define AUDIT(x) ((void)0)
|
2008-08-18 15:32:17 -07:00
|
|
|
#endif
|
2008-07-17 10:22:40 -07:00
|
|
|
|
2008-08-11 18:41:06 -07:00
|
|
|
#define INS_CONST(c) addName(lir->insImm(c), #c)
|
|
|
|
#define INS_CONSTPTR(p) addName(lir->insImmPtr((void*) (p)), #p)
|
2008-07-29 07:32:18 -07:00
|
|
|
|
2008-07-03 23:57:57 -07:00
|
|
|
using namespace avmplus;
|
2008-06-21 14:55:32 -07:00
|
|
|
using namespace nanojit;
|
|
|
|
|
2008-07-03 23:57:57 -07:00
|
|
|
static GC gc = GC();
|
2008-08-11 16:01:21 -07:00
|
|
|
static avmplus::AvmCore s_core = avmplus::AvmCore();
|
|
|
|
static avmplus::AvmCore* core = &s_core;
|
2008-05-30 10:11:56 -07:00
|
|
|
|
2008-08-08 15:26:31 -07:00
|
|
|
/* We really need a better way to configure the JIT. Shaver, where is my fancy JIT object? */
|
2008-08-15 10:21:07 -07:00
|
|
|
static bool nesting_enabled = true;
|
2008-08-20 16:10:56 -07:00
|
|
|
static bool oracle_enabled = true;
|
2008-09-04 10:58:35 -07:00
|
|
|
#if defined(NANOJIT_IA32)
|
2008-08-22 12:21:27 -07:00
|
|
|
static bool did_we_check_sse2 = false;
|
2008-09-04 10:58:35 -07:00
|
|
|
#endif
|
2008-08-08 15:26:31 -07:00
|
|
|
|
2008-09-02 22:29:23 -07:00
|
|
|
#if defined(DEBUG) || defined(INCLUDE_VERBOSE_OUTPUT)
|
2008-08-20 10:15:42 -07:00
|
|
|
static bool verbose_debug = getenv("TRACEMONKEY") && strstr(getenv("TRACEMONKEY"), "verbose");
|
|
|
|
#define debug_only_v(x) if (verbose_debug) { x; }
|
|
|
|
#else
|
|
|
|
#define debug_only_v(x)
|
|
|
|
#endif
|
|
|
|
|
2008-08-07 15:28:43 -07:00
|
|
|
/* The entire VM shares one oracle. Collisions and concurrent updates are tolerated and worst
|
|
|
|
case cause performance regressions. */
|
2008-09-10 12:20:01 -07:00
|
|
|
static Oracle oracle;
|
2008-08-07 15:28:43 -07:00
|
|
|
|
2008-07-10 18:42:04 -07:00
|
|
|
Tracker::Tracker()
|
2008-06-03 21:01:23 -07:00
|
|
|
{
|
2008-06-21 14:55:32 -07:00
|
|
|
pagelist = 0;
|
|
|
|
}
|
2008-06-10 21:40:47 -07:00
|
|
|
|
2008-07-10 18:42:04 -07:00
|
|
|
Tracker::~Tracker()
|
2008-06-21 14:55:32 -07:00
|
|
|
{
|
|
|
|
clear();
|
2008-06-03 21:01:23 -07:00
|
|
|
}
|
|
|
|
|
2008-07-10 18:42:04 -07:00
|
|
|
jsuword
|
|
|
|
Tracker::getPageBase(const void* v) const
|
2008-06-03 21:01:23 -07:00
|
|
|
{
|
2008-07-05 11:18:26 -07:00
|
|
|
return jsuword(v) & ~jsuword(NJ_PAGE_SIZE-1);
|
2008-06-04 00:09:57 -07:00
|
|
|
}
|
2008-06-11 11:29:35 -07:00
|
|
|
|
2008-07-10 18:42:04 -07:00
|
|
|
struct Tracker::Page*
|
|
|
|
Tracker::findPage(const void* v) const
|
2008-06-11 11:29:35 -07:00
|
|
|
{
|
2008-07-05 11:18:26 -07:00
|
|
|
jsuword base = getPageBase(v);
|
2008-07-10 18:42:04 -07:00
|
|
|
struct Tracker::Page* p = pagelist;
|
2008-06-21 14:55:32 -07:00
|
|
|
while (p) {
|
2008-06-29 13:53:40 -07:00
|
|
|
if (p->base == base) {
|
2008-06-21 14:55:32 -07:00
|
|
|
return p;
|
2008-06-29 13:53:40 -07:00
|
|
|
}
|
2008-06-21 14:55:32 -07:00
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-07-10 18:42:04 -07:00
|
|
|
struct Tracker::Page*
|
|
|
|
Tracker::addPage(const void* v) {
|
2008-07-05 11:18:26 -07:00
|
|
|
jsuword base = getPageBase(v);
|
2008-06-26 20:44:23 -07:00
|
|
|
struct Tracker::Page* p = (struct Tracker::Page*)
|
2008-07-10 18:42:04 -07:00
|
|
|
GC::Alloc(sizeof(*p) - sizeof(p->map) + (NJ_PAGE_SIZE >> 2) * sizeof(LIns*));
|
2008-06-21 14:55:32 -07:00
|
|
|
p->base = base;
|
|
|
|
p->next = pagelist;
|
|
|
|
pagelist = p;
|
|
|
|
return p;
|
2008-06-11 11:29:35 -07:00
|
|
|
}
|
|
|
|
|
2008-07-10 18:42:04 -07:00
|
|
|
void
|
|
|
|
Tracker::clear()
|
2008-06-11 11:29:35 -07:00
|
|
|
{
|
2008-06-21 14:55:32 -07:00
|
|
|
while (pagelist) {
|
|
|
|
Page* p = pagelist;
|
|
|
|
pagelist = pagelist->next;
|
|
|
|
GC::Free(p);
|
|
|
|
}
|
2008-06-11 11:29:35 -07:00
|
|
|
}
|
2008-06-11 17:59:28 -07:00
|
|
|
|
2008-07-20 14:23:39 -07:00
|
|
|
bool
|
|
|
|
Tracker::has(const void *v) const
|
|
|
|
{
|
2008-07-23 02:57:56 -07:00
|
|
|
return get(v) != NULL;
|
2008-07-20 14:23:39 -07:00
|
|
|
}
|
|
|
|
|
2008-08-18 12:54:04 -07:00
|
|
|
#if defined NANOJIT_64BIT
|
|
|
|
#define PAGEMASK 0x7ff
|
|
|
|
#else
|
|
|
|
#define PAGEMASK 0xfff
|
|
|
|
#endif
|
|
|
|
|
2008-07-15 10:26:15 -07:00
|
|
|
LIns*
|
2008-07-10 18:42:04 -07:00
|
|
|
Tracker::get(const void* v) const
|
2008-06-21 14:55:32 -07:00
|
|
|
{
|
2008-07-10 18:42:04 -07:00
|
|
|
struct Tracker::Page* p = findPage(v);
|
2008-07-23 02:57:56 -07:00
|
|
|
if (!p)
|
|
|
|
return NULL;
|
2008-08-18 12:54:04 -07:00
|
|
|
return p->map[(jsuword(v) & PAGEMASK) >> 2];
|
2008-06-21 14:55:32 -07:00
|
|
|
}
|
|
|
|
|
2008-07-10 18:42:04 -07:00
|
|
|
void
|
|
|
|
Tracker::set(const void* v, LIns* i)
|
2008-06-11 20:22:00 -07:00
|
|
|
{
|
2008-07-10 18:42:04 -07:00
|
|
|
struct Tracker::Page* p = findPage(v);
|
2008-06-22 23:42:54 -07:00
|
|
|
if (!p)
|
2008-06-21 14:55:32 -07:00
|
|
|
p = addPage(v);
|
2008-08-18 12:54:04 -07:00
|
|
|
p->map[(jsuword(v) & PAGEMASK) >> 2] = i;
|
2008-06-11 20:22:00 -07:00
|
|
|
}
|
|
|
|
|
2008-07-06 11:48:41 -07:00
|
|
|
static inline bool isNumber(jsval v)
|
|
|
|
{
|
|
|
|
return JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(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);
|
|
|
|
}
|
|
|
|
|
2008-07-06 22:35:19 -07:00
|
|
|
static inline bool isInt32(jsval v)
|
|
|
|
{
|
|
|
|
if (!isNumber(v))
|
|
|
|
return false;
|
|
|
|
jsdouble d = asNumber(v);
|
2008-07-19 00:27:45 -07:00
|
|
|
jsint i;
|
|
|
|
return JSDOUBLE_IS_INT(d, i);
|
2008-07-06 22:35:19 -07:00
|
|
|
}
|
|
|
|
|
2008-09-18 13:31:26 -07:00
|
|
|
/* Return JSVAL_DOUBLE for all numbers (int and double) and the tag otherwise. */
|
|
|
|
static inline uint8 getPromotedType(jsval v)
|
|
|
|
{
|
|
|
|
return JSVAL_IS_INT(v) ? JSVAL_DOUBLE : JSVAL_TAG(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return JSVAL_INT for all whole numbers that fit into signed 32-bit and the tag otherwise. */
|
2008-07-30 04:17:22 -07:00
|
|
|
static inline uint8 getCoercedType(jsval v)
|
|
|
|
{
|
2008-08-29 16:04:54 -07:00
|
|
|
return isInt32(v) ? JSVAL_INT : (uint8) JSVAL_TAG(v);
|
2008-07-30 04:17:22 -07:00
|
|
|
}
|
|
|
|
|
2008-08-07 15:28:43 -07:00
|
|
|
/* Tell the oracle that a certain global variable should not be demoted. */
|
|
|
|
void
|
2008-08-07 17:43:13 -07:00
|
|
|
Oracle::markGlobalSlotUndemotable(JSScript* script, unsigned slot)
|
2008-08-07 15:28:43 -07:00
|
|
|
{
|
|
|
|
_dontDemote.set(&gc, (slot % ORACLE_SIZE));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Consult with the oracle whether we shouldn't demote a certain global variable. */
|
|
|
|
bool
|
2008-08-07 17:43:13 -07:00
|
|
|
Oracle::isGlobalSlotUndemotable(JSScript* script, unsigned slot) const
|
2008-08-07 15:28:43 -07:00
|
|
|
{
|
2008-08-20 16:10:56 -07:00
|
|
|
return !oracle_enabled || _dontDemote.get(slot % ORACLE_SIZE);
|
2008-08-07 15:28:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Tell the oracle that a certain slot at a certain bytecode location should not be demoted. */
|
2008-08-18 15:32:17 -07:00
|
|
|
void
|
2008-08-07 15:28:43 -07:00
|
|
|
Oracle::markStackSlotUndemotable(JSScript* script, jsbytecode* ip, unsigned slot)
|
|
|
|
{
|
2008-08-18 12:54:04 -07:00
|
|
|
uint32 hash = uint32(intptr_t(ip)) + (slot << 5);
|
2008-08-07 15:28:43 -07:00
|
|
|
hash %= ORACLE_SIZE;
|
|
|
|
_dontDemote.set(&gc, hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Consult with the oracle whether we shouldn't demote a certain slot. */
|
2008-08-18 15:32:17 -07:00
|
|
|
bool
|
2008-08-07 15:28:43 -07:00
|
|
|
Oracle::isStackSlotUndemotable(JSScript* script, jsbytecode* ip, unsigned slot) const
|
|
|
|
{
|
2008-08-18 12:54:04 -07:00
|
|
|
uint32 hash = uint32(intptr_t(ip)) + (slot << 5);
|
2008-08-07 15:28:43 -07:00
|
|
|
hash %= ORACLE_SIZE;
|
2008-08-20 16:10:56 -07:00
|
|
|
return !oracle_enabled || _dontDemote.get(hash);
|
2008-08-07 15:28:43 -07:00
|
|
|
}
|
|
|
|
|
2008-08-20 15:18:43 -07:00
|
|
|
/* Clear the oracle. */
|
|
|
|
void
|
|
|
|
Oracle::clear()
|
|
|
|
{
|
|
|
|
_dontDemote.reset();
|
|
|
|
}
|
|
|
|
|
2008-10-10 09:31:23 -07:00
|
|
|
#if defined(NJ_SOFTFLOAT)
|
2008-10-20 11:18:30 -07:00
|
|
|
JS_DEFINE_CALLINFO_1(static, DOUBLE, i2f, INT32, 1, 1)
|
|
|
|
JS_DEFINE_CALLINFO_1(static, DOUBLE, u2f, UINT32, 1, 1)
|
2008-10-10 09:31:23 -07:00
|
|
|
#endif
|
|
|
|
|
2008-09-02 22:29:23 -07:00
|
|
|
static bool isi2f(LInsp i)
|
|
|
|
{
|
|
|
|
if (i->isop(LIR_i2f))
|
|
|
|
return true;
|
|
|
|
|
2008-09-02 22:42:25 -07:00
|
|
|
#if defined(NJ_SOFTFLOAT)
|
2008-09-02 22:29:23 -07:00
|
|
|
if (i->isop(LIR_qjoin) &&
|
|
|
|
i->oprnd1()->isop(LIR_call) &&
|
|
|
|
i->oprnd2()->isop(LIR_callh))
|
|
|
|
{
|
2008-10-16 12:24:10 -07:00
|
|
|
if (i->oprnd1()->callInfo() == &i2f_ci)
|
2008-09-02 22:29:23 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isu2f(LInsp i)
|
|
|
|
{
|
|
|
|
if (i->isop(LIR_u2f))
|
|
|
|
return true;
|
|
|
|
|
2008-09-02 22:42:25 -07:00
|
|
|
#if defined(NJ_SOFTFLOAT)
|
2008-09-02 22:29:23 -07:00
|
|
|
if (i->isop(LIR_qjoin) &&
|
|
|
|
i->oprnd1()->isop(LIR_call) &&
|
|
|
|
i->oprnd2()->isop(LIR_callh))
|
|
|
|
{
|
2008-10-16 12:24:10 -07:00
|
|
|
if (i->oprnd1()->callInfo() == &u2f_ci)
|
2008-09-02 22:29:23 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static LInsp iu2fArg(LInsp i)
|
|
|
|
{
|
2008-09-02 22:42:25 -07:00
|
|
|
#if defined(NJ_SOFTFLOAT)
|
2008-09-02 22:29:23 -07:00
|
|
|
if (i->isop(LIR_qjoin))
|
|
|
|
return i->oprnd1()->arg(0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return i->oprnd1();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-05 22:20:35 -07:00
|
|
|
static LIns* demote(LirWriter *out, LInsp i)
|
|
|
|
{
|
|
|
|
if (i->isCall())
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
return callArgN(i, 0);
|
2008-09-02 22:29:23 -07:00
|
|
|
if (isi2f(i) || isu2f(i))
|
|
|
|
return iu2fArg(i);
|
2008-08-08 15:26:31 -07:00
|
|
|
if (i->isconst())
|
|
|
|
return i;
|
2008-07-05 22:20:35 -07:00
|
|
|
AvmAssert(i->isconstq());
|
|
|
|
double cf = i->constvalf();
|
|
|
|
int32_t ci = cf > 0x7fffffff ? uint32_t(cf) : int32_t(cf);
|
|
|
|
return out->insImm(ci);
|
|
|
|
}
|
|
|
|
|
2008-07-20 17:36:11 -07:00
|
|
|
static bool isPromoteInt(LIns* i)
|
2008-07-05 22:20:35 -07:00
|
|
|
{
|
2008-07-07 12:58:28 -07:00
|
|
|
jsdouble d;
|
2008-09-02 22:29:23 -07:00
|
|
|
return isi2f(i) || i->isconst() ||
|
2008-09-05 18:29:08 -07:00
|
|
|
(i->isconstq() && (d = i->constvalf()) == jsdouble(jsint(d)) && !JSDOUBLE_IS_NEGZERO(d));
|
2008-07-05 22:20:35 -07:00
|
|
|
}
|
|
|
|
|
2008-07-20 17:36:11 -07:00
|
|
|
static bool isPromoteUint(LIns* i)
|
2008-07-05 22:20:35 -07:00
|
|
|
{
|
2008-07-07 12:58:28 -07:00
|
|
|
jsdouble d;
|
2008-09-02 22:29:23 -07:00
|
|
|
return isu2f(i) || i->isconst() ||
|
2008-09-18 14:19:17 -07:00
|
|
|
(i->isconstq() && (d = i->constvalf()) == (jsdouble)(jsuint)d && !JSDOUBLE_IS_NEGZERO(d));
|
2008-07-05 22:20:35 -07:00
|
|
|
}
|
|
|
|
|
2008-07-20 17:36:11 -07:00
|
|
|
static bool isPromote(LIns* i)
|
2008-07-05 22:20:35 -07:00
|
|
|
{
|
2008-07-19 00:15:22 -07:00
|
|
|
return isPromoteInt(i) || isPromoteUint(i);
|
2008-07-05 22:20:35 -07:00
|
|
|
}
|
|
|
|
|
2008-07-21 19:37:43 -07:00
|
|
|
static bool isconst(LIns* i, int32_t c)
|
|
|
|
{
|
|
|
|
return i->isconst() && i->constval() == c;
|
|
|
|
}
|
|
|
|
|
2008-07-21 17:50:17 -07:00
|
|
|
static bool overflowSafe(LIns* i)
|
|
|
|
{
|
|
|
|
LIns* c;
|
|
|
|
return (i->isop(LIR_and) && ((c = i->oprnd2())->isconst()) &&
|
|
|
|
((c->constval() & 0xc0000000) == 0)) ||
|
|
|
|
(i->isop(LIR_rsh) && ((c = i->oprnd2())->isconst()) &&
|
|
|
|
((c->constval() > 0)));
|
|
|
|
}
|
|
|
|
|
2008-09-02 22:29:23 -07:00
|
|
|
#if defined(NJ_SOFTFLOAT)
|
2008-10-10 09:31:23 -07:00
|
|
|
/* soft float */
|
|
|
|
|
2008-10-20 11:18:30 -07:00
|
|
|
JS_DEFINE_CALLINFO_1(static, DOUBLE, fneg, DOUBLE, 1, 1)
|
|
|
|
JS_DEFINE_CALLINFO_2(static, INT32, fcmpeq, DOUBLE, DOUBLE, 1, 1)
|
|
|
|
JS_DEFINE_CALLINFO_2(static, INT32, fcmplt, DOUBLE, DOUBLE, 1, 1)
|
|
|
|
JS_DEFINE_CALLINFO_2(static, INT32, fcmple, DOUBLE, DOUBLE, 1, 1)
|
|
|
|
JS_DEFINE_CALLINFO_2(static, INT32, fcmpgt, DOUBLE, DOUBLE, 1, 1)
|
|
|
|
JS_DEFINE_CALLINFO_2(static, INT32, fcmpge, DOUBLE, DOUBLE, 1, 1)
|
|
|
|
JS_DEFINE_CALLINFO_2(static, DOUBLE, fmul, DOUBLE, DOUBLE, 1, 1)
|
|
|
|
JS_DEFINE_CALLINFO_2(static, DOUBLE, fadd, DOUBLE, DOUBLE, 1, 1)
|
|
|
|
JS_DEFINE_CALLINFO_2(static, DOUBLE, fdiv, DOUBLE, DOUBLE, 1, 1)
|
|
|
|
JS_DEFINE_CALLINFO_2(static, DOUBLE, fsub, DOUBLE, DOUBLE, 1, 1)
|
|
|
|
|
2008-10-10 09:31:23 -07:00
|
|
|
jsdouble FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
fneg(jsdouble x)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return -x;
|
|
|
|
}
|
|
|
|
|
|
|
|
jsdouble FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
i2f(int32 i)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
jsdouble FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
u2f(jsuint u)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return u;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
fcmpeq(jsdouble x, jsdouble y)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return x==y;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
fcmplt(jsdouble x, jsdouble y)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return x < y;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
fcmple(jsdouble x, jsdouble y)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return x <= y;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
fcmpgt(jsdouble x, jsdouble y)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return x > y;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
fcmpge(jsdouble x, jsdouble y)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return x >= y;
|
|
|
|
}
|
|
|
|
|
|
|
|
jsdouble FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
fmul(jsdouble x, jsdouble y)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return x * y;
|
|
|
|
}
|
|
|
|
|
|
|
|
jsdouble FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
fadd(jsdouble x, jsdouble y)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return x + y;
|
|
|
|
}
|
|
|
|
|
|
|
|
jsdouble FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
fdiv(jsdouble x, jsdouble y)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return x / y;
|
|
|
|
}
|
|
|
|
|
|
|
|
jsdouble FASTCALL
|
2008-10-20 11:18:30 -07:00
|
|
|
fsub(jsdouble x, jsdouble y)
|
2008-10-10 09:31:23 -07:00
|
|
|
{
|
|
|
|
return x - y;
|
|
|
|
}
|
|
|
|
|
2008-09-02 22:29:23 -07:00
|
|
|
class SoftFloatFilter: public LirWriter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SoftFloatFilter(LirWriter* out):
|
|
|
|
LirWriter(out)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2008-10-08 15:08:33 -07:00
|
|
|
LInsp quadCall(const CallInfo *ci, LInsp args[]) {
|
2008-09-02 22:29:23 -07:00
|
|
|
LInsp qlo, qhi;
|
|
|
|
|
2008-10-08 15:08:33 -07:00
|
|
|
qlo = out->insCall(ci, args);
|
2008-09-02 22:29:23 -07:00
|
|
|
qhi = out->ins1(LIR_callh, qlo);
|
|
|
|
return out->qjoin(qlo, qhi);
|
|
|
|
}
|
|
|
|
|
|
|
|
LInsp ins1(LOpcode v, LInsp s0)
|
|
|
|
{
|
|
|
|
if (v == LIR_fneg)
|
2008-10-16 12:24:10 -07:00
|
|
|
return quadCall(&fneg_ci, &s0);
|
2008-09-02 22:29:23 -07:00
|
|
|
|
|
|
|
if (v == LIR_i2f)
|
2008-10-16 12:24:10 -07:00
|
|
|
return quadCall(&i2f_ci, &s0);
|
2008-09-02 22:29:23 -07:00
|
|
|
|
|
|
|
if (v == LIR_u2f)
|
2008-10-16 12:24:10 -07:00
|
|
|
return quadCall(&u2f_ci, &s0);
|
2008-09-02 22:29:23 -07:00
|
|
|
|
|
|
|
return out->ins1(v, s0);
|
|
|
|
}
|
|
|
|
|
|
|
|
LInsp ins2(LOpcode v, LInsp s0, LInsp s1)
|
|
|
|
{
|
|
|
|
LInsp args[2];
|
|
|
|
LInsp bv;
|
|
|
|
|
|
|
|
// change the numeric value and order of these LIR opcodes and die
|
|
|
|
if (LIR_fadd <= v && v <= LIR_fdiv) {
|
2008-10-16 12:24:10 -07:00
|
|
|
static const CallInfo *fmap[] = { &fadd_ci, &fsub_ci, &fmul_ci, &fdiv_ci };
|
2008-09-02 22:29:23 -07:00
|
|
|
|
|
|
|
args[0] = s1;
|
|
|
|
args[1] = s0;
|
|
|
|
|
|
|
|
return quadCall(fmap[v - LIR_fadd], args);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (LIR_feq <= v && v <= LIR_fge) {
|
2008-10-16 12:24:10 -07:00
|
|
|
static const CallInfo *fmap[] = { &fcmpeq_ci, &fcmplt_ci, &fcmpgt_ci, &fcmple_ci, &fcmpge_ci };
|
2008-09-02 22:29:23 -07:00
|
|
|
|
|
|
|
args[0] = s1;
|
|
|
|
args[1] = s0;
|
|
|
|
|
|
|
|
bv = out->insCall(fmap[v - LIR_feq], args);
|
|
|
|
return out->ins2(LIR_eq, bv, out->insImm(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
return out->ins2(v, s0, s1);
|
|
|
|
}
|
|
|
|
|
2008-10-08 15:08:33 -07:00
|
|
|
LInsp insCall(const CallInfo *ci, LInsp args[])
|
2008-09-02 22:29:23 -07:00
|
|
|
{
|
|
|
|
// if the return type is ARGSIZE_F, we have
|
|
|
|
// to do a quadCall ( qjoin(call,callh) )
|
2008-10-08 15:08:33 -07:00
|
|
|
if ((ci->_argtypes & 3) == ARGSIZE_F)
|
|
|
|
return quadCall(ci, args);
|
2008-09-02 22:29:23 -07:00
|
|
|
|
2008-10-08 15:08:33 -07:00
|
|
|
return out->insCall(ci, args);
|
2008-09-02 22:29:23 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2008-09-02 22:29:23 -07:00
|
|
|
#endif // NJ_SOFTFLOAT
|
2008-09-02 22:29:23 -07:00
|
|
|
|
2008-07-05 22:20:35 -07:00
|
|
|
class FuncFilter: public LirWriter
|
|
|
|
{
|
2008-07-07 00:43:40 -07:00
|
|
|
TraceRecorder& recorder;
|
2008-07-05 22:20:35 -07:00
|
|
|
public:
|
2008-07-20 17:36:11 -07:00
|
|
|
FuncFilter(LirWriter* out, TraceRecorder& _recorder):
|
2008-07-07 00:43:40 -07:00
|
|
|
LirWriter(out), recorder(_recorder)
|
2008-07-05 22:20:35 -07:00
|
|
|
{
|
|
|
|
}
|
2008-07-07 02:21:04 -07:00
|
|
|
|
2008-07-21 17:59:42 -07:00
|
|
|
LInsp ins2(LOpcode v, LInsp s0, LInsp s1)
|
2008-07-05 22:20:35 -07:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2008-07-09 09:59:51 -07:00
|
|
|
} else if (LIR_feq <= v && v <= LIR_fge) {
|
2008-07-05 22:20:35 -07:00
|
|
|
if (isPromoteInt(s0) && isPromoteInt(s1)) {
|
|
|
|
// demote fcmp to cmp
|
|
|
|
v = LOpcode(v + (LIR_eq - LIR_feq));
|
2008-07-21 17:59:42 -07:00
|
|
|
return out->ins2(v, demote(out, s0), demote(out, s1));
|
2008-07-05 22:20:35 -07:00
|
|
|
} 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
|
2008-07-21 17:59:42 -07:00
|
|
|
return out->ins2(v, demote(out, s0), demote(out, s1));
|
2008-07-05 22:20:35 -07:00
|
|
|
}
|
2008-07-19 00:27:45 -07:00
|
|
|
} else if (v == LIR_fadd || v == LIR_fsub) {
|
|
|
|
/* demoting multiplication seems to be tricky since it can quickly overflow the
|
|
|
|
value range of int32 */
|
2008-07-07 00:43:40 -07:00
|
|
|
if (isPromoteInt(s0) && isPromoteInt(s1)) {
|
|
|
|
// demote fop to op
|
|
|
|
v = (LOpcode)((int)v & ~LIR64);
|
2008-07-21 17:50:17 -07:00
|
|
|
LIns* d0;
|
|
|
|
LIns* d1;
|
2008-07-21 17:59:42 -07:00
|
|
|
LIns* result = out->ins2(v, d0 = demote(out, s0), d1 = demote(out, s1));
|
2008-08-06 13:36:29 -07:00
|
|
|
if (!overflowSafe(d0) || !overflowSafe(d1)) {
|
2008-07-28 23:47:20 -07:00
|
|
|
out->insGuard(LIR_xt, out->ins1(LIR_ov, result),
|
2008-08-06 13:36:29 -07:00
|
|
|
recorder.snapshot(OVERFLOW_EXIT));
|
|
|
|
}
|
2008-07-07 00:43:40 -07:00
|
|
|
return out->ins1(LIR_i2f, result);
|
|
|
|
}
|
2008-07-25 16:23:12 -07:00
|
|
|
} else if (v == LIR_or &&
|
2008-07-21 19:37:43 -07:00
|
|
|
s0->isop(LIR_lsh) && isconst(s0->oprnd2(), 16) &&
|
|
|
|
s1->isop(LIR_and) && isconst(s1->oprnd2(), 0xffff)) {
|
|
|
|
LIns* msw = s0->oprnd1();
|
|
|
|
LIns* lsw = s1->oprnd1();
|
|
|
|
LIns* x;
|
|
|
|
LIns* y;
|
|
|
|
if (lsw->isop(LIR_add) &&
|
2008-07-25 16:23:12 -07:00
|
|
|
lsw->oprnd1()->isop(LIR_and) &&
|
2008-07-21 19:37:43 -07:00
|
|
|
lsw->oprnd2()->isop(LIR_and) &&
|
2008-07-25 16:23:12 -07:00
|
|
|
isconst(lsw->oprnd1()->oprnd2(), 0xffff) &&
|
2008-07-21 19:37:43 -07:00
|
|
|
isconst(lsw->oprnd2()->oprnd2(), 0xffff) &&
|
2008-07-25 16:23:12 -07:00
|
|
|
msw->isop(LIR_add) &&
|
|
|
|
msw->oprnd1()->isop(LIR_add) &&
|
2008-07-21 19:37:43 -07:00
|
|
|
msw->oprnd2()->isop(LIR_rsh) &&
|
|
|
|
msw->oprnd1()->oprnd1()->isop(LIR_rsh) &&
|
|
|
|
msw->oprnd1()->oprnd2()->isop(LIR_rsh) &&
|
|
|
|
isconst(msw->oprnd2()->oprnd2(), 16) &&
|
|
|
|
isconst(msw->oprnd1()->oprnd1()->oprnd2(), 16) &&
|
|
|
|
isconst(msw->oprnd1()->oprnd2()->oprnd2(), 16) &&
|
|
|
|
(x = lsw->oprnd1()->oprnd1()) == msw->oprnd1()->oprnd1()->oprnd1() &&
|
|
|
|
(y = lsw->oprnd2()->oprnd1()) == msw->oprnd1()->oprnd2()->oprnd1() &&
|
|
|
|
lsw == msw->oprnd2()->oprnd1()) {
|
|
|
|
return out->ins2(LIR_add, x, y);
|
|
|
|
}
|
2008-07-05 22:20:35 -07:00
|
|
|
}
|
2008-09-02 22:29:23 -07:00
|
|
|
#ifdef NANOJIT_ARM
|
|
|
|
else if (v == LIR_lsh ||
|
|
|
|
v == LIR_rsh ||
|
|
|
|
v == LIR_ush)
|
|
|
|
{
|
|
|
|
// needed on ARM -- arm doesn't mask shifts to 31 like x86 does
|
|
|
|
if (s1->isconst())
|
|
|
|
s1->setimm16(s1->constval() & 31);
|
|
|
|
else
|
|
|
|
s1 = out->ins2(LIR_and, s1, out->insImm(31));
|
|
|
|
return out->ins2(v, s0, s1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-07-21 17:59:42 -07:00
|
|
|
return out->ins2(v, s0, s1);
|
2008-07-05 22:20:35 -07:00
|
|
|
}
|
2008-07-19 00:15:22 -07:00
|
|
|
|
2008-10-08 15:08:33 -07:00
|
|
|
LInsp insCall(const CallInfo *ci, LInsp args[])
|
2008-07-05 22:20:35 -07:00
|
|
|
{
|
2008-07-06 20:27:50 -07:00
|
|
|
LInsp s0 = args[0];
|
2008-10-16 12:24:10 -07:00
|
|
|
if (ci == &js_DoubleToUint32_ci) {
|
2008-07-20 17:36:11 -07:00
|
|
|
if (s0->isconstq())
|
|
|
|
return out->insImm(js_DoubleToECMAUint32(s0->constvalf()));
|
2008-09-02 22:29:23 -07:00
|
|
|
if (isi2f(s0) || isu2f(s0))
|
|
|
|
return iu2fArg(s0);
|
2008-10-16 12:24:10 -07:00
|
|
|
} else if (ci == &js_DoubleToInt32_ci) {
|
2008-07-10 16:55:37 -07:00
|
|
|
if (s0->isconstq())
|
|
|
|
return out->insImm(js_DoubleToECMAInt32(s0->constvalf()));
|
2008-07-05 22:20:35 -07:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
2008-09-02 22:29:23 -07:00
|
|
|
if (isi2f(s0) || isu2f(s0))
|
|
|
|
return iu2fArg(s0);
|
2008-10-08 15:08:33 -07:00
|
|
|
// XXX ARM -- check for qjoin(call(UnboxDouble),call(UnboxDouble))
|
2008-10-16 12:24:10 -07:00
|
|
|
if (s0->isCall() && s0->callInfo() == &js_UnboxDouble_ci) {
|
2008-07-27 14:55:26 -07:00
|
|
|
LIns* args2[] = { callArgN(s0, 0) };
|
2008-10-16 12:24:10 -07:00
|
|
|
return out->insCall(&js_UnboxInt32_ci, args2);
|
2008-07-27 14:55:26 -07:00
|
|
|
}
|
2008-10-16 12:24:10 -07:00
|
|
|
if (s0->isCall() && s0->callInfo() == &js_StringToNumber_ci) {
|
2008-07-31 11:35:08 -07:00
|
|
|
// callArgN's ordering is that as seen by the builtin, not as stored in args here.
|
|
|
|
// True story!
|
|
|
|
LIns* args2[] = { callArgN(s0, 1), callArgN(s0, 0) };
|
2008-10-16 12:24:10 -07:00
|
|
|
return out->insCall(&js_StringToInt32_ci, args2);
|
2008-07-31 11:35:08 -07:00
|
|
|
}
|
2008-10-16 12:24:10 -07:00
|
|
|
} else if (ci == &js_BoxDouble_ci) {
|
2008-07-10 17:03:59 -07:00
|
|
|
JS_ASSERT(s0->isQuad());
|
2008-07-06 20:27:50 -07:00
|
|
|
if (s0->isop(LIR_i2f)) {
|
2008-07-10 17:03:59 -07:00
|
|
|
LIns* args2[] = { s0->oprnd1(), args[1] };
|
2008-10-16 12:24:10 -07:00
|
|
|
return out->insCall(&js_BoxInt32_ci, args2);
|
2008-07-06 16:38:54 -07:00
|
|
|
}
|
2008-10-16 12:24:10 -07:00
|
|
|
if (s0->isCall() && s0->callInfo() == &js_UnboxDouble_ci)
|
2008-08-20 23:19:48 -07:00
|
|
|
return callArgN(s0, 0);
|
2008-07-05 22:20:35 -07:00
|
|
|
}
|
2008-10-08 15:08:33 -07:00
|
|
|
return out->insCall(ci, args);
|
2008-07-05 22:20:35 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2008-07-08 16:29:23 -07:00
|
|
|
/* In debug mode vpname contains a textual description of the type of the
|
|
|
|
slot during the forall iteration over al slots. */
|
2008-09-02 22:29:23 -07:00
|
|
|
#if defined(DEBUG) || defined(INCLUDE_VERBOSE_OUTPUT)
|
2008-07-15 16:04:08 -07:00
|
|
|
#define DEF_VPNAME const char* vpname; unsigned vpnum
|
2008-07-08 16:29:23 -07:00
|
|
|
#define SET_VPNAME(name) do { vpname = name; vpnum = 0; } while(0)
|
|
|
|
#define INC_VPNUM() do { ++vpnum; } while(0)
|
|
|
|
#else
|
2008-07-08 22:40:07 -07:00
|
|
|
#define DEF_VPNAME do {} while (0)
|
|
|
|
#define vpname ""
|
|
|
|
#define vpnum 0
|
2008-07-08 16:29:23 -07:00
|
|
|
#define SET_VPNAME(name) ((void)0)
|
|
|
|
#define INC_VPNUM() ((void)0)
|
|
|
|
#endif
|
|
|
|
|
2008-08-01 22:27:39 -07:00
|
|
|
/* Iterate over all interned global variables. */
|
2008-07-23 23:18:02 -07:00
|
|
|
#define FORALL_GLOBAL_SLOTS(cx, ngslots, gslots, code) \
|
2008-07-08 16:29:23 -07:00
|
|
|
JS_BEGIN_MACRO \
|
2008-07-08 22:40:07 -07:00
|
|
|
DEF_VPNAME; \
|
2008-07-14 16:40:38 -07:00
|
|
|
JSObject* globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain); \
|
2008-07-08 16:29:23 -07:00
|
|
|
unsigned n; \
|
|
|
|
jsval* vp; \
|
2008-07-13 21:14:34 -07:00
|
|
|
SET_VPNAME("global"); \
|
2008-07-15 01:53:39 -07:00
|
|
|
for (n = 0; n < ngslots; ++n) { \
|
|
|
|
vp = &STOBJ_GET_SLOT(globalObj, gslots[n]); \
|
|
|
|
{ code; } \
|
|
|
|
INC_VPNUM(); \
|
2008-07-08 16:29:23 -07:00
|
|
|
} \
|
2008-07-23 23:18:02 -07:00
|
|
|
JS_END_MACRO
|
|
|
|
|
2008-08-01 22:27:39 -07:00
|
|
|
/* Iterate over all slots in the frame, consisting of args, vars, and stack
|
|
|
|
(except for the top-level frame which does not have args or vars. */
|
2008-08-03 22:55:28 -07:00
|
|
|
#define FORALL_FRAME_SLOTS(fp, depth, code) \
|
2008-08-01 22:27:39 -07:00
|
|
|
JS_BEGIN_MACRO \
|
|
|
|
jsval* vp; \
|
|
|
|
jsval* vpstop; \
|
2008-08-03 22:55:28 -07:00
|
|
|
if (fp->callee) { \
|
2008-08-01 22:27:39 -07:00
|
|
|
if (depth == 0) { \
|
2008-08-14 23:22:51 -07:00
|
|
|
SET_VPNAME("callee"); \
|
|
|
|
vp = &fp->argv[-2]; \
|
|
|
|
{ code; } \
|
2008-08-01 22:27:39 -07:00
|
|
|
SET_VPNAME("this"); \
|
2008-08-03 22:55:28 -07:00
|
|
|
vp = &fp->argv[-1]; \
|
2008-08-14 23:22:51 -07:00
|
|
|
{ code; } \
|
2008-08-01 22:27:39 -07:00
|
|
|
SET_VPNAME("argv"); \
|
2008-08-25 16:04:10 -07:00
|
|
|
vp = &fp->argv[0]; vpstop = &fp->argv[fp->fun->nargs]; \
|
2008-08-01 22:27:39 -07:00
|
|
|
while (vp < vpstop) { code; ++vp; INC_VPNUM(); } \
|
|
|
|
} \
|
|
|
|
SET_VPNAME("vars"); \
|
2008-08-03 22:55:28 -07:00
|
|
|
vp = fp->slots; vpstop = &fp->slots[fp->script->nfixed]; \
|
2008-08-01 22:27:39 -07:00
|
|
|
while (vp < vpstop) { code; ++vp; INC_VPNUM(); } \
|
|
|
|
} \
|
|
|
|
SET_VPNAME("stack"); \
|
2008-08-03 22:55:28 -07:00
|
|
|
vp = StackBase(fp); vpstop = fp->regs->sp; \
|
2008-08-01 22:27:39 -07:00
|
|
|
while (vp < vpstop) { code; ++vp; INC_VPNUM(); } \
|
2008-08-12 23:21:52 -07:00
|
|
|
if (fsp < fspstop - 1) { \
|
|
|
|
JSStackFrame* fp2 = fsp[1]; \
|
|
|
|
int missing = fp2->fun->nargs - fp2->argc; \
|
|
|
|
if (missing > 0) { \
|
|
|
|
SET_VPNAME("missing"); \
|
|
|
|
vp = fp->regs->sp; \
|
|
|
|
vpstop = vp + missing; \
|
|
|
|
while (vp < vpstop) { code; ++vp; INC_VPNUM(); } \
|
|
|
|
} \
|
2008-08-12 16:51:55 -07:00
|
|
|
} \
|
2008-08-01 22:27:39 -07:00
|
|
|
JS_END_MACRO
|
|
|
|
|
|
|
|
/* Iterate over all slots in each pending frame. */
|
2008-08-14 00:45:39 -07:00
|
|
|
#define FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth, code) \
|
2008-07-23 23:18:02 -07:00
|
|
|
JS_BEGIN_MACRO \
|
|
|
|
DEF_VPNAME; \
|
|
|
|
unsigned n; \
|
2008-08-14 00:45:39 -07:00
|
|
|
JSStackFrame* currentFrame = cx->fp; \
|
2008-07-21 16:19:38 -07:00
|
|
|
JSStackFrame* entryFrame; \
|
2008-07-08 16:29:23 -07:00
|
|
|
JSStackFrame* fp = currentFrame; \
|
2008-07-21 16:19:38 -07:00
|
|
|
for (n = 0; n < callDepth; ++n) { fp = fp->down; } \
|
|
|
|
entryFrame = fp; \
|
|
|
|
unsigned frames = callDepth+1; \
|
2008-08-03 22:55:28 -07:00
|
|
|
JSStackFrame** fstack = \
|
|
|
|
(JSStackFrame**) alloca(frames * sizeof (JSStackFrame*)); \
|
2008-07-08 16:29:23 -07:00
|
|
|
JSStackFrame** fspstop = &fstack[frames]; \
|
|
|
|
JSStackFrame** fsp = fspstop-1; \
|
|
|
|
fp = currentFrame; \
|
2008-07-08 20:06:54 -07:00
|
|
|
for (;; fp = fp->down) { *fsp-- = fp; if (fp == entryFrame) break; } \
|
2008-08-01 22:27:39 -07:00
|
|
|
unsigned depth; \
|
|
|
|
for (depth = 0, fsp = fstack; fsp < fspstop; ++fsp, ++depth) { \
|
2008-08-12 23:21:52 -07:00
|
|
|
fp = *fsp; \
|
2008-08-03 22:55:28 -07:00
|
|
|
FORALL_FRAME_SLOTS(fp, depth, code); \
|
2008-07-08 16:29:23 -07:00
|
|
|
} \
|
2008-07-09 15:15:32 -07:00
|
|
|
JS_END_MACRO
|
2008-07-08 16:29:23 -07:00
|
|
|
|
2008-08-14 00:45:39 -07:00
|
|
|
#define FORALL_SLOTS(cx, ngslots, gslots, callDepth, code) \
|
2008-07-24 14:51:14 -07:00
|
|
|
JS_BEGIN_MACRO \
|
|
|
|
FORALL_GLOBAL_SLOTS(cx, ngslots, gslots, code); \
|
2008-08-14 00:45:39 -07:00
|
|
|
FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth, code); \
|
2008-07-24 14:51:14 -07:00
|
|
|
JS_END_MACRO
|
|
|
|
|
2008-08-13 17:09:16 -07:00
|
|
|
/* 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. */
|
2008-08-25 16:04:10 -07:00
|
|
|
unsigned
|
|
|
|
js_NativeStackSlots(JSContext *cx, unsigned callDepth)
|
2008-08-13 17:09:16 -07:00
|
|
|
{
|
2008-08-15 10:09:36 -07:00
|
|
|
JSStackFrame* fp = cx->fp;
|
2008-08-13 17:09:16 -07:00
|
|
|
unsigned slots = 0;
|
|
|
|
#if defined _DEBUG
|
|
|
|
unsigned int origCallDepth = callDepth;
|
|
|
|
#endif
|
|
|
|
for (;;) {
|
|
|
|
unsigned operands = fp->regs->sp - StackBase(fp);
|
2008-08-21 11:40:56 -07:00
|
|
|
JS_ASSERT(operands <= unsigned(fp->script->nslots - fp->script->nfixed));
|
2008-08-13 17:09:16 -07:00
|
|
|
slots += operands;
|
|
|
|
if (fp->callee)
|
|
|
|
slots += fp->script->nfixed;
|
|
|
|
if (callDepth-- == 0) {
|
2008-08-25 23:08:21 -07:00
|
|
|
if (fp->callee)
|
2008-08-25 16:04:10 -07:00
|
|
|
slots += 2/*callee,this*/ + fp->fun->nargs;
|
2008-08-13 17:09:16 -07:00
|
|
|
#if defined _DEBUG
|
|
|
|
unsigned int m = 0;
|
2008-08-14 00:45:39 -07:00
|
|
|
FORALL_SLOTS_IN_PENDING_FRAMES(cx, origCallDepth, m++);
|
2008-08-13 17:09:16 -07:00
|
|
|
JS_ASSERT(m == slots);
|
|
|
|
#endif
|
|
|
|
return slots;
|
|
|
|
}
|
|
|
|
JSStackFrame* fp2 = fp;
|
|
|
|
fp = fp->down;
|
|
|
|
int missing = fp2->fun->nargs - fp2->argc;
|
|
|
|
if (missing > 0)
|
|
|
|
slots += missing;
|
|
|
|
}
|
2008-08-25 16:04:10 -07:00
|
|
|
JS_NOT_REACHED("js_NativeStackSlots");
|
2008-08-13 17:09:16 -07:00
|
|
|
}
|
|
|
|
|
2008-08-18 07:32:42 -07:00
|
|
|
/* Capture the type map for the selected slots of the global object. */
|
2008-08-07 17:20:49 -07:00
|
|
|
void
|
2008-08-07 17:22:21 -07:00
|
|
|
TypeMap::captureGlobalTypes(JSContext* cx, SlotList& slots)
|
2008-08-07 17:20:49 -07:00
|
|
|
{
|
|
|
|
unsigned ngslots = slots.length();
|
|
|
|
uint16* gslots = slots.data();
|
|
|
|
setLength(ngslots);
|
|
|
|
uint8* map = data();
|
|
|
|
uint8* m = map;
|
2008-08-18 15:32:17 -07:00
|
|
|
FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
|
2008-08-07 17:20:49 -07:00
|
|
|
uint8 type = getCoercedType(*vp);
|
2008-09-10 12:20:01 -07:00
|
|
|
if ((type == JSVAL_INT) && oracle.isGlobalSlotUndemotable(cx->fp->script, gslots[n]))
|
2008-08-07 17:20:49 -07:00
|
|
|
type = JSVAL_DOUBLE;
|
2008-10-06 16:22:57 -07:00
|
|
|
JS_ASSERT(type != JSVAL_BOXED);
|
2008-08-07 17:20:49 -07:00
|
|
|
*m++ = type;
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2008-08-18 07:32:42 -07:00
|
|
|
/* Capture the type map for the currently pending stack frames. */
|
2008-08-18 15:32:17 -07:00
|
|
|
void
|
2008-08-07 16:23:50 -07:00
|
|
|
TypeMap::captureStackTypes(JSContext* cx, unsigned callDepth)
|
|
|
|
{
|
2008-08-25 16:04:10 -07:00
|
|
|
setLength(js_NativeStackSlots(cx, callDepth));
|
2008-08-07 16:23:50 -07:00
|
|
|
uint8* map = data();
|
|
|
|
uint8* m = map;
|
2008-08-14 00:45:39 -07:00
|
|
|
FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
|
2008-08-07 16:23:50 -07:00
|
|
|
uint8 type = getCoercedType(*vp);
|
2008-08-15 18:01:26 -07:00
|
|
|
if ((type == JSVAL_INT) &&
|
2008-09-10 12:20:01 -07:00
|
|
|
oracle.isStackSlotUndemotable(cx->fp->script, cx->fp->regs->pc, unsigned(m - map))) {
|
2008-08-07 16:23:50 -07:00
|
|
|
type = JSVAL_DOUBLE;
|
2008-08-15 18:01:26 -07:00
|
|
|
}
|
2008-08-07 16:23:50 -07:00
|
|
|
*m++ = type;
|
|
|
|
);
|
2008-08-07 16:26:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare this type map to another one and see whether they match. */
|
|
|
|
bool
|
2008-08-19 11:32:36 -07:00
|
|
|
TypeMap::matches(TypeMap& other) const
|
2008-08-07 16:26:56 -07:00
|
|
|
{
|
2008-08-19 11:32:36 -07:00
|
|
|
if (length() != other.length())
|
|
|
|
return false;
|
2008-08-07 16:27:26 -07:00
|
|
|
return !memcmp(data(), other.data(), length());
|
2008-08-07 16:23:50 -07:00
|
|
|
}
|
|
|
|
|
2008-08-15 16:00:24 -07:00
|
|
|
/* Use the provided storage area to create a new type map that contains the partial type map
|
|
|
|
with the rest of it filled up from the complete type map. */
|
|
|
|
static void
|
|
|
|
mergeTypeMaps(uint8** partial, unsigned* plength, uint8* complete, unsigned clength, uint8* mem)
|
|
|
|
{
|
|
|
|
unsigned l = *plength;
|
|
|
|
JS_ASSERT(l < clength);
|
|
|
|
memcpy(mem, *partial, l * sizeof(uint8));
|
|
|
|
memcpy(mem + l, complete + l, (clength - l) * sizeof(uint8));
|
|
|
|
*partial = mem;
|
|
|
|
*plength = clength;
|
|
|
|
}
|
|
|
|
|
2008-08-27 17:25:56 -07:00
|
|
|
static void
|
|
|
|
js_TrashTree(JSContext* cx, Fragment* f);
|
|
|
|
|
2008-10-21 17:50:32 -07:00
|
|
|
TraceRecorder::TraceRecorder(JSContext* cx, SideExit* _anchor, Fragment* _fragment,
|
2008-08-20 22:37:00 -07:00
|
|
|
TreeInfo* ti, unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap,
|
2008-10-21 17:50:32 -07:00
|
|
|
SideExit* innermostNestedGuard)
|
2008-06-26 22:33:33 -07:00
|
|
|
{
|
2008-08-16 23:43:49 -07:00
|
|
|
JS_ASSERT(!_fragment->vmprivate && ti);
|
2008-08-18 15:32:17 -07:00
|
|
|
|
2008-06-28 18:47:12 -07:00
|
|
|
this->cx = cx;
|
2008-08-19 11:32:36 -07:00
|
|
|
this->traceMonitor = &JS_TRACE_MONITOR(cx);
|
2008-07-14 16:40:38 -07:00
|
|
|
this->globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain);
|
2008-07-17 17:12:28 -07:00
|
|
|
this->anchor = _anchor;
|
2008-07-02 17:19:07 -07:00
|
|
|
this->fragment = _fragment;
|
2008-07-16 21:41:03 -07:00
|
|
|
this->lirbuf = _fragment->lirbuf;
|
2008-08-16 23:43:49 -07:00
|
|
|
this->treeInfo = ti;
|
2008-07-21 13:18:08 -07:00
|
|
|
this->callDepth = _fragment->calldepth;
|
|
|
|
JS_ASSERT(!_anchor || _anchor->calldepth == _fragment->calldepth);
|
2008-07-10 21:55:09 -07:00
|
|
|
this->atoms = cx->fp->script->atomMap.vector;
|
2008-08-29 18:59:21 -07:00
|
|
|
this->deepAborted = false;
|
2008-09-05 18:29:08 -07:00
|
|
|
this->applyingArguments = false;
|
|
|
|
this->trashTree = false;
|
2008-08-28 14:24:58 -07:00
|
|
|
this->whichTreeToTrash = _fragment->root;
|
2008-09-23 13:29:41 -07:00
|
|
|
this->global_dslots = this->globalObj->dslots;
|
2008-10-08 21:02:04 -07:00
|
|
|
this->terminate = false;
|
2008-08-29 18:59:21 -07:00
|
|
|
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("recording starting from %s:%u@%u\n", cx->fp->script->filename,
|
|
|
|
js_PCToLineNumber(cx, cx->fp->script, cx->fp->regs->pc),
|
|
|
|
cx->fp->regs->pc - cx->fp->script->code););
|
2008-09-23 13:29:41 -07:00
|
|
|
debug_only_v(printf("globalObj=%p, shape=%d\n", this->globalObj, OBJ_SHAPE(this->globalObj));)
|
2008-07-07 02:21:04 -07:00
|
|
|
|
2008-06-30 18:08:32 -07:00
|
|
|
lir = lir_buf_writer = new (&gc) LirBufWriter(lirbuf);
|
2008-07-05 10:41:35 -07:00
|
|
|
#ifdef DEBUG
|
2008-08-20 10:15:42 -07:00
|
|
|
if (verbose_debug)
|
|
|
|
lir = verbose_filter = new (&gc) VerboseWriter(&gc, lir, lirbuf->names);
|
2008-09-02 22:29:23 -07:00
|
|
|
#endif
|
2008-09-02 22:29:23 -07:00
|
|
|
#ifdef NJ_SOFTFLOAT
|
2008-09-02 22:29:23 -07:00
|
|
|
lir = float_filter = new (&gc) SoftFloatFilter(lir);
|
2008-07-05 10:41:35 -07:00
|
|
|
#endif
|
2008-06-30 18:08:32 -07:00
|
|
|
lir = cse_filter = new (&gc) CseFilter(lir, &gc);
|
|
|
|
lir = expr_filter = new (&gc) ExprFilter(lir);
|
2008-07-07 00:43:40 -07:00
|
|
|
lir = func_filter = new (&gc) FuncFilter(lir, *this);
|
2008-10-13 13:29:18 -07:00
|
|
|
lir->ins0(LIR_start);
|
2008-07-15 10:26:15 -07:00
|
|
|
|
2008-10-22 16:00:08 -07:00
|
|
|
if (!nanojit::AvmCore::config.tree_opt || fragment->root == fragment)
|
2008-10-13 13:29:18 -07:00
|
|
|
lirbuf->state = addName(lir->insParam(0, 0), "state");
|
|
|
|
|
2008-08-18 12:54:04 -07:00
|
|
|
lirbuf->sp = addName(lir->insLoad(LIR_ldp, lirbuf->state, (int)offsetof(InterpState, sp)), "sp");
|
|
|
|
lirbuf->rp = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, rp)), "rp");
|
|
|
|
cx_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, cx)), "cx");
|
|
|
|
gp_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, gp)), "gp");
|
|
|
|
eos_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, eos)), "eos");
|
|
|
|
eor_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, eor)), "eor");
|
2008-07-05 10:41:35 -07:00
|
|
|
|
2008-08-08 15:26:31 -07:00
|
|
|
/* read into registers all values on the stack and all globals we know so far */
|
2008-08-18 15:32:17 -07:00
|
|
|
import(treeInfo, lirbuf->sp, ngslots, callDepth, globalTypeMap, stackTypeMap);
|
2008-08-20 22:37:00 -07:00
|
|
|
|
|
|
|
/* If we are attached to a tree call guard, make sure the guard the inner tree exited from
|
|
|
|
is what we expect it to be. */
|
2008-10-21 17:50:32 -07:00
|
|
|
if (_anchor && _anchor->exitType == NESTED_EXIT) {
|
2008-08-20 22:37:00 -07:00
|
|
|
LIns* nested_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state,
|
2008-09-19 18:45:57 -07:00
|
|
|
offsetof(InterpState, lastTreeExitGuard)),
|
|
|
|
"lastTreeExitGuard");
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
guard(true, lir->ins2(LIR_eq, nested_ins, INS_CONSTPTR(innermostNestedGuard)), NESTED_EXIT);
|
2008-08-20 22:37:00 -07:00
|
|
|
}
|
2008-06-27 18:06:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
TraceRecorder::~TraceRecorder()
|
|
|
|
{
|
2008-08-16 23:43:49 -07:00
|
|
|
JS_ASSERT(treeInfo);
|
|
|
|
if (fragment->root == fragment && !fragment->root->code()) {
|
|
|
|
JS_ASSERT(!fragment->root->vmprivate);
|
|
|
|
delete treeInfo;
|
|
|
|
}
|
2008-08-27 17:25:56 -07:00
|
|
|
if (trashTree)
|
2008-08-28 14:24:58 -07:00
|
|
|
js_TrashTree(cx, whichTreeToTrash);
|
2008-06-30 18:08:32 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
delete verbose_filter;
|
2008-07-05 10:41:35 -07:00
|
|
|
#endif
|
2008-06-30 18:08:32 -07:00
|
|
|
delete cse_filter;
|
|
|
|
delete expr_filter;
|
2008-07-05 23:21:53 -07:00
|
|
|
delete func_filter;
|
2008-09-02 22:29:23 -07:00
|
|
|
#ifdef NJ_SOFTFLOAT
|
2008-09-02 22:29:23 -07:00
|
|
|
delete float_filter;
|
|
|
|
#endif
|
2008-06-30 18:08:32 -07:00
|
|
|
delete lir_buf_writer;
|
2008-06-26 22:33:33 -07:00
|
|
|
}
|
|
|
|
|
2008-07-17 02:02:48 -07:00
|
|
|
/* Add debug information to a LIR instruction as we emit it. */
|
|
|
|
inline LIns*
|
|
|
|
TraceRecorder::addName(LIns* ins, const char* name)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
lirbuf->names->addName(ins, name);
|
|
|
|
#endif
|
|
|
|
return ins;
|
|
|
|
}
|
|
|
|
|
2008-06-28 17:14:06 -07:00
|
|
|
/* Determine the current call depth (starting with the entry frame.) */
|
|
|
|
unsigned
|
2008-07-05 19:15:00 -07:00
|
|
|
TraceRecorder::getCallDepth() const
|
2008-06-28 17:14:06 -07:00
|
|
|
{
|
2008-07-21 13:18:08 -07:00
|
|
|
return callDepth;
|
2008-06-28 17:14:06 -07:00
|
|
|
}
|
|
|
|
|
2008-07-23 23:18:02 -07:00
|
|
|
/* Determine the offset in the native global frame for a jsval we track */
|
2008-07-23 02:57:56 -07:00
|
|
|
ptrdiff_t
|
2008-07-23 23:18:02 -07:00
|
|
|
TraceRecorder::nativeGlobalOffset(jsval* p) const
|
|
|
|
{
|
2008-07-30 16:28:48 -07:00
|
|
|
JS_ASSERT(isGlobal(p));
|
|
|
|
if (size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS)
|
|
|
|
return size_t(p - globalObj->fslots) * sizeof(double);
|
|
|
|
return ((p - globalObj->dslots) + JS_INITIAL_NSLOTS) * sizeof(double);
|
2008-07-23 23:18:02 -07:00
|
|
|
}
|
2008-07-25 16:23:12 -07:00
|
|
|
|
2008-07-30 13:34:22 -07:00
|
|
|
/* Determine whether a value is a global stack slot */
|
|
|
|
bool
|
2008-07-30 12:15:53 -07:00
|
|
|
TraceRecorder::isGlobal(jsval* p) const
|
|
|
|
{
|
|
|
|
return ((size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) ||
|
|
|
|
(size_t(p - globalObj->dslots) < (STOBJ_NSLOTS(globalObj) - JS_INITIAL_NSLOTS)));
|
|
|
|
}
|
|
|
|
|
2008-07-23 23:18:02 -07:00
|
|
|
/* Determine the offset in the native stack for a jsval we track */
|
|
|
|
ptrdiff_t
|
|
|
|
TraceRecorder::nativeStackOffset(jsval* p) const
|
2008-06-28 17:14:06 -07:00
|
|
|
{
|
2008-07-25 16:23:12 -07:00
|
|
|
#ifdef DEBUG
|
2008-07-23 16:39:17 -07:00
|
|
|
size_t slow_offset = 0;
|
2008-08-14 00:45:39 -07:00
|
|
|
FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
|
2008-07-23 16:39:17 -07:00
|
|
|
if (vp == p) goto done;
|
|
|
|
slow_offset += sizeof(double)
|
2008-07-08 16:29:23 -07:00
|
|
|
);
|
2008-08-01 08:26:32 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If it's not in a pending frame, it must be on the stack of the current frame above
|
|
|
|
* sp but below fp->slots + script->nslots.
|
|
|
|
*/
|
|
|
|
JS_ASSERT(size_t(p - cx->fp->slots) < cx->fp->script->nslots);
|
2008-07-23 16:39:17 -07:00
|
|
|
slow_offset += size_t(p - cx->fp->regs->sp) * sizeof(double);
|
2008-08-01 08:26:32 -07:00
|
|
|
|
2008-07-25 16:23:12 -07:00
|
|
|
done:
|
2008-07-23 16:39:17 -07:00
|
|
|
#define RETURN(offset) { JS_ASSERT((offset) == slow_offset); return offset; }
|
|
|
|
#else
|
|
|
|
#define RETURN(offset) { return offset; }
|
|
|
|
#endif
|
|
|
|
size_t offset = 0;
|
|
|
|
JSStackFrame* currentFrame = cx->fp;
|
|
|
|
JSStackFrame* entryFrame;
|
|
|
|
JSStackFrame* fp = currentFrame;
|
|
|
|
for (unsigned n = 0; n < callDepth; ++n) { fp = fp->down; }
|
|
|
|
entryFrame = fp;
|
|
|
|
unsigned frames = callDepth+1;
|
|
|
|
JSStackFrame** fstack = (JSStackFrame **)alloca(frames * sizeof (JSStackFrame *));
|
|
|
|
JSStackFrame** fspstop = &fstack[frames];
|
|
|
|
JSStackFrame** fsp = fspstop-1;
|
|
|
|
fp = currentFrame;
|
|
|
|
for (;; fp = fp->down) { *fsp-- = fp; if (fp == entryFrame) break; }
|
|
|
|
for (fsp = fstack; fsp < fspstop; ++fsp) {
|
2008-08-03 22:55:28 -07:00
|
|
|
fp = *fsp;
|
|
|
|
if (fp->callee) {
|
2008-08-01 08:26:32 -07:00
|
|
|
if (fsp == fstack) {
|
2008-08-25 23:22:20 -07:00
|
|
|
if (size_t(p - &fp->argv[-2]) < size_t(2/*callee,this*/ + fp->fun->nargs))
|
2008-08-14 23:22:51 -07:00
|
|
|
RETURN(offset + size_t(p - &fp->argv[-2]) * sizeof(double));
|
2008-08-25 16:04:10 -07:00
|
|
|
offset += (2/*callee,this*/ + fp->fun->nargs) * sizeof(double);
|
2008-08-01 08:26:32 -07:00
|
|
|
}
|
2008-08-03 22:55:28 -07:00
|
|
|
if (size_t(p - &fp->slots[0]) < fp->script->nfixed)
|
|
|
|
RETURN(offset + size_t(p - &fp->slots[0]) * sizeof(double));
|
|
|
|
offset += fp->script->nfixed * sizeof(double);
|
2008-07-23 16:39:17 -07:00
|
|
|
}
|
2008-08-03 22:55:28 -07:00
|
|
|
jsval* spbase = StackBase(fp);
|
|
|
|
if (size_t(p - spbase) < size_t(fp->regs->sp - spbase))
|
2008-08-01 08:26:32 -07:00
|
|
|
RETURN(offset + size_t(p - spbase) * sizeof(double));
|
2008-08-03 22:55:28 -07:00
|
|
|
offset += size_t(fp->regs->sp - spbase) * sizeof(double);
|
2008-08-12 23:21:52 -07:00
|
|
|
if (fsp < fspstop - 1) {
|
|
|
|
JSStackFrame* fp2 = fsp[1];
|
|
|
|
int missing = fp2->fun->nargs - fp2->argc;
|
|
|
|
if (missing > 0) {
|
|
|
|
if (size_t(p - fp->regs->sp) < size_t(missing))
|
|
|
|
RETURN(offset + size_t(p - fp->regs->sp) * sizeof(double));
|
|
|
|
offset += size_t(missing) * sizeof(double);
|
|
|
|
}
|
2008-08-12 16:51:55 -07:00
|
|
|
}
|
2008-07-23 16:39:17 -07:00
|
|
|
}
|
2008-08-01 08:26:32 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If it's not in a pending frame, it must be on the stack of the current frame above
|
|
|
|
* sp but below fp->slots + script->nslots.
|
|
|
|
*/
|
|
|
|
JS_ASSERT(size_t(p - currentFrame->slots) < currentFrame->script->nslots);
|
2008-07-23 15:48:15 -07:00
|
|
|
offset += size_t(p - currentFrame->regs->sp) * sizeof(double);
|
2008-07-23 16:39:17 -07:00
|
|
|
RETURN(offset);
|
2008-07-25 16:23:12 -07:00
|
|
|
#undef RETURN
|
2008-06-28 17:14:06 -07:00
|
|
|
}
|
|
|
|
|
2008-07-05 10:41:35 -07:00
|
|
|
/* Track the maximum number of native frame slots we need during
|
2008-07-02 17:19:07 -07:00
|
|
|
execution. */
|
|
|
|
void
|
2008-07-23 23:18:02 -07:00
|
|
|
TraceRecorder::trackNativeStackUse(unsigned slots)
|
2008-07-02 17:19:07 -07:00
|
|
|
{
|
2008-07-23 23:18:02 -07:00
|
|
|
if (slots > treeInfo->maxNativeStackSlots)
|
|
|
|
treeInfo->maxNativeStackSlots = slots;
|
2008-07-02 17:19:07 -07:00
|
|
|
}
|
|
|
|
|
2008-07-05 10:41:35 -07:00
|
|
|
/* Unbox a jsval into a slot. Slots are wide enough to hold double values
|
2008-06-30 09:36:10 -07:00
|
|
|
directly (instead of storing a pointer to them). */
|
2008-07-02 17:19:07 -07:00
|
|
|
static bool
|
2008-08-21 00:43:45 -07:00
|
|
|
ValueToNative(JSContext* cx, jsval v, uint8 type, double* slot)
|
2008-06-29 20:56:06 -07:00
|
|
|
{
|
2008-08-21 00:43:45 -07:00
|
|
|
unsigned tag = JSVAL_TAG(v);
|
|
|
|
switch (type) {
|
|
|
|
case JSVAL_INT:
|
2008-07-07 01:05:53 -07:00
|
|
|
jsint i;
|
|
|
|
if (JSVAL_IS_INT(v))
|
|
|
|
*(jsint*)slot = JSVAL_TO_INT(v);
|
2008-08-21 00:43:45 -07:00
|
|
|
else if ((tag == JSVAL_DOUBLE) && JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i))
|
2008-07-07 01:05:53 -07:00
|
|
|
*(jsint*)slot = i;
|
2008-08-21 00:43:45 -07:00
|
|
|
else if (v == JSVAL_VOID)
|
|
|
|
*(jsint*)slot = 0;
|
2008-07-10 21:23:32 -07:00
|
|
|
else {
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("int != tag%lu(value=%lu) ", JSVAL_TAG(v), v);)
|
2008-07-19 00:27:45 -07:00
|
|
|
return false;
|
2008-07-10 21:23:32 -07:00
|
|
|
}
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("int<%d> ", *(jsint*)slot);)
|
2008-07-07 01:05:53 -07:00
|
|
|
return true;
|
2008-08-21 00:43:45 -07:00
|
|
|
case JSVAL_DOUBLE:
|
2008-07-07 01:05:53 -07:00
|
|
|
jsdouble d;
|
|
|
|
if (JSVAL_IS_INT(v))
|
|
|
|
d = JSVAL_TO_INT(v);
|
2008-08-21 00:43:45 -07:00
|
|
|
else if (tag == JSVAL_DOUBLE)
|
2008-07-07 01:05:53 -07:00
|
|
|
d = *JSVAL_TO_DOUBLE(v);
|
2008-08-21 00:43:45 -07:00
|
|
|
else if (v == JSVAL_VOID)
|
|
|
|
d = js_NaN;
|
2008-07-10 21:23:32 -07:00
|
|
|
else {
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("double != tag%lu ", JSVAL_TAG(v));)
|
2008-07-19 00:27:45 -07:00
|
|
|
return false;
|
2008-07-10 21:23:32 -07:00
|
|
|
}
|
2008-07-07 01:05:53 -07:00
|
|
|
*(jsdouble*)slot = d;
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("double<%g> ", d);)
|
2008-07-07 01:05:53 -07:00
|
|
|
return true;
|
2008-07-10 21:55:09 -07:00
|
|
|
case JSVAL_BOOLEAN:
|
2008-08-21 00:43:45 -07:00
|
|
|
if (tag != JSVAL_BOOLEAN) {
|
|
|
|
debug_only_v(printf("bool != tag%u ", tag);)
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-29 17:19:51 -07:00
|
|
|
*(JSBool*)slot = JSVAL_TO_BOOLEAN(v);
|
2008-09-29 19:27:43 -07:00
|
|
|
debug_only_v(printf("boolean<%d> ", *(JSBool*)slot);)
|
2008-08-21 00:43:45 -07:00
|
|
|
return true;
|
2008-07-10 21:55:09 -07:00
|
|
|
case JSVAL_STRING:
|
2008-08-21 00:43:45 -07:00
|
|
|
if (v == JSVAL_VOID) {
|
|
|
|
*(JSString**)slot = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (tag != JSVAL_STRING) {
|
|
|
|
debug_only_v(printf("string != tag%u ", tag);)
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-02 02:53:24 -07:00
|
|
|
*(JSString**)slot = JSVAL_TO_STRING(v);
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("string<%p> ", *(JSString**)slot);)
|
2008-08-21 00:43:45 -07:00
|
|
|
return true;
|
2008-07-10 21:55:09 -07:00
|
|
|
default:
|
2008-10-06 16:22:57 -07:00
|
|
|
/* Note: we should never see JSVAL_BOXED in an entry type map. */
|
2008-08-21 00:43:45 -07:00
|
|
|
JS_ASSERT(type == JSVAL_OBJECT);
|
2008-08-21 01:13:07 -07:00
|
|
|
if (v == JSVAL_VOID) {
|
|
|
|
*(JSObject**)slot = NULL;
|
|
|
|
return true;
|
|
|
|
}
|
2008-08-21 00:43:45 -07:00
|
|
|
if (tag != JSVAL_OBJECT) {
|
|
|
|
debug_only_v(printf("object != tag%u ", tag);)
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-09 17:09:22 -07:00
|
|
|
*(JSObject**)slot = JSVAL_TO_OBJECT(v);
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("object<%p:%s> ", JSVAL_TO_OBJECT(v),
|
2008-07-10 21:55:09 -07:00
|
|
|
JSVAL_IS_NULL(v)
|
|
|
|
? "null"
|
|
|
|
: STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name);)
|
2008-08-21 00:43:45 -07:00
|
|
|
return true;
|
2008-06-29 20:56:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-25 19:11:48 -07:00
|
|
|
/* We maintain an emergency reserve pool of doubles so we can recover safely if a trace runs
|
|
|
|
out of memory (doubles or objects). */
|
2008-09-26 14:16:11 -07:00
|
|
|
static jsval
|
2008-09-25 19:11:48 -07:00
|
|
|
AllocateDoubleFromReservePool(JSContext* cx)
|
|
|
|
{
|
|
|
|
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
|
|
|
JS_ASSERT(tm->recoveryDoublePoolPtr > tm->recoveryDoublePool);
|
|
|
|
return *--tm->recoveryDoublePoolPtr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2008-09-26 14:16:11 -07:00
|
|
|
ReplenishReservePool(JSContext* cx, JSTraceMonitor* tm, bool& didGC)
|
2008-09-25 19:11:48 -07:00
|
|
|
{
|
2008-09-26 14:16:11 -07:00
|
|
|
/* We should not be called with a full pool. */
|
|
|
|
JS_ASSERT((size_t) (tm->recoveryDoublePoolPtr - tm->recoveryDoublePool) <
|
|
|
|
MAX_NATIVE_STACK_SLOTS);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When the GC runs in js_NewDoubleInRootedValue, it resets
|
|
|
|
* tm->recoveryDoublePoolPtr back to tm->recoveryDoublePool. We tolerate
|
|
|
|
* that only once.
|
|
|
|
*/
|
|
|
|
JSRuntime* rt = cx->runtime;
|
|
|
|
uintN gcNumber = rt->gcNumber;
|
|
|
|
didGC = false;
|
|
|
|
for (; tm->recoveryDoublePoolPtr < tm->recoveryDoublePool + MAX_NATIVE_STACK_SLOTS;
|
|
|
|
++tm->recoveryDoublePoolPtr) {
|
|
|
|
if (!js_NewDoubleInRootedValue(cx, 0.0, tm->recoveryDoublePoolPtr)) {
|
|
|
|
didGC = true;
|
|
|
|
JS_ASSERT(tm->recoveryDoublePoolPtr == tm->recoveryDoublePool);
|
2008-09-25 19:11:48 -07:00
|
|
|
return false;
|
2008-09-26 14:16:11 -07:00
|
|
|
}
|
|
|
|
if (tm->recoveryDoublePoolPtr == tm->recoveryDoublePool) {
|
|
|
|
if (gcNumber != rt->gcNumber) {
|
|
|
|
if (didGC)
|
|
|
|
return false;
|
|
|
|
didGC = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
JS_ASSERT(rt->gcNumber == gcNumber ||
|
|
|
|
(rt->gcNumber - gcNumber == (unsigned) 1 && didGC));
|
|
|
|
}
|
2008-09-25 19:11:48 -07:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-02 02:53:24 -07:00
|
|
|
/* 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. */
|
2008-07-05 10:41:35 -07:00
|
|
|
static bool
|
2008-07-28 18:06:34 -07:00
|
|
|
NativeToValue(JSContext* cx, jsval& v, uint8 type, double* slot)
|
2008-06-29 23:02:22 -07:00
|
|
|
{
|
2008-07-07 01:37:40 -07:00
|
|
|
jsint i;
|
2008-07-06 15:55:04 -07:00
|
|
|
jsdouble d;
|
2008-07-07 17:21:54 -07:00
|
|
|
switch (type) {
|
2008-07-05 10:41:35 -07:00
|
|
|
case JSVAL_BOOLEAN:
|
2008-09-29 19:27:43 -07:00
|
|
|
v = BOOLEAN_TO_JSVAL(*(JSBool*)slot);
|
|
|
|
debug_only_v(printf("boolean<%d> ", *(JSBool*)slot);)
|
2008-07-02 14:38:12 -07:00
|
|
|
break;
|
2008-07-05 10:41:35 -07:00
|
|
|
case JSVAL_INT:
|
2008-07-07 01:37:40 -07:00
|
|
|
i = *(jsint*)slot;
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("int<%d> ", i);)
|
2008-07-07 01:37:40 -07:00
|
|
|
store_int:
|
2008-07-06 15:55:04 -07:00
|
|
|
if (INT_FITS_IN_JSVAL(i)) {
|
2008-07-07 15:15:56 -07:00
|
|
|
v = INT_TO_JSVAL(i);
|
2008-07-06 15:55:04 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
d = (jsdouble)i;
|
2008-07-07 01:37:40 -07:00
|
|
|
goto store_double;
|
2008-07-05 10:41:35 -07:00
|
|
|
case JSVAL_DOUBLE:
|
2008-07-06 15:55:04 -07:00
|
|
|
d = *slot;
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("double<%g> ", d);)
|
2008-07-07 01:37:40 -07:00
|
|
|
if (JSDOUBLE_IS_INT(d, i))
|
|
|
|
goto store_int;
|
2008-09-25 19:11:48 -07:00
|
|
|
store_double: {
|
|
|
|
/* Its not safe to trigger the GC here, so use an emergency heap if we are out of
|
|
|
|
double boxes. */
|
|
|
|
if (cx->doubleFreeList) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
bool ok =
|
|
|
|
#endif
|
|
|
|
js_NewDoubleInRootedValue(cx, d, &v);
|
|
|
|
JS_ASSERT(ok);
|
|
|
|
return true;
|
|
|
|
}
|
2008-09-26 14:16:11 -07:00
|
|
|
v = AllocateDoubleFromReservePool(cx);
|
|
|
|
JS_ASSERT(JSVAL_IS_DOUBLE(v) && *JSVAL_TO_DOUBLE(v) == 0.0);
|
|
|
|
*JSVAL_TO_DOUBLE(v) = d;
|
2008-09-25 19:11:48 -07:00
|
|
|
return true;
|
|
|
|
}
|
2008-07-05 10:41:35 -07:00
|
|
|
case JSVAL_STRING:
|
2008-07-07 15:15:56 -07:00
|
|
|
v = STRING_TO_JSVAL(*(JSString**)slot);
|
2008-10-11 20:26:29 -07:00
|
|
|
JS_ASSERT(JSVAL_TAG(v) == JSVAL_STRING); /* if this fails the pointer was not aligned */
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("string<%p> ", *(JSString**)slot);)
|
2008-07-02 14:38:12 -07:00
|
|
|
break;
|
2008-10-06 16:22:57 -07:00
|
|
|
case JSVAL_BOXED:
|
|
|
|
v = *(jsval*)slot;
|
|
|
|
debug_only_v(printf("box<%lx> ", v));
|
|
|
|
break;
|
2008-07-05 10:41:35 -07:00
|
|
|
default:
|
2008-07-27 02:15:17 -07:00
|
|
|
JS_ASSERT(type == JSVAL_OBJECT);
|
2008-07-07 15:15:56 -07:00
|
|
|
v = OBJECT_TO_JSVAL(*(JSObject**)slot);
|
2008-10-11 20:26:29 -07:00
|
|
|
JS_ASSERT(JSVAL_TAG(v) == JSVAL_OBJECT); /* if this fails the pointer was not aligned */
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("object<%p:%s> ", JSVAL_TO_OBJECT(v),
|
2008-07-23 15:48:15 -07:00
|
|
|
JSVAL_IS_NULL(v)
|
|
|
|
? "null"
|
|
|
|
: STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name);)
|
2008-07-02 14:38:12 -07:00
|
|
|
break;
|
2008-06-29 23:02:22 -07:00
|
|
|
}
|
2008-07-02 14:38:12 -07:00
|
|
|
return true;
|
2008-06-29 23:02:22 -07:00
|
|
|
}
|
|
|
|
|
2008-07-28 18:06:34 -07:00
|
|
|
/* Attempt to unbox the given list of interned globals onto the native global frame, checking
|
|
|
|
along the way that the supplied type-mao holds. */
|
2008-07-02 17:19:07 -07:00
|
|
|
static bool
|
2008-07-28 18:06:34 -07:00
|
|
|
BuildNativeGlobalFrame(JSContext* cx, unsigned ngslots, uint16* gslots, uint8* mp, double* np)
|
2008-06-29 20:56:06 -07:00
|
|
|
{
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("global: ");)
|
2008-07-23 23:18:02 -07:00
|
|
|
FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
|
2008-08-21 00:43:45 -07:00
|
|
|
if (!ValueToNative(cx, *vp, *mp, np + gslots[n]))
|
2008-07-23 23:18:02 -07:00
|
|
|
return false;
|
2008-08-03 22:55:28 -07:00
|
|
|
++mp;
|
2008-07-23 23:18:02 -07:00
|
|
|
);
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("\n");)
|
2008-07-28 18:06:34 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Attempt to unbox the given JS frame onto a native frame, checking along the way that the
|
|
|
|
supplied type-map holds. */
|
|
|
|
static bool
|
|
|
|
BuildNativeStackFrame(JSContext* cx, unsigned callDepth, uint8* mp, double* np)
|
|
|
|
{
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("stack: ");)
|
2008-08-14 00:45:39 -07:00
|
|
|
FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("%s%u=", vpname, vpnum);)
|
2008-08-21 00:43:45 -07:00
|
|
|
if (!ValueToNative(cx, *vp, *mp, np))
|
2008-06-29 20:56:06 -07:00
|
|
|
return false;
|
2008-07-10 13:24:49 -07:00
|
|
|
++mp; ++np;
|
2008-07-08 16:29:23 -07:00
|
|
|
);
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("\n");)
|
2008-06-29 20:56:06 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-07 02:21:04 -07:00
|
|
|
/* Box the given native frame into a JS frame. This only fails due to a hard error
|
2008-07-06 15:55:04 -07:00
|
|
|
(out of memory for example). */
|
2008-08-14 01:44:32 -07:00
|
|
|
static int
|
2008-07-28 18:06:34 -07:00
|
|
|
FlushNativeGlobalFrame(JSContext* cx, unsigned ngslots, uint16* gslots, uint8* mp, double* np)
|
2008-06-29 23:02:22 -07:00
|
|
|
{
|
2008-07-28 18:06:34 -07:00
|
|
|
uint8* mp_base = mp;
|
2008-07-23 23:18:02 -07:00
|
|
|
FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
|
2008-07-30 16:28:48 -07:00
|
|
|
if (!NativeToValue(cx, *vp, *mp, np + gslots[n]))
|
2008-08-14 01:44:32 -07:00
|
|
|
return -1;
|
2008-08-03 22:55:28 -07:00
|
|
|
++mp;
|
2008-07-23 23:18:02 -07:00
|
|
|
);
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("\n");)
|
2008-08-14 01:44:32 -07:00
|
|
|
return mp - mp_base;
|
2008-07-28 18:06:34 -07:00
|
|
|
}
|
|
|
|
|
2008-09-08 05:55:20 -07:00
|
|
|
/**
|
|
|
|
* Box the given native stack frame into the virtual machine stack. This fails
|
|
|
|
* only due to a hard error (out of memory for example).
|
|
|
|
*
|
|
|
|
* @param callDepth the distance between the entry frame into our trace and
|
|
|
|
* cx->fp when we make this call. If this is not called as a
|
|
|
|
* result of a nested exit, callDepth is 0.
|
|
|
|
* @param mp pointer to an array of type tags (JSVAL_INT, etc.) that indicate
|
|
|
|
* what the types of the things on the stack are.
|
|
|
|
* @param np pointer to the native stack. We want to copy values from here to
|
|
|
|
* the JS stack as needed.
|
|
|
|
* @param stopFrame if non-null, this frame and everything above it should not
|
|
|
|
* be restored.
|
|
|
|
* @return the number of things we popped off of np.
|
|
|
|
*/
|
2008-08-14 01:44:32 -07:00
|
|
|
static int
|
2008-09-08 05:55:20 -07:00
|
|
|
FlushNativeStackFrame(JSContext* cx, unsigned callDepth, uint8* mp, double* np,
|
|
|
|
JSStackFrame* stopFrame)
|
2008-07-28 18:06:34 -07:00
|
|
|
{
|
2008-09-08 05:55:20 -07:00
|
|
|
jsval* stopAt = stopFrame ? &stopFrame->argv[-2] : NULL;
|
2008-07-28 18:06:34 -07:00
|
|
|
uint8* mp_base = mp;
|
|
|
|
/* Root all string and object references first (we don't need to call the GC for this). */
|
2008-08-14 00:45:39 -07:00
|
|
|
FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
|
2008-09-25 19:11:48 -07:00
|
|
|
if (vp == stopAt) goto skip;
|
|
|
|
debug_only_v(printf("%s%u=", vpname, vpnum);)
|
|
|
|
if (!NativeToValue(cx, *vp, *mp, np))
|
2008-08-14 01:44:32 -07:00
|
|
|
return -1;
|
2008-07-28 18:06:34 -07:00
|
|
|
++mp; ++np
|
|
|
|
);
|
2008-09-25 19:11:48 -07:00
|
|
|
skip:
|
2008-08-01 10:40:38 -07:00
|
|
|
// Restore thisp from the now-restored argv[-1] in each pending frame.
|
2008-09-08 05:55:20 -07:00
|
|
|
// Keep in mind that we didn't restore frames at stopFrame and above!
|
|
|
|
// Scope to keep |fp| from leaking into the macros we're using.
|
|
|
|
{
|
|
|
|
unsigned n = callDepth+1; // +1 to make sure we restore the entry frame
|
|
|
|
JSStackFrame* fp = cx->fp;
|
|
|
|
if (stopFrame) {
|
|
|
|
for (; fp != stopFrame; fp = fp->down) {
|
|
|
|
JS_ASSERT(n != 0);
|
|
|
|
--n;
|
|
|
|
}
|
|
|
|
// Skip over stopFrame itself.
|
|
|
|
JS_ASSERT(n != 0);
|
|
|
|
--n;
|
|
|
|
fp = fp->down;
|
|
|
|
}
|
|
|
|
for (; n != 0; fp = fp->down) {
|
|
|
|
--n;
|
|
|
|
if (fp->callee) { // might not have it if the entry frame is global
|
|
|
|
JS_ASSERT(JSVAL_IS_OBJECT(fp->argv[-1]));
|
|
|
|
fp->thisp = JSVAL_TO_OBJECT(fp->argv[-1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("\n");)
|
2008-08-14 01:44:32 -07:00
|
|
|
return mp - mp_base;
|
2008-06-29 23:02:22 -07:00
|
|
|
}
|
|
|
|
|
2008-06-30 09:36:10 -07:00
|
|
|
/* Emit load instructions onto the trace that read the initial stack state. */
|
2008-08-18 15:32:17 -07:00
|
|
|
void
|
2008-07-25 16:23:12 -07:00
|
|
|
TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
|
2008-08-11 12:47:18 -07:00
|
|
|
const char *prefix, uintN index, JSStackFrame *fp)
|
2008-06-26 22:19:11 -07:00
|
|
|
{
|
2008-07-07 00:10:22 -07:00
|
|
|
LIns* ins;
|
2008-07-27 02:15:17 -07:00
|
|
|
if (t == JSVAL_INT) { /* demoted */
|
2008-07-07 01:05:53 -07:00
|
|
|
JS_ASSERT(isInt32(*p));
|
2008-07-07 00:10:22 -07:00
|
|
|
/* Ok, we have a valid demotion attempt pending, so insert an integer
|
|
|
|
read and promote it to double since all arithmetic operations expect
|
2008-07-07 02:21:04 -07:00
|
|
|
to see doubles on entry. The first op to use this slot will emit a
|
2008-07-07 00:10:22 -07:00
|
|
|
f2i cast which will cancel out the i2f we insert here. */
|
2008-07-22 17:54:04 -07:00
|
|
|
ins = lir->insLoadi(base, offset);
|
|
|
|
ins = lir->ins1(LIR_i2f, ins);
|
2008-07-07 00:10:22 -07:00
|
|
|
} else {
|
2008-10-06 16:22:57 -07:00
|
|
|
JS_ASSERT(t == JSVAL_BOXED || isNumber(*p) == (t == JSVAL_DOUBLE));
|
2008-08-19 17:19:19 -07:00
|
|
|
if (t == JSVAL_DOUBLE) {
|
|
|
|
ins = lir->insLoad(LIR_ldq, base, offset);
|
2008-10-02 14:30:58 -07:00
|
|
|
} else if (t == JSVAL_BOOLEAN) {
|
|
|
|
ins = lir->insLoad(LIR_ld, base, offset);
|
2008-08-19 17:19:19 -07:00
|
|
|
} else {
|
|
|
|
ins = lir->insLoad(LIR_ldp, base, offset);
|
|
|
|
}
|
2008-07-07 00:10:22 -07:00
|
|
|
}
|
2008-07-01 05:06:02 -07:00
|
|
|
tracker.set(p, ins);
|
|
|
|
#ifdef DEBUG
|
2008-07-17 15:09:48 -07:00
|
|
|
char name[64];
|
2008-07-10 16:55:37 -07:00
|
|
|
JS_ASSERT(strlen(prefix) < 10);
|
2008-08-01 10:40:38 -07:00
|
|
|
void* mark = NULL;
|
|
|
|
jsuword* localNames = NULL;
|
2008-08-09 12:05:34 -07:00
|
|
|
const char* funName = NULL;
|
2008-08-01 10:40:38 -07:00
|
|
|
if (*prefix == 'a' || *prefix == 'v') {
|
|
|
|
mark = JS_ARENA_MARK(&cx->tempPool);
|
2008-08-22 17:32:45 -07:00
|
|
|
if (JS_GET_LOCAL_NAME_COUNT(fp->fun) != 0)
|
|
|
|
localNames = js_GetLocalNameArray(cx, fp->fun, &cx->tempPool);
|
2008-08-09 12:05:34 -07:00
|
|
|
funName = fp->fun->atom ? js_AtomToPrintableString(cx, fp->fun->atom) : "<anonymous>";
|
2008-08-01 10:40:38 -07:00
|
|
|
}
|
2008-07-17 15:09:48 -07:00
|
|
|
if (!strcmp(prefix, "argv")) {
|
2008-08-11 12:47:18 -07:00
|
|
|
if (index < fp->fun->nargs) {
|
|
|
|
JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[index]);
|
2008-08-09 12:05:34 -07:00
|
|
|
JS_snprintf(name, sizeof name, "$%s.%s", funName, js_AtomToPrintableString(cx, atom));
|
|
|
|
} else {
|
|
|
|
JS_snprintf(name, sizeof name, "$%s.<arg%d>", funName, index);
|
|
|
|
}
|
2008-07-17 15:09:48 -07:00
|
|
|
} else if (!strcmp(prefix, "vars")) {
|
2008-08-01 10:40:38 -07:00
|
|
|
JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[fp->fun->nargs + index]);
|
2008-08-09 12:05:34 -07:00
|
|
|
JS_snprintf(name, sizeof name, "$%s.%s", funName, js_AtomToPrintableString(cx, atom));
|
2008-07-17 15:09:48 -07:00
|
|
|
} else {
|
|
|
|
JS_snprintf(name, sizeof name, "$%s%d", prefix, index);
|
|
|
|
}
|
2008-08-01 10:40:38 -07:00
|
|
|
|
|
|
|
if (mark)
|
|
|
|
JS_ARENA_RELEASE(&cx->tempPool, mark);
|
2008-07-17 02:00:23 -07:00
|
|
|
addName(ins, name);
|
2008-07-17 15:09:48 -07:00
|
|
|
|
2008-07-10 16:55:37 -07:00
|
|
|
static const char* typestr[] = {
|
2008-07-15 10:26:15 -07:00
|
|
|
"object", "int", "double", "3", "string", "5", "boolean", "any"
|
|
|
|
};
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("import vp=%p name=%s type=%s flags=%d\n", p, name, typestr[t & 7], t >> 3););
|
2008-07-01 05:06:02 -07:00
|
|
|
#endif
|
2008-06-26 22:19:11 -07:00
|
|
|
}
|
|
|
|
|
2008-08-08 15:26:31 -07:00
|
|
|
void
|
2008-08-18 15:32:17 -07:00
|
|
|
TraceRecorder::import(TreeInfo* treeInfo, LIns* sp, unsigned ngslots, unsigned callDepth,
|
2008-08-12 23:25:17 -07:00
|
|
|
uint8* globalTypeMap, uint8* stackTypeMap)
|
2008-08-08 15:26:31 -07:00
|
|
|
{
|
2008-08-17 11:38:49 -07:00
|
|
|
/* If we get a partial list that doesn't have all the types (i.e. recording from a side
|
|
|
|
exit that was recorded but we added more global slots later), merge the missing types
|
|
|
|
from the entry type map. This is safe because at the loop edge we verify that we
|
|
|
|
have compatible types for all globals (entry type and loop edge type match). While
|
|
|
|
a different trace of the tree might have had a guard with a different type map for
|
|
|
|
these slots we just filled in here (the guard we continue from didn't know about them),
|
|
|
|
since we didn't take that particular guard the only way we could have ended up here
|
|
|
|
is if that other trace had at its end a compatible type distribution with the entry
|
|
|
|
map. Since thats exactly what we used to fill in the types our current side exit
|
|
|
|
didn't provide, this is always safe to do. */
|
|
|
|
unsigned length;
|
2008-08-19 11:32:36 -07:00
|
|
|
if (ngslots < (length = traceMonitor->globalTypeMap->length()))
|
2008-08-18 15:32:17 -07:00
|
|
|
mergeTypeMaps(&globalTypeMap, &ngslots,
|
2008-08-19 11:32:36 -07:00
|
|
|
traceMonitor->globalTypeMap->data(), length,
|
2008-08-17 11:38:49 -07:00
|
|
|
(uint8*)alloca(sizeof(uint8) * length));
|
2008-08-19 11:32:36 -07:00
|
|
|
JS_ASSERT(ngslots == traceMonitor->globalTypeMap->length());
|
2008-08-18 15:32:17 -07:00
|
|
|
|
2008-08-08 15:26:31 -07:00
|
|
|
/* the first time we compile a tree this will be empty as we add entries lazily */
|
2008-08-19 11:32:36 -07:00
|
|
|
uint16* gslots = traceMonitor->globalSlots->data();
|
2008-08-08 15:26:31 -07:00
|
|
|
uint8* m = globalTypeMap;
|
|
|
|
FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
|
|
|
|
import(gp_ins, nativeGlobalOffset(vp), vp, *m, vpname, vpnum, NULL);
|
|
|
|
m++;
|
|
|
|
);
|
|
|
|
ptrdiff_t offset = -treeInfo->nativeStackBase;
|
|
|
|
m = stackTypeMap;
|
2008-08-14 00:45:39 -07:00
|
|
|
FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
|
2008-08-13 15:50:11 -07:00
|
|
|
import(sp, offset, vp, *m, vpname, vpnum, fp);
|
2008-08-08 15:26:31 -07:00
|
|
|
m++; offset += sizeof(double);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2008-07-30 13:34:22 -07:00
|
|
|
/* Lazily import a global slot if we don't already have it in the tracker. */
|
2008-07-30 04:17:22 -07:00
|
|
|
bool
|
2008-07-30 04:20:48 -07:00
|
|
|
TraceRecorder::lazilyImportGlobalSlot(unsigned slot)
|
2008-07-30 04:17:22 -07:00
|
|
|
{
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
if (slot != uint16(slot)) /* we use a table of 16-bit ints, bail out if that's not enough */
|
2008-07-30 04:17:22 -07:00
|
|
|
return false;
|
|
|
|
jsval* vp = &STOBJ_GET_SLOT(globalObj, slot);
|
|
|
|
if (tracker.has(vp))
|
|
|
|
return true; /* we already have it */
|
2008-08-19 11:32:36 -07:00
|
|
|
unsigned index = traceMonitor->globalSlots->length();
|
2008-08-16 21:05:03 -07:00
|
|
|
/* If this the first global we are adding, remember the shape of the global object. */
|
|
|
|
if (index == 0)
|
2008-09-09 09:57:10 -07:00
|
|
|
traceMonitor->globalShape = OBJ_SHAPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain));
|
2008-08-16 21:05:03 -07:00
|
|
|
/* Add the slot to the list of interned global slots. */
|
2008-08-19 11:32:36 -07:00
|
|
|
traceMonitor->globalSlots->add(slot);
|
2008-08-07 15:28:43 -07:00
|
|
|
uint8 type = getCoercedType(*vp);
|
2008-09-10 12:20:01 -07:00
|
|
|
if ((type == JSVAL_INT) && oracle.isGlobalSlotUndemotable(cx->fp->script, slot))
|
2008-08-07 15:28:43 -07:00
|
|
|
type = JSVAL_DOUBLE;
|
2008-08-19 11:32:36 -07:00
|
|
|
traceMonitor->globalTypeMap->add(type);
|
2008-08-12 02:00:05 -07:00
|
|
|
import(gp_ins, slot*sizeof(double), vp, type, "global", index, NULL);
|
2008-07-30 04:17:22 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-08-15 14:47:49 -07:00
|
|
|
/* Write back a value onto the stack or global frames. */
|
|
|
|
LIns*
|
|
|
|
TraceRecorder::writeBack(LIns* i, LIns* base, ptrdiff_t offset)
|
|
|
|
{
|
|
|
|
/* Sink all type casts targeting the stack into the side exit by simply storing the original
|
2008-08-19 20:30:16 -07:00
|
|
|
(uncasted) value. Each guard generates the side exit map based on the types of the
|
|
|
|
last stores to every stack location, so its safe to not perform them on-trace. */
|
2008-08-19 23:31:33 -07:00
|
|
|
if (isPromoteInt(i))
|
2008-08-19 20:30:16 -07:00
|
|
|
i = ::demote(lir, i);
|
|
|
|
return lir->insStorei(i, base, offset);
|
2008-08-15 14:47:49 -07:00
|
|
|
}
|
|
|
|
|
2008-07-21 12:57:02 -07:00
|
|
|
/* Update the tracker, then issue a write back store. */
|
2008-07-05 10:41:35 -07:00
|
|
|
void
|
2008-07-21 12:57:02 -07:00
|
|
|
TraceRecorder::set(jsval* p, LIns* i, bool initializing)
|
2008-06-26 22:41:10 -07:00
|
|
|
{
|
2008-07-21 12:57:02 -07:00
|
|
|
JS_ASSERT(initializing || tracker.has(p));
|
2008-06-30 09:36:10 -07:00
|
|
|
tracker.set(p, i);
|
2008-07-22 17:54:04 -07:00
|
|
|
/* If we are writing to this location for the first time, calculate the offset into the
|
|
|
|
native frame manually, otherwise just look up the last load or store associated with
|
|
|
|
the same source address (p) and use the same offset/base. */
|
2008-09-05 18:29:08 -07:00
|
|
|
LIns* x = nativeFrameTracker.get(p);
|
|
|
|
if (!x) {
|
2008-08-18 15:32:17 -07:00
|
|
|
if (isGlobal(p))
|
2008-08-15 14:47:49 -07:00
|
|
|
x = writeBack(i, gp_ins, nativeGlobalOffset(p));
|
|
|
|
else
|
|
|
|
x = writeBack(i, lirbuf->sp, -treeInfo->nativeStackBase + nativeStackOffset(p));
|
2008-08-06 15:04:02 -07:00
|
|
|
nativeFrameTracker.set(p, x);
|
2008-07-22 17:54:04 -07:00
|
|
|
} else {
|
2008-07-23 23:18:02 -07:00
|
|
|
#define ASSERT_VALID_CACHE_HIT(base, offset) \
|
|
|
|
JS_ASSERT(base == lirbuf->sp || base == gp_ins); \
|
|
|
|
JS_ASSERT(offset == ((base == lirbuf->sp) \
|
2008-08-06 15:04:02 -07:00
|
|
|
? -treeInfo->nativeStackBase + nativeStackOffset(p) \
|
2008-07-23 23:18:02 -07:00
|
|
|
: nativeGlobalOffset(p))); \
|
|
|
|
|
2008-08-12 23:30:49 -07:00
|
|
|
if (x->isop(LIR_st) || x->isop(LIR_stq)) {
|
2008-07-23 23:18:02 -07:00
|
|
|
ASSERT_VALID_CACHE_HIT(x->oprnd2(), x->oprnd3()->constval());
|
2008-08-15 14:47:49 -07:00
|
|
|
writeBack(i, x->oprnd2(), x->oprnd3()->constval());
|
2008-07-22 17:54:04 -07:00
|
|
|
} else {
|
2008-07-23 02:57:56 -07:00
|
|
|
JS_ASSERT(x->isop(LIR_sti) || x->isop(LIR_stqi));
|
2008-07-23 23:18:02 -07:00
|
|
|
ASSERT_VALID_CACHE_HIT(x->oprnd2(), x->immdisp());
|
2008-08-15 14:47:49 -07:00
|
|
|
writeBack(i, x->oprnd2(), x->immdisp());
|
2008-07-22 17:54:04 -07:00
|
|
|
}
|
|
|
|
}
|
2008-07-25 16:23:12 -07:00
|
|
|
#undef ASSERT_VALID_CACHE_HIT
|
2008-06-26 22:41:10 -07:00
|
|
|
}
|
|
|
|
|
2008-07-05 10:41:35 -07:00
|
|
|
LIns*
|
2008-09-09 06:06:23 -07:00
|
|
|
TraceRecorder::get(jsval* p) const
|
2008-06-26 22:41:10 -07:00
|
|
|
{
|
|
|
|
return tracker.get(p);
|
|
|
|
}
|
|
|
|
|
2008-10-08 21:02:04 -07:00
|
|
|
/* Determine whether the current branch instruction terminates the loop. */
|
2008-07-25 01:44:40 -07:00
|
|
|
static bool
|
2008-10-08 21:02:04 -07:00
|
|
|
js_IsLoopExit(jsbytecode* pc, jsbytecode* header)
|
2008-07-25 01:44:40 -07:00
|
|
|
{
|
|
|
|
switch (*pc) {
|
|
|
|
case JSOP_LT:
|
|
|
|
case JSOP_GT:
|
|
|
|
case JSOP_LE:
|
|
|
|
case JSOP_GE:
|
|
|
|
case JSOP_NE:
|
|
|
|
case JSOP_EQ:
|
2008-09-02 22:29:06 -07:00
|
|
|
/* These ops try to dispatch a JSOP_IFEQ or JSOP_IFNE that follows. */
|
2008-07-25 01:44:40 -07:00
|
|
|
JS_ASSERT(js_CodeSpec[*pc].length == 1);
|
|
|
|
pc++;
|
2008-09-02 22:29:06 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
for (;;) {
|
|
|
|
if (*pc == JSOP_AND || *pc == JSOP_OR)
|
|
|
|
pc += GET_JUMP_OFFSET(pc);
|
|
|
|
else if (*pc == JSOP_ANDX || *pc == JSOP_ORX)
|
|
|
|
pc += GET_JUMPX_OFFSET(pc);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-07-25 01:44:40 -07:00
|
|
|
|
2008-09-02 22:29:06 -07:00
|
|
|
switch (*pc) {
|
2008-08-20 18:50:49 -07:00
|
|
|
case JSOP_IFEQ:
|
2008-07-25 01:44:40 -07:00
|
|
|
case JSOP_IFNE:
|
2008-08-20 18:50:49 -07:00
|
|
|
/*
|
2008-09-02 22:29:06 -07:00
|
|
|
* Forward jumps are usually intra-branch, but for-in loops jump to the
|
|
|
|
* trailing enditer to clean up, so check for that case here.
|
2008-08-20 18:50:49 -07:00
|
|
|
*/
|
|
|
|
if (pc[GET_JUMP_OFFSET(pc)] == JSOP_ENDITER)
|
|
|
|
return true;
|
2008-08-26 21:43:59 -07:00
|
|
|
return pc + GET_JUMP_OFFSET(pc) == header;
|
2008-07-25 01:44:40 -07:00
|
|
|
|
2008-09-02 22:29:06 -07:00
|
|
|
case JSOP_IFEQX:
|
|
|
|
case JSOP_IFNEX:
|
|
|
|
if (pc[GET_JUMPX_OFFSET(pc)] == JSOP_ENDITER)
|
|
|
|
return true;
|
|
|
|
return pc + GET_JUMPX_OFFSET(pc) == header;
|
|
|
|
|
2008-07-25 01:44:40 -07:00
|
|
|
default:;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-10-08 21:02:04 -07:00
|
|
|
/* Determine whether the current branch is a loop edge (taken or not taken). */
|
|
|
|
static bool
|
|
|
|
js_IsLoopEdge(jsbytecode* pc, jsbytecode* header)
|
|
|
|
{
|
|
|
|
switch (*pc) {
|
|
|
|
case JSOP_IFEQ:
|
|
|
|
case JSOP_IFNE:
|
|
|
|
return ((pc + GET_JUMP_OFFSET(pc)) == header);
|
|
|
|
case JSOP_IFEQX:
|
|
|
|
case JSOP_IFNEX:
|
|
|
|
return ((pc + GET_JUMPX_OFFSET(pc)) == header);
|
|
|
|
default:
|
|
|
|
JS_ASSERT((*pc == JSOP_AND) || (*pc == JSOP_ANDX) ||
|
|
|
|
(*pc == JSOP_OR) || (*pc == JSOP_ORX));
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-08-20 00:51:56 -07:00
|
|
|
/* Promote slots if necessary to match the called tree' type map and report error if thats
|
|
|
|
impossible. */
|
|
|
|
bool
|
|
|
|
TraceRecorder::adjustCallerTypes(Fragment* f)
|
|
|
|
{
|
|
|
|
JSTraceMonitor* tm = traceMonitor;
|
|
|
|
uint8* m = tm->globalTypeMap->data();
|
|
|
|
uint16* gslots = traceMonitor->globalSlots->data();
|
|
|
|
unsigned ngslots = traceMonitor->globalSlots->length();
|
2008-08-22 17:31:23 -07:00
|
|
|
JSScript* script = ((TreeInfo*)f->vmprivate)->script;
|
|
|
|
uint8* map = ((TreeInfo*)f->vmprivate)->stackTypeMap.data();
|
|
|
|
bool ok = true;
|
2008-08-20 00:51:56 -07:00
|
|
|
FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
|
|
|
|
LIns* i = get(vp);
|
|
|
|
bool isPromote = isPromoteInt(i);
|
|
|
|
if (isPromote && *m == JSVAL_DOUBLE)
|
|
|
|
lir->insStorei(get(vp), gp_ins, nativeGlobalOffset(vp));
|
2008-08-22 17:31:23 -07:00
|
|
|
else if (!isPromote && *m == JSVAL_INT) {
|
2008-09-10 12:20:01 -07:00
|
|
|
oracle.markGlobalSlotUndemotable(script, nativeGlobalOffset(vp)/sizeof(double));
|
2008-08-22 17:31:23 -07:00
|
|
|
ok = false;
|
|
|
|
}
|
2008-08-20 00:51:56 -07:00
|
|
|
++m;
|
|
|
|
);
|
2008-08-22 17:31:23 -07:00
|
|
|
m = map;
|
2008-08-20 12:47:39 -07:00
|
|
|
FORALL_SLOTS_IN_PENDING_FRAMES(cx, 0,
|
2008-08-20 00:51:56 -07:00
|
|
|
LIns* i = get(vp);
|
|
|
|
bool isPromote = isPromoteInt(i);
|
|
|
|
if (isPromote && *m == JSVAL_DOUBLE)
|
|
|
|
lir->insStorei(get(vp), lirbuf->sp,
|
|
|
|
-treeInfo->nativeStackBase + nativeStackOffset(vp));
|
2008-08-22 17:31:23 -07:00
|
|
|
else if (!isPromote && *m == JSVAL_INT) {
|
2008-09-10 12:20:01 -07:00
|
|
|
oracle.markStackSlotUndemotable(script, (jsbytecode*)f->ip, unsigned(m - map));
|
2008-08-22 17:31:23 -07:00
|
|
|
ok = false;
|
|
|
|
}
|
2008-08-20 00:51:56 -07:00
|
|
|
++m;
|
|
|
|
);
|
2008-08-27 17:25:56 -07:00
|
|
|
JS_ASSERT(f == f->root);
|
2008-08-28 14:24:58 -07:00
|
|
|
if (!ok) {
|
2008-08-27 17:25:56 -07:00
|
|
|
trashTree = true;
|
2008-08-28 14:24:58 -07:00
|
|
|
whichTreeToTrash = f;
|
|
|
|
}
|
2008-08-22 17:31:23 -07:00
|
|
|
return ok;
|
2008-08-20 00:51:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Find a peer fragment that we can call, considering our current type distribution. */
|
|
|
|
bool TraceRecorder::selectCallablePeerFragment(Fragment** first)
|
|
|
|
{
|
|
|
|
/* Until we have multiple trees per start point this is always the first fragment. */
|
|
|
|
return (*first)->code();
|
|
|
|
}
|
|
|
|
|
2008-09-09 06:06:23 -07:00
|
|
|
uint8
|
|
|
|
TraceRecorder::determineSlotType(jsval* vp) const
|
|
|
|
{
|
|
|
|
uint8 m;
|
|
|
|
LIns* i = get(vp);
|
|
|
|
m = isNumber(*vp)
|
|
|
|
? (isPromoteInt(i) ? JSVAL_INT : JSVAL_DOUBLE)
|
|
|
|
: JSVAL_TAG(*vp);
|
|
|
|
JS_ASSERT((m != JSVAL_INT) || isInt32(*vp));
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2008-10-21 17:50:32 -07:00
|
|
|
LIns*
|
2008-07-27 16:18:53 -07:00
|
|
|
TraceRecorder::snapshot(ExitType exitType)
|
2008-07-01 19:43:10 -07:00
|
|
|
{
|
2008-08-01 08:26:32 -07:00
|
|
|
JSStackFrame* fp = cx->fp;
|
2008-10-08 21:02:04 -07:00
|
|
|
if (exitType == BRANCH_EXIT && js_IsLoopExit(cx->fp->regs->pc, (jsbytecode*)fragment->root->ip))
|
2008-07-27 16:18:53 -07:00
|
|
|
exitType = LOOP_EXIT;
|
2008-08-18 18:36:05 -07:00
|
|
|
/* Generate the entry map and stash it in the trace. */
|
2008-08-25 16:04:10 -07:00
|
|
|
unsigned stackSlots = js_NativeStackSlots(cx, callDepth);
|
2008-08-18 18:36:05 -07:00
|
|
|
/* It's sufficient to track the native stack use here since all stores above the
|
2008-08-06 15:04:02 -07:00
|
|
|
stack watermark defined by guards are killed. */
|
|
|
|
trackNativeStackUse(stackSlots + 1);
|
2008-10-22 16:27:25 -07:00
|
|
|
/* Capture the type map into a temporary location. */
|
2008-08-19 11:32:36 -07:00
|
|
|
unsigned ngslots = traceMonitor->globalSlots->length();
|
2008-10-22 16:27:25 -07:00
|
|
|
unsigned typemap_size = (stackSlots + ngslots) * sizeof(uint8);
|
|
|
|
uint8* typemap = (uint8*)alloca(typemap_size);
|
|
|
|
uint8* m = typemap;
|
|
|
|
/* 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. */
|
|
|
|
FORALL_SLOTS(cx, ngslots, traceMonitor->globalSlots->data(), callDepth,
|
|
|
|
*m++ = determineSlotType(vp);
|
|
|
|
);
|
|
|
|
JS_ASSERT(unsigned(m - typemap) == ngslots + stackSlots);
|
|
|
|
/* If we are capturing the stack state on a JSOP_RESUME instruction, the value on top of
|
|
|
|
the stack is a boxed value. */
|
|
|
|
jsbytecode* pc = fp->regs->pc;
|
|
|
|
if (*pc == JSOP_RESUME)
|
|
|
|
m[-1] = JSVAL_BOXED;
|
|
|
|
/* If we take a snapshot on a goto, advance to the target address. This avoids inner
|
|
|
|
trees returning on a break goto, which the outer recorder then would confuse with
|
|
|
|
a break in the outer tree. */
|
|
|
|
if (*pc == JSOP_GOTO)
|
|
|
|
pc += GET_JUMP_OFFSET(pc);
|
|
|
|
else if (*pc == JSOP_GOTOX)
|
|
|
|
pc += GET_JUMPX_OFFSET(pc);
|
|
|
|
int ip_adj = pc - (jsbytecode*)fragment->root->ip;
|
|
|
|
/* Check if we already have a matching side exit. If so use that side exit structure,
|
|
|
|
otherwise we have to create our own. */
|
|
|
|
SideExit** exits = treeInfo->sideExits.data();
|
|
|
|
unsigned nexits = treeInfo->sideExits.length();
|
|
|
|
for (unsigned n = 0; n < nexits; ++n) {
|
|
|
|
SideExit* e = exits[n];
|
|
|
|
if (exitType == LOOP_EXIT &&
|
|
|
|
e->exitType == exitType &&
|
|
|
|
e->ip_adj == ip_adj &&
|
|
|
|
!memcmp(getTypeMap(exits[n]), typemap, typemap_size)) {
|
|
|
|
LIns* data = lir_buf_writer->skip(sizeof(GuardRecord));
|
|
|
|
GuardRecord* rec = (GuardRecord*)data->payload();
|
|
|
|
/* setup guard record structure with shared side exit */
|
|
|
|
memset(rec, 0, sizeof(GuardRecord));
|
|
|
|
SideExit* exit = exits[n];
|
|
|
|
rec->exit = exit;
|
|
|
|
exit->addGuard(rec);
|
|
|
|
AUDIT(mergedLoopExits);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* We couldn't find a matching side exit, so create our own side exit structure. */
|
2008-10-21 17:50:32 -07:00
|
|
|
LIns* data = lir_buf_writer->skip(sizeof(GuardRecord) +
|
|
|
|
sizeof(SideExit) +
|
|
|
|
(stackSlots + ngslots) * sizeof(uint8));
|
|
|
|
GuardRecord* rec = (GuardRecord*)data->payload();
|
|
|
|
SideExit* exit = (SideExit*)(rec + 1);
|
|
|
|
/* setup guard record structure */
|
|
|
|
memset(rec, 0, sizeof(GuardRecord));
|
|
|
|
rec->exit = exit;
|
2008-07-01 19:43:10 -07:00
|
|
|
/* setup side exit structure */
|
2008-10-21 17:50:32 -07:00
|
|
|
memset(exit, 0, sizeof(SideExit));
|
|
|
|
exit->from = fragment;
|
|
|
|
exit->calldepth = callDepth;
|
|
|
|
exit->numGlobalSlots = ngslots;
|
|
|
|
exit->numStackSlots = stackSlots;
|
|
|
|
exit->numStackSlotsBelowCurrentFrame = cx->fp->callee
|
2008-08-26 13:25:40 -07:00
|
|
|
? nativeStackOffset(&cx->fp->argv[-2])/sizeof(double)
|
|
|
|
: 0;
|
2008-10-21 17:50:32 -07:00
|
|
|
exit->exitType = exitType;
|
2008-10-22 16:00:08 -07:00
|
|
|
exit->addGuard(rec);
|
2008-10-22 16:27:25 -07:00
|
|
|
exit->ip_adj = ip_adj;
|
2008-10-21 17:50:32 -07:00
|
|
|
exit->sp_adj = (stackSlots * sizeof(double)) - treeInfo->nativeStackBase;
|
|
|
|
exit->rp_adj = exit->calldepth * sizeof(FrameInfo);
|
2008-10-22 16:27:25 -07:00
|
|
|
memcpy(getTypeMap(exit), typemap, typemap_size);
|
|
|
|
/* BIG FAT WARNING: If compilation fails, we currently don't reset the lirbuf so its safe
|
|
|
|
to keep references to the side exits here. If we ever start rewinding those lirbufs,
|
|
|
|
we have to make sure we purge the side exits that then no longer will be in valid
|
|
|
|
memory. */
|
|
|
|
treeInfo->sideExits.add(exit);
|
2008-10-21 17:50:32 -07:00
|
|
|
return data;
|
2008-07-01 19:43:10 -07:00
|
|
|
}
|
|
|
|
|
2008-07-30 13:34:22 -07:00
|
|
|
/* Emit a guard for condition (cond), expecting to evaluate to boolean result (expected). */
|
2008-07-31 06:35:11 -07:00
|
|
|
LIns*
|
2008-07-27 16:18:53 -07:00
|
|
|
TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType)
|
2008-06-27 00:22:16 -07:00
|
|
|
{
|
2008-07-31 06:35:11 -07:00
|
|
|
return lir->insGuard(expected ? LIR_xf : LIR_xt,
|
|
|
|
cond,
|
|
|
|
snapshot(exitType));
|
2008-07-17 02:00:23 -07:00
|
|
|
}
|
|
|
|
|
2008-07-30 13:34:22 -07:00
|
|
|
/* Try to match the type of a slot to type t. checkType is used to verify that the type of
|
|
|
|
values flowing into the loop edge is compatible with the type we expect in the loop header. */
|
2008-07-05 23:21:53 -07:00
|
|
|
bool
|
2008-08-07 15:28:43 -07:00
|
|
|
TraceRecorder::checkType(jsval& v, uint8 t, bool& unstable)
|
2008-07-05 23:21:53 -07:00
|
|
|
{
|
2008-07-27 02:15:17 -07:00
|
|
|
if (t == JSVAL_INT) { /* initially all whole numbers cause the slot to be demoted */
|
|
|
|
if (!isNumber(v))
|
|
|
|
return false; /* not a number? type mismatch */
|
2008-07-06 17:32:21 -07:00
|
|
|
LIns* i = get(&v);
|
2008-09-02 22:29:23 -07:00
|
|
|
if (!isi2f(i)) {
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("int slot is !isInt32, slot #%d, triggering re-compilation\n",
|
|
|
|
!isGlobal(&v)
|
|
|
|
? nativeStackOffset(&v)
|
|
|
|
: nativeGlobalOffset(&v)););
|
2008-07-27 02:15:17 -07:00
|
|
|
AUDIT(slotPromoted);
|
2008-08-07 15:28:43 -07:00
|
|
|
unstable = true;
|
2008-07-27 02:15:17 -07:00
|
|
|
return true; /* keep checking types, but request re-compilation */
|
2008-07-06 17:32:21 -07:00
|
|
|
}
|
2008-08-20 15:43:26 -07:00
|
|
|
/* Looks good, slot is an int32, the last instruction should be i2f. */
|
2008-09-02 22:29:23 -07:00
|
|
|
JS_ASSERT(isInt32(v) && (i->isop(LIR_i2f) || i->isop(LIR_qjoin)));
|
2008-08-20 15:43:26 -07:00
|
|
|
/* We got the final LIR_i2f as we expected. Overwrite the value in that
|
2008-07-06 22:35:19 -07:00
|
|
|
slot with the argument of i2f since we want the integer store to flow along
|
|
|
|
the loop edge, not the casted value. */
|
2008-09-02 22:29:23 -07:00
|
|
|
set(&v, iu2fArg(i));
|
2008-07-05 23:21:53 -07:00
|
|
|
return true;
|
2008-07-06 17:32:21 -07:00
|
|
|
}
|
2008-08-20 15:43:26 -07:00
|
|
|
if (t == JSVAL_DOUBLE) {
|
|
|
|
if (!isNumber(v))
|
|
|
|
return false; /* not a number? type mismatch */
|
|
|
|
LIns* i = get(&v);
|
|
|
|
/* We sink i2f conversions into the side exit, but at the loop edge we have to make
|
|
|
|
sure we promote back to double if at loop entry we want a double. */
|
|
|
|
if (isPromoteInt(i))
|
|
|
|
set(&v, lir->ins1(LIR_i2f, i));
|
|
|
|
return true;
|
|
|
|
}
|
2008-07-06 22:35:19 -07:00
|
|
|
/* for non-number types we expect a precise match of the type */
|
2008-07-07 20:51:56 -07:00
|
|
|
#ifdef DEBUG
|
2008-07-27 02:15:17 -07:00
|
|
|
if (JSVAL_TAG(v) != t) {
|
2008-10-06 16:22:57 -07:00
|
|
|
debug_only_v(printf("Type mismatch: val %c, map %c ", typeChar[JSVAL_TAG(v)],
|
|
|
|
typeChar[t]););
|
2008-07-07 20:51:56 -07:00
|
|
|
}
|
|
|
|
#endif
|
2008-07-27 02:15:17 -07:00
|
|
|
return JSVAL_TAG(v) == t;
|
2008-07-05 23:21:53 -07:00
|
|
|
}
|
|
|
|
|
2008-07-06 22:35:19 -07:00
|
|
|
/* Make sure that the current values in the given stack frame and all stack frames
|
|
|
|
up and including entryFrame are type-compatible with the entry map. */
|
2008-07-05 23:21:53 -07:00
|
|
|
bool
|
2008-07-30 04:17:22 -07:00
|
|
|
TraceRecorder::verifyTypeStability()
|
2008-07-05 23:21:53 -07:00
|
|
|
{
|
2008-08-19 11:32:36 -07:00
|
|
|
unsigned ngslots = traceMonitor->globalSlots->length();
|
|
|
|
uint16* gslots = traceMonitor->globalSlots->data();
|
|
|
|
uint8* typemap = traceMonitor->globalTypeMap->data();
|
|
|
|
JS_ASSERT(traceMonitor->globalTypeMap->length() == ngslots);
|
2008-08-06 13:37:29 -07:00
|
|
|
bool recompile = false;
|
2008-08-07 15:28:43 -07:00
|
|
|
uint8* m = typemap;
|
2008-07-30 16:32:33 -07:00
|
|
|
FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
|
2008-08-07 15:28:43 -07:00
|
|
|
bool demote = false;
|
|
|
|
if (!checkType(*vp, *m, demote))
|
2008-07-30 04:17:22 -07:00
|
|
|
return false;
|
2008-08-07 15:28:43 -07:00
|
|
|
if (demote) {
|
2008-09-10 12:20:01 -07:00
|
|
|
oracle.markGlobalSlotUndemotable(cx->fp->script, gslots[n]);
|
2008-08-07 15:28:43 -07:00
|
|
|
recompile = true;
|
|
|
|
}
|
2008-07-30 04:17:22 -07:00
|
|
|
++m
|
|
|
|
);
|
2008-08-07 15:28:43 -07:00
|
|
|
typemap = treeInfo->stackTypeMap.data();
|
|
|
|
m = typemap;
|
2008-08-14 00:45:39 -07:00
|
|
|
FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
|
2008-08-07 15:28:43 -07:00
|
|
|
bool demote = false;
|
|
|
|
if (!checkType(*vp, *m, demote))
|
2008-07-05 23:21:53 -07:00
|
|
|
return false;
|
2008-08-07 15:28:43 -07:00
|
|
|
if (demote) {
|
2008-09-10 12:20:01 -07:00
|
|
|
oracle.markStackSlotUndemotable(cx->fp->script, (jsbytecode*)fragment->ip,
|
2008-08-07 15:28:43 -07:00
|
|
|
unsigned(m - typemap));
|
|
|
|
recompile = true;
|
|
|
|
}
|
2008-07-08 16:29:23 -07:00
|
|
|
++m
|
|
|
|
);
|
2008-08-27 17:25:56 -07:00
|
|
|
if (recompile)
|
|
|
|
trashTree = true;
|
2008-08-06 13:37:29 -07:00
|
|
|
return !recompile;
|
2008-07-05 23:21:53 -07:00
|
|
|
}
|
|
|
|
|
2008-07-30 13:34:22 -07:00
|
|
|
/* Check whether the current pc location is the loop header of the loop this recorder records. */
|
2008-07-27 14:28:56 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::isLoopHeader(JSContext* cx) const
|
|
|
|
{
|
|
|
|
return cx->fp->regs->pc == fragment->root->ip;
|
|
|
|
}
|
|
|
|
|
2008-08-25 15:17:46 -07:00
|
|
|
/* Compile the current fragment. */
|
2008-06-27 18:06:50 -07:00
|
|
|
void
|
2008-08-25 15:17:46 -07:00
|
|
|
TraceRecorder::compile(Fragmento* fragmento)
|
2008-06-27 18:06:50 -07:00
|
|
|
{
|
2008-08-12 21:39:44 -07:00
|
|
|
if (treeInfo->maxNativeStackSlots >= MAX_NATIVE_STACK_SLOTS) {
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("Trace rejected: excessive stack use.\n"));
|
2008-08-12 21:39:44 -07:00
|
|
|
fragment->blacklist();
|
|
|
|
return;
|
|
|
|
}
|
2008-09-02 23:52:11 -07:00
|
|
|
++treeInfo->branchCount;
|
2008-08-25 15:17:46 -07:00
|
|
|
::compile(fragmento->assm(), fragment);
|
2008-10-22 16:00:08 -07:00
|
|
|
if (anchor)
|
2008-07-17 17:12:28 -07:00
|
|
|
fragmento->assm()->patch(anchor);
|
2008-08-16 23:43:49 -07:00
|
|
|
JS_ASSERT(fragment->code());
|
|
|
|
JS_ASSERT(!fragment->vmprivate);
|
2008-08-18 15:32:17 -07:00
|
|
|
if (fragment == fragment->root)
|
2008-08-16 23:43:49 -07:00
|
|
|
fragment->vmprivate = treeInfo;
|
2008-08-25 15:17:46 -07:00
|
|
|
/* :TODO: windows support */
|
2008-07-29 17:19:51 -07:00
|
|
|
#if defined DEBUG && !defined WIN32
|
2008-08-11 11:37:35 -07:00
|
|
|
char* label = (char*)malloc(strlen(cx->fp->script->filename) + 64);
|
|
|
|
sprintf(label, "%s:%u", cx->fp->script->filename,
|
|
|
|
js_PCToLineNumber(cx, cx->fp->script, cx->fp->regs->pc));
|
2008-07-17 16:20:13 -07:00
|
|
|
fragmento->labels->add(fragment, sizeof(Fragment), 0, label);
|
2008-08-11 16:01:21 -07:00
|
|
|
free(label);
|
2008-07-19 00:15:22 -07:00
|
|
|
#endif
|
2008-08-29 15:29:55 -07:00
|
|
|
AUDIT(traceCompleted);
|
2008-06-27 18:06:50 -07:00
|
|
|
}
|
|
|
|
|
2008-08-25 15:17:46 -07:00
|
|
|
/* Complete and compile a trace and link it to the existing tree if appropriate. */
|
|
|
|
void
|
|
|
|
TraceRecorder::closeLoop(Fragmento* fragmento)
|
|
|
|
{
|
|
|
|
if (!verifyTypeStability()) {
|
|
|
|
AUDIT(unstableLoopVariable);
|
|
|
|
debug_only_v(printf("Trace rejected: unstable loop variables.\n");)
|
2008-10-08 19:58:40 -07:00
|
|
|
if (!trashTree)
|
|
|
|
fragment->blacklist();
|
2008-08-25 15:17:46 -07:00
|
|
|
return;
|
|
|
|
}
|
2008-10-21 17:50:32 -07:00
|
|
|
LIns* skip = snapshot(LOOP_EXIT);
|
|
|
|
((GuardRecord*)skip->payload())->exit->target = fragment->root;
|
2008-10-22 16:00:08 -07:00
|
|
|
fragment->lastIns = lir->insGuard(LIR_loop, lir->insImm(1), skip);
|
2008-08-25 15:17:46 -07:00
|
|
|
compile(fragmento);
|
2008-09-19 16:09:18 -07:00
|
|
|
|
2008-09-24 13:12:25 -07:00
|
|
|
debug_only_v(printf("recording completed at %s:%u@%u via closeLoop\n", cx->fp->script->filename,
|
2008-09-19 16:09:18 -07:00
|
|
|
js_PCToLineNumber(cx, cx->fp->script, cx->fp->regs->pc),
|
|
|
|
cx->fp->regs->pc - cx->fp->script->code););
|
2008-08-25 15:17:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Emit an always-exit guard and compile the tree (used for break statements. */
|
|
|
|
void
|
|
|
|
TraceRecorder::endLoop(Fragmento* fragmento)
|
|
|
|
{
|
2008-10-21 17:50:32 -07:00
|
|
|
fragment->lastIns = lir->insGuard(LIR_x, lir->insImm(1), snapshot(LOOP_EXIT));
|
2008-08-25 15:17:46 -07:00
|
|
|
compile(fragmento);
|
2008-09-24 13:12:25 -07:00
|
|
|
|
|
|
|
debug_only_v(printf("recording completed at %s:%u@%u via endLoop\n", cx->fp->script->filename,
|
|
|
|
js_PCToLineNumber(cx, cx->fp->script, cx->fp->regs->pc),
|
|
|
|
cx->fp->regs->pc - cx->fp->script->code););
|
2008-08-25 15:17:46 -07:00
|
|
|
}
|
|
|
|
|
2008-08-19 13:11:18 -07:00
|
|
|
/* Emit code to adjust the stack to match the inner tree's stack expectations. */
|
2008-08-06 16:24:29 -07:00
|
|
|
void
|
2008-08-20 00:51:56 -07:00
|
|
|
TraceRecorder::prepareTreeCall(Fragment* inner)
|
2008-08-06 16:24:29 -07:00
|
|
|
{
|
2008-08-12 23:25:17 -07:00
|
|
|
TreeInfo* ti = (TreeInfo*)inner->vmprivate;
|
2008-08-19 13:11:18 -07:00
|
|
|
inner_sp_ins = lirbuf->sp;
|
2008-08-17 12:04:33 -07:00
|
|
|
/* The inner tree expects to be called from the current frame. If the outer tree (this
|
|
|
|
trace) is currently inside a function inlining code (calldepth > 0), we have to advance
|
2008-08-08 18:52:08 -07:00
|
|
|
the native stack pointer such that we match what the inner trace expects to see. We
|
|
|
|
move it back when we come out of the inner tree call. */
|
|
|
|
if (callDepth > 0) {
|
2008-08-12 23:25:17 -07:00
|
|
|
/* Calculate the amount we have to lift the native stack pointer by to compensate for
|
|
|
|
any outer frames that the inner tree doesn't expect but the outer tree has. */
|
2008-08-20 19:24:57 -07:00
|
|
|
ptrdiff_t sp_adj = nativeStackOffset(&cx->fp->argv[-2]);
|
2008-08-13 13:51:59 -07:00
|
|
|
/* Calculate the amount we have to lift the call stack by */
|
2008-08-13 15:50:11 -07:00
|
|
|
ptrdiff_t rp_adj = callDepth * sizeof(FrameInfo);
|
2008-08-12 23:25:17 -07:00
|
|
|
/* Guard that we have enough stack space for the tree we are trying to call on top
|
|
|
|
of the new value for sp. */
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("sp_adj=%d outer=%d inner=%d\n",
|
2008-08-18 18:36:05 -07:00
|
|
|
sp_adj, treeInfo->nativeStackBase, ti->nativeStackBase));
|
2008-08-18 16:38:32 -07:00
|
|
|
LIns* sp_top = lir->ins2i(LIR_piadd, lirbuf->sp,
|
2008-08-14 17:52:11 -07:00
|
|
|
- treeInfo->nativeStackBase /* rebase sp to beginning of outer tree's stack */
|
|
|
|
+ sp_adj /* adjust for stack in outer frame inner tree can't see */
|
|
|
|
+ ti->maxNativeStackSlots * sizeof(double)); /* plus the inner tree's stack */
|
2008-08-12 23:25:17 -07:00
|
|
|
guard(true, lir->ins2(LIR_lt, sp_top, eos_ins), OOM_EXIT);
|
2008-08-13 13:51:59 -07:00
|
|
|
/* Guard that we have enough call stack space. */
|
2008-08-18 16:38:32 -07:00
|
|
|
LIns* rp_top = lir->ins2i(LIR_piadd, lirbuf->rp, rp_adj +
|
2008-08-13 13:51:59 -07:00
|
|
|
ti->maxCallDepth * sizeof(FrameInfo));
|
|
|
|
guard(true, lir->ins2(LIR_lt, rp_top, eor_ins), OOM_EXIT);
|
|
|
|
/* We have enough space, so adjust sp and rp to their new level. */
|
2008-08-19 13:11:18 -07:00
|
|
|
lir->insStorei(inner_sp_ins = lir->ins2i(LIR_piadd, lirbuf->sp,
|
2008-08-14 17:52:11 -07:00
|
|
|
- treeInfo->nativeStackBase /* rebase sp to beginning of outer tree's stack */
|
|
|
|
+ sp_adj /* adjust for stack in outer frame inner tree can't see */
|
2008-08-18 15:32:17 -07:00
|
|
|
+ ti->nativeStackBase), /* plus the inner tree's stack base */
|
2008-08-08 18:52:08 -07:00
|
|
|
lirbuf->state, offsetof(InterpState, sp));
|
2008-08-18 16:23:37 -07:00
|
|
|
lir->insStorei(lir->ins2i(LIR_piadd, lirbuf->rp, rp_adj),
|
2008-08-13 13:51:59 -07:00
|
|
|
lirbuf->state, offsetof(InterpState, rp));
|
2008-08-08 18:52:08 -07:00
|
|
|
}
|
2008-08-19 13:11:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Record a call to an inner tree. */
|
|
|
|
void
|
2008-10-21 17:50:32 -07:00
|
|
|
TraceRecorder::emitTreeCall(Fragment* inner, SideExit* exit)
|
2008-08-19 13:11:18 -07:00
|
|
|
{
|
|
|
|
TreeInfo* ti = (TreeInfo*)inner->vmprivate;
|
2008-08-08 18:52:08 -07:00
|
|
|
/* Invoke the inner tree. */
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
LIns* args[] = { INS_CONSTPTR(inner), lirbuf->state }; /* reverse order */
|
2008-10-16 12:24:10 -07:00
|
|
|
LIns* ret = lir->insCall(&js_CallTree_ci, args);
|
2008-08-12 20:18:29 -07:00
|
|
|
/* Read back all registers, in case the called tree changed any of them. */
|
2008-08-19 13:11:18 -07:00
|
|
|
import(ti, inner_sp_ins, exit->numGlobalSlots, exit->calldepth,
|
2008-10-21 17:50:32 -07:00
|
|
|
getTypeMap(exit), getTypeMap(exit) + exit->numGlobalSlots);
|
2008-08-13 19:09:05 -07:00
|
|
|
/* Restore sp and rp to their original values (we still have them in a register). */
|
|
|
|
if (callDepth > 0) {
|
|
|
|
lir->insStorei(lirbuf->sp, lirbuf->state, offsetof(InterpState, sp));
|
|
|
|
lir->insStorei(lirbuf->rp, lirbuf->state, offsetof(InterpState, rp));
|
|
|
|
}
|
2008-08-20 22:23:17 -07:00
|
|
|
/* Guard that we come out of the inner tree along the same side exit we came out when
|
|
|
|
we called the inner tree at recording time. */
|
2008-10-21 17:50:32 -07:00
|
|
|
guard(true, lir->ins2(LIR_eq, ret, INS_CONSTPTR(exit)), NESTED_EXIT);
|
2008-08-27 17:25:56 -07:00
|
|
|
/* Register us as a dependent tree of the inner tree. */
|
|
|
|
((TreeInfo*)inner->vmprivate)->dependentTrees.addUnique(fragment->root);
|
2008-08-06 16:24:29 -07:00
|
|
|
}
|
|
|
|
|
2008-08-29 15:12:17 -07:00
|
|
|
/* Add a if/if-else control-flow merge point to the list of known merge points. */
|
|
|
|
void
|
|
|
|
TraceRecorder::trackCfgMerges(jsbytecode* pc)
|
|
|
|
{
|
|
|
|
/* If we hit the beginning of an if/if-else, then keep track of the merge point after it. */
|
|
|
|
JS_ASSERT((*pc == JSOP_IFEQ) || (*pc == JSOP_IFEQX));
|
|
|
|
jssrcnote* sn = js_GetSrcNote(cx->fp->script, pc);
|
|
|
|
if (sn != NULL) {
|
|
|
|
if (SN_TYPE(sn) == SRC_IF) {
|
|
|
|
cfgMerges.add((*pc == JSOP_IFEQ)
|
|
|
|
? pc + GET_JUMP_OFFSET(pc)
|
|
|
|
: pc + GET_JUMPX_OFFSET(pc));
|
|
|
|
} else if (SN_TYPE(sn) == SRC_IF_ELSE)
|
|
|
|
cfgMerges.add(pc + js_GetSrcNoteOffset(sn, 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-08 21:02:04 -07:00
|
|
|
/* Invert the direction of the guard if this is a loop edge that is not
|
|
|
|
taken (thin loop). */
|
|
|
|
void
|
|
|
|
TraceRecorder::flipIf(jsbytecode* pc, bool& cond)
|
|
|
|
{
|
|
|
|
if (js_IsLoopEdge(pc, (jsbytecode*)fragment->root->ip)) {
|
|
|
|
switch (*pc) {
|
|
|
|
case JSOP_IFEQ:
|
|
|
|
case JSOP_IFEQX:
|
|
|
|
if (!cond)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
case JSOP_IFNE:
|
|
|
|
case JSOP_IFNEX:
|
|
|
|
if (cond)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
JS_NOT_REACHED("flipIf");
|
|
|
|
}
|
|
|
|
/* We are about to walk out of the loop, so terminate it with
|
|
|
|
an inverse loop condition. */
|
|
|
|
debug_only_v(printf("Walking out of the loop, terminating it anyway.\n");)
|
|
|
|
cond = !cond;
|
|
|
|
terminate = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-29 15:12:17 -07:00
|
|
|
/* Emit code for a fused IFEQ/IFNE. */
|
|
|
|
void
|
|
|
|
TraceRecorder::fuseIf(jsbytecode* pc, bool cond, LIns* x)
|
|
|
|
{
|
2008-10-08 21:02:04 -07:00
|
|
|
if (x->isconst()) // no need to guard if condition is constant
|
|
|
|
return;
|
2008-08-29 15:20:36 -07:00
|
|
|
if (*pc == JSOP_IFEQ) {
|
2008-10-08 21:02:04 -07:00
|
|
|
flipIf(pc, cond);
|
2008-08-29 15:12:17 -07:00
|
|
|
guard(cond, x, BRANCH_EXIT);
|
|
|
|
trackCfgMerges(pc);
|
2008-08-29 15:20:36 -07:00
|
|
|
} else if (*pc == JSOP_IFNE) {
|
2008-10-08 21:02:04 -07:00
|
|
|
flipIf(pc, cond);
|
2008-08-29 15:12:17 -07:00
|
|
|
guard(cond, x, BRANCH_EXIT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-08 17:16:51 -07:00
|
|
|
int
|
|
|
|
nanojit::StackFilter::getTop(LInsp guard)
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
if (sp == lirbuf->sp)
|
2008-10-21 17:50:32 -07:00
|
|
|
return guard->record()->exit->sp_adj;
|
2008-10-13 13:29:18 -07:00
|
|
|
JS_ASSERT(sp == lirbuf->rp);
|
2008-10-21 17:50:32 -07:00
|
|
|
return guard->record()->exit->rp_adj;
|
2008-07-08 17:16:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#if defined NJ_VERBOSE
|
|
|
|
void
|
|
|
|
nanojit::LirNameMap::formatGuard(LIns *i, char *out)
|
|
|
|
{
|
|
|
|
uint32_t ip;
|
|
|
|
SideExit *x;
|
|
|
|
|
2008-10-21 17:50:32 -07:00
|
|
|
x = (SideExit *)i->record()->exit;
|
2008-07-08 17:16:51 -07:00
|
|
|
ip = intptr_t(x->from->ip) + x->ip_adj;
|
2008-07-08 18:12:22 -07:00
|
|
|
sprintf(out,
|
2008-07-14 19:12:50 -07:00
|
|
|
"%s: %s %s -> %s sp%+ld rp%+ld",
|
2008-07-08 17:16:51 -07:00
|
|
|
formatRef(i),
|
|
|
|
lirNames[i->opcode()],
|
|
|
|
i->oprnd1()->isCond() ? formatRef(i->oprnd1()) : "",
|
|
|
|
labels->format((void *)ip),
|
2008-08-25 16:54:02 -07:00
|
|
|
(long int)x->sp_adj,
|
|
|
|
(long int)x->rp_adj
|
2008-07-08 17:16:51 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-08-11 16:01:21 -07:00
|
|
|
void
|
|
|
|
nanojit::Fragment::onDestroy()
|
|
|
|
{
|
|
|
|
if (root == this) {
|
|
|
|
delete mergeCounts;
|
|
|
|
delete lirbuf;
|
|
|
|
}
|
|
|
|
delete (TreeInfo *)vmprivate;
|
|
|
|
}
|
|
|
|
|
2008-07-05 10:41:35 -07:00
|
|
|
void
|
2008-07-04 00:51:30 -07:00
|
|
|
js_DeleteRecorder(JSContext* cx)
|
|
|
|
{
|
2008-09-03 00:38:24 -07:00
|
|
|
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
|
|
|
|
2008-08-29 16:04:54 -07:00
|
|
|
/* Aborting and completing a trace end up here. */
|
2008-09-03 00:38:24 -07:00
|
|
|
JS_ASSERT(tm->onTrace);
|
|
|
|
tm->onTrace = false;
|
2008-08-29 16:04:54 -07:00
|
|
|
|
2008-07-04 00:51:30 -07:00
|
|
|
delete tm->recorder;
|
|
|
|
tm->recorder = NULL;
|
2008-06-27 20:58:06 -07:00
|
|
|
}
|
|
|
|
|
2008-07-30 04:17:22 -07:00
|
|
|
static bool
|
2008-10-21 17:50:32 -07:00
|
|
|
js_StartRecorder(JSContext* cx, SideExit* anchor, Fragment* f, TreeInfo* ti,
|
2008-08-20 22:37:00 -07:00
|
|
|
unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap,
|
2008-10-21 17:50:32 -07:00
|
|
|
SideExit* expectedInnerExit)
|
2008-07-17 16:20:13 -07:00
|
|
|
{
|
2008-09-03 00:38:24 -07:00
|
|
|
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
|
|
|
|
2008-08-29 16:04:54 -07:00
|
|
|
/*
|
|
|
|
* Emulate on-trace semantics and avoid rooting headaches while recording,
|
|
|
|
* by suppressing last-ditch GC attempts while recording a trace. This does
|
|
|
|
* means that trace recording must not nest or the following assertion will
|
|
|
|
* botch.
|
|
|
|
*/
|
2008-09-03 00:38:24 -07:00
|
|
|
JS_ASSERT(!tm->onTrace);
|
|
|
|
tm->onTrace = true;
|
2008-08-29 16:04:54 -07:00
|
|
|
|
2008-07-17 16:20:13 -07:00
|
|
|
/* start recording if no exception during construction */
|
2008-09-25 19:11:48 -07:00
|
|
|
tm->recorder = new (&gc) TraceRecorder(cx, anchor, f, ti,
|
|
|
|
ngslots, globalTypeMap, stackTypeMap,
|
|
|
|
expectedInnerExit);
|
2008-07-17 16:20:13 -07:00
|
|
|
if (cx->throwing) {
|
2008-10-09 16:17:13 -07:00
|
|
|
js_AbortRecording(cx, "setting up recorder failed");
|
2008-07-17 16:20:13 -07:00
|
|
|
return false;
|
|
|
|
}
|
2008-08-06 22:18:33 -07:00
|
|
|
/* clear any leftover error state */
|
2008-09-25 19:11:48 -07:00
|
|
|
tm->fragmento->assm()->setError(None);
|
2008-07-17 16:20:13 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-27 21:44:08 -07:00
|
|
|
static void
|
2008-07-30 01:29:13 -07:00
|
|
|
js_TrashTree(JSContext* cx, Fragment* f)
|
2008-07-27 21:44:08 -07:00
|
|
|
{
|
2008-08-16 23:43:49 -07:00
|
|
|
JS_ASSERT((!f->code()) == (!f->vmprivate));
|
2008-08-27 17:25:56 -07:00
|
|
|
JS_ASSERT(f == f->root);
|
2008-08-16 23:43:49 -07:00
|
|
|
if (!f->code())
|
|
|
|
return;
|
2008-07-30 01:29:13 -07:00
|
|
|
AUDIT(treesTrashed);
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("Trashing tree info.\n");)
|
2008-08-11 15:16:34 -07:00
|
|
|
Fragmento* fragmento = JS_TRACE_MONITOR(cx).fragmento;
|
2008-08-27 17:25:56 -07:00
|
|
|
TreeInfo* ti = (TreeInfo*)f->vmprivate;
|
2008-08-16 23:43:49 -07:00
|
|
|
f->vmprivate = NULL;
|
2008-08-11 15:16:34 -07:00
|
|
|
f->releaseCode(fragmento);
|
2008-08-27 17:25:56 -07:00
|
|
|
Fragment** data = ti->dependentTrees.data();
|
|
|
|
unsigned length = ti->dependentTrees.length();
|
|
|
|
for (unsigned n = 0; n < length; ++n)
|
|
|
|
js_TrashTree(cx, data[n]);
|
|
|
|
delete ti;
|
|
|
|
JS_ASSERT(!f->code() && !f->vmprivate);
|
2008-07-27 19:18:51 -07:00
|
|
|
}
|
|
|
|
|
2008-09-24 15:43:19 -07:00
|
|
|
static int
|
2008-08-01 08:26:32 -07:00
|
|
|
js_SynthesizeFrame(JSContext* cx, const FrameInfo& fi)
|
|
|
|
{
|
|
|
|
JS_ASSERT(HAS_FUNCTION_CLASS(fi.callee));
|
|
|
|
|
|
|
|
JSFunction* fun = GET_FUNCTION_PRIVATE(cx, fi.callee);
|
|
|
|
JS_ASSERT(FUN_INTERPRETED(fun));
|
|
|
|
|
2008-09-24 15:40:08 -07:00
|
|
|
/* Assert that we have a correct sp distance from cx->fp->slots in fi. */
|
2008-08-01 08:26:32 -07:00
|
|
|
JS_ASSERT(js_ReconstructStackDepth(cx, cx->fp->script, fi.callpc) ==
|
2008-08-18 18:36:05 -07:00
|
|
|
uintN(fi.s.spdist - cx->fp->script->nfixed));
|
2008-08-01 08:26:32 -07:00
|
|
|
|
|
|
|
uintN nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval));
|
2008-09-24 15:40:08 -07:00
|
|
|
JSScript* script = fun->u.i.script;
|
2008-08-01 08:26:32 -07:00
|
|
|
size_t nbytes = (nframeslots + script->nslots) * sizeof(jsval);
|
|
|
|
|
2008-09-24 15:40:08 -07:00
|
|
|
/* Code duplicated from inline_call: case in js_Interpret (FIXME). */
|
|
|
|
JSArena* a = cx->stackPool.current;
|
|
|
|
void* newmark = (void*) a->avail;
|
|
|
|
uintN argc = fi.s.argc & 0x7fff;
|
|
|
|
jsval* vp = cx->fp->slots + fi.s.spdist - (2 + argc);
|
|
|
|
uintN missing = 0;
|
2008-08-01 08:26:32 -07:00
|
|
|
jsval* newsp;
|
2008-09-24 15:40:08 -07:00
|
|
|
|
|
|
|
if (fun->nargs > argc) {
|
|
|
|
const JSFrameRegs& regs = *cx->fp->regs;
|
|
|
|
|
|
|
|
newsp = vp + 2 + fun->nargs;
|
|
|
|
JS_ASSERT(newsp > regs.sp);
|
|
|
|
if ((jsuword) newsp <= a->limit) {
|
|
|
|
if ((jsuword) newsp > a->avail)
|
|
|
|
a->avail = (jsuword) newsp;
|
|
|
|
jsval* argsp = newsp;
|
|
|
|
do {
|
|
|
|
*--argsp = JSVAL_VOID;
|
|
|
|
} while (argsp != regs.sp);
|
|
|
|
missing = 0;
|
|
|
|
} else {
|
|
|
|
missing = fun->nargs - argc;
|
|
|
|
nbytes += (2 + fun->nargs) * sizeof(jsval);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate the inline frame with its vars and operands. */
|
2008-08-01 08:26:32 -07:00
|
|
|
if (a->avail + nbytes <= a->limit) {
|
|
|
|
newsp = (jsval *) a->avail;
|
|
|
|
a->avail += nbytes;
|
2008-09-24 15:40:08 -07:00
|
|
|
JS_ASSERT(missing == 0);
|
2008-08-01 08:26:32 -07:00
|
|
|
} else {
|
|
|
|
JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool, nbytes);
|
|
|
|
if (!newsp) {
|
|
|
|
js_ReportOutOfScriptQuota(cx);
|
2008-08-18 18:36:05 -07:00
|
|
|
return 0;
|
2008-08-01 08:26:32 -07:00
|
|
|
}
|
2008-09-24 15:40:08 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Move args if the missing ones overflow arena a, then push
|
|
|
|
* undefined for the missing args.
|
|
|
|
*/
|
|
|
|
if (missing) {
|
|
|
|
memcpy(newsp, vp, (2 + argc) * sizeof(jsval));
|
|
|
|
vp = newsp;
|
|
|
|
newsp = vp + 2 + argc;
|
|
|
|
do {
|
|
|
|
*newsp++ = JSVAL_VOID;
|
|
|
|
} while (--missing != 0);
|
|
|
|
}
|
2008-08-01 08:26:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Claim space for the stack frame and initialize it. */
|
|
|
|
JSInlineFrame* newifp = (JSInlineFrame *) newsp;
|
|
|
|
newsp += nframeslots;
|
|
|
|
|
|
|
|
newifp->frame.callobj = NULL;
|
|
|
|
newifp->frame.argsobj = NULL;
|
|
|
|
newifp->frame.varobj = NULL;
|
|
|
|
newifp->frame.script = script;
|
|
|
|
newifp->frame.callee = fi.callee;
|
|
|
|
newifp->frame.fun = fun;
|
|
|
|
|
2008-09-18 14:13:37 -07:00
|
|
|
bool constructing = fi.s.argc & 0x8000;
|
|
|
|
|
|
|
|
newifp->frame.argc = argc;
|
2008-08-01 08:26:32 -07:00
|
|
|
newifp->callerRegs.pc = fi.callpc;
|
|
|
|
newifp->callerRegs.sp = cx->fp->slots + fi.s.spdist;
|
2008-09-25 13:12:01 -07:00
|
|
|
newifp->frame.argv = newifp->callerRegs.sp - argc;
|
2008-09-19 18:58:00 -07:00
|
|
|
JS_ASSERT(newifp->frame.argv);
|
|
|
|
#ifdef DEBUG
|
|
|
|
// Initialize argv[-1] to a known-bogus value so we'll catch it if
|
|
|
|
// someone forgets to initialize it later.
|
|
|
|
newifp->frame.argv[-1] = JSVAL_HOLE;
|
|
|
|
#endif
|
2008-09-25 13:12:01 -07:00
|
|
|
JS_ASSERT(newifp->frame.argv >= StackBase(cx->fp) + 2);
|
2008-08-01 08:26:32 -07:00
|
|
|
|
|
|
|
newifp->frame.rval = JSVAL_VOID;
|
|
|
|
newifp->frame.down = cx->fp;
|
|
|
|
newifp->frame.annotation = NULL;
|
|
|
|
newifp->frame.scopeChain = OBJ_GET_PARENT(cx, fi.callee);
|
|
|
|
newifp->frame.sharpDepth = 0;
|
|
|
|
newifp->frame.sharpArray = NULL;
|
2008-09-18 14:13:37 -07:00
|
|
|
newifp->frame.flags = constructing ? JSFRAME_CONSTRUCTING : 0;
|
2008-08-01 08:26:32 -07:00
|
|
|
newifp->frame.dormantNext = NULL;
|
|
|
|
newifp->frame.xmlNamespace = NULL;
|
|
|
|
newifp->frame.blockChain = NULL;
|
|
|
|
newifp->mark = newmark;
|
2008-08-01 10:40:38 -07:00
|
|
|
newifp->frame.thisp = NULL; // will be set by js_ExecuteTree -> FlushNativeStackFrame
|
2008-08-01 08:26:32 -07:00
|
|
|
|
|
|
|
newifp->frame.regs = cx->fp->regs;
|
|
|
|
newifp->frame.regs->pc = script->code;
|
|
|
|
newifp->frame.regs->sp = newsp + script->nfixed;
|
|
|
|
newifp->frame.slots = newsp;
|
2008-08-21 03:47:33 -07:00
|
|
|
if (script->staticDepth < JS_DISPLAY_SIZE) {
|
|
|
|
JSStackFrame **disp = &cx->display[script->staticDepth];
|
|
|
|
newifp->frame.displaySave = *disp;
|
|
|
|
*disp = &newifp->frame;
|
|
|
|
}
|
2008-08-01 08:26:32 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
newifp->frame.pcDisabledSave = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
cx->fp->regs = &newifp->callerRegs;
|
|
|
|
cx->fp = &newifp->frame;
|
2008-08-18 18:36:05 -07:00
|
|
|
|
2008-09-24 15:43:19 -07:00
|
|
|
if (fun->flags & JSFUN_HEAVYWEIGHT) {
|
|
|
|
if (!js_GetCallObject(cx, &newifp->frame, newifp->frame.scopeChain))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-10-06 22:30:36 -07:00
|
|
|
// FIXME? we must count stack slots from caller's operand stack up to (but not including)
|
2008-08-18 18:36:05 -07:00
|
|
|
// callee's, including missing arguments. Could we shift everything down to the caller's
|
|
|
|
// fp->slots (where vars start) and avoid some of the complexity?
|
|
|
|
return (fi.s.spdist - cx->fp->down->script->nfixed) +
|
|
|
|
((fun->nargs > cx->fp->argc) ? fun->nargs - cx->fp->argc : 0) +
|
|
|
|
script->nfixed;
|
2008-08-01 08:26:32 -07:00
|
|
|
}
|
|
|
|
|
2008-08-01 22:47:15 -07:00
|
|
|
bool
|
|
|
|
js_RecordTree(JSContext* cx, JSTraceMonitor* tm, Fragment* f)
|
|
|
|
{
|
2008-08-19 11:32:36 -07:00
|
|
|
/* Make sure the global type map didn't change on us. */
|
2008-09-09 09:57:10 -07:00
|
|
|
uint32 globalShape = OBJ_SHAPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain));
|
2008-08-21 00:19:04 -07:00
|
|
|
if (tm->globalShape != globalShape) {
|
2008-09-26 11:37:49 -07:00
|
|
|
AUDIT(globalShapeMismatchAtEntry);
|
2008-08-30 02:21:00 -07:00
|
|
|
debug_only_v(printf("Global shape mismatch (%u vs. %u) in RecordTree, flushing cache.\n",
|
2008-08-21 00:19:04 -07:00
|
|
|
globalShape, tm->globalShape);)
|
2008-08-20 15:03:51 -07:00
|
|
|
js_FlushJITCache(cx);
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-19 11:32:36 -07:00
|
|
|
TypeMap current;
|
|
|
|
current.captureGlobalTypes(cx, *tm->globalSlots);
|
2008-08-20 15:03:51 -07:00
|
|
|
if (!current.matches(*tm->globalTypeMap)) {
|
2008-08-19 11:32:36 -07:00
|
|
|
js_FlushJITCache(cx);
|
2008-08-30 02:21:00 -07:00
|
|
|
debug_only_v(printf("Global type map mismatch in RecordTree, flushing cache.\n");)
|
2008-08-19 11:32:36 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-08-01 22:47:15 -07:00
|
|
|
AUDIT(recorderStarted);
|
2008-08-17 00:49:59 -07:00
|
|
|
|
|
|
|
/* Try to find an unused peer fragment, or allocate a new one. */
|
2008-08-18 15:32:17 -07:00
|
|
|
while (f->code() && f->peer)
|
2008-08-17 00:49:59 -07:00
|
|
|
f = f->peer;
|
2008-08-18 15:32:17 -07:00
|
|
|
if (f->code())
|
2008-10-13 13:29:18 -07:00
|
|
|
f = JS_TRACE_MONITOR(cx).fragmento->getAnchor(f->ip);
|
2008-08-17 00:49:59 -07:00
|
|
|
|
2008-08-01 22:47:15 -07:00
|
|
|
f->calldepth = 0;
|
|
|
|
f->root = f;
|
|
|
|
/* allocate space to store the LIR for this tree */
|
|
|
|
if (!f->lirbuf) {
|
2008-10-08 15:08:33 -07:00
|
|
|
f->lirbuf = new (&gc) LirBuffer(tm->fragmento, NULL);
|
2008-08-01 22:47:15 -07:00
|
|
|
#ifdef DEBUG
|
2008-10-08 15:08:33 -07:00
|
|
|
f->lirbuf->names = new (&gc) LirNameMap(&gc, NULL, tm->fragmento->labels);
|
2008-08-01 22:47:15 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2008-08-16 23:43:49 -07:00
|
|
|
JS_ASSERT(!f->code() && !f->vmprivate);
|
2008-08-18 15:32:17 -07:00
|
|
|
|
2008-08-07 15:28:43 -07:00
|
|
|
/* setup the VM-private treeInfo structure for this fragment */
|
2008-08-16 23:43:49 -07:00
|
|
|
TreeInfo* ti = new (&gc) TreeInfo(f);
|
2008-08-07 15:28:43 -07:00
|
|
|
|
2008-08-07 16:23:50 -07:00
|
|
|
/* capture the coerced type of each active slot in the stack type map */
|
|
|
|
ti->stackTypeMap.captureStackTypes(cx, 0/*callDepth*/);
|
2008-08-18 15:32:17 -07:00
|
|
|
|
2008-08-07 15:28:43 -07:00
|
|
|
/* determine the native frame layout at the entry point */
|
2008-08-07 16:23:50 -07:00
|
|
|
unsigned entryNativeStackSlots = ti->stackTypeMap.length();
|
2008-08-25 16:04:10 -07:00
|
|
|
JS_ASSERT(entryNativeStackSlots == js_NativeStackSlots(cx, 0/*callDepth*/));
|
2008-08-07 15:28:43 -07:00
|
|
|
ti->nativeStackBase = (entryNativeStackSlots -
|
|
|
|
(cx->fp->regs->sp - StackBase(cx->fp))) * sizeof(double);
|
|
|
|
ti->maxNativeStackSlots = entryNativeStackSlots;
|
|
|
|
ti->maxCallDepth = 0;
|
2008-08-22 17:31:23 -07:00
|
|
|
ti->script = cx->fp->script;
|
2008-08-01 22:47:15 -07:00
|
|
|
|
|
|
|
/* recording primary trace */
|
2008-08-16 23:43:49 -07:00
|
|
|
return js_StartRecorder(cx, NULL, f, ti,
|
2008-08-19 11:32:36 -07:00
|
|
|
tm->globalSlots->length(), tm->globalTypeMap->data(),
|
2008-08-20 22:37:00 -07:00
|
|
|
ti->stackTypeMap.data(), NULL);
|
2008-08-01 22:47:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2008-10-21 17:50:32 -07:00
|
|
|
js_AttemptToExtendTree(JSContext* cx, SideExit* anchor, SideExit* exitedFrom)
|
2008-08-01 22:47:15 -07:00
|
|
|
{
|
2008-08-25 23:19:00 -07:00
|
|
|
Fragment* f = anchor->from->root;
|
2008-08-25 23:17:38 -07:00
|
|
|
JS_ASSERT(f->vmprivate);
|
2008-09-02 23:52:11 -07:00
|
|
|
TreeInfo* ti = (TreeInfo*)f->vmprivate;
|
|
|
|
|
|
|
|
/* Don't grow trees above a certain size to avoid code explosion due to tail duplication. */
|
|
|
|
if (ti->branchCount >= MAX_BRANCHES)
|
|
|
|
return false;
|
2008-08-28 14:24:58 -07:00
|
|
|
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("trying to attach another branch to the tree\n");)
|
2008-08-01 22:47:15 -07:00
|
|
|
|
|
|
|
Fragment* c;
|
2008-08-25 23:19:00 -07:00
|
|
|
if (!(c = anchor->target)) {
|
2008-10-21 17:50:32 -07:00
|
|
|
c = JS_TRACE_MONITOR(cx).fragmento->createBranch(anchor, cx->fp->regs->pc);
|
|
|
|
c->spawnedFrom = anchor;
|
2008-08-01 22:47:15 -07:00
|
|
|
c->parent = f;
|
2008-08-25 23:19:00 -07:00
|
|
|
anchor->target = c;
|
2008-08-01 22:47:15 -07:00
|
|
|
c->root = f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (++c->hits() >= HOTEXIT) {
|
|
|
|
/* start tracing secondary trace from this point */
|
|
|
|
c->lirbuf = f->lirbuf;
|
2008-08-26 01:00:53 -07:00
|
|
|
unsigned ngslots;
|
|
|
|
uint8* globalTypeMap;
|
|
|
|
uint8* stackTypeMap;
|
|
|
|
TypeMap fullMap;
|
|
|
|
if (exitedFrom == NULL) {
|
|
|
|
/* If we are coming straight from a simple side exit, just use that exit's type map
|
|
|
|
as starting point. */
|
2008-10-21 17:50:32 -07:00
|
|
|
ngslots = anchor->numGlobalSlots;
|
|
|
|
globalTypeMap = getTypeMap(anchor);
|
2008-08-26 01:00:53 -07:00
|
|
|
stackTypeMap = globalTypeMap + ngslots;
|
|
|
|
} else {
|
|
|
|
/* If we side-exited on a loop exit and continue on a nesting guard, the nesting
|
|
|
|
guard (anchor) has the type information for everything below the current scope,
|
|
|
|
and the actual guard we exited from has the types for everything in the current
|
|
|
|
scope (and whatever it inlined). We have to merge those maps here. */
|
2008-10-21 17:50:32 -07:00
|
|
|
SideExit* e1 = anchor;
|
|
|
|
SideExit* e2 = exitedFrom;
|
|
|
|
fullMap.add(getTypeMap(e1) + e1->numGlobalSlots, e1->numStackSlotsBelowCurrentFrame);
|
|
|
|
fullMap.add(getTypeMap(e2) + e2->numGlobalSlots, e2->numStackSlots);
|
2008-08-26 01:00:53 -07:00
|
|
|
ngslots = e2->numGlobalSlots;
|
2008-10-21 17:50:32 -07:00
|
|
|
globalTypeMap = getTypeMap(e2);
|
2008-08-26 01:00:53 -07:00
|
|
|
stackTypeMap = fullMap.data();
|
|
|
|
}
|
2008-08-25 23:19:00 -07:00
|
|
|
return js_StartRecorder(cx, anchor, c, (TreeInfo*)f->vmprivate,
|
2008-08-26 01:00:53 -07:00
|
|
|
ngslots, globalTypeMap, stackTypeMap, exitedFrom);
|
2008-08-01 22:47:15 -07:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-10-21 17:50:32 -07:00
|
|
|
static SideExit*
|
2008-08-20 22:37:00 -07:00
|
|
|
js_ExecuteTree(JSContext* cx, Fragment** treep, uintN& inlineCallCount,
|
2008-10-21 17:50:32 -07:00
|
|
|
SideExit** innermostNestedGuardp);
|
2008-08-06 06:34:16 -07:00
|
|
|
|
2008-10-08 21:02:04 -07:00
|
|
|
static void
|
|
|
|
js_CloseLoop(JSContext* cx)
|
|
|
|
{
|
|
|
|
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
|
|
|
Fragmento* fragmento = tm->fragmento;
|
|
|
|
TraceRecorder* r = tm->recorder;
|
|
|
|
JS_ASSERT(fragmento && r);
|
|
|
|
|
|
|
|
if (fragmento->assm()->error()) {
|
2008-10-09 16:17:13 -07:00
|
|
|
js_AbortRecording(cx, "Error during recording");
|
2008-10-08 21:02:04 -07:00
|
|
|
/* If we ran out of memory, flush the code cache and abort. */
|
|
|
|
if (fragmento->assm()->error() == OutOMem)
|
|
|
|
js_FlushJITCache(cx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
r->closeLoop(fragmento);
|
|
|
|
js_DeleteRecorder(cx);
|
|
|
|
}
|
|
|
|
|
2008-08-01 22:47:15 -07:00
|
|
|
bool
|
2008-10-09 16:17:13 -07:00
|
|
|
js_RecordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCallCount)
|
2008-08-01 22:47:15 -07:00
|
|
|
{
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
if (OBJ_SCOPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain))->title.ownercx != cx) {
|
2008-10-09 16:17:13 -07:00
|
|
|
js_AbortRecording(cx, "Global object not owned by this context");
|
2008-08-01 22:47:15 -07:00
|
|
|
return false; /* we stay away from shared global objects */
|
|
|
|
}
|
2008-08-18 15:32:17 -07:00
|
|
|
#endif
|
2008-08-28 17:46:25 -07:00
|
|
|
Fragmento* fragmento = JS_TRACE_MONITOR(cx).fragmento;
|
|
|
|
/* If we hit our own loop header, close the loop and compile the trace. */
|
2008-10-08 21:02:04 -07:00
|
|
|
if (r->isLoopHeader(cx)) {
|
|
|
|
js_CloseLoop(cx);
|
2008-08-01 22:47:15 -07:00
|
|
|
return false; /* done recording */
|
|
|
|
}
|
2008-08-07 16:41:20 -07:00
|
|
|
/* does this branch go to an inner loop? */
|
|
|
|
Fragment* f = fragmento->getLoop(cx->fp->regs->pc);
|
2008-08-20 00:51:56 -07:00
|
|
|
if (nesting_enabled &&
|
|
|
|
f && /* must have a fragment at that location */
|
|
|
|
r->selectCallablePeerFragment(&f) && /* is there a potentially matching peer fragment? */
|
|
|
|
r->adjustCallerTypes(f)) { /* make sure we can make our arguments fit */
|
|
|
|
r->prepareTreeCall(f);
|
2008-10-21 17:50:32 -07:00
|
|
|
SideExit* innermostNestedGuard = NULL;
|
|
|
|
SideExit* lr = js_ExecuteTree(cx, &f, inlineCallCount, &innermostNestedGuard);
|
2008-08-11 15:16:34 -07:00
|
|
|
if (!lr) {
|
2008-08-28 17:46:25 -07:00
|
|
|
/* js_ExecuteTree might have flushed the cache and aborted us already. */
|
|
|
|
if (JS_TRACE_MONITOR(cx).recorder)
|
2008-10-09 16:17:13 -07:00
|
|
|
js_AbortRecording(cx, "Couldn't call inner tree");
|
2008-08-11 15:16:34 -07:00
|
|
|
return false;
|
|
|
|
}
|
2008-10-21 17:50:32 -07:00
|
|
|
switch (lr->exitType) {
|
2008-08-11 15:16:34 -07:00
|
|
|
case LOOP_EXIT:
|
2008-08-20 22:37:00 -07:00
|
|
|
/* If the inner tree exited on an unknown loop exit, grow the tree around it. */
|
|
|
|
if (innermostNestedGuard) {
|
2008-10-09 16:17:13 -07:00
|
|
|
js_AbortRecording(cx, "Inner tree took different side exit, abort recording");
|
2008-08-25 23:17:38 -07:00
|
|
|
return js_AttemptToExtendTree(cx, innermostNestedGuard, lr);
|
2008-08-20 22:37:00 -07:00
|
|
|
}
|
2008-08-11 15:16:34 -07:00
|
|
|
/* emit a call to the inner tree and continue recording the outer tree trace */
|
|
|
|
r->emitTreeCall(f, lr);
|
|
|
|
return true;
|
|
|
|
case BRANCH_EXIT:
|
|
|
|
/* abort recording the outer tree, extend the inner tree */
|
2008-10-09 16:17:13 -07:00
|
|
|
js_AbortRecording(cx, "Inner tree is trying to grow, abort outer recording");
|
2008-08-25 23:17:38 -07:00
|
|
|
return js_AttemptToExtendTree(cx, lr, NULL);
|
2008-08-11 15:16:34 -07:00
|
|
|
default:
|
2008-10-21 17:50:32 -07:00
|
|
|
debug_only_v(printf("exit_type=%d\n", lr->exitType);)
|
2008-10-09 16:17:13 -07:00
|
|
|
js_AbortRecording(cx, "Inner tree not suitable for calling");
|
2008-08-11 15:16:34 -07:00
|
|
|
return false;
|
2008-08-08 15:26:31 -07:00
|
|
|
}
|
2008-08-07 16:41:20 -07:00
|
|
|
}
|
|
|
|
/* not returning to our own loop header, not an inner loop we can call, abort trace */
|
2008-08-06 17:34:06 -07:00
|
|
|
AUDIT(returnToDifferentLoopHeader);
|
2008-10-09 16:17:13 -07:00
|
|
|
debug_only_v(printf("loop edge to %d, header %d\n",
|
|
|
|
cx->fp->regs->pc - cx->fp->script->code,
|
|
|
|
(jsbytecode*)r->getFragment()->root->ip - cx->fp->script->code));
|
|
|
|
js_AbortRecording(cx, "Loop edge does not return to header");
|
2008-08-28 14:24:58 -07:00
|
|
|
return false;
|
2008-08-01 22:47:15 -07:00
|
|
|
}
|
|
|
|
|
2008-10-21 17:50:32 -07:00
|
|
|
static inline SideExit*
|
2008-08-20 22:37:00 -07:00
|
|
|
js_ExecuteTree(JSContext* cx, Fragment** treep, uintN& inlineCallCount,
|
2008-10-21 17:50:32 -07:00
|
|
|
SideExit** innermostNestedGuardp)
|
2008-07-25 01:20:40 -07:00
|
|
|
{
|
2008-08-16 15:45:48 -07:00
|
|
|
Fragment* f = *treep;
|
2008-08-19 23:28:13 -07:00
|
|
|
|
|
|
|
/* if we don't have a compiled tree available for this location, bail out */
|
|
|
|
if (!f->code()) {
|
|
|
|
JS_ASSERT(!f->vmprivate);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
JS_ASSERT(f->vmprivate);
|
|
|
|
|
|
|
|
AUDIT(traceTriggered);
|
|
|
|
|
|
|
|
/* execute previously recorded trace */
|
|
|
|
TreeInfo* ti = (TreeInfo*)f->vmprivate;
|
|
|
|
|
2008-09-02 22:29:23 -07:00
|
|
|
debug_only_v(printf("entering trace at %s:%u@%u, native stack slots: %u code: %p\n",
|
2008-08-20 10:15:42 -07:00
|
|
|
cx->fp->script->filename,
|
|
|
|
js_PCToLineNumber(cx, cx->fp->script, cx->fp->regs->pc),
|
2008-09-02 22:29:23 -07:00
|
|
|
cx->fp->regs->pc - cx->fp->script->code, ti->maxNativeStackSlots,
|
|
|
|
f->code()););
|
2008-08-19 23:28:13 -07:00
|
|
|
|
2008-08-19 11:32:36 -07:00
|
|
|
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
|
|
|
unsigned ngslots = tm->globalSlots->length();
|
|
|
|
uint16* gslots = tm->globalSlots->data();
|
2008-08-16 21:05:03 -07:00
|
|
|
JSObject* globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain);
|
2008-07-30 16:28:48 -07:00
|
|
|
unsigned globalFrameSize = STOBJ_NSLOTS(globalObj);
|
2008-08-12 21:39:44 -07:00
|
|
|
double* global = (double*)alloca((globalFrameSize+1) * sizeof(double));
|
2008-07-30 16:28:48 -07:00
|
|
|
debug_only(*(uint64*)&global[globalFrameSize] = 0xdeadbeefdeadbeefLL;)
|
2008-08-12 21:39:44 -07:00
|
|
|
double* stack = (double*)alloca(MAX_NATIVE_STACK_SLOTS * sizeof(double));
|
2008-08-16 21:05:03 -07:00
|
|
|
|
2008-08-19 11:32:36 -07:00
|
|
|
/* If any of our trees uses globals, the shape of the global object must not change and
|
|
|
|
the global type map must remain applicable at all times (we expect absolute type
|
|
|
|
stability for globals). */
|
|
|
|
if (ngslots &&
|
2008-09-09 09:57:10 -07:00
|
|
|
(OBJ_SHAPE(globalObj) != tm->globalShape ||
|
2008-08-19 11:32:36 -07:00
|
|
|
!BuildNativeGlobalFrame(cx, ngslots, gslots, tm->globalTypeMap->data(), global))) {
|
|
|
|
AUDIT(globalShapeMismatchAtEntry);
|
2008-08-21 00:19:04 -07:00
|
|
|
debug_only_v(printf("Global shape mismatch (%u vs. %u), flushing cache.\n",
|
2008-09-09 09:57:10 -07:00
|
|
|
OBJ_SHAPE(globalObj), tm->globalShape);)
|
2008-08-19 11:32:36 -07:00
|
|
|
const void* ip = f->ip;
|
|
|
|
js_FlushJITCache(cx);
|
2008-10-13 13:29:18 -07:00
|
|
|
*treep = tm->fragmento->getAnchor(ip);
|
2008-08-19 11:32:36 -07:00
|
|
|
return NULL;
|
2008-08-16 21:05:03 -07:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:28:13 -07:00
|
|
|
if (!BuildNativeStackFrame(cx, 0/*callDepth*/, ti->stackTypeMap.data(), stack)) {
|
2008-07-25 01:20:40 -07:00
|
|
|
AUDIT(typeMapMismatchAtEntry);
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("type-map mismatch.\n");)
|
2008-08-19 23:28:13 -07:00
|
|
|
if (++ti->mismatchCount > MAX_MISMATCH) {
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("excessive mismatches, flushing tree.\n"));
|
2008-08-19 23:28:13 -07:00
|
|
|
js_TrashTree(cx, f);
|
|
|
|
f->blacklist();
|
|
|
|
}
|
2008-08-19 20:30:16 -07:00
|
|
|
return NULL;
|
2008-08-19 23:28:13 -07:00
|
|
|
}
|
|
|
|
|
2008-09-25 19:11:48 -07:00
|
|
|
/* replenish the reserve pool (this might trigger a GC */
|
2008-09-26 14:16:11 -07:00
|
|
|
if (tm->recoveryDoublePoolPtr < tm->recoveryDoublePool + MAX_NATIVE_STACK_SLOTS) {
|
|
|
|
bool didGC;
|
2008-09-25 19:11:48 -07:00
|
|
|
const void* ip = f->ip;
|
2008-09-26 14:16:11 -07:00
|
|
|
if (!ReplenishReservePool(cx, tm, didGC) || didGC) {
|
2008-10-13 13:29:18 -07:00
|
|
|
*treep = tm->fragmento->getAnchor(ip);
|
2008-09-25 19:11:48 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-19 23:28:13 -07:00
|
|
|
ti->mismatchCount = 0;
|
2008-08-01 08:26:32 -07:00
|
|
|
|
2008-08-13 13:51:59 -07:00
|
|
|
double* entry_sp = &stack[ti->nativeStackBase/sizeof(double)];
|
|
|
|
FrameInfo* callstack = (FrameInfo*) alloca(MAX_CALL_STACK_ENTRIES * sizeof(FrameInfo));
|
2008-08-18 15:32:17 -07:00
|
|
|
|
2008-07-25 01:20:40 -07:00
|
|
|
InterpState state;
|
|
|
|
state.sp = (void*)entry_sp;
|
2008-08-12 21:39:44 -07:00
|
|
|
state.eos = ((double*)state.sp) + MAX_NATIVE_STACK_SLOTS;
|
2008-07-25 01:20:40 -07:00
|
|
|
state.rp = callstack;
|
2008-08-13 13:51:59 -07:00
|
|
|
state.eor = callstack + MAX_CALL_STACK_ENTRIES;
|
2008-07-25 01:20:40 -07:00
|
|
|
state.gp = global;
|
|
|
|
state.cx = cx;
|
2008-09-19 18:45:57 -07:00
|
|
|
state.lastTreeExitGuard = NULL;
|
|
|
|
state.lastTreeCallGuard = NULL;
|
2008-10-12 15:39:32 -07:00
|
|
|
state.rpAtLastTreeCall = NULL;
|
2008-07-25 01:20:40 -07:00
|
|
|
union { NIns *code; GuardRecord* (FASTCALL *func)(InterpState*, Fragment*); } u;
|
|
|
|
u.code = f->code();
|
2008-08-01 08:26:32 -07:00
|
|
|
|
2008-09-02 22:29:23 -07:00
|
|
|
#ifdef DEBUG
|
2008-09-04 10:58:35 -07:00
|
|
|
#if defined(NANOJIT_IA32) || (defined(NANOJIT_AMD64) && defined(__GNUC__))
|
2008-07-25 01:20:40 -07:00
|
|
|
uint64 start = rdtsc();
|
2008-09-02 22:29:23 -07:00
|
|
|
#endif
|
2008-07-25 01:20:40 -07:00
|
|
|
#endif
|
2008-08-01 08:26:32 -07:00
|
|
|
|
2008-08-29 16:04:54 -07:00
|
|
|
/*
|
|
|
|
* We may be called from js_MonitorLoopEdge while not recording, or while
|
|
|
|
* recording. Rather than over-generalize by using a counter instead of a
|
2008-09-03 00:38:24 -07:00
|
|
|
* flag, we simply sample and update tm->onTrace if necessary.
|
2008-08-29 16:04:54 -07:00
|
|
|
*/
|
2008-09-03 00:38:24 -07:00
|
|
|
bool onTrace = tm->onTrace;
|
|
|
|
if (!onTrace)
|
|
|
|
tm->onTrace = true;
|
2008-10-21 17:50:32 -07:00
|
|
|
SideExit* lr;
|
2008-09-02 11:43:55 -07:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
debug_only(fflush(NULL);)
|
2008-10-21 17:50:32 -07:00
|
|
|
GuardRecord* rec;
|
2008-09-02 11:43:55 -07:00
|
|
|
#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32)
|
2008-10-21 17:50:32 -07:00
|
|
|
SIMULATE_FASTCALL(rec, &state, NULL, u.func);
|
2008-09-02 11:43:55 -07:00
|
|
|
#else
|
2008-10-21 17:50:32 -07:00
|
|
|
rec = u.func(&state, NULL);
|
2008-09-02 11:43:55 -07:00
|
|
|
#endif
|
2008-10-21 17:50:32 -07:00
|
|
|
lr = rec->exit;
|
2008-09-02 11:43:55 -07:00
|
|
|
|
2008-10-21 17:50:32 -07:00
|
|
|
JS_ASSERT(lr->exitType != LOOP_EXIT || !lr->calldepth);
|
2008-09-24 13:12:25 -07:00
|
|
|
|
2008-09-03 00:38:24 -07:00
|
|
|
if (!onTrace)
|
|
|
|
tm->onTrace = false;
|
2008-08-01 08:26:32 -07:00
|
|
|
|
2008-10-12 15:39:32 -07:00
|
|
|
/* Except if we find that this is a nested bailout, the guard the call returned is the
|
|
|
|
one we have to use to adjust pc and sp. */
|
2008-10-21 17:50:32 -07:00
|
|
|
SideExit* innermost = lr;
|
2008-10-12 15:39:32 -07:00
|
|
|
|
2008-09-19 16:09:18 -07:00
|
|
|
/* While executing a tree we do not update state.sp and state.rp even if they grow. Instead,
|
|
|
|
guards tell us by how much sp and rp should be incremented in case of a side exit. When
|
|
|
|
calling a nested tree, however, we actively adjust sp and rp. If we have such frames
|
|
|
|
from outer trees on the stack, then rp will have been adjusted. Before we can process
|
|
|
|
the stack of the frames of the tree we directly exited from, we have to first work our
|
|
|
|
way through the outer frames and generate interpreter frames for them. Once the call
|
|
|
|
stack (rp) is empty, we can process the final frames (which again are not directly
|
|
|
|
visible and only the guard we exited on will tells us about). */
|
|
|
|
FrameInfo* rp = (FrameInfo*)state.rp;
|
2008-10-21 17:50:32 -07:00
|
|
|
if (lr->exitType == NESTED_EXIT) {
|
|
|
|
SideExit* nested = state.lastTreeCallGuard;
|
2008-10-12 15:39:32 -07:00
|
|
|
if (!nested) {
|
|
|
|
/* If lastTreeCallGuard is not set in state, we only have a single level of
|
|
|
|
nesting in this exit, so lr itself is the innermost and outermost nested
|
|
|
|
guard, and hence we set nested to lr. The calldepth of the innermost guard
|
|
|
|
is not added to state.rp, so we do it here manually. For a nesting depth
|
|
|
|
greater than 1 the CallTree builtin already added the innermost guard's
|
|
|
|
calldepth to state.rpAtLastTreeCall. */
|
|
|
|
nested = lr;
|
|
|
|
rp += lr->calldepth;
|
|
|
|
} else {
|
|
|
|
/* During unwinding state.rp gets overwritten at every step and we restore
|
|
|
|
it here to its state at the innermost nested guard. The builtin already
|
|
|
|
added the calldepth of that innermost guard to rpAtLastTreeCall. */
|
|
|
|
rp = (FrameInfo*)state.rpAtLastTreeCall;
|
|
|
|
}
|
|
|
|
innermost = state.lastTreeExitGuard;
|
2008-09-19 18:45:57 -07:00
|
|
|
if (innermostNestedGuardp)
|
2008-10-12 15:39:32 -07:00
|
|
|
*innermostNestedGuardp = nested;
|
|
|
|
JS_ASSERT(nested);
|
2008-10-21 17:50:32 -07:00
|
|
|
JS_ASSERT(nested->exitType == NESTED_EXIT);
|
2008-10-12 15:39:32 -07:00
|
|
|
JS_ASSERT(state.lastTreeExitGuard);
|
2008-10-21 17:50:32 -07:00
|
|
|
JS_ASSERT(state.lastTreeExitGuard->exitType != NESTED_EXIT);
|
2008-09-19 18:45:57 -07:00
|
|
|
}
|
2008-09-19 16:09:18 -07:00
|
|
|
while (callstack < rp) {
|
|
|
|
/* Synthesize a stack frame and write out the values in it using the type map pointer
|
|
|
|
on the native call stack. */
|
2008-09-24 15:43:19 -07:00
|
|
|
if (js_SynthesizeFrame(cx, *callstack) < 0)
|
|
|
|
return NULL;
|
2008-09-19 16:09:18 -07:00
|
|
|
int slots = FlushNativeStackFrame(cx, 1/*callDepth*/, callstack->typemap, stack, cx->fp);
|
|
|
|
#ifdef DEBUG
|
|
|
|
JSStackFrame* fp = cx->fp;
|
|
|
|
debug_only_v(printf("synthesized deep frame for %s:%u@%u, slots=%d\n",
|
|
|
|
fp->script->filename, js_PCToLineNumber(cx, fp->script, fp->regs->pc),
|
|
|
|
fp->regs->pc - fp->script->code, slots);)
|
|
|
|
#endif
|
|
|
|
if (slots < 0)
|
|
|
|
return NULL;
|
|
|
|
/* Keep track of the additional frames we put on the interpreter stack and the native
|
|
|
|
stack slots we consumed. */
|
|
|
|
++inlineCallCount;
|
|
|
|
++callstack;
|
|
|
|
stack += slots;
|
|
|
|
}
|
2008-09-19 18:45:57 -07:00
|
|
|
|
2008-08-13 21:34:41 -07:00
|
|
|
/* We already synthesized the frames around the innermost guard. Here we just deal
|
|
|
|
with additional frames inside the tree we are bailing out from. */
|
2008-09-19 16:09:18 -07:00
|
|
|
JS_ASSERT(rp == callstack);
|
2008-10-12 15:39:32 -07:00
|
|
|
unsigned calldepth = innermost->calldepth;
|
2008-08-18 18:36:05 -07:00
|
|
|
unsigned calldepth_slots = 0;
|
2008-09-19 16:09:18 -07:00
|
|
|
for (unsigned n = 0; n < calldepth; ++n) {
|
2008-09-24 15:43:19 -07:00
|
|
|
int nslots = js_SynthesizeFrame(cx, callstack[n]);
|
|
|
|
if (nslots < 0)
|
|
|
|
return NULL;
|
|
|
|
calldepth_slots += nslots;
|
2008-10-12 15:39:32 -07:00
|
|
|
++inlineCallCount;
|
2008-09-19 16:09:18 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
JSStackFrame* fp = cx->fp;
|
|
|
|
debug_only_v(printf("synthesized shallow frame for %s:%u@%u\n",
|
|
|
|
fp->script->filename, js_PCToLineNumber(cx, fp->script, fp->regs->pc),
|
|
|
|
fp->regs->pc - fp->script->code);)
|
|
|
|
#endif
|
|
|
|
}
|
2008-08-18 15:32:17 -07:00
|
|
|
|
2008-08-13 18:10:19 -07:00
|
|
|
/* Adjust sp and pc relative to the tree we exited from (not the tree we entered
|
|
|
|
into). These are our final values for sp and pc since js_SynthesizeFrame has
|
|
|
|
already taken care of all frames in between. */
|
2008-08-01 08:26:32 -07:00
|
|
|
JSStackFrame* fp = cx->fp;
|
2008-08-18 18:36:05 -07:00
|
|
|
|
2008-08-15 10:09:36 -07:00
|
|
|
/* If we are not exiting from an inlined frame the state->sp is spbase, otherwise spbase
|
|
|
|
is whatever slots frames around us consume. */
|
2008-10-21 17:50:32 -07:00
|
|
|
fp->regs->pc = (jsbytecode*)innermost->from->root->ip + innermost->ip_adj;
|
|
|
|
fp->regs->sp = StackBase(fp) + (innermost->sp_adj / sizeof(double)) - calldepth_slots;
|
2008-08-18 19:26:47 -07:00
|
|
|
JS_ASSERT(fp->slots + fp->script->nfixed +
|
2008-09-02 22:29:06 -07:00
|
|
|
js_ReconstructStackDepth(cx, fp->script, fp->regs->pc) == fp->regs->sp);
|
2008-08-01 08:26:32 -07:00
|
|
|
|
2008-09-04 10:58:35 -07:00
|
|
|
#if defined(DEBUG) && (defined(NANOJIT_IA32) || (defined(NANOJIT_AMD64) && defined(__GNUC__)))
|
2008-09-02 22:29:23 -07:00
|
|
|
uint64 cycles = rdtsc() - start;
|
2008-09-04 10:58:35 -07:00
|
|
|
#elif defined(DEBUG)
|
|
|
|
uint64 cycles = 0;
|
2008-07-25 01:20:40 -07:00
|
|
|
#endif
|
2008-08-01 08:26:32 -07:00
|
|
|
|
2008-10-21 17:50:32 -07:00
|
|
|
debug_only_v(printf("leaving trace at %s:%u@%u, op=%s, lr=%p, exitType=%d, sp=%d, "
|
2008-09-19 16:09:18 -07:00
|
|
|
"calldepth=%d, cycles=%llu\n",
|
2008-09-02 22:29:23 -07:00
|
|
|
fp->script->filename, js_PCToLineNumber(cx, fp->script, fp->regs->pc),
|
|
|
|
fp->regs->pc - fp->script->code,
|
2008-09-02 22:42:25 -07:00
|
|
|
js_CodeName[*fp->regs->pc],
|
|
|
|
lr,
|
2008-10-21 17:50:32 -07:00
|
|
|
lr->exitType,
|
|
|
|
fp->regs->sp - StackBase(fp),
|
2008-09-19 16:09:18 -07:00
|
|
|
calldepth,
|
2008-09-02 22:29:23 -07:00
|
|
|
cycles));
|
|
|
|
|
2008-08-15 17:11:57 -07:00
|
|
|
/* If this trace is part of a tree, later branches might have added additional globals for
|
|
|
|
with we don't have any type information available in the side exit. We merge in this
|
|
|
|
information from the entry type-map. See also comment in the constructor of TraceRecorder
|
|
|
|
why this is always safe to do. */
|
2008-10-21 17:50:32 -07:00
|
|
|
unsigned exit_gslots = innermost->numGlobalSlots;
|
2008-08-19 11:32:36 -07:00
|
|
|
JS_ASSERT(ngslots == tm->globalTypeMap->length());
|
|
|
|
JS_ASSERT(ngslots >= exit_gslots);
|
2008-10-21 17:50:32 -07:00
|
|
|
uint8* globalTypeMap = getTypeMap(innermost);
|
2008-08-19 11:32:36 -07:00
|
|
|
if (exit_gslots < ngslots)
|
|
|
|
mergeTypeMaps(&globalTypeMap, &exit_gslots, tm->globalTypeMap->data(), ngslots,
|
|
|
|
(uint8*)alloca(sizeof(uint8) * ngslots));
|
|
|
|
JS_ASSERT(exit_gslots == tm->globalTypeMap->length());
|
2008-08-18 15:32:17 -07:00
|
|
|
|
2008-08-13 17:47:18 -07:00
|
|
|
/* write back interned globals */
|
2008-09-19 16:09:18 -07:00
|
|
|
int slots = FlushNativeGlobalFrame(cx, exit_gslots, gslots, globalTypeMap, global);
|
2008-08-29 16:04:54 -07:00
|
|
|
if (slots < 0)
|
|
|
|
return NULL;
|
2008-09-24 23:05:44 -07:00
|
|
|
JS_ASSERT_IF(ngslots != 0, globalFrameSize == STOBJ_NSLOTS(globalObj));
|
2008-07-30 16:28:48 -07:00
|
|
|
JS_ASSERT(*(uint64*)&global[globalFrameSize] == 0xdeadbeefdeadbeefLL);
|
2008-08-18 15:32:17 -07:00
|
|
|
|
2008-08-13 17:47:18 -07:00
|
|
|
/* write back native stack frame */
|
2008-10-21 17:50:32 -07:00
|
|
|
slots = FlushNativeStackFrame(cx, innermost->calldepth, getTypeMap(innermost) +
|
|
|
|
innermost->numGlobalSlots, stack, NULL);
|
2008-08-29 16:04:54 -07:00
|
|
|
if (slots < 0)
|
|
|
|
return NULL;
|
2008-10-21 17:50:32 -07:00
|
|
|
JS_ASSERT(unsigned(slots) == innermost->numStackSlots);
|
2008-08-18 15:32:17 -07:00
|
|
|
|
2008-09-19 18:58:00 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
// Verify that our state restoration worked
|
|
|
|
for (JSStackFrame* fp = cx->fp; fp; fp = fp->down) {
|
|
|
|
JS_ASSERT(!fp->callee || JSVAL_IS_OBJECT(fp->argv[-1]));
|
|
|
|
JS_ASSERT(!fp->callee || fp->thisp == JSVAL_TO_OBJECT(fp->argv[-1]));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-07-25 01:20:40 -07:00
|
|
|
AUDIT(sideExitIntoInterpreter);
|
2008-07-25 16:23:12 -07:00
|
|
|
|
2008-10-13 12:44:37 -07:00
|
|
|
return innermost;
|
2008-07-01 02:37:07 -07:00
|
|
|
}
|
|
|
|
|
2008-08-01 22:47:15 -07:00
|
|
|
bool
|
2008-10-09 16:17:13 -07:00
|
|
|
js_MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount)
|
2008-08-01 22:47:15 -07:00
|
|
|
{
|
|
|
|
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
|
|
|
|
2008-08-28 17:00:52 -07:00
|
|
|
/* Is the recorder currently active? */
|
2008-08-06 13:18:30 -07:00
|
|
|
if (tm->recorder) {
|
2008-10-09 16:17:13 -07:00
|
|
|
if (js_RecordLoopEdge(cx, tm->recorder, inlineCallCount))
|
2008-08-06 13:18:30 -07:00
|
|
|
return true;
|
|
|
|
/* recording was aborted, treat like a regular loop edge hit */
|
|
|
|
}
|
2008-08-21 05:21:01 -07:00
|
|
|
JS_ASSERT(!tm->recorder);
|
2008-08-01 22:47:15 -07:00
|
|
|
|
2008-09-26 11:37:49 -07:00
|
|
|
/* Do an up-front global shape check, since we'll blow away the jit cache
|
|
|
|
on global shape mismatch, wasting all the other work this method does,
|
|
|
|
if we end up in either js_RecordTree or js_ExecuteTree */
|
|
|
|
uint32 globalShape = OBJ_SHAPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain));
|
|
|
|
if (tm->globalShape != globalShape) {
|
|
|
|
debug_only_v(printf("Global shape mismatch (%u vs. %u) in js_MonitorLoopEdge, flushing cache.\n",
|
|
|
|
globalShape, tm->globalShape);)
|
|
|
|
js_FlushJITCache(cx);
|
|
|
|
// Press on to at least increment a hit count
|
|
|
|
}
|
|
|
|
|
2008-08-28 17:00:52 -07:00
|
|
|
/* check if our quick cache has an entry for this ip, otherwise ask fragmento. */
|
2008-08-28 22:33:22 -07:00
|
|
|
jsbytecode* pc = cx->fp->regs->pc;
|
2008-08-01 22:56:52 -07:00
|
|
|
Fragment* f;
|
2008-08-03 22:35:15 -07:00
|
|
|
JSFragmentCacheEntry* cacheEntry = &tm->fcache[jsuword(pc) & JS_FRAGMENT_CACHE_MASK];
|
|
|
|
if (cacheEntry->pc == pc) {
|
2008-08-01 22:56:52 -07:00
|
|
|
f = cacheEntry->fragment;
|
2008-08-03 22:35:15 -07:00
|
|
|
} else {
|
2008-08-01 22:56:52 -07:00
|
|
|
f = tm->fragmento->getLoop(pc);
|
2008-08-15 20:15:47 -07:00
|
|
|
if (!f)
|
2008-10-13 13:29:18 -07:00
|
|
|
f = tm->fragmento->getAnchor(pc);
|
2008-08-01 22:56:52 -07:00
|
|
|
cacheEntry->pc = pc;
|
|
|
|
cacheEntry->fragment = f;
|
|
|
|
}
|
|
|
|
|
2008-08-17 00:30:36 -07:00
|
|
|
/* If there is a chance that js_ExecuteTree will actually succeed, invoke it (either the
|
|
|
|
first fragment must contain some code, or at least it must have a peer fragment). */
|
2008-10-21 17:50:32 -07:00
|
|
|
SideExit* lr = NULL;
|
|
|
|
SideExit* innermostNestedGuard = NULL;
|
2008-08-19 23:28:13 -07:00
|
|
|
if (f->code() || f->peer)
|
2008-08-20 22:37:00 -07:00
|
|
|
lr = js_ExecuteTree(cx, &f, inlineCallCount, &innermostNestedGuard);
|
2008-08-17 00:30:36 -07:00
|
|
|
if (!lr) {
|
|
|
|
JS_ASSERT(!tm->recorder);
|
2008-08-19 23:28:13 -07:00
|
|
|
/* If we don't have compiled code for this entry point (none recorded or we trashed it),
|
|
|
|
count the number of hits and trigger the recorder if appropriate. */
|
|
|
|
if (!f->code() && (++f->hits() >= HOTLOOP))
|
2008-08-01 22:47:15 -07:00
|
|
|
return js_RecordTree(cx, tm, f);
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-20 22:37:00 -07:00
|
|
|
/* If we exit on a branch, or on a tree call guard, try to grow the inner tree (in case
|
|
|
|
of a branch exit), or the tree nested around the tree we exited from (in case of the
|
|
|
|
tree call guard). */
|
2008-10-21 17:50:32 -07:00
|
|
|
switch (lr->exitType) {
|
2008-08-20 22:37:00 -07:00
|
|
|
case BRANCH_EXIT:
|
2008-08-25 23:17:38 -07:00
|
|
|
return js_AttemptToExtendTree(cx, lr, NULL);
|
2008-08-20 22:37:00 -07:00
|
|
|
case LOOP_EXIT:
|
|
|
|
if (innermostNestedGuard)
|
2008-08-25 23:17:38 -07:00
|
|
|
return js_AttemptToExtendTree(cx, innermostNestedGuard, lr);
|
2008-08-20 22:37:00 -07:00
|
|
|
return false;
|
2008-08-25 17:24:49 -07:00
|
|
|
default:
|
2008-08-20 22:37:00 -07:00
|
|
|
/* No, this was an unusual exit (i.e. out of memory/GC), so just resume interpretation. */
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 22:47:15 -07:00
|
|
|
}
|
|
|
|
|
2008-08-28 22:33:22 -07:00
|
|
|
bool
|
2008-09-09 22:22:52 -07:00
|
|
|
js_MonitorRecording(TraceRecorder* tr)
|
2008-08-28 22:33:22 -07:00
|
|
|
{
|
2008-09-09 22:22:52 -07:00
|
|
|
JSContext* cx = tr->cx;
|
2008-09-05 18:29:08 -07:00
|
|
|
|
2008-10-08 21:02:04 -07:00
|
|
|
if (tr->walkedOutOfLoop()) {
|
|
|
|
js_CloseLoop(cx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-09-05 18:29:08 -07:00
|
|
|
// Clear one-shot flag used to communicate between record_JSOP_CALL and record_EnterFrame.
|
|
|
|
tr->applyingArguments = false;
|
|
|
|
|
2008-09-23 13:29:41 -07:00
|
|
|
// In the future, handle dslots realloc by computing an offset from dslots instead.
|
|
|
|
if (tr->global_dslots != tr->globalObj->dslots) {
|
2008-10-09 16:17:13 -07:00
|
|
|
js_AbortRecording(cx, "globalObj->dslots reallocated");
|
2008-09-23 13:29:41 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-09-05 18:29:08 -07:00
|
|
|
// Process deepAbort() requests now.
|
2008-08-29 18:59:21 -07:00
|
|
|
if (tr->wasDeepAborted()) {
|
2008-10-09 16:17:13 -07:00
|
|
|
js_AbortRecording(cx, "deep abort requested");
|
2008-08-29 18:59:21 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-08-29 15:12:17 -07:00
|
|
|
jsbytecode* pc = cx->fp->regs->pc;
|
2008-09-09 22:22:52 -07:00
|
|
|
|
2008-08-28 22:33:22 -07:00
|
|
|
/* If we hit a break, end the loop and generate an always taken loop exit guard. For other
|
|
|
|
downward gotos (like if/else) continue recording. */
|
2008-09-09 22:22:52 -07:00
|
|
|
if (*pc == JSOP_GOTO || *pc == JSOP_GOTOX) {
|
2008-08-29 15:12:17 -07:00
|
|
|
jssrcnote* sn = js_GetSrcNote(cx->fp->script, pc);
|
2008-09-09 22:22:52 -07:00
|
|
|
if (sn && SN_TYPE(sn) == SRC_BREAK) {
|
2008-08-29 15:29:55 -07:00
|
|
|
AUDIT(breakLoopExits);
|
2008-08-29 18:59:21 -07:00
|
|
|
tr->endLoop(JS_TRACE_MONITOR(cx).fragmento);
|
2008-08-29 15:12:17 -07:00
|
|
|
js_DeleteRecorder(cx);
|
|
|
|
return false; /* done recording */
|
|
|
|
}
|
2008-08-28 22:33:22 -07:00
|
|
|
}
|
2008-09-08 00:31:18 -07:00
|
|
|
|
|
|
|
/* An explicit return from callDepth 0 should end the loop, not abort it. */
|
|
|
|
if (*pc == JSOP_RETURN && tr->callDepth == 0) {
|
|
|
|
AUDIT(returnLoopExits);
|
|
|
|
tr->endLoop(JS_TRACE_MONITOR(cx).fragmento);
|
|
|
|
js_DeleteRecorder(cx);
|
|
|
|
return false; /* done recording */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If it's not a break or a return from a loop, continue recording and follow the trace. */
|
2008-08-28 22:33:22 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-06-28 18:19:21 -07:00
|
|
|
void
|
2008-10-09 16:17:13 -07:00
|
|
|
js_AbortRecording(JSContext* cx, const char* reason)
|
2008-06-28 18:19:21 -07:00
|
|
|
{
|
2008-08-25 17:24:49 -07:00
|
|
|
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
|
|
|
JS_ASSERT(tm->recorder != NULL);
|
|
|
|
Fragment* f = tm->recorder->getFragment();
|
2008-08-16 23:43:49 -07:00
|
|
|
JS_ASSERT(!f->vmprivate);
|
2008-08-28 17:46:25 -07:00
|
|
|
/* Abort the trace and blacklist its starting point. */
|
|
|
|
AUDIT(recorderAborted);
|
|
|
|
if (cx->fp) {
|
2008-10-09 16:17:13 -07:00
|
|
|
debug_only_v(printf("Abort recording (line %d, pc %d): %s.\n",
|
|
|
|
js_PCToLineNumber(cx, cx->fp->script, cx->fp->regs->pc),
|
|
|
|
cx->fp->regs->pc - cx->fp->script->code, reason);)
|
2008-08-25 17:24:49 -07:00
|
|
|
}
|
2008-08-28 17:46:25 -07:00
|
|
|
f->blacklist();
|
2008-07-01 02:37:07 -07:00
|
|
|
js_DeleteRecorder(cx);
|
2008-08-25 17:24:49 -07:00
|
|
|
/* If this is the primary trace and we didn't succeed compiling, trash the TreeInfo object. */
|
|
|
|
if (!f->code() && (f->root == f))
|
2008-07-30 01:29:13 -07:00
|
|
|
js_TrashTree(cx, f);
|
2008-06-28 18:19:21 -07:00
|
|
|
}
|
|
|
|
|
2008-08-22 12:21:27 -07:00
|
|
|
#if defined NANOJIT_IA32
|
|
|
|
static bool
|
|
|
|
js_CheckForSSE2()
|
|
|
|
{
|
|
|
|
int features = 0;
|
|
|
|
#if defined _MSC_VER
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
pushad
|
|
|
|
mov eax, 1
|
|
|
|
cpuid
|
|
|
|
mov features, edx
|
|
|
|
popad
|
|
|
|
}
|
|
|
|
#elif defined __GNUC__
|
2008-10-22 18:55:54 -07:00
|
|
|
asm("movl %1, %%eax; cpuid; movl %%edx, %0;"
|
|
|
|
: "m=" (features) /* output */
|
|
|
|
: "r" (0x01) /* input */
|
|
|
|
: "eax", "ebx", "ecx", "edx" /* clobber list */
|
|
|
|
);
|
2008-09-05 16:56:03 -07:00
|
|
|
#elif defined __SUNPRO_C || defined __SUNPRO_CC
|
|
|
|
asm("push %%ebx\n"
|
|
|
|
"mov $0x01, %%eax\n"
|
|
|
|
"cpuid\n"
|
|
|
|
"pop %%ebx\n"
|
|
|
|
: "=d" (features)
|
|
|
|
: /* We have no inputs */
|
|
|
|
: "%eax", "%ecx"
|
|
|
|
);
|
2008-08-22 12:21:27 -07:00
|
|
|
#endif
|
|
|
|
return (features & (1<<26)) != 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-07-05 19:15:00 -07:00
|
|
|
extern void
|
2008-08-11 18:47:22 -07:00
|
|
|
js_InitJIT(JSTraceMonitor *tm)
|
2008-07-05 19:15:00 -07:00
|
|
|
{
|
2008-08-22 12:21:27 -07:00
|
|
|
#if defined NANOJIT_IA32
|
|
|
|
if (!did_we_check_sse2) {
|
2008-09-26 20:39:21 -07:00
|
|
|
avmplus::AvmCore::cmov_available =
|
2008-08-22 12:21:27 -07:00
|
|
|
avmplus::AvmCore::sse2_available = js_CheckForSSE2();
|
|
|
|
did_we_check_sse2 = true;
|
|
|
|
}
|
|
|
|
#endif
|
2008-07-05 19:15:00 -07:00
|
|
|
if (!tm->fragmento) {
|
2008-09-25 19:11:48 -07:00
|
|
|
JS_ASSERT(!tm->globalSlots && !tm->globalTypeMap && !tm->recoveryDoublePool);
|
2008-07-16 23:47:57 -07:00
|
|
|
Fragmento* fragmento = new (&gc) Fragmento(core, 24);
|
2008-07-29 17:19:51 -07:00
|
|
|
verbose_only(fragmento->labels = new (&gc) LabelMap(core, NULL);)
|
2008-07-05 19:15:00 -07:00
|
|
|
tm->fragmento = fragmento;
|
2008-08-20 12:17:31 -07:00
|
|
|
tm->globalSlots = new (&gc) SlotList();
|
|
|
|
tm->globalTypeMap = new (&gc) TypeMap();
|
2008-09-26 14:16:11 -07:00
|
|
|
tm->recoveryDoublePoolPtr = tm->recoveryDoublePool = new jsval[MAX_NATIVE_STACK_SLOTS];
|
2008-07-05 19:15:00 -07:00
|
|
|
}
|
2008-07-29 17:19:51 -07:00
|
|
|
#if !defined XP_WIN
|
2008-09-23 14:35:29 -07:00
|
|
|
debug_only(memset(&jitstats, 0, sizeof(jitstats)));
|
2008-07-29 17:19:51 -07:00
|
|
|
#endif
|
2008-07-17 10:22:40 -07:00
|
|
|
}
|
|
|
|
|
2008-07-19 00:15:22 -07:00
|
|
|
extern void
|
2008-08-11 19:17:30 -07:00
|
|
|
js_FinishJIT(JSTraceMonitor *tm)
|
2008-07-17 10:22:40 -07:00
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("recorder: started(%llu), aborted(%llu), completed(%llu), different header(%llu), "
|
2008-08-29 15:29:55 -07:00
|
|
|
"trees trashed(%llu), slot promoted(%llu), unstable loop variable(%llu), "
|
2008-09-08 00:31:18 -07:00
|
|
|
"breaks(%llu), returns(%llu)\n",
|
2008-09-23 14:35:29 -07:00
|
|
|
jitstats.recorderStarted, jitstats.recorderAborted, jitstats.traceCompleted,
|
|
|
|
jitstats.returnToDifferentLoopHeader, jitstats.treesTrashed, jitstats.slotPromoted,
|
|
|
|
jitstats.unstableLoopVariable, jitstats.breakLoopExits, jitstats.returnLoopExits);
|
2008-08-28 00:16:50 -07:00
|
|
|
printf("monitor: triggered(%llu), exits(%llu), type mismatch(%llu), "
|
2008-09-23 14:35:29 -07:00
|
|
|
"global mismatch(%llu)\n", jitstats.traceTriggered, jitstats.sideExitIntoInterpreter,
|
|
|
|
jitstats.typeMapMismatchAtEntry, jitstats.globalShapeMismatchAtEntry);
|
2008-07-19 00:15:22 -07:00
|
|
|
#endif
|
2008-08-11 21:36:25 -07:00
|
|
|
if (tm->fragmento != NULL) {
|
2008-09-25 19:11:48 -07:00
|
|
|
JS_ASSERT(tm->globalSlots && tm->globalTypeMap && tm->recoveryDoublePool);
|
2008-08-11 21:36:25 -07:00
|
|
|
verbose_only(delete tm->fragmento->labels;)
|
|
|
|
delete tm->fragmento;
|
|
|
|
tm->fragmento = NULL;
|
2008-08-18 21:08:35 -07:00
|
|
|
delete tm->globalSlots;
|
|
|
|
tm->globalSlots = NULL;
|
|
|
|
delete tm->globalTypeMap;
|
|
|
|
tm->globalTypeMap = NULL;
|
2008-09-26 14:16:11 -07:00
|
|
|
delete[] tm->recoveryDoublePool;
|
2008-09-25 19:11:48 -07:00
|
|
|
tm->recoveryDoublePool = tm->recoveryDoublePoolPtr = NULL;
|
2008-08-11 21:36:25 -07:00
|
|
|
}
|
2008-07-05 19:15:00 -07:00
|
|
|
}
|
|
|
|
|
2008-08-20 16:01:56 -07:00
|
|
|
extern void
|
|
|
|
js_FlushJITOracle(JSContext* cx)
|
|
|
|
{
|
2008-09-02 14:02:13 -07:00
|
|
|
if (!TRACING_ENABLED(cx))
|
|
|
|
return;
|
2008-09-10 12:20:01 -07:00
|
|
|
oracle.clear();
|
2008-08-20 16:01:56 -07:00
|
|
|
}
|
|
|
|
|
2008-08-06 21:56:25 -07:00
|
|
|
extern void
|
|
|
|
js_FlushJITCache(JSContext* cx)
|
|
|
|
{
|
2008-09-02 14:02:13 -07:00
|
|
|
if (!TRACING_ENABLED(cx))
|
|
|
|
return;
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("Flushing cache.\n"););
|
2008-08-06 21:56:25 -07:00
|
|
|
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
|
|
|
if (tm->recorder)
|
2008-10-09 16:17:13 -07:00
|
|
|
js_AbortRecording(cx, "flush cache");
|
2008-08-06 21:56:25 -07:00
|
|
|
Fragmento* fragmento = tm->fragmento;
|
|
|
|
if (fragmento) {
|
|
|
|
fragmento->clearFrags();
|
2008-08-18 15:32:17 -07:00
|
|
|
#ifdef DEBUG
|
2008-08-06 21:56:25 -07:00
|
|
|
JS_ASSERT(fragmento->labels);
|
|
|
|
delete fragmento->labels;
|
|
|
|
fragmento->labels = new (&gc) LabelMap(core, NULL);
|
2008-08-18 15:32:17 -07:00
|
|
|
#endif
|
2008-08-06 21:56:25 -07:00
|
|
|
}
|
|
|
|
memset(&tm->fcache, 0, sizeof(tm->fcache));
|
2008-08-20 15:03:51 -07:00
|
|
|
if (cx->fp) {
|
2008-09-09 09:57:10 -07:00
|
|
|
tm->globalShape = OBJ_SHAPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain));
|
2008-08-20 15:03:51 -07:00
|
|
|
tm->globalSlots->clear();
|
|
|
|
tm->globalTypeMap->clear();
|
|
|
|
}
|
2008-08-06 21:56:25 -07:00
|
|
|
}
|
|
|
|
|
2008-07-04 03:06:18 -07:00
|
|
|
jsval&
|
|
|
|
TraceRecorder::argval(unsigned n) const
|
|
|
|
{
|
2008-07-21 19:31:08 -07:00
|
|
|
JS_ASSERT(n < cx->fp->fun->nargs);
|
2008-07-04 03:06:18 -07:00
|
|
|
return cx->fp->argv[n];
|
|
|
|
}
|
|
|
|
|
|
|
|
jsval&
|
|
|
|
TraceRecorder::varval(unsigned n) const
|
|
|
|
{
|
2008-08-13 16:14:22 -07:00
|
|
|
JS_ASSERT(n < cx->fp->script->nslots);
|
2008-07-22 21:20:29 -07:00
|
|
|
return cx->fp->slots[n];
|
2008-07-04 03:06:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
jsval&
|
|
|
|
TraceRecorder::stackval(int n) const
|
|
|
|
{
|
2008-07-08 18:12:22 -07:00
|
|
|
jsval* sp = cx->fp->regs->sp;
|
2008-07-23 16:39:17 -07:00
|
|
|
JS_ASSERT(size_t((sp + n) - StackBase(cx->fp)) < StackDepth(cx->fp->script));
|
2008-07-08 18:12:22 -07:00
|
|
|
return sp[n];
|
2008-07-04 03:06:18 -07:00
|
|
|
}
|
|
|
|
|
2008-08-20 22:40:39 -07:00
|
|
|
LIns*
|
|
|
|
TraceRecorder::scopeChain() const
|
|
|
|
{
|
|
|
|
return lir->insLoad(LIR_ldp,
|
|
|
|
lir->insLoad(LIR_ldp, cx_ins, offsetof(JSContext, fp)),
|
|
|
|
offsetof(JSStackFrame, scopeChain));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
FrameInRange(JSStackFrame* fp, JSStackFrame *target, unsigned callDepth)
|
|
|
|
{
|
|
|
|
while (fp != target) {
|
|
|
|
if (callDepth-- == 0)
|
|
|
|
return false;
|
|
|
|
if (!(fp = fp->down))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::activeCallOrGlobalSlot(JSObject* obj, jsval*& vp)
|
|
|
|
{
|
|
|
|
JS_ASSERT(obj != globalObj);
|
|
|
|
|
|
|
|
JSAtom* atom = atoms[GET_INDEX(cx->fp->regs->pc)];
|
|
|
|
JSObject* obj2;
|
|
|
|
JSProperty* prop;
|
|
|
|
if (js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &obj2, &prop) < 0 || !prop)
|
|
|
|
ABORT_TRACE("failed to find name in non-global scope chain");
|
|
|
|
|
|
|
|
if (obj == globalObj) {
|
|
|
|
JSScopeProperty* sprop = (JSScopeProperty*) prop;
|
|
|
|
if (obj2 != obj || !SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj))) {
|
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
|
|
|
ABORT_TRACE("prototype or slotless globalObj property");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!lazilyImportGlobalSlot(sprop->slot))
|
|
|
|
ABORT_TRACE("lazy import of global slot failed");
|
|
|
|
vp = &STOBJ_GET_SLOT(obj, sprop->slot);
|
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj == obj2 && OBJ_GET_CLASS(cx, obj) == &js_CallClass) {
|
|
|
|
JSStackFrame* cfp = (JSStackFrame*) JS_GetPrivate(cx, obj);
|
|
|
|
if (cfp && FrameInRange(cx->fp, cfp, callDepth)) {
|
|
|
|
JSScopeProperty* sprop = (JSScopeProperty*) prop;
|
|
|
|
uintN slot = sprop->shortid;
|
|
|
|
|
2008-08-24 08:18:17 -07:00
|
|
|
vp = NULL;
|
2008-08-20 22:40:39 -07:00
|
|
|
if (sprop->getter == js_GetCallArg) {
|
|
|
|
JS_ASSERT(slot < cfp->fun->nargs);
|
|
|
|
vp = &cfp->argv[slot];
|
2008-08-24 08:18:17 -07:00
|
|
|
} else if (sprop->getter == js_GetCallVar) {
|
2008-08-20 22:40:39 -07:00
|
|
|
JS_ASSERT(slot < cfp->script->nslots);
|
|
|
|
vp = &cfp->slots[slot];
|
|
|
|
}
|
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
2008-08-24 08:18:17 -07:00
|
|
|
if (!vp)
|
|
|
|
ABORT_TRACE("dynamic property of Call object");
|
2008-08-20 22:40:39 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
|
|
|
ABORT_TRACE("fp->scopeChain is not global or active call object");
|
|
|
|
}
|
|
|
|
|
2008-07-04 03:06:18 -07:00
|
|
|
LIns*
|
2008-07-05 10:41:35 -07:00
|
|
|
TraceRecorder::arg(unsigned n)
|
2008-07-04 03:06:18 -07:00
|
|
|
{
|
|
|
|
return get(&argval(n));
|
|
|
|
}
|
|
|
|
|
2008-07-04 15:21:56 -07:00
|
|
|
void
|
|
|
|
TraceRecorder::arg(unsigned n, LIns* i)
|
|
|
|
{
|
|
|
|
set(&argval(n), i);
|
|
|
|
}
|
|
|
|
|
2008-07-04 03:06:18 -07:00
|
|
|
LIns*
|
2008-07-05 10:41:35 -07:00
|
|
|
TraceRecorder::var(unsigned n)
|
2008-07-04 03:06:18 -07:00
|
|
|
{
|
|
|
|
return get(&varval(n));
|
|
|
|
}
|
|
|
|
|
2008-07-04 15:21:56 -07:00
|
|
|
void
|
|
|
|
TraceRecorder::var(unsigned n, LIns* i)
|
|
|
|
{
|
|
|
|
set(&varval(n), i);
|
|
|
|
}
|
|
|
|
|
2008-07-04 03:06:18 -07:00
|
|
|
LIns*
|
|
|
|
TraceRecorder::stack(int n)
|
|
|
|
{
|
|
|
|
return get(&stackval(n));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-07-05 10:41:35 -07:00
|
|
|
TraceRecorder::stack(int n, LIns* i)
|
2008-07-04 03:06:18 -07:00
|
|
|
{
|
2008-07-21 12:57:02 -07:00
|
|
|
set(&stackval(n), i, n >= 0);
|
2008-07-04 03:06:18 -07:00
|
|
|
}
|
|
|
|
|
2008-07-06 15:55:04 -07:00
|
|
|
LIns* TraceRecorder::f2i(LIns* f)
|
|
|
|
{
|
2008-10-16 12:24:10 -07:00
|
|
|
return lir->insCall(&js_DoubleToInt32_ci, &f);
|
2008-07-06 15:55:04 -07:00
|
|
|
}
|
|
|
|
|
2008-09-25 11:31:40 -07:00
|
|
|
LIns* TraceRecorder::makeNumberInt32(LIns* f)
|
|
|
|
{
|
|
|
|
JS_ASSERT(f->isQuad());
|
|
|
|
LIns* x;
|
|
|
|
if (!isPromote(f)) {
|
|
|
|
x = f2i(f);
|
|
|
|
guard(true, lir->ins2(LIR_feq, f, lir->ins1(LIR_i2f, x)), MISMATCH_EXIT);
|
|
|
|
} else {
|
|
|
|
x = ::demote(lir, f);
|
|
|
|
}
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
2008-10-08 21:02:04 -07:00
|
|
|
TraceRecorder::ifop()
|
2008-07-05 22:23:34 -07:00
|
|
|
{
|
|
|
|
jsval& v = stackval(-1);
|
2008-09-22 17:58:25 -07:00
|
|
|
LIns* v_ins = get(&v);
|
2008-10-08 16:23:54 -07:00
|
|
|
bool cond;
|
|
|
|
LIns* x;
|
2008-09-22 17:58:25 -07:00
|
|
|
/* no need to guard if condition is constant */
|
|
|
|
if (v_ins->isconst() || v_ins->isconstq())
|
|
|
|
return true;
|
2008-08-01 12:06:04 -07:00
|
|
|
if (JSVAL_TAG(v) == JSVAL_BOOLEAN) {
|
2008-10-08 16:23:54 -07:00
|
|
|
/* test for boolean is true, negate later if we are testing for false */
|
|
|
|
cond = JSVAL_TO_BOOLEAN(v) == 1;
|
|
|
|
x = lir->ins2i(LIR_eq, v_ins, 1);
|
2008-07-18 08:01:51 -07:00
|
|
|
} else if (JSVAL_IS_OBJECT(v)) {
|
2008-10-08 17:44:04 -07:00
|
|
|
cond = !JSVAL_IS_NULL(v);
|
|
|
|
x = v_ins;
|
2008-07-18 08:01:51 -07:00
|
|
|
} else if (isNumber(v)) {
|
|
|
|
jsdouble d = asNumber(v);
|
2008-10-08 17:44:04 -07:00
|
|
|
cond = !JSDOUBLE_IS_NaN(d) && d;
|
2008-10-08 16:23:54 -07:00
|
|
|
jsdpun u;
|
|
|
|
u.d = 0;
|
|
|
|
x = lir->ins2(LIR_and,
|
|
|
|
lir->ins2(LIR_feq, v_ins, v_ins),
|
|
|
|
lir->ins_eq0(lir->ins2(LIR_feq, v_ins, lir->insImmq(u.u64))));
|
2008-07-18 08:01:51 -07:00
|
|
|
} else if (JSVAL_IS_STRING(v)) {
|
2008-10-08 17:44:04 -07:00
|
|
|
cond = JSSTRING_LENGTH(JSVAL_TO_STRING(v)) != 0;
|
|
|
|
x = lir->ins2(LIR_piand,
|
|
|
|
lir->insLoad(LIR_ldp,
|
|
|
|
v_ins,
|
|
|
|
(int)offsetof(JSString, length)),
|
|
|
|
INS_CONSTPTR(JSSTRING_LENGTH_MASK));
|
2008-07-05 22:23:34 -07:00
|
|
|
} else {
|
2008-08-01 12:06:04 -07:00
|
|
|
JS_NOT_REACHED("ifop");
|
2008-10-08 16:23:54 -07:00
|
|
|
return false;
|
|
|
|
}
|
2008-10-08 21:02:04 -07:00
|
|
|
flipIf(cx->fp->regs->pc, cond);
|
2008-10-08 17:49:54 -07:00
|
|
|
bool expected = cond;
|
|
|
|
if (!x->isCond()) {
|
|
|
|
x = lir->ins_eq0(x);
|
|
|
|
expected = !expected;
|
|
|
|
}
|
|
|
|
guard(expected, x, BRANCH_EXIT);
|
2008-07-05 22:23:34 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-08-28 23:50:48 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::switchop()
|
|
|
|
{
|
|
|
|
jsval& v = stackval(-1);
|
2008-09-22 17:58:25 -07:00
|
|
|
LIns* v_ins = get(&v);
|
|
|
|
/* no need to guard if condition is constant */
|
|
|
|
if (v_ins->isconst() || v_ins->isconstq())
|
|
|
|
return true;
|
2008-08-28 23:50:48 -07:00
|
|
|
if (isNumber(v)) {
|
|
|
|
jsdouble d = asNumber(v);
|
|
|
|
jsdpun u;
|
|
|
|
u.d = d;
|
|
|
|
guard(true,
|
2008-09-22 17:58:25 -07:00
|
|
|
addName(lir->ins2(LIR_feq, v_ins, lir->insImmq(u.u64)),
|
2008-08-28 23:50:48 -07:00
|
|
|
"guard(switch on numeric)"),
|
|
|
|
BRANCH_EXIT);
|
|
|
|
} else if (JSVAL_IS_STRING(v)) {
|
2008-09-22 17:58:25 -07:00
|
|
|
LIns* args[] = { v_ins, INS_CONSTPTR(JSVAL_TO_STRING(v)) };
|
2008-08-28 23:50:48 -07:00
|
|
|
guard(true,
|
2008-10-16 12:24:10 -07:00
|
|
|
addName(lir->ins_eq0(lir->ins_eq0(lir->insCall(&js_EqualStrings_ci, args))),
|
2008-08-28 23:50:48 -07:00
|
|
|
"guard(switch on string)"),
|
|
|
|
BRANCH_EXIT);
|
2008-09-26 19:20:52 -07:00
|
|
|
} else if (JSVAL_TAG(v) == JSVAL_BOOLEAN) {
|
2008-08-28 23:50:48 -07:00
|
|
|
guard(true,
|
2008-09-22 17:58:25 -07:00
|
|
|
addName(lir->ins2(LIR_eq, v_ins, lir->insImm(JSVAL_TO_BOOLEAN(v))),
|
2008-08-28 23:50:48 -07:00
|
|
|
"guard(switch on boolean)"),
|
|
|
|
BRANCH_EXIT);
|
|
|
|
} else {
|
2008-09-26 19:20:52 -07:00
|
|
|
ABORT_TRACE("switch on object or null");
|
2008-08-28 23:50:48 -07:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-04 03:06:18 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::inc(jsval& v, jsint incr, bool pre)
|
|
|
|
{
|
2008-07-19 10:24:10 -07:00
|
|
|
LIns* v_ins = get(&v);
|
|
|
|
if (!inc(v, v_ins, incr, pre))
|
|
|
|
return false;
|
|
|
|
set(&v, v_ins);
|
|
|
|
return true;
|
2008-07-19 00:15:22 -07:00
|
|
|
}
|
2008-07-09 15:15:32 -07:00
|
|
|
|
2008-07-25 16:23:12 -07:00
|
|
|
/*
|
2008-07-19 10:24:10 -07:00
|
|
|
* On exit, v_ins is the incremented unboxed value, and the appropriate
|
|
|
|
* value (pre- or post-increment as described by pre) is stacked.
|
|
|
|
*/
|
2008-07-19 00:15:22 -07:00
|
|
|
bool
|
2008-07-19 10:24:10 -07:00
|
|
|
TraceRecorder::inc(jsval& v, LIns*& v_ins, jsint incr, bool pre)
|
2008-07-19 00:15:22 -07:00
|
|
|
{
|
|
|
|
if (!isNumber(v))
|
2008-07-19 10:24:10 -07:00
|
|
|
ABORT_TRACE("can only inc numbers");
|
2008-07-19 00:15:22 -07:00
|
|
|
|
|
|
|
jsdpun u;
|
|
|
|
u.d = jsdouble(incr);
|
|
|
|
|
2008-07-19 10:24:10 -07:00
|
|
|
LIns* v_after = lir->ins2(LIR_fadd, v_ins, lir->insImmq(u.u64));
|
2008-07-19 00:15:22 -07:00
|
|
|
|
|
|
|
const JSCodeSpec& cs = js_CodeSpec[*cx->fp->regs->pc];
|
|
|
|
JS_ASSERT(cs.ndefs == 1);
|
2008-07-19 10:24:10 -07:00
|
|
|
stack(-cs.nuses, pre ? v_after : v_ins);
|
|
|
|
v_ins = v_after;
|
2008-07-19 00:15:22 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::incProp(jsint incr, bool pre)
|
|
|
|
{
|
|
|
|
jsval& l = stackval(-1);
|
|
|
|
if (JSVAL_IS_PRIMITIVE(l))
|
|
|
|
ABORT_TRACE("incProp on primitive");
|
|
|
|
|
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(l);
|
|
|
|
LIns* obj_ins = get(&l);
|
|
|
|
|
2008-07-19 10:24:28 -07:00
|
|
|
uint32 slot;
|
2008-07-19 00:15:22 -07:00
|
|
|
LIns* v_ins;
|
2008-07-19 10:24:28 -07:00
|
|
|
if (!prop(obj, obj_ins, slot, v_ins))
|
2008-07-19 00:15:22 -07:00
|
|
|
return false;
|
2008-07-19 10:24:28 -07:00
|
|
|
|
2008-08-28 14:43:44 -07:00
|
|
|
if (slot == SPROP_INVALID_SLOT)
|
|
|
|
ABORT_TRACE("incProp on invalid slot");
|
|
|
|
|
2008-07-19 10:24:28 -07:00
|
|
|
jsval& v = STOBJ_GET_SLOT(obj, slot);
|
|
|
|
if (!inc(v, v_ins, incr, pre))
|
|
|
|
return false;
|
|
|
|
|
2008-07-19 10:24:10 -07:00
|
|
|
if (!box_jsval(v, v_ins))
|
2008-07-19 10:24:28 -07:00
|
|
|
return false;
|
|
|
|
|
|
|
|
LIns* dslots_ins = NULL;
|
2008-07-19 10:24:10 -07:00
|
|
|
stobj_set_slot(obj_ins, slot, dslots_ins, v_ins);
|
2008-07-19 10:24:28 -07:00
|
|
|
return true;
|
2008-07-19 00:15:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::incElem(jsint incr, bool pre)
|
|
|
|
{
|
|
|
|
jsval& r = stackval(-1);
|
|
|
|
jsval& l = stackval(-2);
|
|
|
|
jsval* vp;
|
|
|
|
LIns* v_ins;
|
2008-07-19 10:24:10 -07:00
|
|
|
LIns* addr_ins;
|
|
|
|
if (!elem(l, r, vp, v_ins, addr_ins))
|
|
|
|
return false;
|
|
|
|
if (!inc(*vp, v_ins, incr, pre))
|
2008-07-19 00:15:22 -07:00
|
|
|
return false;
|
2008-07-19 10:24:10 -07:00
|
|
|
if (!box_jsval(*vp, v_ins))
|
|
|
|
return false;
|
|
|
|
lir->insStorei(v_ins, addr_ins, 0);
|
|
|
|
return true;
|
2008-07-04 03:06:18 -07:00
|
|
|
}
|
|
|
|
|
2008-09-18 13:31:26 -07:00
|
|
|
static bool
|
|
|
|
evalCmp(LOpcode op, double result)
|
|
|
|
{
|
|
|
|
bool cond;
|
|
|
|
switch (op) {
|
|
|
|
case LIR_feq:
|
|
|
|
cond = (result == 0);
|
|
|
|
break;
|
|
|
|
case LIR_flt:
|
|
|
|
cond = result < 0;
|
|
|
|
break;
|
|
|
|
case LIR_fgt:
|
|
|
|
cond = result > 0;
|
|
|
|
break;
|
|
|
|
case LIR_fle:
|
|
|
|
cond = result <= 0;
|
|
|
|
break;
|
|
|
|
case LIR_fge:
|
|
|
|
cond = result >= 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
JS_NOT_REACHED("unexpected comparison op");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return cond;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
evalCmp(LOpcode op, double l, double r)
|
|
|
|
{
|
|
|
|
return evalCmp(op, l - r);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
evalCmp(LOpcode op, JSString* l, JSString* r)
|
|
|
|
{
|
|
|
|
if (op == LIR_feq)
|
|
|
|
return js_EqualStrings(l, r);
|
|
|
|
return evalCmp(op, js_CompareStrings(l, r));
|
|
|
|
}
|
|
|
|
|
2008-07-04 03:06:18 -07:00
|
|
|
bool
|
2008-08-28 23:50:48 -07:00
|
|
|
TraceRecorder::cmp(LOpcode op, int flags)
|
2008-07-04 03:06:18 -07:00
|
|
|
{
|
|
|
|
jsval& r = stackval(-1);
|
|
|
|
jsval& l = stackval(-2);
|
2008-09-18 13:31:26 -07:00
|
|
|
LIns* x = NULL;
|
2008-08-28 23:50:48 -07:00
|
|
|
bool negate = !!(flags & CMP_NEGATE);
|
2008-08-09 18:50:52 -07:00
|
|
|
bool cond;
|
2008-09-04 14:44:59 -07:00
|
|
|
LIns* l_ins = get(&l);
|
|
|
|
LIns* r_ins = get(&r);
|
2008-10-01 16:04:39 -07:00
|
|
|
bool fp = false;
|
2008-09-04 14:44:59 -07:00
|
|
|
|
2008-09-18 13:31:26 -07:00
|
|
|
// CMP_STRICT is only set for JSOP_STRICTEQ and JSOP_STRICTNE, which correspond to the
|
|
|
|
// === and !== operators. negate is true for !== and false for ===. The strict equality
|
|
|
|
// operators produce false if the types of the operands differ, i.e. if only one of
|
|
|
|
// them is a number.
|
|
|
|
if ((flags & CMP_STRICT) && getPromotedType(l) != getPromotedType(r)) {
|
|
|
|
x = INS_CONST(negate);
|
|
|
|
cond = negate;
|
|
|
|
} else if (JSVAL_IS_STRING(l) || JSVAL_IS_STRING(r)) {
|
|
|
|
// The following cases always produce a constant false (or true if negated):
|
|
|
|
// - comparing a string against null
|
|
|
|
// - comparing a string against any boolean (including undefined)
|
|
|
|
if ((JSVAL_IS_NULL(l) && l_ins->isconst()) ||
|
|
|
|
(JSVAL_IS_NULL(r) && r_ins->isconst()) ||
|
|
|
|
(JSVAL_TAG(l) == JSVAL_BOOLEAN || JSVAL_TAG(r) == JSVAL_BOOLEAN)) {
|
2008-09-15 15:30:06 -07:00
|
|
|
x = INS_CONST(negate);
|
|
|
|
cond = negate;
|
2008-09-18 13:31:26 -07:00
|
|
|
} else if (!JSVAL_IS_STRING(l) || !JSVAL_IS_STRING(r)) {
|
|
|
|
ABORT_TRACE("unsupported type for cmp vs string");
|
2008-09-15 15:30:06 -07:00
|
|
|
} else {
|
2008-09-18 13:31:26 -07:00
|
|
|
LIns* args[] = { r_ins, l_ins };
|
|
|
|
if (op == LIR_feq)
|
2008-10-16 12:24:10 -07:00
|
|
|
l_ins = lir->ins_eq0(lir->insCall(&js_EqualStrings_ci, args));
|
2008-09-18 13:31:26 -07:00
|
|
|
else
|
2008-10-16 12:24:10 -07:00
|
|
|
l_ins = lir->insCall(&js_CompareStrings_ci, args);
|
2008-09-18 13:31:26 -07:00
|
|
|
r_ins = lir->insImm(0);
|
|
|
|
cond = evalCmp(op, JSVAL_TO_STRING(l), JSVAL_TO_STRING(r));
|
|
|
|
}
|
|
|
|
} else if (isNumber(l) || isNumber(r)) {
|
|
|
|
jsval tmp[2] = {l, r};
|
|
|
|
JSAutoTempValueRooter tvr(cx, 2, tmp);
|
2008-10-01 16:04:39 -07:00
|
|
|
|
|
|
|
fp = true;
|
2008-09-18 13:31:26 -07:00
|
|
|
|
|
|
|
// TODO: coerce non-numbers to numbers if it's not string-on-string above
|
|
|
|
jsdouble lnum;
|
|
|
|
jsdouble rnum;
|
|
|
|
LIns* args[] = { l_ins, cx_ins };
|
|
|
|
if (l == JSVAL_NULL && l_ins->isconst()) {
|
|
|
|
jsdpun u;
|
|
|
|
u.d = js_NaN;
|
|
|
|
l_ins = lir->insImmq(u.u64);
|
|
|
|
} else if (JSVAL_IS_STRING(l)) {
|
2008-10-16 12:24:10 -07:00
|
|
|
l_ins = lir->insCall(&js_StringToNumber_ci, args);
|
2008-09-18 13:31:26 -07:00
|
|
|
} else if (JSVAL_TAG(l) == JSVAL_BOOLEAN) {
|
|
|
|
/*
|
|
|
|
* What I really want here is for undefined to be type-specialized
|
|
|
|
* differently from real booleans. Failing that, I want to be able
|
|
|
|
* to cmov on quads. Failing that, I want to have small forward
|
|
|
|
* branched. Failing that, I want to be able to ins_choose on quads
|
|
|
|
* without cmov. Failing that, eat flaming builtin!
|
|
|
|
*/
|
2008-10-16 12:24:10 -07:00
|
|
|
l_ins = lir->insCall(&js_BooleanToNumber_ci, args);
|
2008-09-18 13:31:26 -07:00
|
|
|
} else if (!isNumber(l)) {
|
|
|
|
ABORT_TRACE("unsupported LHS type for cmp vs number");
|
|
|
|
}
|
|
|
|
lnum = js_ValueToNumber(cx, &tmp[0]);
|
2008-08-14 17:27:45 -07:00
|
|
|
|
2008-09-18 13:31:26 -07:00
|
|
|
args[0] = r_ins;
|
|
|
|
args[1] = cx_ins;
|
|
|
|
if (r == JSVAL_NULL) {
|
|
|
|
jsdpun u;
|
|
|
|
u.d = js_NaN;
|
|
|
|
r_ins = lir->insImmq(u.u64);
|
|
|
|
} else if (JSVAL_IS_STRING(r)) {
|
2008-10-16 12:24:10 -07:00
|
|
|
r_ins = lir->insCall(&js_StringToNumber_ci, args);
|
2008-09-18 13:31:26 -07:00
|
|
|
} else if (JSVAL_TAG(r) == JSVAL_BOOLEAN) {
|
|
|
|
// See above for the sob story.
|
2008-10-16 12:24:10 -07:00
|
|
|
r_ins = lir->insCall(&js_BooleanToNumber_ci, args);
|
2008-09-18 13:31:26 -07:00
|
|
|
} else if (!isNumber(r)) {
|
|
|
|
ABORT_TRACE("unsupported RHS type for cmp vs number");
|
2008-07-04 03:06:18 -07:00
|
|
|
}
|
2008-09-18 13:31:26 -07:00
|
|
|
rnum = js_ValueToNumber(cx, &tmp[1]);
|
|
|
|
cond = evalCmp(op, lnum, rnum);
|
2008-09-26 19:20:52 -07:00
|
|
|
} else if ((JSVAL_TAG(l) == JSVAL_BOOLEAN) && (JSVAL_TAG(r) == JSVAL_BOOLEAN)) {
|
2008-08-25 16:58:18 -07:00
|
|
|
// The well-known values of JSVAL_TRUE and JSVAL_FALSE make this very easy.
|
|
|
|
// In particular: JSVAL_TO_BOOLEAN(0) < JSVAL_TO_BOOLEAN(1) so all of these comparisons do
|
|
|
|
// the right thing.
|
2008-09-18 13:31:26 -07:00
|
|
|
cond = evalCmp(op, l, r);
|
2008-09-26 19:20:52 -07:00
|
|
|
// For ==, !=, ===, and !=== the result is magically correct even if undefined (2) is
|
|
|
|
// involved. For the relational operations we need some additional cmov magic to make
|
|
|
|
// the result always false (since undefined becomes NaN per ECMA and that doesn't
|
|
|
|
// compare to anything, even itself). The code for this is emitted a few lines down.
|
2008-09-18 13:31:26 -07:00
|
|
|
} else if (JSVAL_IS_OBJECT(l) && JSVAL_IS_OBJECT(r)) {
|
|
|
|
if (op != LIR_feq) {
|
|
|
|
negate = !(op == LIR_fle || op == LIR_fge);
|
|
|
|
op = LIR_feq;
|
2008-08-25 16:58:18 -07:00
|
|
|
}
|
2008-09-18 13:31:26 -07:00
|
|
|
cond = (l == r);
|
2008-08-09 18:50:52 -07:00
|
|
|
} else {
|
2008-08-11 22:05:33 -07:00
|
|
|
ABORT_TRACE("unsupported operand types for cmp");
|
2008-07-04 03:06:18 -07:00
|
|
|
}
|
2008-08-09 18:50:52 -07:00
|
|
|
|
2008-09-30 09:45:00 -07:00
|
|
|
/* If we didn't generate a constant result yet, then emit the comparison now. */
|
2008-09-18 13:31:26 -07:00
|
|
|
if (!x) {
|
2008-09-30 09:45:00 -07:00
|
|
|
/* If the result is not a number or it's not a quad, we must use an integer compare. */
|
2008-10-01 16:04:39 -07:00
|
|
|
if (!fp) {
|
2008-09-18 13:31:26 -07:00
|
|
|
JS_ASSERT(op >= LIR_feq && op <= LIR_fge);
|
|
|
|
op = LOpcode(op + (LIR_eq - LIR_feq));
|
|
|
|
}
|
|
|
|
x = lir->ins2(op, l_ins, r_ins);
|
|
|
|
if (negate) {
|
|
|
|
x = lir->ins_eq0(x);
|
|
|
|
cond = !cond;
|
|
|
|
}
|
2008-09-26 19:20:52 -07:00
|
|
|
// For boolean comparison we need a bit post-processing to make the result false if
|
|
|
|
// either side is undefined.
|
|
|
|
if (op != LIR_eq && (JSVAL_TAG(l) == JSVAL_BOOLEAN) && (JSVAL_TAG(r) == JSVAL_BOOLEAN)) {
|
|
|
|
x = lir->ins_choose(lir->ins2i(LIR_eq,
|
|
|
|
lir->ins2i(LIR_and,
|
|
|
|
lir->ins2(LIR_or, l_ins, r_ins),
|
|
|
|
JSVAL_TO_BOOLEAN(JSVAL_VOID)),
|
|
|
|
JSVAL_TO_BOOLEAN(JSVAL_VOID)),
|
|
|
|
lir->insImm(JSVAL_TO_BOOLEAN(JSVAL_FALSE)),
|
2008-09-26 20:39:21 -07:00
|
|
|
x);
|
2008-10-07 13:32:30 -07:00
|
|
|
x = lir->ins_eq0(lir->ins_eq0(x));
|
2008-09-26 19:20:52 -07:00
|
|
|
if ((l == JSVAL_VOID) || (r == JSVAL_VOID))
|
|
|
|
cond = false;
|
|
|
|
}
|
2008-09-18 13:31:26 -07:00
|
|
|
}
|
|
|
|
|
2008-09-04 19:43:07 -07:00
|
|
|
/* Don't guard if the same path is always taken. */
|
2008-09-18 13:31:26 -07:00
|
|
|
if (!x->isconst()) {
|
2008-09-04 19:43:07 -07:00
|
|
|
if (flags & CMP_CASE) {
|
|
|
|
guard(cond, x, BRANCH_EXIT);
|
|
|
|
return true;
|
|
|
|
}
|
2008-08-30 00:01:59 -07:00
|
|
|
|
2008-09-04 19:43:07 -07:00
|
|
|
/* The interpreter fuses comparisons and the following branch,
|
|
|
|
so we have to do that here as well. */
|
2008-09-04 20:11:58 -07:00
|
|
|
if (flags & CMP_TRY_BRANCH_AFTER_COND) {
|
2008-09-04 19:43:07 -07:00
|
|
|
fuseIf(cx->fp->regs->pc + 1, cond, x);
|
2008-09-04 20:11:58 -07:00
|
|
|
}
|
|
|
|
} else if (flags & CMP_CASE) {
|
|
|
|
return true;
|
2008-09-04 19:43:07 -07:00
|
|
|
}
|
2008-08-18 15:32:17 -07:00
|
|
|
|
2008-08-09 18:50:52 -07:00
|
|
|
/* We update the stack after the guard. This is safe since
|
|
|
|
the guard bails out at the comparison and the interpreter
|
2008-08-28 21:15:39 -07:00
|
|
|
will therefore re-execute the comparison. This way the
|
2008-08-09 18:50:52 -07:00
|
|
|
value of the condition doesn't have to be calculated and
|
|
|
|
saved on the stack in most cases. */
|
|
|
|
set(&l, x);
|
|
|
|
return true;
|
2008-07-04 03:06:18 -07:00
|
|
|
}
|
|
|
|
|
2008-07-05 11:35:55 -07:00
|
|
|
bool
|
2008-07-06 13:16:34 -07:00
|
|
|
TraceRecorder::unary(LOpcode op)
|
2008-07-05 11:35:55 -07:00
|
|
|
{
|
|
|
|
jsval& v = stackval(-1);
|
2008-07-06 13:16:34 -07:00
|
|
|
bool intop = !(op & LIR64);
|
2008-07-05 22:04:58 -07:00
|
|
|
if (isNumber(v)) {
|
2008-07-06 13:16:34 -07:00
|
|
|
LIns* a = get(&v);
|
|
|
|
if (intop)
|
2008-07-06 15:55:04 -07:00
|
|
|
a = f2i(a);
|
2008-07-06 13:16:34 -07:00
|
|
|
a = lir->ins1(op, a);
|
|
|
|
if (intop)
|
|
|
|
a = lir->ins1(LIR_i2f, a);
|
|
|
|
set(&v, a);
|
2008-07-05 11:35:55 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-07-04 15:21:56 -07:00
|
|
|
bool
|
2008-07-06 13:16:34 -07:00
|
|
|
TraceRecorder::binary(LOpcode op)
|
2008-07-04 15:21:56 -07:00
|
|
|
{
|
|
|
|
jsval& r = stackval(-1);
|
|
|
|
jsval& l = stackval(-2);
|
2008-07-06 13:16:34 -07:00
|
|
|
bool intop = !(op & LIR64);
|
2008-07-31 07:41:58 -07:00
|
|
|
LIns* a = get(&l);
|
|
|
|
LIns* b = get(&r);
|
|
|
|
bool leftNumber = isNumber(l), rightNumber = isNumber(r);
|
|
|
|
if ((op >= LIR_sub && op <= LIR_ush) || // sub, mul, (callh), or, xor, (not,) lsh, rsh, ush
|
|
|
|
(op >= LIR_fsub && op <= LIR_fdiv)) { // fsub, fmul, fdiv
|
2008-08-29 14:22:21 -07:00
|
|
|
LIns* args[2];
|
2008-07-31 07:41:58 -07:00
|
|
|
if (JSVAL_IS_STRING(l)) {
|
|
|
|
args[0] = a;
|
2008-08-29 14:22:21 -07:00
|
|
|
args[1] = cx_ins;
|
2008-10-16 12:24:10 -07:00
|
|
|
a = lir->insCall(&js_StringToNumber_ci, args);
|
2008-07-31 07:41:58 -07:00
|
|
|
leftNumber = true;
|
|
|
|
}
|
|
|
|
if (JSVAL_IS_STRING(r)) {
|
|
|
|
args[0] = b;
|
2008-08-29 14:22:21 -07:00
|
|
|
args[1] = cx_ins;
|
2008-10-16 12:24:10 -07:00
|
|
|
b = lir->insCall(&js_StringToNumber_ci, args);
|
2008-07-31 07:41:58 -07:00
|
|
|
rightNumber = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (leftNumber && rightNumber) {
|
2008-07-06 13:16:34 -07:00
|
|
|
if (intop) {
|
2008-08-29 14:22:21 -07:00
|
|
|
LIns *args[] = { a };
|
2008-10-16 12:24:10 -07:00
|
|
|
a = lir->insCall(op == LIR_ush ? &js_DoubleToUint32_ci : &js_DoubleToInt32_ci, args);
|
2008-07-06 15:55:04 -07:00
|
|
|
b = f2i(b);
|
2008-07-06 13:16:34 -07:00
|
|
|
}
|
|
|
|
a = lir->ins2(op, a, b);
|
|
|
|
if (intop)
|
|
|
|
a = lir->ins1(op == LIR_ush ? LIR_u2f : LIR_i2f, a);
|
|
|
|
set(&l, a);
|
2008-07-04 15:21:56 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-08-10 11:54:27 -07:00
|
|
|
JS_STATIC_ASSERT(offsetof(JSObjectOps, newObjectMap) == 0);
|
|
|
|
|
2008-07-04 23:53:29 -07:00
|
|
|
bool
|
2008-08-10 11:54:27 -07:00
|
|
|
TraceRecorder::map_is_native(JSObjectMap* map, LIns* map_ins, LIns*& ops_ins, size_t op_offset)
|
2008-07-04 23:53:29 -07:00
|
|
|
{
|
2008-08-18 12:54:04 -07:00
|
|
|
ops_ins = addName(lir->insLoad(LIR_ldp, map_ins, offsetof(JSObjectMap, ops)), "ops");
|
|
|
|
LIns* n = lir->insLoad(LIR_ldp, ops_ins, op_offset);
|
2008-08-10 11:54:27 -07:00
|
|
|
|
|
|
|
#define OP(ops) (*(JSObjectOp*) ((char*)(ops) + op_offset))
|
|
|
|
|
|
|
|
if (OP(map->ops) == OP(&js_ObjectOps)) {
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
guard(true, addName(lir->ins2(LIR_eq, n, INS_CONSTPTR(OP(&js_ObjectOps))),
|
2008-08-12 09:27:54 -07:00
|
|
|
"guard(native-map)"),
|
|
|
|
MISMATCH_EXIT);
|
2008-07-04 23:53:29 -07:00
|
|
|
return true;
|
|
|
|
}
|
2008-08-10 11:54:27 -07:00
|
|
|
|
|
|
|
#undef OP
|
2008-07-11 17:59:10 -07:00
|
|
|
ABORT_TRACE("non-native map");
|
2008-07-04 23:53:29 -07:00
|
|
|
}
|
|
|
|
|
2008-07-06 09:15:55 -07:00
|
|
|
bool
|
2008-07-17 23:57:56 -07:00
|
|
|
TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2, jsuword& pcval)
|
2008-07-06 09:15:55 -07:00
|
|
|
{
|
2008-09-09 22:22:52 -07:00
|
|
|
jsbytecode* pc = cx->fp->regs->pc;
|
|
|
|
JS_ASSERT(*pc != JSOP_INITPROP && *pc != JSOP_SETNAME && *pc != JSOP_SETPROP);
|
|
|
|
|
2008-08-26 21:26:10 -07:00
|
|
|
// Mimic the interpreter's special case for dense arrays by skipping up one
|
|
|
|
// hop along the proto chain when accessing a named (not indexed) property,
|
|
|
|
// typically to find Array.prototype methods.
|
2008-08-10 11:54:27 -07:00
|
|
|
JSObject* aobj = obj;
|
2008-08-11 00:05:39 -07:00
|
|
|
if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
|
2008-08-10 11:54:27 -07:00
|
|
|
aobj = OBJ_GET_PROTO(cx, obj);
|
|
|
|
obj_ins = stobj_get_fslot(obj_ins, JSSLOT_PROTO);
|
|
|
|
}
|
|
|
|
|
2008-08-18 12:54:04 -07:00
|
|
|
LIns* map_ins = lir->insLoad(LIR_ldp, obj_ins, (int)offsetof(JSObject, map));
|
2008-07-30 16:32:33 -07:00
|
|
|
LIns* ops_ins;
|
2008-08-10 11:54:27 -07:00
|
|
|
|
|
|
|
// Interpreter calls to PROPERTY_CACHE_TEST guard on native object ops
|
|
|
|
// (newObjectMap == js_ObjectOps.newObjectMap) which is required to use
|
|
|
|
// native objects (those whose maps are scopes), or even more narrow
|
|
|
|
// conditions required because the cache miss case will call a particular
|
|
|
|
// object-op (js_GetProperty, js_SetProperty).
|
|
|
|
//
|
|
|
|
// We parameterize using offsetof and guard on match against the hook at
|
|
|
|
// the given offset in js_ObjectOps. TraceRecorder::record_JSOP_SETPROP
|
|
|
|
// guards the js_SetProperty case.
|
2008-09-09 22:22:52 -07:00
|
|
|
uint32 format = js_CodeSpec[*pc].format;
|
2008-08-10 11:54:27 -07:00
|
|
|
uint32 mode = JOF_MODE(format);
|
|
|
|
|
2008-09-09 16:01:01 -07:00
|
|
|
// No need to guard native-ness of global object.
|
|
|
|
JS_ASSERT(OBJ_IS_NATIVE(globalObj));
|
|
|
|
if (aobj != globalObj) {
|
|
|
|
size_t op_offset = 0;
|
|
|
|
if (mode == JOF_PROP || mode == JOF_VARPROP) {
|
|
|
|
JS_ASSERT(!(format & JOF_SET));
|
|
|
|
op_offset = offsetof(JSObjectOps, getProperty);
|
|
|
|
} else {
|
|
|
|
JS_ASSERT(mode == JOF_NAME);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!map_is_native(aobj->map, map_ins, ops_ins, op_offset))
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-06 09:15:55 -07:00
|
|
|
|
|
|
|
JSAtom* atom;
|
2008-07-17 23:57:56 -07:00
|
|
|
JSPropCacheEntry* entry;
|
2008-09-09 22:22:52 -07:00
|
|
|
PROPERTY_CACHE_TEST(cx, pc, aobj, obj2, entry, atom);
|
2008-08-26 21:26:10 -07:00
|
|
|
if (atom) {
|
|
|
|
// Miss: pre-fill the cache for the interpreter, as well as for our needs.
|
|
|
|
// FIXME: 452357 - correctly propagate exceptions into the interpreter from
|
|
|
|
// js_FindPropertyHelper, js_LookupPropertyWithFlags, and elsewhere.
|
|
|
|
jsid id = ATOM_TO_JSID(atom);
|
|
|
|
JSProperty* prop;
|
2008-09-09 22:22:52 -07:00
|
|
|
if (JOF_OPMODE(*pc) == JOF_NAME) {
|
2008-08-26 21:26:10 -07:00
|
|
|
JS_ASSERT(aobj == obj);
|
|
|
|
if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0)
|
|
|
|
ABORT_TRACE("failed to find name");
|
|
|
|
} else {
|
2008-09-25 09:13:31 -07:00
|
|
|
int protoIndex = js_LookupPropertyWithFlags(cx, aobj, id,
|
|
|
|
cx->resolveFlags,
|
|
|
|
&obj2, &prop);
|
2008-08-26 21:26:10 -07:00
|
|
|
if (protoIndex < 0)
|
|
|
|
ABORT_TRACE("failed to lookup property");
|
2008-07-06 10:38:55 -07:00
|
|
|
|
2008-08-26 21:26:10 -07:00
|
|
|
if (prop) {
|
2008-09-09 09:57:10 -07:00
|
|
|
js_FillPropertyCache(cx, aobj, OBJ_SHAPE(aobj), 0, protoIndex, obj2,
|
2008-08-26 21:26:10 -07:00
|
|
|
(JSScopeProperty*) prop, &entry);
|
|
|
|
}
|
2008-08-07 18:12:31 -07:00
|
|
|
}
|
|
|
|
|
2008-08-26 21:26:10 -07:00
|
|
|
if (!prop) {
|
|
|
|
// Propagate obj from js_FindPropertyHelper to record_JSOP_BINDNAME
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
// via our obj2 out-parameter. If we are recording JSOP_SETNAME and
|
|
|
|
// the global it's assigning does not yet exist, create it.
|
2008-08-26 21:26:10 -07:00
|
|
|
obj2 = obj;
|
2008-09-09 22:22:52 -07:00
|
|
|
|
|
|
|
// Use PCVAL_NULL to return "no such property" to our caller.
|
|
|
|
pcval = PCVAL_NULL;
|
2008-09-26 19:18:00 -07:00
|
|
|
ABORT_TRACE("failed to find property");
|
2008-08-26 21:26:10 -07:00
|
|
|
}
|
2008-07-07 02:21:04 -07:00
|
|
|
|
2008-08-07 18:12:31 -07:00
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
2008-08-26 21:26:10 -07:00
|
|
|
if (!entry)
|
|
|
|
ABORT_TRACE("failed to fill property cache");
|
2008-08-07 18:12:31 -07:00
|
|
|
}
|
2008-07-29 16:01:00 -07:00
|
|
|
|
2008-08-26 21:26:10 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
// There's a potential race in any JS_THREADSAFE embedding that's nuts
|
|
|
|
// enough to share mutable objects on the scope or proto chain, but we
|
|
|
|
// don't care about such insane embeddings. Anyway, the (scope, proto)
|
|
|
|
// entry->vcap coordinates must reach obj2 from aobj at this point.
|
|
|
|
JS_ASSERT(cx->requestDepth);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Emit guard(s), common code for both hit and miss cases.
|
|
|
|
// Check for first-level cache hit and guard on kshape if possible.
|
|
|
|
// Otherwise guard on key object exact match.
|
2008-08-12 17:21:32 -07:00
|
|
|
if (PCVCAP_TAG(entry->vcap) <= 1) {
|
|
|
|
if (aobj != globalObj) {
|
2008-08-20 22:40:39 -07:00
|
|
|
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)),
|
|
|
|
"shape");
|
2008-08-26 21:26:10 -07:00
|
|
|
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, entry->kshape), "guard(kshape)"),
|
2008-08-11 17:40:26 -07:00
|
|
|
MISMATCH_EXIT);
|
2008-08-07 18:12:31 -07:00
|
|
|
}
|
2008-08-12 17:21:32 -07:00
|
|
|
} else {
|
2008-08-28 21:15:39 -07:00
|
|
|
#ifdef DEBUG
|
2008-09-09 22:22:52 -07:00
|
|
|
JSOp op = JSOp(*pc);
|
2008-08-29 00:58:10 -07:00
|
|
|
ptrdiff_t pcoff = (op == JSOP_GETARGPROP) ? ARGNO_LEN :
|
|
|
|
(op == JSOP_GETLOCALPROP) ? SLOTNO_LEN : 0;
|
2008-09-09 22:22:52 -07:00
|
|
|
jsatomid index = js_GetIndexFromBytecode(cx, cx->fp->script, pc, pcoff);
|
2008-08-28 21:15:39 -07:00
|
|
|
JS_ASSERT(entry->kpc == (jsbytecode*) atoms[index]);
|
2008-08-12 17:21:32 -07:00
|
|
|
JS_ASSERT(entry->kshape == jsuword(aobj));
|
2008-08-28 21:15:39 -07:00
|
|
|
#endif
|
2008-08-26 21:26:10 -07:00
|
|
|
if (aobj != globalObj) {
|
|
|
|
guard(true, addName(lir->ins2i(LIR_eq, obj_ins, entry->kshape), "guard(kobj)"),
|
|
|
|
MISMATCH_EXIT);
|
|
|
|
}
|
2008-08-12 17:21:32 -07:00
|
|
|
}
|
2008-07-29 16:01:00 -07:00
|
|
|
|
2008-08-26 21:26:10 -07:00
|
|
|
// For any hit that goes up the scope and or proto chains, we will need to
|
|
|
|
// guard on the shape of the object containing the property.
|
2008-08-12 17:21:32 -07:00
|
|
|
if (PCVCAP_TAG(entry->vcap) >= 1) {
|
2008-08-26 21:26:10 -07:00
|
|
|
jsuword vcap = entry->vcap;
|
|
|
|
uint32 vshape = PCVCAP_SHAPE(vcap);
|
2008-09-09 09:57:10 -07:00
|
|
|
JS_ASSERT(OBJ_SHAPE(obj2) == vshape);
|
2008-07-29 16:01:00 -07:00
|
|
|
|
2008-08-26 21:26:10 -07:00
|
|
|
LIns* obj2_ins = INS_CONSTPTR(obj2);
|
2008-08-18 12:54:04 -07:00
|
|
|
map_ins = lir->insLoad(LIR_ldp, obj2_ins, (int)offsetof(JSObject, map));
|
2008-08-26 21:26:10 -07:00
|
|
|
if (!map_is_native(obj2->map, map_ins, ops_ins))
|
2008-08-12 17:21:32 -07:00
|
|
|
return false;
|
|
|
|
|
2008-08-26 21:26:10 -07:00
|
|
|
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)),
|
|
|
|
"shape");
|
2008-08-12 17:21:32 -07:00
|
|
|
guard(true,
|
2008-08-26 21:26:10 -07:00
|
|
|
addName(lir->ins2i(LIR_eq, shape_ins, vshape), "guard(vshape)"),
|
2008-08-12 17:21:32 -07:00
|
|
|
MISMATCH_EXIT);
|
2008-07-29 16:01:00 -07:00
|
|
|
}
|
|
|
|
|
2008-08-26 21:26:10 -07:00
|
|
|
pcval = entry->vword;
|
|
|
|
return true;
|
2008-07-06 09:15:55 -07:00
|
|
|
}
|
|
|
|
|
2008-07-09 11:42:31 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::test_property_cache_direct_slot(JSObject* obj, LIns* obj_ins, uint32& slot)
|
|
|
|
{
|
|
|
|
JSObject* obj2;
|
2008-07-17 23:57:56 -07:00
|
|
|
jsuword pcval;
|
2008-07-09 11:42:31 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Property cache ensures that we are dealing with an existing property,
|
|
|
|
* and guards the shape for us.
|
|
|
|
*/
|
2008-07-17 23:57:56 -07:00
|
|
|
if (!test_property_cache(obj, obj_ins, obj2, pcval))
|
2008-07-09 11:42:31 -07:00
|
|
|
return false;
|
|
|
|
|
2008-08-07 18:12:31 -07:00
|
|
|
/* No such property means invalid slot, which callers must check for first. */
|
|
|
|
if (PCVAL_IS_NULL(pcval)) {
|
|
|
|
slot = SPROP_INVALID_SLOT;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-09-15 19:40:43 -07:00
|
|
|
/* Insist on obj being the directly addressed object. */
|
|
|
|
if (obj2 != obj)
|
|
|
|
ABORT_TRACE("test_property_cache_direct_slot hit prototype chain");
|
2008-07-09 11:42:31 -07:00
|
|
|
|
2008-08-06 17:54:36 -07:00
|
|
|
/* Don't trace getter or setter calls, our caller wants a direct slot. */
|
2008-07-17 23:57:56 -07:00
|
|
|
if (PCVAL_IS_SPROP(pcval)) {
|
|
|
|
JSScopeProperty* sprop = PCVAL_TO_SPROP(pcval);
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-09-15 19:40:43 -07:00
|
|
|
uint32 setflags = (js_CodeSpec[*cx->fp->regs->pc].format & (JOF_SET | JOF_INCDEC));
|
2008-08-12 18:52:28 -07:00
|
|
|
if (setflags && !SPROP_HAS_STUB_SETTER(sprop))
|
|
|
|
ABORT_TRACE("non-stub setter");
|
|
|
|
if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop))
|
|
|
|
ABORT_TRACE("non-stub getter");
|
2008-07-09 16:37:31 -07:00
|
|
|
if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)))
|
2008-07-11 17:59:10 -07:00
|
|
|
ABORT_TRACE("no valid slot");
|
2008-07-09 16:37:31 -07:00
|
|
|
slot = sprop->slot;
|
|
|
|
} else {
|
2008-07-17 23:57:56 -07:00
|
|
|
if (!PCVAL_IS_SLOT(pcval))
|
2008-07-11 17:59:10 -07:00
|
|
|
ABORT_TRACE("PCE is not a slot");
|
2008-07-17 23:57:56 -07:00
|
|
|
slot = PCVAL_TO_SLOT(pcval);
|
2008-07-09 16:37:31 -07:00
|
|
|
}
|
2008-07-09 11:42:31 -07:00
|
|
|
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)
|
2008-07-05 11:35:55 -07:00
|
|
|
{
|
2008-07-07 02:55:03 -07:00
|
|
|
if (slot < JS_INITIAL_NSLOTS) {
|
2008-07-17 02:00:23 -07:00
|
|
|
addName(lir->insStorei(v_ins, obj_ins,
|
|
|
|
offsetof(JSObject, fslots) + slot * sizeof(jsval)),
|
|
|
|
"set_slot(fslots)");
|
2008-07-07 02:55:03 -07:00
|
|
|
} else {
|
2008-07-05 13:44:48 -07:00
|
|
|
if (!dslots_ins)
|
2008-08-18 12:54:04 -07:00
|
|
|
dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots));
|
2008-07-17 02:00:23 -07:00
|
|
|
addName(lir->insStorei(v_ins, dslots_ins,
|
|
|
|
(slot - JS_INITIAL_NSLOTS) * sizeof(jsval)),
|
|
|
|
"set_slot(dslots");
|
2008-07-05 13:44:48 -07:00
|
|
|
}
|
2008-07-07 02:21:04 -07:00
|
|
|
}
|
2008-07-05 11:35:55 -07:00
|
|
|
|
2008-07-30 16:32:33 -07:00
|
|
|
LIns*
|
|
|
|
TraceRecorder::stobj_get_fslot(LIns* obj_ins, unsigned slot)
|
|
|
|
{
|
|
|
|
JS_ASSERT(slot < JS_INITIAL_NSLOTS);
|
2008-08-18 12:54:04 -07:00
|
|
|
return lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, fslots) + slot * sizeof(jsval));
|
2008-07-30 16:32:33 -07:00
|
|
|
}
|
|
|
|
|
2008-07-06 09:15:55 -07:00
|
|
|
LIns*
|
|
|
|
TraceRecorder::stobj_get_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins)
|
2008-07-05 11:35:55 -07:00
|
|
|
{
|
2008-07-30 16:32:33 -07:00
|
|
|
if (slot < JS_INITIAL_NSLOTS)
|
|
|
|
return stobj_get_fslot(obj_ins, slot);
|
2008-07-06 09:15:55 -07:00
|
|
|
|
|
|
|
if (!dslots_ins)
|
2008-08-18 12:54:04 -07:00
|
|
|
dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots));
|
|
|
|
return lir->insLoad(LIR_ldp, dslots_ins, (slot - JS_INITIAL_NSLOTS) * sizeof(jsval));
|
2008-07-07 02:21:04 -07:00
|
|
|
}
|
2008-07-05 11:35:55 -07:00
|
|
|
|
|
|
|
bool
|
2008-07-05 13:44:48 -07:00
|
|
|
TraceRecorder::native_set(LIns* obj_ins, JSScopeProperty* sprop, LIns*& dslots_ins, LIns* v_ins)
|
2008-07-05 11:35:55 -07:00
|
|
|
{
|
|
|
|
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;
|
2008-07-05 11:35:55 -07:00
|
|
|
}
|
2008-07-17 02:00:23 -07:00
|
|
|
ABORT_TRACE("unallocated or non-stub sprop");
|
2008-07-05 11:35:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2008-07-07 02:21:04 -07:00
|
|
|
TraceRecorder::native_get(LIns* obj_ins, LIns* pobj_ins, JSScopeProperty* sprop,
|
2008-07-05 13:44:48 -07:00
|
|
|
LIns*& dslots_ins, LIns*& v_ins)
|
2008-07-05 11:35:55 -07:00
|
|
|
{
|
2008-07-06 09:15:55 -07:00
|
|
|
if (!SPROP_HAS_STUB_GETTER(sprop))
|
|
|
|
return false;
|
|
|
|
|
2008-07-07 02:21:04 -07:00
|
|
|
if (sprop->slot != SPROP_INVALID_SLOT)
|
2008-07-06 09:15:55 -07:00
|
|
|
v_ins = stobj_get_slot(pobj_ins, sprop->slot, dslots_ins);
|
|
|
|
else
|
2008-08-28 23:50:48 -07:00
|
|
|
v_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
|
2008-07-06 09:15:55 -07:00
|
|
|
return true;
|
2008-07-07 02:21:04 -07:00
|
|
|
}
|
2008-07-05 11:35:55 -07:00
|
|
|
|
2008-08-08 14:38:44 -07:00
|
|
|
// So box_jsval can emit no LIR_or at all to tag an object jsval.
|
|
|
|
JS_STATIC_ASSERT(JSVAL_OBJECT == 0);
|
|
|
|
|
2008-07-06 13:59:59 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::box_jsval(jsval v, LIns*& v_ins)
|
|
|
|
{
|
2008-07-06 16:18:45 -07:00
|
|
|
if (isNumber(v)) {
|
2008-07-06 20:25:49 -07:00
|
|
|
LIns* args[] = { v_ins, cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
v_ins = lir->insCall(&js_BoxDouble_ci, args);
|
2008-08-28 21:15:39 -07:00
|
|
|
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)),
|
2008-07-28 23:47:20 -07:00
|
|
|
OOM_EXIT);
|
2008-07-06 16:18:45 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
switch (JSVAL_TAG(v)) {
|
2008-07-10 21:55:09 -07:00
|
|
|
case JSVAL_BOOLEAN:
|
2008-08-27 16:08:59 -07:00
|
|
|
v_ins = lir->ins2i(LIR_pior, lir->ins2i(LIR_pilsh, v_ins, JSVAL_TAGBITS), JSVAL_BOOLEAN);
|
2008-07-06 16:18:45 -07:00
|
|
|
return true;
|
2008-08-08 14:38:44 -07:00
|
|
|
case JSVAL_OBJECT:
|
|
|
|
return true;
|
2008-07-29 07:32:18 -07:00
|
|
|
case JSVAL_STRING:
|
2008-08-27 16:08:59 -07:00
|
|
|
v_ins = lir->ins2(LIR_pior, v_ins, INS_CONST(JSVAL_STRING));
|
2008-07-29 07:32:18 -07:00
|
|
|
return true;
|
2008-07-06 16:18:45 -07:00
|
|
|
}
|
|
|
|
return false;
|
2008-07-06 13:59:59 -07:00
|
|
|
}
|
|
|
|
|
2008-07-06 10:38:55 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
|
|
|
|
{
|
2008-07-06 16:18:45 -07:00
|
|
|
if (isNumber(v)) {
|
|
|
|
// JSVAL_IS_NUMBER(v)
|
2008-07-19 00:15:22 -07:00
|
|
|
guard(false,
|
2008-08-27 16:08:59 -07:00
|
|
|
lir->ins_eq0(lir->ins2(LIR_pior,
|
2008-08-27 16:32:11 -07:00
|
|
|
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_INT)),
|
2008-07-19 00:15:22 -07:00
|
|
|
lir->ins2i(LIR_eq,
|
2008-08-18 16:23:37 -07:00
|
|
|
lir->ins2(LIR_piand, v_ins,
|
2008-08-27 16:32:11 -07:00
|
|
|
INS_CONST(JSVAL_TAGMASK)),
|
2008-08-12 09:27:54 -07:00
|
|
|
JSVAL_DOUBLE))),
|
2008-09-19 12:07:46 -07:00
|
|
|
MISMATCH_EXIT);
|
2008-08-29 14:22:21 -07:00
|
|
|
LIns* args[] = { v_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
v_ins = lir->insCall(&js_UnboxDouble_ci, args);
|
2008-07-06 16:18:45 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
switch (JSVAL_TAG(v)) {
|
2008-07-09 11:42:31 -07:00
|
|
|
case JSVAL_BOOLEAN:
|
|
|
|
guard(true,
|
|
|
|
lir->ins2i(LIR_eq,
|
2008-08-27 16:32:11 -07:00
|
|
|
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
|
2008-08-12 09:27:54 -07:00
|
|
|
JSVAL_BOOLEAN),
|
2008-09-19 12:07:46 -07:00
|
|
|
MISMATCH_EXIT);
|
2008-07-07 02:21:04 -07:00
|
|
|
v_ins = lir->ins2i(LIR_ush, v_ins, JSVAL_TAGBITS);
|
2008-07-06 16:18:45 -07:00
|
|
|
return true;
|
2008-07-11 17:59:10 -07:00
|
|
|
case JSVAL_OBJECT:
|
|
|
|
guard(true,
|
|
|
|
lir->ins2i(LIR_eq,
|
2008-08-27 16:32:11 -07:00
|
|
|
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
|
2008-08-12 09:27:54 -07:00
|
|
|
JSVAL_OBJECT),
|
2008-09-19 12:07:46 -07:00
|
|
|
MISMATCH_EXIT);
|
2008-07-11 17:59:10 -07:00
|
|
|
return true;
|
2008-07-29 07:32:18 -07:00
|
|
|
case JSVAL_STRING:
|
|
|
|
guard(true,
|
|
|
|
lir->ins2i(LIR_eq,
|
2008-08-27 16:32:11 -07:00
|
|
|
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
|
2008-08-12 09:27:54 -07:00
|
|
|
JSVAL_STRING),
|
2008-09-19 12:07:46 -07:00
|
|
|
MISMATCH_EXIT);
|
2008-08-27 16:08:59 -07:00
|
|
|
v_ins = lir->ins2(LIR_piand, v_ins, INS_CONST(~JSVAL_TAGMASK));
|
2008-07-29 07:32:18 -07:00
|
|
|
return true;
|
2008-07-06 16:18:45 -07:00
|
|
|
}
|
|
|
|
return false;
|
2008-07-06 10:38:55 -07:00
|
|
|
}
|
|
|
|
|
2008-07-16 14:36:50 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::getThis(LIns*& this_ins)
|
|
|
|
{
|
|
|
|
if (cx->fp->callee) { /* in a function */
|
|
|
|
if (JSVAL_IS_NULL(cx->fp->argv[-1]))
|
|
|
|
return false;
|
|
|
|
this_ins = get(&cx->fp->argv[-1]);
|
2008-08-11 15:40:04 -07:00
|
|
|
guard(false, lir->ins_eq0(this_ins), MISMATCH_EXIT);
|
2008-07-16 14:36:50 -07:00
|
|
|
} else { /* in global code */
|
2008-08-20 22:40:39 -07:00
|
|
|
this_ins = scopeChain();
|
2008-07-16 14:36:50 -07:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-11 17:59:10 -07:00
|
|
|
bool
|
2008-07-30 16:32:33 -07:00
|
|
|
TraceRecorder::guardClass(JSObject* obj, LIns* obj_ins, JSClass* clasp)
|
2008-07-05 13:44:48 -07:00
|
|
|
{
|
2008-07-30 16:32:33 -07:00
|
|
|
if (STOBJ_GET_CLASS(obj) != clasp)
|
2008-07-05 13:44:48 -07:00
|
|
|
return false;
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-09-09 09:57:10 -07:00
|
|
|
LIns* class_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, classword));
|
2008-08-27 16:08:59 -07:00
|
|
|
class_ins = lir->ins2(LIR_piand, class_ins, lir->insImm(~3));
|
2008-07-30 16:32:33 -07:00
|
|
|
|
|
|
|
char namebuf[32];
|
|
|
|
JS_snprintf(namebuf, sizeof namebuf, "guard(class is %s)", clasp->name);
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
guard(true, addName(lir->ins2(LIR_eq, class_ins, INS_CONSTPTR(clasp)), namebuf),
|
|
|
|
MISMATCH_EXIT);
|
2008-07-05 13:44:48 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::guardDenseArray(JSObject* obj, LIns* obj_ins)
|
2008-07-11 17:59:10 -07:00
|
|
|
{
|
2008-07-30 16:32:33 -07:00
|
|
|
return guardClass(obj, obj_ins, &js_ArrayClass);
|
2008-07-11 17:59:10 -07:00
|
|
|
}
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::guardDenseArrayIndex(JSObject* obj, jsint idx, LIns* obj_ins,
|
|
|
|
LIns* dslots_ins, LIns* idx_ins)
|
2008-07-05 13:44:48 -07:00
|
|
|
{
|
2008-07-31 06:35:11 -07:00
|
|
|
jsuint length = ARRAY_DENSE_LENGTH(obj);
|
|
|
|
if (!((jsuint)idx < length && idx < obj->fslots[JSSLOT_ARRAY_LENGTH]))
|
|
|
|
return false;
|
2008-07-30 16:32:33 -07:00
|
|
|
|
|
|
|
LIns* length_ins = stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH);
|
|
|
|
|
2008-10-06 22:30:36 -07:00
|
|
|
// guard(0 <= index && index < length)
|
|
|
|
guard(true, lir->ins2(LIR_ult, idx_ins, length_ins), MISMATCH_EXIT);
|
2008-07-31 06:35:11 -07:00
|
|
|
|
2008-10-06 22:30:36 -07:00
|
|
|
// At this point, the guard above => 0 < length <=> obj->dslots != null.
|
|
|
|
JS_ASSERT(obj->dslots);
|
2008-07-31 06:35:11 -07:00
|
|
|
|
|
|
|
// guard(index < capacity)
|
2008-08-12 09:27:54 -07:00
|
|
|
guard(true,
|
2008-09-04 10:58:35 -07:00
|
|
|
lir->ins2(LIR_lt, idx_ins, lir->insLoad(LIR_ldp, dslots_ins, 0 - (int)sizeof(jsval))),
|
2008-08-12 09:27:54 -07:00
|
|
|
MISMATCH_EXIT);
|
2008-07-31 06:35:11 -07:00
|
|
|
return true;
|
2008-07-05 11:45:53 -07:00
|
|
|
}
|
|
|
|
|
2008-09-25 15:04:48 -07:00
|
|
|
/*
|
|
|
|
* Guard that a computed property access via an element op (JSOP_GETELEM, etc.)
|
|
|
|
* does not find an alias to a global variable, or a property without a slot,
|
|
|
|
* or a slot-ful property with a getter or setter (depending on op_offset in
|
|
|
|
* JSObjectOps). Finally, beware resolve hooks mutating objects. Oh, and watch
|
|
|
|
* out for bears too ;-).
|
|
|
|
*
|
|
|
|
* One win here is that we do not need to generate a guard that obj_ins does
|
|
|
|
* not result in the global object on trace, because we guard on shape and rule
|
|
|
|
* out obj's shape being the global object's shape at recording time. This is
|
|
|
|
* safe because the global shape cannot change on trace.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
TraceRecorder::guardElemOp(JSObject* obj, LIns* obj_ins, jsid id, size_t op_offset, jsval* vp)
|
|
|
|
{
|
|
|
|
LIns* map_ins = lir->insLoad(LIR_ldp, obj_ins, (int)offsetof(JSObject, map));
|
|
|
|
LIns* ops_ins;
|
|
|
|
if (!map_is_native(obj->map, map_ins, ops_ins, op_offset))
|
|
|
|
return false;
|
|
|
|
|
2008-09-30 20:10:46 -07:00
|
|
|
uint32 shape = OBJ_SHAPE(obj);
|
|
|
|
if (JSID_IS_ATOM(id) && shape == traceMonitor->globalShape)
|
|
|
|
ABORT_TRACE("elem op probably aliases global");
|
|
|
|
|
2008-09-25 15:04:48 -07:00
|
|
|
JSObject* pobj;
|
|
|
|
JSProperty* prop;
|
|
|
|
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (vp)
|
|
|
|
*vp = JSVAL_VOID;
|
|
|
|
if (prop) {
|
|
|
|
bool traceable_slot = true;
|
|
|
|
if (pobj == obj) {
|
|
|
|
JSScopeProperty* sprop = (JSScopeProperty*) prop;
|
|
|
|
traceable_slot = ((op_offset == offsetof(JSObjectOps, getProperty))
|
|
|
|
? SPROP_HAS_STUB_GETTER(sprop)
|
|
|
|
: SPROP_HAS_STUB_SETTER(sprop)) &&
|
|
|
|
SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj));
|
|
|
|
if (vp && traceable_slot)
|
|
|
|
*vp = LOCKED_OBJ_GET_SLOT(obj, sprop->slot);
|
|
|
|
}
|
|
|
|
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
|
|
|
if (pobj != obj)
|
|
|
|
ABORT_TRACE("elem op hit prototype property, can't shape-guard");
|
|
|
|
if (!traceable_slot)
|
|
|
|
ABORT_TRACE("elem op hit direct and slotless getter or setter");
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we got this far, we're almost safe -- but we must check for a rogue resolve hook.
|
|
|
|
if (OBJ_SHAPE(obj) != shape)
|
|
|
|
ABORT_TRACE("resolve hook mutated elem op base object");
|
|
|
|
|
|
|
|
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)), "shape");
|
|
|
|
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, shape), "guard(shape)"), MISMATCH_EXIT);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-24 10:35:10 -07:00
|
|
|
void
|
|
|
|
TraceRecorder::clearFrameSlotsFromCache()
|
|
|
|
{
|
|
|
|
/* Clear out all slots of this frame in the nativeFrameTracker. Different locations on the
|
|
|
|
VM stack might map to different locations on the native stack depending on the
|
|
|
|
number of arguments (i.e.) of the next call, so we have to make sure we map
|
|
|
|
those in to the cache with the right offsets. */
|
|
|
|
JSStackFrame* fp = cx->fp;
|
|
|
|
jsval* vp;
|
|
|
|
jsval* vpstop;
|
2008-08-13 14:02:35 -07:00
|
|
|
if (fp->callee) {
|
2008-08-14 23:22:51 -07:00
|
|
|
vp = &fp->argv[-2];
|
2008-08-25 16:04:10 -07:00
|
|
|
vpstop = &fp->argv[fp->fun->nargs];
|
2008-08-13 14:02:35 -07:00
|
|
|
while (vp < vpstop)
|
|
|
|
nativeFrameTracker.set(vp++, (LIns*)0);
|
|
|
|
}
|
|
|
|
vp = &fp->slots[0];
|
|
|
|
vpstop = &fp->slots[fp->script->nslots];
|
|
|
|
while (vp < vpstop)
|
|
|
|
nativeFrameTracker.set(vp++, (LIns*)0);
|
2008-07-24 10:35:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_EnterFrame()
|
|
|
|
{
|
2008-09-08 13:49:17 -07:00
|
|
|
JSStackFrame* fp = cx->fp;
|
|
|
|
|
2008-07-27 16:28:09 -07:00
|
|
|
if (++callDepth >= MAX_CALLDEPTH)
|
|
|
|
ABORT_TRACE("exceeded maximum call depth");
|
2008-10-13 23:20:39 -07:00
|
|
|
// FIXME: Allow and attempt to inline a single level of recursion until we compile
|
|
|
|
// recursive calls as independent trees (459301).
|
|
|
|
if (fp->script == fp->down->script && fp->down->down && fp->down->down->script == fp->script)
|
2008-09-08 13:49:17 -07:00
|
|
|
ABORT_TRACE("recursive call");
|
|
|
|
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(printf("EnterFrame %s, callDepth=%d\n",
|
|
|
|
js_AtomToPrintableString(cx, cx->fp->fun->atom),
|
|
|
|
callDepth););
|
2008-08-28 23:50:48 -07:00
|
|
|
LIns* void_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
|
2008-08-12 23:21:52 -07:00
|
|
|
|
|
|
|
jsval* vp = &fp->argv[fp->argc];
|
2008-09-16 10:48:54 -07:00
|
|
|
jsval* vpstop = vp + ptrdiff_t(fp->fun->nargs) - ptrdiff_t(fp->argc);
|
2008-09-05 18:29:08 -07:00
|
|
|
if (applyingArguments) {
|
|
|
|
applyingArguments = false;
|
|
|
|
while (vp < vpstop) {
|
|
|
|
JS_ASSERT(vp >= fp->down->regs->sp);
|
2008-08-12 23:21:52 -07:00
|
|
|
nativeFrameTracker.set(vp, (LIns*)0);
|
2008-09-05 18:29:08 -07:00
|
|
|
LIns* arg_ins = get(&fp->down->argv[fp->argc + (vp - vpstop)]);
|
|
|
|
set(vp++, arg_ins, true);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (vp < vpstop) {
|
|
|
|
if (vp >= fp->down->regs->sp)
|
|
|
|
nativeFrameTracker.set(vp, (LIns*)0);
|
|
|
|
set(vp++, void_ins, true);
|
|
|
|
}
|
2008-08-12 23:21:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
vp = &fp->slots[0];
|
|
|
|
vpstop = vp + fp->script->nfixed;
|
|
|
|
while (vp < vpstop)
|
|
|
|
set(vp++, void_ins, true);
|
2008-07-24 10:35:10 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-25 16:23:12 -07:00
|
|
|
bool
|
2008-07-24 10:35:10 -07:00
|
|
|
TraceRecorder::record_LeaveFrame()
|
|
|
|
{
|
2008-08-20 10:15:42 -07:00
|
|
|
debug_only_v(
|
|
|
|
if (cx->fp->fun)
|
|
|
|
printf("LeaveFrame (back to %s), callDepth=%d\n",
|
|
|
|
js_AtomToPrintableString(cx, cx->fp->fun->atom),
|
|
|
|
callDepth);
|
|
|
|
);
|
2008-07-24 10:35:10 -07:00
|
|
|
if (callDepth-- <= 0)
|
2008-08-20 22:40:39 -07:00
|
|
|
ABORT_TRACE("returned out of a loop we started tracing");
|
2008-08-08 14:38:44 -07:00
|
|
|
|
|
|
|
// LeaveFrame gets called after the interpreter popped the frame and
|
|
|
|
// stored rval, so cx->fp not cx->fp->down, and -1 not 0.
|
2008-07-24 10:35:10 -07:00
|
|
|
atoms = cx->fp->script->atomMap.vector;
|
2008-08-13 17:45:49 -07:00
|
|
|
set(&stackval(-1), rval_ins, true);
|
2008-07-24 10:35:10 -07:00
|
|
|
return true;
|
2008-07-22 20:16:12 -07:00
|
|
|
}
|
|
|
|
|
2008-07-10 21:55:09 -07:00
|
|
|
bool TraceRecorder::record_JSOP_INTERRUPT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_PUSH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
stack(0, INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID)));
|
2008-07-06 06:52:50 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_POPV()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-10-15 13:22:05 -07:00
|
|
|
jsval& rval = stackval(-1);
|
|
|
|
LIns *rval_ins = get(&rval);
|
|
|
|
if (!box_jsval(rval, rval_ins))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Store it in cx->fp->rval. NB: Tricky dependencies. cx->fp is the right
|
|
|
|
// frame because POPV appears only in global and eval code and we don't
|
|
|
|
// trace JSOP_EVAL or leaving the frame where tracing started.
|
|
|
|
LIns *fp_ins = lir->insLoad(LIR_ldp, cx_ins, offsetof(JSContext, fp));
|
|
|
|
lir->insStorei(rval_ins, fp_ins, offsetof(JSStackFrame, rval));
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
2008-07-10 21:55:09 -07:00
|
|
|
bool TraceRecorder::record_JSOP_ENTERWITH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
bool TraceRecorder::record_JSOP_LEAVEWITH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_RETURN()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-08 14:38:44 -07:00
|
|
|
jsval& rval = stackval(-1);
|
2008-08-28 11:55:58 -07:00
|
|
|
JSStackFrame *fp = cx->fp;
|
2008-09-03 18:21:15 -07:00
|
|
|
if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && JSVAL_IS_PRIMITIVE(rval)) {
|
|
|
|
JS_ASSERT(OBJECT_TO_JSVAL(fp->thisp) == fp->argv[-1]);
|
|
|
|
rval_ins = get(&fp->argv[-1]);
|
2008-08-28 11:55:58 -07:00
|
|
|
} else {
|
|
|
|
rval_ins = get(&rval);
|
2008-08-08 14:38:44 -07:00
|
|
|
}
|
2008-08-20 23:12:28 -07:00
|
|
|
debug_only_v(printf("returning from %s\n", js_AtomToPrintableString(cx, cx->fp->fun->atom)););
|
2008-07-24 10:35:10 -07:00
|
|
|
clearFrameSlotsFromCache();
|
2008-07-16 15:01:55 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GOTO()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 17:46:25 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_IFEQ()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-29 15:12:17 -07:00
|
|
|
trackCfgMerges(cx->fp->regs->pc);
|
2008-10-08 21:02:04 -07:00
|
|
|
return ifop();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_IFNE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-10-08 21:02:04 -07:00
|
|
|
return ifop();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ARGUMENTS()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-25 08:51:40 -07:00
|
|
|
#if 1
|
|
|
|
ABORT_TRACE("can't trace arguments yet");
|
|
|
|
#else
|
2008-09-05 18:29:08 -07:00
|
|
|
LIns* args[] = { cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
LIns* a_ins = lir->insCall(&js_Arguments_ci, args);
|
2008-09-05 18:29:08 -07:00
|
|
|
guard(false, lir->ins_eq0(a_ins), OOM_EXIT);
|
|
|
|
stack(0, a_ins);
|
|
|
|
return true;
|
2008-09-25 08:51:40 -07:00
|
|
|
#endif
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DUP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-05 20:47:24 -07:00
|
|
|
stack(0, get(&stackval(-1)));
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DUP2()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-05 20:47:24 -07:00
|
|
|
stack(0, get(&stackval(-2)));
|
|
|
|
stack(1, get(&stackval(-1)));
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SETCONST()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_BITOR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 15:55:04 -07:00
|
|
|
return binary(LIR_or);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_BITXOR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 15:55:04 -07:00
|
|
|
return binary(LIR_xor);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_BITAND()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 15:55:04 -07:00
|
|
|
return binary(LIR_and);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_EQ()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-18 13:31:26 -07:00
|
|
|
return cmp(LIR_feq, CMP_TRY_BRANCH_AFTER_COND);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-18 13:31:26 -07:00
|
|
|
return cmp(LIR_feq, CMP_NEGATE | CMP_TRY_BRANCH_AFTER_COND);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_LT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
return cmp(LIR_flt, CMP_TRY_BRANCH_AFTER_COND);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_LE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
return cmp(LIR_fle, CMP_TRY_BRANCH_AFTER_COND);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
return cmp(LIR_fgt, CMP_TRY_BRANCH_AFTER_COND);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
return cmp(LIR_fge, CMP_TRY_BRANCH_AFTER_COND);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_LSH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 15:55:04 -07:00
|
|
|
return binary(LIR_lsh);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_RSH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 15:55:04 -07:00
|
|
|
return binary(LIR_rsh);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_URSH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 15:55:04 -07:00
|
|
|
return binary(LIR_ush);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ADD()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-29 07:53:31 -07:00
|
|
|
jsval& r = stackval(-1);
|
|
|
|
jsval& l = stackval(-2);
|
2008-08-11 20:27:47 -07:00
|
|
|
if (JSVAL_IS_STRING(l)) {
|
|
|
|
LIns* args[] = { NULL, get(&l), cx_ins };
|
|
|
|
if (JSVAL_IS_STRING(r)) {
|
|
|
|
args[0] = get(&r);
|
|
|
|
} else {
|
2008-08-20 02:35:17 -07:00
|
|
|
LIns* args2[] = { get(&r), cx_ins };
|
|
|
|
if (JSVAL_IS_NUMBER(r)) {
|
2008-10-16 12:24:10 -07:00
|
|
|
args[0] = lir->insCall(&js_NumberToString_ci, args2);
|
2008-08-20 02:35:17 -07:00
|
|
|
} else if (JSVAL_IS_OBJECT(r)) {
|
2008-10-16 12:24:10 -07:00
|
|
|
args[0] = lir->insCall(&js_ObjectToString_ci, args2);
|
2008-08-20 02:35:17 -07:00
|
|
|
} else {
|
|
|
|
ABORT_TRACE("untraceable right operand to string-JSOP_ADD");
|
|
|
|
}
|
|
|
|
guard(false, lir->ins_eq0(args[0]), OOM_EXIT);
|
2008-08-11 20:27:47 -07:00
|
|
|
}
|
2008-10-16 12:24:10 -07:00
|
|
|
LIns* concat = lir->insCall(&js_ConcatStrings_ci, args);
|
2008-07-29 11:13:41 -07:00
|
|
|
guard(false, lir->ins_eq0(concat), OOM_EXIT);
|
2008-07-29 07:53:31 -07:00
|
|
|
set(&l, concat);
|
|
|
|
return true;
|
|
|
|
}
|
2008-07-06 15:55:04 -07:00
|
|
|
return binary(LIR_fadd);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SUB()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 15:55:04 -07:00
|
|
|
return binary(LIR_fsub);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_MUL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 15:55:04 -07:00
|
|
|
return binary(LIR_fmul);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DIV()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 15:55:04 -07:00
|
|
|
return binary(LIR_fdiv);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_MOD()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-15 20:19:29 -07:00
|
|
|
jsval& r = stackval(-1);
|
|
|
|
jsval& l = stackval(-2);
|
|
|
|
if (isNumber(l) && isNumber(r)) {
|
2008-09-24 23:28:56 -07:00
|
|
|
LIns* l_ins = get(&l);
|
|
|
|
LIns* r_ins = get(&r);
|
|
|
|
LIns* x;
|
|
|
|
/* We can't demote this in a filter since we need the actual values of l and r. */
|
|
|
|
if (isPromote(l_ins) && isPromote(r_ins) && asNumber(l) >= 0 && asNumber(r) > 0) {
|
|
|
|
LIns* args[] = { ::demote(lir, r_ins), ::demote(lir, l_ins) };
|
2008-10-16 12:24:10 -07:00
|
|
|
x = lir->insCall(&js_imod_ci, args);
|
2008-09-24 23:28:56 -07:00
|
|
|
guard(false, lir->ins2(LIR_eq, x, lir->insImm(-1)), BRANCH_EXIT);
|
|
|
|
x = lir->ins1(LIR_i2f, x);
|
|
|
|
} else {
|
|
|
|
LIns* args[] = { r_ins, l_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
x = lir->insCall(&js_dmod_ci, args);
|
2008-09-24 23:28:56 -07:00
|
|
|
}
|
|
|
|
set(&l, x);
|
2008-07-15 20:19:29 -07:00
|
|
|
return true;
|
|
|
|
}
|
2008-07-03 23:57:57 -07:00
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NOT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-05 17:26:00 -07:00
|
|
|
jsval& v = stackval(-1);
|
2008-09-05 18:57:32 -07:00
|
|
|
if (JSVAL_TAG(v) == JSVAL_BOOLEAN) {
|
|
|
|
set(&v, lir->ins_eq0(lir->ins2i(LIR_eq, get(&v), 1)));
|
|
|
|
return true;
|
2008-09-15 17:08:15 -07:00
|
|
|
}
|
|
|
|
if (isNumber(v)) {
|
|
|
|
set(&v, lir->ins2(LIR_feq, get(&v), lir->insImmq(0)));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (JSVAL_IS_OBJECT(v)) {
|
|
|
|
set(&v, lir->ins_eq0(get(&v)));
|
2008-07-05 17:26:00 -07:00
|
|
|
return true;
|
|
|
|
}
|
2008-10-13 12:10:35 -07:00
|
|
|
JS_ASSERT(JSVAL_IS_STRING(v));
|
|
|
|
set(&v, lir->ins_eq0(lir->ins2(LIR_piand,
|
|
|
|
lir->insLoad(LIR_ldp, get(&v), (int)offsetof(JSString, length)),
|
|
|
|
INS_CONSTPTR(JSSTRING_LENGTH_MASK))));
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_BITNOT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 15:55:04 -07:00
|
|
|
return unary(LIR_not);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NEG()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-03 23:27:58 -07:00
|
|
|
jsval& v = stackval(-1);
|
|
|
|
if (isNumber(v)) {
|
|
|
|
LIns* a = get(&v);
|
|
|
|
|
|
|
|
/* If we're a promoted integer, we have to watch out for 0s since -0 is a double.
|
|
|
|
Only follow this path if we're not an integer that's 0 and we're not a double
|
|
|
|
that's zero.
|
|
|
|
*/
|
|
|
|
if (isPromoteInt(a) &&
|
|
|
|
(!JSVAL_IS_INT(v) || JSVAL_TO_INT(v) != 0) &&
|
|
|
|
(!JSVAL_IS_DOUBLE(v) || !JSDOUBLE_IS_NEGZERO(*JSVAL_TO_DOUBLE(v)))) {
|
|
|
|
a = lir->ins1(LIR_neg, ::demote(lir, a));
|
|
|
|
lir->insGuard(LIR_xt, lir->ins1(LIR_ov, a), snapshot(OVERFLOW_EXIT));
|
|
|
|
lir->insGuard(LIR_xt, lir->ins2(LIR_eq, a, lir->insImm(0)), snapshot(OVERFLOW_EXIT));
|
|
|
|
a = lir->ins1(LIR_i2f, a);
|
|
|
|
} else {
|
|
|
|
a = lir->ins1(LIR_fneg, a);
|
|
|
|
}
|
|
|
|
|
|
|
|
set(&v, a);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
2008-08-21 00:50:20 -07:00
|
|
|
JSBool
|
|
|
|
js_Array(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
|
|
|
|
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
JSBool
|
|
|
|
js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
|
2008-10-06 17:28:22 -07:00
|
|
|
JSBool
|
|
|
|
js_Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
|
2008-10-13 09:04:26 -07:00
|
|
|
JSBool
|
|
|
|
js_fun_apply(JSContext* cx, uintN argc, jsval* vp);
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
2008-10-13 09:04:26 -07:00
|
|
|
TraceRecorder::functionCall(bool constructing)
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-10-13 09:04:26 -07:00
|
|
|
JSStackFrame* fp = cx->fp;
|
|
|
|
jsbytecode *pc = fp->regs->pc;
|
|
|
|
uintN argc = GET_ARGC(pc);
|
2008-08-21 00:50:20 -07:00
|
|
|
jsval& fval = stackval(0 - (2 + argc));
|
2008-10-13 09:04:26 -07:00
|
|
|
JS_ASSERT(&fval >= StackBase(fp));
|
2008-08-08 14:38:44 -07:00
|
|
|
|
2008-08-27 13:52:46 -07:00
|
|
|
jsval& tval = stackval(0 - (argc + 1));
|
|
|
|
LIns* this_ins = get(&tval);
|
2008-10-13 09:04:26 -07:00
|
|
|
|
2008-08-27 13:52:46 -07:00
|
|
|
if (this_ins->isconstp() && !this_ins->constvalp() && !guardShapelessCallee(fval))
|
|
|
|
return false;
|
|
|
|
|
2008-08-08 14:38:44 -07:00
|
|
|
/*
|
|
|
|
* Require that the callee be a function object, to avoid guarding on its
|
|
|
|
* class here. We know if the callee and this were pushed by JSOP_CALLNAME
|
2008-08-10 15:26:39 -07:00
|
|
|
* or JSOP_CALLPROP that callee is a *particular* function, since these hit
|
|
|
|
* the property cache and guard on the object (this) in which the callee
|
|
|
|
* was found. So it's sufficient to test here that the particular function
|
|
|
|
* is interpreted, not guard on that condition.
|
2008-08-08 14:38:44 -07:00
|
|
|
*
|
2008-08-10 15:26:39 -07:00
|
|
|
* Bytecode sequences that push shapeless callees must guard on the callee
|
|
|
|
* class being Function and the function being interpreted.
|
2008-08-08 14:38:44 -07:00
|
|
|
*/
|
2008-08-21 00:50:20 -07:00
|
|
|
JS_ASSERT(VALUE_IS_FUNCTION(cx, fval));
|
2008-10-13 09:04:26 -07:00
|
|
|
JSFunction* fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(fval));
|
2008-08-08 14:38:44 -07:00
|
|
|
|
2008-08-10 15:26:39 -07:00
|
|
|
if (FUN_INTERPRETED(fun)) {
|
2008-10-13 09:04:26 -07:00
|
|
|
if (constructing) {
|
|
|
|
LIns* args[] = { get(&fval), cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
LIns* tv_ins = lir->insCall(&js_FastNewObject_ci, args);
|
2008-10-13 09:04:26 -07:00
|
|
|
guard(false, lir->ins_eq0(tv_ins), OOM_EXIT);
|
|
|
|
set(&tval, tv_ins);
|
|
|
|
}
|
|
|
|
return interpretedFunctionCall(fval, fun, argc, constructing);
|
2008-08-21 00:50:20 -07:00
|
|
|
}
|
|
|
|
|
2008-10-13 09:04:26 -07:00
|
|
|
LIns* arg1_ins = NULL;
|
|
|
|
jsval arg1 = JSVAL_VOID;
|
|
|
|
jsval thisval = tval;
|
|
|
|
if (!constructing && FUN_FAST_NATIVE(fun) == js_fun_apply) {
|
|
|
|
if (argc != 2)
|
|
|
|
ABORT_TRACE("can't trace Function.prototype.apply with other than 2 args");
|
|
|
|
|
|
|
|
if (!guardShapelessCallee(tval))
|
|
|
|
return false;
|
|
|
|
JSObject* tfunobj = JSVAL_TO_OBJECT(tval);
|
|
|
|
JSFunction* tfun = GET_FUNCTION_PRIVATE(cx, tfunobj);
|
|
|
|
|
|
|
|
jsval& oval = stackval(-2);
|
|
|
|
if (JSVAL_IS_PRIMITIVE(oval))
|
|
|
|
ABORT_TRACE("can't trace Function.prototype.apply with primitive 1st arg");
|
|
|
|
|
|
|
|
jsval& aval = stackval(-1);
|
|
|
|
if (JSVAL_IS_PRIMITIVE(aval))
|
|
|
|
ABORT_TRACE("can't trace Function.prototype.apply with primitive 2nd arg");
|
|
|
|
JSObject* aobj = JSVAL_TO_OBJECT(aval);
|
|
|
|
|
|
|
|
LIns* aval_ins = get(&aval);
|
|
|
|
if (!aval_ins->isCall())
|
|
|
|
ABORT_TRACE("can't trace Function.prototype.apply on non-builtin-call 2nd arg");
|
|
|
|
|
2008-10-16 12:24:10 -07:00
|
|
|
if (aval_ins->callInfo() == &js_Arguments_ci) {
|
2008-10-13 09:04:26 -07:00
|
|
|
JS_ASSERT(OBJ_GET_CLASS(cx, aobj) == &js_ArgumentsClass);
|
|
|
|
JS_ASSERT(OBJ_GET_PRIVATE(cx, aobj) == fp);
|
|
|
|
if (!FUN_INTERPRETED(tfun))
|
|
|
|
ABORT_TRACE("can't trace Function.prototype.apply(native_function, arguments)");
|
|
|
|
|
|
|
|
// We can only fasttrack applys where the argument array we pass in has the
|
|
|
|
// same length (fp->argc) as the number of arguments the function expects (tfun->nargs).
|
|
|
|
argc = fp->argc;
|
|
|
|
if (tfun->nargs != argc || fp->fun->nargs != argc)
|
|
|
|
ABORT_TRACE("can't trace Function.prototype.apply(scripted_function, arguments)");
|
|
|
|
|
|
|
|
jsval* sp = fp->regs->sp - 4;
|
|
|
|
set(sp, get(&tval));
|
|
|
|
*sp++ = tval;
|
|
|
|
set(sp, get(&oval));
|
|
|
|
*sp++ = oval;
|
|
|
|
jsval* newsp = sp + argc;
|
|
|
|
if (newsp > fp->slots + fp->script->nslots) {
|
|
|
|
JSArena* a = cx->stackPool.current;
|
|
|
|
if (jsuword(newsp) > a->limit)
|
|
|
|
ABORT_TRACE("can't grow stack for Function.prototype.apply");
|
|
|
|
if (jsuword(newsp) > a->avail)
|
|
|
|
a->avail = jsuword(newsp);
|
|
|
|
}
|
|
|
|
|
|
|
|
jsval* argv = fp->argv;
|
|
|
|
for (uintN i = 0; i < JS_MIN(argc, 2); i++) {
|
|
|
|
set(&sp[i], get(&argv[i]));
|
|
|
|
sp[i] = argv[i];
|
|
|
|
}
|
|
|
|
applyingArguments = true;
|
|
|
|
return interpretedFunctionCall(tval, tfun, argc, false);
|
|
|
|
}
|
|
|
|
|
2008-10-16 12:24:10 -07:00
|
|
|
if (aval_ins->callInfo() != &js_Array_1str_ci)
|
2008-10-13 09:04:26 -07:00
|
|
|
ABORT_TRACE("can't trace Function.prototype.apply on other than [str] 2nd arg");
|
|
|
|
|
|
|
|
JS_ASSERT(OBJ_IS_ARRAY(cx, aobj));
|
|
|
|
JS_ASSERT(aobj->fslots[JSSLOT_ARRAY_LENGTH] == 1);
|
|
|
|
JS_ASSERT(JSVAL_IS_STRING(aobj->dslots[0]));
|
|
|
|
|
|
|
|
if (FUN_INTERPRETED(tfun))
|
|
|
|
ABORT_TRACE("can't trace Function.prototype.apply for scripted functions");
|
|
|
|
|
|
|
|
if (!(tfun->flags & JSFUN_TRACEABLE))
|
|
|
|
ABORT_TRACE("Function.prototype.apply on untraceable native");
|
|
|
|
|
|
|
|
thisval = oval;
|
|
|
|
this_ins = get(&oval);
|
|
|
|
arg1_ins = callArgN(aval_ins, 1);
|
|
|
|
arg1 = aobj->dslots[0];
|
|
|
|
fun = tfun;
|
|
|
|
argc = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!constructing && !(fun->flags & JSFUN_TRACEABLE))
|
|
|
|
ABORT_TRACE("untraceable native");
|
|
|
|
|
2008-08-21 00:50:20 -07:00
|
|
|
static JSTraceableNative knownNatives[] = {
|
2008-10-21 17:50:32 -07:00
|
|
|
{ (JSFastNative)js_Array, &js_FastNewArray_ci, "pC", "", FAIL_NULL | JSTN_MORE },
|
|
|
|
{ (JSFastNative)js_Array, &js_Array_1int_ci, "pC", "i", FAIL_NULL | JSTN_MORE },
|
|
|
|
{ (JSFastNative)js_Array, &js_Array_2obj_ci, "pC", "oo", FAIL_NULL | JSTN_MORE },
|
|
|
|
{ (JSFastNative)js_Array, &js_Array_3num_ci, "pC", "ddd", FAIL_NULL | JSTN_MORE },
|
|
|
|
{ (JSFastNative)js_Object, &js_FastNewObject_ci, "fC", "", FAIL_NULL | JSTN_MORE },
|
|
|
|
{ (JSFastNative)js_Date, &js_FastNewDate_ci, "pC", "", FAIL_NULL },
|
2008-08-21 00:50:20 -07:00
|
|
|
};
|
|
|
|
|
2008-10-13 09:04:26 -07:00
|
|
|
LIns* args[5];
|
|
|
|
JSTraceableNative* known = constructing ? knownNatives : FUN_TRCINFO(fun);
|
|
|
|
do {
|
|
|
|
if (constructing && (JSFastNative)fun->u.n.native != known->native)
|
2008-08-21 00:50:20 -07:00
|
|
|
continue;
|
|
|
|
|
|
|
|
uintN knownargc = strlen(known->argtypes);
|
|
|
|
if (argc != knownargc)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
intN prefixc = strlen(known->prefix);
|
2008-10-13 09:04:26 -07:00
|
|
|
JS_ASSERT(prefixc <= 3);
|
2008-08-21 00:50:20 -07:00
|
|
|
LIns** argp = &args[argc + prefixc - 1];
|
|
|
|
char argtype;
|
|
|
|
|
|
|
|
#if defined _DEBUG
|
|
|
|
memset(args, 0xCD, sizeof(args));
|
|
|
|
#endif
|
|
|
|
|
2008-10-13 09:04:26 -07:00
|
|
|
uintN i;
|
|
|
|
for (i = prefixc; i--; ) {
|
|
|
|
argtype = known->prefix[i];
|
|
|
|
if (argtype == 'C') {
|
|
|
|
*argp = cx_ins;
|
|
|
|
} else if (argtype == 'T') { /* this, as an object */
|
|
|
|
if (!JSVAL_IS_OBJECT(thisval))
|
|
|
|
goto next_specialization;
|
|
|
|
*argp = this_ins;
|
|
|
|
} else if (argtype == 'S') { /* this, as a string */
|
|
|
|
if (!JSVAL_IS_STRING(thisval))
|
|
|
|
goto next_specialization;
|
|
|
|
*argp = this_ins;
|
|
|
|
} else if (argtype == 'f') {
|
|
|
|
*argp = INS_CONSTPTR(JSVAL_TO_OBJECT(fval));
|
|
|
|
} else if (argtype == 'p') {
|
|
|
|
JSObject* ctor = JSVAL_TO_OBJECT(fval);
|
|
|
|
jsval pval;
|
|
|
|
if (!OBJ_GET_PROPERTY(cx, ctor,
|
|
|
|
ATOM_TO_JSID(cx->runtime->atomState
|
|
|
|
.classPrototypeAtom),
|
|
|
|
&pval)) {
|
|
|
|
ABORT_TRACE("error getting prototype from constructor");
|
|
|
|
}
|
|
|
|
if (!JSVAL_IS_OBJECT(pval))
|
|
|
|
ABORT_TRACE("got primitive prototype from constructor");
|
|
|
|
*argp = INS_CONSTPTR(JSVAL_TO_OBJECT(pval));
|
|
|
|
} else if (argtype == 'R') {
|
|
|
|
*argp = INS_CONSTPTR(cx->runtime);
|
|
|
|
} else if (argtype == 'P') {
|
|
|
|
*argp = INS_CONSTPTR(pc);
|
|
|
|
} else if (argtype == 'D') { /* this, as a number */
|
|
|
|
if (!isNumber(thisval))
|
|
|
|
goto next_specialization;
|
|
|
|
*argp = this_ins;
|
|
|
|
} else {
|
|
|
|
JS_NOT_REACHED("unknown prefix arg type");
|
|
|
|
}
|
|
|
|
argp--;
|
|
|
|
}
|
2008-08-21 00:50:20 -07:00
|
|
|
|
2008-10-13 09:04:26 -07:00
|
|
|
for (i = knownargc; i--; ) {
|
|
|
|
jsval& arg = (!constructing && i == 0 && arg1_ins) ? arg1 : stackval(-(i + 1));
|
|
|
|
*argp = (!constructing && i == 0 && arg1_ins) ? arg1_ins : get(&arg);
|
|
|
|
|
|
|
|
argtype = known->argtypes[i];
|
|
|
|
if (argtype == 'd' || argtype == 'i') {
|
|
|
|
if (!isNumber(arg))
|
|
|
|
goto next_specialization;
|
|
|
|
if (argtype == 'i')
|
|
|
|
*argp = f2i(*argp);
|
|
|
|
} else if (argtype == 'o') {
|
|
|
|
if (!JSVAL_IS_OBJECT(arg))
|
|
|
|
goto next_specialization;
|
|
|
|
} else if (argtype == 's') {
|
|
|
|
if (!JSVAL_IS_STRING(arg))
|
|
|
|
goto next_specialization;
|
|
|
|
} else if (argtype == 'r') {
|
|
|
|
if (!VALUE_IS_REGEXP(cx, arg))
|
|
|
|
goto next_specialization;
|
|
|
|
} else if (argtype == 'f') {
|
|
|
|
if (!VALUE_IS_FUNCTION(cx, arg))
|
|
|
|
goto next_specialization;
|
|
|
|
} else if (argtype == 'v') {
|
|
|
|
if (!box_jsval(arg, *argp))
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
goto next_specialization;
|
|
|
|
}
|
|
|
|
argp--;
|
2008-08-21 00:50:20 -07:00
|
|
|
}
|
|
|
|
|
2008-10-13 09:04:26 -07:00
|
|
|
/*
|
|
|
|
* If we got this far, and we have a charCodeAt, check that charCodeAt
|
|
|
|
* isn't going to return a NaN.
|
|
|
|
*/
|
2008-10-16 12:24:10 -07:00
|
|
|
if (!constructing && known->builtin == &js_String_p_charCodeAt_ci) {
|
2008-10-13 09:04:26 -07:00
|
|
|
JSString* str = JSVAL_TO_STRING(thisval);
|
|
|
|
jsval& arg = arg1_ins ? arg1 : stackval(-1);
|
|
|
|
|
|
|
|
JS_ASSERT(JSVAL_IS_STRING(thisval));
|
|
|
|
JS_ASSERT(isNumber(arg));
|
|
|
|
|
|
|
|
if (JSVAL_IS_INT(arg)) {
|
|
|
|
if (size_t(JSVAL_TO_INT(arg)) >= JSSTRING_LENGTH(str))
|
|
|
|
ABORT_TRACE("invalid charCodeAt index");
|
|
|
|
} else {
|
|
|
|
double d = js_DoubleToInteger(*JSVAL_TO_DOUBLE(arg));
|
|
|
|
if (d < 0 || JSSTRING_LENGTH(str) <= d)
|
|
|
|
ABORT_TRACE("invalid charCodeAt index");
|
|
|
|
}
|
2008-08-21 00:50:20 -07:00
|
|
|
}
|
2008-10-13 09:04:26 -07:00
|
|
|
goto success;
|
2008-08-21 00:50:20 -07:00
|
|
|
|
2008-10-13 09:04:26 -07:00
|
|
|
next_specialization:;
|
|
|
|
} while ((known++)->flags & JSTN_MORE);
|
2008-08-21 00:50:20 -07:00
|
|
|
|
2008-10-13 09:04:26 -07:00
|
|
|
if (!constructing)
|
|
|
|
ABORT_TRACE("unknown native");
|
|
|
|
if (!(fun->flags & JSFUN_TRACEABLE) && FUN_CLASP(fun))
|
|
|
|
ABORT_TRACE("can't trace native constructor");
|
|
|
|
ABORT_TRACE("can't trace unknown constructor");
|
|
|
|
|
|
|
|
success:
|
2008-08-21 00:50:20 -07:00
|
|
|
#if defined _DEBUG
|
2008-10-13 09:04:26 -07:00
|
|
|
JS_ASSERT(args[0] != (LIns *)0xcdcdcdcd);
|
2008-08-21 00:50:20 -07:00
|
|
|
#endif
|
|
|
|
|
2008-10-13 09:04:26 -07:00
|
|
|
LIns* res_ins = lir->insCall(known->builtin, args);
|
|
|
|
if (!constructing)
|
|
|
|
rval_ins = res_ins;
|
|
|
|
switch (JSTN_ERRTYPE(known)) {
|
|
|
|
case FAIL_NULL:
|
|
|
|
guard(false, lir->ins_eq0(res_ins), OOM_EXIT);
|
|
|
|
break;
|
|
|
|
case FAIL_NEG:
|
|
|
|
{
|
|
|
|
res_ins = lir->ins1(LIR_i2f, res_ins);
|
|
|
|
jsdpun u;
|
|
|
|
u.d = 0.0;
|
|
|
|
guard(false, lir->ins2(LIR_flt, res_ins, lir->insImmq(u.u64)), OOM_EXIT);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case FAIL_VOID:
|
|
|
|
guard(false, lir->ins2i(LIR_eq, res_ins, JSVAL_TO_BOOLEAN(JSVAL_VOID)), OOM_EXIT);
|
|
|
|
break;
|
|
|
|
default:;
|
2008-08-08 14:38:44 -07:00
|
|
|
}
|
2008-10-13 09:04:26 -07:00
|
|
|
set(&fval, res_ins);
|
2008-08-08 14:38:44 -07:00
|
|
|
|
2008-10-13 09:04:26 -07:00
|
|
|
if (!constructing) {
|
|
|
|
/*
|
|
|
|
* The return value will be processed by FastNativeCallComplete since
|
|
|
|
* we have to know the actual return value type for calls that return
|
|
|
|
* jsval (like Array_p_pop).
|
|
|
|
*/
|
|
|
|
pendingTraceableNative = known;
|
|
|
|
}
|
2008-08-08 14:38:44 -07:00
|
|
|
|
2008-10-13 09:04:26 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NEW()
|
|
|
|
{
|
|
|
|
return functionCall(true);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DELNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DELPROP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DELELEM()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_TYPEOF()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-09 19:54:01 -07:00
|
|
|
jsval& r = stackval(-1);
|
|
|
|
LIns* type;
|
|
|
|
if (JSVAL_IS_STRING(r)) {
|
2008-08-28 12:40:48 -07:00
|
|
|
type = INS_CONSTPTR(ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_STRING]));
|
2008-08-09 19:54:01 -07:00
|
|
|
} else if (isNumber(r)) {
|
2008-08-28 12:40:48 -07:00
|
|
|
type = INS_CONSTPTR(ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]));
|
2008-08-09 19:54:01 -07:00
|
|
|
} else {
|
|
|
|
LIns* args[] = { get(&r), cx_ins };
|
2008-08-09 22:27:58 -07:00
|
|
|
if (JSVAL_TAG(r) == JSVAL_BOOLEAN) {
|
|
|
|
// We specialize identically for boolean and undefined. We must not have a hole here.
|
|
|
|
// Pass the unboxed type here, since TypeOfBoolean knows how to handle it.
|
|
|
|
JS_ASSERT(JSVAL_TO_BOOLEAN(r) <= 2);
|
2008-10-16 12:24:10 -07:00
|
|
|
type = lir->insCall(&js_TypeOfBoolean_ci, args);
|
2008-08-09 19:54:01 -07:00
|
|
|
} else {
|
|
|
|
JS_ASSERT(JSVAL_IS_OBJECT(r));
|
2008-10-16 12:24:10 -07:00
|
|
|
type = lir->insCall(&js_TypeOfObject_ci, args);
|
2008-08-09 19:54:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
set(&r, type);
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_VOID()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
stack(-1, INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID)));
|
2008-07-17 02:00:23 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INCNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-12 08:33:40 -07:00
|
|
|
return incName(1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-19 00:15:22 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INCPROP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-19 00:15:22 -07:00
|
|
|
return incProp(1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-19 00:15:22 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INCELEM()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-19 00:15:22 -07:00
|
|
|
return incElem(1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-19 00:15:22 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DECNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-12 08:33:40 -07:00
|
|
|
return incName(-1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DECPROP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-19 00:15:22 -07:00
|
|
|
return incProp(-1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DECELEM()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-19 00:15:22 -07:00
|
|
|
return incElem(-1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-12 08:33:40 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::incName(jsint incr, bool pre)
|
|
|
|
{
|
2008-08-12 09:42:29 -07:00
|
|
|
jsval* vp;
|
|
|
|
if (!name(vp))
|
2008-08-12 08:33:40 -07:00
|
|
|
return false;
|
2008-08-12 09:42:29 -07:00
|
|
|
LIns* v_ins = get(vp);
|
|
|
|
if (!inc(*vp, v_ins, incr, pre))
|
2008-08-12 08:33:40 -07:00
|
|
|
return false;
|
2008-08-12 09:42:29 -07:00
|
|
|
set(vp, v_ins);
|
2008-08-12 08:33:40 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NAMEINC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-12 08:33:40 -07:00
|
|
|
return incName(1, false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_PROPINC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-19 00:15:22 -07:00
|
|
|
return incProp(1, false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-07-19 00:15:22 -07:00
|
|
|
// XXX consolidate with record_JSOP_GETELEM code...
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ELEMINC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-19 00:15:22 -07:00
|
|
|
return incElem(1, false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NAMEDEC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-12 08:33:40 -07:00
|
|
|
return incName(-1, true);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_PROPDEC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-19 00:15:22 -07:00
|
|
|
return incProp(-1, false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ELEMDEC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-19 00:15:22 -07:00
|
|
|
return incElem(-1, false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GETPROP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-16 14:36:50 -07:00
|
|
|
return getProp(stackval(-1));
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SETPROP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-17 02:00:23 -07:00
|
|
|
jsval& l = stackval(-2);
|
|
|
|
if (JSVAL_IS_PRIMITIVE(l))
|
|
|
|
ABORT_TRACE("primitive this for SETPROP");
|
|
|
|
|
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(l);
|
|
|
|
if (obj->map->ops->setProperty != js_SetProperty)
|
2008-08-27 23:00:43 -07:00
|
|
|
ABORT_TRACE("non-native JSObjectOps::setProperty");
|
2008-09-09 22:22:52 -07:00
|
|
|
return true;
|
|
|
|
}
|
2008-07-17 02:00:23 -07:00
|
|
|
|
2008-09-09 22:22:52 -07:00
|
|
|
bool
|
2008-09-11 15:53:00 -07:00
|
|
|
TraceRecorder::record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop)
|
2008-09-09 22:22:52 -07:00
|
|
|
{
|
2008-09-30 13:34:17 -07:00
|
|
|
if (sprop->setter == js_watch_set)
|
|
|
|
ABORT_TRACE("watchpoint detected");
|
|
|
|
|
2008-09-09 22:22:52 -07:00
|
|
|
jsbytecode* pc = cx->fp->regs->pc;
|
|
|
|
jsval& r = stackval(-1);
|
|
|
|
jsval& l = stackval(-2);
|
|
|
|
|
|
|
|
JS_ASSERT(!JSVAL_IS_PRIMITIVE(l));
|
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(l);
|
2008-09-09 16:05:04 -07:00
|
|
|
LIns* obj_ins = get(&l);
|
2008-07-17 02:00:23 -07:00
|
|
|
|
2008-09-09 22:22:52 -07:00
|
|
|
if (obj == globalObj) {
|
|
|
|
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)));
|
|
|
|
uint32 slot = sprop->slot;
|
|
|
|
if (!lazilyImportGlobalSlot(slot))
|
|
|
|
ABORT_TRACE("lazy import of global slot failed");
|
2008-09-09 16:55:07 -07:00
|
|
|
|
2008-09-09 22:22:52 -07:00
|
|
|
LIns* r_ins = get(&r);
|
|
|
|
set(&STOBJ_GET_SLOT(obj, slot), r_ins);
|
|
|
|
|
|
|
|
JS_ASSERT(*pc != JSOP_INITPROP);
|
|
|
|
if (pc[JSOP_SETPROP_LENGTH] != JSOP_POP)
|
|
|
|
set(&l, r_ins);
|
|
|
|
return true;
|
|
|
|
}
|
2008-09-09 16:55:07 -07:00
|
|
|
|
2008-09-09 22:22:52 -07:00
|
|
|
// The global object's shape is guarded at trace entry, all others need a guard here.
|
2008-09-09 16:55:07 -07:00
|
|
|
LIns* map_ins = lir->insLoad(LIR_ldp, obj_ins, (int)offsetof(JSObject, map));
|
|
|
|
LIns* ops_ins;
|
|
|
|
if (!map_is_native(obj->map, map_ins, ops_ins, offsetof(JSObjectOps, setProperty)))
|
|
|
|
return false;
|
|
|
|
|
2008-09-09 22:22:52 -07:00
|
|
|
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)), "shape");
|
2008-09-11 15:53:00 -07:00
|
|
|
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, entry->kshape), "guard(shape)"),
|
|
|
|
MISMATCH_EXIT);
|
2008-07-17 02:00:23 -07:00
|
|
|
|
2008-09-11 15:53:00 -07:00
|
|
|
if (entry->kshape != PCVCAP_SHAPE(entry->vcap)) {
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
LIns* ok_ins = lir->insCall(&js_AddProperty_ci, args);
|
2008-09-11 15:53:00 -07:00
|
|
|
guard(false, lir->ins_eq0(ok_ins), OOM_EXIT);
|
2008-08-08 14:38:44 -07:00
|
|
|
}
|
2008-07-17 02:00:23 -07:00
|
|
|
|
|
|
|
LIns* dslots_ins = NULL;
|
|
|
|
LIns* v_ins = get(&r);
|
|
|
|
LIns* boxed_ins = v_ins;
|
|
|
|
if (!box_jsval(r, boxed_ins))
|
|
|
|
return false;
|
|
|
|
if (!native_set(obj_ins, sprop, dslots_ins, boxed_ins))
|
|
|
|
return false;
|
2008-09-09 22:22:52 -07:00
|
|
|
|
|
|
|
if (*pc != JSOP_INITPROP && pc[JSOP_SETPROP_LENGTH] != JSOP_POP)
|
|
|
|
set(&l, v_ins);
|
2008-07-17 02:00:23 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-09-09 22:22:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_SetPropMiss(JSPropCacheEntry* entry)
|
|
|
|
{
|
2008-09-11 15:53:00 -07:00
|
|
|
if (entry->kpc != cx->fp->regs->pc || !PCVAL_IS_SPROP(entry->vword))
|
2008-09-11 00:00:37 -07:00
|
|
|
ABORT_TRACE("can't trace uncacheable property set");
|
|
|
|
|
2008-09-11 15:53:00 -07:00
|
|
|
JSScopeProperty* sprop = PCVAL_TO_SPROP(entry->vword);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
jsval& l = stackval(-2);
|
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(l);
|
|
|
|
JSScope* scope = OBJ_SCOPE(obj);
|
|
|
|
JS_ASSERT(scope->object == obj);
|
|
|
|
JS_ASSERT(scope->shape == PCVCAP_SHAPE(entry->vcap));
|
|
|
|
JS_ASSERT(SCOPE_HAS_PROPERTY(scope, sprop));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return record_SetPropHit(entry, sprop);
|
2008-09-09 22:22:52 -07:00
|
|
|
}
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GETELEM()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-25 11:31:40 -07:00
|
|
|
jsval& idx = stackval(-1);
|
2008-09-25 15:04:48 -07:00
|
|
|
jsval& lval = stackval(-2);
|
2008-08-06 13:36:29 -07:00
|
|
|
|
2008-09-25 15:04:48 -07:00
|
|
|
LIns* obj_ins = get(&lval);
|
2008-09-25 11:31:40 -07:00
|
|
|
LIns* idx_ins = get(&idx);
|
|
|
|
|
2008-09-25 15:04:48 -07:00
|
|
|
if (JSVAL_IS_STRING(lval) && JSVAL_IS_INT(idx)) {
|
2008-09-25 11:31:40 -07:00
|
|
|
int i = JSVAL_TO_INT(idx);
|
2008-09-25 15:04:48 -07:00
|
|
|
if ((size_t)i >= JSSTRING_LENGTH(JSVAL_TO_STRING(lval)))
|
2008-08-29 13:05:41 -07:00
|
|
|
ABORT_TRACE("Invalid string index in JSOP_GETELEM");
|
2008-09-25 11:31:40 -07:00
|
|
|
idx_ins = makeNumberInt32(idx_ins);
|
|
|
|
LIns* args[] = { idx_ins, obj_ins, cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
LIns* unitstr_ins = lir->insCall(&js_String_getelem_ci, args);
|
2008-08-11 15:40:04 -07:00
|
|
|
guard(false, lir->ins_eq0(unitstr_ins), MISMATCH_EXIT);
|
2008-09-25 15:04:48 -07:00
|
|
|
set(&lval, unitstr_ins);
|
2008-07-30 15:19:25 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-09-25 15:04:48 -07:00
|
|
|
if (JSVAL_IS_PRIMITIVE(lval))
|
2008-09-25 11:31:40 -07:00
|
|
|
ABORT_TRACE("JSOP_GETLEM on a primitive");
|
2008-10-06 22:30:36 -07:00
|
|
|
|
2008-09-25 15:04:48 -07:00
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(lval);
|
2008-09-25 11:31:40 -07:00
|
|
|
jsval id;
|
|
|
|
jsval v;
|
|
|
|
LIns* v_ins;
|
2008-10-06 22:30:36 -07:00
|
|
|
|
2008-09-25 11:31:40 -07:00
|
|
|
/* Property access using a string name. */
|
|
|
|
if (JSVAL_IS_STRING(idx)) {
|
|
|
|
if (!js_ValueToStringId(cx, idx, &id))
|
|
|
|
return false;
|
2008-09-25 15:04:48 -07:00
|
|
|
// Store the interned string to the stack to save the interpreter from redoing this work.
|
|
|
|
idx = ID_TO_VALUE(id);
|
2008-10-06 22:30:36 -07:00
|
|
|
jsuint index;
|
|
|
|
if (js_IdIsIndex(idx, &index) && guardDenseArray(obj, obj_ins)) {
|
|
|
|
v = (index >= ARRAY_DENSE_LENGTH(obj)) ? JSVAL_HOLE : obj->dslots[index];
|
|
|
|
if (v == JSVAL_HOLE)
|
|
|
|
ABORT_TRACE("can't see through hole in dense array");
|
|
|
|
} else {
|
|
|
|
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, getProperty), &v))
|
|
|
|
return false;
|
|
|
|
}
|
2008-09-25 11:31:40 -07:00
|
|
|
LIns* args[] = { idx_ins, obj_ins, cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
v_ins = lir->insCall(&js_Any_getprop_ci, args);
|
2008-09-15 00:54:28 -07:00
|
|
|
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), MISMATCH_EXIT);
|
2008-08-06 13:36:29 -07:00
|
|
|
if (!unbox_jsval(v, v_ins))
|
|
|
|
ABORT_TRACE("JSOP_GETELEM");
|
2008-09-25 15:04:48 -07:00
|
|
|
set(&lval, v_ins);
|
2008-09-25 11:31:40 -07:00
|
|
|
return true;
|
|
|
|
}
|
2008-10-06 22:30:36 -07:00
|
|
|
|
2008-09-25 11:31:40 -07:00
|
|
|
/* At this point we expect a whole number or we bail. */
|
|
|
|
if (!JSVAL_IS_INT(idx))
|
|
|
|
ABORT_TRACE("non-string, non-int JSOP_GETELEM index");
|
2008-09-28 17:11:47 -07:00
|
|
|
if (JSVAL_TO_INT(idx) < 0)
|
|
|
|
ABORT_TRACE("negative JSOP_GETELEM index");
|
2008-10-06 22:30:36 -07:00
|
|
|
|
2008-09-25 11:31:40 -07:00
|
|
|
/* Accessing an object using integer index but not a dense array. */
|
2008-09-25 15:04:48 -07:00
|
|
|
if (!OBJ_IS_DENSE_ARRAY(cx, obj)) {
|
2008-09-25 11:31:40 -07:00
|
|
|
idx_ins = makeNumberInt32(idx_ins);
|
2008-09-28 17:11:47 -07:00
|
|
|
LIns* args[] = { idx_ins, obj_ins, cx_ins };
|
2008-09-25 11:31:40 -07:00
|
|
|
if (!js_IndexToId(cx, JSVAL_TO_INT(idx), &id))
|
|
|
|
return false;
|
2008-09-25 17:05:06 -07:00
|
|
|
idx = ID_TO_VALUE(id);
|
|
|
|
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, getProperty), &v))
|
2008-09-25 11:31:40 -07:00
|
|
|
return false;
|
2008-10-16 12:24:10 -07:00
|
|
|
LIns* v_ins = lir->insCall(&js_Any_getelem_ci, args);
|
2008-09-25 11:31:40 -07:00
|
|
|
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), MISMATCH_EXIT);
|
|
|
|
if (!unbox_jsval(v, v_ins))
|
|
|
|
ABORT_TRACE("JSOP_GETELEM");
|
2008-09-25 15:04:48 -07:00
|
|
|
set(&lval, v_ins);
|
2008-08-06 13:36:29 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-19 00:15:22 -07:00
|
|
|
jsval* vp;
|
2008-07-19 10:24:10 -07:00
|
|
|
LIns* addr_ins;
|
2008-09-25 15:04:48 -07:00
|
|
|
if (!elem(lval, idx, vp, v_ins, addr_ins))
|
2008-07-06 10:38:55 -07:00
|
|
|
return false;
|
2008-09-25 15:04:48 -07:00
|
|
|
set(&lval, v_ins);
|
2008-07-05 13:44:48 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SETELEM()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-05 06:47:59 -07:00
|
|
|
jsval& v = stackval(-1);
|
2008-09-25 11:31:40 -07:00
|
|
|
jsval& idx = stackval(-2);
|
2008-09-25 15:04:48 -07:00
|
|
|
jsval& lval = stackval(-3);
|
2008-07-19 00:15:22 -07:00
|
|
|
|
2008-07-05 14:00:32 -07:00
|
|
|
/* no guards for type checks, trace specialized this already */
|
2008-09-25 15:04:48 -07:00
|
|
|
if (JSVAL_IS_PRIMITIVE(lval))
|
2008-08-06 13:36:29 -07:00
|
|
|
ABORT_TRACE("left JSOP_SETELEM operand is not an object");
|
2008-07-19 00:15:22 -07:00
|
|
|
|
2008-09-25 15:04:48 -07:00
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(lval);
|
|
|
|
LIns* obj_ins = get(&lval);
|
2008-09-25 11:31:40 -07:00
|
|
|
LIns* idx_ins = get(&idx);
|
|
|
|
LIns* v_ins = get(&v);
|
2008-09-28 17:11:47 -07:00
|
|
|
jsid id;
|
2008-09-26 17:33:40 -07:00
|
|
|
|
|
|
|
LIns* boxed_v_ins = v_ins;
|
|
|
|
if (!box_jsval(v, boxed_v_ins))
|
2008-09-28 17:11:47 -07:00
|
|
|
ABORT_TRACE("boxing JSOP_SETELEM value");
|
2008-10-06 22:30:36 -07:00
|
|
|
|
2008-09-25 11:31:40 -07:00
|
|
|
if (JSVAL_IS_STRING(idx)) {
|
2008-09-25 15:04:48 -07:00
|
|
|
if (!js_ValueToStringId(cx, idx, &id))
|
|
|
|
return false;
|
|
|
|
// Store the interned string to the stack to save the interpreter from redoing this work.
|
|
|
|
idx = ID_TO_VALUE(id);
|
|
|
|
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, setProperty), NULL))
|
2008-09-25 11:31:40 -07:00
|
|
|
return false;
|
2008-09-26 17:33:40 -07:00
|
|
|
LIns* args[] = { boxed_v_ins, idx_ins, obj_ins, cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
LIns* ok_ins = lir->insCall(&js_Any_setprop_ci, args);
|
2008-10-08 15:08:33 -07:00
|
|
|
guard(false, lir->ins_eq0(ok_ins), MISMATCH_EXIT);
|
2008-09-26 17:33:40 -07:00
|
|
|
} else if (JSVAL_IS_INT(idx)) {
|
2008-09-28 17:11:47 -07:00
|
|
|
if (JSVAL_TO_INT(idx) < 0)
|
2008-09-30 13:02:23 -07:00
|
|
|
ABORT_TRACE("negative JSOP_SETELEM index");
|
2008-09-26 17:33:40 -07:00
|
|
|
idx_ins = makeNumberInt32(idx_ins);
|
|
|
|
LIns* args[] = { boxed_v_ins, idx_ins, obj_ins, cx_ins };
|
|
|
|
LIns* res_ins;
|
2008-09-28 17:11:47 -07:00
|
|
|
if (guardDenseArray(obj, obj_ins)) {
|
2008-10-16 12:24:10 -07:00
|
|
|
res_ins = lir->insCall(&js_Array_dense_setelem_ci, args);
|
2008-09-28 17:11:47 -07:00
|
|
|
} else {
|
|
|
|
if (!js_IndexToId(cx, JSVAL_TO_INT(idx), &id))
|
|
|
|
return false;
|
|
|
|
idx = ID_TO_VALUE(id);
|
2008-09-30 13:02:23 -07:00
|
|
|
if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, setProperty), NULL))
|
2008-09-28 17:11:47 -07:00
|
|
|
return false;
|
2008-10-16 12:24:10 -07:00
|
|
|
res_ins = lir->insCall(&js_Any_setelem_ci, args);
|
2008-09-28 17:11:47 -07:00
|
|
|
}
|
2008-09-26 17:33:40 -07:00
|
|
|
guard(false, lir->ins_eq0(res_ins), MISMATCH_EXIT);
|
|
|
|
} else {
|
2008-08-06 13:36:29 -07:00
|
|
|
ABORT_TRACE("non-string, non-int JSOP_SETELEM index");
|
2008-09-25 11:31:40 -07:00
|
|
|
}
|
2008-10-06 22:30:36 -07:00
|
|
|
|
2008-08-11 10:24:47 -07:00
|
|
|
jsbytecode* pc = cx->fp->regs->pc;
|
|
|
|
if (*pc == JSOP_SETELEM && pc[JSOP_SETELEM_LENGTH] != JSOP_POP)
|
2008-09-25 15:04:48 -07:00
|
|
|
set(&lval, v_ins);
|
2008-09-26 17:33:40 -07:00
|
|
|
|
2008-07-05 14:00:32 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_CALLNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-16 15:40:35 -07:00
|
|
|
JSObject* obj = cx->fp->scopeChain;
|
2008-08-20 22:40:39 -07:00
|
|
|
if (obj != globalObj) {
|
|
|
|
jsval* vp;
|
|
|
|
if (!activeCallOrGlobalSlot(obj, vp))
|
|
|
|
return false;
|
|
|
|
stack(0, get(vp));
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
stack(1, INS_CONSTPTR(NULL));
|
2008-08-20 22:40:39 -07:00
|
|
|
return true;
|
|
|
|
}
|
2008-07-16 15:40:35 -07:00
|
|
|
|
2008-08-20 22:40:39 -07:00
|
|
|
LIns* obj_ins = scopeChain();
|
2008-08-20 14:57:49 -07:00
|
|
|
JSObject* obj2;
|
2008-07-17 23:57:56 -07:00
|
|
|
jsuword pcval;
|
|
|
|
if (!test_property_cache(obj, obj_ins, obj2, pcval))
|
2008-08-07 18:12:31 -07:00
|
|
|
return false;
|
2008-07-16 15:40:35 -07:00
|
|
|
|
2008-08-07 18:12:31 -07:00
|
|
|
if (PCVAL_IS_NULL(pcval) || !PCVAL_IS_OBJECT(pcval))
|
|
|
|
ABORT_TRACE("callee is not an object");
|
|
|
|
JS_ASSERT(HAS_FUNCTION_CLASS(PCVAL_TO_OBJECT(pcval)));
|
2008-07-16 15:40:35 -07:00
|
|
|
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
stack(0, INS_CONSTPTR(PCVAL_TO_OBJECT(pcval)));
|
2008-07-16 15:40:35 -07:00
|
|
|
stack(1, obj_ins);
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-21 03:47:33 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GETUPVAR()
|
|
|
|
{
|
|
|
|
ABORT_TRACE("GETUPVAR");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_CALLUPVAR()
|
|
|
|
{
|
|
|
|
ABORT_TRACE("CALLUPVAR");
|
|
|
|
}
|
|
|
|
|
2008-08-08 16:37:01 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::guardShapelessCallee(jsval& callee)
|
|
|
|
{
|
2008-08-10 22:36:48 -07:00
|
|
|
if (!VALUE_IS_FUNCTION(cx, callee))
|
|
|
|
ABORT_TRACE("shapeless callee is not a function");
|
2008-08-08 16:37:01 -07:00
|
|
|
|
2008-08-10 22:36:48 -07:00
|
|
|
guard(true,
|
2008-08-13 14:02:35 -07:00
|
|
|
addName(lir->ins2(LIR_eq, get(&callee), INS_CONSTPTR(JSVAL_TO_OBJECT(callee))),
|
2008-08-12 09:27:54 -07:00
|
|
|
"guard(shapeless callee)"),
|
|
|
|
MISMATCH_EXIT);
|
2008-08-10 22:36:48 -07:00
|
|
|
return true;
|
2008-08-08 16:37:01 -07:00
|
|
|
}
|
|
|
|
|
2008-08-08 14:38:44 -07:00
|
|
|
bool
|
2008-09-18 14:13:37 -07:00
|
|
|
TraceRecorder::interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc, bool constructing)
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-11 00:07:50 -07:00
|
|
|
if (JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(fval)) != globalObj)
|
|
|
|
ABORT_TRACE("JSOP_CALL or JSOP_NEW crosses global scopes");
|
|
|
|
|
2008-08-01 08:26:32 -07:00
|
|
|
JSStackFrame* fp = cx->fp;
|
2008-07-11 19:59:09 -07:00
|
|
|
|
2008-08-15 18:01:26 -07:00
|
|
|
// TODO: track the copying via the tracker...
|
2008-08-08 14:38:44 -07:00
|
|
|
if (argc < fun->nargs &&
|
|
|
|
jsuword(fp->regs->sp + (fun->nargs - argc)) > cx->stackPool.current->limit) {
|
|
|
|
ABORT_TRACE("can't trace calls with too few args requiring argv move");
|
|
|
|
}
|
|
|
|
|
2008-09-09 06:16:21 -07:00
|
|
|
// Generate a type map for the outgoing frame and stash it in the LIR
|
|
|
|
unsigned stackSlots = js_NativeStackSlots(cx, 0/*callDepth*/);
|
|
|
|
LIns* data = lir_buf_writer->skip(stackSlots * sizeof(uint8));
|
|
|
|
uint8* typemap = (uint8 *)data->payload();
|
|
|
|
uint8* m = typemap;
|
|
|
|
/* 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. */
|
|
|
|
FORALL_SLOTS_IN_PENDING_FRAMES(cx, 0/*callDepth*/,
|
|
|
|
*m++ = determineSlotType(vp);
|
|
|
|
);
|
2008-09-18 14:13:37 -07:00
|
|
|
|
|
|
|
if (argc >= 0x8000)
|
|
|
|
ABORT_TRACE("too many arguments");
|
|
|
|
|
2008-08-08 14:38:44 -07:00
|
|
|
FrameInfo fi = {
|
|
|
|
JSVAL_TO_OBJECT(fval),
|
|
|
|
fp->regs->pc,
|
2008-09-09 06:16:21 -07:00
|
|
|
typemap,
|
2008-09-18 14:13:37 -07:00
|
|
|
{ { fp->regs->sp - fp->slots, argc | (constructing ? 0x8000 : 0) } }
|
2008-08-08 14:38:44 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
unsigned callDepth = getCallDepth();
|
|
|
|
if (callDepth >= treeInfo->maxCallDepth)
|
|
|
|
treeInfo->maxCallDepth = callDepth + 1;
|
|
|
|
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
lir->insStorei(INS_CONSTPTR(fi.callee), lirbuf->rp,
|
2008-08-08 14:38:44 -07:00
|
|
|
callDepth * sizeof(FrameInfo) + offsetof(FrameInfo, callee));
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
lir->insStorei(INS_CONSTPTR(fi.callpc), lirbuf->rp,
|
2008-08-08 14:38:44 -07:00
|
|
|
callDepth * sizeof(FrameInfo) + offsetof(FrameInfo, callpc));
|
2008-09-19 16:09:18 -07:00
|
|
|
lir->insStorei(INS_CONSTPTR(fi.typemap), lirbuf->rp,
|
|
|
|
callDepth * sizeof(FrameInfo) + offsetof(FrameInfo, typemap));
|
2008-08-28 23:50:48 -07:00
|
|
|
lir->insStorei(INS_CONST(fi.word), lirbuf->rp,
|
2008-08-08 14:38:44 -07:00
|
|
|
callDepth * sizeof(FrameInfo) + offsetof(FrameInfo, word));
|
|
|
|
|
|
|
|
atoms = fun->u.i.script->atomMap.vector;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_CALL()
|
|
|
|
{
|
2008-10-13 09:04:26 -07:00
|
|
|
return functionCall(false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-09 11:42:31 -07:00
|
|
|
|
2008-10-07 11:00:16 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_FastNativeCallComplete()
|
|
|
|
{
|
|
|
|
JS_ASSERT(pendingTraceableNative);
|
|
|
|
|
|
|
|
/* At this point the generated code has already called the native function
|
|
|
|
and we can no longer fail back to the original pc location (JSOP_CALL)
|
|
|
|
because that would cause the interpreter to re-execute the native
|
|
|
|
function, which might have side effects. Instead we advance pc to
|
|
|
|
the JSOP_RESUME opcode that follows JSOP_CALL. snapshot() which is
|
|
|
|
invoked from unbox_jsval() will see that we are currently parked on
|
|
|
|
a JSOP_RESUME instruction and it will indicate in the type map that
|
|
|
|
the element on top of the stack is a boxed value which doesn't need
|
|
|
|
to be boxed if the type guard generated by unbox_jsval() fails. */
|
|
|
|
JSFrameRegs* regs = cx->fp->regs;
|
|
|
|
regs->pc += JSOP_CALL_LENGTH;
|
|
|
|
JS_ASSERT(*regs->pc == JSOP_RESUME);
|
|
|
|
|
|
|
|
jsval& v = stackval(-1);
|
|
|
|
LIns* v_ins = get(&v);
|
|
|
|
|
|
|
|
bool ok = true;
|
2008-10-08 15:08:33 -07:00
|
|
|
if (JSTN_ERRTYPE(pendingTraceableNative) == FAIL_JSVAL) {
|
2008-10-07 11:00:16 -07:00
|
|
|
ok = unbox_jsval(v, v_ins);
|
|
|
|
if (ok)
|
|
|
|
set(&v, v_ins);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Restore the original pc location. The interpreter will advance pc to
|
|
|
|
step over JSOP_RESUME. */
|
|
|
|
regs->pc -= JSOP_CALL_LENGTH;
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2008-09-29 18:49:40 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_RESUME()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-08-12 09:42:29 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::name(jsval*& vp)
|
|
|
|
{
|
|
|
|
JSObject* obj = cx->fp->scopeChain;
|
|
|
|
if (obj != globalObj)
|
2008-08-20 22:40:39 -07:00
|
|
|
return activeCallOrGlobalSlot(obj, vp);
|
2008-08-12 18:52:28 -07:00
|
|
|
|
|
|
|
/* Can't use prop here, because we don't want unboxing from global slots. */
|
2008-08-20 22:40:39 -07:00
|
|
|
LIns* obj_ins = scopeChain();
|
2008-08-12 09:42:29 -07:00
|
|
|
uint32 slot;
|
|
|
|
if (!test_property_cache_direct_slot(obj, obj_ins, slot))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (slot == SPROP_INVALID_SLOT)
|
|
|
|
ABORT_TRACE("name op can't find named property");
|
|
|
|
|
|
|
|
if (!lazilyImportGlobalSlot(slot))
|
|
|
|
ABORT_TRACE("lazy import of global slot failed");
|
|
|
|
|
|
|
|
vp = &STOBJ_GET_SLOT(obj, slot);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-15 10:17:51 -07:00
|
|
|
bool
|
2008-07-19 10:24:28 -07:00
|
|
|
TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
|
2008-07-15 10:17:51 -07:00
|
|
|
{
|
2008-07-19 00:15:22 -07:00
|
|
|
/*
|
|
|
|
* Can't specialize to assert obj != global, must guard to avoid aliasing
|
|
|
|
* stale homes of stacked global variables.
|
|
|
|
*/
|
|
|
|
if (obj == globalObj)
|
|
|
|
ABORT_TRACE("prop op aliases global");
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
guard(false, lir->ins2(LIR_eq, obj_ins, INS_CONSTPTR(globalObj)), MISMATCH_EXIT);
|
2008-07-19 00:15:22 -07:00
|
|
|
|
2008-08-12 18:52:28 -07:00
|
|
|
/*
|
|
|
|
* Property cache ensures that we are dealing with an existing property,
|
|
|
|
* and guards the shape for us.
|
|
|
|
*/
|
|
|
|
JSObject* obj2;
|
|
|
|
jsuword pcval;
|
|
|
|
if (!test_property_cache(obj, obj_ins, obj2, pcval))
|
2008-07-15 10:17:51 -07:00
|
|
|
return false;
|
|
|
|
|
2008-08-07 18:12:31 -07:00
|
|
|
/* Check for non-existent property reference, which results in undefined. */
|
2008-08-12 18:52:28 -07:00
|
|
|
const JSCodeSpec& cs = js_CodeSpec[*cx->fp->regs->pc];
|
|
|
|
if (PCVAL_IS_NULL(pcval)) {
|
2008-08-28 23:50:48 -07:00
|
|
|
v_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
|
2008-08-12 18:52:28 -07:00
|
|
|
JS_ASSERT(cs.ndefs == 1);
|
2008-08-07 18:12:31 -07:00
|
|
|
stack(-cs.nuses, v_ins);
|
2008-08-28 14:43:44 -07:00
|
|
|
slot = SPROP_INVALID_SLOT;
|
2008-08-07 18:12:31 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-08-12 18:52:28 -07:00
|
|
|
/* Insist if setting on obj being the directly addressed object. */
|
|
|
|
uint32 setflags = (cs.format & (JOF_SET | JOF_INCDEC));
|
2008-08-13 17:10:18 -07:00
|
|
|
LIns* dslots_ins = NULL;
|
|
|
|
if (obj2 != obj) {
|
|
|
|
if (setflags)
|
|
|
|
ABORT_TRACE("JOF_SET opcode hit prototype chain");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We're getting a proto-property. Walk up the prototype chain emitting
|
|
|
|
* proto slot loads, updating obj as we go, leaving obj set to obj2 with
|
|
|
|
* obj_ins the last proto-load.
|
|
|
|
*/
|
|
|
|
while (obj != obj2) {
|
|
|
|
obj_ins = stobj_get_slot(obj_ins, JSSLOT_PROTO, dslots_ins);
|
|
|
|
obj = STOBJ_GET_PROTO(obj);
|
|
|
|
}
|
|
|
|
}
|
2008-08-12 18:52:28 -07:00
|
|
|
|
|
|
|
/* Don't trace getter or setter calls, our caller wants a direct slot. */
|
|
|
|
if (PCVAL_IS_SPROP(pcval)) {
|
|
|
|
JSScopeProperty* sprop = PCVAL_TO_SPROP(pcval);
|
|
|
|
|
|
|
|
if (setflags && !SPROP_HAS_STUB_SETTER(sprop))
|
|
|
|
ABORT_TRACE("non-stub setter");
|
|
|
|
if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop)) {
|
|
|
|
// FIXME 450335: generalize this away from regexp built-in getters.
|
|
|
|
if (setflags == 0 &&
|
|
|
|
sprop->getter == js_RegExpClass.getProperty &&
|
|
|
|
sprop->shortid < 0) {
|
2008-09-25 17:45:14 -07:00
|
|
|
if (sprop->shortid == REGEXP_LAST_INDEX)
|
|
|
|
ABORT_TRACE("can't trace regexp.lastIndex yet");
|
2008-08-12 18:52:28 -07:00
|
|
|
LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
v_ins = lir->insCall(&js_CallGetter_ci, args);
|
2008-09-03 22:32:13 -07:00
|
|
|
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT);
|
2008-08-20 22:40:39 -07:00
|
|
|
if (!unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_BOOLEAN,
|
2008-08-12 18:52:28 -07:00
|
|
|
v_ins)) {
|
|
|
|
ABORT_TRACE("unboxing");
|
|
|
|
}
|
|
|
|
JS_ASSERT(cs.ndefs == 1);
|
|
|
|
stack(-cs.nuses, v_ins);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
ABORT_TRACE("non-stub getter");
|
|
|
|
}
|
|
|
|
if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)))
|
|
|
|
ABORT_TRACE("no valid slot");
|
|
|
|
slot = sprop->slot;
|
|
|
|
} else {
|
|
|
|
if (!PCVAL_IS_SLOT(pcval))
|
|
|
|
ABORT_TRACE("PCE is not a slot");
|
|
|
|
slot = PCVAL_TO_SLOT(pcval);
|
|
|
|
}
|
|
|
|
|
2008-07-19 00:15:22 -07:00
|
|
|
v_ins = stobj_get_slot(obj_ins, slot, dslots_ins);
|
2008-07-19 10:24:28 -07:00
|
|
|
if (!unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins))
|
2008-07-15 10:17:51 -07:00
|
|
|
ABORT_TRACE("unboxing");
|
2008-07-19 00:15:22 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2008-09-25 15:04:48 -07:00
|
|
|
TraceRecorder::elem(jsval& oval, jsval& idx, jsval*& vp, LIns*& v_ins, LIns*& addr_ins)
|
2008-07-19 00:15:22 -07:00
|
|
|
{
|
|
|
|
/* no guards for type checks, trace specialized this already */
|
2008-09-25 15:04:48 -07:00
|
|
|
if (JSVAL_IS_PRIMITIVE(oval) || !JSVAL_IS_INT(idx))
|
2008-07-19 00:15:22 -07:00
|
|
|
return false;
|
|
|
|
|
2008-09-25 15:04:48 -07:00
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(oval);
|
|
|
|
LIns* obj_ins = get(&oval);
|
2008-07-19 00:15:22 -07:00
|
|
|
|
|
|
|
/* make sure the object is actually a dense array */
|
2008-09-25 15:04:48 -07:00
|
|
|
if (!guardDenseArray(obj, obj_ins))
|
2008-07-19 00:15:22 -07:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* check that the index is within bounds */
|
2008-09-25 11:31:40 -07:00
|
|
|
jsint i = JSVAL_TO_INT(idx);
|
|
|
|
LIns* idx_ins = makeNumberInt32(get(&idx));
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-18 12:54:04 -07:00
|
|
|
LIns* dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots));
|
2008-09-25 15:04:48 -07:00
|
|
|
if (!guardDenseArrayIndex(obj, i, obj_ins, dslots_ins, idx_ins))
|
2008-07-31 06:35:11 -07:00
|
|
|
return false;
|
2008-10-06 22:30:36 -07:00
|
|
|
|
|
|
|
// We can't "see through" a hole to a possible Array.prototype property, so
|
|
|
|
// we abort here and guard below (after unboxing).
|
2008-09-25 15:04:48 -07:00
|
|
|
vp = &obj->dslots[i];
|
2008-10-06 22:30:36 -07:00
|
|
|
if (*vp == JSVAL_HOLE)
|
|
|
|
ABORT_TRACE("can't see through hole in dense array");
|
2008-07-19 00:15:22 -07:00
|
|
|
|
2008-08-18 16:23:37 -07:00
|
|
|
addr_ins = lir->ins2(LIR_piadd, dslots_ins,
|
|
|
|
lir->ins2i(LIR_pilsh, idx_ins, (sizeof(jsval) == 4) ? 2 : 3));
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-10-06 22:30:36 -07:00
|
|
|
/* Load the value and guard on its type to unbox it. */
|
2008-08-18 12:54:04 -07:00
|
|
|
v_ins = lir->insLoad(LIR_ldp, addr_ins, 0);
|
2008-09-04 22:14:27 -07:00
|
|
|
if (!unbox_jsval(*vp, v_ins))
|
|
|
|
return false;
|
2008-10-06 22:30:36 -07:00
|
|
|
|
2008-09-04 22:14:27 -07:00
|
|
|
if (JSVAL_TAG(*vp) == JSVAL_BOOLEAN) {
|
2008-10-06 22:30:36 -07:00
|
|
|
// Optimize to guard for a hole only after untagging, so we know that
|
|
|
|
// we have a boolean, to avoid an extra guard for non-boolean values.
|
|
|
|
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_HOLE))),
|
|
|
|
MISMATCH_EXIT);
|
2008-09-04 22:14:27 -07:00
|
|
|
}
|
|
|
|
return v_ins;
|
2008-07-19 00:15:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::getProp(JSObject* obj, LIns* obj_ins)
|
|
|
|
{
|
2008-07-19 10:24:28 -07:00
|
|
|
uint32 slot;
|
2008-07-19 00:15:22 -07:00
|
|
|
LIns* v_ins;
|
2008-07-19 10:24:28 -07:00
|
|
|
if (!prop(obj, obj_ins, slot, v_ins))
|
2008-07-19 00:15:22 -07:00
|
|
|
return false;
|
|
|
|
|
|
|
|
const JSCodeSpec& cs = js_CodeSpec[*cx->fp->regs->pc];
|
|
|
|
JS_ASSERT(cs.ndefs == 1);
|
|
|
|
stack(-cs.nuses, v_ins);
|
2008-07-15 10:17:51 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2008-07-16 14:36:50 -07:00
|
|
|
TraceRecorder::getProp(jsval& v)
|
2008-07-15 10:17:51 -07:00
|
|
|
{
|
|
|
|
if (JSVAL_IS_PRIMITIVE(v))
|
|
|
|
ABORT_TRACE("primitive lhs");
|
|
|
|
|
2008-07-16 14:36:50 -07:00
|
|
|
return getProp(JSVAL_TO_OBJECT(v), get(&v));
|
2008-07-15 10:17:51 -07:00
|
|
|
}
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-12 09:42:29 -07:00
|
|
|
jsval* vp;
|
|
|
|
if (!name(vp))
|
2008-07-06 09:15:55 -07:00
|
|
|
return false;
|
2008-08-12 09:42:29 -07:00
|
|
|
stack(0, get(vp));
|
2008-07-06 09:15:55 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-09 11:42:31 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DOUBLE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-10 21:55:09 -07:00
|
|
|
jsval v = jsval(atoms[GET_INDEX(cx->fp->regs->pc)]);
|
2008-07-14 15:22:05 -07:00
|
|
|
jsdpun u;
|
|
|
|
u.d = *JSVAL_TO_DOUBLE(v);
|
|
|
|
stack(0, lir->insImmq(u.u64));
|
2008-07-05 23:21:53 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_STRING()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-30 17:28:59 -07:00
|
|
|
JSAtom* atom = atoms[GET_INDEX(cx->fp->regs->pc)];
|
|
|
|
JS_ASSERT(ATOM_IS_STRING(atom));
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
stack(0, INS_CONSTPTR(ATOM_TO_STRING(atom)));
|
2008-07-30 17:28:59 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ZERO()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-14 15:22:05 -07:00
|
|
|
jsdpun u;
|
|
|
|
u.d = 0.0;
|
|
|
|
stack(0, lir->insImmq(u.u64));
|
2008-07-05 06:15:33 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ONE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-14 15:22:05 -07:00
|
|
|
jsdpun u;
|
|
|
|
u.d = 1.0;
|
|
|
|
stack(0, lir->insImmq(u.u64));
|
2008-07-05 06:15:33 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NULL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
stack(0, INS_CONSTPTR(NULL));
|
2008-07-05 06:15:33 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_THIS()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-16 14:36:50 -07:00
|
|
|
LIns* this_ins;
|
|
|
|
if (!getThis(this_ins))
|
|
|
|
return false;
|
|
|
|
stack(0, this_ins);
|
2008-07-15 18:37:00 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_FALSE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-05 06:15:33 -07:00
|
|
|
stack(0, lir->insImm(0));
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_TRUE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-05 06:15:33 -07:00
|
|
|
stack(0, lir->insImm(1));
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_OR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-10-08 21:02:04 -07:00
|
|
|
return ifop();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_AND()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-10-08 21:02:04 -07:00
|
|
|
return ifop();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_TABLESWITCH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
return switchop();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_LOOKUPSWITCH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
return switchop();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_STRICTEQ()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-18 13:31:26 -07:00
|
|
|
return cmp(LIR_feq, CMP_STRICT);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_STRICTNE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-18 13:31:26 -07:00
|
|
|
return cmp(LIR_feq, CMP_STRICT | CMP_NEGATE);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_OBJECT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-10 12:01:03 -07:00
|
|
|
JSStackFrame* fp = cx->fp;
|
|
|
|
JSScript* script = fp->script;
|
|
|
|
unsigned index = atoms - script->atomMap.vector + GET_INDEX(fp->regs->pc);
|
|
|
|
|
|
|
|
JSObject* obj;
|
|
|
|
JS_GET_SCRIPT_OBJECT(script, index, obj);
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
stack(0, INS_CONSTPTR(obj));
|
2008-08-10 12:01:03 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_POP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 06:52:50 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_POS()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-30 16:32:33 -07:00
|
|
|
jsval& r = stackval(-1);
|
|
|
|
return isNumber(r);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_TRAP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GETARG()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-04 03:06:18 -07:00
|
|
|
stack(0, arg(GET_ARGNO(cx->fp->regs->pc)));
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SETARG()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-04 15:21:56 -07:00
|
|
|
arg(GET_ARGNO(cx->fp->regs->pc), stack(-1));
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
2008-08-08 18:58:04 -07:00
|
|
|
TraceRecorder::record_JSOP_GETLOCAL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
stack(0, var(GET_SLOTNO(cx->fp->regs->pc)));
|
2008-07-04 03:06:18 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
2008-08-08 18:58:04 -07:00
|
|
|
TraceRecorder::record_JSOP_SETLOCAL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
var(GET_SLOTNO(cx->fp->regs->pc), stack(-1));
|
2008-07-04 15:21:56 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_UINT16()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-14 15:22:05 -07:00
|
|
|
jsdpun u;
|
|
|
|
u.d = (jsdouble)GET_UINT16(cx->fp->regs->pc);
|
|
|
|
stack(0, lir->insImmq(u.u64));
|
2008-07-04 03:06:18 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NEWINIT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-11 10:24:47 -07:00
|
|
|
JSProtoKey key = JSProtoKey(GET_INT8(cx->fp->regs->pc));
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
JSObject* obj;
|
2008-10-08 15:08:33 -07:00
|
|
|
const CallInfo *ci;
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
if (key == JSProto_Array) {
|
|
|
|
if (!js_GetClassPrototype(cx, globalObj, INT_TO_JSID(key), &obj))
|
|
|
|
return false;
|
2008-10-16 12:24:10 -07:00
|
|
|
ci = &js_FastNewArray_ci;
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
} else {
|
2008-09-25 17:30:01 -07:00
|
|
|
jsval v_obj;
|
|
|
|
if (!js_FindClassObject(cx, globalObj, INT_TO_JSID(key), &v_obj))
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
return false;
|
2008-09-25 17:30:01 -07:00
|
|
|
if (JSVAL_IS_PRIMITIVE(v_obj))
|
|
|
|
ABORT_TRACE("primitive Object value");
|
|
|
|
obj = JSVAL_TO_OBJECT(v_obj);
|
2008-10-16 12:24:10 -07:00
|
|
|
ci = &js_FastNewObject_ci;
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
}
|
|
|
|
LIns* args[] = { INS_CONSTPTR(obj), cx_ins };
|
2008-10-08 15:08:33 -07:00
|
|
|
LIns* v_ins = lir->insCall(ci, args);
|
2008-08-11 10:24:47 -07:00
|
|
|
guard(false, lir->ins_eq0(v_ins), OOM_EXIT);
|
|
|
|
stack(0, v_ins);
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ENDINIT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
jsval& v = stackval(-1);
|
|
|
|
JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
|
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(v);
|
|
|
|
if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
|
|
|
|
// Until we get JSOP_NEWARRAY working, we do our optimizing here...
|
2008-09-01 13:59:30 -07:00
|
|
|
if (obj->fslots[JSSLOT_ARRAY_LENGTH] == 1 &&
|
|
|
|
obj->dslots && JSVAL_IS_STRING(obj->dslots[0])) {
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
LIns* v_ins = get(&v);
|
2008-10-16 12:24:10 -07:00
|
|
|
JS_ASSERT(v_ins->isCall() && v_ins->callInfo() == &js_FastNewArray_ci);
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
LIns* args[] = { stack(1), callArgN(v_ins, 1), cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
v_ins = lir->insCall(&js_Array_1str_ci, args);
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
set(&v, v_ins);
|
|
|
|
}
|
|
|
|
}
|
2008-08-11 10:24:47 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INITPROP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-09 22:22:52 -07:00
|
|
|
// All the action is in record_SetPropHit.
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INITELEM()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-11 10:24:47 -07:00
|
|
|
return record_JSOP_SETELEM();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DEFSHARP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_USESHARP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INCARG()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-09 15:15:32 -07:00
|
|
|
return inc(argval(GET_ARGNO(cx->fp->regs->pc)), 1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
2008-08-08 18:58:04 -07:00
|
|
|
TraceRecorder::record_JSOP_INCLOCAL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
return inc(varval(GET_SLOTNO(cx->fp->regs->pc)), 1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DECARG()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-09 15:15:32 -07:00
|
|
|
return inc(argval(GET_ARGNO(cx->fp->regs->pc)), -1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
2008-08-08 18:58:04 -07:00
|
|
|
TraceRecorder::record_JSOP_DECLOCAL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
return inc(varval(GET_SLOTNO(cx->fp->regs->pc)), -1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ARGINC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-04 03:06:18 -07:00
|
|
|
return inc(argval(GET_ARGNO(cx->fp->regs->pc)), 1, false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
2008-08-08 18:58:04 -07:00
|
|
|
TraceRecorder::record_JSOP_LOCALINC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
return inc(varval(GET_SLOTNO(cx->fp->regs->pc)), 1, false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ARGDEC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-04 03:06:18 -07:00
|
|
|
return inc(argval(GET_ARGNO(cx->fp->regs->pc)), -1, false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
2008-08-08 18:58:04 -07:00
|
|
|
TraceRecorder::record_JSOP_LOCALDEC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
return inc(varval(GET_SLOTNO(cx->fp->regs->pc)), -1, false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ITER()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-06 13:36:29 -07:00
|
|
|
jsval& v = stackval(-1);
|
|
|
|
if (!JSVAL_IS_PRIMITIVE(v)) {
|
2008-08-13 14:02:35 -07:00
|
|
|
jsuint flags = cx->fp->regs->pc[1];
|
|
|
|
LIns* args[] = { get(&v), INS_CONST(flags), cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
LIns* v_ins = lir->insCall(&js_FastValueToIterator_ci, args);
|
2008-08-11 15:40:04 -07:00
|
|
|
guard(false, lir->ins_eq0(v_ins), MISMATCH_EXIT);
|
2008-08-06 13:36:29 -07:00
|
|
|
set(&v, v_ins);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ABORT_TRACE("for-in on a primitive value");
|
2008-07-30 16:32:33 -07:00
|
|
|
}
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
2008-08-13 14:02:35 -07:00
|
|
|
TraceRecorder::forInLoop(jsval* vp)
|
2008-07-30 16:32:33 -07:00
|
|
|
{
|
2008-08-13 14:02:35 -07:00
|
|
|
jsval& iterobj_val = stackval(-1);
|
|
|
|
if (!JSVAL_IS_PRIMITIVE(iterobj_val)) {
|
|
|
|
LIns* args[] = { get(&iterobj_val), cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
LIns* v_ins = lir->insCall(&js_FastCallIteratorNext_ci, args);
|
2008-08-28 21:15:39 -07:00
|
|
|
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT);
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-28 21:15:39 -07:00
|
|
|
LIns* flag_ins = lir->ins_eq0(lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_HOLE)));
|
2008-08-18 15:32:17 -07:00
|
|
|
LIns* iter_ins = get(vp);
|
2008-09-09 22:22:52 -07:00
|
|
|
jsval expected = JSVAL_IS_VOID(*vp) ? JSVAL_STRING : JSVAL_TAG(*vp);
|
|
|
|
if (!box_jsval(expected, iter_ins))
|
2008-07-30 16:32:33 -07:00
|
|
|
return false;
|
2008-09-26 20:39:21 -07:00
|
|
|
iter_ins = lir->ins_choose(flag_ins, v_ins, iter_ins);
|
2008-09-09 22:22:52 -07:00
|
|
|
if (!unbox_jsval(expected, iter_ins))
|
2008-08-13 14:02:35 -07:00
|
|
|
return false;
|
|
|
|
set(vp, iter_ins);
|
|
|
|
stack(0, flag_ins);
|
|
|
|
return true;
|
2008-07-30 16:32:33 -07:00
|
|
|
}
|
|
|
|
|
2008-08-13 14:02:35 -07:00
|
|
|
ABORT_TRACE("for-in on a primitive value");
|
2008-07-30 16:32:33 -07:00
|
|
|
}
|
|
|
|
|
2008-08-12 00:34:43 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ENDITER()
|
|
|
|
{
|
|
|
|
LIns* args[] = { stack(-1), cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
LIns* ok_ins = lir->insCall(&js_CloseIterator_ci, args);
|
2008-08-12 00:34:43 -07:00
|
|
|
guard(false, lir->ins_eq0(ok_ins), MISMATCH_EXIT);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_FORNAME()
|
|
|
|
{
|
2008-08-12 09:42:29 -07:00
|
|
|
jsval* vp;
|
2008-08-13 14:02:35 -07:00
|
|
|
return name(vp) && forInLoop(vp);
|
2008-08-12 00:34:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_FORPROP()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_FORELEM()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_FORARG()
|
|
|
|
{
|
2008-08-13 14:02:35 -07:00
|
|
|
return forInLoop(&argval(GET_ARGNO(cx->fp->regs->pc)));
|
2008-08-12 00:34:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_FORLOCAL()
|
|
|
|
{
|
2008-08-13 14:02:35 -07:00
|
|
|
return forInLoop(&varval(GET_SLOTNO(cx->fp->regs->pc)));
|
2008-08-12 00:34:43 -07:00
|
|
|
}
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_FORCONST()
|
2008-07-30 16:32:33 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_POPN()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 06:52:50 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_BINDNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-09 15:15:32 -07:00
|
|
|
JSObject* obj = cx->fp->scopeChain;
|
2008-07-14 16:40:38 -07:00
|
|
|
if (obj != globalObj)
|
2008-08-07 18:12:31 -07:00
|
|
|
ABORT_TRACE("JSOP_BINDNAME crosses global scopes");
|
2008-07-09 15:15:32 -07:00
|
|
|
|
2008-08-20 22:40:39 -07:00
|
|
|
LIns* obj_ins = scopeChain();
|
2008-07-09 15:15:32 -07:00
|
|
|
JSObject* obj2;
|
2008-07-17 23:57:56 -07:00
|
|
|
jsuword pcval;
|
|
|
|
if (!test_property_cache(obj, obj_ins, obj2, pcval))
|
2008-07-09 15:15:32 -07:00
|
|
|
return false;
|
2008-08-28 16:37:03 -07:00
|
|
|
if (obj2 != obj)
|
|
|
|
ABORT_TRACE("JSOP_BINDNAME found a non-direct property on the global object");
|
2008-07-09 15:15:32 -07:00
|
|
|
|
|
|
|
stack(0, obj_ins);
|
2008-07-04 23:53:29 -07:00
|
|
|
return true;
|
2008-07-05 10:41:35 -07:00
|
|
|
}
|
2008-07-09 11:42:31 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SETNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-09 16:37:31 -07:00
|
|
|
jsval& l = stackval(-2);
|
2008-08-12 00:34:43 -07:00
|
|
|
JS_ASSERT(!JSVAL_IS_PRIMITIVE(l));
|
2008-07-08 18:07:27 -07:00
|
|
|
|
|
|
|
/*
|
2008-07-09 11:42:31 -07:00
|
|
|
* Trace cases that are global code or in lightweight functions scoped by
|
|
|
|
* the global object only.
|
2008-07-08 18:07:27 -07:00
|
|
|
*/
|
2008-07-09 11:42:31 -07:00
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(l);
|
2008-07-14 16:40:38 -07:00
|
|
|
if (obj != cx->fp->scopeChain || obj != globalObj)
|
2008-09-09 22:22:52 -07:00
|
|
|
ABORT_TRACE("JSOP_SETNAME left operand is not the global object");
|
2008-07-09 09:59:51 -07:00
|
|
|
|
2008-09-09 22:22:52 -07:00
|
|
|
// The rest of the work is in record_SetPropHit.
|
2008-07-08 18:07:27 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-08 18:07:27 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_THROW()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_IN()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-27 23:00:43 -07:00
|
|
|
jsval& rval = stackval(-1);
|
|
|
|
if (JSVAL_IS_PRIMITIVE(rval))
|
|
|
|
ABORT_TRACE("JSOP_IN on non-object right operand");
|
|
|
|
|
|
|
|
jsval& lval = stackval(-2);
|
|
|
|
if (!JSVAL_IS_PRIMITIVE(lval))
|
|
|
|
ABORT_TRACE("JSOP_IN on E4X QName left operand");
|
|
|
|
|
|
|
|
jsid id;
|
2008-08-29 00:58:10 -07:00
|
|
|
if (JSVAL_IS_INT(lval)) {
|
2008-08-27 23:00:43 -07:00
|
|
|
id = INT_JSVAL_TO_JSID(lval);
|
2008-08-29 00:58:10 -07:00
|
|
|
} else {
|
|
|
|
if (!JSVAL_IS_STRING(lval))
|
|
|
|
ABORT_TRACE("non-string left operand to JSOP_IN");
|
2008-08-29 15:59:09 -07:00
|
|
|
if (!js_ValueToStringId(cx, lval, &id))
|
|
|
|
return false;
|
2008-08-29 00:58:10 -07:00
|
|
|
}
|
2008-08-27 23:00:43 -07:00
|
|
|
|
|
|
|
// Expect what we see at trace recording time (hit or miss) to be the same
|
|
|
|
// when executing the trace. Use a builtin helper for named properties, as
|
|
|
|
// forInLoop does. First, handle indexes in dense arrays as a special case.
|
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(rval);
|
|
|
|
LIns* obj_ins = get(&rval);
|
|
|
|
|
|
|
|
bool cond;
|
|
|
|
LIns* x;
|
|
|
|
do {
|
|
|
|
if (guardDenseArray(obj, obj_ins)) {
|
|
|
|
if (JSVAL_IS_INT(lval)) {
|
|
|
|
jsint idx = JSVAL_TO_INT(lval);
|
|
|
|
LIns* idx_ins = f2i(get(&lval));
|
|
|
|
LIns* dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots));
|
|
|
|
if (!guardDenseArrayIndex(obj, idx, obj_ins, dslots_ins, idx_ins))
|
|
|
|
ABORT_TRACE("dense array index out of bounds");
|
|
|
|
|
2008-10-06 22:30:36 -07:00
|
|
|
// We can't "see through" a hole to a possible Array.prototype
|
|
|
|
// property, so we must abort/guard.
|
|
|
|
if (obj->dslots[idx] == JSVAL_HOLE)
|
|
|
|
ABORT_TRACE("can't see through hole in dense array");
|
|
|
|
|
|
|
|
LIns* addr_ins = lir->ins2(LIR_piadd, dslots_ins,
|
|
|
|
lir->ins2i(LIR_pilsh, idx_ins,
|
|
|
|
(sizeof(jsval) == 4) ? 2 : 3));
|
|
|
|
guard(false,
|
|
|
|
lir->ins2(LIR_eq, lir->insLoad(LIR_ldp, addr_ins, 0), INS_CONST(JSVAL_HOLE)),
|
|
|
|
MISMATCH_EXIT);
|
|
|
|
|
|
|
|
cond = true;
|
|
|
|
x = INS_CONST(cond);
|
2008-08-27 23:00:43 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not an index id, but a dense array -- go up to the proto. */
|
|
|
|
obj = STOBJ_GET_PROTO(obj);
|
|
|
|
obj_ins = stobj_get_fslot(obj_ins, JSSLOT_PROTO);
|
|
|
|
} else {
|
|
|
|
if (JSVAL_IS_INT(id))
|
|
|
|
ABORT_TRACE("INT in OBJ where OBJ is not a dense array");
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject* obj2;
|
|
|
|
JSProperty* prop;
|
|
|
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
|
|
|
|
ABORT_TRACE("OBJ_LOOKUP_PROPERTY failed in JSOP_IN");
|
|
|
|
|
|
|
|
cond = prop != NULL;
|
|
|
|
if (prop)
|
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
|
|
|
|
|
|
|
LIns* args[] = { get(&lval), obj_ins, cx_ins };
|
2008-10-16 12:24:10 -07:00
|
|
|
x = lir->insCall(&js_HasNamedProperty_ci, args);
|
2008-08-28 21:15:39 -07:00
|
|
|
guard(false, lir->ins2i(LIR_eq, x, JSVAL_TO_BOOLEAN(JSVAL_VOID)), OOM_EXIT);
|
2008-08-27 23:00:43 -07:00
|
|
|
x = lir->ins2i(LIR_eq, x, 1);
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
/* The interpreter fuses comparisons and the following branch,
|
|
|
|
so we have to do that here as well. */
|
2008-08-29 15:12:17 -07:00
|
|
|
fuseIf(cx->fp->regs->pc + 1, cond, x);
|
2008-08-27 23:00:43 -07:00
|
|
|
|
|
|
|
/* We update the stack after the guard. This is safe since
|
|
|
|
the guard bails out at the comparison and the interpreter
|
2008-08-28 21:15:39 -07:00
|
|
|
will therefore re-execute the comparison. This way the
|
2008-08-27 23:00:43 -07:00
|
|
|
value of the condition doesn't have to be calculated and
|
|
|
|
saved on the stack in most cases. */
|
|
|
|
set(&lval, x);
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INSTANCEOF()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DEBUGGER()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GOSUB()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_RETSUB()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_EXCEPTION()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_LINENO()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 06:52:50 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_CONDSWITCH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 06:52:50 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_CASE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-18 13:31:26 -07:00
|
|
|
return cmp(LIR_feq, CMP_CASE);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DEFAULT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_EVAL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ENUMELEM()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GETTER()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SETTER()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DEFFUN()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DEFCONST()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DEFVAR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
2008-08-09 20:12:32 -07:00
|
|
|
/*
|
|
|
|
* XXX could hoist out to jsinterp.h and share with jsinterp.cpp, but
|
|
|
|
* XXX jsopcode.cpp has different definitions of same-named macros.
|
|
|
|
*/
|
|
|
|
#define GET_FULL_INDEX(PCOFF) \
|
|
|
|
(atoms - script->atomMap.vector + GET_INDEX(regs.pc + PCOFF))
|
|
|
|
|
|
|
|
#define LOAD_FUNCTION(PCOFF) \
|
|
|
|
JS_GET_SCRIPT_FUNCTION(script, GET_FULL_INDEX(PCOFF), fun)
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ANONFUNOBJ()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-09 20:12:32 -07:00
|
|
|
JSFunction* fun;
|
|
|
|
JSFrameRegs& regs = *cx->fp->regs;
|
|
|
|
JSScript* script = cx->fp->script;
|
|
|
|
LOAD_FUNCTION(0); // needs script, regs, fun
|
2008-08-18 15:32:17 -07:00
|
|
|
|
2008-08-09 20:12:32 -07:00
|
|
|
JSObject* obj = FUN_OBJECT(fun);
|
|
|
|
if (OBJ_GET_PARENT(cx, obj) != cx->fp->scopeChain)
|
|
|
|
ABORT_TRACE("can't trace with activation object on scopeChain");
|
|
|
|
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
stack(0, INS_CONSTPTR(obj));
|
2008-08-09 20:12:32 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NAMEDFUNOBJ()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SETLOCALPOP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-10-02 15:39:25 -07:00
|
|
|
var(GET_SLOTNO(cx->fp->regs->pc), stack(-1));
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SETCALL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_TRY()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 06:52:50 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_FINALLY()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 06:52:50 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NOP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 06:52:50 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ARGSUB()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-05 18:29:08 -07:00
|
|
|
JSStackFrame* fp = cx->fp;
|
|
|
|
if (!(fp->fun->flags & JSFUN_HEAVYWEIGHT)) {
|
|
|
|
uintN slot = GET_ARGNO(fp->regs->pc);
|
2008-09-18 15:23:22 -07:00
|
|
|
if (slot < fp->fun->nargs && slot < fp->argc && !fp->argsobj) {
|
2008-09-05 18:29:08 -07:00
|
|
|
stack(0, get(&cx->fp->argv[slot]));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ABORT_TRACE("can't trace JSOP_ARGSUB hard case");
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ARGCNT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-05 18:29:08 -07:00
|
|
|
if (!(cx->fp->fun->flags & JSFUN_HEAVYWEIGHT)) {
|
|
|
|
jsdpun u;
|
|
|
|
u.d = cx->fp->argc;
|
|
|
|
stack(0, lir->insImmq(u.u64));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
ABORT_TRACE("can't trace heavyweight JSOP_ARGCNT");
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-19 15:42:31 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
2008-09-24 19:31:51 -07:00
|
|
|
TraceRecorder::record_DefLocalFunSetSlot(uint32 slot, JSObject* obj)
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-24 19:31:51 -07:00
|
|
|
var(slot, INS_CONSTPTR(obj));
|
|
|
|
return true;
|
|
|
|
}
|
2008-07-19 06:29:56 -07:00
|
|
|
|
2008-09-24 19:31:51 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DEFLOCALFUN()
|
|
|
|
{
|
2008-07-19 06:29:56 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-19 15:42:31 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GOTOX()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 17:46:25 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_IFEQX()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-29 15:12:17 -07:00
|
|
|
trackCfgMerges(cx->fp->regs->pc);
|
2008-07-10 21:55:09 -07:00
|
|
|
return record_JSOP_IFEQ();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_IFNEX()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-10 21:55:09 -07:00
|
|
|
return record_JSOP_IFNE();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ORX()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-10 21:55:09 -07:00
|
|
|
return record_JSOP_OR();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ANDX()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-10 21:55:09 -07:00
|
|
|
return record_JSOP_AND();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GOSUBX()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-10 21:55:09 -07:00
|
|
|
return record_JSOP_GOSUB();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_CASEX()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-09-18 13:31:26 -07:00
|
|
|
return cmp(LIR_feq, CMP_CASE);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DEFAULTX()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_TABLESWITCHX()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
return switchop();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_LOOKUPSWITCHX()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
return switchop();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_BACKPATCH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 06:52:50 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_BACKPATCH_POP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-06 06:52:50 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_THROWING()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SETRVAL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-27 12:47:43 -07:00
|
|
|
// If we implement this, we need to update JSOP_STOP.
|
2008-07-03 23:57:57 -07:00
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_RETRVAL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GETGVAR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)];
|
2008-07-06 13:59:59 -07:00
|
|
|
if (JSVAL_IS_NULL(slotval))
|
|
|
|
return true; // We will see JSOP_NAME from the interpreter's jump, so no-op here.
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-07-06 13:59:59 -07:00
|
|
|
uint32 slot = JSVAL_TO_INT(slotval);
|
2008-07-30 04:17:22 -07:00
|
|
|
|
2008-07-30 04:20:48 -07:00
|
|
|
if (!lazilyImportGlobalSlot(slot))
|
2008-07-30 04:17:22 -07:00
|
|
|
ABORT_TRACE("lazy import of global slot failed");
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-09-24 23:09:46 -07:00
|
|
|
stack(0, get(&STOBJ_GET_SLOT(globalObj, slot)));
|
2008-07-06 13:59:59 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SETGVAR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)];
|
2008-07-07 17:21:54 -07:00
|
|
|
if (JSVAL_IS_NULL(slotval))
|
|
|
|
return true; // We will see JSOP_NAME from the interpreter's jump, so no-op here.
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-07-07 17:21:54 -07:00
|
|
|
uint32 slot = JSVAL_TO_INT(slotval);
|
2008-07-30 04:17:22 -07:00
|
|
|
|
2008-07-30 04:20:48 -07:00
|
|
|
if (!lazilyImportGlobalSlot(slot))
|
2008-07-30 04:17:22 -07:00
|
|
|
ABORT_TRACE("lazy import of global slot failed");
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-09-24 23:09:46 -07:00
|
|
|
set(&STOBJ_GET_SLOT(globalObj, slot), stack(-1));
|
2008-07-07 17:21:54 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INCGVAR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)];
|
2008-07-06 13:59:59 -07:00
|
|
|
if (JSVAL_IS_NULL(slotval))
|
|
|
|
return true; // We will see JSOP_INCNAME from the interpreter's jump, so no-op here.
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-07-06 13:59:59 -07:00
|
|
|
uint32 slot = JSVAL_TO_INT(slotval);
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-07-30 04:20:48 -07:00
|
|
|
if (!lazilyImportGlobalSlot(slot))
|
2008-07-30 04:17:22 -07:00
|
|
|
ABORT_TRACE("lazy import of global slot failed");
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-09-24 23:09:46 -07:00
|
|
|
return inc(STOBJ_GET_SLOT(globalObj, slot), 1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DECGVAR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)];
|
2008-07-07 17:21:54 -07:00
|
|
|
if (JSVAL_IS_NULL(slotval))
|
|
|
|
return true; // We will see JSOP_INCNAME from the interpreter's jump, so no-op here.
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-07-07 17:21:54 -07:00
|
|
|
uint32 slot = JSVAL_TO_INT(slotval);
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-07-30 04:20:48 -07:00
|
|
|
if (!lazilyImportGlobalSlot(slot))
|
2008-07-30 04:17:22 -07:00
|
|
|
ABORT_TRACE("lazy import of global slot failed");
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-09-24 23:09:46 -07:00
|
|
|
return inc(STOBJ_GET_SLOT(globalObj, slot), -1);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GVARINC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)];
|
2008-07-07 17:21:54 -07:00
|
|
|
if (JSVAL_IS_NULL(slotval))
|
|
|
|
return true; // We will see JSOP_INCNAME from the interpreter's jump, so no-op here.
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-07-07 17:21:54 -07:00
|
|
|
uint32 slot = JSVAL_TO_INT(slotval);
|
2008-07-30 04:17:22 -07:00
|
|
|
|
2008-07-30 04:20:48 -07:00
|
|
|
if (!lazilyImportGlobalSlot(slot))
|
2008-07-30 04:17:22 -07:00
|
|
|
ABORT_TRACE("lazy import of global slot failed");
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-09-24 23:09:46 -07:00
|
|
|
return inc(STOBJ_GET_SLOT(globalObj, slot), 1, false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GVARDEC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-22 21:20:29 -07:00
|
|
|
jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)];
|
2008-07-07 17:21:54 -07:00
|
|
|
if (JSVAL_IS_NULL(slotval))
|
|
|
|
return true; // We will see JSOP_INCNAME from the interpreter's jump, so no-op here.
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-07-07 17:21:54 -07:00
|
|
|
uint32 slot = JSVAL_TO_INT(slotval);
|
2008-07-30 04:17:22 -07:00
|
|
|
|
2008-07-30 04:20:48 -07:00
|
|
|
if (!lazilyImportGlobalSlot(slot))
|
2008-07-30 04:17:22 -07:00
|
|
|
ABORT_TRACE("lazy import of global slot failed");
|
2008-07-30 16:32:33 -07:00
|
|
|
|
2008-09-24 23:09:46 -07:00
|
|
|
return inc(STOBJ_GET_SLOT(globalObj, slot), -1, false);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-09 16:37:31 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_REGEXP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
// begin JS_HAS_XML_SUPPORT
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DEFXMLNS()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ANYNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_QNAMEPART()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_QNAMECONST()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_QNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_TOATTRNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_TOATTRVAL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ADDATTRNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ADDATTRVAL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_BINDXMLNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_SETXMLNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_XMLNAME()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DESCENDANTS()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_FILTER()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ENDFILTER()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_TOXML()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_TOXMLLIST()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_XMLTAGEXPR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_XMLELTEXPR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_XMLOBJECT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_XMLCDATA()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_XMLCOMMENT()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_XMLPI()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GETFUNNS()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_STARTXML()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_STARTXMLEXPR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-12 12:35:36 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
// end JS_HAS_XML_SUPPORT
|
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_CALLPROP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-11 17:59:10 -07:00
|
|
|
jsval& l = stackval(-1);
|
2008-07-29 07:32:18 -07:00
|
|
|
JSObject* obj;
|
|
|
|
LIns* obj_ins;
|
2008-10-11 20:26:29 -07:00
|
|
|
LIns* this_ins;
|
2008-07-29 07:32:18 -07:00
|
|
|
if (!JSVAL_IS_PRIMITIVE(l)) {
|
|
|
|
obj = JSVAL_TO_OBJECT(l);
|
|
|
|
obj_ins = get(&l);
|
2008-10-11 20:26:29 -07:00
|
|
|
this_ins = obj_ins; // |this| for subsequent call
|
2008-07-29 07:32:18 -07:00
|
|
|
} else {
|
|
|
|
jsint i;
|
2008-08-07 18:12:31 -07:00
|
|
|
debug_only(const char* protoname = NULL;)
|
2008-07-29 07:32:18 -07:00
|
|
|
if (JSVAL_IS_STRING(l)) {
|
|
|
|
i = JSProto_String;
|
2008-08-07 18:12:31 -07:00
|
|
|
debug_only(protoname = "String.prototype";)
|
2008-07-29 07:32:18 -07:00
|
|
|
} else if (JSVAL_IS_NUMBER(l)) {
|
|
|
|
i = JSProto_Number;
|
2008-08-07 18:12:31 -07:00
|
|
|
debug_only(protoname = "Number.prototype";)
|
2008-09-26 19:20:52 -07:00
|
|
|
} else if (JSVAL_TAG(l) == JSVAL_BOOLEAN) {
|
|
|
|
if (l == JSVAL_VOID)
|
|
|
|
ABORT_TRACE("callprop on void");
|
|
|
|
guard(false, lir->ins2i(LIR_eq, get(&l), JSVAL_TO_BOOLEAN(JSVAL_VOID)), MISMATCH_EXIT);
|
2008-07-29 07:32:18 -07:00
|
|
|
i = JSProto_Boolean;
|
2008-08-07 18:12:31 -07:00
|
|
|
debug_only(protoname = "Boolean.prototype";)
|
2008-07-29 07:32:18 -07:00
|
|
|
} else {
|
|
|
|
JS_ASSERT(JSVAL_IS_NULL(l) || JSVAL_IS_VOID(l));
|
|
|
|
ABORT_TRACE("callprop on null or void");
|
|
|
|
}
|
2008-07-11 17:59:10 -07:00
|
|
|
|
2008-07-29 07:32:18 -07:00
|
|
|
if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj))
|
|
|
|
ABORT_TRACE("GetClassPrototype failed!");
|
2008-07-29 16:01:00 -07:00
|
|
|
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
obj_ins = INS_CONSTPTR(obj);
|
2008-08-07 18:12:31 -07:00
|
|
|
debug_only(obj_ins = addName(obj_ins, protoname);)
|
2008-10-11 20:26:29 -07:00
|
|
|
this_ins = get(&l); // use primitive as |this|
|
2008-07-29 07:32:18 -07:00
|
|
|
}
|
2008-07-29 16:01:00 -07:00
|
|
|
|
2008-07-11 19:59:09 -07:00
|
|
|
JSObject* obj2;
|
2008-07-17 23:57:56 -07:00
|
|
|
jsuword pcval;
|
|
|
|
if (!test_property_cache(obj, obj_ins, obj2, pcval))
|
2008-08-07 18:12:31 -07:00
|
|
|
return false;
|
2008-07-11 19:59:09 -07:00
|
|
|
|
2008-08-07 18:12:31 -07:00
|
|
|
if (PCVAL_IS_NULL(pcval) || !PCVAL_IS_OBJECT(pcval))
|
|
|
|
ABORT_TRACE("callee is not an object");
|
|
|
|
JS_ASSERT(HAS_FUNCTION_CLASS(PCVAL_TO_OBJECT(pcval)));
|
2008-07-11 19:59:09 -07:00
|
|
|
|
2008-09-05 13:55:03 -07:00
|
|
|
if (JSVAL_IS_PRIMITIVE(l)) {
|
|
|
|
JSFunction* fun = GET_FUNCTION_PRIVATE(cx, PCVAL_TO_OBJECT(pcval));
|
|
|
|
if (!PRIMITIVE_THIS_TEST(fun, l))
|
|
|
|
ABORT_TRACE("callee does not accept primitive |this|");
|
|
|
|
}
|
|
|
|
|
2008-10-11 20:26:29 -07:00
|
|
|
stack(0, this_ins);
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
stack(-1, INS_CONSTPTR(PCVAL_TO_OBJECT(pcval)));
|
2008-07-11 17:59:10 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-12 12:35:36 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_DELDESC()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_UINT24()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-14 15:22:05 -07:00
|
|
|
jsdpun u;
|
|
|
|
u.d = (jsdouble)GET_UINT24(cx->fp->regs->pc);
|
|
|
|
stack(0, lir->insImmq(u.u64));
|
2008-07-04 03:06:18 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-01 11:03:52 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INDEXBASE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-10 21:55:09 -07:00
|
|
|
atoms += GET_INDEXBASE(cx->fp->regs->pc);
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_RESETBASE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-10 21:55:09 -07:00
|
|
|
atoms = cx->fp->script->atomMap.vector;
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_RESETBASE0()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-10 21:55:09 -07:00
|
|
|
atoms = cx->fp->script->atomMap.vector;
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_CALLELEM()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_STOP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-08 14:38:44 -07:00
|
|
|
/*
|
|
|
|
* We know falling off the end of a constructor returns the new object that
|
|
|
|
* was passed in via fp->argv[-1], while falling off the end of a function
|
|
|
|
* returns undefined.
|
|
|
|
*
|
|
|
|
* NB: we do not support script rval (eval, API users who want the result
|
|
|
|
* of the last expression-statement, debugger API calls).
|
|
|
|
*/
|
|
|
|
JSStackFrame *fp = cx->fp;
|
|
|
|
if (fp->flags & JSFRAME_CONSTRUCTING) {
|
|
|
|
JS_ASSERT(OBJECT_TO_JSVAL(fp->thisp) == fp->argv[-1]);
|
|
|
|
rval_ins = get(&fp->argv[-1]);
|
|
|
|
} else {
|
2008-08-28 23:50:48 -07:00
|
|
|
rval_ins = INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID));
|
2008-08-08 14:38:44 -07:00
|
|
|
}
|
2008-07-24 10:35:10 -07:00
|
|
|
clearFrameSlotsFromCache();
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GETXPROP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-16 10:10:17 -07:00
|
|
|
jsval& l = stackval(-1);
|
|
|
|
if (JSVAL_IS_PRIMITIVE(l))
|
|
|
|
ABORT_TRACE("primitive-this for GETXPROP?");
|
2008-07-19 00:15:22 -07:00
|
|
|
|
2008-07-16 10:10:17 -07:00
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(l);
|
2008-08-12 09:42:29 -07:00
|
|
|
if (obj != cx->fp->scopeChain || obj != globalObj)
|
2008-07-16 10:10:17 -07:00
|
|
|
return false;
|
|
|
|
|
2008-08-12 09:42:29 -07:00
|
|
|
jsval* vp;
|
|
|
|
if (!name(vp))
|
|
|
|
return false;
|
|
|
|
stack(-1, get(vp));
|
2008-07-16 10:10:17 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_CALLXMLNAME()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_TYPEOFEXPR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-09 19:54:01 -07:00
|
|
|
return record_JSOP_TYPEOF();
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ENTERBLOCK()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_LEAVEBLOCK()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GENERATOR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_YIELD()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ARRAYPUSH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_ENUMCONSTELEM()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_LEAVEBLOCKEXPR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GETTHISPROP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-16 14:36:50 -07:00
|
|
|
LIns* this_ins;
|
2008-07-19 00:15:22 -07:00
|
|
|
|
|
|
|
/* its safe to just use cx->fp->thisp here because getThis() returns false if thisp
|
2008-07-16 14:36:50 -07:00
|
|
|
is not available */
|
|
|
|
return getThis(this_ins) && getProp(cx->fp->thisp, this_ins);
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GETARGPROP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-16 14:36:50 -07:00
|
|
|
return getProp(argval(GET_ARGNO(cx->fp->regs->pc)));
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_GETLOCALPROP()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-08 18:58:04 -07:00
|
|
|
return getProp(varval(GET_SLOTNO(cx->fp->regs->pc)));
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INDEXBASE1()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-10 21:55:09 -07:00
|
|
|
atoms += 1 << 16;
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INDEXBASE2()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-10 21:55:09 -07:00
|
|
|
atoms += 2 << 16;
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INDEXBASE3()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-10 21:55:09 -07:00
|
|
|
atoms += 3 << 16;
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_CALLGVAR()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-08 16:37:01 -07:00
|
|
|
jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)];
|
|
|
|
if (JSVAL_IS_NULL(slotval))
|
|
|
|
return true; // We will see JSOP_CALLNAME from the interpreter's jump, so no-op here.
|
|
|
|
|
|
|
|
uint32 slot = JSVAL_TO_INT(slotval);
|
|
|
|
|
|
|
|
if (!lazilyImportGlobalSlot(slot))
|
|
|
|
ABORT_TRACE("lazy import of global slot failed");
|
|
|
|
|
2008-08-10 22:36:48 -07:00
|
|
|
jsval& v = STOBJ_GET_SLOT(cx->fp->scopeChain, slot);
|
|
|
|
stack(0, get(&v));
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
stack(1, INS_CONSTPTR(NULL));
|
2008-08-20 19:24:57 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
2008-08-08 18:58:04 -07:00
|
|
|
TraceRecorder::record_JSOP_CALLLOCAL()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-08 16:37:01 -07:00
|
|
|
uintN slot = GET_SLOTNO(cx->fp->regs->pc);
|
|
|
|
stack(0, var(slot));
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
stack(1, INS_CONSTPTR(NULL));
|
2008-08-20 19:24:57 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_CALLARG()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-08 16:37:01 -07:00
|
|
|
uintN slot = GET_ARGNO(cx->fp->regs->pc);
|
|
|
|
stack(0, arg(slot));
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
stack(1, INS_CONSTPTR(NULL));
|
2008-08-20 19:24:57 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-10 22:36:48 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NULLTHIS()
|
|
|
|
{
|
Kind of an Array initialiser tour-de-force for bug 452878:
1. Split FastNewArray from FastNewObject built-in for greater speed/specialization and further splitting into Array_1str, etc.
2. Add Array_1str, Array_2obj, and Array_3num builtins for benchmarked new Array(...) constructions.
3. Export ARRAY_SET_DENSE_LENGTH and ARRAY_GROWBY via jsarray.h to jstracer.cpp.
4. Tweaked SetArrayElement to make common/best case code be the predicted/prefetched path.
5. js_MakeArraySlow now preserves the pre-slow length in JSSLOT_ARRAY_COUTN as a jsval-tagged int if possible -- this will help the tracer avoid aborting on dense arrays that turned slow but not sparse by addition of a named property.
6. Export js_fun_apply and js_Object from their respective .cpp files, in these cases just to jstracer.cpp via local prototypes (no .h files involved).
7. More INS_CONSTPTR and INS_CONST macrology for better names in trace debug spew.
8. Fix TraceRecorder::test_property_cache to avoid aborting on JSOP_SETNAME that creates a new global, by setting it to undefined so it can be lazily imported. This helps 3d-raytrace.js, which has an unintended global loop control variable in a function.
9. JSTraceableNative loses its premature-deadwood tclasp member (my bad).
10. TraceRecorder::record_JSOP_NEW() handles 'new Object' now along with the 'new Array' variations. I also cut down the copy-paste code from JSOP_CALL's record method to mostly what is needed now.
11. Add KNOWN_NATIVE_DECL macro for concise prototype of library-private js_* native functions, and alphabetized the lists (too long for any other order to be winning).
12. Big honking special case for foo.apply(obj, [str]), which we can generalize as needed. Helps string-tagcloud.js. What's cool is how tracing allows us to rewrite this to foo(str) with this set to obj, eliminating the Function.prototype.apply. This requires some rewriting in JSOP_ENDINIT's record method.
2008-09-01 01:24:58 -07:00
|
|
|
stack(0, INS_CONSTPTR(NULL));
|
2008-08-20 19:24:57 -07:00
|
|
|
return true;
|
2008-08-10 22:36:48 -07:00
|
|
|
}
|
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INT8()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-14 15:22:05 -07:00
|
|
|
jsdpun u;
|
|
|
|
u.d = (jsdouble)GET_INT8(cx->fp->regs->pc);
|
|
|
|
stack(0, lir->insImmq(u.u64));
|
2008-07-04 03:06:18 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_INT32()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-14 15:22:05 -07:00
|
|
|
jsdpun u;
|
|
|
|
u.d = (jsdouble)GET_INT32(cx->fp->regs->pc);
|
|
|
|
stack(0, lir->insImmq(u.u64));
|
2008-07-04 03:06:18 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_LENGTH()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-07-16 23:53:14 -07:00
|
|
|
jsval& l = stackval(-1);
|
2008-07-29 06:51:27 -07:00
|
|
|
if (JSVAL_IS_PRIMITIVE(l)) {
|
|
|
|
if (!JSVAL_IS_STRING(l))
|
|
|
|
ABORT_TRACE("non-string primitives unsupported");
|
|
|
|
LIns* str_ins = get(&l);
|
2008-08-19 17:19:19 -07:00
|
|
|
LIns* len_ins = lir->insLoad(LIR_ldp, str_ins, (int)offsetof(JSString, length));
|
2008-07-29 10:53:58 -07:00
|
|
|
|
2008-08-20 10:38:50 -07:00
|
|
|
LIns* masked_len_ins = lir->ins2(LIR_piand,
|
2008-08-11 18:41:06 -07:00
|
|
|
len_ins,
|
2008-08-19 17:19:19 -07:00
|
|
|
INS_CONSTPTR(JSSTRING_LENGTH_MASK));
|
2008-08-11 18:41:06 -07:00
|
|
|
|
|
|
|
LIns *choose_len_ins =
|
2008-08-18 16:23:37 -07:00
|
|
|
lir->ins_choose(lir->ins_eq0(lir->ins2(LIR_piand,
|
2008-08-11 18:41:06 -07:00
|
|
|
len_ins,
|
|
|
|
INS_CONSTPTR(JSSTRFLAG_DEPENDENT))),
|
|
|
|
masked_len_ins,
|
2008-08-18 16:23:37 -07:00
|
|
|
lir->ins_choose(lir->ins_eq0(lir->ins2(LIR_piand,
|
2008-08-11 18:41:06 -07:00
|
|
|
len_ins,
|
|
|
|
INS_CONSTPTR(JSSTRFLAG_PREFIX))),
|
2008-08-18 16:23:37 -07:00
|
|
|
lir->ins2(LIR_piand,
|
2008-08-11 18:41:06 -07:00
|
|
|
len_ins,
|
|
|
|
INS_CONSTPTR(JSSTRDEP_LENGTH_MASK)),
|
2008-09-26 20:39:21 -07:00
|
|
|
masked_len_ins));
|
2008-08-11 18:41:06 -07:00
|
|
|
|
|
|
|
set(&l, lir->ins1(LIR_i2f, choose_len_ins));
|
2008-07-29 06:51:27 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-30 16:32:33 -07:00
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(l);
|
2008-07-29 06:51:27 -07:00
|
|
|
if (!OBJ_IS_DENSE_ARRAY(cx, obj))
|
2008-07-16 23:53:14 -07:00
|
|
|
ABORT_TRACE("only dense arrays supported");
|
2008-07-30 16:32:33 -07:00
|
|
|
if (!guardDenseArray(obj, get(&l)))
|
2008-07-16 23:53:14 -07:00
|
|
|
ABORT_TRACE("OBJ_IS_DENSE_ARRAY but not?!?");
|
2008-07-30 16:32:33 -07:00
|
|
|
LIns* v_ins = lir->ins1(LIR_i2f, stobj_get_fslot(get(&l), JSSLOT_ARRAY_LENGTH));
|
2008-07-16 23:53:14 -07:00
|
|
|
set(&l, v_ins);
|
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_NEWARRAY()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-07-10 21:55:09 -07:00
|
|
|
|
2008-08-01 11:03:52 -07:00
|
|
|
bool
|
|
|
|
TraceRecorder::record_JSOP_HOLE()
|
2008-07-03 23:57:57 -07:00
|
|
|
{
|
2008-08-28 23:50:48 -07:00
|
|
|
stack(0, INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_HOLE)));
|
2008-07-06 06:52:50 -07:00
|
|
|
return true;
|
2008-07-03 23:57:57 -07:00
|
|
|
}
|
2008-08-08 18:58:04 -07:00
|
|
|
|
|
|
|
#define UNUSED(op) bool TraceRecorder::record_##op() { return false; }
|
|
|
|
|
|
|
|
UNUSED(JSOP_UNUSED76)
|
|
|
|
UNUSED(JSOP_UNUSED77)
|
|
|
|
UNUSED(JSOP_UNUSED78)
|
|
|
|
UNUSED(JSOP_UNUSED79)
|
2008-10-21 11:35:22 -07:00
|
|
|
UNUSED(JSOP_UNUSED131)
|
2008-08-08 18:58:04 -07:00
|
|
|
UNUSED(JSOP_UNUSED201)
|
|
|
|
UNUSED(JSOP_UNUSED202)
|
|
|
|
UNUSED(JSOP_UNUSED203)
|
|
|
|
UNUSED(JSOP_UNUSED204)
|
|
|
|
UNUSED(JSOP_UNUSED205)
|
|
|
|
UNUSED(JSOP_UNUSED206)
|
|
|
|
UNUSED(JSOP_UNUSED207)
|
|
|
|
UNUSED(JSOP_UNUSED219)
|
|
|
|
UNUSED(JSOP_UNUSED226)
|