Browse Source

Rework pre/post inc/dec in compiler and executor

Pre/post inc/dec are an important fast path for fastints.  Reimplement these
operations as atomic opcodes so that fast pathing is easier.  Opcodes emitted
for typical loop constructs are also reduced by the change.  Several opcodes
were moved to extraops to make space for the 12 pre/post inc/dec reg/var/prop
variants, and the compiler was changed to support two-argument (dest, dest,
src) extraop arithmetic for its ispec/ivalue.

Example of bytecode change, in Duktape 1.1.0:

    function foo() { var x = 10; print(x++, x++, x--, x--, x--); print(x); }

    BC 0000: LDINT 0, 10, 256    ; 0x80028003 op=3 (LDINT) a=0 b=10 c=256
    BC 0001: CSVAR 1, 256, 0     ; 0x00400052 op=18 (CSVAR) a=1 b=256 c=0
    BC 0002: LDREG 3, 0, 0       ; 0x000000c0 op=0 (LDREG) a=3 b=0 c=0
    BC 0003: TONUM 3, 3          ; 0x0180c2fe op=62 (EXTRA) a=11 b=3 c=3
    BC 0004: INC 0, 3            ; 0x0180067e op=62 (EXTRA) a=25 b=0 c=3
    BC 0005: LDREG 4, 0, 0       ; 0x00000100 op=0 (LDREG) a=4 b=0 c=0
    BC 0006: TONUM 4, 4          ; 0x020102fe op=62 (EXTRA) a=11 b=4 c=4
    BC 0007: INC 0, 4            ; 0x0200067e op=62 (EXTRA) a=25 b=0 c=4
    BC 0008: LDREG 5, 0, 0       ; 0x00000140 op=0 (LDREG) a=5 b=0 c=0
    BC 0009: TONUM 5, 5          ; 0x028142fe op=62 (EXTRA) a=11 b=5 c=5
    BC 0010: DEC 0, 5            ; 0x028006be op=62 (EXTRA) a=26 b=0 c=5
    BC 0011: LDREG 6, 0, 0       ; 0x00000180 op=0 (LDREG) a=6 b=0 c=0
    BC 0012: TONUM 6, 6          ; 0x030182fe op=62 (EXTRA) a=11 b=6 c=6
    BC 0013: DEC 0, 6            ; 0x030006be op=62 (EXTRA) a=26 b=0 c=6
    BC 0014: LDREG 7, 0, 0       ; 0x000001c0 op=0 (LDREG) a=7 b=0 c=0
    BC 0015: TONUM 7, 7          ; 0x0381c2fe op=62 (EXTRA) a=11 b=7 c=7
    BC 0016: DEC 0, 7            ; 0x038006be op=62 (EXTRA) a=26 b=0 c=7
    BC 0017: CALL 0, 1, 5        ; 0x02804034 op=52 (CALL) a=0 b=1 c=5
    BC 0018: CSVAR 1, 256, 0     ; 0x00400052 op=18 (CSVAR) a=1 b=256 c=0
    BC 0019: LDREG 3, 0, 0       ; 0x000000c0 op=0 (LDREG) a=3 b=0 c=0
    BC 0020: CALL 0, 1, 1        ; 0x00804034 op=52 (CALL) a=0 b=1 c=1
    BC 0021: RETURN 1, 0, 0      ; 0x00000073 op=51 (RETURN) a=1 b=0 c=0

After this commit:

    function foo() { var x = 10; print(x++, x++, x--, x--, x--); print(x); }

    BC 0000: LDINT 0, 10, 256    ; 0x80028003 op=3 (LDINT) a=0 b=10 c=256
    BC 0001: CSVAR 1, 256, 0     ; 0x00400052 op=18 (CSVAR) a=1 b=256 c=0
    BC 0002: POSTINC 3, 0, 0     ; 0x000000f9 op=57 (POSTINC) a=3 b=0 c=0
    BC 0003: POSTINC 4, 0, 0     ; 0x00000139 op=57 (POSTINC) a=4 b=0 c=0
    BC 0004: POSTDEC 5, 0, 0     ; 0x0000017c op=60 (POSTDEC) a=5 b=0 c=0
    BC 0005: POSTDEC 6, 0, 0     ; 0x000001bc op=60 (POSTDEC) a=6 b=0 c=0
    BC 0006: POSTDEC 7, 0, 0     ; 0x000001fc op=60 (POSTDEC) a=7 b=0 c=0
    BC 0007: CALL 0, 1, 5        ; 0x02804030 op=48 (CALL) a=0 b=1 c=5
    BC 0008: CSVAR 1, 256, 0     ; 0x00400052 op=18 (CSVAR) a=1 b=256 c=0
    BC 0009: LDREG 3, 0, 0       ; 0x000000c0 op=0 (LDREG) a=3 b=0 c=0
    BC 0010: CALL 0, 1, 1        ; 0x00804030 op=48 (CALL) a=0 b=1 c=1
    BC 0011: RETURN 1, 0, 0      ; 0x0000006f op=47 (RETURN) a=1 b=0 c=0

For an empty for-loop, in Duktape 1.1.0:

    function foo() { for (var i = 0; i < 1000; i++) {} }

    BC 0000: LABEL 0, 0, 0        ; 0x00000036 op=54 (LABEL) a=0 b=0 c=0
    BC 0001: JUMP 11 (to pc+12)   ; 0x800002f2 op=50 (JUMP) a=11 b=0 c=256
    BC 0002: JUMP 5 (to pc+6)     ; 0x80000172 op=50 (JUMP) a=5 b=0 c=256
    BC 0003: LDINT 0, 0, 256      ; 0x80000003 op=3 (LDINT) a=0 b=0 c=256
    BC 0004: LT 1, 0, 256         ; 0x8000006d op=45 (LT) a=1 b=0 c=256
    BC 0005: IF 0, 1, 0           ; 0x0000402f op=47 (IF) a=0 b=1 c=0
    BC 0006: JUMP 1 (to pc+2)     ; 0x80000072 op=50 (JUMP) a=1 b=0 c=256
    BC 0007: JUMP 5 (to pc+6)     ; 0x80000172 op=50 (JUMP) a=5 b=0 c=256
    BC 0008: LDREG 1, 0, 0        ; 0x00000040 op=0 (LDREG) a=1 b=0 c=0
    BC 0009: TONUM 1, 1           ; 0x008042fe op=62 (EXTRA) a=11 b=1 c=1
    BC 0010: INC 0, 1             ; 0x0080067e op=62 (EXTRA) a=25 b=0 c=1
    BC 0011: JUMP -8 (to pc-7)    ; 0x7ffffe32 op=50 (JUMP) a=248 b=511 c=255
    BC 0012: JUMP -5 (to pc-4)    ; 0x7ffffef2 op=50 (JUMP) a=251 b=511 c=255
    BC 0013: ENDLABEL 0, 0, 0     ; 0x00000037 op=55 (ENDLABEL) a=0 b=0 c=0
    BC 0014: RETURN 1, 0, 0       ; 0x00000073 op=51 (RETURN) a=1 b=0 c=0

