[INFER] Compile IFEQX/IFNEX, GOTOX and TABLESWITCHX, bug 678939. r=bhackett
authorJan de Mooij <jandemooij@gmail.com>
Wed, 17 Aug 2011 11:44:02 +0200
changeset 77438 427522c34b31ba8039e5a2304739986f4f9d42c8
parent 77437 74bfd74ca2895c602d8c3f6269b9ef8106b69723
child 77439 aa547ed80bba625a45c620c2757489c263637d32
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs678939
milestone8.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
[INFER] Compile IFEQX/IFNEX, GOTOX and TABLESWITCHX, bug 678939. r=bhackett
js/src/jit-test/tests/jaeger/testIfEqX.js
js/src/jit-test/tests/jaeger/testTableSwitchX.js
js/src/methodjit/Compiler.cpp
js/src/methodjit/FastOps.cpp
js/src/methodjit/StubCalls.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/testIfEqX.js
@@ -0,0 +1,37 @@
+// Tests for IFEQX and GOTOX ops.
+function testIfElse() {
+    var src =
+        "var a = 0;\n" +
+        "if (x) {\n";
+    for (var i=0; i<7000; i++) {
+        src += "a = 1;";
+    }
+    src += "} else {\n";
+    for (var i=0; i<7000; i++) {
+        src += "a = 2;";
+    }
+    src += "}\n";
+    src += "return a;";
+
+    var f = new Function("x", src);
+    assertEq(f(true), 1);
+    assertEq(f(false), 2);
+    assertEq(f([1, 2, 3]), 1);
+    assertEq(f(), 2);
+}
+testIfElse();
+
+function testWhile() {
+    var src =
+        "var i = 0, j = 0;\n" +
+        "while (i++ < 50) {\n";
+    for (var i=0; i<5000; i++) {
+        src += "j = i;";
+    }
+    src += "}\n";
+    src += "return j;";
+
+    var f = new Function(src);
+    assertEq(f(), 50);
+}
+testWhile();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/testTableSwitchX.js
@@ -0,0 +1,25 @@
+// Tests for JSOP_TABLESWITCHX.
+function test1() {
+    var src =
+        "var a = 0;\n" +
+        "switch(x) {\n";
+    for (var i=-1; i<4; i++) {
+        src += (i >= 0) ?
+                "case " + i + ":\n" :
+                "default:\n";
+        for (var j=0; j<1500; j++) {
+            src += "a = " + i + ";";
+        }
+        src += "break;\n";
+    }
+    src += "}\n";
+    src += "return a;";
+
+    var f = new Function("x", src);
+    assertEq(f(0), 0);
+    assertEq(f(4), -1);
+    assertEq(f(), -1);
+    assertEq(f(1.1), -1);
+    assertEq(f(3), 3);
+}
+test1();
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1596,53 +1596,60 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_RETURN)
             if (script->pcCounters)
                 updatePCCounters(PC, &codeStart, &countersUpdated);
             emitReturn(frame.peek(-1));
             fallthrough = false;
           END_CASE(JSOP_RETURN)
 
           BEGIN_CASE(JSOP_GOTO)
+          BEGIN_CASE(JSOP_GOTOX)
           BEGIN_CASE(JSOP_DEFAULT)
           {
             unsigned targetOffset = FollowBranch(cx, script, PC - script->code);
             jsbytecode *target = script->code + targetOffset;
 
             fixDoubleTypes(target);
 
             /*
              * Watch for gotos which are entering a 'for' or 'while' loop.
              * These jump to the loop condition test and are immediately
              * followed by the head of the loop.
              */
-            jsbytecode *next = PC + JSOP_GOTO_LENGTH;
+            jsbytecode *next = PC + js_CodeSpec[op].length;
             if (cx->typeInferenceEnabled() && analysis->maybeCode(next) &&
                 analysis->getCode(next).loopHead) {
                 frame.syncAndForgetEverything();
                 Jump j = masm.jump();
                 if (!startLoop(next, j, target))
                     return Compile_Error;
             } else {
                 if (!frame.syncForBranch(target, Uses(0)))
                     return Compile_Error;
                 Jump j = masm.jump();
                 if (!jumpAndTrace(j, target))
                     return Compile_Error;
             }
             fallthrough = false;
+            PC += js_CodeSpec[op].length;
+            break;
           }
           END_CASE(JSOP_GOTO)
 
           BEGIN_CASE(JSOP_IFEQ)
           BEGIN_CASE(JSOP_IFNE)
