Bug 994957 - Add site-tracking of inlined functions to Ion compilation. r=luke
authorKannan Vijayan <kvijayan@mozilla.com>
Tue, 29 Apr 2014 16:02:04 -0400
changeset 181216 9dff0b9f82945b22ae2bf2308cbf51aaebe4b89c
parent 181215 67e9e1fd1b6d41b5b3a3fef5ff4d52765a113255
child 181217 78d86c9ceee35a66570bb163aa8ff4dac2fda10c
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersluke
bugs994957
milestone32.0a1
Bug 994957 - Add site-tracking of inlined functions to Ion compilation. r=luke
js/src/jit/CompileInfo-inl.h
js/src/jit/CompileInfo.h
js/src/jit/Ion.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/MIR.h
js/src/jit/MIRGraph.cpp
js/src/jit/MIRGraph.h
js/src/jit/ParallelSafetyAnalysis.cpp
--- a/js/src/jit/CompileInfo-inl.h
+++ b/js/src/jit/CompileInfo-inl.h
@@ -3,16 +3,17 @@
  * 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_CompileInfo_inl_h
 #define jit_CompileInfo_inl_h
 
 #include "jit/CompileInfo.h"
+#include "jit/IonAllocPolicy.h"
 
 #include "jsscriptinlines.h"
 
 namespace js {
 namespace jit {
 
 inline RegExpObject *
 CompileInfo::getRegExp(jsbytecode *pc) const
@@ -21,12 +22,42 @@ CompileInfo::getRegExp(jsbytecode *pc) c
 }
 
 inline JSFunction *
 CompileInfo::getFunction(jsbytecode *pc) const
 {
     return script_->getFunction(GET_UINT32_INDEX(pc));
 }
 
+InlineScriptTree *
+InlineScriptTree::New(TempAllocator *allocator, InlineScriptTree *callerTree,
+                      jsbytecode *callerPc, JSScript *script)
+{
+    JS_ASSERT_IF(!callerTree, !callerPc);
+    JS_ASSERT_IF(callerTree, callerTree->script()->containsPC(callerPc));
+
+    // Allocate a new InlineScriptTree
+    void *treeMem = allocator->allocate(sizeof(InlineScriptTree));
+    if (!treeMem)
+        return nullptr;
+
+    // Initialize it.
+    return new (treeMem) InlineScriptTree(callerTree, callerPc, script);
+}
+
+InlineScriptTree *
+InlineScriptTree::addCallee(TempAllocator *allocator, jsbytecode *callerPc,
+                            JSScript *calleeScript)
+{
+    JS_ASSERT(script_ && script_->containsPC(callerPc));
+    InlineScriptTree *calleeTree = New(allocator, this, callerPc, calleeScript);
+    if (!calleeTree)
+        return nullptr;
+
+    calleeTree->nextCallee_ = children_;
+    children_ = calleeTree;
+    return calleeTree;
+}
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_CompileInfo_inl_h */
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -10,16 +10,18 @@
 #include "jsfun.h"
 
 #include "jit/Registers.h"
 #include "vm/ScopeObject.h"
 
 namespace js {
 namespace jit {
 
+class TempAllocator;
+
 inline unsigned
 StartArgSlot(JSScript *script)
 {
     // Reserved slots:
     // Slot 0: Scope chain.
     // Slot 1: Return value.
 
     // When needed:
@@ -36,24 +38,105 @@ CountArgSlots(JSScript *script, JSFuncti
     // Slot x + 1: Argument 1.
     // ...
     // Slot x + n: Argument n.
 
     // Note: when updating this, please also update the assert in SnapshotWriter::startFrame
     return StartArgSlot(script) + (fun ? fun->nargs() + 1 : 0);
 }
 
+
+// The compiler at various points needs to be able to store references to the
+// current inline path (the sequence of scripts and call-pcs that lead to the
+// current function being inlined).
+//
+// To support this, the top-level IonBuilder keeps a tree that records the
+// inlinings done during compilation.
+class InlineScriptTree {
+    // InlineScriptTree for the caller
+    InlineScriptTree *caller_;
+
+    // PC in the caller corresponding to this script.
+    jsbytecode *callerPc_;
+
+    // Script for this entry.
+    JSScript *script_;
+
+    // Child entries (linked together by nextCallee pointer)
+    InlineScriptTree *children_;
+    InlineScriptTree *nextCallee_;
+
+  public:
+    InlineScriptTree(InlineScriptTree *caller, jsbytecode *callerPc, JSScript *script)
+      : caller_(caller), callerPc_(callerPc), script_(script),
+        children_(nullptr), nextCallee_(nullptr)
+    {}
+
+    static InlineScriptTree *New(TempAllocator *allocator, InlineScriptTree *caller,
+                                 jsbytecode *callerPc, JSScript *script);
+
+    InlineScriptTree *addCallee(TempAllocator *allocator, jsbytecode *callerPc,
+                                 JSScript *calleeScript);
+
+    InlineScriptTree *caller() const {
+        return caller_;
+    }
+
+    jsbytecode *callerPc() const {
+        return callerPc_;
+    }
+
+    JSScript *script() const {
+        return script_;
+    }
+
+    InlineScriptTree *children() const {
+        return children_;
+    }
+    InlineScriptTree *nextCallee() const {
+        return nextCallee_;
+    }
+};
+
+class BytecodeSite {
+    // InlineScriptTree identifying innermost active function at site.
+    InlineScriptTree *tree_;
+
+    // Bytecode address within innermost active function.
+    jsbytecode *pc_;
+
+  public:
+    BytecodeSite()
+      : tree_(nullptr), pc_(nullptr)
+    {}
+
+    BytecodeSite(InlineScriptTree *tree, jsbytecode *pc)
+      : tree_(tree), pc_(pc)
+    {}
+
+    InlineScriptTree *tree() const {
+        return tree_;
+    }
+
+    jsbytecode *pc() const {
+        return pc_;
+    }
+};
+
+
 // Contains information about the compilation source for IR being generated.
 class CompileInfo
 {
   public:
     CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
-                ExecutionMode executionMode, bool scriptNeedsArgsObj)
+                ExecutionMode executionMode, bool scriptNeedsArgsObj,
+                InlineScriptTree *inlineScriptTree)
       : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing),
-        executionMode_(executionMode), scriptNeedsArgsObj_(scriptNeedsArgsObj)
+        executionMode_(executionMode), scriptNeedsArgsObj_(scriptNeedsArgsObj),
+        inlineScriptTree_(inlineScriptTree)
     {
         JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
 
         // The function here can flow in from anywhere so look up the canonical
         // function to ensure that we do not try to embed a nursery pointer in
         // jit-code. Precisely because it can flow in from anywhere, it's not
         // guaranteed to be non-lazy. Hence, don't access its script!
         if (fun_) {
@@ -69,17 +152,18 @@ class CompileInfo
         nfixedvars_ = script->nfixedvars();
         nlocals_ = script->nfixed();
         nstack_ = script->nslots() - script->nfixed();
         nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
     }
 
     CompileInfo(unsigned nlocals, ExecutionMode executionMode)
       : script_(nullptr), fun_(nullptr), osrPc_(nullptr), osrStaticScope_(nullptr),
-        constructing_(false), executionMode_(executionMode), scriptNeedsArgsObj_(false)
+        constructing_(false), executionMode_(executionMode), scriptNeedsArgsObj_(false),
+        inlineScriptTree_(nullptr)
     {
         nimplicit_ = 0;
         nargs_ = 0;
         nfixedvars_ = 0;
         nlocals_ = nlocals;
         nstack_ = 1;  /* For FunctionCompiler::pushPhiInput/popPhiOutput */
         nslots_ = nlocals_ + nstack_;
     }
@@ -94,16 +178,19 @@ class CompileInfo
         return constructing_;
     }
     jsbytecode *osrPc() {
         return osrPc_;
     }
     NestedScopeObject *osrStaticScope() const {
         return osrStaticScope_;
     }
+    InlineScriptTree *inlineScriptTree() const {
+        return inlineScriptTree_;
+    }
 
     bool hasOsrAt(jsbytecode *pc) {
         JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
         return pc == osrPc();
     }
 
     jsbytecode *startPC() const {
         return script_->code();
@@ -318,14 +405,16 @@ class CompileInfo
     NestedScopeObject *osrStaticScope_;
     bool constructing_;
     ExecutionMode executionMode_;
 
     // Whether a script needs an arguments object is unstable over compilation
     // since the arguments optimization could be marked as failed on the main
     // thread, so cache a value here and use it throughout for consistency.
     bool scriptNeedsArgsObj_;
+
+    InlineScriptTree *inlineScriptTree_;
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_CompileInfo_h */
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1823,19 +1823,23 @@ IonCompile(JSContext *cx, JSScript *scri
     {
         return AbortReason_Alloc;
     }
 
     MIRGraph *graph = alloc->new_<MIRGraph>(temp);
     if (!graph)
         return AbortReason_Alloc;
 
+    InlineScriptTree *inlineScriptTree = InlineScriptTree::New(temp, nullptr, nullptr, script);
+    if (!inlineScriptTree)
+        return AbortReason_Alloc;
+
     CompileInfo *info = alloc->new_<CompileInfo>(script, script->functionNonDelazifying(), osrPc,
                                                  constructing, executionMode,
-                                                 script->needsArgsObj());
+                                                 script->needsArgsObj(), inlineScriptTree);
     if (!info)
         return AbortReason_Alloc;
 
     BaselineInspector *inspector = alloc->new_<BaselineInspector>(script);
     if (!inspector)
         return AbortReason_Alloc;
 
     BaselineFrameInspector *baselineFrameInspector = nullptr;
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2253,20 +2253,25 @@ jit::AnalyzeNewScriptProperties(JSContex
             return false;
         if (status != Method_Compiled)
             return true;
     }
 
     types::TypeScript::SetThis(cx, script, types::Type::ObjectType(type));
 
     MIRGraph graph(&temp);
+    InlineScriptTree *inlineScriptTree = InlineScriptTree::New(&temp, nullptr, nullptr, script);
+    if (!inlineScriptTree)
+        return false;
+
     CompileInfo info(script, fun,
                      /* osrPc = */ nullptr, /* constructing = */ false,
                      DefinitePropertiesAnalysis,
-                     script->needsArgsObj());
+                     script->needsArgsObj(),
+                     inlineScriptTree);
 
     AutoTempAllocatorRooter root(cx, &temp);
 
     const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_Normal);
 
     types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(temp);
     if (!constraints) {
         js_ReportOutOfMemory(cx);
@@ -2417,20 +2422,24 @@ jit::AnalyzeArgumentsUsage(JSContext *cx
 
     TempAllocator temp(&alloc);
     IonContext ictx(cx, &temp);
 
     if (!cx->compartment()->ensureJitCompartmentExists(cx))
         return false;
 
     MIRGraph graph(&temp);
+    InlineScriptTree *inlineScriptTree = InlineScriptTree::New(&temp, nullptr, nullptr, script);
+    if (!inlineScriptTree)
+        return false;
     CompileInfo info(script, script->functionNonDelazifying(),
                      /* osrPc = */ nullptr, /* constructing = */ false,
                      ArgumentsUsageAnalysis,
-                     /* needsArgsObj = */ true);
+                     /* needsArgsObj = */ true,
+                     inlineScriptTree);
 
     AutoTempAllocatorRooter root(cx, &temp);
 
     const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_Normal);
 
     types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(temp);
     if (!constraints)
         return false;
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1319,17 +1319,17 @@ IonBuilder::traverseBytecode()
 
                           popped[i]->defUseCount() > poppedUses[i]);
                 break;
             }
         }
 #endif
 
         pc += js_CodeSpec[op].length;