After this commit:

    function foo() { for (var i = 0; i < 1000; i++) {} }

    BC 0000: LABEL 0, 0           ; 0x0000083f op=63 (EXTRA) a=32 b=0 c=0
    BC 0001: JUMP 9 (to pc+10)    ; 0x8000026e op=46 (JUMP) a=9 b=0 c=256
    BC 0002: JUMP 5 (to pc+6)     ; 0x8000016e op=46 (JUMP) a=5 b=0 c=256
    BC 0003: LDINT 0, 0, 256      ; 0x80000003 op=3 (LDINT) a=0 b=0 c=256
    BC 0004: LT 1, 0, 256         ; 0x8000006b op=43 (LT) a=1 b=0 c=256
    BC 0005: IF 0, 1, 0           ; 0x0000402d op=45 (IF) a=0 b=1 c=0
    BC 0006: JUMP 1 (to pc+2)     ; 0x8000006e op=46 (JUMP) a=1 b=0 c=256
    BC 0007: JUMP 3 (to pc+4)     ; 0x800000ee op=46 (JUMP) a=3 b=0 c=256
    BC 0008: POSTINC 1, 0, 0      ; 0x00000079 op=57 (POSTINC) a=1 b=0 c=0
    BC 0009: JUMP -6 (to pc-5)    ; 0x7ffffeae op=46 (JUMP) a=250 b=511 c=255
    BC 0010: JUMP -3 (to pc-2)    ; 0x7fffff6e op=46 (JUMP) a=253 b=511 c=255
    BC 0011: ENDLABEL 0, 0        ; 0x0000087f op=63 (EXTRA) a=33 b=0 c=0
    BC 0012: RETURN 1, 0, 0       ; 0x0000006f op=47 (RETURN) a=1 b=0 c=0
pull/156/head
Sami Vaarala 10 years ago
parent
commit
c281a7e8a0
  1. 11
      ecmascript-testcases/test-dev-postincdec.js
  2. 22
      src/duk_debug_vsnprintf.c
  3. 90
      src/duk_js_bytecode.h
  4. 245
      src/duk_js_compiler.c
  5. 7
      src/duk_js_compiler.h
  6. 451
      src/duk_js_executor.c

11
ecmascript-testcases/test-dev-postincdec.js

@ -63,6 +63,7 @@ NaN NaN
NaN NaN
NaN NaN
number 124
number
===*/
/* The value is coerced with ToNumber(). Note that the value assigned to
@ -88,6 +89,7 @@ y = 'foo'; print(y--, y); // ToNumber('foo') -> NaN
y = 'foo'; print(y++, y);
y = '123'; print(typeof y++, y); // ToNumber() is applied to OLD value too
print(typeof y);
/*===
1000
@ -180,3 +182,12 @@ try {
print(e.name);
}
print(z);
/*===
10 11 12 11 10
9
===*/
var x = 10;
print(x++, x++, x--, x--, x--);
print(x);

22
src/duk_debug_vsnprintf.c

