- Fix shapeless callee guarding to guard on function object value.

- Add JSOP_NULLTHIS to help the tracer guard shapeless callees (see trace-tests.js)
- Culled bogus record_JSOP_CALLGVAR left-over forwarding to record_JSOP_GETGVAR.
- Better shapeless callee tests.
This commit is contained in:
Brendan Eich 2008-08-10 22:36:48 -07:00
parent a67dad8f4d
commit 64fc3b0367
6 changed files with 107 additions and 92 deletions

View File

@ -5868,9 +5868,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
default:
/*
* Push null as a placeholder for the global object, per ECMA-262
* 11.2.3 step 6.
* 11.2.3 step 6. We use JSOP_NULLTHIS to distinguish this opcode
* from JSOP_NULL (see jstracer.cpp for one use-case).
*/
if (!js_EmitTree(cx, cg, pn2) || js_Emit1(cx, cg, JSOP_NULL) < 0)
if (!js_EmitTree(cx, cg, pn2))
return JS_FALSE;
if (js_Emit1(cx, cg, JSOP_NULLTHIS) < 0)
return JS_FALSE;
}

View File

@ -2413,9 +2413,10 @@ JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
/*
* Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
* remain distinct for the decompiler.
* remain distinct for the decompiler. Ditto for JSOP_NULL{,THIS}.
*/
JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
JS_STATIC_ASSERT(JSOP_NULL_LENGTH == JSOP_NULLTHIS_LENGTH);
/* Ensure we can share deffun and closure code. */
JS_STATIC_ASSERT(JSOP_DEFFUN_LENGTH == JSOP_CLOSURE_LENGTH);
@ -5226,6 +5227,7 @@ js_Interpret(JSContext *cx)
END_CASE(JSOP_ONE)
BEGIN_CASE(JSOP_NULL)
BEGIN_CASE(JSOP_NULLTHIS)
PUSH_OPND(JSVAL_NULL);
END_CASE(JSOP_NULL)

View File

