Bug 917441 - Remove dependence of IonBuilder on ScriptAnalysis, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 18 Sep 2013 09:43:21 -0600
changeset 147731 ed91189f940ed062d3c17a258f9a8614c8868edf
parent 147730 5b51f1daa3a417d400077007f64a0a9bd7c24cb5
child 147732 5291a2bc37895354bdcc7cc2ad7e340a85237cc3
push id25314
push userkwierso@gmail.com
push dateWed, 18 Sep 2013 23:48:47 +0000
treeherdermozilla-central@dc09d922d41f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs917441
milestone27.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 917441 - Remove dependence of IonBuilder on ScriptAnalysis, r=jandem.
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineInspector.cpp
js/src/jit/BaselineJIT.h
js/src/jit/BytecodeAnalysis.cpp
js/src/jit/BytecodeAnalysis.h
js/src/jit/Ion.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/MIRGraph.cpp
js/src/jit/MIRGraph.h
js/src/jsanalyze.h
js/src/jsobj.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/vm/Interpreter.cpp
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -4812,16 +4812,26 @@ ICSetElem_Fallback::Compiler::generateSt
     masm.push(R0.scratchReg());
 
     masm.push(BaselineStubReg);
     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     return tailCallVM(DoSetElemFallbackInfo, masm);
 }
 
+void
+BaselineScript::noteArrayWriteHole(uint32_t pcOffset)
+{
+    ICEntry &entry = icEntryFromPCOffset(pcOffset);
+    ICFallbackStub *stub = entry.fallbackStub();
+
+    if (stub->isSetElem_Fallback())
+        stub->toSetElem_Fallback()->noteArrayWriteHole();
+}
+
 //
 // SetElem_Dense
 //
 
 bool
 ICSetElem_Dense::Compiler::generateStubCode(MacroAssembler &masm)
 {
     // R0 = object
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -3370,16 +3370,23 @@ class ICSetElem_Fallback : public ICFall
     static const uint32_t MAX_OPTIMIZED_STUBS = 8;
 
     static inline ICSetElem_Fallback *New(ICStubSpace *space, IonCode *code) {
         if (!code)
             return NULL;
         return space->allocate<ICSetElem_Fallback>(code);
     }
 
+    void noteArrayWriteHole() {
+        extra_ = 1;
+    }
+    bool hasArrayWriteHole() const {
+        return extra_;
+    }
+
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler &masm);
 
       public:
         Compiler(JSContext *cx)
           : ICStubCompiler(cx, ICStub::SetElem_Fallback)
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -21,16 +21,22 @@ SetElemICInspector::sawOOBDenseWrite() c
     if (!icEntry_)
         return false;
 
     // Check for a SetElem_DenseAdd stub.
     for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
         if (stub->isSetElem_DenseAdd())
             return true;
     }
+
+    // Check for a write hole bit on the SetElem_Fallback stub.
+    ICStub *stub = icEntry_->fallbackStub();
+    if (stub->isSetElem_Fallback())
+        return stub->toSetElem_Fallback()->hasArrayWriteHole();
+
     return false;
 }
 
 bool
 SetElemICInspector::sawOOBTypedArrayWrite() const
 {
     if (!icEntry_)
         return false;
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -262,16 +262,17 @@ struct BaselineScript
     // Toggle debug traps (used for breakpoints and step mode) in the script.
     // If |pc| is NULL, toggle traps for all ops in the script. Else, only
     // toggle traps at |pc|.
     void toggleDebugTraps(JSScript *script, jsbytecode *pc);
 
     void toggleSPS(bool enable);
 
     void noteAccessedGetter(uint32_t pcOffset);
+    void noteArrayWriteHole(uint32_t pcOffset);
 
     static size_t offsetOfFlags() {
         return offsetof(BaselineScript, flags_);
     }
 
     static void writeBarrierPre(Zone *zone, BaselineScript *script);
 };
 
--- a/js/src/jit/BytecodeAnalysis.cpp
+++ b/js/src/jit/BytecodeAnalysis.cpp
@@ -10,17 +10,19 @@
 
 #include "jsopcodeinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 BytecodeAnalysis::BytecodeAnalysis(JSScript *script)
   : script_(script),
