Checkpoint arguments tour-de-force (453730).

This commit is contained in:
Brendan Eich 2008-09-05 18:29:08 -07:00
parent ff7bea2566
commit fc936fa1da
5 changed files with 167 additions and 27 deletions

View File

@ -98,6 +98,10 @@ BUILTIN3(Object_p_propertyIsEnumerable,
BUILTIN2(BooleanToNumber, LO, LO, F, jsdouble, JSContext*, jsint, 1, 1)
BUILTIN2(ObjectToString, LO, LO, P, JSString*, JSContext*, JSObject*, 0, 0)
BUILTIN3(Array_1int, LO, LO, LO, P, JSObject*, JSContext*, JSObject*, jsint, 0, 0)
BUILTIN3(Array_1str, LO, LO, LO, P, JSObject*, JSContext*, JSObject*, JSString*, 0, 0)
BUILTIN4(Array_2obj, LO, LO, LO, LO, P, JSObject*, JSContext*, JSObject*, JSObject*, JSObject**, 0, 0)
BUILTIN5(Array_3num, LO, LO, F, F, F, P, JSObject*, JSContext*, JSObject*, jsdouble, jsdouble, jsdouble, 0, 0)
BUILTIN1(Arguments, LO, P, JSObject*, JSContext*, 0, 0)
// soft float
BUILTIN1(fneg, F, F, jsdouble, jsdouble, 1, 1)
@ -112,6 +116,3 @@ BUILTIN2(fmul, F, F, F, jsdouble, jsdouble, jsdouble, 1, 1)
BUILTIN2(fadd, F, F, F, jsdouble, jsdouble, jsdouble, 1, 1)
BUILTIN2(fdiv, F, F, F, jsdouble, jsdouble, jsdouble, 1, 1)
BUILTIN2(fsub, F, F, F, jsdouble, jsdouble, jsdouble, 1, 1)
BUILTIN3(Array_1str, LO, LO, LO, P, JSObject*, JSContext*, JSObject*, JSString*, 0, 0)
BUILTIN4(Array_2obj, LO, LO, LO, LO, P, JSObject*, JSContext*, JSObject*, JSObject*, JSObject**, 0, 0)
BUILTIN5(Array_3num, LO, LO, F, F, F, P, JSObject*, JSContext*, JSObject*, jsdouble, jsdouble, jsdouble, 0, 0)

View File

@ -718,6 +718,12 @@ js_Array_3num(JSContext* cx, JSObject* proto, jsdouble n1, jsdouble n2, jsdouble
return NULL;)
}
JSObject* FASTCALL
js_Arguments(JSContext* cx)
{
return NULL;
}
/* soft float */
jsdouble FASTCALL

View File