@ -187,7 +187,9 @@ OPDEF(JSOP_STRICTNE, 73, "strictne", NULL, 1, 2, 1, 10, JOF_BYTE|
/* Lexical closure constructor. */
OPDEF(JSOP_CLOSURE, 74, "closure", NULL, 3, 0, 0, 0, JOF_OBJECT)
OPDEF(JSOP_UNUSED75, 75, "unused75", NULL, 1, 0, 0, 0, JOF_BYTE)
/* Variant of JSOP_NULL for default (global) |this| parameter pushing. */
OPDEF(JSOP_NULLTHIS, 75, js_null_str, js_null_str, 1, 0, 1, 19, JOF_BYTE)
OPDEF(JSOP_UNUSED76, 76, "unused76", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED77, 77, "unused77", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED78, 78, "unused78", NULL, 1, 0, 0, 0, JOF_BYTE)

View File

@ -3119,68 +3119,16 @@ TraceRecorder::record_JSOP_CALLNAME()
return true;
}
JSBool
js_math_sin(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_math_cos(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_math_pow(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_math_sqrt(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_str_substring(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_str_fromCharCode(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_str_charCodeAt(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_str_charAt(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_str_concat(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_math_random(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_math_floor(JSContext* cx, uintN argc, jsval* vp);
bool
TraceRecorder::guardInterpretedFunction(JSFunction* fun, LIns* fun_ins)
{
if (FUN_INTERPRETED(fun)) {
guard(false,
lir->ins_eq0(lir->ins2(LIR_and,
lir->insLoadi(fun_ins, offsetof(JSFunction, flags) & ~3),
lir->insImm(
#ifdef IS_LITTLE_ENDIAN
JSFUN_INTERPRETED << 16
#else
JSFUN_INTERPRETED
#endif
))));
return true;
}
return false;
}
bool
TraceRecorder::guardShapelessCallee(jsval& callee)
{
if (JSVAL_IS_PRIMITIVE(callee))
return false;
if (!VALUE_IS_FUNCTION(cx, callee))
ABORT_TRACE("shapeless callee is not a function");
JSObject* callee_obj = JSVAL_TO_OBJECT(callee);
LIns* callee_ins = get(&callee);
return guardClass(callee_obj, callee_ins, &js_FunctionClass) &&
guardInterpretedFunction(GET_FUNCTION_PRIVATE(cx, callee_obj), callee_ins);
guard(true,
addName(lir->ins2(LIR_eq, get(&callee), lir->insImmPtr((void*) JSVAL_TO_OBJECT(callee))),
"guard(shapeless callee)"));
return true;
}
bool
@ -3216,6 +3164,39 @@ TraceRecorder::interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc)
return true;
}
JSBool
js_math_sin(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_math_cos(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_math_pow(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_math_sqrt(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_str_substring(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_str_fromCharCode(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_str_charCodeAt(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_str_charAt(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_str_concat(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_math_random(JSContext* cx, uintN argc, jsval* vp);
JSBool
js_math_floor(JSContext* cx, uintN argc, jsval* vp);
bool
TraceRecorder::record_JSOP_CALL()
{
@ -4761,10 +4742,10 @@ TraceRecorder::record_JSOP_CALLGVAR()
if (!lazilyImportGlobalSlot(slot))
ABORT_TRACE("lazy import of global slot failed");
jsval* vp = &STOBJ_GET_SLOT(cx->fp->scopeChain, slot);
stack(0, get(vp));
jsval& v = STOBJ_GET_SLOT(cx->fp->scopeChain, slot);
stack(0, get(&v));
stack(1, lir->insImmPtr(NULL));
return record_JSOP_GETGVAR() && guardShapelessCallee(*vp);
return guardShapelessCallee(v);
}
bool
@ -4785,6 +4766,13 @@ TraceRecorder::record_JSOP_CALLARG()
return guardShapelessCallee(argval(slot));
}
bool
TraceRecorder::record_JSOP_NULLTHIS()
{
stack(0, lir->insImmPtr(NULL));
return guardShapelessCallee(stackval(-1));
}
bool
TraceRecorder::record_JSOP_INT8()
{
@ -4847,7 +4835,6 @@ TraceRecorder::record_JSOP_HOLE()
#define UNUSED(op) bool TraceRecorder::record_##op() { return false; }
UNUSED(JSOP_UNUSED75)
UNUSED(JSOP_UNUSED76)
UNUSED(JSOP_UNUSED77)
UNUSED(JSOP_UNUSED78)

View File

@ -286,7 +286,6 @@ class TraceRecorder {
bool guardDenseArrayIndex(JSObject* obj, jsint idx, nanojit::LIns* obj_ins,
nanojit::LIns* dslots_ins, nanojit::LIns* idx_ins);
void clearFrameSlotsFromCache();
bool guardInterpretedFunction(JSFunction* fun, nanojit::LIns* fun_ins);
bool guardShapelessCallee(jsval& callee);
bool interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc);
bool forInProlog(JSObject*& iterobj, nanojit::LIns*& iterobj_ins);

View File

@ -515,51 +515,73 @@ function newTest()
newTest.expected = "0123456789";
test(newTest);
function shapelessArgCalleeLoop(f, a)
{
for (var i = 0; i < 10; i++)
f(i, a);
}
function shapelessVarCalleeLoop(f, a)
{
var g = f;
for (var i = 0; i < 10; i++)
g(i, a);
}
function shapelessLetCalleeLoop(f, a)
// The following functions use a delay line of length 2 to change the value
// of the callee without exiting the traced loop. This is obviously tuned to
// match the current HOTLOOP setting of 2.
function shapelessArgCalleeLoop(f, g, h, a)
{
for (var i = 0; i < 10; i++) {
let g = f;
g(i, a);
f(i, a);
f = g;
g = h;
}
}
function shapelessUnknownCalleeLoop(f, g, a)
function shapelessVarCalleeLoop(f0, g, h, a)
{
var f = f0;
for (var i = 0; i < 10; i++) {
f(i, a);
f = g;
g = h;
}
}
function shapelessLetCalleeLoop(f0, g, h, a)
{
for (var i = 0; i < 10; i++) {
(f || g)(i, a);
f = null;
let f = f0;
f(i, a);
f = g;
g = h;
}
}
function shapelessUnknownCalleeLoop(n, f, g, h, a)
{
for (var i = 0; i < 10; i++) {
(n || f)(i, a);
f = g;
g = h;
}
}
function shapelessCalleeTest()
{
var a = [];
shapelessArgCalleeLoop(function (i, a) a[i] = i, a);
shapelessVarCalleeLoop(function (i, a) a[10 + i] = i, a);
shapelessLetCalleeLoop(function (i, a) a[20 + i] = i, a);
shapelessUnknownCalleeLoop(null, function (i, a) a[30 + i] = i, a);
var helper = function (i, a) a[i] = i;
shapelessArgCalleeLoop(helper, helper, function (i, a) a[i] = -i, a);
helper = function (i, a) a[10 + i] = i;
shapelessVarCalleeLoop(helper, helper, function (i, a) a[10 + i] = -i, a);
helper = function (i, a) a[20 + i] = i;
shapelessLetCalleeLoop(helper, helper, function (i, a) a[20 + i] = -i, a);
helper = function (i, a) a[30 + i] = i;
shapelessUnknownCalleeLoop(null, helper, helper, function (i, a) a[30 + i] = -i, a);
try {
shapelessUnknownCalleeLoop(null, {hack: 42}, a);
helper = {hack: 42};
shapelessUnknownCalleeLoop(null, helper, helper, helper, a);
} catch (e) {
if (e + "" != "TypeError: g is not a function")
print("shapelessUnknownCalleeLoop: unexpected exception " + e);
}
return a.join("");
}
shapelessCalleeTest.expected = "0123456789012345678901234567890123456789";
shapelessCalleeTest.expected = "01-2-3-4-5-6-7-8-901-2-3-4-5-6-7-8-9012345678901-2-3-4-5-6-7-8-9";
test(shapelessCalleeTest);
function typeofTest()