From c6b43c74943fdb8de20396ebcb68d63d1770825d Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Fri, 20 Jun 2008 11:55:49 +0200 Subject: [PATCH] [Bug 433382] More efficient interpreter switch when computed goto is not available. r=brendan --- js/src/Makefile.in | 17 +- js/src/Makefile.ref | 34 +-- js/src/jsdbgapi.cpp | 2 + js/src/jsemit.cpp | 2 + js/src/jsinterp.cpp | 416 ++++++++++++++------------ js/src/jsinterp.h | 7 + js/src/jsobj.cpp | 2 + js/src/jsopcode.cpp | 26 +- js/src/jsopcode.h | 8 - js/src/jsopcode.tbl | 6 +- js/src/jsoplengen.cpp | 121 ++++++++ js/src/jsxdrapi.h | 2 +- js/src/liveconnect/nsCLiveconnect.cpp | 4 +- js/src/rules.mk | 2 - 14 files changed, 402 insertions(+), 247 deletions(-) create mode 100644 js/src/jsoplengen.cpp diff --git a/js/src/Makefile.in b/js/src/Makefile.in index d0d0e922fe7..8a4d7580918 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -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 - diff --git a/js/src/Makefile.ref b/js/src/Makefile.ref index a6ada0ca6cf..520e801d71c 100644 --- a/js/src/Makefile.ref +++ b/js/src/Makefile.ref @@ -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 # diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 311d7cc06ed..7309ebbabf5 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -62,6 +62,8 @@ #include "jsscript.h" #include "jsstr.h" +#include "jsautooplen.h" + typedef struct JSTrap { JSCList links; JSScript *script; diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index daff25c9e53..638ade9eef5 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -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 */ diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 3c58cabee4b..9bc14c21e7a 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -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("", 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 @@ -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("", tracefp); - else - js_FileEscapedString(tracefp, str, 0); - fputc(' ', tracefp); - } - fputc('\n', tracefp); - } -#endif /* DEBUG */ } #endif /* !JS_THREADED_INTERP */ diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 7fb54660a74..3f8bdfe9bba 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -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. */ diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index a9540166152..e8ac435e8da 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -88,6 +88,8 @@ #include "jsdtracef.h" #endif +#include "jsautooplen.h" + #ifdef JS_THREADSAFE #define NATIVE_DROP_PROPERTY js_DropProperty diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index cf0accc147d..91e0f9e0fa7 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -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 diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 9e185e4c3b0..831b95fa940 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -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. */ diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 7806e08e018..f9ea46a03a5 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -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: diff --git a/js/src/jsoplengen.cpp b/js/src/jsoplengen.cpp new file mode 100644 index 00000000000..ecebc06ed80 --- /dev/null +++ b/js/src/jsoplengen.cpp @@ -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 +#include +#include + +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; +} diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index 3026b49ee21..68a45432af9 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -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. diff --git a/js/src/liveconnect/nsCLiveconnect.cpp b/js/src/liveconnect/nsCLiveconnect.cpp index 4adfc740142..3c0c344fcfc 100644 --- a/js/src/liveconnect/nsCLiveconnect.cpp +++ b/js/src/liveconnect/nsCLiveconnect.cpp @@ -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(*mRegs.pc) == JSOP_STOP); mRegs.sp = NULL; mFrame.regs = &mRegs; cx->fp = &mFrame; diff --git a/js/src/rules.mk b/js/src/rules.mk index 4ab596f23a6..ad80ad0f449 100644 --- a/js/src/rules.mk +++ b/js/src/rules.mk @@ -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)