Bug 825892 - Remove JSOP_LOOKUPSWITCH. r=bhackett
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 03 Jan 2013 18:35:57 +0100
changeset 126559 c0e7cc40f35648eb8c07a8b4354734a7142a3a34
parent 126558 ae47afdbb7d144d22a381db0f3cad67bb9fba3a3
child 126560 3d51d7f54e5d027854e9533a4a420688198dc3be
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs825892
milestone20.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
Bug 825892 - Remove JSOP_LOOKUPSWITCH. r=bhackett
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsopcode.cpp
js/src/jsopcode.h
js/src/jsopcode.tbl
js/src/methodjit/Compiler.cpp
js/src/methodjit/StubCalls.cpp
js/src/methodjit/StubCalls.h
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/Xdr.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2176,30 +2176,16 @@ EmitNumberOp(JSContext *cx, double dval,
     }
 
     if (!bce->constList.append(DoubleValue(dval)))
         return false;
 
     return EmitIndex32(cx, JSOP_DOUBLE, bce->constList.length() - 1, bce);
 }
 
-/*
- * To avoid bloating all parse nodes for the special case of switch, values are
- * allocated in the temp pool and pointed to by the parse node. These values
- * are not currently recycled (like parse nodes) and the temp pool is only
- * flushed at the end of compiling a script, so these values are technically
- * leaked. This would only be a problem for scripts containing a large number
- * of large switches, which seems unlikely.
- */
-static Value *
-AllocateSwitchConstant(JSContext *cx)
-{
-    return cx->tempLifoAlloc().new_<Value>();
-}
-
 static inline void
 SetJumpOffsetAt(BytecodeEmitter *bce, ptrdiff_t off)
 {
     SET_JUMP_OFFSET(bce->code(off), bce->offset() - off);
 }
 
 /*
  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047.
@@ -2208,17 +2194,17 @@ SetJumpOffsetAt(BytecodeEmitter *bce, pt
  */
 MOZ_NEVER_INLINE static bool
 EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     JSOp switchOp;
     bool hasDefault;
     ptrdiff_t top, off, defaultOffset;
     ParseNode *pn2, *pn3, *pn4;
-    int32_t i, low, high;
+    int32_t low, high;
     int noteIndex;
     size_t switchSize;
     jsbytecode *pc;
     StmtInfoBCE stmtInfo(cx);
 
     /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */
     switchOp = JSOP_TABLESWITCH;
     hasDefault = false;
@@ -2298,67 +2284,42 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
                 caseCount--;    /* one of the "cases" was the default */
                 continue;
             }
 
             JS_ASSERT(pn3->isKind(PNK_CASE));
             if (switchOp == JSOP_CONDSWITCH)
                 continue;
 
+            JS_ASSERT(switchOp == JSOP_TABLESWITCH);
+
             pn4 = pn3->pn_left;
-            Value constVal;
-            switch (pn4->getKind()) {
-              case PNK_NUMBER:
-                constVal.setNumber(pn4->pn_dval);
-                break;
-              case PNK_STRING:
-                constVal.setString(pn4->pn_atom);
-                break;
-              case PNK_TRUE:
-                constVal.setBoolean(true);
-                break;
-              case PNK_FALSE:
-                constVal.setBoolean(false);
-                break;
-              case PNK_NULL:
-                constVal.setNull();
-                break;
-              case PNK_NAME:
-              default:
+
+            if (pn4->getKind() != PNK_NUMBER) {
                 switchOp = JSOP_CONDSWITCH;
                 continue;
             }
-            JS_ASSERT(constVal.isPrimitive());
-
-            pn3->pn_pval = AllocateSwitchConstant(cx);
-            if (!pn3->pn_pval) {
-                ok = false;
-                goto release;
-            }
-
-            *pn3->pn_pval = constVal;
-
-            if (switchOp != JSOP_TABLESWITCH)
-                continue;
-            if (!pn3->pn_pval->isInt32()) {
-                switchOp = JSOP_LOOKUPSWITCH;
+
+            int32_t i;
+            if (!MOZ_DOUBLE_IS_INT32(pn4->pn_dval, &i)) {
+                switchOp = JSOP_CONDSWITCH;
                 continue;
             }
-            i = pn3->pn_pval->toInt32();
+
             if ((unsigned)(i + (int)JS_BIT(15)) >= (unsigned)JS_BIT(16)) {
-                switchOp = JSOP_LOOKUPSWITCH;
+                switchOp = JSOP_CONDSWITCH;
                 continue;
             }
             if (i < low)
                 low = i;
             if (high < i)
                 high = i;
 
             /*
-             * Check for duplicates, which require a JSOP_LOOKUPSWITCH.
+             * Check for duplicates, which require a JSOP_CONDSWITCH.
              * We bias i by 65536 if it's negative, and hope that's a rare
              * case (because it requires a malloc'd bitmap).
              */
             if (i < 0)
                 i += JS_BIT(16);
             if (i >= intmap_bitlen) {
                 if (!intmap &&
                     i < (INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2)) {
@@ -2371,44 +2332,34 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
                     if (!intmap) {
                         JS_ReportOutOfMemory(cx);
                         return false;
                     }
                 }
                 memset(intmap, 0, intmap_bitlen >> JS_BITS_PER_BYTE_LOG2);
             }
             if (JS_TEST_BIT(intmap, i)) {
-                switchOp = JSOP_LOOKUPSWITCH;
+                switchOp = JSOP_CONDSWITCH;
                 continue;
             }
             JS_SET_BIT(intmap, i);
         }
 
-      release:
         if (intmap && intmap != intmap_space)
             js_free(intmap);
         if (!ok)
             return false;
 
         /*
-         * Compute table length and select lookup instead if overlarge or
+         * Compute table length and select condswitch instead if overlarge or
          * more than half-sparse.
          */
         if (switchOp == JSOP_TABLESWITCH) {
             tableLength = (uint32_t)(high - low + 1);
             if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount)
-                switchOp = JSOP_LOOKUPSWITCH;
-        } else if (switchOp == JSOP_LOOKUPSWITCH) {
-            /*
-             * Lookup switch supports only atom indexes below 64K limit.
-             * Conservatively estimate the maximum possible index during
-             * switch generation and use conditional switch if it exceeds
-             * the limit.
-             */
-            if (caseCount + bce->constList.length() > JS_BIT(16))
                 switchOp = JSOP_CONDSWITCH;
         }
     }
 
     /*
      * Emit a note with two offsets: first tells total switch code length,
      * second tells offset to first JSOP_CASE if condswitch.
      */
@@ -2416,29 +2367,21 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
     if (noteIndex < 0)
         return false;
 
     if (switchOp == JSOP_CONDSWITCH) {
         /*
          * 0 bytes of immediate for unoptimized ECMAv2 switch.
          */
         switchSize = 0;
-    } else if (switchOp == JSOP_TABLESWITCH) {
-        /*
-         * 3 offsets (len, low, high) before the table, 1 per entry.
-         */
+    } else {
+        JS_ASSERT(switchOp == JSOP_TABLESWITCH);
+
+        /* 3 offsets (len, low, high) before the table, 1 per entry. */
         switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength));
