[Bug 433382] More efficient interpreter switch when computed goto is not available. r=brendan

This commit is contained in:
Igor Bukanov 2008-06-20 11:55:49 +02:00
parent 7a2a078f0f
commit 79c9e30964
14 changed files with 402 additions and 247 deletions

View File

@ -242,6 +242,10 @@ JSJAVA_CFLAGS = \
HOST_SIMPLE_PROGRAMS += host_jskwgen$(HOST_BIN_SUFFIX)
GARBAGE += jsautokw.h host_jskwgen$(HOST_BIN_SUFFIX)
HOST_SIMPLE_PROGRAMS += host_jsoplengen$(HOST_BIN_SUFFIX)
GARBAGE += jsautooplen.h host_jsoplengen$(HOST_BIN_SUFFIX)
USE_HOST_CXX = 1
ifdef HAVE_DTRACE
@ -445,18 +449,23 @@ jscpucfg$(HOST_BIN_SUFFIX): jscpucfg.cpp Makefile.in
endif
endif
# Extra dependancies and rules for keyword switch code
jsscan.$(OBJ_SUFFIX): jsautokw.h jskeyword.tbl
# Extra dependancies and rules for auto-generated headers
host_jskwgen.$(OBJ_SUFFIX): jsconfig.h jskeyword.tbl
jsautokw.h: host_jskwgen$(HOST_BIN_SUFFIX)
./host_jskwgen$(HOST_BIN_SUFFIX) $@
host_jsoplengen.$(OBJ_SUFFIX): jsopcode.tbl
jsautooplen.h: host_jsoplengen$(HOST_BIN_SUFFIX)
./host_jsoplengen$(HOST_BIN_SUFFIX) $@
# Force auto-header generation before compiling any source that may use them
$(CPPSRCS:%.cpp=%.$(OBJ_SUFFIX)): jsautokw.h jsautooplen.h
ifdef HAVE_DTRACE
javascript-trace.h: $(srcdir)/javascript-trace.d
dtrace -h -s $(srcdir)/javascript-trace.d -o javascript-trace.h.in
sed 's/if _DTRACE_VERSION/ifdef INCLUDE_MOZILLA_DTRACE/' \
javascript-trace.h.in > javascript-trace.h
endif

View File

