Bug 995336 - Use IonBuilder for arguments usage analysis, r=jandem.
☠☠ backed out by a02cface4e29 ☠ ☠
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 24 Apr 2014 08:57:26 -0600
changeset 180044 0b365c68c8a9f9036024343b07098a499e50517a
parent 179980 5fa1fb55300723c08e5106e2d39871a330650949
child 180045 496007a2b3aeed32e03fc9223ecfd93f2355233c
push id26653
push usercbook@mozilla.com
push dateFri, 25 Apr 2014 10:50:28 +0000
treeherderautoland@2b02d933c39a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs995336
milestone31.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 995336 - Use IonBuilder for arguments usage analysis, r=jandem.
js/src/gc/Statistics.cpp
js/src/gc/Statistics.h
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineInspector.cpp
js/src/jit/CompileInfo.h
js/src/jit/ExecutionMode-inl.h
js/src/jit/Ion.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/IonAnalysis.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/MCallOptimize.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsopcode.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/vm/Interpreter.cpp
js/src/vm/ScopeObject.cpp
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -295,17 +295,16 @@ static const PhaseInfo phases[] = {
     { PHASE_SWEEP_TABLES_INITIAL_SHAPE, "Sweep Initial Shapes", PHASE_SWEEP_TABLES },
     { PHASE_SWEEP_TABLES_TYPE_OBJECT, "Sweep Type Objects", PHASE_SWEEP_TABLES },
     { PHASE_SWEEP_TABLES_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_TABLES },
     { PHASE_SWEEP_TABLES_REGEXP, "Sweep Regexps", PHASE_SWEEP_TABLES },
     { PHASE_DISCARD_ANALYSIS, "Discard Analysis", PHASE_SWEEP_COMPARTMENTS },
     { PHASE_DISCARD_TI, "Discard TI", PHASE_DISCARD_ANALYSIS },
     { PHASE_FREE_TI_ARENA, "Free TI Arena", PHASE_DISCARD_ANALYSIS },
     { PHASE_SWEEP_TYPES, "Sweep Types", PHASE_DISCARD_ANALYSIS },
-    { PHASE_CLEAR_SCRIPT_ANALYSIS, "Clear Script Analysis", PHASE_DISCARD_ANALYSIS },
     { PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP },
     { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP },
     { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP },
     { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP },
     { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP },
     { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP },
     { PHASE_DESTROY, "Deallocate", PHASE_SWEEP },
     { PHASE_GC_END, "End Callback", PHASE_NO_PARENT },
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -47,17 +47,16 @@ enum Phase {
     PHASE_SWEEP_TABLES_INITIAL_SHAPE,
     PHASE_SWEEP_TABLES_TYPE_OBJECT,
     PHASE_SWEEP_TABLES_BREAKPOINT,
     PHASE_SWEEP_TABLES_REGEXP,
     PHASE_DISCARD_ANALYSIS,
     PHASE_DISCARD_TI,
     PHASE_FREE_TI_ARENA,
     PHASE_SWEEP_TYPES,
-    PHASE_CLEAR_SCRIPT_ANALYSIS,
     PHASE_SWEEP_OBJECT,
     PHASE_SWEEP_STRING,
     PHASE_SWEEP_SCRIPT,
     PHASE_SWEEP_SHAPE,
     PHASE_SWEEP_JITCODE,
     PHASE_FINALIZE_END,
     PHASE_DESTROY,
     PHASE_GC_END,
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/BaselineCompiler.h"
 
 #include "jit/BaselineHelpers.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/FixedList.h"
+#include "jit/IonAnalysis.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
 #include "vm/TraceLogging.h"
 
@@ -74,26 +75,19 @@ BaselineCompiler::compile()
 
     IonSpew(IonSpew_Codegen, "# Emitting baseline code for script %s:%d",
             script->filename(), script->lineno());
 
     TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
     AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
     AutoTraceLog logCompile(logger, TraceLogger::BaselineCompilation);
 
-    if (!script->ensureHasTypes(cx))
+    if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(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);
 
     JS_ASSERT(!script->hasBaselineScript());
 
     if (!emitPrologue())
         return Method_Error;
 
@@ -235,28 +229,17 @@ BaselineCompiler::compile()
     if (cx->zone()->needsBarrier())
         baselineScript->toggleBarriers(true);
 
     // All SPS instrumentation is emitted toggled off.  Toggle them on if needed.
     if (cx->runtime()->spsProfiler.enabled())
         baselineScript->toggleSPS(true);
 
     uint32_t *bytecodeMap = baselineScript->bytecodeTypeMap();
-
-    uint32_t added = 0;
-    for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
-        JSOp op = JSOp(*pc);
-        if (js_CodeSpec[op].format & JOF_TYPESET) {
-            bytecodeMap[added++] = script->pcToOffset(pc);
-            if (added == script->nTypeSets())
-                break;
-        }
-    }
-
-    JS_ASSERT(added == script->nTypeSets());
+    types::FillBytecodeTypeMap(script, bytecodeMap);
 
     // The last entry in the last index found, and is used to avoid binary
     // searches for the sought entry when queries are in linear order.
     bytecodeMap[script->nTypeSets()] = 0;
 
     if (script->compartment()->debugMode())
         baselineScript->setDebugMode();
 
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -299,16 +299,19 @@ TryToSpecializeBinaryArithOp(ICStub **st
     JS_ASSERT(sawInt32);
     *result = MIRType_Int32;
     return true;
 }
 
 MIRType
 BaselineInspector::expectedBinaryArithSpecialization(jsbytecode *pc)
 {
+    if (!hasBaselineScript())
+        return MIRType_None;
+
     MIRType result;
     ICStub *stubs[2];
 
     const ICEntry &entry = icEntryFromPC(pc);
     ICStub *stub = entry.fallbackStub();
     if (stub->isBinaryArith_Fallback() &&
         stub->toBinaryArith_Fallback()->hadUnoptimizableOperands())
     {
@@ -444,34 +447,43 @@ BaselineInspector::getTemplateObjectForN
     }
 
     return nullptr;
 }
 
 DeclEnvObject *
 BaselineInspector::templateDeclEnvObject()
 {
+    if (!hasBaselineScript())
+        return nullptr;
+
     JSObject *res = &templateCallObject()->as<ScopeObject>().enclosingScope();
     JS_ASSERT(res);
 
     return &res->as<DeclEnvObject>();
 }
 
 CallObject *
 BaselineInspector::templateCallObject()
 {
+    if (!hasBaselineScript())
+        return nullptr;
+
     JSObject *res = baselineScript()->templateScope();
     JS_ASSERT(res);
 
     return &res->as<CallObject>();
 }
 
 JSObject *
 BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter)
 {
+    if (!hasBaselineScript())
+        return nullptr;
+
     const ICEntry &entry = icEntryFromPC(pc);
     for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
         if (stub->isGetProp_CallScripted()  ||
             stub->isGetProp_CallNative()    ||
             stub->isGetProp_CallNativePrototype())
         {
             ICGetPropCallGetter *nstub = static_cast<ICGetPropCallGetter *>(stub);
             *lastProperty = nstub->holderShape();
@@ -480,16 +492,19 @@ BaselineInspector::commonGetPropFunction
         }
     }
     return nullptr;
 }
 
 JSObject *
 BaselineInspector::commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter)
 {
+    if (!hasBaselineScript())
+        return nullptr;
+
     const ICEntry &entry = icEntryFromPC(pc);
     for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
         if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
             ICSetPropCallSetter *nstub = static_cast<ICSetPropCallSetter *>(stub);
             *lastProperty = nstub->holderShape();
             *commonSetter = nstub->setter();
             return nstub->holder();
         }
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -292,16 +292,20 @@ class CompileInfo
     bool argsObjAliasesFormals() const {
         return scriptNeedsArgsObj_ && !script()->strict();
     }
 
     ExecutionMode executionMode() const {
         return executionMode_;
     }
 