-        current->updateTrackedPc(pc);
+        current->updateTrackedSite(bytecodeSite(pc));
     }
 
     return true;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::snoopControlFlow(JSOp op)
 {
@@ -3982,20 +3982,25 @@ IonBuilder::inlineScriptedCall(CallInfo 
             MTypeBarrier *barrier = MTypeBarrier::New(alloc(), callInfo.thisArg(), clonedTypes);
             current->add(barrier);
             callInfo.setThis(barrier);
         }
     }
 
     // Start inlining.
     LifoAlloc *lifoAlloc = alloc_->lifoAlloc();
+    InlineScriptTree *inlineScriptTree =
+        info().inlineScriptTree()->addCallee(alloc_, pc, calleeScript);
+    if (!inlineScriptTree)
+        return false;
     CompileInfo *info = lifoAlloc->new_<CompileInfo>(calleeScript, target,
                                                      (jsbytecode *)nullptr, callInfo.constructing(),
                                                      this->info().executionMode(),
-                                                     /* needsArgsObj = */ false);
+                                                     /* needsArgsObj = */ false,
+                                                     inlineScriptTree);
     if (!info)
         return false;
 
     MIRGraphReturns returns(alloc());
     AutoAccumulateReturns aar(graph(), returns);
 
     // Build the graph.
     IonBuilder inlineBuilder(analysisContext, compartment, options, &alloc(), &graph(), constraints(),
@@ -5722,52 +5727,53 @@ IonBuilder::addBlock(MBasicBlock *block,
     graph().addBlock(block);
     block->setLoopDepth(loopDepth);
     return block;
 }
 
 MBasicBlock *
 IonBuilder::newBlock(MBasicBlock *predecessor, jsbytecode *pc)
 {
-    MBasicBlock *block = MBasicBlock::New(graph(), &analysis(), info(),
-                                          predecessor, pc, MBasicBlock::NORMAL);
+    MBasicBlock *block = MBasicBlock::New(graph(), &analysis(), info(), predecessor,
+                                          bytecodeSite(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);
+    MBasicBlock *block = MBasicBlock::NewWithResumePoint(graph(), info(), predecessor,
+                                                         bytecodeSite(pc), priorResumePoint);
     return addBlock(block, loopDepth_);
 }
 
 MBasicBlock *
 IonBuilder::newBlockPopN(MBasicBlock *predecessor, jsbytecode *pc, uint32_t popped)
 {
-    MBasicBlock *block = MBasicBlock::NewPopN(graph(), info(), predecessor, pc, MBasicBlock::NORMAL, popped);
+    MBasicBlock *block = MBasicBlock::NewPopN(graph(), info(), predecessor, bytecodeSite(pc),
+                                              MBasicBlock::NORMAL, popped);
     return addBlock(block, loopDepth_);
 }
 
 MBasicBlock *
 IonBuilder::newBlockAfter(MBasicBlock *at, MBasicBlock *predecessor, jsbytecode *pc)
 {
-    MBasicBlock *block = MBasicBlock::New(graph(), &analysis(), info(),
-                                          predecessor, pc, MBasicBlock::NORMAL);
+    MBasicBlock *block = MBasicBlock::New(graph(), &analysis(), info(), predecessor,
+                                          bytecodeSite(pc), MBasicBlock::NORMAL);
     if (!block)
         return nullptr;
     graph().insertBlockAfter(at, block);
     return block;
 }
 
 MBasicBlock *
 IonBuilder::newBlock(MBasicBlock *predecessor, jsbytecode *pc, uint32_t loopDepth)
 {
-    MBasicBlock *block = MBasicBlock::New(graph(), &analysis(), info(),
-                                          predecessor, pc, MBasicBlock::NORMAL);
+    MBasicBlock *block = MBasicBlock::New(graph(), &analysis(), info(), predecessor,
+                                          bytecodeSite(pc), MBasicBlock::NORMAL);
     return addBlock(block, loopDepth);
 }
 
 MBasicBlock *
 IonBuilder::newOsrPreheader(MBasicBlock *predecessor, jsbytecode *loopEntry)
 {
     JS_ASSERT(LoopEntryCanIonOsr(loopEntry));
     JS_ASSERT(loopEntry == info().osrPc());
@@ -5938,18 +5944,18 @@ IonBuilder::newOsrPreheader(MBasicBlock 
 MBasicBlock *
 IonBuilder::newPendingLoopHeader(MBasicBlock *predecessor, jsbytecode *pc, bool osr, bool canOsr,
                                  unsigned stackPhiCount)
 {
     loopDepth_++;
     // If this site can OSR, all values on the expression stack are part of the loop.
     if (canOsr)
         stackPhiCount = predecessor->stackDepth() - info().firstStackSlot();
-    MBasicBlock *block = MBasicBlock::NewPendingLoopHeader(graph(), info(), predecessor, pc,
-                                                           stackPhiCount);
+    MBasicBlock *block = MBasicBlock::NewPendingLoopHeader(graph(), info(), predecessor,
+                                                           bytecodeSite(pc), stackPhiCount);
     if (!addBlock(block, loopDepth_))
         return nullptr;
 
     if (osr) {
         // Incorporate type information from the OSR frame into the loop
         // header. The OSR frame may have unexpected types due to type changes
         // within the loop body or due to incomplete profiling information,
         // in which case this may avoid restarts of loop analysis or bailouts
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -844,16 +844,21 @@ class IonBuilder : public MIRGenerator
 
     GSNCache gsn;
     ScopeCoordinateNameCache scopeCoordinateNameCache;
 
     jsbytecode *pc;
     MBasicBlock *current;
     uint32_t loopDepth_;
 
+    BytecodeSite bytecodeSite(jsbytecode *pc) {
+        JS_ASSERT(info().inlineScriptTree()->script()->containsPC(pc));
+        return BytecodeSite(info().inlineScriptTree(), pc);
+    }
+
     /* Information used for inline-call builders. */
     MResumePoint *callerResumePoint_;
     jsbytecode *callerPC() {
         return callerResumePoint_ ? callerResumePoint_->pc() : nullptr;
     }
     IonBuilder *callerBuilder_;
 
     bool oom() {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -312,17 +312,17 @@ class MDefinition : public MNode
     union {
         MDefinition *dependency_;  // Implicit dependency (store, call, etc.) of this instruction.
                                    // Used by alias analysis, GVN and LICM.
         uint32_t virtualRegister_;   // Used by lowering to map definitions to virtual registers.
     };
 
     // Track bailouts by storing the current pc in MIR instruction. Also used
     // for profiling and keeping track of what the last known pc was.
-    jsbytecode *trackedPc_;
+    BytecodeSite trackedSite_;
 
   private:
     enum Flag {
         None = 0,
 #   define DEFINE_FLAG(flag) flag,
         MIR_FLAG_LIST(DEFINE_FLAG)
 #   undef DEFINE_FLAG
         Total
@@ -347,17 +347,17 @@ class MDefinition : public MNode
     MDefinition()
       : id_(0),
         valueNumber_(nullptr),
         range_(nullptr),
         resultType_(MIRType_None),
         resultTypeSet_(nullptr),
         flags_(0),
         dependency_(nullptr),
-        trackedPc_(nullptr)
+        trackedSite_()
     { }
 
     virtual Opcode op() const = 0;
     virtual const char *opName() const = 0;
     void printName(FILE *fp) const;
     static void PrintOpcodeName(FILE *fp, Opcode op);
     virtual void printOpcode(FILE *fp) const;
     void dump(FILE *fp) const;
@@ -367,22 +367,27 @@ class MDefinition : public MNode
     virtual bool neverHoist() const { return false; }
 
     // Also for LICM. Test whether this definition is likely to be a call, which
     // would clobber all or many of the floating-point registers, such that
     // hoisting floating-point constants out of containing loops isn't likely to
     // be worthwhile.
     virtual bool possiblyCalls() const { return false; }
 
-    void setTrackedPc(jsbytecode *pc) {
-        trackedPc_ = pc;
-    }
-
+    void setTrackedSite(const BytecodeSite &site) {
+        trackedSite_ = site;
+    }
+    const BytecodeSite &trackedSite() const {
+        return trackedSite_;
+    }
     jsbytecode *trackedPc() {
-        return trackedPc_;
+        return trackedSite_.pc();
+    }
+    InlineScriptTree *trackedTree() {
+        return trackedSite_.tree();
     }
 
     // Return the range of this value, *before* any bailout checks. Contrast
     // this with the type() method, and the Range constructor which takes an
     // MDefinition*, which describe the value *after* any bailout checks.
     //
     // Warning: Range analysis is removing the bit-operations such as '| 0' at
     // the end of the transformations. Using this function to analyse any
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -156,94 +156,95 @@ MIRGraph::forkJoinContext()
 
     MForkJoinContext *cx = MForkJoinContext::New(alloc());
     entry->insertAfter(start, cx);
     return cx;
 }
 
 MBasicBlock *
 MBasicBlock::New(MIRGraph &graph, BytecodeAnalysis *analysis, CompileInfo &info,
-                 MBasicBlock *pred, jsbytecode *entryPc, Kind kind)
+                 MBasicBlock *pred, const BytecodeSite &site, Kind kind)
 {
-    JS_ASSERT(entryPc != nullptr);
+    JS_ASSERT(site.pc() != nullptr);
 
-    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, entryPc, kind);
+    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, site, kind);
     if (!block->init())
         return nullptr;
 
     if (!block->inherit(graph.alloc(), analysis, pred, 0))
         return nullptr;
 
     return block;
 }
 
 MBasicBlock *
 MBasicBlock::NewPopN(MIRGraph &graph, CompileInfo &info,
-                     MBasicBlock *pred, jsbytecode *entryPc, Kind kind, uint32_t popped)
+                     MBasicBlock *pred, const BytecodeSite &site, Kind kind, uint32_t popped)
 {
-    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, entryPc, kind);
+    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, site, kind);
     if (!block->init())
         return nullptr;
 
     if (!block->inherit(graph.alloc(), nullptr, pred, popped))
         return nullptr;
 
     return block;
 }
 
 MBasicBlock *
 MBasicBlock::NewWithResumePoint(MIRGraph &graph, CompileInfo &info,
-                                MBasicBlock *pred, jsbytecode *entryPc,
+                                MBasicBlock *pred, const BytecodeSite &site,
                                 MResumePoint *resumePoint)
 {
-    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, entryPc, NORMAL);
+    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, site, NORMAL);
 
     resumePoint->block_ = block;
     block->entryResumePoint_ = resumePoint;
 
     if (!block->init())
         return nullptr;
 
     if (!block->inheritResumePoint(pred))
         return nullptr;
 
     return block;
 }
 
 MBasicBlock *
 MBasicBlock::NewPendingLoopHeader(MIRGraph &graph, CompileInfo &info,
-                                  MBasicBlock *pred, jsbytecode *entryPc,
+                                  MBasicBlock *pred, const BytecodeSite &site,
                                   unsigned stackPhiCount)
 {
-    JS_ASSERT(entryPc != nullptr);
+    JS_ASSERT(site.pc() != nullptr);
 
-    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, entryPc, PENDING_LOOP_HEADER);
+    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, site, PENDING_LOOP_HEADER);
     if (!block->init())
         return nullptr;
 
     if (!block->inherit(graph.alloc(), nullptr, pred, 0, stackPhiCount))
         return nullptr;
 
     return block;
 }
 
 MBasicBlock *
 MBasicBlock::NewSplitEdge(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred)
 {
     return pred->pc()
-           ? MBasicBlock::New(graph, nullptr, info, pred, pred->pc(), SPLIT_EDGE)
+           ? MBasicBlock::New(graph, nullptr, info, pred,
+                              BytecodeSite(pred->trackedTree(), pred->pc()), SPLIT_EDGE)
            : MBasicBlock::NewAsmJS(graph, info, pred, SPLIT_EDGE);
 }
 
 MBasicBlock *
 MBasicBlock::NewAbortPar(MIRGraph &graph, CompileInfo &info,
-                         MBasicBlock *pred, jsbytecode *entryPc,
+                         MBasicBlock *pred, const BytecodeSite &site,
                          MResumePoint *resumePoint)
 {
-    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, entryPc, NORMAL);
+    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, site, NORMAL);
 
     resumePoint->block_ = block;
     block->entryResumePoint_ = resumePoint;
 
     if (!block->init())
         return nullptr;
 
     if (!block->addPredecessorWithoutPhis(pred))