@ -277,39 +277,39 @@ nsinstall-target:
cd ../../config; $(MAKE) OBJDIR=$(OBJDIR) OBJDIR_NAME=$(OBJDIR)
#
# Rules for keyword switch generation
# Automatic header generation
#
GARBAGE += $(OBJDIR)/jsautokw.h $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX)
GARBAGE += $(OBJDIR)/jskwgen.$(OBJ_SUFFIX)
AUTO_HEADERS = $(OBJDIR)/jsautokw.h $(OBJDIR)/jsautooplen.h
$(OBJDIR)/jsscan.$(OBJ_SUFFIX): $(OBJDIR)/jsautokw.h jskeyword.tbl
$(OBJDIR)/jsautokw.h: jskeyword.tbl
$(OBJDIR)/jskwgen.$(OBJ_SUFFIX): jskwgen.cpp jskeyword.tbl
$(OBJDIR)/jsautooplen.h: jsopcode.tbl
$(OBJDIR)/jsautokw.h: $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX) jskeyword.tbl
$(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX) $@
GARBAGE += $(AUTO_HEADERS)
GARBAGE += $(AUTO_HEADERS:$(OBJDIR)/jsauto%.h=$(OBJDIR)/js%gen$(HOST_BIN_SUFFIX))
ifdef USE_MSVC
$(OBJDIR)/jskwgen.obj: jskwgen.cpp jskeyword.tbl
GARBAGE += $(AUTO_HEADERS:$(OBJDIR)/jsauto%.h=$(OBJDIR)/js%gen.obj)
$(AUTO_HEADERS): $(OBJDIR)/jsauto%.h: js%gen.cpp
@$(MAKE_OBJDIR)
$(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $<
$(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX): $(OBJDIR)/jskwgen.$(OBJ_SUFFIX)
link.exe -out:"$@" $(EXE_LINK_FLAGS) $^
link.exe -out:"$(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX)" $(EXE_LINK_FLAGS) $(OBJDIR)/js$*gen.obj
$(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX) $@
else
$(OBJDIR)/jskwgen.o: jskwgen.cpp jskeyword.tbl
$(AUTO_HEADERS): $(OBJDIR)/jsauto%.h: js%gen.cpp
@$(MAKE_OBJDIR)
$(CXX) -o $@ -c $(CFLAGS) $<
$(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX): $(OBJDIR)/jskwgen.$(OBJ_SUFFIX)
$(CXX) -o $@ $(CFLAGS) $(LDFLAGS) $^
$(CXX) -o $(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX) $(CFLAGS) $(LDFLAGS) $<
$(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX) $@
endif
# force creation of autoheaders before compiling any source that may use them
$(LIB_OBJS) : $(AUTO_HEADERS)
#
# JS shell executable
#

View File

@ -62,6 +62,8 @@
#include "jsscript.h"
#include "jsstr.h"
#include "jsautooplen.h"
typedef struct JSTrap {
JSCList links;
JSScript *script;

View File

@ -66,6 +66,8 @@
#include "jsscope.h"
#include "jsscript.h"
#include "jsautooplen.h"
/* Allocation chunk counts, must be powers of two in general. */
#define BYTECODE_CHUNK 256 /* code allocation increment */
#define SRCNOTE_CHUNK 64 /* initial srcnote allocation increment */

View File

@ -77,6 +77,8 @@
#include "jsxml.h"
#endif
#include "jsautooplen.h"
#ifdef js_invoke_c__
uint32
@ -2074,6 +2076,77 @@ js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2)
return JS_TRUE;
}
#ifdef DEBUG
void
js_TraceOpcode(JSContext *cx, jsint len)
{
FILE *tracefp;
JSStackFrame *fp;
JSFrameRegs *regs;
JSOp prevop;
intN ndefs, n, nuses;
jsval *siter;
JSString *str;
JSOp op;
tracefp = (FILE *) cx->tracefp;
JS_ASSERT(tracefp);
fp = cx->fp;
regs = fp->regs;
if (len != 0) {
prevop = (JSOp) regs->pc[-len];
ndefs = js_CodeSpec[prevop].ndefs;
if (ndefs != 0) {
if (prevop == JSOP_FORELEM && regs->sp[-1] == JSVAL_FALSE)
--ndefs;
for (n = -ndefs; n < 0; n++) {
char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
NULL);
if (bytes) {
fprintf(tracefp, "%s %s",
(n == -ndefs) ? " output:" : ",",
bytes);
JS_free(cx, bytes);
}
}
fprintf(tracefp, " @ %d\n", regs->sp - fp->spbase);
}
fprintf(tracefp, " stack: ");
for (siter = fp->spbase; siter < regs->sp; siter++) {
str = js_ValueToString(cx, *siter);
if (!str)
fputs("<null>", tracefp);
else
js_FileEscapedString(tracefp, str, 0);
fputc(' ', tracefp);
}
fputc('\n', tracefp);
}
fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, fp->script, regs->pc));
js_Disassemble1(cx, fp->script, regs->pc,
PTRDIFF(regs->pc, fp->script->code, jsbytecode),
JS_FALSE, tracefp);
op = (JSOp) *regs->pc;
nuses = js_CodeSpec[op].nuses;
if (nuses != 0) {
for (n = -nuses; n < 0; n++) {
char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
NULL);
if (bytes) {
fprintf(tracefp, "%s %s",
(n == -nuses) ? " inputs:" : ",",
bytes);
JS_free(cx, bytes);
}
}
fprintf(tracefp, " @ %d\n", regs->sp - fp->spbase);
}
}
#endif /* DEBUG */
#ifdef JS_OPMETER
# include <stdlib.h>
@ -2425,6 +2498,13 @@ JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MAX)));
# endif
#endif
/*
* Interpreter assumes the following to implement condition-free interrupt
* implementation when !JS_THREADED_INTERP.
*/
JS_STATIC_ASSERT(JSOP_INTERRUPT == 0);
/*
* Ensure that the intrepreter switch can close call-bytecode cases in the
* same way as non-call bytecodes.
@ -2462,7 +2542,6 @@ js_Interpret(JSContext *cx)
JSFrameRegs regs;
JSObject *obj, *obj2, *parent;
JSBool ok, cond;
JSTrapHandler interruptHandler;
jsint len;
jsbytecode *endpc, *pc2;
JSOp op, op2;
@ -2481,8 +2560,11 @@ js_Interpret(JSContext *cx)
JSClass *clasp;
JSFunction *fun;
JSType type;
#if !JS_THREADED_INTERP && defined DEBUG
FILE *tracefp = NULL;
#if JS_THREADED_INTERP
register const void * const *jumpTable;
#else
register uint32 switchMask;
uintN switchOp;
#endif
#if JS_HAS_EXPORT_IMPORT
JSIdArray *ida;
@ -2502,42 +2584,68 @@ js_Interpret(JSContext *cx)
#endif
#if JS_THREADED_INTERP
static void *normalJumpTable[] = {
static const void *const normalJumpTable[] = {
# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
JS_EXTENSION &&L_##op,
# include "jsopcode.tbl"
# undef OPDEF
};
static void *interruptJumpTable[] = {
static const void *const interruptJumpTable[] = {
# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
JS_EXTENSION &&interrupt,
JS_EXTENSION &&L_JSOP_INTERRUPT,
# include "jsopcode.tbl"
# undef OPDEF
};
register void **jumpTable = normalJumpTable;
METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */
# define DO_OP() JS_EXTENSION_(goto *jumpTable[op])
# define DO_NEXT_OP(n) do { METER_OP_PAIR(op, regs.pc[n]); \
op = (JSOp) *(regs.pc += (n)); \
DO_OP(); } while (0)
# define DO_NEXT_OP(n) JS_BEGIN_MACRO \
METER_OP_PAIR(op, regs.pc[n]); \
op = (JSOp) *(regs.pc += (n)); \
DO_OP(); \
JS_END_MACRO
# define BEGIN_CASE(OP) L_##OP:
# define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
# define END_VARLEN_CASE DO_NEXT_OP(len);
# define EMPTY_CASE(OP) BEGIN_CASE(OP) op = (JSOp) *++regs.pc; DO_OP();
#else
# define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \
JS_ASSERT(js_CodeSpec[OP].length == 1); \
op = (JSOp) *++regs.pc; \
DO_OP();
# define END_EMPTY_CASES
#else /* !JS_THREADED_INTERP */
# define DO_OP() goto do_op
# define DO_NEXT_OP(n) goto advance_pc
# define DO_NEXT_OP(n) JS_BEGIN_MACRO \
JS_ASSERT((n) == len); \
goto advance_pc; \
JS_END_MACRO
# define BEGIN_CASE(OP) case OP:
# define END_CASE(OP) break;
# define END_VARLEN_CASE break;
# define EMPTY_CASE(OP) BEGIN_CASE(OP) END_CASE(OP)
# define END_CASE(OP) END_CASE_LEN(OP##_LENGTH)
# define END_CASE_LEN(n) END_CASE_LENX(n)
# define END_CASE_LENX(n) END_CASE_LEN##n
/*
* To share the code for all len == 1 cases we use the specialized label with
* code that falls through to advance_pc: .
*/
# define END_CASE_LEN1 goto advance_pc_by_one;
# define END_CASE_LEN2 len = 2; goto advance_pc;
# define END_CASE_LEN3 len = 3; goto advance_pc;
# define END_CASE_LEN4 len = 4; goto advance_pc;
# define END_CASE_LEN5 len = 5; goto advance_pc;
# define END_VARLEN_CASE goto advance_pc;
# define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
# define END_EMPTY_CASES goto advance_pc_by_one;
#endif
/* Check for too deep a C stack. */
/* Check for too deep of a native thread stack. */
JS_CHECK_RECURSION(cx, return JS_FALSE);
rt = cx->runtime;
@ -2616,18 +2724,15 @@ js_Interpret(JSContext *cx)
* the compiler can keep it in a register when it is non-null.
*/
#if JS_THREADED_INTERP
# define LOAD_JUMP_TABLE() \
(jumpTable = interruptHandler ? interruptJumpTable : normalJumpTable)
# define LOAD_INTERRUPT_HANDLER(cx) \
((void) (jumpTable = (cx)->debugHooks->interruptHandler \
? interruptJumpTable \
: normalJumpTable))
#else
# define LOAD_JUMP_TABLE() ((void) 0)
# define LOAD_INTERRUPT_HANDLER(cx) \
((void) (switchMask = (cx)->debugHooks->interruptHandler ? 0 : 255))
#endif
#define LOAD_INTERRUPT_HANDLER(cx) \
JS_BEGIN_MACRO \
interruptHandler = (cx)->debugHooks->interruptHandler; \
LOAD_JUMP_TABLE(); \
JS_END_MACRO
LOAD_INTERRUPT_HANDLER(cx);
/*
@ -2675,107 +2780,89 @@ js_Interpret(JSContext *cx)
}
}
#if JS_THREADED_INTERP
/*
* This is a loop, but it does not look like a loop. The loop-closing
* jump is distributed throughout interruptJumpTable, and comes back to
* the interrupt label. The dispatch on op is through normalJumpTable.
* The trick is LOAD_INTERRUPT_HANDLER setting jumpTable appropriately.
*
* It is important that "op" be initialized before the interrupt label
* because it is possible for "op" to be specially assigned during the
* normally processing of an opcode while looping (in particular, this
* happens in JSOP_TRAP while debugging). We rely on DO_NEXT_OP to
* correctly manage "op" in all other cases.
* It is important that "op" be initialized before calling DO_OP because
* it is possible for "op" to be specially assigned during the normal
* processing of an opcode while looping. We rely on DO_NEXT_OP to manage
* "op" correctly in all other cases.
*/
op = (JSOp) *regs.pc;
if (interruptHandler) {
interrupt:
switch (interruptHandler(cx, script, regs.pc, &rval,
cx->debugHooks->interruptHandlerData)) {
case JSTRAP_ERROR:
goto error;
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
fp->rval = rval;
ok = JS_TRUE;
goto forced_return;
case JSTRAP_THROW:
cx->throwing = JS_TRUE;
cx->exception = rval;
goto error;
default:;
}
LOAD_INTERRUPT_HANDLER(cx);
}
JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
JS_EXTENSION_(goto *normalJumpTable[op]);
#else /* !JS_THREADED_INTERP */
len = 0;
DO_NEXT_OP(len);
#if JS_THREADED_INTERP
/*
* This is a loop, but it does not look like a loop. The loop-closing
* jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
* When interrupts are enabled, jumpTable is set to interruptJumpTable
* where all jumps point to the JSOP_INTERRUPT case. The latter, after
* calling the interrupt handler, dispatches through normalJumpTable to
* continue the normal bytecode processing.
*/
#else
for (;;) {
advance_pc_by_one:
JS_ASSERT(js_CodeSpec[op].length == 1);
len = 1;
advance_pc:
regs.pc += len;
op = (JSOp) *regs.pc;
do_op:
len = js_CodeSpec[op].length;
#ifdef DEBUG
tracefp = (FILE *) cx->tracefp;
if (tracefp) {
intN nuses, n;
fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, regs.pc));
js_Disassemble1(cx, script, regs.pc,
PTRDIFF(regs.pc, script->code, jsbytecode),
JS_FALSE, tracefp);
nuses = js_CodeSpec[op].nuses;
if (nuses) {
for (n = -nuses; n < 0; n++) {
char *bytes = js_DecompileValueGenerator(cx, n, regs.sp[n],
NULL);
if (bytes) {
fprintf(tracefp, "%s %s",
(n == -nuses) ? " inputs:" : ",",
bytes);
JS_free(cx, bytes);
}
}
fprintf(tracefp, " @ %d\n", regs.sp - fp->spbase);
}
}
#endif /* DEBUG */
if (interruptHandler) {
switch (interruptHandler(cx, script, regs.pc, &rval,
cx->debugHooks->interruptHandlerData)) {
case JSTRAP_ERROR:
goto error;
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
fp->rval = rval;
ok = JS_TRUE;
goto forced_return;
case JSTRAP_THROW:
cx->throwing = JS_TRUE;
cx->exception = rval;
goto error;
default:;
}
LOAD_INTERRUPT_HANDLER(cx);
}
switch (op) {
if (cx->tracefp)
js_TraceOpcode(cx, len);
#endif
do_op:
switchOp = op & switchMask;
do_switch:
switch (switchOp) {
#endif /* !JS_THREADED_INTERP */
EMPTY_CASE(JSOP_NOP)
BEGIN_CASE(JSOP_INTERRUPT)
{
JSTrapHandler handler;
EMPTY_CASE(JSOP_GROUP)
handler = cx->debugHooks->interruptHandler;
if (handler) {
switch (handler(cx, script, regs.pc, &rval,
cx->debugHooks->interruptHandlerData)) {
case JSTRAP_ERROR:
goto error;
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
fp->rval = rval;
ok = JS_TRUE;
goto forced_return;
case JSTRAP_THROW:
cx->throwing = JS_TRUE;
cx->exception = rval;
goto error;
default:;
}
}
LOAD_INTERRUPT_HANDLER(cx);
/* EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
#if JS_THREADED_INTERP
JS_EXTENSION_(goto *normalJumpTable[op]);
#else
switchOp = op;
goto do_switch;
#endif
}
/* No-ops for ease of decompilation. */
ADD_EMPTY_CASE(JSOP_NOP)
ADD_EMPTY_CASE(JSOP_GROUP)
ADD_EMPTY_CASE(JSOP_CONDSWITCH)
ADD_EMPTY_CASE(JSOP_TRY)
ADD_EMPTY_CASE(JSOP_FINALLY)
#if JS_HAS_XML_SUPPORT
ADD_EMPTY_CASE(JSOP_STARTXML)
ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
#endif
END_EMPTY_CASES
/* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
BEGIN_CASE(JSOP_LINENO)
END_CASE(JSOP_LINENO)
@ -2810,12 +2897,6 @@ interrupt:
#endif
END_CASE(JSOP_POPN)
BEGIN_CASE(JSOP_SWAP)
rtmp = regs.sp[-1];
regs.sp[-1] = regs.sp[-2];
regs.sp[-2] = rtmp;
END_CASE(JSOP_SWAP)
BEGIN_CASE(JSOP_SETRVAL)
BEGIN_CASE(JSOP_POPV)
ASSERT_NOT_THROWING(cx);
@ -4139,7 +4220,6 @@ interrupt:
i = 0;
COMPUTE_THIS(cx, fp, obj);
PUSH(JSVAL_NULL);
len = JSOP_GETTHISPROP_LENGTH;
goto do_getprop_with_obj;
#undef COMPUTE_THIS
@ -4149,7 +4229,6 @@ interrupt:
slot = GET_ARGNO(regs.pc);
JS_ASSERT(slot < fp->fun->nargs);
PUSH_OPND(fp->argv[slot]);
len = JSOP_GETARGPROP_LENGTH;
goto do_getprop_body;
BEGIN_CASE(JSOP_GETVARPROP)
@ -4157,7 +4236,6 @@ interrupt:
slot = GET_VARNO(regs.pc);
JS_ASSERT(slot < fp->fun->u.i.nvars);
PUSH_OPND(fp->vars[slot]);
len = JSOP_GETVARPROP_LENGTH;
goto do_getprop_body;
BEGIN_CASE(JSOP_GETLOCALPROP)
@ -4165,13 +4243,11 @@ interrupt:
slot = GET_UINT16(regs.pc);
JS_ASSERT(slot < script->depth);
PUSH_OPND(fp->spbase[slot]);
len = JSOP_GETLOCALPROP_LENGTH;
goto do_getprop_body;
BEGIN_CASE(JSOP_GETPROP)
BEGIN_CASE(JSOP_GETXPROP)
i = 0;
len = JSOP_GETPROP_LENGTH;
do_getprop_body:
lval = FETCH_OPND(-1);
@ -4217,6 +4293,8 @@ interrupt:
} while (0);
STORE_OPND(-1, rval);
JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length);
len = JSOP_GETPROP_LENGTH + i;
END_VARLEN_CASE
BEGIN_CASE(JSOP_LENGTH)
@ -4241,8 +4319,7 @@ interrupt:
goto error;
}
} else {
i = -1;
len = JSOP_LENGTH_LENGTH;
i = -2;
goto do_getprop_with_lval;
}
END_CASE(JSOP_LENGTH)
@ -4996,12 +5073,12 @@ interrupt:
goto error;
if (!prop) {
/* Kludge to allow (typeof foo == "undefined") tests. */
len = JSOP_NAME_LENGTH;
endpc = script->code + script->length;
for (pc2 = regs.pc + len; pc2 < endpc; pc2++) {
for (pc2 = regs.pc + JSOP_NAME_LENGTH; pc2 < endpc; pc2++) {
op2 = (JSOp)*pc2;
if (op2 == JSOP_TYPEOF) {
PUSH_OPND(JSVAL_VOID);
len = JSOP_NAME_LENGTH;
DO_NEXT_OP(len);
}
if (op2 != JSOP_GROUP)
@ -5338,8 +5415,6 @@ interrupt:
: GET_JUMPX_OFFSET(pc2);
END_VARLEN_CASE
EMPTY_CASE(JSOP_CONDSWITCH)
#if JS_HAS_EXPORT_IMPORT
BEGIN_CASE(JSOP_EXPORTALL)
obj = fp->varobj;
@ -5414,15 +5489,13 @@ interrupt:
#endif /* JS_HAS_EXPORT_IMPORT */
BEGIN_CASE(JSOP_TRAP)
switch (JS_HandleTrap(cx, script, regs.pc, &rval)) {
{
JSTrapStatus status;
status = JS_HandleTrap(cx, script, regs.pc, &rval);
switch (status) {
case JSTRAP_ERROR:
goto error;
case JSTRAP_CONTINUE:
JS_ASSERT(JSVAL_IS_INT(rval));
op = (JSOp) JSVAL_TO_INT(rval);
JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
LOAD_INTERRUPT_HANDLER(cx);
DO_OP();
case JSTRAP_RETURN:
fp->rval = rval;
ok = JS_TRUE;
@ -5432,9 +5505,15 @@ interrupt:
cx->exception = rval;
goto error;
default:;
break;
}
JS_ASSERT(status == JSTRAP_CONTINUE);
LOAD_INTERRUPT_HANDLER(cx);
END_CASE(JSOP_TRAP)
JS_ASSERT(JSVAL_IS_INT(rval));
op = (JSOp) JSVAL_TO_INT(rval);
JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
DO_OP();
}
BEGIN_CASE(JSOP_ARGUMENTS)
if (!js_GetArgsValue(cx, fp, &rval))
@ -6172,7 +6251,7 @@ interrupt:
goto error;
}
regs.sp -= 2;
END_CASE(JSOP_INITELEM);
END_CASE(JSOP_INITELEM)
#if JS_HAS_SHARP_VARS
BEGIN_CASE(JSOP_DEFSHARP)
@ -6219,15 +6298,11 @@ interrupt:
END_CASE(JSOP_USESHARP)
#endif /* JS_HAS_SHARP_VARS */
/* No-ops for ease of decompilation and jit'ing. */
EMPTY_CASE(JSOP_TRY)
EMPTY_CASE(JSOP_FINALLY)
BEGIN_CASE(JSOP_GOSUB)
PUSH(JSVAL_FALSE);
i = PTRDIFF(regs.pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH;
len = GET_JUMP_OFFSET(regs.pc);
PUSH(INT_TO_JSVAL(i));
len = GET_JUMP_OFFSET(regs.pc);
END_VARLEN_CASE
BEGIN_CASE(JSOP_GOSUBX)
@ -6482,9 +6557,6 @@ interrupt:
regs.sp--;
END_CASE(JSOP_ENDFILTER);
EMPTY_CASE(JSOP_STARTXML)
EMPTY_CASE(JSOP_STARTXMLEXPR)
BEGIN_CASE(JSOP_TOXML)
rval = FETCH_OPND(-1);
obj = js_ValueToXMLObject(cx, rval);
@ -6784,48 +6856,6 @@ interrupt:
#if !JS_THREADED_INTERP
} /* switch (op) */
advance_pc:
regs.pc += len;
#ifdef DEBUG
if (tracefp) {
intN ndefs, n;
jsval *siter;
/*
* op may be invalid here when a catch or finally handler jumps to
* advance_pc.
*/
op = (JSOp) regs.pc[-len];
ndefs = js_CodeSpec[op].ndefs;
if (ndefs) {
if (op == JSOP_FORELEM && regs.sp[-1] == JSVAL_FALSE)
--ndefs;
for (n = -ndefs; n < 0; n++) {
char *bytes = js_DecompileValueGenerator(cx, n, regs.sp[n],
NULL);
if (bytes) {
fprintf(tracefp, "%s %s",
(n == -ndefs) ? " output:" : ",",
bytes);
JS_free(cx, bytes);
}
}
fprintf(tracefp, " @ %d\n", regs.sp - fp->spbase);
}
fprintf(tracefp, " stack: ");
for (siter = fp->spbase; siter < regs.sp; siter++) {
str = js_ValueToString(cx, *siter);
if (!str)
fputs("<null>", tracefp);
else
js_FileEscapedString(tracefp, str, 0);
fputc(' ', tracefp);
}
fputc('\n', tracefp);
}
#endif /* DEBUG */
}
#endif /* !JS_THREADED_INTERP */

View File

@ -507,6 +507,13 @@ js_OnUnknownMethod(JSContext *cx, jsval *vp);
extern JSBool
js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2);
/*
* Opcode tracing helper. When len is not 0, cx->fp->regs->pc[-len] gives the
* previous opcode.
*/
extern void
js_TraceOpcode(JSContext *cx, jsint len);
/*
* JS_OPMETER helper functions.
*/

View File

@ -88,6 +88,8 @@
#include "jsdtracef.h"
#endif
#include "jsautooplen.h"
#ifdef JS_THREADSAFE
#define NATIVE_DROP_PROPERTY js_DropProperty

View File

@ -75,6 +75,14 @@
# include "jsnum.h"
#endif
#include "jsautooplen.h"
/* Verify JSOP_XXX_LENGTH constant definitions. */
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
JS_STATIC_ASSERT(op##_LENGTH == length);
#include "jsopcode.tbl"
#undef OPDEF
static const char js_incop_strs[][3] = {"++", "--"};
const JSCodeSpec js_CodeSpec[] = {
@ -2152,14 +2160,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
todo = -2;
break;
case JSOP_SWAP:
/*
* We don't generate this opcode currently, and previously we
* did not need to decompile it. If old, serialized bytecode
* uses it still, we should fall through and set todo = -2.
*/
/* FALL THROUGH */
case JSOP_GOSUB:
case JSOP_GOSUBX:
/*
@ -4993,8 +4993,7 @@ DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
/* None of these stack-writing ops generates novel values. */
JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX &&
op != JSOP_DUP && op != JSOP_DUP2 &&
op != JSOP_SWAP);
op != JSOP_DUP && op != JSOP_DUP2);
/*
* |this| could convert to a very long object initialiser, so cite it by
@ -5274,13 +5273,6 @@ ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
pcstack[pcdepth + 3] = pcstack[pcdepth + 1];
break;
case JSOP_SWAP:
JS_ASSERT(ndefs == 2);
pc2 = pcstack[pcdepth];
pcstack[pcdepth] = pcstack[pcdepth + 1];
pcstack[pcdepth + 1] = pc2;
break;
case JSOP_LEAVEBLOCKEXPR:
/*
* The decompiler wants to see [leaveblockexpr] on pcstack, not

View File

@ -60,14 +60,6 @@ typedef enum JSOp {
JSOP_LIMIT
} JSOp;
typedef enum JSOpLength {
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
op##_LENGTH = length,
#include "jsopcode.tbl"
#undef OPDEF
JSOP_LIMIT_LENGTH
} JSOpLength;
/*
* JS bytecode formats.
*/

View File

@ -97,7 +97,7 @@
/* legend: op val name image len use def prec format */
/* Longstanding JavaScript bytecodes. */
OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_INTERRUPT, 0, "interrupt", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE)
OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE)
OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE|JOF_PARENHEAD)
@ -331,9 +331,9 @@ OPDEF(JSOP_TRY, 133,"try", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 0, 0, JOF_BYTE)
/*
* Swap the top two stack elements.
* Generic nop for the decompiler.
*/
OPDEF(JSOP_SWAP, 135,"swap", NULL, 1, 2, 2, 0, JOF_BYTE)
OPDEF(JSOP_NOP, 135,"nop", NULL, 1, 0, 0, 0, JOF_BYTE)
/*
* Bytecodes that avoid making an arguments object in most cases:

121
js/src/jsoplengen.cpp Normal file
View File

@ -0,0 +1,121 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set sw=4 ts=8 et tw=80:
*
* ***** 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 String Switch Generator for JavaScript Keywords,
* released 2005-12-09.
*
* The Initial Developer of the Original Code is
* Igor Bukanov.
* Portions created by the Initial Developer are Copyright (C) 2005-2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const struct {
const char *name;
int length;
} pairs[] = {
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
{ #op, length } ,
#include "jsopcode.tbl"
#undef OPDEF
};
int
main(int argc, char **argv)
{
FILE *fp;
size_t maxNameWidth, i, nameWidth, tabStop;
int lengthGap;
static const char prefix[] = "#define ";
static const char suffix[] = "_LENGTH";
static const size_t tabWidth = 8;
static const size_t prefixWidth = sizeof(prefix) - 1;
static const size_t suffixWidth = sizeof(suffix) - 1;
if (argc != 2) {
fputs("Bad usage\n", stderr);
return EXIT_FAILURE;
}
fp = fopen(argv[1], "w");
if (!fp) {
perror("fopen");
return EXIT_FAILURE;
}
fputs("/*\n"
" * Automatically generated header with JS opcode length constants.\n"
" *\n"
" * Do not edit it, alter jsopcode.tbl instead.\n"
" */\n",
fp);
/*
* Print
*
* #define name_LENGTH length
*
* with all length values aligned on the same column. The column is at the
* second character position after a tab-stop with the first position
* reserved for the minus sign of variable-length opcodes.
*/
maxNameWidth = 0;
for (i = 0; i != sizeof pairs / sizeof pairs[0]; ++i) {
nameWidth = strlen(pairs[i].name);
if (maxNameWidth < nameWidth)
maxNameWidth = nameWidth;
}
tabStop = prefixWidth + maxNameWidth + suffixWidth + 1;
tabStop = (tabStop + tabWidth - 1) / tabWidth * tabWidth;
for (i = 0; i != sizeof pairs / sizeof pairs[0]; ++i) {
lengthGap = (int) (tabStop - prefixWidth - strlen(pairs[i].name) -
suffixWidth);
fprintf(fp, "%s%s%s%*c%2d\n",
prefix, pairs[i].name, suffix, lengthGap, ' ',
pairs[i].length);
if (ferror(fp)) {
perror("fclose");
exit(EXIT_FAILURE);
}
}
if (fclose(fp)) {
perror("fclose");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -202,7 +202,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 25)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 26)
/*
* Library-private functions.

View File

@ -192,8 +192,8 @@ AutoPushJSContext::AutoPushJSContext(nsISupports* aSecuritySupports,
mFrame.callee = JS_GetFunctionObject(fun);
mFrame.scopeChain = JS_GetParent(cx, mFrame.callee);
mFrame.down = cx->fp;
mRegs.pc = script->code + script->length
- JSOP_STOP_LENGTH;
mRegs.pc = script->code + script->length - 1;
JS_ASSERT(static_cast<JSOp>(*mRegs.pc) == JSOP_STOP);
mRegs.sp = NULL;
mFrame.regs = &mRegs;
cx->fp = &mFrame;

View File

@ -185,11 +185,9 @@ endif
clean:
rm -rf $(OBJS) $(GARBAGE)
@cd fdlibm; $(MAKE) -f Makefile.ref clean
clobber:
rm -rf $(OBJS) $(TARGETS) $(DEPENDENCIES)
@cd fdlibm; $(MAKE) -f Makefile.ref clobber
tar:
tar cvf $(TARNAME) $(TARFILES)