+    bool executionModeIsAnalysis() const {
+        return executionMode_ == DefinitePropertiesAnalysis || executionMode_ == ArgumentsUsageAnalysis;
+    }
+
     bool isParallelExecution() const {
         return executionMode_ == ParallelExecution;
     }
 
   private:
     unsigned nimplicit_;
     unsigned nargs_;
     unsigned nfixedvars_;
--- a/js/src/jit/ExecutionMode-inl.h
+++ b/js/src/jit/ExecutionMode-inl.h
@@ -62,16 +62,17 @@ OffsetOfIonInJSScript(ExecutionMode cmod
 
 static inline bool
 CanIonCompile(JSScript *script, ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return script->canIonCompile();
       case ParallelExecution: return script->canParallelIonCompile();
       case DefinitePropertiesAnalysis: return true;
+      case ArgumentsUsageAnalysis: return true;
       default:;
     }
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
     return false;
 }
 
 static inline bool
 CompilingOffThread(JSScript *script, ExecutionMode cmode)
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1942,21 +1942,16 @@ CheckScript(JSContext *cx, JSScript *scr
     if (script->isForEval()) {
         // Eval frames are not yet supported. Supporting this will require new
         // logic in pushBailoutFrame to deal with linking prev.
         // Additionally, JSOP_DEFVAR support will require baking in isEvalFrame().
         IonSpew(IonSpew_Abort, "eval script");
         return false;
     }
 
-    if (!script->analyzedArgsUsage() && !script->ensureRanAnalysis(cx)) {
-        IonSpew(IonSpew_Abort, "OOM under ensureRanAnalysis");
-        return false;
-    }
-
     if (!script->compileAndGo()) {
         IonSpew(IonSpew_Abort, "not compile-and-go");
         return false;
     }
 
     return true;
 }
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2236,17 +2236,17 @@ jit::AnalyzeNewScriptProperties(JSContex
     Vector<PropertyName *> accessedProperties(cx);
 
     LifoAlloc alloc(types::TypeZone::TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
 
     TempAllocator temp(&alloc);
     IonContext ictx(cx, &temp);
 
     if (!cx->compartment()->ensureJitCompartmentExists(cx))
-        return Method_Error;
+        return false;
 
     if (!script->hasBaselineScript()) {
         MethodStatus status = BaselineCompile(cx, script);
         if (status == Method_Error)
             return false;
         if (status != Method_Compiled)
             return true;
     }
@@ -2296,17 +2296,16 @@ jit::AnalyzeNewScriptProperties(JSContex
         return false;
 
     MDefinition *thisValue = graph.begin()->getSlot(info.thisSlot());
 
     // Get a list of instructions using the |this| value in the order they
     // appear in the graph.
     Vector<MInstruction *> instructions(cx);
 
-    Vector<MDefinition *> useWorklist(cx);
     for (MUseDefIterator uses(thisValue); uses; uses++) {
         MDefinition *use = uses.def();
 
         // Don't track |this| through assignments to phis.
         if (!use->isInstruction())
             return true;
 
         if (!instructions.append(use->toInstruction()))
@@ -2356,8 +2355,119 @@ jit::AnalyzeNewScriptProperties(JSContex
             return false;
         }
         if (!handled)
             return true;
     }
 
     return true;
 }
+
+static bool
+ArgumentsUseCanBeLazy(JSContext *cx, JSScript *script, MInstruction *ins, size_t index)
+{
+    // We can read the frame's arguments directly for f.apply(x, arguments).
+    if (ins->isCall()) {
+        if (*ins->toCall()->resumePoint()->pc() == JSOP_FUNAPPLY &&
+            ins->toCall()->numActualArgs() == 2 &&
+            index == MCall::IndexOfArgument(1))
+        {
+            return true;
+        }
+    }
+
+    // arguments[i] can read fp->canonicalActualArg(i) directly.
+    if (ins->isCallGetElement() && index == 0)
+        return true;
+
+    // arguments.length length can read fp->numActualArgs() directly.
+    if (ins->isCallGetProperty() && index == 0 && ins->toCallGetProperty()->name() == cx->names().length)
+        return true;
+
+    return false;
+}
+
+bool
+jit::AnalyzeArgumentsUsage(JSContext *cx, JSScript *scriptArg)
+{
+    RootedScript script(cx, scriptArg);
+    types::AutoEnterAnalysis enter(cx);
+
+    JS_ASSERT(!script->analyzedArgsUsage());
+
+    // Treat the script as needing an arguments object until we determine it
+    // does not need one. This both allows us to easily see where the arguments
+    // object can escape through assignments to the function's named arguments,
+    // and also simplifies handling of early returns.
+    script->setNeedsArgsObj(true);
+
+    if (!jit::IsIonEnabled(cx) || !script->compileAndGo())
+        return true;
+
+    static const uint32_t MAX_SCRIPT_SIZE = 10000;
+    if (script->length() > MAX_SCRIPT_SIZE)
+        return true;
+
+    if (!script->ensureHasTypes(cx))
+        return false;
+
+    LifoAlloc alloc(types::TypeZone::TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
+
+    TempAllocator temp(&alloc);
+    IonContext ictx(cx, &temp);
+
+    if (!cx->compartment()->ensureJitCompartmentExists(cx))
+        return false;
+
+    MIRGraph graph(&temp);
+    CompileInfo info(script, script->functionNonDelazifying(),
+                     /* osrPc = */ nullptr, /* constructing = */ false,
+                     ArgumentsUsageAnalysis,
+                     /* needsArgsObj = */ true);
+
+    AutoTempAllocatorRooter root(cx, &temp);
+
+    const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_Normal);
+
+    types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(temp);
+    if (!constraints)
+        return false;
+
+    BaselineInspector inspector(script);
+    const JitCompileOptions options(cx);
+
+    IonBuilder builder(nullptr, CompileCompartment::get(cx->compartment()), options, &temp, &graph, constraints,
+                       &inspector, &info, optimizationInfo, /* baselineFrame = */ nullptr);
+
+    if (!builder.build()) {
+        if (builder.abortReason() == AbortReason_Alloc)
+            return false;
+        return true;
+    }
+
+    if (!SplitCriticalEdges(graph))
+        return false;
+
+    if (!RenumberBlocks(graph))
+        return false;
+
+    if (!BuildDominatorTree(graph))
+        return false;
+
+    if (!EliminatePhis(&builder, graph, AggressiveObservability))
+        return false;
+
+    MDefinition *argumentsValue = graph.begin()->getSlot(info.argsObjSlot());
+
+    for (MUseDefIterator uses(argumentsValue); uses; uses++) {
+        MDefinition *use = uses.def();
+
+        // Don't track |arguments| through assignments to phis.
+        if (!use->isInstruction())
+            return true;
+
+        if (!ArgumentsUseCanBeLazy(cx, script, use->toInstruction(), uses.index()))
+            return true;
+    }
+
+    script->setNeedsArgsObj(false);
+    return true;
+}
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -2,16 +2,18 @@
  * 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 jit_IonAnalysis_h
 #define jit_IonAnalysis_h
 
+#ifdef JS_ION
+
 // This file declares various analysis passes that operate on MIR.
 
 #include "jit/IonAllocPolicy.h"
 #include "jit/MIR.h"
 
 namespace js {
 namespace jit {
 
@@ -131,12 +133,17 @@ class LinearSum
     int32_t constant_;
 };
 
 bool
 AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
                            types::TypeObject *type, HandleObject baseobj,
                            Vector<types::TypeNewScript::Initializer> *initializerList);
 
+bool
+AnalyzeArgumentsUsage(JSContext *cx, JSScript *script);
+
 } // namespace jit
 } // namespace js
 