+          BEGIN_CASE(JSOP_IFEQX)
+          BEGIN_CASE(JSOP_IFNEX)
           {
-            jsbytecode *target = PC + GET_JUMP_OFFSET(PC);
+            jsbytecode *target = PC + GetJumpOffset(PC, PC);
             fixDoubleTypes(target);
             if (!jsop_ifneq(op, target))
                 return Compile_Error;
+            PC += js_CodeSpec[op].length;
+            break;
           }
           END_CASE(JSOP_IFNE)
 
           BEGIN_CASE(JSOP_ARGUMENTS)
             /*
              * For calls of the form 'f.apply(x, arguments)' we can avoid
              * creating an args object by having ic::SplatApplyArgs pull
              * directly from the stack. To do this, we speculate here that
@@ -2077,16 +2084,17 @@ mjit::Compiler::generateMethod()
             jsbytecode *target = PC + GET_JUMP_OFFSET(PC);
             fixDoubleTypes(target);
             if (!jsop_andor(op, target))
                 return Compile_Error;
           }
           END_CASE(JSOP_AND)
 
           BEGIN_CASE(JSOP_TABLESWITCH)
+          BEGIN_CASE(JSOP_TABLESWITCHX)
             /*
              * Note: there is no need to syncForBranch for the various targets of
              * switch statement. The liveness analysis has already marked these as
              * allocated with no registers in use. There is also no need to fix
              * double types, as we don't track types of slots in scripts with
              * switch statements (could be fixed).
              */
             if (script->pcCounters)