-    } else {
-        /*
-         * JSOP_LOOKUPSWITCH:
-         * 1 offset (len) and 1 atom index (npairs) before the table,
-         * 1 atom index and 1 jump offset per entry.
-         */
-        switchSize = (size_t)(JUMP_OFFSET_LEN + UINT16_LEN +
-                              (UINT32_INDEX_LEN + JUMP_OFFSET_LEN) * caseCount);
     }
 
     /* Emit switchOp followed by switchSize bytes of jump or lookup table. */
     if (EmitN(cx, bce, switchOp, switchSize) < 0)
         return false;
 
     off = -1;
     if (switchOp == JSOP_CONDSWITCH) {
@@ -2493,48 +2436,49 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
             return false;
         }
 
         /* Emit default even if no explicit default statement. */
         defaultOffset = EmitJump(cx, bce, JSOP_DEFAULT, 0);
         if (defaultOffset < 0)
             return false;
     } else {
+        JS_ASSERT(switchOp == JSOP_TABLESWITCH);
         pc = bce->code(top + JUMP_OFFSET_LEN);
 
-        if (switchOp == JSOP_TABLESWITCH) {
-            /* Fill in switch bounds, which we know fit in 16-bit offsets. */
-            SET_JUMP_OFFSET(pc, low);
-            pc += JUMP_OFFSET_LEN;
-            SET_JUMP_OFFSET(pc, high);
-            pc += JUMP_OFFSET_LEN;
-
-            /*
-             * Use malloc to avoid arena bloat for programs with many switches.
-             * ScopedFreePtr takes care of freeing it on exit.
-             */
-            if (tableLength != 0) {
-                table = cx->pod_calloc<ParseNode*>(tableLength);
-                if (!table)
-                    return false;
-                for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
-                    if (pn3->isKind(PNK_DEFAULT))
-                        continue;
-                    i = pn3->pn_pval->toInt32();
-                    i -= low;
-                    JS_ASSERT((uint32_t)i < tableLength);
-                    table[i] = pn3;
-                }
+        /* Fill in switch bounds, which we know fit in 16-bit offsets. */
+        SET_JUMP_OFFSET(pc, low);
+        pc += JUMP_OFFSET_LEN;
+        SET_JUMP_OFFSET(pc, high);
+        pc += JUMP_OFFSET_LEN;
+
+        /*
+         * Use malloc to avoid arena bloat for programs with many switches.
+         * ScopedFreePtr takes care of freeing it on exit.
+         */
+        if (tableLength != 0) {
+            table = cx->pod_calloc<ParseNode*>(tableLength);
+            if (!table)
+                return false;
+            for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
+                if (pn3->isKind(PNK_DEFAULT))
+                    continue;
+
+                JS_ASSERT(pn3->isKind(PNK_CASE));
+
+                pn4 = pn3->pn_left;
+                JS_ASSERT(pn4->getKind() == PNK_NUMBER);
+
+                int32_t i = int32_t(pn4->pn_dval);
+                JS_ASSERT(double(i) == pn4->pn_dval);
+
+                i -= low;
+                JS_ASSERT(uint32_t(i) < tableLength);
+                table[i] = pn3;
             }
-        } else {
-            JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH);
-
-            /* Fill in the number of cases. */
-            SET_UINT16(pc, caseCount);
-            pc += UINT16_LEN;
         }
     }
 
     /* Emit code for each case's statements, copying pn_offset up to pn3. */
     for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
         if (switchOp == JSOP_CONDSWITCH && !pn3->isKind(PNK_DEFAULT))
             SetJumpOffsetAt(bce, pn3->pn_offset);
         pn4 = pn3->pn_right;
@@ -2569,38 +2513,22 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
     if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, off))
         return false;
 
     if (switchOp == JSOP_TABLESWITCH) {
         /* Skip over the already-initialized switch bounds. */
         pc += 2 * JUMP_OFFSET_LEN;
 
         /* Fill in the jump table, if there is one. */
-        for (i = 0; i < (int)tableLength; i++) {
+        for (uint32_t i = 0; i < tableLength; i++) {
             pn3 = table[i];
             off = pn3 ? pn3->pn_offset - top : 0;
             SET_JUMP_OFFSET(pc, off);
             pc += JUMP_OFFSET_LEN;
         }
-    } else if (switchOp == JSOP_LOOKUPSWITCH) {
-        /* Skip over the already-initialized number of cases. */
-        pc += UINT16_LEN;
-
-        for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
-            if (pn3->isKind(PNK_DEFAULT))
-                continue;
-            if (!bce->constList.append(*pn3->pn_pval))
-                return false;
-            SET_UINT32_INDEX(pc, bce->constList.length() - 1);
-            pc += UINT32_INDEX_LEN;
-
-            off = pn3->pn_offset - top;
-            SET_JUMP_OFFSET(pc, off);
-            pc += JUMP_OFFSET_LEN;
-        }
     }
 
     if (!PopStatementBCE(cx, bce))
         return false;
 
 #if JS_HAS_BLOCK_SCOPE
     if (pn->pn_right->isKind(PNK_LEXICALSCOPE))
         EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObjCount);
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -458,17 +458,16 @@ CloneParseTree(ParseNode *opn, Parser *p
         break;
 
       case PN_BINARY:
         NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, parser));
         if (opn->pn_right != opn->pn_left)
             NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, parser));
         else
             pn->pn_right = pn->pn_left;