+#endif // JS_ION
+
 #endif /* jit_IonAnalysis_h */
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -119,16 +119,17 @@ IonBuilder::IonBuilder(JSContext *analys
     abortReason_(AbortReason_Disable),
     descrSetHash_(nullptr),
     constraints_(constraints),
     analysis_(*temp, info->script()),
     thisTypes(nullptr),
     argTypes(nullptr),
     typeArray(nullptr),
     typeArrayHint(0),
+    bytecodeTypeMap(nullptr),
     loopDepth_(loopDepth),
     callerResumePoint_(nullptr),
     callerBuilder_(nullptr),
     cfgStack_(*temp),
     loops_(*temp),
     switches_(*temp),
     labels_(*temp),
     iterators_(*temp),
@@ -140,17 +141,17 @@ IonBuilder::IonBuilder(JSContext *analys
     failedShapeGuard_(info->script()->failedShapeGuard()),
     nonStringIteration_(false),
     lazyArguments_(nullptr),
     inlineCallInfo_(nullptr)
 {
     script_ = info->script();
     pc = info->startPC();
 
-    JS_ASSERT(script()->hasBaselineScript());
+    JS_ASSERT(script()->hasBaselineScript() == (info->executionMode() != ArgumentsUsageAnalysis));
     JS_ASSERT(!!analysisContext == (info->executionMode() == DefinitePropertiesAnalysis));
 }
 
 void
 IonBuilder::clearForBackEnd()
 {
     JS_ASSERT(!analysisContext);
     baselineFrame_ = nullptr;
@@ -604,16 +605,27 @@ IonBuilder::init()
                                            &thisTypes, &argTypes, &typeArray))
     {
         return false;
     }
 
     if (!analysis().init(alloc(), gsn))
         return false;
 
+    // The baseline script normally has the bytecode type map, but compute
+    // it ourselves if we do not have a baseline script.
+    if (script()->hasBaselineScript()) {
+        bytecodeTypeMap = script()->baselineScript()->bytecodeTypeMap();
+    } else {
+        bytecodeTypeMap = alloc_->lifoAlloc()->newArrayUninitialized<uint32_t>(script()->nTypeSets());
+        if (!bytecodeTypeMap)
+            return false;
+        types::FillBytecodeTypeMap(script(), bytecodeTypeMap);
+    }
+
     return true;
 }
 
 bool
 IonBuilder::build()
 {
     if (!init())
         return false;
@@ -981,18 +993,20 @@ IonBuilder::initScopeChain(MDefinition *
         if (!callee) {
             MCallee *calleeIns = MCallee::New(alloc());
             current->add(calleeIns);
             callee = calleeIns;
         }
         scope = MFunctionEnvironment::New(alloc(), callee);
         current->add(scope);
 
-        // This reproduce what is done in CallObject::createForFunction
-        if (fun->isHeavyweight()) {
+        // This reproduce what is done in CallObject::createForFunction. Skip
+        // this for analyses, as the script might not have a baseline script
+        // with template objects yet.
+        if (fun->isHeavyweight() && !info().executionModeIsAnalysis()) {
             if (fun->isNamedLambda()) {
                 scope = createDeclEnvObject(callee, scope);
                 if (!scope)
                     return false;
             }
 
             scope = createCallObject(callee, scope);
             if (!scope)
@@ -3568,17 +3582,22 @@ IonBuilder::jsop_try()
     if (!js_JitOptions.compileTryCatch)
         return abort("Try-catch support disabled");
 
     // Try-finally is not yet supported.
     if (analysis().hasTryFinally())
         return abort("Has try-finally");
 
     // Try-catch within inline frames is not yet supported.
-    JS_ASSERT(script()->uninlineable() && !isInlineBuilder());
+    JS_ASSERT(!isInlineBuilder());
+
+    // Try-catch during the arguments usage analysis is not yet supported. Code
+    // accessing the arguments within the 'catch' block is not accounted for.
+    if (info().executionMode() == ArgumentsUsageAnalysis)
+        return abort("Try-catch during arguments usage analysis");
 
     graph().setHasTryBlock();
 
     jssrcnote *sn = info().getNote(gsn, pc);
     JS_ASSERT(SN_TYPE(sn) == SRC_TRY);
 
     // Get the pc of the last instruction in the try block. It's a JSOP_GOTO to
     // jump over the catch block.
@@ -4091,16 +4110,20 @@ IonBuilder::patchInlinedReturns(CallInfo
 
 IonBuilder::InliningDecision
 IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo)
 {
     // When there is no target, inlining is impossible.
     if (target == nullptr)
         return InliningDecision_DontInline;
 
+    // Never inline during the arguments usage analysis.
+    if (info().executionMode() == ArgumentsUsageAnalysis)
+        return InliningDecision_DontInline;
+
     // Native functions provide their own detection in inlineNativeCall().
     if (target->isNative())
         return InliningDecision_Inline;
 
     // Determine whether inlining is possible at callee site
     InliningDecision decision = canInlineTarget(target, callInfo);
     if (decision != InliningDecision_Inline)
         return decision;
@@ -5636,24 +5659,18 @@ IonBuilder::jsop_initprop(PropertyName *
     if (obj->resultTypeSet() &&
         !obj->resultTypeSet()->propertyNeedsBarrier(constraints(), NameToId(name)))
     {
         needsBarrier = false;
     }
 
     // In parallel execution, we never require write barriers.  See
     // forkjoin.cpp for more information.
-    switch (info().executionMode()) {
-      case SequentialExecution:
-      case DefinitePropertiesAnalysis:
-        break;
-      case ParallelExecution:
+    if (info().executionMode() == ParallelExecution)
         needsBarrier = false;
-        break;
-    }
 
     if (templateObject->isFixedSlot(shape->slot())) {
         MStoreFixedSlot *store = MStoreFixedSlot::New(alloc(), obj, shape->slot(), value);
         if (needsBarrier)
             store->setNeedsBarrier();
 
         current->add(store);
         return resumeAfter(store);
@@ -6659,16 +6676,31 @@ GetElemKnownType(bool needsHoleCheck, ty
 }
 
 bool
 IonBuilder::jsop_getelem()
 {
     MDefinition *index = current->pop();
     MDefinition *obj = current->pop();
 
+    // Always use a call if we are performing analysis and not actually
+    // emitting code, to simplify later analysis.
+    if (info().executionModeIsAnalysis()) {
+        MInstruction *ins = MCallGetElement::New(alloc(), obj, index);
+
+        current->add(ins);
+        current->push(ins);
+
+        if (!resumeAfter(ins))
+            return false;
+
+        types::TemporaryTypeSet *types = bytecodeTypes(pc);
+        return pushTypeBarrier(ins, types, true);
+    }
+
     bool emitted = false;
 
     if (!getElemTryTypedObject(&emitted, obj, index) || emitted)
         return emitted;
 
     if (!getElemTryDense(&emitted, obj, index) || emitted)
         return emitted;
 
@@ -8531,30 +8563,30 @@ IonBuilder::jsop_getprop(PropertyName *n
     // Try to optimize arguments.length.
     if (!getPropTryArgumentsLength(&emitted) || emitted)
         return emitted;
 
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
     bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
                                                 current->peek(-1), name, types);
 
-    // Always use a call if we are doing the definite properties analysis and
+    // Always use a call if we are performing analysis and
     // not actually emitting code, to simplify later analysis. Also skip deeper
     // analysis if there are no known types for this operation, as it will
     // always invalidate when executing.
-    if (info().executionMode() == DefinitePropertiesAnalysis || types->empty()) {
+    if (info().executionModeIsAnalysis() || types->empty()) {
         MDefinition *obj = current->peek(-1);
         MCallGetProperty *call = MCallGetProperty::New(alloc(), obj, name, *pc == JSOP_CALLPROP);
         current->add(call);
 
         // During the definite properties analysis we can still try to bake in
         // constants read off the prototype chain, to allow inlining later on.
         // In this case we still need the getprop call so that the later
         // analysis knows when the |this| value has been read from.
-        if (info().executionMode() == DefinitePropertiesAnalysis) {
+        if (info().executionModeIsAnalysis()) {
             if (!getPropTryConstant(&emitted, name, types) || emitted)
                 return emitted;
         }
 
         current->pop();
         current->push(call);
         return resumeAfter(call) && pushTypeBarrier(call, types, true);
     }
@@ -9011,17 +9043,17 @@ IonBuilder::jsop_setprop(PropertyName *n
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->pop();
 
     bool emitted = false;
 
     // Always use a call if we are doing the definite properties analysis and
     // not actually emitting code, to simplify later analysis.
-    if (info().executionMode() == DefinitePropertiesAnalysis) {
+    if (info().executionModeIsAnalysis()) {
         MInstruction *ins = MCallSetProperty::New(alloc(), obj, value, name, script()->strict());
         current->add(ins);
         current->push(value);
         return resumeAfter(ins);
     }
 
     // Add post barrier if needed.
     if (NeedsPostBarrier(info(), value))
@@ -9597,21 +9629,20 @@ IonBuilder::jsop_this()
     {
         // This is safe, because if the entry type of |this| is an object, it
         // will necessarily be an object throughout the entire function. OSR
         // can introduce a phi, but this phi will be specialized.
         current->pushSlot(info().thisSlot());
         return true;
     }
 
-    // If we are doing a definite properties analysis, we don't yet know the
-    // |this| type as its type object is being created right now. Instead of
-    // bailing out just push the |this| slot, as this code won't actually
-    // execute and it does not matter whether |this| is primitive.
-    if (info().executionMode() == DefinitePropertiesAnalysis) {
+    // If we are doing an analysis, we might not yet know the type of |this|.
+    // Instead of bailing out just push the |this| slot, as this code won't
+    // actually execute and it does not matter whether |this| is primitive.
+    if (info().executionModeIsAnalysis()) {
         current->pushSlot(info().thisSlot());
         return true;
     }
 
     // Hard case: |this| may be a primitive we have to wrap.
     MDefinition *def = current->getSlot(info().thisSlot());
 
     if (def->type() == MIRType_Object) {
@@ -10002,17 +10033,17 @@ IonBuilder::addShapeGuard(MDefinition *o
         guard->setNotMovable();
 
     return guard;
 }
 
 types::TemporaryTypeSet *
 IonBuilder::bytecodeTypes(jsbytecode *pc)
 {
-    return types::TypeScript::BytecodeTypes(script(), pc, &typeArrayHint, typeArray);
+    return types::TypeScript::BytecodeTypes(script(), pc, bytecodeTypeMap, &typeArrayHint, typeArray);
 }
 
 TypeDescrSetHash *
 IonBuilder::getOrCreateDescrSetHash()
 {
     if (!descrSetHash_) {
         TypeDescrSetHash *hash =
             alloc_->lifoAlloc()->new_<TypeDescrSetHash>(alloc());
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -832,16 +832,17 @@ class IonBuilder : public MIRGenerator
     // Basic analysis information about the script.
     BytecodeAnalysis analysis_;
     BytecodeAnalysis &analysis() {
         return analysis_;
     }
 
     types::TemporaryTypeSet *thisTypes, *argTypes, *typeArray;
     uint32_t typeArrayHint;
+    uint32_t *bytecodeTypeMap;
 
     GSNCache gsn;
     ScopeCoordinateNameCache scopeCoordinateNameCache;
 
     jsbytecode *pc;
     MBasicBlock *current;
     uint32_t loopDepth_;
 
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1477,32 +1477,31 @@ IonBuilder::inlineUnsafeSetTypedObjectAr
 IonBuilder::InliningStatus
 IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo)
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
     ExecutionMode executionMode = info().executionMode();
     switch (executionMode) {
-      case SequentialExecution:
-      case DefinitePropertiesAnalysis:
-        // In sequential mode, leave as is, because we'd have to
-        // access the "in warmup" flag of the runtime.
-        return InliningStatus_NotInlined;
-
       case ParallelExecution: {
         // During Parallel Exec, we always force sequential, so
         // replace with true.  This permits UCE to eliminate the
         // entire path as dead, which is important.
         callInfo.setImplicitlyUsedUnchecked();
         MConstant *ins = MConstant::New(alloc(), BooleanValue(true));
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
       }
+
+      default:
+        // In sequential mode, leave as is, because we'd have to
+        // access the "in warmup" flag of the runtime.
+        return InliningStatus_NotInlined;
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid execution mode");
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineForkJoinGetSlice(CallInfo &callInfo)
 {
@@ -1517,50 +1516,49 @@ IonBuilder::inlineForkJoinGetSlice(CallI
     // Test this, as we might have not executed the native despite knowing the
     // target here.
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     switch (info().executionMode()) {
-      case SequentialExecution:
-      case DefinitePropertiesAnalysis:
-        // ForkJoinGetSlice acts as identity for sequential execution.
-        current->push(callInfo.getArg(0));
-        return InliningStatus_Inlined;
       case ParallelExecution:
         if (LIRGenerator::allowInlineForkJoinGetSlice()) {
             MForkJoinGetSlice *getSlice = MForkJoinGetSlice::New(alloc(),
                                                                  graph().forkJoinContext());
             current->add(getSlice);
             current->push(getSlice);
             return InliningStatus_Inlined;
         }
         return InliningStatus_NotInlined;
+
+      default:
+        // ForkJoinGetSlice acts as identity for sequential execution.
+        current->push(callInfo.getArg(0));
+        return InliningStatus_Inlined;
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid execution mode");
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNewDenseArray(CallInfo &callInfo)
 {
     if (callInfo.constructing() || callInfo.argc() != 1)
         return InliningStatus_NotInlined;
 
     // For now, in seq. mode we just call the C function.  In
     // par. mode we use inlined MIR.
     ExecutionMode executionMode = info().executionMode();
     switch (executionMode) {
-      case SequentialExecution:
-      case DefinitePropertiesAnalysis:
-        return inlineNewDenseArrayForSequentialExecution(callInfo);
       case ParallelExecution:
         return inlineNewDenseArrayForParallelExecution(callInfo);
+      default:
+        return inlineNewDenseArrayForSequentialExecution(callInfo);
     }
 
     MOZ_ASSUME_UNREACHABLE("unknown ExecutionMode");
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNewDenseArrayForSequentialExecution(CallInfo &callInfo)
 {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1917,91 +1917,16 @@ TypeCompartment::newTypeObject(Exclusive
     TypeObject *object = js::NewTypeObject(cx);
     if (!object)
         return nullptr;
     new(object) TypeObject(clasp, proto, initialFlags);
 
     return object;
 }
 
-static inline jsbytecode *
-PreviousOpcode(HandleScript script, jsbytecode *pc)
-{
-    ScriptAnalysis *analysis = script->analysis();
-    JS_ASSERT(analysis->isReachable(pc));
-
-    if (pc == script->code())
-        return nullptr;
-
-    for (pc--;; pc--) {
-        if (analysis->isReachable(pc))
-            break;
-    }
-
-    return pc;
-}
-
-/*
- * If pc is an array initializer within an outer multidimensional array
- * initializer, find the opcode of the previous newarray. nullptr otherwise.
- */
-static inline jsbytecode *
-FindPreviousInnerInitializer(HandleScript script, jsbytecode *initpc)
-{
-    if (!script->hasAnalysis())
-        return nullptr;
-
-    if (!script->analysis()->isReachable(initpc))
-        return nullptr;
-
-    /*
-     * Pattern match the following bytecode, which will appear between
-     * adjacent initializer elements:
-     *
-     * endinit (for previous initializer)
-     * initelem_array (for previous initializer)
-     * newarray
-     */
-
-    if (*initpc != JSOP_NEWARRAY)
-        return nullptr;
-
-    jsbytecode *last = PreviousOpcode(script, initpc);
-    if (!last || *last != JSOP_INITELEM_ARRAY)
-        return nullptr;
-
-    last = PreviousOpcode(script, last);
-    if (!last || *last != JSOP_ENDINIT)
-        return nullptr;
-
-    /*
-     * Find the start of the previous initializer. Keep track of initializer
-     * depth to skip over inner initializers within the previous one (e.g. for
-     * arrays with three or more dimensions).
-     */
-    size_t initDepth = 0;
-    jsbytecode *previnit;
-    for (previnit = last; previnit; previnit = PreviousOpcode(script, previnit)) {
-        if (*previnit == JSOP_ENDINIT)
-            initDepth++;
-        if (*previnit == JSOP_NEWINIT ||
-            *previnit == JSOP_NEWARRAY ||
-            *previnit == JSOP_NEWOBJECT)
-        {
-            if (--initDepth == 0)
-                break;
-        }
-    }
-
-    if (!previnit || *previnit != JSOP_NEWARRAY)
-        return nullptr;
-
-    return previnit;
-}
-
 TypeObject *
 TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key)
 {
     AutoEnterAnalysis enter(cx);
 
     if (!allocationSiteTable) {
         allocationSiteTable = cx->new_<AllocationSiteTable>();
         if (!allocationSiteTable || !allocationSiteTable->init()) {
@@ -2010,34 +1935,18 @@ TypeCompartment::addAllocationSiteTypeOb
         }
     }
 
     AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key);
     JS_ASSERT(!p);
 
     TypeObject *res = nullptr;
 
-    /*
-     * If this is an array initializer nested in another array initializer,
-     * try to reuse the type objects from earlier elements to avoid
-     * distinguishing elements of the outer array unnecessarily.
-     */
     jsbytecode *pc = key.script->offsetToPC(key.offset);
     RootedScript keyScript(cx, key.script);
-    jsbytecode *prev = FindPreviousInnerInitializer(keyScript, pc);
-    if (prev) {
-        AllocationSiteKey nkey;
-        nkey.script = key.script;
-        nkey.offset = key.script->pcToOffset(prev);
-        nkey.kind = JSProto_Array;
-
-        AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(nkey);
-        if (p)
-            res = p->value();
-    }
 
     if (!res) {
         RootedObject proto(cx);
         if (!GetBuiltinPrototype(cx, key.kind, &proto))
             return nullptr;
 
         Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
         res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE);
@@ -3484,16 +3393,31 @@ IsAboutToBeFinalized(TypeObjectKey *key)
     /* Mask out the low bit indicating whether this is a type or JS object. */
     gc::Cell *tmp = reinterpret_cast<gc::Cell *>(uintptr_t(key) & ~1);
     bool isAboutToBeFinalized = IsCellAboutToBeFinalized(&tmp);
     JS_ASSERT(tmp == reinterpret_cast<gc::Cell *>(uintptr_t(key) & ~1));
     return isAboutToBeFinalized;
 }
 
 void
+types::FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap)
+{
+    uint32_t added = 0;
+    for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
+        JSOp op = JSOp(*pc);
+        if (js_CodeSpec[op].format & JOF_TYPESET) {
+            bytecodeMap[added++] = script->pcToOffset(pc);
+            if (added == script->nTypeSets())
+                break;
+        }
+    }
+    JS_ASSERT(added == script->nTypeSets());
+}
+
+void
 types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
 {
     /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
     if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
         return;
 
     if (!script->hasBaselineScript())
         return;
@@ -3604,38 +3528,16 @@ JSScript::makeTypes(JSContext *cx)
     for (unsigned i = 0; i < nargs; i++) {
         TypeSet *types = TypeScript::ArgTypes(this, i);
         InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u",
                   InferSpewColor(types), types, InferSpewColorReset(),
                   i, id());
     }
 #endif
 
-    return analyzedArgsUsage() || ensureRanAnalysis(cx);
-}
-
-bool
-JSScript::makeAnalysis(JSContext *cx)
-{
-    JS_ASSERT(types && !types->analysis);
-
-    AutoEnterAnalysis enter(cx);
-
-    types->analysis = cx->typeLifoAlloc().new_<ScriptAnalysis>(this);
-
-    if (!types->analysis)
-        return false;
-
-    RootedScript self(cx, this);
-
-    if (!self->types->analysis->analyzeBytecode(cx)) {
-        self->types->analysis = nullptr;
-        return false;
-    }
-
     return true;
 }
 
 /* static */ bool
 JSFunction::setTypeForScriptedFunction(ExclusiveContext *cx, HandleFunction fun,
                                        bool singleton /* = false */)
 {
     if (singleton) {
@@ -4353,46 +4255,53 @@ TypeZone::sweep(FreeOp *fop, bool releas
      * Clear the analysis pool, but don't release its data yet. While
      * sweeping types any live data will be allocated into the pool.
      */
     LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize());
     oldAlloc.steal(&typeLifoAlloc);
 
     /* Sweep and find compressed indexes for each compiler output. */
     size_t newCompilerOutputCount = 0;
+
+#ifdef JS_ION
     if (compilerOutputs) {
         for (size_t i = 0; i < compilerOutputs->length(); i++) {
             CompilerOutput &output = (*compilerOutputs)[i];
             if (output.isValid()) {
                 JSScript *script = output.script();
                 if (IsScriptAboutToBeFinalized(&script)) {
                     jit::GetIonScript(script, output.mode())->recompileInfoRef() = uint32_t(-1);
                     output.invalidate();
                 } else {
                     output.setSweepIndex(newCompilerOutputCount++);
                 }
             }
         }
     }
+#endif
 
     {
         gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
 
         for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
             JSScript *script = i.get<JSScript>();
             if (script->types) {
                 types::TypeScript::Sweep(fop, script, oom);
 
                 if (releaseTypes) {
                     if (script->hasParallelIonScript()) {
+#ifdef JS_ION
                         // It's possible that we preserved the parallel
                         // IonScript. The heuristic for their preservation is
                         // independent of general JIT code preservation.
                         MOZ_ASSERT(jit::ShouldPreserveParallelJITCode(rt, script));
                         script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
+#else
+                        MOZ_CRASH();
+#endif
                     } else {
                         script->types->destroy();
                         script->types = nullptr;
 
                         /*
                          * Freeze constraints on stack type sets need to be
                          * regenerated the next time the script is analyzed.
                          */
@@ -4435,24 +4344,16 @@ TypeZone::sweep(FreeOp *fop, bool releas
                 (*compilerOutputs)[sweepIndex++] = output;
             }
         }
         JS_ASSERT(sweepIndex == newCompilerOutputCount);
         JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount));
     }
 
     {
-        gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS);
-        for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
-            JSScript *script = i.get<JSScript>();
-            script->clearAnalysis();
-        }
-    }
-
-    {
         gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_FREE_TI_ARENA);
         rt->freeLifoAlloc.transferFrom(&oldAlloc);
     }
 }
 
 void
 TypeZone::clearAllNewScriptAddendumsOnOOM()
 {
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -145,17 +145,23 @@ enum ExecutionMode {
      * Modes after this point are internal and are not counted in
      * NumExecutionModes below.
      */
 
     /*
      * MIR analysis performed when invoking 'new' on a script, to determine
      * definite properties. Used by the optimizing JIT.
      */
-    DefinitePropertiesAnalysis
+    DefinitePropertiesAnalysis,
+
+    /*
+     * MIR analysis performed when executing a script which uses its arguments,
+     * when it is not known whether a lazy arguments value can be used.
+     */
+    ArgumentsUsageAnalysis
 };
 
 /*
  * Not as part of the enum so we don't get warnings about unhandled enum
  * values.
  */
 static const unsigned NumExecutionModes = ParallelExecution + 1;
 
@@ -181,20 +187,16 @@ template <> struct ExecutionModeTraits<P
 };
 
 namespace jit {
     struct IonScript;
     class IonAllocPolicy;
     class TempAllocator;
 }
 
-namespace analyze {
-    class ScriptAnalysis;
-}
-
 namespace types {
 
 class TypeZone;
 class TypeSet;
 class TypeObjectKey;
 
 /*
  * Information about a single concrete type. We pack this into a single word,
@@ -1237,33 +1239,30 @@ ArrayPrototypeHasIndexedProperty(Compile
 bool
 TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints, TemporaryTypeSet *types);
 
 /* Persistent type information for a script, retained across GCs. */
 class TypeScript
 {
     friend class ::JSScript;
 
-    /* Analysis information for the script, cleared on each GC. */
-    analyze::ScriptAnalysis *analysis;
-
   public:
     /* Array of type type sets for variables and JOF_TYPESET ops. */
     StackTypeSet *typeArray() const { return (StackTypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
 
     static inline unsigned NumTypeSets(JSScript *script);
 
     static inline StackTypeSet *ThisTypes(JSScript *script);
     static inline StackTypeSet *ArgTypes(JSScript *script, unsigned i);
 
     /* Get the type set for values observed at an opcode. */
     static inline StackTypeSet *BytecodeTypes(JSScript *script, jsbytecode *pc);
 
     template <typename TYPESET>
-    static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc,
+    static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap,
                                          uint32_t *hint, TYPESET *typeArray);
 
     /* Get a type object for an allocation site in this script. */
     static inline TypeObject *InitObject(JSContext *cx, JSScript *script, jsbytecode *pc,
                                          JSProtoKey kind);
 
     /*
      * Monitor a bytecode pushing any value. This must be called for any opcode
@@ -1305,16 +1304,19 @@ class TypeScript
         return mallocSizeOf(this);
     }
 
 #ifdef DEBUG
     void printTypes(JSContext *cx, HandleScript script) const;
 #endif
 };
 
+void
+FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap);
+
 class RecompileInfo;
 
 // Allocate a CompilerOutput for a finished compilation and generate the type
 // constraints for the compilation. Returns whether the type constraints
 // still hold.
 bool
 FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode executionMode,
                   CompilerConstraintList *constraints, RecompileInfo *precompileInfo);
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -562,25 +562,20 @@ TypeScript::ThisTypes(JSScript *script)
 TypeScript::ArgTypes(JSScript *script, unsigned i)
 {
     JS_ASSERT(i < script->functionNonDelazifying()->nargs());
     return script->types->typeArray() + script->nTypeSets() + analyze::ArgSlot(i);
 }
 
 template <typename TYPESET>
 /* static */ inline TYPESET *
-TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *hint, TYPESET *typeArray)
+TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap,
+                          uint32_t *hint, TYPESET *typeArray)
 {
     JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
-#ifdef JS_ION
-    uint32_t *bytecodeMap = script->baselineScript()->bytecodeTypeMap();
-#else
-    uint32_t *bytecodeMap = nullptr;
-    MOZ_CRASH();
-#endif
     uint32_t offset = script->pcToOffset(pc);
 
     // See if this pc is the next typeset opcode after the last one looked up.
     if ((*hint + 1) < script->nTypeSets() && bytecodeMap[*hint + 1] == offset) {
         (*hint)++;
         return typeArray + *hint;
     }
 
@@ -612,21 +607,21 @@ TypeScript::BytecodeTypes(JSScript *scri
 }
 
 /* static */ inline StackTypeSet *
 TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
 {
     JS_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread()));
 #ifdef JS_ION
     uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets();
+    return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(),
+                         hint, script->types->typeArray());
 #else
-    uint32_t *hint = nullptr;
     MOZ_CRASH();
 #endif
-    return BytecodeTypes(script, pc, hint, script->types->typeArray());
 }
 
 struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
     JSScript *script;
 
     uint32_t offset : 24;
     JSProtoKey kind : 8;
 
@@ -1351,49 +1346,16 @@ TypeNewScript::writeBarrierPre(TypeNewSc
 } } /* namespace js::types */
 
 inline bool
 JSScript::ensureHasTypes(JSContext *cx)
 {
     return types || makeTypes(cx);
 }
 
-inline bool
-JSScript::ensureRanAnalysis(JSContext *cx)
-{
-    js::types::AutoEnterAnalysis aea(cx);
-
-    if (!ensureHasTypes(cx))
-        return false;
-    if (!hasAnalysis() && !makeAnalysis(cx))
-        return false;
-    JS_ASSERT(hasAnalysis());
-    return true;
-}
-
-inline bool
-JSScript::hasAnalysis()
-{
-    return types && types->analysis;
-}
-
-inline js::analyze::ScriptAnalysis *
-JSScript::analysis()
-{
-    JS_ASSERT(hasAnalysis());
-    return types->analysis;
-}
-
-inline void
-JSScript::clearAnalysis()
-{
-    if (types)
-        types->analysis = nullptr;
-}
-
 namespace js {
 
 template <>
 struct GCMethods<const types::Type>
 {
     static types::Type initial() { return types::Type::UnknownType(); }
     static ThingRootKind kind() { return THING_ROOT_TYPE; }
     static bool poisoned(const types::Type &v) {
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -279,29 +279,16 @@ js_DumpPCCounts(JSContext *cx, HandleScr
     }
 #endif
 }
 
 /////////////////////////////////////////////////////////////////////
 // Bytecode Parser
 /////////////////////////////////////////////////////////////////////
 
-// Ensure that script analysis reports the same stack depth.
-static void
-AssertStackDepth(JSScript *script, uint32_t offset, uint32_t stackDepth) {
-    /*
-     * If this assertion fails, run the failing test case under gdb and use the
-     * following gdb command to understand the execution path of this function.
-     *
-     *     call js_DumpScriptDepth(cx, script, pc)
-     */
-    if (script->hasAnalysis())
-        script->analysis()->assertMatchingStackDepthAtOffset(offset, stackDepth);
-}
-
 namespace {
 
 class BytecodeParser
 {
     class Bytecode
     {
       public:
         Bytecode() { mozilla::PodZero(this); }
@@ -495,17 +482,16 @@ BytecodeParser::addJump(uint32_t offset,
 {
     JS_ASSERT(offset < script_->length());
 
     Bytecode *&code = codeArray_[offset];
     if (!code) {
         code = alloc().new_<Bytecode>();
         if (!code)
             return false;
-        AssertStackDepth(script_, offset, stackDepth);
         if (!code->captureOffsetStack(alloc(), offsetStack, stackDepth)) {
             reportOOM();
             return false;
         }
     } else {
         code->mergeOffsetStack(offsetStack, stackDepth);
     }
 
@@ -648,17 +634,16 @@ BytecodeParser::parse()
             Bytecode *&nextcode = codeArray_[successorOffset];
 
             if (!nextcode) {
                 nextcode = alloc().new_<Bytecode>();
                 if (!nextcode) {
                     reportOOM();
                     return false;
                 }
-                AssertStackDepth(script_, successorOffset, stackDepth);
                 if (!nextcode->captureOffsetStack(alloc(), offsetStack, stackDepth)) {
                     reportOOM();
                     return false;
                 }
             } else {
                 nextcode->mergeOffsetStack(offsetStack, stackDepth);
             }
         }
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3445,23 +3445,27 @@ JSScript::getStaticScope(jsbytecode *pc)
 
     return blockChain;
 }
 
 void
 JSScript::setArgumentsHasVarBinding()
 {
     argsHasVarBinding_ = true;
+#ifdef JS_ION
     needsArgsAnalysis_ = true;
+#else
+    // The arguments analysis is performed by IonBuilder.
+    needsArgsObj_ = true;
+#endif
 }
 
 void
 JSScript::setNeedsArgsObj(bool needsArgsObj)
 {
-    JS_ASSERT(!analyzedArgsUsage());
     JS_ASSERT_IF(needsArgsObj, argumentsHasVarBinding());
     needsArgsAnalysis_ = false;
     needsArgsObj_ = needsArgsObj;
 }
 
 void
 js::SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame,
                             HandleScript script, JSObject *argsobj)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -45,20 +45,16 @@ class BreakpointSite;
 class BindingIter;
 class LazyScript;
 class RegExpObject;
 struct SourceCompressionTask;
 class Shape;
 class WatchpointMap;
 class NestedScopeObject;
 
-namespace analyze {
-    class ScriptAnalysis;
-}
-
 namespace frontend {
     class BytecodeEmitter;
 }
 
 }
 
 /*
  * Type of try note associated with each catch or finally block, and also with
@@ -1129,24 +1125,25 @@ class JSScript : public js::gc::Barriere
         // so it can only transition from not being a generator.
         JS_ASSERT(!isGenerator());
         generatorKindBits_ = GeneratorKindAsBits(kind);
     }
 
     /*
      * As an optimization, even when argsHasLocalBinding, the function prologue
      * may not need to create an arguments object. This is determined by
-     * needsArgsObj which is set by ScriptAnalysis::analyzeSSA before running
+     * needsArgsObj which is set by AnalyzeArgumentsUsage before running
      * the script the first time. When !needsArgsObj, the prologue may simply
      * write MagicValue(JS_OPTIMIZED_ARGUMENTS) to 'arguments's slot and any
      * uses of 'arguments' will be guaranteed to handle this magic value.
      * So avoid spurious arguments object creation, we maintain the invariant
      * that needsArgsObj is only called after the script has been analyzed.
      */
     bool analyzedArgsUsage() const { return !needsArgsAnalysis_; }
+    inline bool ensureHasAnalyzedArgsUsage(JSContext *cx);
     bool needsArgsObj() const {
         JS_ASSERT(analyzedArgsUsage());
         return needsArgsObj_;
     }
     void setNeedsArgsObj(bool needsArgsObj);
     static bool argumentsOptimizationFailed(JSContext *cx, js::HandleScript script);
 
     /*
@@ -1312,42 +1309,28 @@ class JSScript : public js::gc::Barriere
     unsigned id();
 #else
     unsigned id() { return 0; }
 #endif
 
     /* Ensure the script has a TypeScript. */
     inline bool ensureHasTypes(JSContext *cx);
 
-    /*
-     * Ensure the script has bytecode analysis information. Performed when the
-     * script first runs, or first runs after a TypeScript GC purge.
-     */
-    inline bool ensureRanAnalysis(JSContext *cx);
-
-    /* Ensure the script has type inference analysis information. */
-    inline bool ensureRanInference(JSContext *cx);
-
-    inline bool hasAnalysis();
-    inline void clearAnalysis();
-    inline js::analyze::ScriptAnalysis *analysis();
-
     inline js::GlobalObject &global() const;
     js::GlobalObject &uninlinedGlobal() const;
 
     /* See StaticScopeIter comment. */
     JSObject *enclosingStaticScope() const {
         if (isCallsiteClone())
             return nullptr;
         return enclosingScopeOrOriginalFunction_;
     }
 
   private:
     bool makeTypes(JSContext *cx);
-    bool makeAnalysis(JSContext *cx);
 
   public:
     uint32_t getUseCount() const {
         return useCount;
     }
     uint32_t incUseCount(uint32_t amount = 1) { return useCount += amount; }
     uint32_t *addressOfUseCount() { return &useCount; }
     static size_t offsetOfUseCount() { return offsetof(JSScript, useCount); }
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -6,16 +6,17 @@
 
 #ifndef jsscriptinlines_h
 #define jsscriptinlines_h
 
 #include "jsscript.h"
 
 #include "jit/AsmJSLink.h"
 #include "jit/BaselineJIT.h"
+#include "jit/IonAnalysis.h"
 #include "vm/ScopeObject.h"
 
 #include "jscompartmentinlines.h"
 
 #include "vm/Shape-inl.h"
 
 namespace js {
 
@@ -168,9 +169,21 @@ JSScript::setBaselineScript(JSContext *m
     if (hasBaselineScript())
         js::jit::BaselineScript::writeBarrierPre(tenuredZone(), baseline);
 #endif
     MOZ_ASSERT(!hasIonScript());
     baseline = baselineScript;
     updateBaselineOrIonRaw();
 }
 
+inline bool
+JSScript::ensureHasAnalyzedArgsUsage(JSContext *cx)
+{
+    if (analyzedArgsUsage())
+        return true;
+#ifdef JS_ION
+    return js::jit::AnalyzeArgumentsUsage(cx, this);
+#else
+    MOZ_CRASH();
+#endif
+}
+
 #endif /* jsscriptinlines_h */
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -28,16 +28,17 @@
 #include "jsopcode.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jsstr.h"
 
 #include "builtin/Eval.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
+#include "jit/IonAnalysis.h"
 #include "js/OldDebugAPI.h"
 #include "vm/Debugger.h"
 #include "vm/Opcodes.h"
 #include "vm/Shape.h"
 #include "vm/TraceLogging.h"
 
 #include "jsatominlines.h"
 #include "jsboolinlines.h"
@@ -2860,17 +2861,17 @@ CASE(JSOP_TABLESWITCH)
         if (off)
             len = off;
     }
     ADVANCE_AND_DISPATCH(len);
 }
 
 CASE(JSOP_ARGUMENTS)
     JS_ASSERT(!REGS.fp()->fun()->hasRest());
-    if (!script->analyzedArgsUsage() && !script->ensureRanAnalysis(cx))
+    if (!script->ensureHasAnalyzedArgsUsage(cx))
         goto error;
     if (script->needsArgsObj()) {
         ArgumentsObject *obj = ArgumentsObject::createExpected(cx, REGS.fp());
         if (!obj)
             goto error;
         PUSH_COPY(ObjectValue(*obj));
     } else {
         PUSH_COPY(MagicValue(JS_OPTIMIZED_ARGUMENTS));
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1145,17 +1145,17 @@ class DebugScopeProxy : public BaseProxy
     {
         JS_ASSERT(&debugScope->scope() == scope);
         ScopeIterVal *maybeLiveScope = DebugScopes::hasLiveScope(*scope);
 
         /* Handle unaliased formals, vars, and consts at function scope. */
         if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
             CallObject &callobj = scope->as<CallObject>();
             RootedScript script(cx, callobj.callee().nonLazyScript());
-            if (!script->ensureHasTypes(cx))
+            if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
                 return false;
 
             Bindings &bindings = script->bindings;
             BindingIter bi(script);
             while (bi && NameToId(bi->name()) != id)
                 bi++;
             if (!bi)
                 return false;