@@ -6738,19 +6746,22 @@ mjit::Compiler::constructThis()
 bool
 mjit::Compiler::jsop_tableswitch(jsbytecode *pc)
 {
 #if defined JS_CPU_ARM
     JS_NOT_REACHED("Implement jump(BaseIndex) for ARM");
     return true;
 #else
     jsbytecode *originalPC = pc;
-
-    uint32 defaultTarget = GET_JUMP_OFFSET(pc);
-    pc += JUMP_OFFSET_LEN;
+    JSOp op = JSOp(*originalPC);
+    JS_ASSERT(op == JSOP_TABLESWITCH || op == JSOP_TABLESWITCHX);
+
+    uint32 defaultTarget = GetJumpOffset(pc, pc);
+    unsigned jumpLength = (op == JSOP_TABLESWITCHX) ? JUMPX_OFFSET_LEN : JUMP_OFFSET_LEN;
+    pc += jumpLength;
 
     jsint low = GET_JUMP_OFFSET(pc);
     pc += JUMP_OFFSET_LEN;
     jsint high = GET_JUMP_OFFSET(pc);
     pc += JUMP_OFFSET_LEN;
     int numJumps = high + 1 - low;
     JS_ASSERT(numJumps >= 0);
 
@@ -6792,22 +6803,22 @@ mjit::Compiler::jsop_tableswitch(jsbytec
         notInt = masm.testInt32(Assembler::NotEqual, frame.addressOf(fe));
 
     JumpTable jt;
     jt.offsetIndex = jumpTableOffsets.length();
     jt.label = masm.moveWithPatch(ImmPtr(NULL), reg);
     jumpTables.append(jt);
 
     for (int i = 0; i < numJumps; i++) {
-        uint32 target = GET_JUMP_OFFSET(pc);
+        uint32 target = GetJumpOffset(originalPC, pc);
         if (!target)
             target = defaultTarget;
         uint32 offset = (originalPC + target) - script->code;
         jumpTableOffsets.append(offset);
-        pc += JUMP_OFFSET_LEN;
+        pc += jumpLength;
     }
     if (low != 0)
         masm.sub32(Imm32(low), dataReg);
     Jump defaultCase = masm.branch32(Assembler::AboveOrEqual, dataReg, Imm32(numJumps));
     BaseIndex jumpTarget(reg, dataReg, Assembler::ScalePtr);
     masm.jump(jumpTarget);
 
     if (notInt.isSet()) {
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -536,16 +536,17 @@ bool
 mjit::Compiler::jsop_relational(JSOp op, BoolStub stub,
                                 jsbytecode *target, JSOp fused)
 {
     FrameEntry *rhs = frame.peek(-1);
     FrameEntry *lhs = frame.peek(-2);
 
     /* The compiler should have handled constant folding. */
     JS_ASSERT(!(rhs->isConstant() && lhs->isConstant()));
+    JS_ASSERT(fused == JSOP_NOP || fused == JSOP_IFEQ || fused == JSOP_IFNE);
 
     /* Always slow path... */
     if ((lhs->isNotType(JSVAL_TYPE_INT32) && lhs->isNotType(JSVAL_TYPE_DOUBLE) &&
          lhs->isNotType(JSVAL_TYPE_STRING)) ||
         (rhs->isNotType(JSVAL_TYPE_INT32) && rhs->isNotType(JSVAL_TYPE_DOUBLE) &&
          rhs->isNotType(JSVAL_TYPE_STRING))) {
         if (op == JSOP_EQ || op == JSOP_NE)
             return jsop_equality(op, stub, target, fused);
@@ -799,20 +800,20 @@ mjit::Compiler::booleanJumpScript(JSOp o
 
     if (!fe->isTypeKnown() && !frame.shouldAvoidTypeRemat(fe))
         type.setReg(frame.copyTypeIntoReg(fe));
     if (!fe->isType(JSVAL_TYPE_DOUBLE))
         data.setReg(frame.copyDataIntoReg(fe));
 
     frame.syncAndForgetEverything();
 
-    Assembler::Condition cond = (op == JSOP_IFNE || op == JSOP_OR)
+    Assembler::Condition cond = (op == JSOP_IFNE || op == JSOP_IFNEX || op == JSOP_OR)
                                 ? Assembler::NonZero
                                 : Assembler::Zero;
-    Assembler::Condition ncond = (op == JSOP_IFNE || op == JSOP_OR)
+    Assembler::Condition ncond = (op == JSOP_IFNE || op == JSOP_IFNEX || op == JSOP_OR)
                                  ? Assembler::Zero
                                  : Assembler::NonZero;
 
     /* Inline path: Boolean guard + call script. */
     MaybeJump jmpNotBool;
     MaybeJump jmpNotExecScript;
     if (type.isSet()) {
         jmpNotBool.setJump(masm.testBoolean(Assembler::NotEqual, type.reg()));
@@ -876,17 +877,17 @@ mjit::Compiler::jsop_ifneq(JSOp op, jsby
 {
     FrameEntry *fe = frame.peek(-1);
 
     if (fe->isConstant()) {
         JSBool b = js_ValueToBoolean(fe->getValue());
 
         frame.pop();
 
-        if (op == JSOP_IFEQ)
+        if (op == JSOP_IFEQ || op == JSOP_IFEQX)
             b = !b;
         if (b) {
             if (!frame.syncForBranch(target, Uses(0)))
                 return false;
             if (!jumpAndTrace(masm.jump(), target))
                 return false;
         } else {
             if (target < PC && !finishLoop(target))
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -2143,18 +2143,23 @@ stubs::LookupSwitch(VMFrame &f, jsbyteco
     return native;
 }
 
 void * JS_FASTCALL
 stubs::TableSwitch(VMFrame &f, jsbytecode *origPc)
 {
     jsbytecode * const originalPC = origPc;
     jsbytecode *pc = originalPC;
-    uint32 jumpOffset = GET_JUMP_OFFSET(pc);
-    pc += JUMP_OFFSET_LEN;
+
+    JSOp op = JSOp(*originalPC);
+    JS_ASSERT(op == JSOP_TABLESWITCH || op == JSOP_TABLESWITCHX);
+
+    uint32 jumpOffset = js::analyze::GetJumpOffset(originalPC, pc);
+    unsigned jumpLength = (op == JSOP_TABLESWITCHX) ? JUMPX_OFFSET_LEN : JUMP_OFFSET_LEN;
+    pc += jumpLength;
 
     /* Note: compiler adjusts the stack beforehand. */
     Value rval = f.regs.sp[-1];
 
     jsint tableIdx;
     if (rval.isInt32()) {
         tableIdx = rval.toInt32();
     } else if (rval.isDouble()) {
@@ -2172,18 +2177,18 @@ stubs::TableSwitch(VMFrame &f, jsbytecod
     {
         jsint low = GET_JUMP_OFFSET(pc);
         pc += JUMP_OFFSET_LEN;
         jsint high = GET_JUMP_OFFSET(pc);
         pc += JUMP_OFFSET_LEN;
 
         tableIdx -= low;
         if ((jsuint) tableIdx < (jsuint)(high - low + 1)) {
-            pc += JUMP_OFFSET_LEN * tableIdx;
-            uint32 candidateOffset = GET_JUMP_OFFSET(pc);
+            pc += jumpLength * tableIdx;
+            uint32 candidateOffset = js::analyze::GetJumpOffset(originalPC, pc);
             if (candidateOffset)
                 jumpOffset = candidateOffset;
         }
     }
 
 finally:
     /* Provide the native address. */
     JSScript* script = f.fp()->script();