-        pn->pn_pval = opn->pn_pval;
         pn->pn_iflags = opn->pn_iflags;
         break;
 
       case PN_UNARY:
         NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, parser));
         pn->pn_hidden = opn->pn_hidden;
         break;
 
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -588,18 +588,17 @@ struct ParseNode {
         struct {                        /* ternary: if, for(;;), ?: */
             ParseNode   *kid1;          /* condition, discriminant, etc. */
             ParseNode   *kid2;          /* then-part, case list, etc. */
             ParseNode   *kid3;          /* else-part, default case, etc. */
         } ternary;
         struct {                        /* two kids if binary */
             ParseNode   *left;
             ParseNode   *right;
-            Value       *pval;          /* switch case value */
-            unsigned       iflags;         /* JSITER_* flags for PNK_FOR node */
+            unsigned    iflags;         /* JSITER_* flags for PNK_FOR node */
         } binary;
         struct {                        /* one kid if unary */
             ParseNode   *kid;
             bool        hidden;         /* hidden genexp-induced JSOP_YIELD
                                            or directive prologue member (as
                                            pn_prologue) */
         } unary;
         struct {                        /* name, labeled statement, etc. */
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -131,30 +131,16 @@ IonBuilder::CFGState::TableSwitch(jsbyte
     state.stopAt = exitpc;
     state.tableswitch.exitpc = exitpc;
     state.tableswitch.breaks = NULL;
     state.tableswitch.ins = ins;
     state.tableswitch.currentBlock = 0;
     return state;
 }
 
-IonBuilder::CFGState
-IonBuilder::CFGState::LookupSwitch(jsbytecode *exitpc)
-{
-    CFGState state;
-    state.state = LOOKUP_SWITCH;
-    state.stopAt = exitpc;
-    state.lookupswitch.exitpc = exitpc;
-    state.lookupswitch.breaks = NULL;
-    state.lookupswitch.bodies =
-        (FixedList<MBasicBlock *> *)GetIonContext()->temp->allocate(sizeof(FixedList<MBasicBlock *>));
-    state.lookupswitch.currentBlock = 0;
-    return state;
-}
-
 JSFunction *
 IonBuilder::getSingleCallTarget(uint32_t argc, jsbytecode *pc)
 {
     AutoAssertNoGC nogc;
 
     types::StackTypeSet *calleeTypes = oracle->getCallTarget(script(), argc, pc);
     if (!calleeTypes)
         return NULL;
@@ -754,19 +740,16 @@ IonBuilder::snoopControlFlow(JSOp op)
             break;
         }
         break;
       }
 
       case JSOP_TABLESWITCH:
         return tableSwitch(op, info().getNote(cx, pc));
 
-      case JSOP_LOOKUPSWITCH:
-        return lookupSwitch(op, info().getNote(cx, pc));
-
       case JSOP_IFNE:
         // We should never reach an IFNE, it's a stopAt point, which will
         // trigger closing the loop.
         JS_NOT_REACHED("we should never reach an ifne!");
         return ControlStatus_Error;
 
       default:
         break;
@@ -1204,19 +1187,16 @@ IonBuilder::processCfgEntry(CFGState &st
         return processForBodyEnd(state);
 
       case CFGState::FOR_LOOP_UPDATE:
         return processForUpdateEnd(state);
 
       case CFGState::TABLE_SWITCH:
         return processNextTableSwitchCase(state);
 
-      case CFGState::LOOKUP_SWITCH:
-        return processNextLookupSwitchCase(state);
-
       case CFGState::COND_SWITCH_CASE:
         return processCondSwitchCase(state);
 
       case CFGState::COND_SWITCH_BODY:
         return processCondSwitchBody(state);
 
       case CFGState::AND_OR:
         return processAndOrEnd(state);
@@ -1629,56 +1609,16 @@ IonBuilder::processNextTableSwitchCase(C
         state.stopAt = state.tableswitch.exitpc;
 
     current = successor;
     pc = current->pc();
     return ControlStatus_Jumped;
 }
 
 IonBuilder::ControlStatus
-IonBuilder::processNextLookupSwitchCase(CFGState &state)
-{
-    JS_ASSERT(state.state == CFGState::LOOKUP_SWITCH);
-
-    size_t curBlock = state.lookupswitch.currentBlock;
-    IonSpew(IonSpew_MIR, "processNextLookupSwitchCase curBlock=%d", curBlock);
-
-    state.lookupswitch.currentBlock = ++curBlock;
-
-    // Test if there are still unprocessed successors (cases/default)
-    if (curBlock >= state.lookupswitch.bodies->length())
-        return processSwitchEnd(state.lookupswitch.breaks, state.lookupswitch.exitpc);
-
-    // Get the next successor
-    MBasicBlock *successor = (*state.lookupswitch.bodies)[curBlock];
-
-    // Add current block as predecessor if available.
-    // This means the previous case didn't have a break statement.
-    // So flow will continue in this block.
-    if (current) {
-        current->end(MGoto::New(successor));
-        successor->addPredecessor(current);
-    }
-
-    // Move next body block to end to maintain RPO.
-    graph().moveBlockToEnd(successor);
-
-    // If this is the last successor the block should stop at the end of the lookupswitch
-    // Else it should stop at the start of the next successor
-    if (curBlock + 1 < state.lookupswitch.bodies->length())
-        state.stopAt = (*state.lookupswitch.bodies)[curBlock + 1]->pc();
-    else
-        state.stopAt = state.lookupswitch.exitpc;
-
-    current = successor;
-    pc = current->pc();
-    return ControlStatus_Jumped;
-}
-
-IonBuilder::ControlStatus
 IonBuilder::processAndOrEnd(CFGState &state)
 {
     // We just processed the RHS of an && or || expression.
     // Now jump to the join point (the false block).
     current->end(MGoto::New(state.branch.ifFalse));
 
     if (!state.branch.ifFalse->addPredecessor(current))
         return ControlStatus_Error;
@@ -1792,19 +1732,16 @@ IonBuilder::processSwitchBreak(JSOp op, 
     JS_ASSERT(found);
     CFGState &state = *found;
 
     DeferredEdge **breaks = NULL;
     switch (state.state) {
       case CFGState::TABLE_SWITCH:
         breaks = &state.tableswitch.breaks;
         break;
-      case CFGState::LOOKUP_SWITCH:
-        breaks = &state.lookupswitch.breaks;
-        break;
       case CFGState::COND_SWITCH_BODY:
         breaks = &state.condswitch.breaks;
         break;
       default:
         JS_NOT_REACHED("Unexpected switch state.");
         return ControlStatus_Error;
     }
 
@@ -2189,17 +2126,17 @@ IonBuilder::tableSwitch(JSOp op, jssrcno
         JS_ASSERT(casepc >= pc && casepc <= exitpc);
 
         MBasicBlock *caseblock = newBlock(current, casepc);
         if (!caseblock)
             return ControlStatus_Error;
 
         // If the casepc equals the current pc, it is not a written case,
         // but a filled gap. That way we can use a tableswitch instead of
-        // lookupswitch, even if not all numbers are consecutive.
+        // condswitch, even if not all numbers are consecutive.
         // In that case this block goes to the default case
         if (casepc == pc) {
             caseblock->end(MGoto::New(defaultcase));
             defaultcase->addPredecessor(caseblock);
         }
 
         tableswitch->addCase(caseblock);
 
@@ -2240,217 +2177,16 @@ IonBuilder::tableSwitch(JSOp op, jssrcno
 
     if (!cfgStack_.append(state))
         return ControlStatus_Error;
 
     pc = current->pc();
     return ControlStatus_Jumped;
 }
 
-IonBuilder::ControlStatus
-IonBuilder::lookupSwitch(JSOp op, jssrcnote *sn)
-{
-    // LookupSwitch op looks as follows:
-    // DEFAULT  : JUMP_OFFSET           # jump offset (exitpc if no default block)
-    // NCASES   : UINT16                # number of cases
-    // CONST_1  : UINT32_INDEX          # case 1 constant index
-    // OFFSET_1 : JUMP_OFFSET           # case 1 offset
-    // ...
-    // CONST_N  : UINT32_INDEX          # case N constant index
-    // OFFSET_N : JUMP_OFFSET           # case N offset
-
-    // A sketch of some of the design decisions on this code.
-    //
-    // 1. The bodies of case expressions may be shared, e.g.:
-    //   case FOO:
-    //   case BAR:
-    //     /* code */
-    //   case BAZ:
-    //     /* code */
-    //  In this cases we want to build a single codeblock for the conditionals (e.g. for FOO and BAR).
-    //
-    // 2. The ending MTest can only be added to a conditional block once the next conditional
-    //    block has been created, and ending MTest on the final conditional block can only be
-    //    added after the default body block has been created.
-    //
-    //    For the above two reasons, the loop keeps track of the previous iteration's major
-    //    components (cond block, body block, cmp instruction, body start pc, whether the
-    //    previous case had a shared body, etc.) and uses them in the next iteration.
-    //
-    // 3. The default body block may be shared with the body of a 'case'.  This is tested for
-    //    within the iteration loop in IonBuilder::lookupSwitch.  Also, the default body block
-    //    may not occur at the end of the switch statements, and instead may occur in between.
-    //
-    //    For this reason, the default body may be created within the loop (when a regular body
-    //    block is created, because the default body IS the regular body), or it will be created
-    //    after the loop.  It must then still be inserted into the right location into the list
-    //    of body blocks to process, which is done later in lookupSwitch.
-
-    JS_ASSERT(op == JSOP_LOOKUPSWITCH);
-
-    // Pop input.
-    MDefinition *ins = current->pop();
-
-    // Get the default and exit pc
-    jsbytecode *exitpc = pc + js_GetSrcNoteOffset(sn, 0);
-    jsbytecode *defaultpc = pc + GET_JUMP_OFFSET(pc);
-
-    JS_ASSERT(defaultpc > pc && defaultpc <= exitpc);
-
-    // Get ncases, which will be >= 1, since a zero-case switch
-    // will get byte-compiled into a TABLESWITCH.
-    jsbytecode *pc2 = pc;
-    pc2 += JUMP_OFFSET_LEN;
-    unsigned int ncases = GET_UINT16(pc2);
-    pc2 += UINT16_LEN;
-    JS_ASSERT(ncases >= 1);
-
-    // Vector of body blocks.
-    Vector<MBasicBlock*, 0, IonAllocPolicy> bodyBlocks;
-
-    MBasicBlock *defaultBody = NULL;
-    unsigned int defaultIdx = UINT_MAX;
-    bool defaultShared = false;
-
-    MBasicBlock *prevCond = NULL;
-    MCompare *prevCmpIns = NULL;
-    MBasicBlock *prevBody = NULL;
-    bool prevShared = false;
-    jsbytecode *prevpc = NULL;
-    for (unsigned int i = 0; i < ncases; i++) {
-        Value rval = script()->getConst(GET_UINT32_INDEX(pc2));
-        pc2 += UINT32_INDEX_LEN;
-        jsbytecode *casepc = pc + GET_JUMP_OFFSET(pc2);
-        pc2 += JUMP_OFFSET_LEN;
-        JS_ASSERT(casepc > pc && casepc <= exitpc);
-        JS_ASSERT_IF(i > 0, prevpc <= casepc);
-
-        // Create case block
-        MBasicBlock *cond = newBlock(((i == 0) ? current : prevCond), casepc);
-        if (!cond)
-            return ControlStatus_Error;
-
-        MConstant *rvalIns = MConstant::New(rval);
-        cond->add(rvalIns);
-
-        MCompare *cmpIns = MCompare::New(ins, rvalIns, JSOP_STRICTEQ);
-        cond->add(cmpIns);
-        if (cmpIns->isEffectful() && !resumeAfter(cmpIns))
-            return ControlStatus_Error;
-
-        // Create or pull forward body block
-        MBasicBlock *body;
-        if (prevpc == casepc) {
-            body = prevBody;
-        } else {
-            body = newBlock(cond, casepc);
-            if (!body)
-                return ControlStatus_Error;
-            bodyBlocks.append(body);
-        }
-
-        // Check for default body
-        if (defaultpc <= casepc && defaultIdx == UINT_MAX) {
-            defaultIdx = bodyBlocks.length() - 1;
-            if (defaultpc == casepc) {
-                defaultBody = body;
-                defaultShared = true;
-            }
-        }
-
-        // Go back and fill in the MTest for the previous case block, or add the MGoto
-        // to the current block
-        if (i == 0) {
-            // prevCond is definitely NULL, end 'current' with MGoto to this case.
-            current->end(MGoto::New(cond));
-        } else {
-            // End previous conditional block with an MTest.
-            MTest *test = MTest::New(prevCmpIns, prevBody, cond);
-            prevCond->end(test);
-
-            // If the previous cond shared its body with a prior cond, then
-            // add the previous cond as a predecessor to its body (since it's
-            // now finished).
-            if (prevShared)
-                prevBody->addPredecessor(prevCond);
-        }
-
-        // Save the current cond block, compare ins, and body block for next iteration
-        prevCond = cond;
-        prevCmpIns = cmpIns;
-        prevBody = body;
-        prevShared = (prevpc == casepc);
-        prevpc = casepc;
-    }
-
-    // Create a new default body block if one was not already created.
-    if (!defaultBody) {
-        JS_ASSERT(!defaultShared);
-        defaultBody = newBlock(prevCond, defaultpc);
-        if (!defaultBody)
-            return ControlStatus_Error;
-
-        if (defaultIdx >= bodyBlocks.length())
-            bodyBlocks.append(defaultBody);
-        else
-            bodyBlocks.insert(&bodyBlocks[defaultIdx], defaultBody);
-    }
-
-    // Add edge from last conditional block to the default block
-    if (defaultBody == prevBody) {
-        // Last conditional block goes to default body on both comparison
-        // success and comparison failure.
-        prevCond->end(MGoto::New(defaultBody));
-    } else {
-        // Last conditional block has body that is distinct from
-        // the default block.
-        MTest *test = MTest::New(prevCmpIns, prevBody, defaultBody);
-        prevCond->end(test);
-
-        // Add the cond as a predecessor as a default, but only if
-        // the default is shared with another block, because otherwise
-        // the default block would have been constructed with the final
-        // cond as its predecessor anyway.
-        if (defaultShared)
-            defaultBody->addPredecessor(prevCond);
-    }
-
-    // If the last cond shared its body with a prior cond, then
-    // it needs to be explicitly added as a predecessor now that it's finished.
-    if (prevShared)
-        prevBody->addPredecessor(prevCond);
-
-    // Create CFGState
-    CFGState state = CFGState::LookupSwitch(exitpc);
-    if (!state.lookupswitch.bodies || !state.lookupswitch.bodies->init(bodyBlocks.length()))
-        return ControlStatus_Error;
-
-    // Fill bodies in CFGState using bodies in bodyBlocks, move them to
-    // end in order in order to maintain RPO
-    for (size_t i = 0; i < bodyBlocks.length(); i++) {
-        (*state.lookupswitch.bodies)[i] = bodyBlocks[i];
-    }
-    graph().moveBlockToEnd(bodyBlocks[0]);
-
-    // Create control flow info
-    ControlFlowInfo switchinfo(cfgStack_.length(), exitpc);
-    if (!switches_.append(switchinfo))
-        return ControlStatus_Error;
-
-    // If there is more than one block, next stopAt is at beginning of second block.
-    if (state.lookupswitch.bodies->length() > 1)
-        state.stopAt = (*state.lookupswitch.bodies)[1]->pc();
-    if (!cfgStack_.append(state))
-        return ControlStatus_Error;
-
-    current = (*state.lookupswitch.bodies)[0];
-    pc = current->pc();
-    return ControlStatus_Jumped;
-}
-
 bool
 IonBuilder::jsop_condswitch()
 {
     // CondSwitch op looks as follows:
     //   condswitch [length +exit_pc; first case offset +next-case ]
     //   {
     //     {
     //       ... any code ...
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -65,17 +65,16 @@ class IonBuilder : public MIRGenerator
             DO_WHILE_LOOP_BODY, // do { x } while ()
             DO_WHILE_LOOP_COND, // do { } while (x)
             WHILE_LOOP_COND,    // while (x) { }
             WHILE_LOOP_BODY,    // while () { x }
             FOR_LOOP_COND,      // for (; x;) { }
             FOR_LOOP_BODY,      // for (; ;) { x }
             FOR_LOOP_UPDATE,    // for (; ; x) { }
             TABLE_SWITCH,       // switch() { x }
-            LOOKUP_SWITCH,      // switch() { x }
             COND_SWITCH_CASE,   // switch() { case X: ... }
             COND_SWITCH_BODY,   // switch() { case ...: X }
             AND_OR              // && x, || x
         };
 
         State state;            // Current state of this control structure.
         jsbytecode *stopAt;     // Bytecode at which to stop the processing loop.
 
@@ -119,29 +118,16 @@ class IonBuilder : public MIRGenerator
                 // MIR instruction
                 MTableSwitch *ins;
 
                 // The number of current successor that get mapped into a block. 
                 uint32_t currentBlock;
 
             } tableswitch;
             struct {
-                // pc immediately after the switch.
-                jsbytecode *exitpc;
-
-                // Deferred break and continue targets.
-                DeferredEdge *breaks;
-
-                // Vector of body blocks to process
-                FixedList<MBasicBlock *> *bodies;
-
-                // The number of current successor that get mapped into a block. 
-                uint32_t currentBlock;
-            } lookupswitch;
-            struct {
                 // Vector of body blocks to process after the cases.
                 FixedList<MBasicBlock *> *bodies;
 
                 // When processing case statements, this counter points at the
                 // last uninitialized body.  When processing bodies, this
                 // counter targets the next body to process.
                 uint32_t currentIdx;
 
@@ -169,17 +155,16 @@ class IonBuilder : public MIRGenerator
                 return false;
             }
         }
 
         static CFGState If(jsbytecode *join, MBasicBlock *ifFalse);
         static CFGState IfElse(jsbytecode *trueEnd, jsbytecode *falseEnd, MBasicBlock *ifFalse);
         static CFGState AndOr(jsbytecode *join, MBasicBlock *joinStart);
         static CFGState TableSwitch(jsbytecode *exitpc, MTableSwitch *ins);
-        static CFGState LookupSwitch(jsbytecode *exitpc);
         static CFGState CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget);
     };
 
     static int CmpSuccessors(const void *a, const void *b);
 
   public:
     IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
                TypeOracle *oracle, CompileInfo *info, size_t inliningDepth = 0, uint32_t loopDepth = 0);
