Bug 867160 - Allow baseline to bypass scriptAnalysis on most scripts. r=jandem
authorKannan Vijayan <kvijayan@mozilla.com>
Fri, 03 May 2013 11:49:51 -0400
changeset 142525 4768761daaef02ad8aba3e16e4f14c8d018712e6
parent 142524 3f5a76354adb99098f8b40b2080c96d128d2ad37
child 142526 be579e9be3e4cf54b9f006fbc6f31363d18a3b3f
push idunknown
push userunknown
push dateunknown
reviewersjandem
bugs867160
milestone23.0a1
Bug 867160 - Allow baseline to bypass scriptAnalysis on most scripts. r=jandem
js/src/Makefile.in
js/src/ion/BaselineCompiler.cpp
js/src/ion/BaselineCompiler.h
js/src/ion/BaselineFrameInfo.cpp
js/src/ion/BaselineFrameInfo.h
js/src/ion/BytecodeAnalysis.cpp
js/src/ion/BytecodeAnalysis.h
js/src/ion/shared/BaselineCompiler-shared.cpp
js/src/ion/shared/BaselineCompiler-shared.h
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsopcodeinlines.h
js/src/methodjit/Compiler.cpp
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -208,16 +208,17 @@ CPPSRCS += 	MethodJIT.cpp \
 		$(NULL)
 
 # Ion
 ifdef ENABLE_ION
 VPATH +=	$(srcdir)/ion
 VPATH +=	$(srcdir)/ion/shared
 
 CPPSRCS +=	MIR.cpp \
+		BytecodeAnalysis.cpp \
 		BaselineCompiler.cpp \
 		BaselineIC.cpp \
 		BaselineFrame.cpp \
 		BaselineFrameInfo.cpp \
 		BaselineJIT.cpp \
 		BaselineInspector.cpp \
 		BaselineBailouts.cpp \
 		BacktrackingAllocator.cpp \