-    infos_()
+    infos_(),
+    usesScopeChain_(false),
+    hasTryFinally_(false)
 {
 }
 
 // Bytecode range containing only catch or finally code.
 struct CatchFinallyRange
 {
     uint32_t start; // Inclusive.
     uint32_t end;   // Exclusive.
@@ -139,16 +141,36 @@ BytecodeAnalysis::init(JSContext *cx)
 
           case JSOP_LOOPENTRY:
             for (size_t i = 0; i < catchFinallyRanges.length(); i++) {
                 if (catchFinallyRanges[i].contains(offset))
                     infos_[offset].loopEntryInCatchOrFinally = true;
             }
             break;
 
+          case JSOP_NAME:
+          case JSOP_CALLNAME:
+          case JSOP_BINDNAME:
+          case JSOP_SETNAME:
+          case JSOP_DELNAME:
+          case JSOP_GETALIASEDVAR:
+          case JSOP_CALLALIASEDVAR:
+          case JSOP_SETALIASEDVAR:
+          case JSOP_LAMBDA:
+          case JSOP_DEFFUN:
+          case JSOP_DEFVAR:
+          case JSOP_DEFCONST:
+          case JSOP_SETCONST:
+            usesScopeChain_ = true;
+            break;
+
+          case JSOP_FINALLY:
+            hasTryFinally_ = true;
+            break;
+
           default:
             break;
         }
 
         bool jump = IsJumpOpcode(op);
         if (jump) {
             // Case instructions do not push the lvalue back when branching.
             unsigned newStackDepth = stackDepth;
--- a/js/src/jit/BytecodeAnalysis.h
+++ b/js/src/jit/BytecodeAnalysis.h
@@ -34,30 +34,41 @@ struct BytecodeInfo
     }
 };
 
 class BytecodeAnalysis
 {
     JSScript *script_;
     Vector<BytecodeInfo, 0, IonAllocPolicy> infos_;
 
+    bool usesScopeChain_;
+    bool hasTryFinally_;
+
   public:
     explicit BytecodeAnalysis(JSScript *script);
 
     bool init(JSContext *cx);
 
     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;
     }