@@ -218,17 +203,16 @@ class IonBuilder : public MIRGenerator
     ControlStatus processDoWhileBodyEnd(CFGState &state);
     ControlStatus processDoWhileCondEnd(CFGState &state);
     ControlStatus processWhileCondEnd(CFGState &state);
     ControlStatus processWhileBodyEnd(CFGState &state);
     ControlStatus processForCondEnd(CFGState &state);
     ControlStatus processForBodyEnd(CFGState &state);
     ControlStatus processForUpdateEnd(CFGState &state);
     ControlStatus processNextTableSwitchCase(CFGState &state);
-    ControlStatus processNextLookupSwitchCase(CFGState &state);
     ControlStatus processCondSwitchCase(CFGState &state);
     ControlStatus processCondSwitchBody(CFGState &state);
     ControlStatus processSwitchBreak(JSOp op, jssrcnote *sn);
     ControlStatus processSwitchEnd(DeferredEdge *breaks, jsbytecode *exitpc);
     ControlStatus processAndOrEnd(CFGState &state);
     ControlStatus processReturn(JSOp op);
     ControlStatus processThrow();
     ControlStatus processContinue(JSOp op, jssrcnote *sn);
@@ -266,17 +250,16 @@ class IonBuilder : public MIRGenerator
     ControlStatus finishLoop(CFGState &state, MBasicBlock *successor);
 
     void assertValidLoopHeadOp(jsbytecode *pc);
 
     ControlStatus forLoop(JSOp op, jssrcnote *sn);
     ControlStatus whileOrForInLoop(JSOp op, jssrcnote *sn);
     ControlStatus doWhileLoop(JSOp op, jssrcnote *sn);
     ControlStatus tableSwitch(JSOp op, jssrcnote *sn);