@@ -251,17 +252,17 @@ MBasicBlock::NewAbortPar(MIRGraph &graph
 
     block->end(MAbortPar::New(graph.alloc()));
     return block;
 }
 
 MBasicBlock *
 MBasicBlock::NewAsmJS(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred, Kind kind)
 {
-    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, /* entryPC = */ nullptr, kind);
+    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, BytecodeSite(), kind);
     if (!block->init())
         return nullptr;
 
     if (pred) {
         block->stackPosition_ = pred->stackPosition_;
 
         if (block->kind_ == PENDING_LOOP_HEADER) {
             size_t nphis = block->stackPosition_;
@@ -289,36 +290,36 @@ MBasicBlock::NewAsmJS(MIRGraph &graph, C
 
         if (!block->predecessors_.append(pred))
             return nullptr;
     }
 
     return block;
 }
 
-MBasicBlock::MBasicBlock(MIRGraph &graph, CompileInfo &info, jsbytecode *pc, Kind kind)
+MBasicBlock::MBasicBlock(MIRGraph &graph, CompileInfo &info, const BytecodeSite &site, Kind kind)
   : unreachable_(false),
     graph_(graph),
     info_(info),
     predecessors_(graph.alloc()),
     stackPosition_(info_.firstStackSlot()),
     lastIns_(nullptr),
-    pc_(pc),
+    pc_(site.pc()),
     lir_(nullptr),
     start_(nullptr),
     entryResumePoint_(nullptr),
     successorWithPhis_(nullptr),
     positionInPhiSuccessor_(0),
     kind_(kind),
     loopDepth_(0),
     mark_(false),
     immediatelyDominated_(graph.alloc()),
     immediateDominator_(nullptr),
     numDominated_(0),
-    trackedPc_(pc)
+    trackedSite_(site)
 #if defined (JS_ION_PERF)
     , lineno_(0u),
     columnIndex_(0u)
 #endif
 {
 }
 
 bool
@@ -814,37 +815,37 @@ MBasicBlock::discardAllResumePoints(bool
 
 void
 MBasicBlock::insertBefore(MInstruction *at, MInstruction *ins)
 {
     JS_ASSERT(at->block() == this);
     ins->setBlock(this);
     graph().allocDefinitionId(ins);
     instructions_.insertBefore(at, ins);
-    ins->setTrackedPc(at->trackedPc());
+    ins->setTrackedSite(at->trackedSite());
 }
 
 void
 MBasicBlock::insertAfter(MInstruction *at, MInstruction *ins)
 {
     JS_ASSERT(at->block() == this);
     ins->setBlock(this);
     graph().allocDefinitionId(ins);
     instructions_.insertAfter(at, ins);
-    ins->setTrackedPc(at->trackedPc());
+    ins->setTrackedSite(at->trackedSite());
 }
 
 void
 MBasicBlock::add(MInstruction *ins)
 {
     JS_ASSERT(!lastIns_);
     ins->setBlock(this);
     graph().allocDefinitionId(ins);
     instructions_.pushBack(ins);
-    ins->setTrackedPc(trackedPc_);
+    ins->setTrackedSite(trackedSite_);
 }
 
 void
 MBasicBlock::end(MControlInstruction *ins)
 {
     JS_ASSERT(!lastIns_); // Existing control instructions should be removed first.
     JS_ASSERT(ins);
     add(ins);
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -38,17 +38,17 @@ class MBasicBlock : public TempObject, p
         NORMAL,
         PENDING_LOOP_HEADER,
         LOOP_HEADER,
         SPLIT_EDGE,
         DEAD
     };
 
   private:
-    MBasicBlock(MIRGraph &graph, CompileInfo &info, jsbytecode *pc, Kind kind);
+    MBasicBlock(MIRGraph &graph, CompileInfo &info, const BytecodeSite &site, Kind kind);
     bool init();
     void copySlots(MBasicBlock *from);
     bool inherit(TempAllocator &alloc, BytecodeAnalysis *analysis, MBasicBlock *pred,
                  uint32_t popped, unsigned stackPhiCount = 0);
     bool inheritResumePoint(MBasicBlock *pred);
     void assertUsesAreNotWithin(MUseIterator use, MUseIterator end);
 
     // This block cannot be reached by any means.
@@ -64,28 +64,28 @@ class MBasicBlock : public TempObject, p
   public:
     ///////////////////////////////////////////////////////
     ////////// BEGIN GRAPH BUILDING INSTRUCTIONS //////////
     ///////////////////////////////////////////////////////
 
     // Creates a new basic block for a MIR generator. If |pred| is not nullptr,
     // its slots and stack depth are initialized from |pred|.
     static MBasicBlock *New(MIRGraph &graph, BytecodeAnalysis *analysis, CompileInfo &info,
-                            MBasicBlock *pred, jsbytecode *entryPc, Kind kind);
+                            MBasicBlock *pred, const BytecodeSite &site, Kind kind);
     static MBasicBlock *NewPopN(MIRGraph &graph, CompileInfo &info,
-                                MBasicBlock *pred, jsbytecode *entryPc, Kind kind, uint32_t popn);
+                                MBasicBlock *pred, const BytecodeSite &site, Kind kind, uint32_t popn);
     static MBasicBlock *NewWithResumePoint(MIRGraph &graph, CompileInfo &info,
-                                           MBasicBlock *pred, jsbytecode *entryPc,
+                                           MBasicBlock *pred, const BytecodeSite &site,
                                            MResumePoint *resumePoint);
     static MBasicBlock *NewPendingLoopHeader(MIRGraph &graph, CompileInfo &info,
-                                             MBasicBlock *pred, jsbytecode *entryPc,
+                                             MBasicBlock *pred, const BytecodeSite &site,
                                              unsigned loopStateSlots);
     static MBasicBlock *NewSplitEdge(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred);
     static MBasicBlock *NewAbortPar(MIRGraph &graph, CompileInfo &info,
-                                    MBasicBlock *pred, jsbytecode *entryPc,
+                                    MBasicBlock *pred, const BytecodeSite &site,
                                     MResumePoint *resumePoint);
     static MBasicBlock *NewAsmJS(MIRGraph &graph, CompileInfo &info,
                                  MBasicBlock *pred, Kind kind);
 
     bool dominates(const MBasicBlock *other) const;
 
     void setId(uint32_t id) {
         id_ = id;
@@ -464,22 +464,28 @@ class MBasicBlock : public TempObject, p
 
     void dumpStack(FILE *fp);
 
     void dump(FILE *fp);
     void dump();
 
     // Track bailouts by storing the current pc in MIR instruction added at this
     // cycle. This is also used for tracking calls when profiling.
-    void updateTrackedPc(jsbytecode *pc) {
-        trackedPc_ = pc;
+    void updateTrackedSite(const BytecodeSite &site) {
+        JS_ASSERT(site.tree() == trackedSite_.tree());
+        trackedSite_ = site;
     }
-
-    jsbytecode *trackedPc() {
-        return trackedPc_;
+    const BytecodeSite &trackedSite() const {
+        return trackedSite_;
+    }
+    jsbytecode *trackedPc() const {
+        return trackedSite_.pc();
+    }
+    InlineScriptTree *trackedTree() const {
+        return trackedSite_.tree();
     }
 
   private:
     MIRGraph &graph_;
     CompileInfo &info_; // Each block originates from a particular script.
     InlineList<MInstruction> instructions_;
     Vector<MBasicBlock *, 1, IonAllocPolicy> predecessors_;
     InlineForwardList<MPhi> phis_;
@@ -500,17 +506,17 @@ class MBasicBlock : public TempObject, p
 
     // Utility mark for traversal algorithms.
     bool mark_;
 
     Vector<MBasicBlock *, 1, IonAllocPolicy> immediatelyDominated_;
     MBasicBlock *immediateDominator_;
     size_t numDominated_;
 
-    jsbytecode *trackedPc_;
+    BytecodeSite trackedSite_;
 
 #if defined (JS_ION_PERF)
     unsigned lineno_;
     unsigned columnIndex_;
 
   public:
     void setLineno(unsigned l) { lineno_ = l; }
     unsigned lineno() const { return lineno_; }
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -485,18 +485,19 @@ ParallelSafetyVisitor::convertToBailout(
         MBasicBlock *pred = block->getPredecessor(i);
 
         // We only care about incoming edges from reachable predecessors.
         if (!pred->isMarked())
             continue;
 
         // create bailout block to insert on this edge
         MBasicBlock *bailBlock = MBasicBlock::NewAbortPar(graph_, block->info(), pred,
-                                                               block->pc(),
-                                                               block->entryResumePoint());
+                                                          BytecodeSite(block->trackedTree(),
+                                                                       block->pc()),
+                                                          block->entryResumePoint());
         if (!bailBlock)
             return false;
 
         // if `block` had phis, we are replacing it with `bailBlock` which does not
         if (pred->successorWithPhis() == block)
             pred->setSuccessorWithPhis(nullptr, 0);
 
         // redirect the predecessor to the bailout block