+
+    bool usesScopeChain() {
+        return usesScopeChain_;
+    }
+
+    bool hasTryFinally() {
+        return hasTryFinally_;
+    }
 };
 
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_BytecodeAnalysis_h */
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1601,19 +1601,16 @@ IonCompile(JSContext *cx, JSScript *scri
 {
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::ION_COMPILE_START,
                         TraceLogging::ION_COMPILE_STOP,
                         script);
 #endif
 
-    if (!script->ensureRanAnalysis(cx))
-        return AbortReason_Alloc;
-
     LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
     if (!alloc)
         return AbortReason_Alloc;
 
     ScopedJSDeletePtr<LifoAlloc> autoDelete(alloc);
 
     TempAllocator *temp = alloc->new_<TempAllocator>(alloc);
     if (!temp)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -37,16 +37,17 @@ IonBuilder::IonBuilder(JSContext *cx, Te
                        BaselineInspector *inspector, CompileInfo *info, BaselineFrame *baselineFrame,
                        size_t inliningDepth, uint32_t loopDepth)
   : MIRGenerator(cx->compartment(), temp, graph, info),
     backgroundCodegen_(NULL),
     recompileInfo(cx->compartment()->types.compiledInfo),
     cx(cx),
     baselineFrame_(baselineFrame),
     abortReason_(AbortReason_Disable),
+    analysis_(info->script()),
     loopDepth_(loopDepth),
     callerResumePoint_(NULL),
     callerBuilder_(NULL),
     inspector(inspector),
     inliningDepth_(inliningDepth),
     numLoopRestarts_(0),
     failedBoundsCheck_(info->script()->failedBoundsCheck),
     failedShapeGuard_(info->script()->failedShapeGuard),
@@ -226,22 +227,22 @@ IonBuilder::getPolyCallTargets(types::Te
 bool
 IonBuilder::canEnterInlinedFunction(JSFunction *target)
 {
     if (target->isHeavyweight())
         return false;
 
     JSScript *targetScript = target->nonLazyScript();
 
-    if (!targetScript->ensureRanAnalysis(cx))
-        return false;
-
     if (targetScript->uninlineable)
         return false;
 
+    if (!targetScript->analyzedArgsUsage())
+        return false;
+
     if (targetScript->needsArgsObj())
         return false;
 
     if (!targetScript->compileAndGo)
         return false;
 
     types::TypeObject *targetType = target->getType(cx);
     if (!targetType || targetType->unknownProperties())
@@ -365,17 +366,17 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl
         if (*pc == JSOP_SETLOCAL)
             slot = info().localSlot(GET_SLOTNO(pc));
         else if (*pc == JSOP_SETARG)
             slot = info().argSlotUnchecked(GET_SLOTNO(pc));
         else
             continue;
         if (slot >= info().firstStackSlot())
             continue;
-        if (!script()->analysis()->maybeCode(pc))
+        if (!analysis().maybeInfo(pc))
             continue;
 
         MPhi *phi = entry->getSlot(slot)->toPhi();
 
         if (*last == JSOP_POS)
             last = earlier;
 
         if (js_CodeSpec[*last].format & JOF_TYPESET) {
@@ -486,21 +487,33 @@ IonBuilder::pushLoop(CFGState::State ini
     state.loop.initialState = initial;
     state.loop.initialPc = initialPc;
     state.loop.initialStopAt = stopAt;
     state.loop.loopHead = loopHead;
     return cfgStack_.append(state);
 }
 
 bool
-IonBuilder::build()
+IonBuilder::init()
 {
     if (!script()->ensureHasBytecodeTypeMap(cx))
         return false;
 
+    if (!analysis().init(cx))
+        return false;
+
+    return true;
+}
+
+bool
+IonBuilder::build()
+{
+    if (!init())
+        return false;
+
     setCurrentAndSpecializePhis(newBlock(pc));
     if (!current)
         return false;
 
     IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d)",
             script()->filename(), script()->lineno, (void *)script(), (int)script()->getUseCount());
 
     if (!initParameters())
@@ -628,21 +641,21 @@ IonBuilder::processIterators()
 
     return true;
 }
 
 bool
 IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
                         CallInfo &callInfo)
 {
+    if (!init())
+        return false;
+
     inlineCallInfo_ = &callInfo;
 
-    if (!script()->ensureHasBytecodeTypeMap(cx))
-        return false;
-
     IonSpew(IonSpew_Scripts, "Inlining script %s:%d (%p)",
             script()->filename(), script()->lineno, (void *)script());
 
     callerBuilder_ = callerBuilder;
     callerResumePoint_ = callerResumePoint;
 
     if (callerBuilder->failedBoundsCheck_)
         failedBoundsCheck_ = true;
@@ -837,17 +850,17 @@ bool
 IonBuilder::initScopeChain(MDefinition *callee)
 {
     MInstruction *scope = NULL;
 
     // If the script doesn't use the scopechain, then it's already initialized
     // from earlier.  However, always make a scope chain when |needsArgsObj| is true
     // for the script, since arguments object construction requires the scope chain
     // to be passed in.
-    if (!info().needsArgsObj() && !script()->analysis()->usesScopeChain())
+    if (!info().needsArgsObj() && !analysis().usesScopeChain())
         return true;
 
     // The scope chain is only tracked in scripts that have NAME opcodes which
     // will try to access the scope. For other scripts, the scope instructions
     // will be held live by resume points and code will still be generated for
     // them, so just use a constant undefined value.
     if (!script()->compileAndGo)
         return abort("non-CNG global scripts are not supported");
@@ -3350,17 +3363,17 @@ bool
 IonBuilder::jsop_try()
 {
     JS_ASSERT(JSOp(*pc) == JSOP_TRY);
 
     if (!js_IonOptions.compileTryCatch)
         return abort("Try-catch support disabled");
 
     // Try-finally is not yet supported.
-    if (script()->analysis()->hasTryFinally())
+    if (analysis().hasTryFinally())
         return abort("Has try-finally");
 
     // Try-catch within inline frames is not yet supported.
     if (callerBuilder_)
         return abort("try-catch within inline frame");
 
     graph().setHasTryBlock();
 
@@ -3396,17 +3409,17 @@ IonBuilder::jsop_try()
     // try and catch blocks is terminated), only create the try block, to avoid
     // parsing unreachable code.
 
     MBasicBlock *tryBlock = newBlock(current, GetNextPc(pc));
     if (!tryBlock)
         return false;
 
     MBasicBlock *successor;
-    if (script()->analysis()->maybeCode(afterTry)) {
+    if (analysis().maybeInfo(afterTry)) {
         successor = newBlock(current, afterTry);
         if (!successor)
             return false;
 
         // Add MTest(true, tryBlock, successorBlock).
         MConstant *true_ = MConstant::New(BooleanValue(true));
         current->add(true_);
         current->end(MTest::New(true_, tryBlock, successor));
@@ -3743,17 +3756,20 @@ IonBuilder::inlineScriptedCall(CallInfo 
     // Pop formals again, except leave |fun| on stack for duration of call.
     callInfo.popFormals(current);
     current->push(callInfo.fun());
 
     JSScript *calleeScript = target->nonLazyScript();
     BaselineInspector inspector(cx, calleeScript);
 
     // Improve type information of |this| when not set.
-    if (callInfo.constructing() && !callInfo.thisArg()->resultTypeSet()) {
+    if (callInfo.constructing() &&
+        !callInfo.thisArg()->resultTypeSet() &&
+        calleeScript->types)
+    {
         types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript);
         if (!types->unknown()) {
             MTypeBarrier *barrier = MTypeBarrier::New(callInfo.thisArg(), cloneTypeSet(types), Bailout_Normal);
             current->add(barrier);
             callInfo.setThis(barrier);
         }
     }
 
@@ -3768,17 +3784,16 @@ IonBuilder::inlineScriptedCall(CallInfo 
     MIRGraphExits saveExits;
     AutoAccumulateExits aae(graph(), saveExits);
 
     // Build the graph.
     JS_ASSERT(!cx->isExceptionPending());
     IonBuilder inlineBuilder(cx, &temp(), &graph(), &inspector, info, NULL,
                              inliningDepth_ + 1, loopDepth_);
     if (!inlineBuilder.buildInline(this, outerResumePoint, callInfo)) {
-        JS_ASSERT(calleeScript->hasAnalysis());
         if (cx->isExceptionPending()) {
             IonSpew(IonSpew_Abort, "Inline builder raised exception.");
             abortReason_ = AbortReason_Error;
             return false;
         }
 
         // Inlining the callee failed. Mark the callee as uninlineable only if
         // the inlining was aborted for a non-exception reason.
@@ -3926,17 +3941,17 @@ IonBuilder::makeInliningDecision(JSFunct
         }
     } else {
         if (inliningDepth_ >= js_IonOptions.maxInlineDepth) {
             IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: exceeding allowed inline depth",
                                       targetScript->filename(), targetScript->lineno);
             return false;
         }
 
-        if (targetScript->analysis()->hasLoops()) {
+        if (targetScript->hasLoops()) {
             IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: big function that contains a loop",
                                       targetScript->filename(), targetScript->lineno);
             return false;
         }
      }
 
     // Callee must not be excessively large.
     // This heuristic also applies to the callsite as a whole.
@@ -5082,17 +5097,17 @@ IonBuilder::testNeedsArgumentCheck(JSCon
 {
     // If we have a known target, check if the caller arg types are a subset of callee.
     // Since typeset accumulates and can't decrease that means we don't need to check
     // the arguments anymore.
     if (!target->isInterpreted())
         return true;
 
     JSScript *targetScript = target->nonLazyScript();
-    if (!targetScript->hasAnalysis())
+    if (!targetScript->types)
         return true;
 
     if (!ArgumentTypesMatch(callInfo.thisArg(), cloneTypeSet(types::TypeScript::ThisTypes(targetScript))))
         return true;
     uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->nargs);
     for (size_t i = 0; i < expected_args; i++) {
         if (!ArgumentTypesMatch(callInfo.getArg(i), cloneTypeSet(types::TypeScript::ArgTypes(targetScript, i))))
             return true;
@@ -5656,17 +5671,18 @@ IonBuilder::addBlock(MBasicBlock *block,
     graph().addBlock(block);
     block->setLoopDepth(loopDepth);
     return block;
 }
 
 MBasicBlock *
 IonBuilder::newBlock(MBasicBlock *predecessor, jsbytecode *pc)
 {
-    MBasicBlock *block = MBasicBlock::New(graph(), info(), predecessor, pc, MBasicBlock::NORMAL);
+    MBasicBlock *block = MBasicBlock::New(graph(), &analysis(), info(),
+                                          predecessor, pc, MBasicBlock::NORMAL);
     return addBlock(block, loopDepth_);
 }
 
 MBasicBlock *
 IonBuilder::newBlock(MBasicBlock *predecessor, jsbytecode *pc, MResumePoint *priorResumePoint)
 {
     MBasicBlock *block = MBasicBlock::NewWithResumePoint(graph(), info(), predecessor, pc,
                                                          priorResumePoint);
@@ -5678,27 +5694,29 @@ IonBuilder::newBlockPopN(MBasicBlock *pr
 {
     MBasicBlock *block = MBasicBlock::NewPopN(graph(), info(), predecessor, pc, MBasicBlock::NORMAL, popped);
     return addBlock(block, loopDepth_);
 }
 
 MBasicBlock *
 IonBuilder::newBlockAfter(MBasicBlock *at, MBasicBlock *predecessor, jsbytecode *pc)
 {
-    MBasicBlock *block = MBasicBlock::New(graph(), info(), predecessor, pc, MBasicBlock::NORMAL);
+    MBasicBlock *block = MBasicBlock::New(graph(), &analysis(), info(),
+                                          predecessor, pc, MBasicBlock::NORMAL);
     if (!block)
         return NULL;
     graph().insertBlockAfter(at, block);
     return block;
 }
 
 MBasicBlock *
 IonBuilder::newBlock(MBasicBlock *predecessor, jsbytecode *pc, uint32_t loopDepth)
 {
-    MBasicBlock *block = MBasicBlock::New(graph(), info(), predecessor, pc, MBasicBlock::NORMAL);
+    MBasicBlock *block = MBasicBlock::New(graph(), &analysis(), info(),
+                                          predecessor, pc, MBasicBlock::NORMAL);
     return addBlock(block, loopDepth);
 }
 
 MBasicBlock *
 IonBuilder::newOsrPreheader(MBasicBlock *predecessor, jsbytecode *loopEntry)
 {
     JS_ASSERT((JSOp)*loopEntry == JSOP_LOOPENTRY);
     JS_ASSERT(loopEntry == info().osrPc());
@@ -5714,17 +5732,17 @@ IonBuilder::newOsrPreheader(MBasicBlock 
     MOsrEntry *entry = MOsrEntry::New();
     osrBlock->add(entry);
 
     // Initialize |scopeChain|.
     {
         uint32_t slot = info().scopeChainSlot();
 
         MInstruction *scopev;
-        if (script()->analysis()->usesScopeChain()) {
+        if (analysis().usesScopeChain()) {
             scopev = MOsrScopeChain::New(entry);
         } else {
             // Use an undefined value if the script does not need its scope
             // chain, to match the type that is already being tracked for the
             // slot.
             scopev = MConstant::New(UndefinedValue());
         }
 
@@ -6465,17 +6483,17 @@ IonBuilder::jsop_intrinsic(PropertyName 
     current->push(ins);
 
     return true;
 }
 
 bool
 IonBuilder::jsop_bindname(PropertyName *name)
 {
-    JS_ASSERT(script()->analysis()->usesScopeChain());
+    JS_ASSERT(analysis().usesScopeChain());
 
     MDefinition *scopeChain = current->scopeChain();
     MBindNameCache *ins = MBindNameCache::New(scopeChain, name, script(), pc);
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
@@ -7343,23 +7361,20 @@ IonBuilder::jsop_setelem_dense(types::Te
 
       case types::TemporaryTypeSet::DontConvertToDoubles:
         break;
 
       default:
         MOZ_ASSUME_UNREACHABLE("Unknown double conversion");
     }
 
-    bool writeHole;
+    bool writeHole = false;
     if (safety == SetElem_Normal) {
-        writeHole = script()->analysis()->getCode(pc).arrayWriteHole;
         SetElemICInspector icInspect(inspector->setElemICInspector(pc));
-        writeHole |= icInspect.sawOOBDenseWrite();
-    } else {
-        writeHole = false;
+        writeHole = icInspect.sawOOBDenseWrite();
     }
 
     // Use MStoreElementHole if this SETELEM has written to out-of-bounds
     // indexes in the past. Otherwise, use MStoreElement so that we can hoist
     // the initialized length and bounds check.
     MStoreElementCommon *store;
     if (writeHole && writeOutOfBounds) {
         JS_ASSERT(safety == SetElem_Normal);
@@ -8394,19 +8409,17 @@ IonBuilder::getPropTryInlineAccess(bool 
     return true;
 }
 
 bool
 IonBuilder::getPropTryCache(bool *emitted, PropertyName *name, jsid id,
                             bool barrier, types::TemporaryTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
-    bool accessGetter =
-        script()->analysis()->getCode(pc).accessGetter ||
-        inspector->hasSeenAccessedGetter(pc);
+    bool accessGetter = inspector->hasSeenAccessedGetter(pc);
 
     MDefinition *obj = current->peek(-1);
 
     // The input value must either be an object, or we should have strong suspicions
     // that it can be safely unboxed to an object.
     if (obj->type() != MIRType_Object) {
         types::TemporaryTypeSet *types = obj->resultTypeSet();
         if (!types || !types->objectOrSentinel())
@@ -8826,17 +8839,17 @@ IonBuilder::jsop_object(JSObject *obj)
 }
 
 bool
 IonBuilder::jsop_lambda(JSFunction *fun)
 {
     if (fun->isInterpreted() && !fun->getOrCreateScript(cx))
         return false;
 
-    JS_ASSERT(script()->analysis()->usesScopeChain());
+    JS_ASSERT(analysis().usesScopeChain());
     if (fun->isArrow())
         return abort("bound arrow function");
     if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
         return abort("asm.js module function");
 
     MLambda *ins = MLambda::New(current->scopeChain(), fun);
     current->add(ins);
     current->push(ins);
@@ -8852,33 +8865,33 @@ IonBuilder::jsop_defvar(uint32_t index)
     PropertyName *name = script()->getName(index);
 
     // Bake in attrs.
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
     if (JSOp(*pc) == JSOP_DEFCONST)
         attrs |= JSPROP_READONLY;
 
     // Pass the ScopeChain.
-    JS_ASSERT(script()->analysis()->usesScopeChain());
+    JS_ASSERT(analysis().usesScopeChain());
 
     // Bake the name pointer into the MDefVar.
     MDefVar *defvar = MDefVar::New(name, attrs, current->scopeChain());
     current->add(defvar);
 
     return resumeAfter(defvar);
 }
 
 bool
 IonBuilder::jsop_deffun(uint32_t index)
 {
     JSFunction *fun = script()->getFunction(index);
     if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
         return abort("asm.js module function");
 
-    JS_ASSERT(script()->analysis()->usesScopeChain());
+    JS_ASSERT(analysis().usesScopeChain());
 
     MDefFun *deffun = MDefFun::New(fun, current->scopeChain());
     current->add(deffun);
 
     return resumeAfter(deffun);
 }
 
 bool
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -7,16 +7,17 @@
 #ifndef jit_IonBuilder_h
 #define jit_IonBuilder_h
 
 #ifdef JS_ION
 
 // This file declares the data structures for building a MIRGraph from a
 // JSScript.
 
+#include "jit/BytecodeAnalysis.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 
 namespace js {
 namespace jit {
 
 class CodeGenerator;
 class CallInfo;
@@ -635,20 +636,28 @@ class IonBuilder : public MIRGenerator
     JSScript *script() const { return script_.get(); }
 
     CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
     void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
 
     AbortReason abortReason() { return abortReason_; }
 
   private:
+    bool init();
+
     JSContext *cx;
     BaselineFrame *baselineFrame_;
     AbortReason abortReason_;
 
+    // Basic analysis information about the script.
+    BytecodeAnalysis analysis_;
+    BytecodeAnalysis &analysis() {
+        return analysis_;
+    }
+
     jsbytecode *pc;
     MBasicBlock *current;
     uint32_t loopDepth_;
 
     /* Information used for inline-call builders. */
     MResumePoint *callerResumePoint_;
     jsbytecode *callerPC() {
         return callerResumePoint_ ? callerResumePoint_->pc() : NULL;
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -153,40 +153,40 @@ MIRGraph::forkJoinSlice()
     JS_ASSERT(start);
 
     MForkJoinSlice *slice = new MForkJoinSlice();
     entry->insertAfter(start, slice);
     return slice;
 }
 
 MBasicBlock *
-MBasicBlock::New(MIRGraph &graph, CompileInfo &info,
+MBasicBlock::New(MIRGraph &graph, BytecodeAnalysis *analysis, CompileInfo &info,
                  MBasicBlock *pred, jsbytecode *entryPc, Kind kind)
 {
     JS_ASSERT(entryPc != NULL);
 
     MBasicBlock *block = new MBasicBlock(graph, info, entryPc, kind);
     if (!block->init())
         return NULL;
 
-    if (!block->inherit(pred, 0))
+    if (!block->inherit(analysis, pred, 0))
         return NULL;
 
     return block;
 }
 
 MBasicBlock *
 MBasicBlock::NewPopN(MIRGraph &graph, CompileInfo &info,
                      MBasicBlock *pred, jsbytecode *entryPc, Kind kind, uint32_t popped)
 {
     MBasicBlock *block = new MBasicBlock(graph, info, entryPc, kind);
     if (!block->init())
         return NULL;
 
-    if (!block->inherit(pred, popped))
+    if (!block->inherit(NULL, pred, popped))
         return NULL;
 
     return block;
 }
 
 MBasicBlock *
 MBasicBlock::NewWithResumePoint(MIRGraph &graph, CompileInfo &info,
                                 MBasicBlock *pred, jsbytecode *entryPc,
@@ -205,24 +205,24 @@ MBasicBlock::NewWithResumePoint(MIRGraph
 
     return block;
 }
 
 MBasicBlock *
 MBasicBlock::NewPendingLoopHeader(MIRGraph &graph, CompileInfo &info,
                                   MBasicBlock *pred, jsbytecode *entryPc)
 {
-    return MBasicBlock::New(graph, info, pred, entryPc, PENDING_LOOP_HEADER);
+    return MBasicBlock::New(graph, NULL, info, pred, entryPc, PENDING_LOOP_HEADER);
 }
 
 MBasicBlock *
 MBasicBlock::NewSplitEdge(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred)
 {
     return pred->pc()
-           ? MBasicBlock::New(graph, info, pred, pred->pc(), SPLIT_EDGE)
+           ? MBasicBlock::New(graph, NULL, info, pred, pred->pc(), SPLIT_EDGE)
            : MBasicBlock::NewAsmJS(graph, info, pred, SPLIT_EDGE);
 }
 
 MBasicBlock *
 MBasicBlock::NewAbortPar(MIRGraph &graph, CompileInfo &info,
                          MBasicBlock *pred, jsbytecode *entryPc,
                          MResumePoint *resumePoint)
 {
@@ -318,26 +318,26 @@ MBasicBlock::copySlots(MBasicBlock *from
 {
     JS_ASSERT(stackPosition_ <= from->stackPosition_);
 
     for (uint32_t i = 0; i < stackPosition_; i++)
         slots_[i] = from->slots_[i];
 }
 
 bool
-MBasicBlock::inherit(MBasicBlock *pred, uint32_t popped)
+MBasicBlock::inherit(BytecodeAnalysis *analysis, MBasicBlock *pred, uint32_t popped)
 {
     if (pred) {
         stackPosition_ = pred->stackPosition_;
         JS_ASSERT(stackPosition_ >= popped);
         stackPosition_ -= popped;
         if (kind_ != PENDING_LOOP_HEADER)
             copySlots(pred);
     } else {
-        uint32_t stackDepth = info().script()->analysis()->getCode(pc()).stackDepth;
+        uint32_t stackDepth = analysis->info(pc()).stackDepth;
         stackPosition_ = info().firstStackSlot() + stackDepth;
         JS_ASSERT(stackPosition_ >= popped);
         stackPosition_ -= popped;
     }
 
     JS_ASSERT(info_.nslots() >= stackPosition_);
     JS_ASSERT(!entryResumePoint_);
 
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -13,16 +13,17 @@
 #include "jit/FixedList.h"
 #include "jit/IonAllocPolicy.h"
 #include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 
 namespace js {
 namespace jit {
 
+class BytecodeAnalysis;
 class MBasicBlock;
 class MIRGraph;
 class MStart;
 
 class MDefinitionIterator;
 
 typedef InlineListIterator<MInstruction> MInstructionIterator;
 typedef InlineListReverseIterator<MInstruction> MInstructionReverseIterator;
@@ -41,17 +42,17 @@ class MBasicBlock : public TempObject, p
         SPLIT_EDGE,
         DEAD
     };
 
   private:
     MBasicBlock(MIRGraph &graph, CompileInfo &info, jsbytecode *pc, Kind kind);
     bool init();
     void copySlots(MBasicBlock *from);
-    bool inherit(MBasicBlock *pred, uint32_t popped);
+    bool inherit(BytecodeAnalysis *analysis, MBasicBlock *pred, uint32_t popped);
     bool inheritResumePoint(MBasicBlock *pred);
     void assertUsesAreNotWithin(MUseIterator use, MUseIterator end);
 
     // Does this block do something that forces it to terminate early?
     bool earlyAbort_;
 
     // Pushes a copy of a local variable or argument.
     void pushVariable(uint32_t slot);
@@ -62,17 +63,17 @@ class MBasicBlock : public TempObject, p
 
   public:
     ///////////////////////////////////////////////////////
     ////////// BEGIN GRAPH BUILDING INSTRUCTIONS //////////
     ///////////////////////////////////////////////////////
 
     // Creates a new basic block for a MIR generator. If |pred| is not NULL,
     // its slots and stack depth are initialized from |pred|.
-    static MBasicBlock *New(MIRGraph &graph, CompileInfo &info,
+    static MBasicBlock *New(MIRGraph &graph, BytecodeAnalysis *analysis, CompileInfo &info,
                             MBasicBlock *pred, jsbytecode *entryPc, Kind kind);
     static MBasicBlock *NewPopN(MIRGraph &graph, CompileInfo &info,
                                 MBasicBlock *pred, jsbytecode *entryPc, Kind kind, uint32_t popn);
     static MBasicBlock *NewWithResumePoint(MIRGraph &graph, CompileInfo &info,
                                            MBasicBlock *pred, jsbytecode *entryPc,
                                            MResumePoint *resumePoint);
     static MBasicBlock *NewPendingLoopHeader(MIRGraph &graph, CompileInfo &info,
                                              MBasicBlock *pred, jsbytecode *entryPc);
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -73,23 +73,16 @@ class Bytecode
     bool unconditional : 1;
 
     /* Whether this instruction has been analyzed to get its output defines and stack. */
     bool analyzed : 1;
 
     /* Whether this is a catch/finally entry point. */
     bool exceptionEntry : 1;
 
-    /*
-     * Dynamically observed state about the execution of this opcode. These are
-     * hints about the script for use during compilation.
-     */
-    bool arrayWriteHole: 1;     /* SETELEM which has written to an array hole. */
-    bool accessGetter: 1;       /* Property read on a shape with a getter hook. */
-
     /* Stack depth before this opcode. */
     uint32_t stackDepth;
 
   private:
 
     /* If this is a JSOP_LOOPHEAD or JSOP_LOOPENTRY, information about the loop. */
     LoopAnalysis *loop;
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4035,22 +4035,16 @@ NativeGetInline(JSContext *cx,
         vp.setUndefined();
     }
     if (shape->hasDefaultGetter())
         return true;
 
     {
         jsbytecode *pc;
         JSScript *script = cx->currentScript(&pc);
-        if (script && script->hasAnalysis()) {
-            analyze::Bytecode *code = script->analysis()->maybeCode(pc);
-            if (code)
-                code->accessGetter = true;
-        }
-
 #ifdef JS_ION
         if (script && script->hasBaselineScript()) {
             switch (JSOp(*pc)) {
               case JSOP_GETPROP:
               case JSOP_CALLPROP:
               case JSOP_LENGTH:
                 script->baselineScript()->noteAccessedGetter(pc - script->code);
                 break;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2954,16 +2954,30 @@ JSScript::updateBaselineOrIonRaw()
         baselineOrIonSkipArgCheck = baseline->method()->raw();
     } else {
         baselineOrIonRaw = NULL;
         baselineOrIonSkipArgCheck = NULL;
     }
 #endif
 }
 
+bool
+JSScript::hasLoops()
+{
+    if (!hasTrynotes())
+        return false;
+    JSTryNote *tn = trynotes()->vector;
+    JSTryNote *tnlimit = tn + trynotes()->length;
+    for (; tn < tnlimit; tn++) {
+        if (tn->kind == JSTRY_ITER || tn->kind == JSTRY_LOOP)
+            return true;
+    }
+    return false;
+}
+
 static inline void
 LazyScriptHash(uint32_t lineno, uint32_t column, uint32_t begin, uint32_t end,
                HashNumber hashes[3])
 {
     HashNumber hash = lineno;
     hash = JS_ROTATE_LEFT32(hash, 4) ^ column;
     hash = JS_ROTATE_LEFT32(hash, 4) ^ begin;
     hash = JS_ROTATE_LEFT32(hash, 4) ^ end;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -927,16 +927,18 @@ class JSScript : public js::gc::Cell
         return reinterpret_cast<js::ObjectArray *>(data + regexpsOffset());
     }
 
     js::TryNoteArray *trynotes() {
         JS_ASSERT(hasTrynotes());
         return reinterpret_cast<js::TryNoteArray *>(data + trynotesOffset());
     }
 
+    bool hasLoops();
+
     js::HeapPtrAtom &getAtom(size_t index) const {
         JS_ASSERT(index < natoms);
         return atoms[index];
     }
 
     js::HeapPtrAtom &getAtom(jsbytecode *pc) const {
         JS_ASSERT(pc >= code && pc + sizeof(uint32_t) < code + length);
         return getAtom(GET_UINT32_INDEX(pc));
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1219,34 +1219,31 @@ ModOperation(JSContext *cx, HandleScript
 
     res->setNumber(NumberMod(d1, d2));
     types::TypeScript::MonitorOverflow(cx, script, pc);
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
 SetObjectElementOperation(JSContext *cx, Handle<JSObject*> obj, HandleId id, const Value &value,
-                          bool strict, JSScript *maybeScript = NULL, jsbytecode *pc = NULL)
+                          bool strict, JSScript *script = NULL, jsbytecode *pc = NULL)
 {
-    RootedScript script(cx, maybeScript);
     types::TypeScript::MonitorAssign(cx, obj, id);
 
     if (obj->isNative() && JSID_IS_INT(id)) {
         uint32_t length = obj->getDenseInitializedLength();
         int32_t i = JSID_TO_INT(id);
         if ((uint32_t)i >= length) {
             // Annotate script if provided with information (e.g. baseline)
-            if (script && script->hasAnalysis()) {
-                JS_ASSERT(pc);
-                script->analysis()->getCode(pc).arrayWriteHole = true;
-            }
+            if (script && script->hasBaselineScript() && *pc == JSOP_SETELEM)
+                script->baselineScript()->noteArrayWriteHole(pc - script->code);
         }
     }
 
-    if (obj->isNative() && !obj->setHadElementsAccess(cx))
+    if (obj->isNative() && !JSID_IS_INT(id) && !obj->setHadElementsAccess(cx))
         return false;
 
     RootedValue tmp(cx, value);
     return JSObject::setGeneric(cx, obj, obj, id, &tmp, strict);
 }
 
 static JS_NEVER_INLINE bool
 Interpret(JSContext *cx, RunState &state)