-    ControlStatus lookupSwitch(JSOp op, jssrcnote *sn);
     ControlStatus condSwitch(JSOp op, jssrcnote *sn);
 
     // Please see the Big Honkin' Comment about how resume points work in
     // IonBuilder.cpp, near the definition for this function.
     bool resume(MInstruction *ins, jsbytecode *pc, MResumePoint::Mode mode);
     bool resumeAt(MInstruction *ins, jsbytecode *pc);
     bool resumeAfter(MInstruction *ins);
     bool maybeInsertResume();
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -383,39 +383,16 @@ ScriptAnalysis::analyzeBytecode(JSContex
                         return;
                 }
                 getCode(targetOffset).safePoint = true;
                 pc2 += JUMP_OFFSET_LEN;
             }
             break;
           }
 
-          case JSOP_LOOKUPSWITCH: {
-            isJaegerInlineable = isIonInlineable = false;
-            unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
-            jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
-            unsigned npairs = GET_UINT16(pc2);
-            pc2 += UINT16_LEN;
-
-            if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
-                return;
-            getCode(defaultOffset).safePoint = true;
-
-            while (npairs) {
-                pc2 += UINT32_INDEX_LEN;
-                unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
-                if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
-                    return;
-                getCode(targetOffset).safePoint = true;
-                pc2 += JUMP_OFFSET_LEN;
-                npairs--;
-            }
-            break;
-          }
-
           case JSOP_TRY: {
             /*
              * Everything between a try and corresponding catch or finally is conditional.
              * Note that there is no problem with code which is skipped by a thrown
              * exception but is not caught by a later handler in the same function:
              * no more code will execute, and it does not matter what is defined.
              */
             isJaegerInlineable = isIonInlineable = false;
@@ -785,17 +762,16 @@ ScriptAnalysis::analyzeLifetimes(JSConte
           case JSOP_SETARG:
           case JSOP_SETLOCAL: {
             uint32_t slot = GetBytecodeSlot(script_, pc);
             if (!slotEscapes(slot))
                 killVariable(cx, lifetimes[slot], offset, saved, savedCount);
             break;
           }
 
-          case JSOP_LOOKUPSWITCH:
           case JSOP_TABLESWITCH:
             /* Restore all saved variables. :FIXME: maybe do this precisely. */
             for (unsigned i = 0; i < savedCount; i++) {
                 LifetimeVariable &var = *saved[i];
                 var.lifetime = alloc.new_<Lifetime>(offset, var.savedEnd, var.saved);
                 if (!var.lifetime) {
                     js_free(saved);
                     setOOM(cx);
@@ -1519,34 +1495,16 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
                     checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth);
                 pc2 += JUMP_OFFSET_LEN;
             }
 
             checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth);
             break;
           }
 
-          case JSOP_LOOKUPSWITCH: {
-            unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
-            jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
-            unsigned npairs = GET_UINT16(pc2);
-            pc2 += UINT16_LEN;
-
-            while (npairs) {
-                pc2 += UINT32_INDEX_LEN;
-                unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
-                checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth);
-                pc2 += JUMP_OFFSET_LEN;
-                npairs--;
-            }
-
-            checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth);
-            break;
-          }
-
           case JSOP_TRY: {
             JSTryNote *tn = script_->trynotes()->vector;
             JSTryNote *tnlimit = tn + script_->trynotes()->length;
             for (; tn < tnlimit; tn++) {
                 unsigned startOffset = script_->mainOffset + tn->start;
                 if (startOffset == offset + 1) {
                     unsigned catchOffset = startOffset + tn->length;
 
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -242,17 +242,16 @@ BytecodeNoFallThrough(JSOp op)
     switch (op) {
       case JSOP_GOTO:
       case JSOP_DEFAULT:
       case JSOP_RETURN:
       case JSOP_STOP:
       case JSOP_RETRVAL:
       case JSOP_THROW:
       case JSOP_TABLESWITCH:
-      case JSOP_LOOKUPSWITCH:
       case JSOP_FILTER:
         return true;
       case JSOP_GOSUB:
         /* These fall through indirectly, after executing a 'finally'. */
         return false;
       default:
         return false;
     }
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3760,17 +3760,16 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_POPN:
       case JSOP_STARTXML:
       case JSOP_STARTXMLEXPR:
       case JSOP_DEFXMLNS:
       case JSOP_POPV:
       case JSOP_DEBUGGER:
       case JSOP_SETCALL:
       case JSOP_TABLESWITCH:
-      case JSOP_LOOKUPSWITCH:
       case JSOP_TRY:
       case JSOP_LABEL:
         break;
 
         /* Bytecodes pushing values of known type. */
       case JSOP_VOID:
       case JSOP_UNDEFINED:
         pushed[0].addType(cx, Type::UndefinedType());
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1341,16 +1341,17 @@ js::Interpret(JSContext *cx, StackFrame 
 
         switchMask = moreInterrupts ? -1 : 0;
         switchOp = int(op);
         goto do_switch;
     }
 
 /* No-ops for ease of decompilation. */
 ADD_EMPTY_CASE(JSOP_NOP)
+ADD_EMPTY_CASE(JSOP_UNUSED0)
 ADD_EMPTY_CASE(JSOP_UNUSED1)
 ADD_EMPTY_CASE(JSOP_UNUSED2)
 ADD_EMPTY_CASE(JSOP_UNUSED3)
 ADD_EMPTY_CASE(JSOP_UNUSED12)
 ADD_EMPTY_CASE(JSOP_UNUSED13)
 ADD_EMPTY_CASE(JSOP_UNUSED17)
 ADD_EMPTY_CASE(JSOP_UNUSED18)
 ADD_EMPTY_CASE(JSOP_UNUSED19)
@@ -2605,83 +2606,16 @@ BEGIN_CASE(JSOP_TABLESWITCH)
         int32_t off = (int32_t) GET_JUMP_OFFSET(pc2);
         if (off)
             len = off;
     }
 }
 END_VARLEN_CASE
 }
 