@ -347,7 +347,7 @@ static bool isPromoteInt(LIns* i)
{
jsdouble d;
return isi2f(i) || i->isconst() ||
(i->isconstq() && ((d = i->constvalf()) == (jsdouble)(jsint)d) && !JSDOUBLE_IS_NEGZERO(d));
(i->isconstq() && (d = i->constvalf()) == jsdouble(jsint(d)) && !JSDOUBLE_IS_NEGZERO(d));
}
static bool isPromoteUint(LIns* i)
@ -803,8 +803,9 @@ TraceRecorder::TraceRecorder(JSContext* cx, GuardRecord* _anchor, Fragment* _fra
this->callDepth = _fragment->calldepth;
JS_ASSERT(!_anchor || _anchor->calldepth == _fragment->calldepth);
this->atoms = cx->fp->script->atomMap.vector;
this->trashTree = false;
this->deepAborted = false;
this->applyingArguments = false;
this->trashTree = false;
this->whichTreeToTrash = _fragment->root;
debug_only_v(printf("recording starting from %s:%u@%u\n", cx->fp->script->filename,
@ -1353,8 +1354,8 @@ TraceRecorder::set(jsval* p, LIns* i, bool initializing)
/* 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. */
LIns* x;
if ((x = nativeFrameTracker.get(p)) == NULL) {
LIns* x = nativeFrameTracker.get(p);
if (!x) {
if (isGlobal(p))
x = writeBack(i, gp_ins, nativeGlobalOffset(p));
else
@ -2481,6 +2482,11 @@ bool
js_MonitorRecording(JSContext* cx)
{
TraceRecorder *tr = JS_TRACE_MONITOR(cx).recorder;
// Clear one-shot flag used to communicate between record_JSOP_CALL and record_EnterFrame.
tr->applyingArguments = false;
// Process deepAbort() requests now.
if (tr->wasDeepAborted()) {
js_AbortRecording(cx, NULL, "deep abort requested");
return false;
@ -3644,10 +3650,20 @@ TraceRecorder::record_EnterFrame()
jsval* vp = &fp->argv[fp->argc];
jsval* vpstop = vp + (fp->fun->nargs - fp->argc);
while (vp < vpstop) {
if (vp >= fp->down->regs->sp)
if (applyingArguments) {
applyingArguments = false;
while (vp < vpstop) {
JS_ASSERT(vp >= fp->down->regs->sp);
nativeFrameTracker.set(vp, (LIns*)0);
set(vp++, void_ins, true);
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);
}
}
vp = &fp->slots[0];
@ -3742,7 +3758,11 @@ TraceRecorder::record_JSOP_IFNE()
bool
TraceRecorder::record_JSOP_ARGUMENTS()
{
return false;
LIns* args[] = { cx_ins };
LIns* a_ins = lir->insCall(F_Arguments, args);
guard(false, lir->ins_eq0(a_ins), OOM_EXIT);
stack(0, a_ins);
return true;
}
bool
@ -4549,10 +4569,11 @@ KNOWN_NATIVE_DECL(js_str_substring)
bool
TraceRecorder::record_JSOP_CALL()
{
jsbytecode *pc = cx->fp->regs->pc;
JSStackFrame* fp = cx->fp;
jsbytecode *pc = fp->regs->pc;
uintN argc = GET_ARGC(pc);
jsval& fval = stackval(0 - (argc + 2));
JS_ASSERT(&fval >= StackBase(cx->fp));
JS_ASSERT(&fval >= StackBase(fp));
jsval& tval = stackval(0 - (argc + 1));
LIns* this_ins = get(&tval);
@ -4618,6 +4639,11 @@ TraceRecorder::record_JSOP_CALL()
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");
@ -4625,22 +4651,54 @@ TraceRecorder::record_JSOP_CALL()
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() || aval_ins->fid() != F_Array_1str)
ABORT_TRACE("can't yet trace Function.prototype.apply on other than [str] 2nd arg");
if (!aval_ins->isCall())
ABORT_TRACE("can't trace Function.prototype.apply on non-builtin-call 2nd arg");
if (aval_ins->fid() == F_Arguments) {
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)");
argc = fp->argc;
if (tfun->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);
}
if (aval_ins->fid() != F_Array_1str)
ABORT_TRACE("can't trace Function.prototype.apply on other than [str] 2nd arg");
JSObject* aobj = JSVAL_TO_OBJECT(aval);
JS_ASSERT(OBJ_IS_ARRAY(cx, aobj));
JS_ASSERT(aobj->fslots[JSSLOT_ARRAY_LENGTH] == 1);
JS_ASSERT(JSVAL_IS_STRING(aobj->dslots[0]));
if (!guardShapelessCallee(tval))
return false;
JSObject* tfunobj = JSVAL_TO_OBJECT(tval);
JSFunction* tfun = GET_FUNCTION_PRIVATE(cx, tfunobj);
if (FUN_INTERPRETED(tfun))
ABORT_TRACE("can't yet trace Function.prototype.apply for scripted functions");
ABORT_TRACE("can't trace Function.prototype.apply for scripted functions");
JSTraceableNative* known;
for (;;) {
@ -4907,7 +4965,7 @@ bool
TraceRecorder::elem(jsval& l, jsval& r, jsval*& vp, LIns*& v_ins, LIns*& addr_ins)
{
/* no guards for type checks, trace specialized this already */
if (!JSVAL_IS_INT(r) || JSVAL_IS_PRIMITIVE(l))
if (JSVAL_IS_PRIMITIVE(l) || !JSVAL_IS_INT(r))
return false;
/*
@ -5672,13 +5730,27 @@ TraceRecorder::record_JSOP_NOP()
bool
TraceRecorder::record_JSOP_ARGSUB()
{
return false;
JSStackFrame* fp = cx->fp;
if (!(fp->fun->flags & JSFUN_HEAVYWEIGHT)) {
uintN slot = GET_ARGNO(fp->regs->pc);
if (slot < fp->argc && !fp->argsobj) {
stack(0, get(&cx->fp->argv[slot]));
return true;
}
}
ABORT_TRACE("can't trace JSOP_ARGSUB hard case");
}
bool
TraceRecorder::record_JSOP_ARGCNT()
{
return false;
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");
}
bool

View File

@ -232,8 +232,9 @@ class TraceRecorder {
nanojit::LIns* rval_ins;
nanojit::LIns* inner_sp_ins;
nanojit::SideExit exit;
bool trashTree;
bool deepAborted;
bool applyingArguments;
bool trashTree;
nanojit::Fragment* whichTreeToTrash;
Queue<jsbytecode*> inlinedLoopEdges;
Queue<jsbytecode*> cfgMerges;
@ -331,7 +332,10 @@ class TraceRecorder {
void trackCfgMerges(jsbytecode* pc);
void fuseIf(jsbytecode* pc, bool cond, nanojit::LIns* x);
public:
friend bool js_MonitorRecording(JSContext* cx);
TraceRecorder(JSContext* cx, nanojit::GuardRecord*, nanojit::Fragment*, TreeInfo*,
unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap,
nanojit::GuardRecord* expectedInnerExit);
@ -370,8 +374,7 @@ public:
TraceRecorder* r = JS_TRACE_MONITOR(cx).recorder; \
if (!js_MonitorRecording(cx)) { \
ENABLE_TRACER(0); \
} else \
if (!r->record_##x()) { \
} else if (!r->record_##x()) { \
js_AbortRecording(cx, NULL, #x); \
ENABLE_TRACER(0); \
} \

View File

@ -1211,6 +1211,64 @@ function testTypeofHole() {
testTypeofHole.expected = "undefined,undefined,undefined,undefined,undefined,number"
test(testTypeofHole);
function test_JSOP_ARGSUB() {
function f0() { return arguments[0]; }
function f1() { return arguments[1]; }
function f2() { return arguments[2]; }
function f3() { return arguments[3]; }
function f4() { return arguments[4]; }
function f5() { return arguments[5]; }
function f6() { return arguments[6]; }
function f7() { return arguments[7]; }
function f8() { return arguments[8]; }
function f9() { return arguments[9]; }
var a = [];
for (var i = 0; i < 10; i++) {
a[0] = f0('a');
a[1] = f1('a','b');
a[2] = f2('a','b','c');
a[3] = f3('a','b','c','d');
a[4] = f4('a','b','c','d','e');
a[5] = f5('a','b','c','d','e','f');
a[6] = f6('a','b','c','d','e','f','g');
a[7] = f7('a','b','c','d','e','f','g','h');
a[8] = f8('a','b','c','d','e','f','g','h','i');
a[9] = f9('a','b','c','d','e','f','g','h','i','j');
}
return a.join("");
}
test_JSOP_ARGSUB.expected = "abcdefghij";
test(test_JSOP_ARGSUB);
function test_JSOP_ARGCNT() {
function f0() { return arguments.length; }
function f1() { return arguments.length; }
function f2() { return arguments.length; }
function f3() { return arguments.length; }
function f4() { return arguments.length; }
function f5() { return arguments.length; }
function f6() { return arguments.length; }
function f7() { return arguments.length; }
function f8() { return arguments.length; }
function f9() { return arguments.length; }
var a = [];
for (var i = 0; i < 10; i++) {
a[0] = f0('a');
a[1] = f1('a','b');
a[2] = f2('a','b','c');
a[3] = f3('a','b','c','d');
a[4] = f4('a','b','c','d','e');
a[5] = f5('a','b','c','d','e','f');
a[6] = f6('a','b','c','d','e','f','g');
a[7] = f7('a','b','c','d','e','f','g','h');
a[8] = f8('a','b','c','d','e','f','g','h','i');
a[9] = f9('a','b','c','d','e','f','g','h','i','j');
}
return a.join(",");
}
test_JSOP_ARGCNT.expected = "1,2,3,4,5,6,7,8,9,10";
test(test_JSOP_ARGCNT);
/* Keep these at the end so that we can see the summary after the trace-debug spew. */
print("\npassed:", passes.length && passes.join(","));
print("\nFAILED:", fails.length && fails.join(","));