--- a/js/src/ion/BaselineCompiler.cpp
+++ b/js/src/ion/BaselineCompiler.cpp
@@ -24,16 +24,19 @@ BaselineCompiler::BaselineCompiler(JSCon
   : BaselineCompilerSpecific(cx, script),
     return_(new HeapLabel())
 {
 }
 
 bool
 BaselineCompiler::init()
 {
+    if (!analysis_.init())
+        return false;
+
     if (!labels_.init(script->length))
         return false;
 
     for (size_t i = 0; i < script->length; i++)
         new (&labels_[i]) Label();
 
     if (!frame.init())
         return false;
@@ -59,18 +62,22 @@ BaselineCompiler::addPCMappingEntry(bool
 }
 
 MethodStatus
 BaselineCompiler::compile()
 {
     IonSpew(IonSpew_BaselineScripts, "Baseline compiling script %s:%d (%p)",
             script->filename(), script->lineno, script.get());
 
-    if (!script->ensureRanAnalysis(cx))
-        return Method_Error;
+    // Only need to analyze scripts which are marked |argumensHasVarBinding|, to
+    // compute |needsArgsObj| flag.
+    if (script->argumentsHasVarBinding()) {
+        if (!script->ensureRanAnalysis(cx))
+            return Method_Error;
+    }
 
     // Pin analysis info during compilation.
     types::AutoEnterAnalysis autoEnterAnalysis(cx);
 
     if (!emitPrologue())
         return Method_Error;
 
     MethodStatus status = emitBody();
@@ -532,31 +539,31 @@ BaselineCompiler::emitBody()
     uint32_t emittedOps = 0;
 
     while (true) {
         SPEW_OPCODE();
         JSOp op = JSOp(*pc);
         IonSpew(IonSpew_BaselineOp, "Compiling op @ %d: %s",
                 int(pc - script->code), js_CodeName[op]);
 
-        analyze::Bytecode *code = script->analysis()->maybeCode(pc);
+        BytecodeInfo *info = analysis_.maybeInfo(pc);
 
         // Skip unreachable ops.
-        if (!code) {
+        if (!info) {
             if (op == JSOP_STOP)
                 break;
             pc += GetBytecodeLength(pc);
             lastOpUnreachable = true;
             continue;
         }
 
         // Fully sync the stack if there are incoming jumps.
-        if (code->jumpTarget) {
+        if (info->jumpTarget) {
             frame.syncStack(0);
-            frame.setStackDepth(code->stackDepth);
+            frame.setStackDepth(info->stackDepth);
         }
 
         // Always sync in debug mode.
         if (debugMode_)
             frame.syncStack(0);
 
         // At the beginning of any op, at most the top 2 stack-values are unsynced.
         if (frame.stackDepth() > 2)
@@ -2414,17 +2421,17 @@ static const VMFunction NewArgumentsObje
     FunctionInfo<NewArgumentsObjectFn>(ion::NewArgumentsObject);
 
 bool
 BaselineCompiler::emit_JSOP_ARGUMENTS()
 {
     frame.syncStack(0);
 
     Label done;
-    if (!script->needsArgsObj()) {
+    if (!script->argumentsHasVarBinding() || !script->needsArgsObj()) {
         // We assume the script does not need an arguments object. However, this
         // assumption can be invalidated later, see argumentsOptimizationFailed
         // in JSScript. Because we can't invalidate baseline JIT code, we set a
         // flag on BaselineScript when that happens and guard on it here.
         masm.moveValue(MagicValue(JS_OPTIMIZED_ARGUMENTS), R0);
 
         // Load script->baseline.
         Register scratch = R1.scratchReg();
--- a/js/src/ion/BaselineCompiler.h
+++ b/js/src/ion/BaselineCompiler.h
@@ -8,19 +8,21 @@
 #define jsion_baseline_compiler_h__
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "IonCode.h"
 #include "jsinfer.h"
 #include "jsinterp.h"
 
+#include "IonAllocPolicy.h"
 #include "BaselineJIT.h"
 #include "BaselineIC.h"
 #include "FixedList.h"
+#include "BytecodeAnalysis.h"
 
 #if defined(JS_CPU_X86)
 # include "x86/BaselineCompiler-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/BaselineCompiler-x64.h"
 #else
 # include "arm/BaselineCompiler-arm.h"
 #endif
--- a/js/src/ion/BaselineFrameInfo.cpp
+++ b/js/src/ion/BaselineFrameInfo.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BaselineFrameInfo.h"
 #include "IonSpewer.h"
+#include "shared/BaselineCompiler-shared.h"
 
 #include "jsanalyze.h"
 #include "jsinferinlines.h"
 
 using namespace js;
 using namespace js::ion;
 
 bool
@@ -142,18 +143,18 @@ FrameInfo::popRegsAndSync(uint32_t uses)
     }
 }
 
 #ifdef DEBUG
 void
 FrameInfo::assertValidState(jsbytecode *pc)
 {
     // Check stack depth.
-    analyze::Bytecode *code = script->analysis()->maybeCode(pc);
-    JS_ASSERT_IF(code, stackDepth() == code->stackDepth);
+    BytecodeInfo *info = compiler.analysis().maybeInfo(pc);
+    JS_ASSERT_IF(info, stackDepth() == info->stackDepth);
 
     // Start at the bottom, find the first value that's not synced.
     uint32_t i = 0;
     for (; i < stackDepth(); i++) {
         if (stack[i].kind() != StackValue::Stack)
             break;
     }
 
--- a/js/src/ion/BaselineFrameInfo.h
+++ b/js/src/ion/BaselineFrameInfo.h
@@ -8,16 +8,17 @@
 #define jsion_baseline_frameinfo_h__
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "BaselineJIT.h"
 #include "BaselineFrame.h"
 #include "BaselineRegisters.h"
+#include "BytecodeAnalysis.h"
 #include "IonMacroAssembler.h"
 #include "FixedList.h"
 
 namespace js {
 namespace ion {
 
 // FrameInfo overview.
 //
@@ -153,27 +154,32 @@ class StackValue
     void setStack() {
         kind_ = Stack;
         knownType_ = JSVAL_TYPE_UNKNOWN;
     }
 };
 
 enum StackAdjustment { AdjustStack, DontAdjustStack };
 
+class BaselineCompilerShared;
+
 class FrameInfo
 {
+    BaselineCompilerShared &compiler;
     RootedScript script;
     MacroAssembler &masm;
 
     FixedList<StackValue> stack;
     size_t spIndex;
 
   public:
-    FrameInfo(JSContext *cx, HandleScript script, MacroAssembler &masm)
-      : script(cx, script),
+    FrameInfo(JSContext *cx, BaselineCompilerShared &compiler, HandleScript script,
+              MacroAssembler &masm)
+      : compiler(compiler),
+        script(cx, script),
         masm(masm),
         stack(),
         spIndex(0)
     { }
 
     bool init();
 
     uint32_t nlocals() const {
new file mode 100644
--- /dev/null
+++ b/js/src/ion/BytecodeAnalysis.cpp
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ion/BytecodeAnalysis.h"
+#include "jsopcode.h"
+#include "jsopcodeinlines.h"
+
+using namespace js;
+using namespace js::ion;
+
+BytecodeAnalysis::BytecodeAnalysis(JSScript *script)
+  : script_(script),
+    infos_()
+{
+}
+
+bool
+BytecodeAnalysis::init()
+{
+    if (!infos_.growByUninitialized(script_->length))
+        return false;
+
+    jsbytecode *end = script_->code + script_->length;
+
+    // Clear all BytecodeInfo.
+    mozilla::PodZero(infos_.begin(), infos_.length());
+    infos_[0].init(/*stackDepth=*/0);
+
+    for (jsbytecode *pc = script_->code; pc < end; pc += GetBytecodeLength(pc)) {
+        JSOp op = JSOp(*pc);
+        unsigned offset = pc - script_->code;
+
+        IonSpew(IonSpew_BaselineOp, "Analyzing op @ %d (end=%d): %s",
+                int(pc - script_->code), int(end - script_->code), js_CodeName[op]);
+
+        // If this bytecode info has not yet been initialized, it's not reachable.
+        if (!infos_[offset].initialized)
+            continue;
+
+
+        unsigned stackDepth = infos_[offset].stackDepth;
+#ifdef DEBUG
+        for (jsbytecode *chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++)
+            JS_ASSERT(!infos_[chkpc - script_->code].initialized);
+#endif
+
+        // Treat decompose ops as no-ops which do not adjust the stack. We will
+        // pick up the stack depths as we go through the decomposed version.
+        if (!(js_CodeSpec[op].format & JOF_DECOMPOSE)) {
+            unsigned nuses = GetUseCount(script_, offset);
+            unsigned ndefs = GetDefCount(script_, offset);
+
+            JS_ASSERT(stackDepth >= nuses);
+            stackDepth -= nuses;
+            stackDepth += ndefs;
+
+            // If stack depth exceeds max allowed by analysis, fail fast.
+            JS_ASSERT(stackDepth <= BytecodeInfo::MAX_STACK_DEPTH);
+        }
+
+        if (op == JSOP_TABLESWITCH) {
+            unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
+            jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
+            int32_t low = GET_JUMP_OFFSET(pc2);
+            pc2 += JUMP_OFFSET_LEN;
+            int32_t high = GET_JUMP_OFFSET(pc2);
+            pc2 += JUMP_OFFSET_LEN;
+
+            infos_[defaultOffset].init(stackDepth);
+            infos_[defaultOffset].jumpTarget = true;
+
+            for (int32_t i = low; i <= high; i++) {
+                unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
+                if (targetOffset != offset) {
+                    infos_[targetOffset].init(stackDepth);
+                    infos_[targetOffset].jumpTarget = true;
+                }
+                pc2 += JUMP_OFFSET_LEN;
+            }
+        } else if (op == 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;
+
+                    if (tn->kind != JSTRY_ITER) {
+                        infos_[catchOffset].init(stackDepth);
+                        infos_[catchOffset].jumpTarget = true;
+                    }
+                }
+            }
+        }
+
+        bool jump = IsJumpOpcode(op);
+        if (jump) {
+            // Case instructions do not push the lvalue back when branching.
+            unsigned newStackDepth = stackDepth;
+            if (op == JSOP_CASE)
+                newStackDepth--;
+
+            unsigned targetOffset = offset + GET_JUMP_OFFSET(pc);
+
+            // If this is a a backedge to an un-analyzed segment, analyze from there.
+            bool jumpBack = (targetOffset < offset) && !infos_[targetOffset].initialized;
+
+            infos_[targetOffset].init(newStackDepth);
+            infos_[targetOffset].jumpTarget = true;
+
+            if (jumpBack)
+                pc = script_->code + targetOffset;
+        }
+
+        // Handle any fallthrough from this opcode.
+        if (BytecodeFallsThrough(op)) {
+            jsbytecode *nextpc = pc + GetBytecodeLength(pc);
+            JS_ASSERT(nextpc < end);
+            unsigned nextOffset = nextpc - script_->code;
+
+            infos_[nextOffset].init(stackDepth);
+
+            if (jump)
+                infos_[nextOffset].jumpFallthrough = true;
+
+            // Treat the fallthrough of a branch instruction as a jump target.
+            if (jump)
+                infos_[nextOffset].jumpTarget = true;
+            else
+                infos_[nextOffset].fallthrough = true;
+        }
+    }
+
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/ion/BytecodeAnalysis.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jsion_bytecodeanalysis_h__
+#define jsion_bytecodeanalysis_h__
+
+#include "jscntxt.h"
+
+#include "IonAllocPolicy.h"
+#include "js/Vector.h"
+
+namespace js {
+namespace ion {
+
+
+// Basic information about bytecodes in the script.  Used to help baseline compilation.
+struct BytecodeInfo
+{
+    static const uint16_t MAX_STACK_DEPTH = 0xffffU;
+    uint16_t stackDepth;
+    bool initialized : 1;
+    bool jumpTarget : 1;
+    bool jumpFallthrough : 1;
+    bool fallthrough : 1;
+
+    void init(unsigned depth) {
+        JS_ASSERT(depth <= MAX_STACK_DEPTH);
+        JS_ASSERT_IF(initialized, stackDepth == depth);
+        initialized = true;
+        stackDepth = depth;
+    }
+};
+
+class BytecodeAnalysis
+{
+    JSScript *script_;
+    Vector<BytecodeInfo, 0, IonAllocPolicy> infos_;
+
+  public:
+    BytecodeAnalysis(JSScript *script);
+
+    bool init();
+
+    BytecodeInfo &info(jsbytecode *pc) {
+        JS_ASSERT(infos_[pc - script_->code].initialized);
+        return infos_[pc - script_->code];
+    }
+
+    BytecodeInfo *maybeInfo(jsbytecode *pc) {
+        if (infos_[pc - script_->code].initialized)
+            return &infos_[pc - script_->code];
+        return NULL;
+    }
+};
+
+
+} // namespace ion
+} // namespace js
+
+#endif // jsion_bytecodeanalysis_h__
--- a/js/src/ion/shared/BaselineCompiler-shared.cpp
+++ b/js/src/ion/shared/BaselineCompiler-shared.cpp
@@ -13,17 +13,18 @@ using namespace js::ion;
 
 BaselineCompilerShared::BaselineCompilerShared(JSContext *cx, HandleScript script)
   : cx(cx),
     script(cx, script),
     pc(script->code),
     ionCompileable_(ion::IsEnabled(cx) && CanIonCompileScript(cx, script, false)),
     ionOSRCompileable_(ion::IsEnabled(cx) && CanIonCompileScript(cx, script, true)),
     debugMode_(cx->compartment->debugMode()),
-    frame(cx, script, masm),
+    analysis_(script),
+    frame(cx, *this, script, masm),
     stubSpace_(),
     icEntries_(),
     pcMappingEntries_(),
     icLoadLabels_(),
     pushedBeforeCall_(0),
     inCall_(false),
     spsPushToggleOffset_()
 { }
--- a/js/src/ion/shared/BaselineCompiler-shared.h
+++ b/js/src/ion/shared/BaselineCompiler-shared.h
@@ -23,16 +23,17 @@ class BaselineCompilerShared
     JSContext *cx;
     RootedScript script;
     jsbytecode *pc;
     MacroAssembler masm;
     bool ionCompileable_;
     bool ionOSRCompileable_;
     bool debugMode_;
 
+    BytecodeAnalysis analysis_;
     FrameInfo frame;
 
     FallbackICStubSpace stubSpace_;
     js::Vector<ICEntry, 16, SystemAllocPolicy> icEntries_;
 
     // Stores the native code offset for a bytecode pc.
     struct PCMappingEntry
     {
@@ -124,14 +125,19 @@ class BaselineCompilerShared
         // Ensure everything is synced.
         frame.syncStack(0);
 
         // Save the frame pointer.
         masm.Push(BaselineFrameReg);
     }
 
     bool callVM(const VMFunction &fun);
+
+  public:
+    BytecodeAnalysis &analysis() {
+        return analysis_;
+    }
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_baselinecompiler_shared_h__
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -34,28 +34,16 @@ analyze::PrintBytecode(JSContext *cx, Ha
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return;
     js_Disassemble1(cx, script, pc, pc - script->code, true, &sprinter);
     fprintf(stdout, "%s", sprinter.string());
 }
 #endif
 
-static inline bool
-IsJumpOpcode(JSOp op)
-{
-    uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
-
-    /*
-     * LABEL opcodes have type JOF_JUMP but are no-ops, don't treat them as
-     * jumps to avoid degrading precision.
-     */
-    return type == JOF_JUMP && op != JSOP_LABEL;
-}
-
 /////////////////////////////////////////////////////////////////////
 // Bytecode Analysis
 /////////////////////////////////////////////////////////////////////
 
 inline bool
 ScriptAnalysis::addJump(JSContext *cx, unsigned offset,
                         unsigned *currentOffset, unsigned *forwardJump, unsigned *forwardLoop,
                         unsigned stackDepth)
@@ -585,17 +573,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
             if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, &forwardLoop, newStackDepth))
                 return;
 
             if (op == JSOP_CASE || op == JSOP_DEFAULT)
                 getCode(targetOffset).safePoint = true;
         }
 
         /* Handle any fallthrough from this opcode. */
-        if (!BytecodeNoFallThrough(op)) {
+        if (BytecodeFallsThrough(op)) {
             JS_ASSERT(successorOffset < script_->length);
 
             Bytecode *&nextcode = codeArray[successorOffset];
 
             if (!nextcode) {
                 nextcode = alloc.new_<Bytecode>();
                 if (!nextcode) {
                     setOOM(cx);
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -12,16 +12,17 @@
 #include "mozilla/PodOperations.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jsautooplen.h"
 #include "jscompartment.h"
 #include "jscntxt.h"
 #include "jsinfer.h"
 #include "jsscript.h"
+#include "jsopcodeinlines.h"
 
 #include "ds/LifoAlloc.h"
 #include "js/TemplateLib.h"
 #include "vm/ScopeObject.h"
 
 class JSScript;
 
 /* Forward declaration of downstream register allocations computed for join points. */
@@ -166,56 +167,16 @@ class Bytecode
 
     /* Types for all values pushed by this bytecode. */
     types::StackTypeSet *pushedTypes;
 
     /* Any type barriers in place at this bytecode. */
     types::TypeBarrier *typeBarriers;
 };
 
-static inline unsigned
-GetDefCount(JSScript *script, unsigned offset)
-{
-    JS_ASSERT(offset < script->length);
-    jsbytecode *pc = script->code + offset;
-
-    /*
-     * Add an extra pushed value for OR/AND opcodes, so that they are included
-     * in the pushed array of stack values for type inference.
-     */
-    switch (JSOp(*pc)) {
-      case JSOP_OR:
-      case JSOP_AND:
-        return 1;
-      case JSOP_PICK:
-        /*
-         * Pick pops and pushes how deep it looks in the stack + 1
-         * items. i.e. if the stack were |a b[2] c[1] d[0]|, pick 2
-         * would pop b, c, and d to rearrange the stack to |a c[0]
-         * d[1] b[2]|.
-         */
-        return (pc[1] + 1);
-      default:
-        return StackDefs(script, pc);
-    }
-}
-
-static inline unsigned
-GetUseCount(JSScript *script, unsigned offset)
-{
-    JS_ASSERT(offset < script->length);
-    jsbytecode *pc = script->code + offset;
-
-    if (JSOp(*pc) == JSOP_PICK)
-        return (pc[1] + 1);
-    if (js_CodeSpec[*pc].nuses == -1)
-        return StackUses(script, pc);
-    return js_CodeSpec[*pc].nuses;
-}
-
 /*
  * For opcodes which assign to a local variable or argument, track an extra def
  * during SSA analysis for the value's use chain and assigned type.
  */
 static inline bool
 ExtendedDef(jsbytecode *pc)
 {
     switch ((JSOp)*pc) {
@@ -230,37 +191,16 @@ ExtendedDef(jsbytecode *pc)
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC:
         return true;
       default:
         return false;
     }
 }
 
-/* Return whether op bytecodes do not fallthrough (they may do a jump). */
-static inline bool
-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:
-        return true;
-      case JSOP_GOSUB:
-        /* These fall through indirectly, after executing a 'finally'. */
-        return false;
-      default:
-        return false;
-    }
-}
-
 /*
  * For opcodes which access local variables or arguments, we track an extra
  * use during SSA analysis for the value of the variable before/after the op.
  */
 static inline bool
 ExtendedUse(jsbytecode *pc)
 {
     if (ExtendedDef(pc))
--- a/js/src/jsopcodeinlines.h
+++ b/js/src/jsopcodeinlines.h
@@ -8,16 +8,88 @@
 #define jsopcodeinlines_h__
 
 #include "jsautooplen.h"
 
 #include "frontend/BytecodeEmitter.h"
 
 namespace js {
 
+static inline unsigned
+GetDefCount(JSScript *script, unsigned offset)
+{
+    JS_ASSERT(offset < script->length);
+    jsbytecode *pc = script->code + offset;
+
+    /*
+     * Add an extra pushed value for OR/AND opcodes, so that they are included
+     * in the pushed array of stack values for type inference.
+     */
+    switch (JSOp(*pc)) {
+      case JSOP_OR:
+      case JSOP_AND:
+        return 1;
+      case JSOP_PICK:
+        /*
+         * Pick pops and pushes how deep it looks in the stack + 1
+         * items. i.e. if the stack were |a b[2] c[1] d[0]|, pick 2
+         * would pop b, c, and d to rearrange the stack to |a c[0]
+         * d[1] b[2]|.
+         */
+        return (pc[1] + 1);
+      default:
+        return StackDefs(script, pc);
+    }
+}
+
+static inline unsigned
+GetUseCount(JSScript *script, unsigned offset)
+{
+    JS_ASSERT(offset < script->length);
+    jsbytecode *pc = script->code + offset;
+
+    if (JSOp(*pc) == JSOP_PICK)
+        return (pc[1] + 1);
+    if (js_CodeSpec[*pc].nuses == -1)
+        return StackUses(script, pc);
+    return js_CodeSpec[*pc].nuses;
+}
+
+static inline bool
+IsJumpOpcode(JSOp op)
+{
+    uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
+
+    /*
+     * LABEL opcodes have type JOF_JUMP but are no-ops, don't treat them as
+     * jumps to avoid degrading precision.
+     */
+    return type == JOF_JUMP && op != JSOP_LABEL;
+}
+
+static inline bool
+BytecodeFallsThrough(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:
+        return false;
+      case JSOP_GOSUB:
+        /* These fall through indirectly, after executing a 'finally'. */
+        return true;
+      default:
+        return true;
+    }
+}
+
 static inline PropertyName *
 GetNameFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op)
 {
     if (op == JSOP_LENGTH)
         return cx->names().length;
 
     // The method JIT's implementation of instanceof contains an internal lookup
     // of the prototype property.
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -832,17 +832,17 @@ MakeJITScript(JSContext *cx, JSScript *s
             if (finishChunk && !preserveChunk) {
                 ChunkDescriptor desc;
                 desc.begin = chunkStart;
                 desc.end = nextOffset;
                 if (!chunks.append(desc))
                     return NULL;
 
                 /* Add an edge for fallthrough from this chunk to the next one. */
-                if (!BytecodeNoFallThrough(op)) {
+                if (BytecodeFallsThrough(op)) {
                     CrossChunkEdge edge;
                     edge.source = offset;
                     edge.target = nextOffset;
                     analysis->getCode(edge.target).safePoint = true;
                     if (!edges.append(edge))
                         return NULL;
                 }