-{
-BEGIN_CASE(JSOP_LOOKUPSWITCH)
-{
-    int32_t off;
-    off = JUMP_OFFSET_LEN;
-
-    /*
-     * JSOP_LOOKUPSWITCH are never used if any atom index in it would exceed
-     * 64K limit.
-     */
-    jsbytecode *pc2 = regs.pc;
-
-    Value lval = regs.sp[-1];
-    regs.sp--;
-
-    int npairs;
-    if (!lval.isPrimitive())
-        goto end_lookup_switch;
-
-    pc2 += off;
-    npairs = GET_UINT16(pc2);
-    pc2 += UINT16_LEN;
-    JS_ASSERT(npairs);  /* empty switch uses JSOP_TABLESWITCH */
-
-    bool match;
-#define SEARCH_PAIRS(MATCH_CODE)                                              \
-    for (;;) {                                                                \
-        Value rval = script->getConst(GET_UINT32_INDEX(pc2));                 \
-        MATCH_CODE                                                            \
-        pc2 += UINT32_INDEX_LEN;                                              \
-        if (match)                                                            \
-            break;                                                            \
-        pc2 += off;                                                           \
-        if (--npairs == 0) {                                                  \
-            pc2 = regs.pc;                                                    \
-            break;                                                            \
-        }                                                                     \
-    }
-
-    if (lval.isString()) {
-        JSLinearString *str = lval.toString()->ensureLinear(cx);
-        if (!str)
-            goto error;
-        JSLinearString *str2;
-        SEARCH_PAIRS(
-            match = (rval.isString() &&
-                     ((str2 = &rval.toString()->asLinear()) == str ||
-                      EqualStrings(str2, str)));
-        )
-    } else if (lval.isNumber()) {
-        double ldbl = lval.toNumber();
-        SEARCH_PAIRS(
-            match = rval.isNumber() && ldbl == rval.toNumber();
-        )
-    } else {
-        SEARCH_PAIRS(
-            match = (lval == rval);
-        )
-    }
-#undef SEARCH_PAIRS
-
-  end_lookup_switch:
-    len = GET_JUMP_OFFSET(pc2);
-}
-END_VARLEN_CASE
-}
-
 BEGIN_CASE(JSOP_ARGUMENTS)
     JS_ASSERT(!regs.fp()->fun()->hasRest());
     if (script->needsArgsObj()) {
         ArgumentsObject *obj = ArgumentsObject::createExpected(cx, regs.fp());
         if (!obj)
             goto error;
         PUSH_COPY(ObjectValue(*obj));
     } else {
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -115,37 +115,31 @@ static bool
 Dup(const char *chars, DupBuffer *cb)
 {
     return cb->append(chars, strlen(chars) + 1);
 }
 
 size_t
 js_GetVariableBytecodeLength(jsbytecode *pc)
 {
-    unsigned ncases;
-    int32_t low, high;
-
     JSOp op = JSOp(*pc);
     JS_ASSERT(js_CodeSpec[op].length == -1);
     switch (op) {
-      case JSOP_TABLESWITCH:
+      case JSOP_TABLESWITCH: {
         /* Structure: default-jump case-low case-high case1-jump ... */
         pc += JUMP_OFFSET_LEN;
-        low = GET_JUMP_OFFSET(pc);
+        int32_t low = GET_JUMP_OFFSET(pc);
         pc += JUMP_OFFSET_LEN;
-        high = GET_JUMP_OFFSET(pc);
-        ncases = (unsigned)(high - low + 1);
+        int32_t high = GET_JUMP_OFFSET(pc);
+        unsigned ncases = unsigned(high - low + 1);
         return 1 + 3 * JUMP_OFFSET_LEN + ncases * JUMP_OFFSET_LEN;
-
+      }
       default:
-        /* Structure: default-jump case-count (case1-value case1-jump) ... */
-        JS_ASSERT(op == JSOP_LOOKUPSWITCH);
-        pc += JUMP_OFFSET_LEN;
-        ncases = GET_UINT16(pc);
-        return 1 + JUMP_OFFSET_LEN + UINT16_LEN + ncases * (UINT32_INDEX_LEN + JUMP_OFFSET_LEN);
+        JS_NOT_REACHED("Unexpected op");
+        return 0;
     }
 }
 
 static uint32_t
 NumBlockSlots(JSScript *script, jsbytecode *pc)
 {
     JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1);
     JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH);
@@ -657,41 +651,16 @@ js_Disassemble1(JSContext *cx, HandleScr
             off = GET_JUMP_OFFSET(pc2);
             Sprint(sp, "\n\t%d: %d", i, int(off));
             pc2 += JUMP_OFFSET_LEN;
         }
         len = 1 + pc2 - pc;
         break;
       }
 
-      case JOF_LOOKUPSWITCH:
-      {
-        jsatomid npairs;
-
-        ptrdiff_t off = GET_JUMP_OFFSET(pc);
-        jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
-        npairs = GET_UINT16(pc2);
-        pc2 += UINT16_LEN;
-        Sprint(sp, " offset %d npairs %u", int(off), unsigned(npairs));
-        while (npairs) {
-            uint32_t constIndex = GET_UINT32_INDEX(pc2);
-            pc2 += UINT32_INDEX_LEN;
-            off = GET_JUMP_OFFSET(pc2);
-            pc2 += JUMP_OFFSET_LEN;
-
-            JSAutoByteString bytes;
-            if (!ToDisassemblySource(cx, script->getConst(constIndex), &bytes))
-                return 0;
-            Sprint(sp, "\n\t%s: %d", bytes.ptr(), int(off));
-            npairs--;
-        }
-        len = 1 + pc2 - pc;
-        break;
-      }
-
       case JOF_QARG:
         Sprint(sp, " %u", GET_ARGNO(pc));
         break;
 
       case JOF_LOCAL:
         Sprint(sp, " %u", GET_SLOTNO(pc));
         break;
 
@@ -3623,18 +3592,17 @@ Decompile(SprintStack *ss, jsbytecode *p
 
                 LOCAL_ASSERT(js_GetSrcNote(cx, jp->script, pc) == NULL);
                 LOCAL_ASSERT(ss->top - 1 == blockObj.stackDepth() + blockObj.slotCount());
                 jsbytecode *nextpc = pc + JSOP_ENTERLET1_LENGTH;
                 if (*nextpc == JSOP_GOTO) {
                     LOCAL_ASSERT(SN_TYPE(js_GetSrcNote(cx, jp->script, nextpc)) == SRC_FOR_IN);
                 } else {
                     LOCAL_ASSERT(*nextpc == JSOP_CONDSWITCH ||
-                                 *nextpc == JSOP_TABLESWITCH ||
-                                 *nextpc == JSOP_LOOKUPSWITCH);
+                                 *nextpc == JSOP_TABLESWITCH);
                 }
 
                 DupBuffer rhs(cx);
                 if (!Dup(PopStr(ss, JSOP_NOP), &rhs))
                     return NULL;
                 if (!AssignBlockNamesToPushedSlots(ss, atoms))
                     return NULL;
                 if (!PushStr(ss, rhs.begin(), op))