@ -73,21 +73,21 @@
#define DUK__LOOP_STACK_DEPTH 256
/* must match bytecode defines now; build autogenerate? */
DUK_LOCAL const char *duk__bc_optab[] = {
DUK_LOCAL const char *duk__bc_optab[64] = {
"LDREG", "STREG", "LDCONST", "LDINT", "LDINTX", "MPUTOBJ", "MPUTOBJI", "MPUTARR", "MPUTARRI", "NEW",
"NEWI", "REGEXP", "CSREG", "CSREGI", "GETVAR", "PUTVAR", "DECLVAR", "DELVAR", "CSVAR", "CSVARI",
"CLOSURE", "GETPROP", "PUTPROP", "DELPROP", "CSPROP", "CSPROPI", "ADD", "SUB", "MUL", "DIV",
"MOD", "BAND", "BOR", "BXOR", "BASL", "BLSR", "BASR", "BNOT", "LNOT", "EQ",
"NEQ", "SEQ", "SNEQ", "GT", "GE", "LT", "LE", "IF", "INSTOF", "IN",
"JUMP", "RETURN", "CALL", "CALLI", "LABEL", "ENDLABEL", "BREAK", "CONTINUE", "TRYCATCH", "UNUSED59",
"UNUSED60", "EXTRA", "DEBUG", "INVALID",
"MOD", "BAND", "BOR", "BXOR", "BASL", "BLSR", "BASR", "EQ", "NEQ", "SEQ",
"SNEQ", "GT", "GE", "LT", "LE", "IF", "JUMP", "RETURN", "CALL", "CALLI",
"TRYCATCH", "EXTRA", "PREINCR", "PREDECR", "POSTINCR", "POSTDECR", "PREINCV", "PREDECV", "POSTINCV", "POSTDECV",
"PREINCP", "PREDECP", "POSTINCP", "POSTDECP"
};
DUK_LOCAL const char *duk__bc_extraoptab[] = {
"NOP", "LDTHIS", "LDUNDEF", "LDNULL", "LDTRUE", "LDFALSE", "NEWOBJ", "NEWARR", "SETALEN", "TYPEOF",
"TYPEOFID", "TONUM", "INITENUM", "NEXTENUM", "INITSET", "INITSETI", "INITGET", "INITGETI", "ENDTRY", "ENDCATCH",
"ENDFIN", "THROW", "INVLHS", "UNM", "UNP", "INC", "DEC", "XXX", "XXX", "XXX",
"XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
DUK_LOCAL const char *duk__bc_extraoptab[256] = {
"NOP", "INVALID", "LDTHIS", "LDUNDEF", "LDNULL", "LDTRUE", "LDFALSE", "NEWOBJ", "NEWARR", "SETALEN",
"TYPEOF", "TYPEOFID", "INITENUM", "NEXTENUM", "INITSET", "INITSETI", "INITGET", "INITGETI", "ENDTRY", "ENDCATCH",
"ENDFIN", "THROW", "INVLHS", "UNM", "UNP", "DEBUGGER", "BREAK", "CONTINUE", "BNOT", "LNOT",
"INSTOF", "IN", "LABEL", "ENDLABEL", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
"XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
"XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
@ -114,7 +114,7 @@ DUK_LOCAL const char *duk__bc_extraoptab[] = {
"XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
"XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
"XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
"XXX", "XXX", "XXX", "XXX", "XXX", "XXX"
};
typedef struct duk__dprint_state duk__dprint_state;

90
src/duk_js_bytecode.h

@ -38,6 +38,7 @@ typedef duk_uint32_t duk_instr_t;
#define DUK_DEC_BC(x) (((x) >> 14) & 0x3ffffUL)
#define DUK_DEC_ABC(x) (((x) >> 6) & 0x3ffffffUL)
#define DUK_ENC_OP(op) ((duk_instr_t) (op))
#define DUK_ENC_OP_ABC(op,abc) ((duk_instr_t) ( \
(((duk_instr_t) (abc)) << 6) | \
((duk_instr_t) (op)) \
@ -111,47 +112,48 @@ typedef duk_uint32_t duk_instr_t;
#define DUK_OP_BASL 34
#define DUK_OP_BLSR 35
#define DUK_OP_BASR 36
#define DUK_OP_BNOT 37
#define DUK_OP_LNOT 38
#define DUK_OP_EQ 39
#define DUK_OP_NEQ 40
#define DUK_OP_SEQ 41
#define DUK_OP_SNEQ 42
#define DUK_OP_GT 43
#define DUK_OP_GE 44
#define DUK_OP_LT 45
#define DUK_OP_LE 46
#define DUK_OP_IF 47
#define DUK_OP_INSTOF 48
#define DUK_OP_IN 49
#define DUK_OP_JUMP 50
#define DUK_OP_RETURN 51
#define DUK_OP_CALL 52
#define DUK_OP_CALLI 53
#define DUK_OP_LABEL 54
#define DUK_OP_ENDLABEL 55
#define DUK_OP_BREAK 56
#define DUK_OP_CONTINUE 57
#define DUK_OP_TRYCATCH 58
#define DUK_OP_UNUSED59 59
#define DUK_OP_UNUSED60 60
#define DUK_OP_UNUSED61 61
#define DUK_OP_EXTRA 62
#define DUK_OP_INVALID 63
#define DUK_OP_EQ 37
#define DUK_OP_NEQ 38
#define DUK_OP_SEQ 39
#define DUK_OP_SNEQ 40
#define DUK_OP_GT 41
#define DUK_OP_GE 42
#define DUK_OP_LT 43
#define DUK_OP_LE 44
#define DUK_OP_IF 45
#define DUK_OP_JUMP 46
#define DUK_OP_RETURN 47
#define DUK_OP_CALL 48
#define DUK_OP_CALLI 49
#define DUK_OP_TRYCATCH 50
#define DUK_OP_EXTRA 51
#define DUK_OP_PREINCR 52 /* pre/post opcode values have constraints, */
#define DUK_OP_PREDECR 53 /* see duk_js_executor.c */
#define DUK_OP_POSTINCR 54
#define DUK_OP_POSTDECR 55
#define DUK_OP_PREINCV 56
#define DUK_OP_PREDECV 57
#define DUK_OP_POSTINCV 58
#define DUK_OP_POSTDECV 59
#define DUK_OP_PREINCP 60
#define DUK_OP_PREDECP 61
#define DUK_OP_POSTINCP 62
#define DUK_OP_POSTDECP 63
#define DUK_OP_NONE 64 /* dummy value used as marker */
/* DUK_OP_EXTRA, sub-operation in A */
#define DUK_EXTRAOP_NOP 0
#define DUK_EXTRAOP_LDTHIS 1
#define DUK_EXTRAOP_LDUNDEF 2
#define DUK_EXTRAOP_LDNULL 3
#define DUK_EXTRAOP_LDTRUE 4
#define DUK_EXTRAOP_LDFALSE 5
#define DUK_EXTRAOP_NEWOBJ 6
#define DUK_EXTRAOP_NEWARR 7
#define DUK_EXTRAOP_SETALEN 8
#define DUK_EXTRAOP_TYPEOF 9
#define DUK_EXTRAOP_TYPEOFID 10
#define DUK_EXTRAOP_TONUM 11
#define DUK_EXTRAOP_INVALID 1
#define DUK_EXTRAOP_LDTHIS 2
#define DUK_EXTRAOP_LDUNDEF 3
#define DUK_EXTRAOP_LDNULL 4
#define DUK_EXTRAOP_LDTRUE 5
#define DUK_EXTRAOP_LDFALSE 6
#define DUK_EXTRAOP_NEWOBJ 7
#define DUK_EXTRAOP_NEWARR 8
#define DUK_EXTRAOP_SETALEN 9
#define DUK_EXTRAOP_TYPEOF 10
#define DUK_EXTRAOP_TYPEOFID 11
#define DUK_EXTRAOP_INITENUM 12
#define DUK_EXTRAOP_NEXTENUM 13
#define DUK_EXTRAOP_INITSET 14
@ -165,9 +167,15 @@ typedef duk_uint32_t duk_instr_t;
#define DUK_EXTRAOP_INVLHS 22
#define DUK_EXTRAOP_UNM 23
#define DUK_EXTRAOP_UNP 24
#define DUK_EXTRAOP_INC 25
#define DUK_EXTRAOP_DEC 26
#define DUK_EXTRAOP_DEBUGGER 27
#define DUK_EXTRAOP_DEBUGGER 25
#define DUK_EXTRAOP_BREAK 26
#define DUK_EXTRAOP_CONTINUE 27
#define DUK_EXTRAOP_BNOT 28
#define DUK_EXTRAOP_LNOT 29
#define DUK_EXTRAOP_INSTOF 30
#define DUK_EXTRAOP_IN 31
#define DUK_EXTRAOP_LABEL 32
#define DUK_EXTRAOP_ENDLABEL 33
/* DUK_OP_EXTRA for debugging */
#define DUK_EXTRAOP_DUMPREG 128

245
src/duk_js_compiler.c

@ -1675,7 +1675,7 @@ DUK_LOCAL void duk__emit_if_true_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t
}
DUK_LOCAL void duk__emit_invalid(duk_compiler_ctx *comp_ctx) {
duk__emit_abc(comp_ctx, DUK_OP_INVALID, 0);
duk__emit_extraop_bc(comp_ctx, DUK_EXTRAOP_INVALID, 0);
}
/*
@ -2092,7 +2092,8 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x
return;
}
/* XXX: support unary arithmetic ivalues (useful?) */
case DUK_IVAL_ARITH: {
case DUK_IVAL_ARITH:
case DUK_IVAL_ARITH_EXTRAOP: {
duk_regconst_t arg1;
duk_regconst_t arg2;
duk_reg_t dest;
@ -2103,7 +2104,7 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x
/* inline arithmetic check for constant values */
/* XXX: use the exactly same arithmetic function here as in executor */
if (x->x1.t == DUK_ISPEC_VALUE && x->x2.t == DUK_ISPEC_VALUE) {
if (x->x1.t == DUK_ISPEC_VALUE && x->x2.t == DUK_ISPEC_VALUE && x->t == DUK_IVAL_ARITH) {
tv1 = duk_get_tval(ctx, x->x1.valstack_idx);
tv2 = duk_get_tval(ctx, x->x2.valstack_idx);
DUK_ASSERT(tv1 != NULL);
@ -2157,18 +2158,56 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x
/* If forced reg, use it as destination. Otherwise try to
* use either coerced ispec if it is a temporary.
*
* When using extraops, avoid reusing arg2 as dest because that
* would lead to an LDREG shuffle below. We still can't guarantee
* dest != arg2 because we may have a forced_reg.
*/
if (forced_reg >= 0) {
dest = forced_reg;
} else if (DUK__ISTEMP(comp_ctx, arg1)) {
dest = (duk_reg_t) arg1;
} else if (DUK__ISTEMP(comp_ctx, arg2)) {
} else if (DUK__ISTEMP(comp_ctx, arg2) && x->t != DUK_IVAL_ARITH_EXTRAOP) {
dest = (duk_reg_t) arg2;
} else {
dest = DUK__ALLOCTEMP(comp_ctx);
}
duk__emit_a_b_c(comp_ctx, x->op, (duk_regconst_t) dest, arg1, arg2);
/* Extraop arithmetic opcodes must have destination same as
* first source. If second source matches destination we need
* a temporary register to avoid clobbering the second source.
*
* XXX: change calling code to avoid this situation in most cases.
*/
if (x->t == DUK_IVAL_ARITH_EXTRAOP) {
if (!(DUK__ISREG(comp_ctx, arg1) && (duk_reg_t) arg1 == dest)) {
if (DUK__ISREG(comp_ctx, arg2) && (duk_reg_t) arg2 == dest) {
/* arg2 would be clobbered so reassign it to a temp. */
duk_reg_t tempreg;
tempreg = DUK__ALLOCTEMP(comp_ctx);
duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, tempreg, arg2);
arg2 = tempreg;
}
if (DUK__ISREG(comp_ctx, arg1)) {
duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, dest, arg1);
} else {
DUK_ASSERT(DUK__ISCONST(comp_ctx, arg1));
duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, dest, arg1);
}
}
DUK_ASSERT(DUK__ISREG(comp_ctx, dest));
duk__emit_extraop_b_c(comp_ctx,
x->op | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) dest,
(duk_regconst_t) arg2);
} else {
DUK_ASSERT(DUK__ISREG(comp_ctx, dest));
duk__emit_a_b_c(comp_ctx, x->op, (duk_regconst_t) dest, arg1, arg2);
}
x->t = DUK_IVAL_PLAIN;
x->x1.t = DUK_ISPEC_REGCONST;
@ -3467,12 +3506,12 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
goto unary_extraop;
}
case DUK_TOK_INCREMENT: {
args = (DUK_EXTRAOP_INC << 8) + 0;
goto preincdec_extraop;
args = (DUK_OP_PREINCR << 8) + 0;
goto preincdec;
}
case DUK_TOK_DECREMENT: {
args = (DUK_EXTRAOP_DEC << 8) + 0;
goto preincdec_extraop;
args = (DUK_OP_PREDECR << 8) + 0;
goto preincdec;
}
case DUK_TOK_ADD: {
/* unary plus */
@ -3510,8 +3549,8 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
}
case DUK_TOK_BNOT: {
duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */
args = (DUK_OP_BNOT << 8) + 0;
goto unary;
args = (DUK_EXTRAOP_BNOT << 8) + 0;
goto unary_extraop;
}
case DUK_TOK_LNOT: {
duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */
@ -3544,8 +3583,8 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
return;
}
}
args = (DUK_OP_LNOT << 8) + 0;
goto unary;
args = (DUK_EXTRAOP_LNOT << 8) + 0;
goto unary_extraop;
}
} /* end switch */
@ -3553,45 +3592,35 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
DUK_ERROR(thr, DUK_ERR_SYNTAX_ERROR, DUK_STR_PARSE_ERROR);
return;
unary:
unary_extraop:
{
/* Note: must coerce to a (writable) temp register, so that e.g. "!x" where x
* is a reg-mapped variable works correctly (does not mutate the variable register).
*/
duk_regconst_t rc_temp;
rc_temp = duk__ivalue_toregconst_raw(comp_ctx, res, -1 /*forced_reg*/, DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/);
duk__emit_a_b(comp_ctx,
args >> 8,
rc_temp,
rc_temp);
res->t = DUK_IVAL_PLAIN;
res->x1.t = DUK_ISPEC_REGCONST;
res->x1.regconst = rc_temp;
return;
}
unary_extraop:
{
/* XXX: refactor into unary2: above? */
duk_reg_t reg_temp;
reg_temp = duk__ivalue_toregconst_raw(comp_ctx, res, -1 /*forced_reg*/, DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/);
duk__emit_extraop_b_c(comp_ctx,
(args >> 8) | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) reg_temp,
(duk_regconst_t) reg_temp);
duk__emit_extraop_bc(comp_ctx,
(args >> 8),
(duk_regconst_t) reg_temp);
res->t = DUK_IVAL_PLAIN;
res->x1.t = DUK_ISPEC_REGCONST;
res->x1.regconst = (duk_regconst_t) reg_temp;
return;
}
preincdec_extraop:
preincdec:
{
/* preincrement and predecrement */
duk_reg_t reg_res;
duk_small_uint_t args_op = args >> 8;
/* Specific assumptions for opcode numbering. */
DUK_ASSERT(DUK_OP_PREINCR + 4 == DUK_OP_PREINCV);
DUK_ASSERT(DUK_OP_PREDECR + 4 == DUK_OP_PREDECV);
DUK_ASSERT(DUK_OP_PREINCR + 8 == DUK_OP_PREINCP);
DUK_ASSERT(DUK_OP_PREDECR + 8 == DUK_OP_PREDECP);
reg_res = DUK__ALLOCTEMP(comp_ctx);
duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */
@ -3609,30 +3638,18 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
duk_dup(ctx, res->x1.valstack_idx);
if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
duk__emit_extraop_b_c(comp_ctx,
args_op | DUK__EMIT_FLAG_B_IS_TARGET,
reg_varbind,
reg_varbind);
duk__emit_a_bc(comp_ctx,
DUK_OP_LDREG,
args_op, /* e.g. DUK_OP_PREINCR */
(duk_regconst_t) reg_res,
(duk_regconst_t) reg_varbind);
} else {
duk__emit_a_bc(comp_ctx,
DUK_OP_GETVAR,
(duk_regconst_t) reg_res,
rc_varname);
duk__emit_extraop_b_c(comp_ctx,
args_op | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) reg_res,
(duk_regconst_t) reg_res);
duk__emit_a_bc(comp_ctx,
DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE,
(duk_regconst_t) reg_res,
rc_varname);
args_op + 4, /* e.g. DUK_OP_PREINCV */
(duk_regconst_t) reg_res,
rc_varname);
}
DUK_DDD(DUK_DDDPRINT("postincdec to '%!O' -> reg_varbind=%ld, rc_varname=%ld",
DUK_DDD(DUK_DDDPRINT("preincdec to '%!O' -> reg_varbind=%ld, rc_varname=%ld",
(duk_heaphdr *) h_varname, (long) reg_varbind, (long) rc_varname));
} else if (res->t == DUK_IVAL_PROP) {
duk_reg_t reg_obj; /* allocate to reg only (not const) */
@ -3640,29 +3657,21 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
reg_obj = duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/); /* don't allow const */
rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
duk__emit_a_b_c(comp_ctx,
DUK_OP_GETPROP,
args_op + 8, /* e.g. DUK_OP_PREINCP */
(duk_regconst_t) reg_res,
(duk_regconst_t) reg_obj,
rc_key);
duk__emit_extraop_b_c(comp_ctx,
args_op | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) reg_res,
(duk_regconst_t) reg_res);
duk__emit_a_b_c(comp_ctx,
DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE,
(duk_regconst_t) reg_obj,
rc_key,
(duk_regconst_t) reg_res);
} else {
/* Technically return value is not needed because INVLHS will
* unconditially throw a ReferenceError. Coercion is necessary
* for proper semantics (consider ToNumber() called for an object).
* Use DUK_EXTRAOP_UNP with a dummy register to get ToNumber().
*/
duk__ivalue_toforcedreg(comp_ctx, res, reg_res);
duk__emit_extraop_b_c(comp_ctx,
DUK_EXTRAOP_TONUM | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) reg_res,
(duk_regconst_t) reg_res); /* for side effects */
duk__emit_extraop_bc(comp_ctx,
DUK_EXTRAOP_UNP,
reg_res); /* for side effects, result ignored */
duk__emit_extraop_only(comp_ctx,
DUK_EXTRAOP_INVLHS);
}
@ -3869,12 +3878,12 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
/* POSTFIX EXPRESSION */
case DUK_TOK_INCREMENT: {
args = (DUK_EXTRAOP_INC << 8) + 0;
goto postincdec_extraop;
args = (DUK_OP_POSTINCR << 8) + 0;
goto postincdec;
}
case DUK_TOK_DECREMENT: {
args = (DUK_EXTRAOP_DEC << 8) + 0;
goto postincdec_extraop;
args = (DUK_OP_POSTDECR << 8) + 0;
goto postincdec;
}
/* MULTIPLICATIVE EXPRESSION */
@ -3941,11 +3950,11 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
goto binary;
}
case DUK_TOK_INSTANCEOF: {
args = (DUK_OP_INSTOF << 8) + DUK__BP_RELATIONAL;
args = (1 << 16 /*is_extra*/) + (DUK_EXTRAOP_INSTOF << 8) + DUK__BP_RELATIONAL;
goto binary;
}
case DUK_TOK_IN: {
args = (DUK_OP_IN << 8) + DUK__BP_RELATIONAL;
args = (1 << 16 /*is_extra*/) + (DUK_EXTRAOP_IN << 8) + DUK__BP_RELATIONAL;
goto binary;
}
@ -4038,7 +4047,7 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
*
* XXX: just use DUK__BP_COMMA (i.e. no need for 2-step bp levels)?
*/
args = (DUK_OP_INVALID << 8) + DUK__BP_ASSIGNMENT - 1; /* DUK_OP_INVALID marks a 'plain' assignment */
args = (DUK_OP_NONE << 8) + DUK__BP_ASSIGNMENT - 1; /* DUK_OP_NONE marks a 'plain' assignment */
goto assign;
}
case DUK_TOK_ADD_EQ: {
@ -4129,7 +4138,7 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
/*
* Shared handling of binary operations
*
* args = (opcode << 8) + rbp
* args = (is_extraop << 16) + (opcode << 8) + rbp
*/
{
duk__ivalue_toplain(comp_ctx, left);
@ -4139,8 +4148,8 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
DUK_ASSERT(left->t == DUK_IVAL_PLAIN);
DUK_ASSERT(res->t == DUK_IVAL_PLAIN);
res->t = DUK_IVAL_ARITH;
res->op = args >> 8;
res->t = (args >> 16) ? DUK_IVAL_ARITH_EXTRAOP : DUK_IVAL_ARITH;
res->op = (args >> 8) & 0xff;
res->x2.t = res->x1.t;
res->x2.regconst = res->x1.regconst;
@ -4150,8 +4159,8 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
res->x1.regconst = left->x1.regconst;
duk_copy(ctx, left->x1.valstack_idx, res->x1.valstack_idx);
DUK_DDD(DUK_DDDPRINT("binary op, res: t=%ld, x1.t=%ld, x2.t=%ld",
(long) res->t, (long) res->x1.t, (long) res->x2.t));
DUK_DDD(DUK_DDDPRINT("binary op, res: t=%ld, x1.t=%ld, x1.regconst=0x%08lx, x2.t=%ld, x2.regconst=0x%08lx",
(long) res->t, (long) res->x1.t, (unsigned long) res->x1.regconst, (long) res->x2.t, (unsigned long) res->x2.regconst));
return;
}
@ -4206,7 +4215,7 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
*
* args = (opcode << 8) + rbp
*
* If 'opcode' is DUK_OP_INVALID, plain assignment without arithmetic.
* If 'opcode' is DUK_OP_NONE, plain assignment without arithmetic.
* Syntactically valid left-hand-side forms which are not accepted as
* left-hand-side values (e.g. as in "f() = 1") must NOT cause a
* SyntaxError, but rather a run-time ReferenceError.
@ -4249,7 +4258,7 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
DUK_DDD(DUK_DDDPRINT("assign to '%!O' -> reg_varbind=%ld, rc_varname=%ld",
(duk_heaphdr *) h_varname, (long) reg_varbind, (long) rc_varname));
if (args_op == DUK_OP_INVALID) {
if (args_op == DUK_OP_NONE) {
rc_res = res->x1.regconst;
} else {
reg_temp = DUK__ALLOCTEMP(comp_ctx);
@ -4335,7 +4344,7 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/);
DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST);
if (args_op == DUK_OP_INVALID) {
if (args_op == DUK_OP_NONE) {
rc_res = res->x1.regconst;
} else {
reg_temp = DUK__ALLOCTEMP(comp_ctx);
@ -4394,14 +4403,13 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
return;
}
postincdec_extraop:
postincdec:
{
/*
* Post-increment/decrement will return the original value as its
* result value. However, even that value will be coerced using
* ToNumber().
*
* XXX: the current solution for this is very ugly.
* ToNumber() which is quite awkward. Specific bytecode opcodes
* are used to handle these semantics.
*
* Note that post increment/decrement has a "no LineTerminator here"
* restriction. This is handled by duk__expr_lbp(), which forcibly terminates
@ -4411,6 +4419,12 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
duk_reg_t reg_res;
duk_small_uint_t args_op = args >> 8;
/* Specific assumptions for opcode numbering. */
DUK_ASSERT(DUK_OP_POSTINCR + 4 == DUK_OP_POSTINCV);
DUK_ASSERT(DUK_OP_POSTDECR + 4 == DUK_OP_POSTDECV);
DUK_ASSERT(DUK_OP_POSTINCR + 8 == DUK_OP_POSTINCP);
DUK_ASSERT(DUK_OP_POSTDECR + 8 == DUK_OP_POSTDECP);
reg_res = DUK__ALLOCTEMP(comp_ctx);
if (left->t == DUK_IVAL_VAR) {
@ -4428,35 +4442,14 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
duk_dup(ctx, left->x1.valstack_idx);
if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
duk__emit_a_bc(comp_ctx,
DUK_OP_LDREG,
args_op, /* e.g. DUK_OP_POSTINCR */
(duk_regconst_t) reg_res,
(duk_regconst_t) reg_varbind);
duk__emit_extraop_b_c(comp_ctx,
DUK_EXTRAOP_TONUM | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) reg_res,
(duk_regconst_t) reg_res);
duk__emit_extraop_b_c(comp_ctx,
args_op | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) reg_varbind,
(duk_regconst_t) reg_res);
} else {
duk_reg_t reg_temp = DUK__ALLOCTEMP(comp_ctx);
duk__emit_a_bc(comp_ctx,
DUK_OP_GETVAR,
args_op + 4, /* e.g. DUK_OP_POSTINCV */
(duk_regconst_t) reg_res,
rc_varname);
duk__emit_extraop_b_c(comp_ctx,
DUK_EXTRAOP_TONUM | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) reg_res,
(duk_regconst_t) reg_res);
duk__emit_extraop_b_c(comp_ctx,
args_op | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) reg_temp,
(duk_regconst_t) reg_res);
duk__emit_a_bc(comp_ctx,
DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE,
(duk_regconst_t) reg_temp,
rc_varname);
}
DUK_DDD(DUK_DDDPRINT("postincdec to '%!O' -> reg_varbind=%ld, rc_varname=%ld",
@ -4464,38 +4457,24 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i
} else if (left->t == DUK_IVAL_PROP) {
duk_reg_t reg_obj; /* allocate to reg only (not const) */
duk_regconst_t rc_key;
duk_reg_t reg_temp = DUK__ALLOCTEMP(comp_ctx);
reg_obj = duk__ispec_toregconst_raw(comp_ctx, &left->x1, -1 /*forced_reg*/, 0 /*flags*/); /* don't allow const */
rc_key = duk__ispec_toregconst_raw(comp_ctx, &left->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
duk__emit_a_b_c(comp_ctx,
DUK_OP_GETPROP,
args_op + 8, /* e.g. DUK_OP_POSTINCP */
(duk_regconst_t) reg_res,
(duk_regconst_t) reg_obj,
rc_key);
duk__emit_extraop_b_c(comp_ctx,
DUK_EXTRAOP_TONUM | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) reg_res,
(duk_regconst_t) reg_res);
duk__emit_extraop_b_c(comp_ctx,
args_op | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) reg_temp,
(duk_regconst_t) reg_res);
duk__emit_a_b_c(comp_ctx,
DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE,
(duk_regconst_t) reg_obj,
rc_key,
(duk_regconst_t) reg_temp);
} else {
/* Technically return value is not needed because INVLHS will
* unconditially throw a ReferenceError. Coercion is necessary
* for proper semantics (consider ToNumber() called for an object).
* Use DUK_EXTRAOP_UNP with a dummy register to get ToNumber().
*/
duk__ivalue_toforcedreg(comp_ctx, left, reg_res);
duk__emit_extraop_b_c(comp_ctx,
DUK_EXTRAOP_TONUM | DUK__EMIT_FLAG_B_IS_TARGET,
(duk_regconst_t) reg_res,
(duk_regconst_t) reg_res); /* for side effects */
duk__emit_extraop_bc(comp_ctx,
DUK_EXTRAOP_UNP,
reg_res); /* for side effects, result ignored */
duk__emit_extraop_only(comp_ctx,
DUK_EXTRAOP_INVLHS);
}
@ -5554,8 +5533,8 @@ DUK_LOCAL void duk__parse_break_or_continue_stmt(duk_compiler_ctx *comp_ctx, duk
(long) is_break, (long) label_id, (long) label_is_closest,
(long) label_catch_depth, (long) comp_ctx->curr_func.catch_depth));
duk__emit_abc(comp_ctx,
is_break ? DUK_OP_BREAK : DUK_OP_CONTINUE,
duk__emit_extraop_bc(comp_ctx,
is_break ? DUK_EXTRAOP_BREAK : DUK_EXTRAOP_CONTINUE,
(duk_regconst_t) label_id);
}
}
@ -5976,9 +5955,9 @@ DUK_LOCAL duk_int_t duk__stmt_label_site(duk_compiler_ctx *comp_ctx, duk_int_t l
label_id = comp_ctx->curr_func.label_next++;
DUK_DDD(DUK_DDDPRINT("allocated new label id for label site: %ld", (long) label_id));
duk__emit_abc(comp_ctx,
DUK_OP_LABEL,
(duk_regconst_t) label_id);
duk__emit_extraop_bc(comp_ctx,
DUK_EXTRAOP_LABEL,
(duk_regconst_t) label_id);
duk__emit_invalid(comp_ctx);
duk__emit_invalid(comp_ctx);
@ -6471,7 +6450,9 @@ DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_
*/
if (label_id >= 0) {
duk__emit_abc(comp_ctx, DUK_OP_ENDLABEL, label_id);
duk__emit_extraop_bc(comp_ctx,
DUK_EXTRAOP_ENDLABEL,
(duk_regconst_t) label_id);
}
DUK__SETTEMP(comp_ctx, temp_at_entry);

7
src/duk_js_compiler.h

@ -30,8 +30,9 @@
#define DUK_IVAL_NONE 0 /* no value */
#define DUK_IVAL_PLAIN 1 /* register, constant, or value */
#define DUK_IVAL_ARITH 2 /* binary arithmetic; DUK_OP_ADD, DUK_OP_EQ, other binary ops */
#define DUK_IVAL_PROP 3 /* property access */
#define DUK_IVAL_VAR 4 /* variable access */
#define DUK_IVAL_ARITH_EXTRAOP 3 /* binary arithmetic using extraops; DUK_EXTRAOP_INSTOF etc */
#define DUK_IVAL_PROP 4 /* property access */
#define DUK_IVAL_VAR 5 /* variable access */
#define DUK_ISPEC_NONE 0 /* no value */
#define DUK_ISPEC_VALUE 1 /* value resides in 'valstack_idx' */
@ -62,7 +63,7 @@ typedef struct {
/* XXX: can be optimized for smaller footprint esp. on 32-bit environments */
duk_small_uint_t t; /* DUK_IVAL_XXX */
duk_small_uint_t op; /* bytecode opcode for binary ops */
duk_small_uint_t op; /* bytecode opcode (or extraop) for binary ops */
duk_ispec x1;
duk_ispec x2;
} duk_ivalue;

451
src/duk_js_executor.c

@ -321,6 +321,7 @@ DUK_LOCAL void duk__vm_bitwise_binary_op(duk_hthread *thr, duk_tval *tv_x, duk_t
DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
}
/* FIXME: reimplement */
DUK_LOCAL void duk__vm_arith_unary_op(duk_hthread *thr, duk_tval *tv_x, duk_small_uint_fast_t idx_z, duk_small_uint_fast_t opcode) {
/*
* Arithmetic operations other than '+' have number-only semantics
@ -342,7 +343,8 @@ DUK_LOCAL void duk__vm_arith_unary_op(duk_hthread *thr, duk_tval *tv_x, duk_smal
DUK_ASSERT_DISABLE(idx_z >= 0); /* unsigned */
DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(ctx));
#if defined(DUK_USE_FASTINT)
/* FIXME: reimplement */
#if defined(DUK_USE_FASTINT) && defined(FIXME)
if (DUK_TVAL_IS_NUMBER_FASTINT(tv_x)) {
/* fast path */
duk_int64_t v1 = DUK_TVAL_GET_NUMBER_FASTINT(tv_x);
@ -387,14 +389,6 @@ DUK_LOCAL void duk__vm_arith_unary_op(duk_hthread *thr, duk_tval *tv_x, duk_smal
du.d = d1;
break;
}
case DUK_EXTRAOP_INC: {
du.d = d1 + 1.0;
break;
}
case DUK_EXTRAOP_DEC: {
du.d = d1 - 1.0;
break;
}
default: {
du.d = DUK_DOUBLE_NAN; /* should not happen */
break;
@ -2516,6 +2510,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
duk_tval *tv1;
duk_hstring *name;
/* FIXME: change into assert? */
tv1 = DUK__CONSTP(bc);
if (!DUK_TVAL_IS_STRING(tv1)) {
DUK_DDD(DUK_DDDPRINT("GETVAR not a string: %!T", (duk_tval *) tv1));
@ -2892,22 +2887,6 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
break;
}
case DUK_OP_BNOT: {
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk__vm_bitwise_not(thr, DUK__REGCONSTP(b), a);
break;
}
case DUK_OP_LNOT: {
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk__vm_logical_not(thr, DUK__REGCONSTP(b), DUK__REGP(a));
break;
}
case DUK_OP_EQ:
case DUK_OP_NEQ: {
duk_context *ctx = (duk_context *) thr;
@ -3042,32 +3021,6 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
break;
}
case DUK_OP_INSTOF: {
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk_small_uint_fast_t c = DUK_DEC_C(ins);
duk_bool_t tmp;
tmp = duk_js_instanceof(thr, DUK__REGCONSTP(b), DUK__REGCONSTP(c));
duk_push_boolean(ctx, tmp);
duk_replace(ctx, (duk_idx_t) a);
break;
}
case DUK_OP_IN: {
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk_small_uint_fast_t c = DUK_DEC_C(ins);
duk_bool_t tmp;
tmp = duk_js_in(thr, DUK__REGCONSTP(b), DUK__REGCONSTP(c));
duk_push_boolean(ctx, tmp);
duk_replace(ctx, (duk_idx_t) a);
break;
}
case DUK_OP_JUMP: {
duk_int_fast_t abc = DUK_DEC_ABC(ins);
@ -3296,92 +3249,6 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
break;
}
case DUK_OP_LABEL: {
duk_catcher *cat;
duk_uint_fast_t abc = DUK_DEC_ABC(ins);
/* allocate catcher and populate it (should be atomic) */
duk_hthread_catchstack_grow(thr);
cat = thr->catchstack + thr->catchstack_top;
thr->catchstack_top++;
cat->flags = DUK_CAT_TYPE_LABEL | (abc << DUK_CAT_LABEL_SHIFT);
cat->callstack_index = thr->callstack_top - 1;
cat->pc_base = act->pc; /* pre-incremented, points to first jump slot */
cat->idx_base = 0; /* unused for label */
cat->h_varname = NULL;
DUK_DDD(DUK_DDDPRINT("LABEL catcher: flags=0x%08lx, callstack_index=%ld, pc_base=%ld, "
"idx_base=%ld, h_varname=%!O, label_id=%ld",
(long) cat->flags, (long) cat->callstack_index, (long) cat->pc_base,
(long) cat->idx_base, (duk_heaphdr *) cat->h_varname, (long) DUK_CAT_GET_LABEL(cat)));
act->pc += 2; /* skip jump slots */
break;
}
case DUK_OP_ENDLABEL: {
duk_catcher *cat;
#if defined(DUK_USE_DDDPRINT) || defined(DUK_USE_ASSERTIONS)
duk_uint_fast_t abc = DUK_DEC_ABC(ins);
#endif
#if defined(DUK_USE_DDDPRINT)
DUK_DDD(DUK_DDDPRINT("ENDLABEL %ld", (long) abc));
#endif
DUK_ASSERT(thr->catchstack_top >= 1);
cat = thr->catchstack + thr->catchstack_top - 1;
DUK_UNREF(cat);
DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_LABEL);
DUK_ASSERT((duk_uint_fast_t) DUK_CAT_GET_LABEL(cat) == abc);
duk_hthread_catchstack_unwind(thr, thr->catchstack_top - 1);
/* no need to unwind callstack */
break;
}
case DUK_OP_BREAK: {
duk_context *ctx = (duk_context *) thr;
duk_uint_fast_t abc = DUK_DEC_ABC(ins);
/* always the "slow break" variant (longjmp'ing); a "fast break" is
* simply an DUK_OP_JUMP.
*/
DUK_DDD(DUK_DDDPRINT("BREAK: %ld", (long) abc));
duk_push_uint(ctx, (duk_uint_t) abc);
duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_BREAK);
DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* always in executor */
duk_err_longjmp(thr);
DUK_UNREACHABLE();
break;
}
case DUK_OP_CONTINUE: {
duk_context *ctx = (duk_context *) thr;
duk_uint_fast_t abc = DUK_DEC_ABC(ins);
/* always the "slow continue" variant (longjmp'ing); a "fast continue" is
* simply an DUK_OP_JUMP.
*/
DUK_DDD(DUK_DDDPRINT("CONTINUE: %ld", (long) abc));
duk_push_uint(ctx, (duk_uint_t) abc);
duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_CONTINUE);
DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* always in executor */
duk_err_longjmp(thr);
DUK_UNREACHABLE();
break;
}
case DUK_OP_TRYCATCH: {
duk_context *ctx = (duk_context *) thr;
duk_catcher *cat;
@ -3525,6 +3392,144 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
break;
}
/* Pre/post inc/dec for register variables, important for loops. */
case DUK_OP_PREINCR:
case DUK_OP_PREDECR:
case DUK_OP_POSTINCR:
case DUK_OP_POSTDECR: {
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_uint_fast_t bc = DUK_DEC_BC(ins);
duk_double_t x, y;
/* Two lowest bits of opcode are used to distinguish
* variants. Bit 0 = inc(0)/dec(1), bit 1 = pre(0)/post(1).
*/
DUK_ASSERT((DUK_OP_PREINCR & 0x03) == 0x00);
DUK_ASSERT((DUK_OP_PREDECR & 0x03) == 0x01);
DUK_ASSERT((DUK_OP_POSTINCR & 0x03) == 0x02);
DUK_ASSERT((DUK_OP_POSTDECR & 0x03) == 0x03);
/* FIXME: improve */
x = duk_to_number(ctx, bc);
if (ins & DUK_ENC_OP(0x01)) {
y = x - 1.0;
} else {
y = x + 1.0;
}
duk_push_number(ctx, y);
duk_replace(ctx, bc);
duk_push_number(ctx, (ins & DUK_ENC_OP(0x02)) ? x : y);
duk_replace(ctx, a);
break;
}
/* Preinc/predec for var-by-name, slow path. */
case DUK_OP_PREINCV:
case DUK_OP_PREDECV:
case DUK_OP_POSTINCV:
case DUK_OP_POSTDECV: {
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_uint_fast_t bc = DUK_DEC_BC(ins);
duk_double_t x, y;
duk_tval *tv1;
duk_hstring *name;
/* Two lowest bits of opcode are used to distinguish
* variants. Bit 0 = inc(0)/dec(1), bit 1 = pre(0)/post(1).
*/
DUK_ASSERT((DUK_OP_PREINCV & 0x03) == 0x00);
DUK_ASSERT((DUK_OP_PREDECV & 0x03) == 0x01);
DUK_ASSERT((DUK_OP_POSTINCV & 0x03) == 0x02);
DUK_ASSERT((DUK_OP_POSTDECV & 0x03) == 0x03);
/* FIXME: improve */
tv1 = DUK__CONSTP(bc);
DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
name = DUK_TVAL_GET_STRING(tv1);
DUK_ASSERT(name != NULL);
(void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */
x = duk_to_number(ctx, -2);
duk_pop_2(ctx);
if (ins & DUK_ENC_OP(0x01)) {
y = x - 1.0;
} else {
y = x + 1.0;
}
duk_push_number(ctx, y);
tv1 = duk_get_tval(ctx, -1);
DUK_ASSERT(tv1 != NULL);
duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT());
duk_pop(ctx);
duk_push_number(ctx, (ins & DUK_ENC_OP(0x02)) ? x : y);
duk_replace(ctx, (duk_idx_t) a);
break;
}
/* Preinc/predec for object properties. */
case DUK_OP_PREINCP:
case DUK_OP_PREDECP:
case DUK_OP_POSTINCP:
case DUK_OP_POSTDECP: {
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t a = DUK_DEC_A(ins);
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk_small_uint_fast_t c = DUK_DEC_C(ins);
duk_tval *tv_obj;
duk_tval *tv_key;
duk_tval *tv_val;
duk_bool_t rc;
duk_double_t x, y;
/* A -> target reg
* B -> object reg/const (may be const e.g. in "'foo'[1]")
* C -> key reg/const
*/
/* Two lowest bits of opcode are used to distinguish
* variants. Bit 0 = inc(0)/dec(1), bit 1 = pre(0)/post(1).
*/
DUK_ASSERT((DUK_OP_PREINCP & 0x03) == 0x00);
DUK_ASSERT((DUK_OP_PREDECP & 0x03) == 0x01);
DUK_ASSERT((DUK_OP_POSTINCP & 0x03) == 0x02);
DUK_ASSERT((DUK_OP_POSTDECP & 0x03) == 0x03);
tv_obj = DUK__REGCONSTP(b);
tv_key = DUK__REGCONSTP(c);
rc = duk_hobject_getprop(thr, tv_obj, tv_key); /* -> [val] */
DUK_UNREF(rc); /* ignore */
tv_obj = NULL; /* invalidated */
tv_key = NULL; /* invalidated */
x = duk_to_number(ctx, -1);
duk_pop(ctx);
if (ins & DUK_ENC_OP(0x01)) {
y = x - 1.0;
} else {
y = x + 1.0;
}
duk_push_number(ctx, y);
tv_val = duk_get_tval(ctx, -1);
DUK_ASSERT(tv_val != NULL);
tv_obj = DUK__REGCONSTP(b);
tv_key = DUK__REGCONSTP(c);
rc = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, DUK__STRICT());
DUK_UNREF(rc); /* ignore */
tv_obj = NULL; /* invalidated */
tv_key = NULL; /* invalidated */
duk_pop(ctx);
duk_push_number(ctx, (ins & DUK_ENC_OP(0x02)) ? x : y);
duk_replace(ctx, (duk_idx_t) a);
break;
}
case DUK_OP_EXTRA: {
/* XXX: shared decoding of 'b' and 'c'? */
@ -3532,6 +3537,11 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
switch ((int) extraop) {
/* XXX: switch cast? */
case DUK_EXTRAOP_INVALID: {
DUK_ERROR(thr, DUK_ERR_INTERNAL_ERROR, "INVALID opcode (%ld)", (long) DUK_DEC_ABC(ins));
break;
}
case DUK_EXTRAOP_NOP: {
/* nop */
break;
@ -3634,10 +3644,9 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
case DUK_EXTRAOP_TYPEOF: {
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk_small_uint_fast_t c = DUK_DEC_C(ins);
duk_push_hstring(ctx, duk_js_typeof(thr, DUK__REGCONSTP(c)));
duk_replace(ctx, (duk_idx_t) b);
duk_uint_fast_t bc = DUK_DEC_BC(ins);
duk_push_hstring(ctx, duk_js_typeof(thr, DUK__REGP(bc)));
duk_replace(ctx, (duk_idx_t) bc);
break;
}
@ -3670,25 +3679,6 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
break;
}
case DUK_EXTRAOP_TONUM: {
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk_tval *tv;
/* FIXME: compiler behaves this way, change op to be inline */
DUK_ASSERT(DUK_DEC_B(ins) == DUK_DEC_C(ins));
tv = DUK__REGP(b);
if (DUK_TVAL_IS_NUMBER(tv)) {
/* Nop: important special case in e.g. post-inc/dec now. */
} else {
duk_dup(ctx, (duk_idx_t) b);
duk_to_number(ctx, -1);
duk_replace(ctx, (duk_idx_t) b);
}
break;
}
case DUK_EXTRAOP_INITENUM: {
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t b = DUK_DEC_B(ins);
@ -4012,13 +4002,9 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
}
case DUK_EXTRAOP_UNM:
case DUK_EXTRAOP_UNP:
case DUK_EXTRAOP_INC:
case DUK_EXTRAOP_DEC: {
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk_small_uint_fast_t c = DUK_DEC_C(ins);
duk__vm_arith_unary_op(thr, DUK__REGCONSTP(c), b, extraop);
case DUK_EXTRAOP_UNP: {
duk_uint_fast_t bc = DUK_DEC_BC(ins);
duk__vm_arith_unary_op(thr, DUK__REGP(bc), bc, extraop);
break;
}
@ -4040,6 +4026,132 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
break;
}
case DUK_EXTRAOP_BREAK: {
duk_context *ctx = (duk_context *) thr;
duk_uint_fast_t bc = DUK_DEC_BC(ins);
/* always the "slow break" variant (longjmp'ing); a "fast break" is
* simply an DUK_OP_JUMP.
*/
DUK_DDD(DUK_DDDPRINT("BREAK: %ld", (long) bc));
duk_push_uint(ctx, (duk_uint_t) bc);
duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_BREAK);
DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* always in executor */
duk_err_longjmp(thr);
DUK_UNREACHABLE();
break;
}
case DUK_EXTRAOP_CONTINUE: {
duk_context *ctx = (duk_context *) thr;
duk_uint_fast_t bc = DUK_DEC_BC(ins);
/* always the "slow continue" variant (longjmp'ing); a "fast continue" is
* simply an DUK_OP_JUMP.
*/
DUK_DDD(DUK_DDDPRINT("CONTINUE: %ld", (long) bc));
duk_push_uint(ctx, (duk_uint_t) bc);
duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_CONTINUE);
DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* always in executor */
duk_err_longjmp(thr);
DUK_UNREACHABLE();
break;
}
case DUK_EXTRAOP_BNOT: {
duk_uint_fast_t bc = DUK_DEC_BC(ins);
duk__vm_bitwise_not(thr, DUK__REGP(bc), bc);
break;
}
case DUK_EXTRAOP_LNOT: {
duk_uint_fast_t bc = DUK_DEC_BC(ins);
duk_tval *tv1;
tv1 = DUK__REGP(bc);
duk__vm_logical_not(thr, tv1, tv1);
break;
}
case DUK_EXTRAOP_INSTOF: {
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk_small_uint_fast_t c = DUK_DEC_C(ins);
duk_bool_t tmp;
tmp = duk_js_instanceof(thr, DUK__REGP(b), DUK__REGCONSTP(c));
duk_push_boolean(ctx, tmp);
duk_replace(ctx, (duk_idx_t) b);
break;
}
case DUK_EXTRAOP_IN: {
duk_context *ctx = (duk_context *) thr;
duk_small_uint_fast_t b = DUK_DEC_B(ins);
duk_small_uint_fast_t c = DUK_DEC_C(ins);
duk_bool_t tmp;
tmp = duk_js_in(thr, DUK__REGP(b), DUK__REGCONSTP(c));
duk_push_boolean(ctx, tmp);
duk_replace(ctx, (duk_idx_t) b);
break;
}
case DUK_EXTRAOP_LABEL: {
duk_catcher *cat;
duk_uint_fast_t bc = DUK_DEC_BC(ins);
/* allocate catcher and populate it (should be atomic) */
duk_hthread_catchstack_grow(thr);
cat = thr->catchstack + thr->catchstack_top;
thr->catchstack_top++;
cat->flags = DUK_CAT_TYPE_LABEL | (bc << DUK_CAT_LABEL_SHIFT);
cat->callstack_index = thr->callstack_top - 1;
cat->pc_base = act->pc; /* pre-incremented, points to first jump slot */
cat->idx_base = 0; /* unused for label */
cat->h_varname = NULL;
DUK_DDD(DUK_DDDPRINT("LABEL catcher: flags=0x%08lx, callstack_index=%ld, pc_base=%ld, "
"idx_base=%ld, h_varname=%!O, label_id=%ld",
(long) cat->flags, (long) cat->callstack_index, (long) cat->pc_base,
(long) cat->idx_base, (duk_heaphdr *) cat->h_varname, (long) DUK_CAT_GET_LABEL(cat)));
act->pc += 2; /* skip jump slots */
break;
}
case DUK_EXTRAOP_ENDLABEL: {
duk_catcher *cat;
#if defined(DUK_USE_DDDPRINT) || defined(DUK_USE_ASSERTIONS)
duk_uint_fast_t bc = DUK_DEC_BC(ins);
#endif
#if defined(DUK_USE_DDDPRINT)
DUK_DDD(DUK_DDDPRINT("ENDLABEL %ld", (long) bc));
#endif
DUK_ASSERT(thr->catchstack_top >= 1);
cat = thr->catchstack + thr->catchstack_top - 1;
DUK_UNREF(cat);
DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_LABEL);
DUK_ASSERT((duk_uint_fast_t) DUK_CAT_GET_LABEL(cat) == bc);
duk_hthread_catchstack_unwind(thr, thr->catchstack_top - 1);
/* no need to unwind callstack */
break;
}
#ifdef DUK_USE_DEBUG
case DUK_EXTRAOP_DUMPREG: {
DUK_D(DUK_DPRINT("DUMPREG: %ld -> %!T",
@ -4075,11 +4187,6 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
break;
}
case DUK_OP_INVALID: {
DUK_ERROR(thr, DUK_ERR_INTERNAL_ERROR, "INVALID opcode (%ld)", (long) DUK_DEC_ABC(ins));
break;
}
default: {
/* this should never be possible, because the switch-case is
* comprehensive

Loading…
Cancel
Save