@@ -5051,58 +5019,16 @@ Decompile(SprintStack *ss, jsbytecode *p
                     ok = DecompileSwitch(ss, table, (unsigned)j, pc, len, off, false);
                 js_free(table);
                 if (!ok)
                     return NULL;
                 todo = -2;
                 break;
               }
 
-              case JSOP_LOOKUPSWITCH:
-              {
-                ptrdiff_t off, off2;
-                jsatomid npairs, k;
-                TableEntry *table;
-
-                sn = js_GetSrcNote(cx, jp->script, pc);
-                LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
-                len = js_GetSrcNoteOffset(sn, 0);
-                off = GET_JUMP_OFFSET(pc);
-                pc2 = pc + JUMP_OFFSET_LEN;
-                npairs = GET_UINT16(pc2);
-                pc2 += UINT16_LEN;
-
-                table = cx->pod_malloc<TableEntry>(npairs);
-                if (!table)
-                    return NULL;
-                for (k = 0; k < npairs; k++) {
-                    sn = js_GetSrcNote(cx, jp->script, pc2);
-                    if (sn) {
-                        LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
-                        GET_SOURCE_NOTE_ATOM(sn, table[k].label);
-                    } else {
-                        table[k].label = NULL;
-                    }
-                    uint32_t constIndex = GET_UINT32_INDEX(pc2);
-                    pc2 += UINT32_INDEX_LEN;
-                    off2 = GET_JUMP_OFFSET(pc2);
-                    pc2 += JUMP_OFFSET_LEN;
-                    table[k].key = jp->script->getConst(constIndex);
-                    table[k].offset = off2;
-                }
-
-                ok = DecompileSwitch(ss, table, (unsigned)npairs, pc, len, off,
-                                     JS_FALSE);
-                js_free(table);
-                if (!ok)
-                    return NULL;
-                todo = -2;
-                break;
-              }
-
               case JSOP_CONDSWITCH:
               {
                 ptrdiff_t off, off2, caseOff;
                 int ncases;
                 TableEntry *table;
 
                 sn = js_GetSrcNote(cx, jp->script, pc);
                 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -39,17 +39,17 @@ typedef enum JSOp {
 /*
  * JS bytecode formats.
  */
 #define JOF_BYTE          0       /* single bytecode, no immediates */
 #define JOF_JUMP          1       /* signed 16-bit jump offset immediate */
 #define JOF_ATOM          2       /* unsigned 16-bit constant index */
 #define JOF_UINT16        3       /* unsigned 16-bit immediate operand */
 #define JOF_TABLESWITCH   4       /* table switch */
-#define JOF_LOOKUPSWITCH  5       /* lookup switch */
+/* 5 is unused */
 #define JOF_QARG          6       /* quickened get/set function argument ops */
 #define JOF_LOCAL         7       /* var or block-local variable */
 #define JOF_DOUBLE        8       /* uint32_t index for double value */
 #define JOF_UINT24        12      /* extended unsigned 24-bit literal (index) */
 #define JOF_UINT8         13      /* uint8_t immediate, e.g. top 8 bits of 24-bit
                                      atom index */
 #define JOF_INT32         14      /* int32_t immediate operand */
 #define JOF_OBJECT        15      /* unsigned 16-bit object index */
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -155,17 +155,17 @@ OPDEF(JSOP_NULL,      64, js_null_str,  
 OPDEF(JSOP_THIS,      65, js_this_str,  js_this_str,  1,  0,  1, 19,  JOF_BYTE)
 OPDEF(JSOP_FALSE,     66, js_false_str, js_false_str, 1,  0,  1, 19,  JOF_BYTE)
 OPDEF(JSOP_TRUE,      67, js_true_str,  js_true_str,  1,  0,  1, 19,  JOF_BYTE)
 OPDEF(JSOP_OR,        68, "or",         NULL,         5,  1,  1,  5,  JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC)
 OPDEF(JSOP_AND,       69, "and",        NULL,         5,  1,  1,  6,  JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC)
 
 /* The switch bytecodes have variable length. */
 OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL,     -1,  1,  0,  0,  JOF_TABLESWITCH|JOF_DETECTING|JOF_PARENHEAD)
-OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL,   -1,  1,  0,  0,  JOF_LOOKUPSWITCH|JOF_DETECTING|JOF_PARENHEAD)
+OPDEF(JSOP_UNUSED0,   71, "unused0",    NULL,         1,  0,  0,  0,  JOF_BYTE)
 
 /* New, infallible/transitive identity ops. */
 OPDEF(JSOP_STRICTEQ,  72, "stricteq",   "===",        1,  2,  1, 10,  JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC|JOF_ARITH)
 OPDEF(JSOP_STRICTNE,  73, "strictne",   "!==",        1,  2,  1, 10,  JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC|JOF_ARITH)
 
 /*
  * Sometimes web pages do 'o.Item(i) = j'. This is not an early SyntaxError,
  * for web compatibility. Instead we emit JSOP_SETCALL after the function call,
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -778,41 +778,16 @@ MakeJITScript(JSContext *cx, HandleScrip
                         edge.target = targetOffset;
                         if (!currentEdges.append(edge))
                             return NULL;
                     }
                     pc2 += JUMP_OFFSET_LEN;
                 }
             }
 
-            if (op == JSOP_LOOKUPSWITCH) {
-                unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
-                jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
-                unsigned npairs = GET_UINT16(pc2);
-                pc2 += UINT16_LEN;
-
-                CrossChunkEdge edge;
-                edge.source = offset;
-                edge.target = defaultOffset;
-                if (!currentEdges.append(edge))
-                    return NULL;
-
-                while (npairs) {
-                    pc2 += UINT32_INDEX_LEN;
-                    unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
-                    CrossChunkEdge edge;
-                    edge.source = offset;
-                    edge.target = targetOffset;
-                    if (!currentEdges.append(edge))
-                        return NULL;
-                    pc2 += JUMP_OFFSET_LEN;
-                    npairs--;
-                }
-            }
-
             if (unsigned(offset - chunkStart) > CHUNK_LIMIT)
                 finishChunk = true;
 
             if (nextOffset >= script->length || !analysis->maybeCode(nextOffset)) {
                 /* Ensure that chunks do not start on unreachable opcodes. */
                 preserveChunk = true;
             } else {
                 /*
@@ -2867,31 +2842,16 @@ mjit::Compiler::generateMethod()
 #else
             if (!jsop_tableswitch(PC))
                 return Compile_Error;
 #endif
             PC += js_GetVariableBytecodeLength(PC);
             break;
           END_CASE(JSOP_TABLESWITCH)
 
-          BEGIN_CASE(JSOP_LOOKUPSWITCH)
-            if (script_->hasScriptCounts)
-                updatePCCounts(PC, &countsUpdated);
-            frame.syncAndForgetEverything();
-            masm.move(ImmPtr(PC), Registers::ArgReg1);
-
-            /* prepareStubCall() is not needed due to syncAndForgetEverything() */
-            INLINE_STUBCALL(stubs::LookupSwitch, REJOIN_NONE);
-            frame.pop();
-
-            masm.jump(Registers::ReturnReg);
-            PC += js_GetVariableBytecodeLength(PC);
-            break;
-          END_CASE(JSOP_LOOKUPSWITCH)
-
           BEGIN_CASE(JSOP_CASE)
             // X Y
 
             frame.dupAt(-2);
             // X Y X
 
             jsop_stricteq(JSOP_STRICTEQ);
             // X cond
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1312,73 +1312,16 @@ FindNativeCode(VMFrame &f, jsbytecode *t
             return edge.shimLabel;
     }
 
     JS_NOT_REACHED("Missing edge");
     return NULL;
 }
 
 void * JS_FASTCALL
-stubs::LookupSwitch(VMFrame &f, jsbytecode *pc)
-{
-    AutoAssertNoGC nogc;
-    jsbytecode *jpc = pc;
-    UnrootedScript script = f.fp()->script();
-
-    /* This is correct because the compiler adjusts the stack beforehand. */
-    Value lval = f.regs.sp[-1];
-
-    if (!lval.isPrimitive())
-        return FindNativeCode(f, pc + GET_JUMP_OFFSET(pc));
-
-    JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH);
-
-    pc += JUMP_OFFSET_LEN;
-    uint32_t npairs = GET_UINT16(pc);
-    pc += UINT16_LEN;
-
-    JS_ASSERT(npairs);
-
-    if (lval.isString()) {
-        JSLinearString *str = lval.toString()->ensureLinear(f.cx);
-        if (!str)
-            THROWV(NULL);
-        for (uint32_t i = 1; i <= npairs; i++) {
-            Value rval = script->getConst(GET_UINT32_INDEX(pc));
-            pc += UINT32_INDEX_LEN;
-            if (rval.isString()) {
-                JSLinearString *rhs = &rval.toString()->asLinear();
-                if (rhs == str || EqualStrings(str, rhs))
-                    return FindNativeCode(f, jpc + GET_JUMP_OFFSET(pc));
-            }
-            pc += JUMP_OFFSET_LEN;
-        }
-    } else if (lval.isNumber()) {
-        double d = lval.toNumber();
-        for (uint32_t i = 1; i <= npairs; i++) {
-            Value rval = script->getConst(GET_UINT32_INDEX(pc));
-            pc += UINT32_INDEX_LEN;
-            if (rval.isNumber() && d == rval.toNumber())
-                return FindNativeCode(f, jpc + GET_JUMP_OFFSET(pc));
-            pc += JUMP_OFFSET_LEN;
-        }
-    } else {
-        for (uint32_t i = 1; i <= npairs; i++) {
-            Value rval = script->getConst(GET_UINT32_INDEX(pc));
-            pc += UINT32_INDEX_LEN;
-            if (lval == rval)
-                return FindNativeCode(f, jpc + GET_JUMP_OFFSET(pc));
-            pc += JUMP_OFFSET_LEN;
-        }
-    }
-
-    return FindNativeCode(f, jpc + GET_JUMP_OFFSET(jpc));
-}
-
-void * JS_FASTCALL
 stubs::TableSwitch(VMFrame &f, jsbytecode *origPc)
 {
     jsbytecode * const originalPC = origPc;
 
     DebugOnly<JSOp> op = JSOp(*originalPC);
     JS_ASSERT(op == JSOP_TABLESWITCH);
 
     uint32_t jumpOffset = GET_JUMP_OFFSET(originalPC);
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -76,17 +76,16 @@ struct UncachedCallResult {
  * pointer that can be used to call the function, or throw.
  */
 void UncachedCallHelper(VMFrame &f, uint32_t argc, bool lowered, UncachedCallResult &ucr);
 void UncachedNewHelper(VMFrame &f, uint32_t argc, UncachedCallResult &ucr);
 
 void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
 void JS_FASTCALL Throw(VMFrame &f);
 
-void * JS_FASTCALL LookupSwitch(VMFrame &f, jsbytecode *pc);
 void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc);
 
 void JS_FASTCALL BindName(VMFrame &f, PropertyName *name);
 JSObject * JS_FASTCALL BindGlobalName(VMFrame &f);
 void JS_FASTCALL SetName(VMFrame &f, PropertyName *name);
 void JS_FASTCALL IntrinsicName(VMFrame &f, PropertyName *name);
 void JS_FASTCALL Name(VMFrame &f);
 void JS_FASTCALL GetProp(VMFrame &f, PropertyName *name);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1710,24 +1710,16 @@ UpdateSwitchTableBounds(JSContext *cx, H
         pc += jmplen;
         low = GET_JUMP_OFFSET(pc);
         pc += JUMP_OFFSET_LEN;
         high = GET_JUMP_OFFSET(pc);
         pc += JUMP_OFFSET_LEN;
         n = high - low + 1;
         break;
 
-      case JSOP_LOOKUPSWITCH:
-        jmplen = JUMP_OFFSET_LEN;
-        pc += jmplen;
-        n = GET_UINT16(pc);
-        pc += UINT16_LEN;
-        jmplen += JUMP_OFFSET_LEN;
-        break;
-
       default:
         /* [condswitch] switch does not have any jump or lookup tables. */
         JS_ASSERT(op == JSOP_CONDSWITCH);
         return;
     }
 
     *start = (unsigned)(pc - script->code);
     *end = *start + (unsigned)(n * jmplen);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2910,39 +2910,30 @@ class FlowGraphSummary : public Vector<s
             size_t lineno = r.frontLineNumber();
             JSOp op = r.frontOpcode();
 
             if (FlowsIntoNext(prevOp))
                 addEdge(prevLine, r.frontOffset());
 
             if (js_CodeSpec[op].type() == JOF_JUMP) {
                 addEdge(lineno, r.frontOffset() + GET_JUMP_OFFSET(r.frontPC()));
-            } else if (op == JSOP_TABLESWITCH || op == JSOP_LOOKUPSWITCH) {
+            } else if (op == JSOP_TABLESWITCH) {
                 jsbytecode *pc = r.frontPC();
                 size_t offset = r.frontOffset();
                 ptrdiff_t step = JUMP_OFFSET_LEN;
                 size_t defaultOffset = offset + GET_JUMP_OFFSET(pc);
                 pc += step;
                 addEdge(lineno, defaultOffset);
 
-                int ncases;
-                if (op == JSOP_TABLESWITCH) {
-                    int32_t low = GET_JUMP_OFFSET(pc);
-                    pc += JUMP_OFFSET_LEN;
-                    ncases = GET_JUMP_OFFSET(pc) - low + 1;
-                    pc += JUMP_OFFSET_LEN;
-                } else {
-                    ncases = GET_UINT16(pc);
-                    pc += UINT16_LEN;
-                    JS_ASSERT(ncases > 0);
-                }
+                int32_t low = GET_JUMP_OFFSET(pc);
+                pc += JUMP_OFFSET_LEN;
+                int ncases = GET_JUMP_OFFSET(pc) - low + 1;
+                pc += JUMP_OFFSET_LEN;
 
                 for (int i = 0; i < ncases; i++) {
-                    if (op == JSOP_LOOKUPSWITCH)
-                        pc += UINT32_INDEX_LEN;
                     size_t target = offset + GET_JUMP_OFFSET(pc);
                     addEdge(lineno, target);
                     pc += step;
                 }
             }
 
             prevOp = op;
             prevLine = lineno;
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -20,17 +20,17 @@ namespace js {
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number is XDR'd near the front of xdr bytecode and
  * aborts deserialization if there is a mismatch between the current
  * and saved versions. If deserialization fails, the data should be
  * invalidated if possible.
  */
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 134);
+static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 135);
 
 class XDRBuffer {
   public:
     XDRBuffer(JSContext *cx)
       : context(cx), base(NULL), cursor(NULL), limit(NULL) { }
 
     JSContext *cx() const {
         return context;