Bug 1127156 - Rework optimization tracking JSAPI to be more usable from the profiler. (r=djvj)
☠☠ backed out by db6d27b3a8f0 ☠ ☠
authorShu-yu Guo <shu@rfrn.org>
Tue, 03 Feb 2015 18:55:25 -0800
changeset 227303 66c90dec344b46bf967a7a4d642c6c28a23892bb
parent 227302 9fcc56bc052ba55dc8c2f2ff3cd2abf515241423
child 227304 f1d372961125cf503129bb725cfbe69623ca599b
push id55089
push usershu@rfrn.org
push dateWed, 04 Feb 2015 02:53:33 +0000
treeherdermozilla-inbound@f1d372961125 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdjvj
bugs1127156
milestone38.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 1127156 - Rework optimization tracking JSAPI to be more usable from the profiler. (r=djvj)
js/public/ProfilingFrameIterator.h
js/public/TrackedOptimizationInfo.h
js/src/jit/CodeGenerator.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/JitcodeMap.h
js/src/jit/MCallOptimize.cpp
js/src/jit/OptimizationTracking.cpp
js/src/jit/OptimizationTracking.h
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jit/shared/CodeGenerator-shared.h
js/src/jsapi-tests/testIntTypesABI.cpp
js/src/jscntxt.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/moz.build
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -98,16 +98,18 @@ class JS_PUBLIC_API(ProfilingFrameIterat
 
     struct Frame
     {
         FrameKind kind;
         void *stackAddress;
         void *returnAddress;
         void *activation;
         const char *label;
+        bool hasTrackedOptimizations;
+        uint8_t trackedOptimizationIndex;
     };
     uint32_t extractStack(Frame *frames, uint32_t offset, uint32_t end) const;
 
   private:
     void iteratorConstruct(const RegisterState &state);
     void iteratorConstruct();
     void iteratorDestroy();
     bool iteratorDone();
new file mode 100644
--- /dev/null
+++ b/js/public/TrackedOptimizationInfo.h
@@ -0,0 +1,316 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_TrackedOptimizationInfo_h
+#define js_TrackedOptimizationInfo_h
+
+namespace JS {
+
+#define TRACKED_STRATEGY_LIST(_)                        \
+    _(GetProp_ArgumentsLength,                          \
+      "getprop arguments.length")                       \
+    _(GetProp_ArgumentsCallee,                          \
+      "getprop arguments.callee")                       \
+    _(GetProp_InferredConstant,                         \
+      "getprop inferred constant")                      \
+    _(GetProp_Constant,                                 \
+      "getprop constant")                               \
+    _(GetProp_TypedObject,                              \
+      "getprop TypedObject")                            \
+    _(GetProp_DefiniteSlot,                             \
+      "getprop definite slot")                          \
+    _(GetProp_Unboxed,                                  \
+      "getprop unboxed object")                         \
+    _(GetProp_CommonGetter,                             \
+      "getprop common getter")                          \
+    _(GetProp_InlineAccess,                             \
+      "getprop inline access")                          \
+    _(GetProp_Innerize,                                 \
+      "getprop innerize (access on global window)")     \
+    _(GetProp_InlineCache,                              \
+      "getprop IC")                                     \
+                                                        \
+    _(SetProp_CommonSetter,                             \
+      "setprop common setter")                          \
+    _(SetProp_TypedObject,                              \
+      "setprop TypedObject")                            \
+    _(SetProp_DefiniteSlot,                             \
+      "setprop definite slot")                          \
+    _(SetProp_Unboxed,                                  \
+      "setprop unboxed object")                         \
+    _(SetProp_InlineAccess,                             \
+      "setprop inline access")                          \
+                                                        \
+    _(GetElem_TypedObject,                              \
+      "getprop TypedObject")                            \
+    _(GetElem_Dense,                                    \
+      "getelem dense")                                  \
+    _(GetElem_TypedStatic,                              \
+      "getelem TypedArray static")                      \
+    _(GetElem_TypedArray,                               \
+      "getelem TypedArray")                             \
+    _(GetElem_String,                                   \
+      "getelem string")                                 \
+    _(GetElem_Arguments,                                \
+      "getelem arguments")                              \
+    _(GetElem_ArgumentsInlined,                         \
+      "getelem arguments inlined")                      \
+    _(GetElem_InlineCache,                              \
+      "getelem IC")                                     \
+                                                        \
+    _(SetElem_TypedObject,                              \
+      "setelem TypedObject")                            \
+    _(SetElem_TypedStatic,                              \
+      "setelem TypedArray static")                      \
+    _(SetElem_TypedArray,                               \
+      "setelem TypedArray")                             \
+    _(SetElem_Dense,                                    \
+      "setelem dense")                                  \
+    _(SetElem_Arguments,                                \
+      "setelem arguments")                              \
+    _(SetElem_InlineCache,                              \
+      "setelem IC")                                     \
+                                                        \
+    _(Call_Inline,                                      \
+      "call inline")
+
+
+// Ordering is important below. All outcomes before GenericSuccess will be
+// considered failures, and all outcomes after GenericSuccess will be
+// considered successes.
+#define TRACKED_OUTCOME_LIST(_)                                         \
+    _(GenericFailure,                                                   \
+      "failure")                                                        \
+    _(Disabled,                                                         \
+      "disabled")                                                       \
+    _(NoTypeInfo,                                                       \
+      "no type info")                                                   \
+    _(NoAnalysisInfo,                                                   \
+      "no newscript analysis")                                          \
+    _(NoShapeInfo,                                                      \
+      "cannot determine shape")                                         \
+    _(UnknownObject,                                                    \
+      "unknown object")                                                 \
+    _(UnknownProperties,                                                \
+      "unknown properties")                                             \
+    _(Singleton,                                                        \
+      "is singleton")                                                   \
+    _(NotSingleton,                                                     \
+      "is not singleton")                                               \
+    _(NotFixedSlot,                                                     \
+      "property not in fixed slot")                                     \
+    _(InconsistentFixedSlot,                                            \
+      "property not in a consistent fixed slot")                        \
+    _(NotObject,                                                        \
+      "not definitely an object")                                       \
+    _(NotStruct,                                                        \
+      "not definitely a TypedObject struct")                            \
+    _(NotUnboxed,                                                       \
+      "not definitely an unboxed object")                               \
+    _(StructNoField,                                                    \
+      "struct doesn't definitely have field")                           \
+    _(InconsistentFieldType,                                            \
+      "unboxed property does not have consistent type")                 \
+    _(InconsistentFieldOffset,                                          \
+      "unboxed property does not have consistent offset")               \
+    _(NeedsTypeBarrier,                                                 \
+      "needs type barrier")                                             \
+    _(InDictionaryMode,                                                 \
+      "object in dictionary mode")                                      \
+    _(NoProtoFound,                                                     \
+      "no proto found")                                                 \
+    _(MultiProtoPaths,                                                  \
+      "not all paths to property go through same proto")                \
+    _(NonWritableProperty,                                              \
+      "non-writable property")                                          \
+    _(ProtoIndexedProps,                                                \
+      "prototype has indexed properties")                               \
+    _(ArrayBadFlags,                                                    \
+      "array observed to be sparse, overflowed .length, or has been iterated") \
+    _(ArrayDoubleConversion,                                            \
+      "array has ambiguous double conversion")                          \
+    _(ArrayRange,                                                       \
+      "array range issue (.length problems)")                           \
+    _(ArraySeenNegativeIndex,                                           \
+      "has seen array access with negative index")                      \
+    _(TypedObjectNeutered,                                              \
+      "TypedObject might have been neutered")                           \
+    _(TypedObjectArrayRange,                                            \
+      "TypedObject array of unknown length")                            \
+    _(AccessNotDense,                                                   \
+      "access not on dense native (check receiver, index, and result types)") \
+    _(AccessNotTypedObject,                                             \
+      "access not on typed array (check receiver and index types)")     \
+    _(AccessNotTypedArray,                                              \
+      "access not on typed array (check receiver, index, and result types)") \
+    _(AccessNotString,                                                  \
+      "getelem not on string (check receiver and index types)")         \
+    _(StaticTypedArrayUint32,                                           \
+      "static uint32 arrays currently cannot be optimized")             \
+    _(StaticTypedArrayCantComputeMask,                                  \
+      "can't compute mask for static typed array access (index isn't constant or not int32)") \
+    _(OutOfBounds,                                                      \
+      "observed out of bounds access")                                  \
+    _(GetElemStringNotCached,                                           \
+      "getelem on strings is not inline cached")                        \
+    _(NonNativeReceiver,                                                \
+      "observed non-native receiver")                                   \
+    _(IndexType,                                                        \
+      "index type must be int32, string, or symbol")                    \
+    _(SetElemNonDenseNonTANotCached,                                    \
+      "setelem on non-dense non-TAs are not inline cached")             \
+                                                                        \
+    _(CantInlineGeneric,                                                \
+      "can't inline")                                                   \
+    _(CantInlineNoTarget,                                               \
+      "can't inline: no target")                                        \
+    _(CantInlineNotInterpreted,                                         \
+      "can't inline: not interpreted")                                  \
+    _(CantInlineNoBaseline,                                             \
+      "can't inline: no baseline code")                                 \
+    _(CantInlineLazy,                                                   \
+      "can't inline: lazy script")                                      \
+    _(CantInlineNotConstructor,                                         \
+      "can't inline: calling non-constructor with 'new'")               \
+    _(CantInlineDisabledIon,                                            \
+      "can't inline: ion disabled for callee")                          \
+    _(CantInlineTooManyArgs,                                            \
+      "can't inline: too many arguments")                               \
+    _(CantInlineRecursive,                                              \
+      "can't inline: recursive")                                        \
+    _(CantInlineHeavyweight,                                            \
+      "can't inline: heavyweight")                                      \
+    _(CantInlineNeedsArgsObj,                                           \
+      "can't inline: needs arguments object")                           \
+    _(CantInlineDebuggee,                                               \
+      "can't inline: debuggee")                                         \
+    _(CantInlineUnknownProps,                                           \
+      "can't inline: type has unknown properties")                      \
+    _(CantInlineExceededDepth,                                          \
+      "can't inline: exceeded inlining depth")                          \
+    _(CantInlineBigLoop,                                                \
+      "can't inline: big function with a loop")                         \
+    _(CantInlineBigCaller,                                              \
+      "can't inline: big caller")                                       \
+    _(CantInlineBigCallee,                                              \
+      "can't inline: big callee")                                       \
+    _(CantInlineNotHot,                                                 \
+      "can't inline: not hot enough")                                   \
+    _(CantInlineNotInDispatch,                                          \
+      "can't inline: not in dispatch table")                            \
+    _(CantInlineNativeBadForm,                                          \
+      "can't inline native: bad form (arity mismatch/constructing)")    \
+    _(CantInlineNativeBadType,                                          \
+      "can't inline native: bad argument or return type observed")      \
+    _(CantInlineNativeNoTemplateObj,                                    \
+      "can't inline native: no template object")                        \
+    _(CantInlineBound,                                                  \
+      "can't inline bound function invocation")                         \
+                                                                        \
+    _(GenericSuccess,                                                   \
+      "success")                                                        \
+    _(Inlined,                                                          \
+      "inlined")                                                        \
+    _(DOM,                                                              \
+      "DOM")                                                            \
+    _(Monomorphic,                                                      \
+      "monomorphic")                                                    \
+    _(Polymorphic,                                                      \
+      "polymorphic")
+
+#define TRACKED_TYPESITE_LIST(_)                \
+    _(Receiver,                                 \
+      "receiver object")                        \
+    _(Index,                                    \
+      "index")                                  \
+    _(Value,                                    \
+      "value")                                  \
+    _(Call_Target,                              \
+      "call target")                            \
+    _(Call_This,                                \
+      "call 'this'")                            \
+    _(Call_Arg,                                 \
+      "call argument")                          \
+    _(Call_Return,                              \
+      "call return")
+
+enum class TrackedStrategy : uint32_t {
+#define STRATEGY_OP(name, msg) name,
+    TRACKED_STRATEGY_LIST(STRATEGY_OP)
+#undef STRATEGY_OPT
+
+    Count
+};
+
+enum class TrackedOutcome : uint32_t {
+#define OUTCOME_OP(name, msg) name,
+    TRACKED_OUTCOME_LIST(OUTCOME_OP)
+#undef OUTCOME_OP
+
+    Count
+};
+
+enum class TrackedTypeSite : uint32_t {
+#define TYPESITE_OP(name, msg) name,
+    TRACKED_TYPESITE_LIST(TYPESITE_OP)
+#undef TYPESITE_OP
+
+    Count
+};
+
+extern JS_PUBLIC_API(const char *)
+TrackedStrategyString(TrackedStrategy strategy);
+
+extern JS_PUBLIC_API(const char *)
+TrackedOutcomeString(TrackedOutcome outcome);
+
+extern JS_PUBLIC_API(const char *)
+TrackedTypeSiteString(TrackedTypeSite site);
+
+struct ForEachTrackedOptimizationAttemptOp
+{
+    virtual void operator()(TrackedStrategy strategy, TrackedOutcome outcome) = 0;
+};
+
+JS_PUBLIC_API(void)
+ForEachTrackedOptimizationAttempt(JSRuntime *rt, void *addr, uint8_t index,
+                                  ForEachTrackedOptimizationAttemptOp &op);
+
+struct ForEachTrackedOptimizationTypeInfoOp
+{
+    // Called 0+ times per entry, once for each type in the type set that Ion
+    // saw during MIR construction. readType is always called _before_
+    // operator() on the same entry.
+    //
+    // The keyedBy parameter describes how the type is keyed:
+    //   - "primitive"   for primitive types
+    //   - "constructor" for object types tied to a scripted constructor
+    //                   function.
+    //   - "alloc site"  for object types tied to an allocation site.
+    //   - "prototype"   for object types tied neither to a constructor nor
+    //                   to an allocation site.
+    //
+    // The name parameter is the string representation of the type. If the
+    // type is keyed by "constructor", or if the type itself refers to a
+    // scripted function, the name is the function's displayAtom.
+    //
+    // If the type is keyed by "constructor", "alloc site", or if the type
+    // itself refers to a scripted function, the location and lineno
+    // parameters will be respectively non-nullptr and non-0.
+    virtual void readType(const char *keyedBy, const char *name,
+                          const char *location, unsigned lineno) = 0;
+
+    // Called once per entry.
+    virtual void operator()(TrackedTypeSite site, const char *mirType) = 0;
+};
+
+extern JS_PUBLIC_API(void)
+ForEachTrackedOptimizationTypeInfo(JSRuntime *rt, void *addr, uint8_t index,
+                                   ForEachTrackedOptimizationTypeInfoOp &op);
+
+} // namespace JS
+
+#endif // js_TrackedOptimizationInfo_h
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7323,17 +7323,17 @@ CodeGenerator::link(JSContext *cx, types
         }
 
         // nativeToBytecodeScriptList_ is no longer needed.
         js_free(nativeToBytecodeScriptList_);
 
         // Generate the tracked optimizations map.
         if (isOptimizationTrackingEnabled()) {
             // Treat OOMs and failures as if optimization tracking were turned off.
-            types::TypeSet::TypeList *allTypes = cx->new_<types::TypeSet::TypeList>();
+            IonTrackedTypeVector *allTypes = cx->new_<IonTrackedTypeVector>();
             if (allTypes && generateCompactTrackedOptimizationsMap(cx, code, allTypes)) {
                 const uint8_t *optsRegionTableAddr = trackedOptimizationsMap_ +
                                                      trackedOptimizationsRegionTableOffset_;
                 const IonTrackedOptimizationsRegionTable *optsRegionTable =
                     (const IonTrackedOptimizationsRegionTable *) optsRegionTableAddr;
                 const uint8_t *optsTypesTableAddr = trackedOptimizationsMap_ +
                                                     trackedOptimizationsTypesTableOffset_;
                 const IonTrackedOptimizationsTypesTable *optsTypesTable =
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -33,16 +33,20 @@
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::AssertedCast;
 using mozilla::DebugOnly;
 using mozilla::Maybe;
 
+using JS::TrackedStrategy;
+using JS::TrackedOutcome;
+using JS::TrackedTypeSite;
+
 class jit::BaselineFrameInspector
 {
   public:
     types::Type thisType;
     JSObject *singletonScopeChain;
 
     Vector<types::Type, 4, JitAllocPolicy> argTypes;
     Vector<types::Type, 4, JitAllocPolicy> varTypes;
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -1111,60 +1111,60 @@ class IonBuilder
     MGetPropertyCache *maybeFallbackFunctionGetter_;
 
     // Used in tracking outcomes of optimization strategies for devtools.
     void startTrackingOptimizations();
 
     // The track* methods below are called often. Do not combine them with the
     // unchecked variants, despite the unchecked variants having no other
     // callers.
-    void trackTypeInfo(TrackedTypeSite site, MIRType mirType,
+    void trackTypeInfo(JS::TrackedTypeSite site, MIRType mirType,
                        types::TemporaryTypeSet *typeSet)
     {
         if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
             trackTypeInfoUnchecked(site, mirType, typeSet);
     }
-    void trackTypeInfo(TrackedTypeSite site, JSObject *obj) {
+    void trackTypeInfo(JS::TrackedTypeSite site, JSObject *obj) {
         if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
             trackTypeInfoUnchecked(site, obj);
     }
     void trackTypeInfo(CallInfo &callInfo) {
         if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
             trackTypeInfoUnchecked(callInfo);
     }
-    void trackOptimizationAttempt(TrackedStrategy strategy) {
+    void trackOptimizationAttempt(JS::TrackedStrategy strategy) {
         if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
             trackOptimizationAttemptUnchecked(strategy);
     }
     void amendOptimizationAttempt(uint32_t index) {
         if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
             amendOptimizationAttemptUnchecked(index);
     }
-    void trackOptimizationOutcome(TrackedOutcome outcome) {
+    void trackOptimizationOutcome(JS::TrackedOutcome outcome) {
         if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
             trackOptimizationOutcomeUnchecked(outcome);
     }
     void trackOptimizationSuccess() {
         if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
             trackOptimizationSuccessUnchecked();
     }
     void trackInlineSuccess(InliningStatus status = InliningStatus_Inlined) {
         if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
             trackInlineSuccessUnchecked(status);
     }
 
     // Out-of-line variants that don't check if optimization tracking is
     // enabled.
-    void trackTypeInfoUnchecked(TrackedTypeSite site, MIRType mirType,
+    void trackTypeInfoUnchecked(JS::TrackedTypeSite site, MIRType mirType,
                                 types::TemporaryTypeSet *typeSet);
-    void trackTypeInfoUnchecked(TrackedTypeSite site, JSObject *obj);
+    void trackTypeInfoUnchecked(JS::TrackedTypeSite site, JSObject *obj);
     void trackTypeInfoUnchecked(CallInfo &callInfo);
-    void trackOptimizationAttemptUnchecked(TrackedStrategy strategy);
+    void trackOptimizationAttemptUnchecked(JS::TrackedStrategy strategy);
     void amendOptimizationAttemptUnchecked(uint32_t index);
-    void trackOptimizationOutcomeUnchecked(TrackedOutcome outcome);
+    void trackOptimizationOutcomeUnchecked(JS::TrackedOutcome outcome);
     void trackOptimizationSuccessUnchecked();
     void trackInlineSuccessUnchecked(InliningStatus status);
 };
 
 class CallInfo
 {
     MDefinition *fun_;
     MDefinition *thisArg_;
--- a/js/src/jit/JitcodeMap.h
+++ b/js/src/jit/JitcodeMap.h
@@ -110,24 +110,24 @@ class JitcodeGlobalEntry
         // optsRegionTable_ points to the table within the compact
         // optimizations map indexing all regions that have tracked
         // optimization attempts. optsTypesTable_ is the tracked typed info
         // associated with the attempts vectors; it is the same length as the
         // attempts table. optsAttemptsTable_ is the table indexing those
         // attempts vectors.
         //
         // All pointers point into the same block of memory; the beginning of
-        // the block is optimizationRegionTable_->payloadStart().
+        // the block is optRegionTable_->payloadStart().
         const IonTrackedOptimizationsRegionTable *optsRegionTable_;
         const IonTrackedOptimizationsTypesTable *optsTypesTable_;
         const IonTrackedOptimizationsAttemptsTable *optsAttemptsTable_;
 
         // The types table above records type sets, which have been gathered
         // into one vector here.
-        types::TypeSet::TypeList *optsAllTypes_;
+        IonTrackedTypeVector *optsAllTypes_;
 
         struct ScriptNamePair {
             JSScript *script;
             char *str;
         };
 
         struct SizedScriptList {
             uint32_t size;
@@ -158,17 +158,17 @@ class JitcodeGlobalEntry
             optsTypesTable_ = nullptr;
             optsAllTypes_ = nullptr;
             optsAttemptsTable_ = nullptr;
         }
 
         void initTrackedOptimizations(const IonTrackedOptimizationsRegionTable *regionTable,
                                       const IonTrackedOptimizationsTypesTable *typesTable,
                                       const IonTrackedOptimizationsAttemptsTable *attemptsTable,
-                                      types::TypeSet::TypeList *allTypes)
+                                      IonTrackedTypeVector *allTypes)
         {
             optsRegionTable_ = regionTable;
             optsTypesTable_ = typesTable;
             optsAttemptsTable_ = attemptsTable;
             optsAllTypes_ = allTypes;
         }
 
         SizedScriptList *sizedScriptList() const {
@@ -209,17 +209,32 @@ class JitcodeGlobalEntry
 
         uint32_t callStackAtAddr(JSRuntime *rt, void *ptr, const char **results,
                                  uint32_t maxResults) const;
 
         bool hasTrackedOptimizations() const {
             return !!optsRegionTable_;
         }
 
-        bool optimizationAttemptsAtAddr(void *ptr, mozilla::Maybe<AttemptsVector> &attempts);
+        IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) {
+            MOZ_ASSERT(hasTrackedOptimizations());
+            return optsAttemptsTable_->entry(index);
+        }
+
+        IonTrackedOptimizationsTypeInfo trackedOptimizationTypeInfo(uint8_t index) {
+            MOZ_ASSERT(hasTrackedOptimizations());
+            return optsTypesTable_->entry(index);
+        }
+
+        const IonTrackedTypeVector *allTrackedTypes() {
+            MOZ_ASSERT(hasTrackedOptimizations());
+            return optsAllTypes_;
+        }
+
+        mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr(void *ptr);
     };
 
     struct BaselineEntry : public BaseEntry
     {
         JSScript *script_;
         const char *str_;
 
         // Last location that caused Ion to abort compilation and the reason
@@ -540,16 +555,56 @@ class JitcodeGlobalEntry
     // at this location.
     uint32_t lookupInlineCallDepth(void *ptr);
 
     // Compare two global entries.
     static int compare(const JitcodeGlobalEntry &ent1, const JitcodeGlobalEntry &ent2);
 
     // Compute a profiling string for a given script.
     static char *createScriptString(JSContext *cx, JSScript *script, size_t *length=nullptr);
+
+    bool hasTrackedOptimizations() const {
+        switch (kind()) {
+          case Ion:
+            return ionEntry().hasTrackedOptimizations();
+          case Baseline:
+          case IonCache:
+          case Dummy:
+            break;
+          default:
+            MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
+        }
+        return false;
+    }
+
+    mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr(void *addr) {
+        switch (kind()) {
+          case Ion:
+            return ionEntry().trackedOptimizationIndexAtAddr(addr);
+          case Baseline:
+          case IonCache:
+          case Dummy:
+            break;
+          default:
+            MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
+        }
+        return mozilla::Nothing();
+    }
+
+    IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) {
+        return ionEntry().trackedOptimizationAttempts(index);
+    }
+
+    IonTrackedOptimizationsTypeInfo trackedOptimizationTypeInfo(uint8_t index) {
+        return ionEntry().trackedOptimizationTypeInfo(index);
+    }
+
+    const IonTrackedTypeVector *allTrackedTypes() {
+        return ionEntry().allTrackedTypes();
+    }
 };
 
 /*
  * Global table of JitcodeGlobalEntry values sorted by native address range.
  */
 class JitcodeGlobalTable
 {
   public:
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -19,16 +19,20 @@
 
 #include "jsscriptinlines.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/StringObject-inl.h"
 
 using mozilla::ArrayLength;
 
+using JS::TrackedStrategy;
+using JS::TrackedOutcome;
+using JS::TrackedTypeSite;
+
 namespace js {
 namespace jit {
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
 {
     MOZ_ASSERT(target->isNative());
     JSNative native = target->native();
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -5,28 +5,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/OptimizationTracking.h"
 
 #include "ds/Sort.h"
 #include "jit/IonBuilder.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitSpewer.h"
+#include "js/TrackedOptimizationInfo.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::Maybe;
 using mozilla::Some;
 using mozilla::Nothing;
 
-typedef CodeGeneratorShared::NativeToTrackedOptimizations NativeToTrackedOptimizations;
+using JS::TrackedStrategy;
+using JS::TrackedOutcome;
+using JS::TrackedTypeSite;
+using JS::ForEachTrackedOptimizationAttemptOp;
+using JS::ForEachTrackedOptimizationTypeInfoOp;
 
 bool
-TrackedOptimizations::trackTypeInfo(TrackedTypeInfo &&ty)
+TrackedOptimizations::trackTypeInfo(OptimizationTypeInfo &&ty)
 {
     return types_.append(mozilla::Move(ty));
 }
 
 bool
 TrackedOptimizations::trackAttempt(TrackedStrategy strategy)
 {
     OptimizationAttempt attempt(strategy, TrackedOutcome::GenericFailure);
@@ -62,131 +67,132 @@ VectorContentsMatch(const Vec *xs, const
         MOZ_ASSERT(y != ys->end());
         if (*x != *y)
             return false;
     }
     return true;
 }
 
 bool
-TrackedOptimizations::matchTypes(const TempTrackedTypeInfoVector &other) const
+TrackedOptimizations::matchTypes(const TempOptimizationTypeInfoVector &other) const
 {
     return VectorContentsMatch(&types_, &other);
 }
 
 bool
-TrackedOptimizations::matchAttempts(const TempAttemptsVector &other) const
+TrackedOptimizations::matchAttempts(const TempOptimizationAttemptsVector &other) const
 {
     return VectorContentsMatch(&attempts_, &other);
 }
 
-#ifdef DEBUG
-static const char *
-StrategyString(TrackedStrategy strategy)
+JS_PUBLIC_API(const char *)
+JS::TrackedStrategyString(TrackedStrategy strategy)
 {
     switch (strategy) {
 #define STRATEGY_CASE(name, msg)                  \
       case TrackedStrategy::name:                 \
         return msg;
     TRACKED_STRATEGY_LIST(STRATEGY_CASE)
 #undef STRATEGY_CASE
 
       default:
         MOZ_CRASH("bad strategy");
     }
 }
 
-static const char *
-OutcomeString(TrackedOutcome outcome)
+JS_PUBLIC_API(const char *)
+JS::TrackedOutcomeString(TrackedOutcome outcome)
 {
     switch (outcome) {
 #define OUTCOME_CASE(name, msg)                   \
       case TrackedOutcome::name:                  \
         return msg;
       TRACKED_OUTCOME_LIST(OUTCOME_CASE)
 #undef OUTCOME_CASE
 
       default:
         MOZ_CRASH("bad outcome");
     }
 }
 
-static const char *
-TypeSiteString(TrackedTypeSite kind)
+JS_PUBLIC_API(const char *)
+JS::TrackedTypeSiteString(TrackedTypeSite site)
 {
-    switch (kind) {
+    switch (site) {
 #define TYPESITE_CASE(name, msg)                  \
       case TrackedTypeSite::name:                 \
         return msg;
       TRACKED_TYPESITE_LIST(TYPESITE_CASE)
 #undef TYPESITE_CASE
 
       default:
         MOZ_CRASH("bad type site");
     }
 }
-#endif // DEBUG
 
 void
-SpewTempTrackedTypeInfoVector(const TempTrackedTypeInfoVector *types, const char *indent = nullptr)
+SpewTempOptimizationTypeInfoVector(const TempOptimizationTypeInfoVector *types,
+                                   const char *indent = nullptr)
 {
 #ifdef DEBUG
-    for (const TrackedTypeInfo *t = types->begin(); t != types->end(); t++) {
-        JitSpewStart(JitSpew_OptimizationTracking, "   %s%s of type %s, type set", indent ? indent : "",
-                     TypeSiteString(t->site()), StringFromMIRType(t->mirType()));
+    for (const OptimizationTypeInfo *t = types->begin(); t != types->end(); t++) {
+        JitSpewStart(JitSpew_OptimizationTracking, "   %s%s of type %s, type set",
+                     indent ? indent : "",
+                     TrackedTypeSiteString(t->site()), StringFromMIRType(t->mirType()));
         for (uint32_t i = 0; i < t->types().length(); i++)
             JitSpewCont(JitSpew_OptimizationTracking, " %s", types::TypeString(t->types()[i]));
         JitSpewFin(JitSpew_OptimizationTracking);
     }
 #endif
 }
 
 void
-SpewTempAttemptsVector(const TempAttemptsVector *attempts, const char *indent = nullptr)
+SpewTempOptimizationAttemptsVector(const TempOptimizationAttemptsVector *attempts,
+                                   const char *indent = nullptr)
 {
 #ifdef DEBUG
     for (const OptimizationAttempt *a = attempts->begin(); a != attempts->end(); a++) {
         JitSpew(JitSpew_OptimizationTracking, "   %s%s: %s", indent ? indent : "",
-                StrategyString(a->strategy()), OutcomeString(a->outcome()));
+                TrackedStrategyString(a->strategy()), TrackedOutcomeString(a->outcome()));
     }
 #endif
 }
 
 void
 TrackedOptimizations::spew() const
 {
 #ifdef DEBUG
-    SpewTempTrackedTypeInfoVector(&types_);
-    SpewTempAttemptsVector(&attempts_);
+    SpewTempOptimizationTypeInfoVector(&types_);
+    SpewTempOptimizationAttemptsVector(&attempts_);
 #endif
 }
 
 bool
-TrackedTypeInfo::trackTypeSet(types::TemporaryTypeSet *typeSet)
+OptimizationTypeInfo::trackTypeSet(types::TemporaryTypeSet *typeSet)
 {
     if (!typeSet)
         return true;
     return typeSet->enumerateTypes(&types_);
 }
 
 bool
-TrackedTypeInfo::trackType(types::Type type)
+OptimizationTypeInfo::trackType(types::Type type)
 {
     return types_.append(type);
 }
 
 bool
-TrackedTypeInfo::operator ==(const TrackedTypeInfo &other) const
+OptimizationTypeInfo::operator ==(const OptimizationTypeInfo &other) const
 {
     return site_ == other.site_ && mirType_ == other.mirType_ &&
            VectorContentsMatch(&types_, &other.types_);
 }
 
 bool
-TrackedTypeInfo::operator !=(const TrackedTypeInfo &other) const
+OptimizationTypeInfo::operator !=(const OptimizationTypeInfo &other) const
 {
     return !(*this == other);
 }
 
 static inline HashNumber
 CombineHash(HashNumber h, HashNumber n)
 {
     h += n;
@@ -208,17 +214,17 @@ HashTypeList(const types::TypeSet::TypeL
 {
     HashNumber h = 0;
     for (uint32_t i = 0; i < types.length(); i++)
         h = CombineHash(h, HashType(types[i]));
     return h;
 }
 
 HashNumber
-TrackedTypeInfo::hash() const
+OptimizationTypeInfo::hash() const
 {
     return ((HashNumber(site_) << 24) + (HashNumber(mirType_) << 16)) ^ HashTypeList(types_);
 }
 
 template <class Vec>
 static HashNumber
 HashVectorContents(const Vec *xs, HashNumber h)
 {
@@ -429,38 +435,64 @@ IonTrackedOptimizationsRegion::RangeIter
     uint32_t startDelta, length;
     ReadDelta(reader, &startDelta, &length, index);
     *startOffset = prevEndOffset_ + startDelta;
     *endOffset = prevEndOffset_ = *startOffset + length;
     cur_ = reader.currentPosition();
     MOZ_ASSERT(cur_ <= end_);
 }
 
-bool
-JitcodeGlobalEntry::IonEntry::optimizationAttemptsAtAddr(void *ptr,
-                                                         Maybe<AttemptsVector> &attempts)
+Maybe<uint8_t>
+JitcodeGlobalEntry::IonEntry::trackedOptimizationIndexAtAddr(void *ptr)
 {
-    MOZ_ASSERT(!attempts);
+    MOZ_ASSERT(hasTrackedOptimizations());
     MOZ_ASSERT(containsPointer(ptr));
     uint32_t ptrOffset = ((uint8_t *) ptr) - ((uint8_t *) nativeStartAddr());
     Maybe<IonTrackedOptimizationsRegion> region = optsRegionTable_->findRegion(ptrOffset);
     if (region.isNothing())
-        return true;
-    Maybe<uint8_t> attemptsIdx = region->findAttemptsIndex(ptrOffset);
-    if (attemptsIdx.isNothing())
-        return true;
-    IonTrackedOptimizationsAttempts attemptsEntry = optsAttemptsTable_->entry(*attemptsIdx);
-    attempts.emplace();
-    if (!attemptsEntry.readVector(attempts.ptr()))
-        return false;
-    return true;
+        return Nothing();
+    return region->findIndex(ptrOffset);
+}
+
+void
+IonTrackedOptimizationsAttempts::forEach(ForEachTrackedOptimizationAttemptOp &op)
+{
+    CompactBufferReader reader(start_, end_);
+    const uint8_t *cur = start_;
+    while (cur != end_) {
+        TrackedStrategy strategy = TrackedStrategy(reader.readUnsigned());
+        TrackedOutcome outcome = TrackedOutcome(reader.readUnsigned());
+        MOZ_ASSERT(strategy < TrackedStrategy::Count);
+        MOZ_ASSERT(outcome < TrackedOutcome::Count);
+        op(strategy, outcome);
+        cur = reader.currentPosition();
+        MOZ_ASSERT(cur <= end_);
+    }
+}
+
+void
+IonTrackedOptimizationsTypeInfo::forEach(ForEachOp &op, const IonTrackedTypeVector *allTypes)
+{
+    CompactBufferReader reader(start_, end_);
+    const uint8_t *cur = start_;
+    while (cur != end_) {
+        TrackedTypeSite site = JS::TrackedTypeSite(reader.readUnsigned());
+        MOZ_ASSERT(site < JS::TrackedTypeSite::Count);
+        MIRType mirType = MIRType(reader.readUnsigned());
+        uint32_t length = reader.readUnsigned();
+        for (uint32_t i = 0; i < length; i++)
+            op.readType((*allTypes)[reader.readByte()]);
+        op(site, mirType);
+        cur = reader.currentPosition();
+        MOZ_ASSERT(cur <= end_);
+    }
 }
 
 Maybe<uint8_t>
-IonTrackedOptimizationsRegion::findAttemptsIndex(uint32_t offset) const
+IonTrackedOptimizationsRegion::findIndex(uint32_t offset) const
 {
     if (offset < startOffset_ || offset >= endOffset_)
         return Nothing();
 
     // Linear search through the run.
     RangeIterator iter = ranges();
     while (iter.more()) {
         uint32_t startOffset, endOffset;
@@ -550,17 +582,17 @@ IonTrackedOptimizationsRegion::ExpectedR
 void
 OptimizationAttempt::writeCompact(CompactBufferWriter &writer) const
 {
     writer.writeUnsigned((uint32_t) strategy_);
     writer.writeUnsigned((uint32_t) outcome_);
 }
 
 bool
-TrackedTypeInfo::writeCompact(CompactBufferWriter &writer,
+OptimizationTypeInfo::writeCompact(CompactBufferWriter &writer,
                               UniqueTrackedTypes &uniqueTypes) const
 {
     writer.writeUnsigned((uint32_t) site_);
     writer.writeUnsigned((uint32_t) mirType_);
     writer.writeUnsigned(types_.length());
     for (uint32_t i = 0; i < types_.length(); i++) {
         uint8_t index;
         if (!uniqueTypes.getIndexOf(types_[i], &index))
@@ -742,42 +774,16 @@ IonTrackedOptimizationsRegion::WriteRun(
     }
 
     if (writer.oom())
         return false;
 
     return true;
 }
 
-TrackedTypeInfo::TrackedTypeInfo(CompactBufferReader &reader)
-  : site_(TrackedTypeSite(reader.readUnsigned())),
-    mirType_(MIRType(reader.readUnsigned()))
-{
-    MOZ_ASSERT(site_ < TrackedTypeSite::Count);
-}
-
-bool
-TrackedTypeInfo::readTypes(CompactBufferReader &reader, const types::TypeSet::TypeList *allTypes)
-{
-    uint32_t length = reader.readUnsigned();
-    for (uint32_t i = 0; i < length; i++) {
-        if (!types_.append((*allTypes)[reader.readByte()]))
-            return false;
-    }
-    return true;
-}
-
-OptimizationAttempt::OptimizationAttempt(CompactBufferReader &reader)
-  : strategy_(TrackedStrategy(reader.readUnsigned())),
-    outcome_(TrackedOutcome(reader.readUnsigned()))
-{
-    MOZ_ASSERT(strategy_ < TrackedStrategy::Count);
-    MOZ_ASSERT(outcome_ < TrackedOutcome::Count);
-}
-
 static bool
 WriteOffsetsTable(CompactBufferWriter &writer, const Vector<uint32_t, 16> &offsets,
                   uint32_t *tableOffsetp)
 {
     // 4-byte align for the uint32s.
     uint32_t padding = sizeof(uint32_t) - (writer.length() % sizeof(uint32_t));
     if (padding == sizeof(uint32_t))
         padding = 0;
@@ -802,26 +808,70 @@ WriteOffsetsTable(CompactBufferWriter &w
 
     if (writer.oom())
         return false;
 
     *tableOffsetp = tableOffset;
     return true;
 }
 
+static JSFunction *
+MaybeConstructorFromType(types::Type ty)
+{
+    if (ty.isUnknown() || ty.isAnyObject() || !ty.isGroup())
+        return nullptr;
+    types::ObjectGroup *obj = ty.group();
+    types::TypeNewScript *newScript = obj->newScript();
+    if (!newScript && obj->maybeUnboxedLayout())
+        newScript = obj->unboxedLayout().newScript();
+    return newScript ? newScript->function() : nullptr;
+}
+
+static void
+SpewConstructor(types::Type ty, JSFunction *constructor)
+{
+#ifdef DEBUG
+    char buf[512];
+    PutEscapedString(buf, 512, constructor->displayAtom(), 0);
+
+    const char *filename;
+    uint32_t lineno;
+    if (constructor->hasScript()) {
+        filename = constructor->nonLazyScript()->filename();
+        lineno = constructor->nonLazyScript()->lineno();
+    } else {
+        filename = constructor->lazyScript()->filename();
+        lineno = constructor->lazyScript()->lineno();
+    }
+
+    JitSpew(JitSpew_OptimizationTracking, "   Unique type %s has constructor %s (%s:%u)",
+            types::TypeString(ty), buf, filename, lineno);
+#endif
+}
+
+static void
+SpewAllocationSite(types::Type ty, JSScript *script, uint32_t offset)
+{
+#ifdef DEBUG
+    JitSpew(JitSpew_OptimizationTracking, "   Unique type %s has alloc site %s:%u",
+            types::TypeString(ty), script->filename(),
+            PCToLineNumber(script, script->offsetToPC(offset)));
+#endif
+}
+
 bool
 jit::WriteIonTrackedOptimizationsTable(JSContext *cx, CompactBufferWriter &writer,
                                        const NativeToTrackedOptimizations *start,
                                        const NativeToTrackedOptimizations *end,
                                        const UniqueTrackedOptimizations &unique,
                                        uint32_t *numRegions,
                                        uint32_t *regionTableOffsetp,
                                        uint32_t *typesTableOffsetp,
                                        uint32_t *optimizationTableOffsetp,
-                                       types::TypeSet::TypeList *allTypes)
+                                       IonTrackedTypeVector *allTypes)
 {
     MOZ_ASSERT(unique.sorted());
 
 #ifdef DEBUG
     // Spew training data, which may be fed into a script to determine a good
     // encoding strategy.
     if (JitSpewEnabled(JitSpew_OptimizationTracking)) {
         JitSpewStart(JitSpew_OptimizationTracking, "=> Training data: ");
@@ -868,44 +918,68 @@ jit::WriteIonTrackedOptimizationsTable(J
             vec.length(), vec.length() == 1 ? "y" : "ies");
 
     // Write out type info payloads.
     UniqueTrackedTypes uniqueTypes(cx);
     if (!uniqueTypes.init())
         return false;
 
     for (const UniqueTrackedOptimizations::SortEntry *p = vec.begin(); p != vec.end(); p++) {
-        const TempTrackedTypeInfoVector *v = p->types;
+        const TempOptimizationTypeInfoVector *v = p->types;
         JitSpew(JitSpew_OptimizationTracking, "   Type info entry %u of length %u, offset %u",
                 p - vec.begin(), v->length(), writer.length());
-        SpewTempTrackedTypeInfoVector(v, "  ");
+        SpewTempOptimizationTypeInfoVector(v, "  ");
 
         if (!offsets.append(writer.length()))
             return false;
 
-        for (const TrackedTypeInfo *t = v->begin(); t != v->end(); t++) {
+        for (const OptimizationTypeInfo *t = v->begin(); t != v->end(); t++) {
             if (!t->writeCompact(writer, uniqueTypes))
                 return false;
         }
     }
 
-    // Copy the unique type list into the outparam TypeList.
-    if (!uniqueTypes.enumerate(allTypes))
+    // Enumerate the unique types, and pull out any 'new' script constructor
+    // functions and allocation site information. We do this during linking
+    // instead of during profiling to avoid touching compartment tables during
+    // profiling. Additionally, TypeNewScript is subject to GC in the
+    // meantime.
+    types::TypeSet::TypeList uniqueTypeList;
+    if (!uniqueTypes.enumerate(&uniqueTypeList))
         return false;
+    for (uint32_t i = 0; i < uniqueTypeList.length(); i++) {
+        types::Type ty = uniqueTypeList[i];
+        if (JSFunction *constructor = MaybeConstructorFromType(ty)) {
+            if (!allTypes->append(IonTrackedTypeWithAddendum(ty, constructor)))
+                return false;
+            SpewConstructor(ty, constructor);
+        } else {
+            JSScript *script;
+            uint32_t offset;
+            if (cx->findAllocationSiteForType(ty, &script, &offset)) {
+                if (!allTypes->append(IonTrackedTypeWithAddendum(ty, script, offset)))
+                    return false;
+                SpewAllocationSite(ty, script, offset);
+            } else {
+                if (!allTypes->append(IonTrackedTypeWithAddendum(ty)))
+                    return false;
+            }
+        }
+    }
 
     if (!WriteOffsetsTable(writer, offsets, typesTableOffsetp))
         return false;
     offsets.clear();
 
     // Write out attempts payloads.
     for (const UniqueTrackedOptimizations::SortEntry *p = vec.begin(); p != vec.end(); p++) {
-        const TempAttemptsVector *v = p->attempts;
+        const TempOptimizationAttemptsVector *v = p->attempts;
         JitSpew(JitSpew_OptimizationTracking, "   Attempts entry %u of length %u, offset %u",
                 p - vec.begin(), v->length(), writer.length());
-        SpewTempAttemptsVector(v, "  ");
+        SpewTempOptimizationAttemptsVector(v, "  ");
 
         if (!offsets.append(writer.length()))
             return false;
 
         for (const OptimizationAttempt *a = v->begin(); a != v->end(); a++)
             a->writeCompact(writer);
     }
 
@@ -957,31 +1031,31 @@ IonBuilder::startTrackingOptimizations()
 }
 
 void
 IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, MIRType mirType,
                                    types::TemporaryTypeSet *typeSet)
 {
     BytecodeSite *site = current->trackedSite();
     // OOMs are handled as if optimization tracking were turned off.
-    TrackedTypeInfo typeInfo(kind, mirType);
+    OptimizationTypeInfo typeInfo(kind, mirType);
     if (!typeInfo.trackTypeSet(typeSet)) {
         site->setOptimizations(nullptr);
         return;
     }
     if (!site->optimizations()->trackTypeInfo(mozilla::Move(typeInfo)))
         site->setOptimizations(nullptr);
 }
 
 void
 IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, JSObject *obj)
 {
     BytecodeSite *site = current->trackedSite();
     // OOMs are handled as if optimization tracking were turned off.
-    TrackedTypeInfo typeInfo(kind, MIRType_Object);
+    OptimizationTypeInfo typeInfo(kind, MIRType_Object);
     if (!typeInfo.trackType(types::Type::ObjectType(obj)))
         return;
     if (!site->optimizations()->trackTypeInfo(mozilla::Move(typeInfo)))
         site->setOptimizations(nullptr);
 }
 
 void
 IonBuilder::trackTypeInfoUnchecked(CallInfo &callInfo)
@@ -1030,8 +1104,109 @@ IonBuilder::trackOptimizationSuccessUnch
 }
 
 void
 IonBuilder::trackInlineSuccessUnchecked(InliningStatus status)
 {
     if (status == InliningStatus_Inlined)
         trackOptimizationOutcome(TrackedOutcome::Inlined);
 }
+
+JS_PUBLIC_API(void)
+JS::ForEachTrackedOptimizationAttempt(JSRuntime *rt, void *addr, uint8_t index,
+                                      ForEachTrackedOptimizationAttemptOp &op)
+{
+    JitcodeGlobalTable *table = rt->jitRuntime()->getJitcodeGlobalTable();
+    JitcodeGlobalEntry entry;
+    table->lookupInfallible(addr, &entry, rt);
+    entry.trackedOptimizationAttempts(index).forEach(op);
+}
+
+static void
+InterpretedFunctionFilenameAndLineNumber(JSFunction *fun, const char **filename, unsigned *lineno)
+{
+    ScriptSource *source;
+    if (fun->hasScript()) {
+        source = fun->nonLazyScript()->maybeForwardedScriptSource();
+        *lineno = fun->nonLazyScript()->lineno();
+    } else {
+        source = fun->lazyScript()->maybeForwardedScriptSource();
+        *lineno = fun->lazyScript()->lineno();
+    }
+    *filename = source->introducerFilename();
+}
+
+static JSFunction *
+InterpretedFunctionFromTrackedType(const IonTrackedTypeWithAddendum &tracked)
+{
+    if (tracked.hasConstructor())
+        return tracked.constructor;
+
+    types::Type ty = tracked.type;
+
+    if (ty.isSingleton()) {
+        JSObject *obj = ty.singleton();
+        return obj->is<JSFunction>() ? &obj->as<JSFunction>() : nullptr;
+    }
+
+    return ty.group()->maybeInterpretedFunction();
+}
+
+// This adapter is needed as the internal API can deal with engine-internal
+// data structures directly, while the public API cannot.
+class ForEachTypeInfoAdapter : public IonTrackedOptimizationsTypeInfo::ForEachOp
+{
+    ForEachTrackedOptimizationTypeInfoOp &op_;
+
+  public:
+    explicit ForEachTypeInfoAdapter(ForEachTrackedOptimizationTypeInfoOp &op)
+      : op_(op)
+    { }
+
+    void readType(const IonTrackedTypeWithAddendum &tracked) MOZ_OVERRIDE {
+        types::Type ty = tracked.type;
+
+        if (ty.isPrimitive() || ty.isUnknown() || ty.isAnyObject()) {
+            op_.readType("primitive", types::NonObjectTypeString(ty), nullptr, 0);
+            return;
+        }
+
+        char buf[512];
+        const uint32_t bufsize = mozilla::ArrayLength(buf);
+
+        if (JSFunction *fun = InterpretedFunctionFromTrackedType(tracked)) {
+            PutEscapedString(buf, bufsize, fun->displayAtom(), 0);
+            const char *filename;
+            unsigned lineno;
+            InterpretedFunctionFilenameAndLineNumber(fun, &filename, &lineno);
+            op_.readType(tracked.constructor ? "constructor" : "function", buf, filename, lineno);
+            return;
+        }
+
+        const char *className = ty.objectKey()->clasp()->name;
+        JS_snprintf(buf, bufsize, "[object %s]", className);
+
+        if (tracked.hasAllocationSite()) {
+            JSScript *script = tracked.script;
+            op_.readType("alloc site", buf,
+                         script->maybeForwardedScriptSource()->introducerFilename(),
+                         PCToLineNumber(script, script->offsetToPC(tracked.offset)));
+            return;
+        }
+
+        op_.readType("prototype", buf, nullptr, 0);
+    }
+
+    void operator()(JS::TrackedTypeSite site, MIRType mirType) MOZ_OVERRIDE {
+        op_(site, StringFromMIRType(mirType));
+    }
+};
+
+JS_PUBLIC_API(void)
+JS::ForEachTrackedOptimizationTypeInfo(JSRuntime *rt, void *addr, uint8_t index,
+                                       ForEachTrackedOptimizationTypeInfoOp &op)
+{
+    JitcodeGlobalTable *table = rt->jitRuntime()->getJitcodeGlobalTable();
+    JitcodeGlobalEntry entry;
+    table->lookupInfallible(addr, &entry, rt);
+    ForEachTypeInfoAdapter adapter(op);
+    entry.trackedOptimizationTypeInfo(index).forEach(adapter, entry.allTrackedTypes());
+}
--- a/js/src/jit/OptimizationTracking.h
+++ b/js/src/jit/OptimizationTracking.h
@@ -8,418 +8,160 @@
 #define jit_OptimizationTracking_h
 
 #include "mozilla/Maybe.h"
 
 #include "jsinfer.h"
 #include "jit/CompactBuffer.h"
 #include "jit/CompileInfo.h"
 #include "jit/JitAllocPolicy.h"
-#include "jit/shared/CodeGenerator-shared.h"
+#include "js/TrackedOptimizationInfo.h"
 
 namespace js {
 
 namespace jit {
 
-#define TRACKED_STRATEGY_LIST(_)                        \
-    _(GetProp_ArgumentsLength,                          \
-      "getprop arguments.length")                       \
-    _(GetProp_ArgumentsCallee,                          \
-      "getprop arguments.callee")                       \
-    _(GetProp_InferredConstant,                         \
-      "getprop inferred constant")                      \
-    _(GetProp_Constant,                                 \
-      "getprop constant")                               \
-    _(GetProp_TypedObject,                              \
-      "getprop TypedObject")                            \
-    _(GetProp_DefiniteSlot,                             \
-      "getprop definite slot")                          \
-    _(GetProp_Unboxed,                                  \
-      "getprop unboxed object")                         \
-    _(GetProp_CommonGetter,                             \
-      "getprop common getter")                          \
-    _(GetProp_InlineAccess,                             \
-      "getprop inline access")                          \
-    _(GetProp_Innerize,                                 \
-      "getprop innerize (access on global window)")     \
-    _(GetProp_InlineCache,                              \
-      "getprop IC")                                     \
-                                                        \
-    _(SetProp_CommonSetter,                             \
-      "setprop common setter")                          \
-    _(SetProp_TypedObject,                              \
-      "setprop TypedObject")                            \
-    _(SetProp_DefiniteSlot,                             \
-      "setprop definite slot")                          \
-    _(SetProp_Unboxed,                                  \
-      "setprop unboxed object")                         \
-    _(SetProp_InlineAccess,                             \
-      "setprop inline access")                          \
-                                                        \
-    _(GetElem_TypedObject,                              \
-      "getprop TypedObject")                            \
-    _(GetElem_Dense,                                    \
-      "getelem dense")                                  \
-    _(GetElem_TypedStatic,                              \
-      "getelem TypedArray static")                      \
-    _(GetElem_TypedArray,                               \
-      "getelem TypedArray")                             \
-    _(GetElem_String,                                   \
-      "getelem string")                                 \
-    _(GetElem_Arguments,                                \
-      "getelem arguments")                              \
-    _(GetElem_ArgumentsInlined,                         \
-      "getelem arguments inlined")                      \
-    _(GetElem_InlineCache,                              \
-      "getelem IC")                                     \
-                                                        \
-    _(SetElem_TypedObject,                              \
-      "setelem TypedObject")                            \
-    _(SetElem_TypedStatic,                              \
-      "setelem TypedArray static")                      \
-    _(SetElem_TypedArray,                               \
-      "setelem TypedArray")                             \
-    _(SetElem_Dense,                                    \
-      "setelem dense")                                  \
-    _(SetElem_Arguments,                                \
-      "setelem arguments")                              \
-    _(SetElem_InlineCache,                              \
-      "setelem IC")                                     \
-                                                        \
-    _(Call_Inline,                                      \
-      "call inline")
-
-
-// Ordering is important below. All outcomes before GenericSuccess will be
-// considered failures, and all outcomes after GenericSuccess will be
-// considered successes.
-#define TRACKED_OUTCOME_LIST(_)                                         \
-    _(GenericFailure,                                                   \
-      "failure")                                                        \
-    _(Disabled,                                                         \
-      "disabled")                                                       \
-    _(NoTypeInfo,                                                       \
-      "no type info")                                                   \
-    _(NoAnalysisInfo,                                                   \
-      "no newscript analysis")                                          \
-    _(NoShapeInfo,                                                      \
-      "cannot determine shape")                                         \
-    _(UnknownObject,                                                    \
-      "unknown object")                                                 \
-    _(UnknownProperties,                                                \
-      "unknown properties")                                             \
-    _(Singleton,                                                        \
-      "is singleton")                                                   \
-    _(NotSingleton,                                                     \
-      "is not singleton")                                               \
-    _(NotFixedSlot,                                                     \
-      "property not in fixed slot")                                     \
-    _(InconsistentFixedSlot,                                            \
-      "property not in a consistent fixed slot")                        \
-    _(NotObject,                                                        \
-      "not definitely an object")                                       \
-    _(NotStruct,                                                        \
-      "not definitely a TypedObject struct")                            \
-    _(NotUnboxed,                                                       \
-      "not definitely an unboxed object")                               \
-    _(StructNoField,                                                    \
-      "struct doesn't definitely have field")                           \
-    _(InconsistentFieldType,                                            \
-      "unboxed property does not consistent type")                      \
-    _(InconsistentFieldOffset,                                          \
-      "unboxed property does not consistent offset")                    \
-    _(NeedsTypeBarrier,                                                 \
-      "needs type barrier")                                             \
-    _(InDictionaryMode,                                                 \
-      "object in dictionary mode")                                      \
-    _(NoProtoFound,                                                     \
-      "no proto found")                                                 \
-    _(MultiProtoPaths,                                                  \
-      "not all paths to property go through same proto")                \
-    _(NonWritableProperty,                                              \
-      "non-writable property")                                          \
-    _(ProtoIndexedProps,                                                \
-      "prototype has indexed properties")                               \
-    _(ArrayBadFlags,                                                    \
-      "array observed to be sparse, overflowed .length, or has been iterated") \
-    _(ArrayDoubleConversion,                                            \
-      "array has ambiguous double conversion")                          \
-    _(ArrayRange,                                                       \
-      "array range issue (.length problems)")                           \
-    _(ArraySeenNegativeIndex,                                           \
-      "has seen array access with negative index")                      \
-    _(TypedObjectNeutered,                                              \
-      "TypedObject might have been neutered")                           \
-    _(TypedObjectArrayRange,                                            \
-      "TypedObject array of unknown length")                            \
-    _(AccessNotDense,                                                   \
-      "access not on dense native (check receiver, index, and result types)") \
-    _(AccessNotTypedObject,                                             \
-      "access not on typed array (check receiver and index types)")     \
-    _(AccessNotTypedArray,                                              \
-      "access not on typed array (check receiver, index, and result types)") \
-    _(AccessNotString,                                                  \
-      "getelem not on string (check receiver and index types)")         \
-    _(StaticTypedArrayUint32,                                           \
-      "static uint32 arrays currently cannot be optimized")             \
-    _(StaticTypedArrayCantComputeMask,                                  \
-      "can't compute mask for static typed array access (index isn't constant or not int32)") \
-    _(OutOfBounds,                                                      \
-      "observed out of bounds access")                                  \
-    _(GetElemStringNotCached,                                           \
-      "getelem on strings is not inline cached")                        \
-    _(NonNativeReceiver,                                                \
-      "observed non-native receiver")                                   \
-    _(IndexType,                                                        \
-      "index type must be int32, string, or symbol")                    \
-    _(SetElemNonDenseNonTANotCached,                                    \
-      "setelem on non-dense non-TAs are not inline cached")             \
-                                                                        \
-    _(CantInlineGeneric,                                                \
-      "can't inline")                                                   \
-    _(CantInlineNoTarget,                                               \
-      "can't inline: no target")                                        \
-    _(CantInlineNotInterpreted,                                         \
-      "can't inline: not interpreted")                                  \
-    _(CantInlineNoBaseline,                                             \
-      "can't inline: no baseline code")                                 \
-    _(CantInlineLazy,                                                   \
-      "can't inline: lazy script")                                      \
-    _(CantInlineNotConstructor,                                         \
-      "can't inline: calling non-constructor with 'new'")               \
-    _(CantInlineDisabledIon,                                            \
-      "can't inline: ion disabled for callee")                          \
-    _(CantInlineTooManyArgs,                                            \
-      "can't inline: too many arguments")                               \
-    _(CantInlineRecursive,                                              \
-      "can't inline: recursive")                                        \
-    _(CantInlineHeavyweight,                                            \
-      "can't inline: heavyweight")                                      \
-    _(CantInlineNeedsArgsObj,                                           \
-      "can't inline: needs arguments object")                           \
-    _(CantInlineDebuggee,                                               \
-      "can't inline: debuggee")                                         \
-    _(CantInlineUnknownProps,                                           \
-      "can't inline: type has unknown properties")                      \
-    _(CantInlineExceededDepth,                                          \
-      "can't inline: exceeded inlining depth")                          \
-    _(CantInlineBigLoop,                                                \
-      "can't inline: big function with a loop")                         \
-    _(CantInlineBigCaller,                                              \
-      "can't inline: big caller")                                       \
-    _(CantInlineBigCallee,                                              \
-      "can't inline: big callee")                                       \
-    _(CantInlineNotHot,                                                 \
-      "can't inline: not hot enough")                                   \
-    _(CantInlineNotInDispatch,                                          \
-      "can't inline: not in dispatch table")                            \
-    _(CantInlineNativeBadForm,                                          \
-      "can't inline native: bad form (arity mismatch/constructing)")    \
-    _(CantInlineNativeBadType,                                          \
-      "can't inline native: bad argument or return type observed")      \
-    _(CantInlineNativeNoTemplateObj,                                    \
-      "can't inline native: no template object")                        \
-    _(CantInlineBound,                                                  \
-      "can't inline bound function invocation")                         \
-                                                                        \
-    _(GenericSuccess,                                                   \
-      "success")                                                        \
-    _(Inlined,                                                          \
-      "inlined")                                                        \
-    _(DOM,                                                              \
-      "DOM")                                                            \
-    _(Monomorphic,                                                      \
-      "monomorphic")                                                    \
-    _(Polymorphic,                                                      \
-      "polymorphic")
-
-#define TRACKED_TYPESITE_LIST(_)                \
-    _(Receiver,                                 \
-      "receiver object")                        \
-    _(Index,                                    \
-      "index")                                  \
-    _(Value,                                    \
-      "value")                                  \
-    _(Call_Target,                              \
-      "call target")                            \
-    _(Call_This,                                \
-      "call 'this'")                            \
-    _(Call_Arg,                                 \
-      "call argument")                          \
-    _(Call_Return,                              \
-      "call return")
-
-enum class TrackedStrategy : uint32_t {
-#define STRATEGY_OP(name, msg) name,
-    TRACKED_STRATEGY_LIST(STRATEGY_OP)
-#undef STRATEGY_OPT
-
-    Count
-};
-
-enum class TrackedOutcome : uint32_t {
-#define OUTCOME_OP(name, msg) name,
-    TRACKED_OUTCOME_LIST(OUTCOME_OP)
-#undef OUTCOME_OP
-
-    Count
-};
-
-enum class TrackedTypeSite : uint32_t {
-#define TYPESITE_OP(name, msg) name,
-    TRACKED_TYPESITE_LIST(TYPESITE_OP)
-#undef TYPESITE_OP
-
-    Count
-};
+struct NativeToTrackedOptimizations;
 
 class OptimizationAttempt
 {
-    TrackedStrategy strategy_;
-    TrackedOutcome outcome_;
+    JS::TrackedStrategy strategy_;
+    JS::TrackedOutcome outcome_;
 
   public:
-    OptimizationAttempt(TrackedStrategy strategy, TrackedOutcome outcome)
+    OptimizationAttempt(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome)
       : strategy_(strategy),
         outcome_(outcome)
     { }
 
-    void setOutcome(TrackedOutcome outcome) { outcome_ = outcome; }
-    bool succeeded() const { return outcome_ >= TrackedOutcome::GenericSuccess; }
-    bool failed() const { return outcome_ < TrackedOutcome::GenericSuccess; }
-    TrackedStrategy strategy() const { return strategy_; }
-    TrackedOutcome outcome() const { return outcome_; }
+    void setOutcome(JS::TrackedOutcome outcome) { outcome_ = outcome; }
+    bool succeeded() const { return outcome_ >= JS::TrackedOutcome::GenericSuccess; }
+    bool failed() const { return outcome_ < JS::TrackedOutcome::GenericSuccess; }
+    JS::TrackedStrategy strategy() const { return strategy_; }
+    JS::TrackedOutcome outcome() const { return outcome_; }
 
     bool operator ==(const OptimizationAttempt &other) const {
         return strategy_ == other.strategy_ && outcome_ == other.outcome_;
     }
     bool operator !=(const OptimizationAttempt &other) const {
         return strategy_ != other.strategy_ || outcome_ != other.outcome_;
     }
     HashNumber hash() const {
         return (HashNumber(strategy_) << 8) + HashNumber(outcome_);
     }
 
-    explicit OptimizationAttempt(CompactBufferReader &reader);
     void writeCompact(CompactBufferWriter &writer) const;
 };
 
-typedef Vector<OptimizationAttempt, 4, JitAllocPolicy> TempAttemptsVector;
-typedef Vector<OptimizationAttempt, 4, SystemAllocPolicy> AttemptsVector;
+typedef Vector<OptimizationAttempt, 4, JitAllocPolicy> TempOptimizationAttemptsVector;
 
 class UniqueTrackedTypes;
 
-class TrackedTypeInfo
+class OptimizationTypeInfo
 {
-    TrackedTypeSite site_;
+    JS::TrackedTypeSite site_;
     MIRType mirType_;
     types::TypeSet::TypeList types_;
 
   public:
-    TrackedTypeInfo(TrackedTypeInfo &&other)
+    OptimizationTypeInfo(OptimizationTypeInfo &&other)
       : site_(other.site_),
         mirType_(other.mirType_),
         types_(mozilla::Move(other.types_))
     { }
 
-    TrackedTypeInfo(TrackedTypeSite site, MIRType mirType)
+    OptimizationTypeInfo(JS::TrackedTypeSite site, MIRType mirType)
       : site_(site),
         mirType_(mirType)
     { }
 
     bool trackTypeSet(types::TemporaryTypeSet *typeSet);
     bool trackType(types::Type type);
 
-    TrackedTypeSite site() const { return site_; }
+    JS::TrackedTypeSite site() const { return site_; }
     MIRType mirType() const { return mirType_; }
     const types::TypeSet::TypeList &types() const { return types_; }
 
-    bool operator ==(const TrackedTypeInfo &other) const;
-    bool operator !=(const TrackedTypeInfo &other) const;
+    bool operator ==(const OptimizationTypeInfo &other) const;
+    bool operator !=(const OptimizationTypeInfo &other) const;
 
     HashNumber hash() const;
 
-    // This constructor is designed to be used in conjunction with readTypes
-    // below it. The same reader must be passed to readTypes after
-    // instantiating the TrackedTypeInfo.
-    explicit TrackedTypeInfo(CompactBufferReader &reader);
-    bool readTypes(CompactBufferReader &reader, const types::TypeSet::TypeList *allTypes);
     bool writeCompact(CompactBufferWriter &writer, UniqueTrackedTypes &uniqueTypes) const;
 };
 
-typedef Vector<TrackedTypeInfo, 1, JitAllocPolicy> TempTrackedTypeInfoVector;
-typedef Vector<TrackedTypeInfo, 1, SystemAllocPolicy> TrackedTypeInfoVector;
+typedef Vector<OptimizationTypeInfo, 1, JitAllocPolicy> TempOptimizationTypeInfoVector;
 
 // Tracks the optimization attempts made at a bytecode location.
 class TrackedOptimizations : public TempObject
 {
     friend class UniqueTrackedOptimizations;
-    TempTrackedTypeInfoVector types_;
-    TempAttemptsVector attempts_;
+    TempOptimizationTypeInfoVector types_;
+    TempOptimizationAttemptsVector attempts_;
     uint32_t currentAttempt_;
 
   public:
     explicit TrackedOptimizations(TempAllocator &alloc)
       : types_(alloc),
         attempts_(alloc),
         currentAttempt_(UINT32_MAX)
     { }
 
-    bool trackTypeInfo(TrackedTypeInfo &&ty);
+    bool trackTypeInfo(OptimizationTypeInfo &&ty);
 
-    bool trackAttempt(TrackedStrategy strategy);
+    bool trackAttempt(JS::TrackedStrategy strategy);
     void amendAttempt(uint32_t index);
-    void trackOutcome(TrackedOutcome outcome);
+    void trackOutcome(JS::TrackedOutcome outcome);
     void trackSuccess();
 
-    bool matchTypes(const TempTrackedTypeInfoVector &other) const;
-    bool matchAttempts(const TempAttemptsVector &other) const;
+    bool matchTypes(const TempOptimizationTypeInfoVector &other) const;
+    bool matchAttempts(const TempOptimizationAttemptsVector &other) const;
 
     void spew() const;
 };
 
 // Assigns each unique sequence of optimization attempts an index; outputs a
 // compact table.
 class UniqueTrackedOptimizations
 {
   public:
     struct SortEntry
     {
-        const TempTrackedTypeInfoVector *types;
-        const TempAttemptsVector *attempts;
+        const TempOptimizationTypeInfoVector *types;
+        const TempOptimizationAttemptsVector *attempts;
         uint32_t frequency;
     };
     typedef Vector<SortEntry, 4> SortedVector;
 
   private:
     struct Key
     {
-        const TempTrackedTypeInfoVector *types;
-        const TempAttemptsVector *attempts;
+        const TempOptimizationTypeInfoVector *types;
+        const TempOptimizationAttemptsVector *attempts;
 
         typedef Key Lookup;
         static HashNumber hash(const Lookup &lookup);
         static bool match(const Key &key, const Lookup &lookup);
         static void rekey(Key &key, const Key &newKey) {
             key = newKey;
         }
     };
 
     struct Entry
     {
         uint8_t index;
         uint32_t frequency;
     };
 
-    // Map of unique (TempTrackedTypeInfoVector, TempAttemptsVector) pairs to
-    // indices.
+    // Map of unique (TempOptimizationTypeInfoVector,
+    // TempOptimizationAttemptsVector) pairs to indices.
     typedef HashMap<Key, Entry, Key> AttemptsMap;
     AttemptsMap map_;
 
-    // TempAttemptsVectors sorted by frequency.
+    // TempOptimizationAttemptsVectors sorted by frequency.
     SortedVector sorted_;
 
   public:
     explicit UniqueTrackedOptimizations(JSContext *cx)
       : map_(cx),
         sorted_(cx)
     { }
 
@@ -452,17 +194,17 @@ class UniqueTrackedOptimizations
 //    |   ...                                          |  |
 //    +------------------------------------------------+  |
 //    | Region M                                       |  |
 //    |   uint32_t regionOffset                        |  |
 //    +================================================+
 //    |  Optimization type info 1                      |  |
 //    |------------------------------------------------|  |
 //    |  Optimization type info 2                      |  |-- PayloadT of list of
-//    |------------------------------------------------|  |   IonTrackedOptimizationTypeInfo in
+//    |------------------------------------------------|  |   OptimizationTypeInfo in
 //    |               ...                              |  |   order of decreasing frequency
 //    |------------------------------------------------|  |
 //    |  Optimization type info N                      |  |
 //    +================================================+ <- IonTrackedOptimizationsTypesTable
 //    | uint32_t numEntries_ = N                       |  |
 //    +------------------------------------------------+  |
 //    | Optimization type info 1                       |  |
 //    |   uint32_t entryOffset = size(PayloadT)        |  |
@@ -470,17 +212,17 @@ class UniqueTrackedOptimizations
 //    |   ...                                          |  |
 //    +------------------------------------------------+  |
 //    | Optimization type info N                       |  |
 //    |   uint32_t entryOffset                         |  |
 //    +================================================+
 //    |  Optimization attempts 1                       |  |
 //    |------------------------------------------------|  |
 //    |  Optimization attempts 2                       |  |-- PayloadA of list of
-//    |------------------------------------------------|  |   IonTrackedOptimizationAttempts in
+//    |------------------------------------------------|  |   OptimizationAttempts in
 //    |               ...                              |  |   order of decreasing frequency
 //    |------------------------------------------------|  |
 //    |  Optimization attempts N                       |  |
 //    +================================================+ <- IonTrackedOptimizationsAttemptsTable
 //    | uint32_t numEntries_ = N                       |  |
 //    +------------------------------------------------+  |
 //    | Optimization attempts 1                        |  |
 //    |   uint32_t entryOffset = size(PayloadA)        |  |
@@ -564,17 +306,19 @@ class IonTrackedOptimizationsRegion
         { }
 
         bool more() const { return cur_ < end_; }
         void readNext(uint32_t *startOffset, uint32_t *endOffset, uint8_t *index);
     };
 
     RangeIterator ranges() const { return RangeIterator(rangesStart_, end_, startOffset_); }
 
-    mozilla::Maybe<uint8_t> findAttemptsIndex(uint32_t offset) const;
+    // Find the index of tracked optimization info (e.g., type info and
+    // attempts) at a native code offset.
+    mozilla::Maybe<uint8_t> findIndex(uint32_t offset) const;
 
     // For the variants below, S stands for startDelta, L for length, and I
     // for index. These were automatically generated from training on the
     // Octane benchmark.
     //
     // byte 1    byte 0
     // SSSS-SSSL LLLL-LII0
     //     startDelta max 127, length max 63, index max 3
@@ -641,17 +385,16 @@ class IonTrackedOptimizationsRegion
 
     static bool IsDeltaEncodeable(uint32_t startDelta, uint32_t length) {
         MOZ_ASSERT(length != 0);
         return startDelta <= ENC4_START_DELTA_MAX && length <= ENC4_LENGTH_MAX;
     }
 
     static const uint32_t MAX_RUN_LENGTH = 100;
 
-    typedef CodeGeneratorShared::NativeToTrackedOptimizations NativeToTrackedOptimizations;
     static uint32_t ExpectedRunLength(const NativeToTrackedOptimizations *start,
                                       const NativeToTrackedOptimizations *end);
 
     static void ReadDelta(CompactBufferReader &reader, uint32_t *startDelta, uint32_t *length,
                           uint8_t *index);
     static void WriteDelta(CompactBufferWriter &writer, uint32_t startDelta, uint32_t length,
                            uint8_t index);
     static bool WriteRun(CompactBufferWriter &writer,
@@ -668,59 +411,90 @@ class IonTrackedOptimizationsAttempts
   public:
     IonTrackedOptimizationsAttempts(const uint8_t *start, const uint8_t *end)
       : start_(start), end_(end)
     {
         // Cannot be empty.
         MOZ_ASSERT(start < end);
     }
 
-    template <class T>
-    bool readVector(T *attempts) {
-        CompactBufferReader reader(start_, end_);
-        const uint8_t *cur = start_;
-        while (cur != end_) {
-            if (!attempts->append(OptimizationAttempt(reader)))
-                return false;
-            cur = reader.currentPosition();
-            MOZ_ASSERT(cur <= end_);
-        }
-        return true;
-    }
+    void forEach(JS::ForEachTrackedOptimizationAttemptOp &op);
 };
 
+struct IonTrackedTypeWithAddendum
+{
+    types::Type type;
+
+    enum HasAddendum {
+        HasNothing,
+        HasAllocationSite,
+        HasConstructor
+    };
+    HasAddendum hasAddendum;
+
+    // If type is a type object and is tied to a site, the script and pc are
+    // resolved early and stored below. This is done to avoid accessing the
+    // compartment during profiling time.
+    union {
+        struct {
+            JSScript *script;
+            uint32_t offset;
+        };
+        JSFunction *constructor;
+    };
+
+    IonTrackedTypeWithAddendum(types::Type type)
+      : type(type),
+        hasAddendum(HasNothing)
+    { }
+
+    IonTrackedTypeWithAddendum(types::Type type, JSScript *script, uint32_t offset)
+      : type(type),
+        hasAddendum(HasAllocationSite),
+        script(script),
+        offset(offset)
+    { }
+
+    IonTrackedTypeWithAddendum(types::Type type, JSFunction *constructor)
+      : type(type),
+        hasAddendum(HasConstructor),
+        constructor(constructor)
+    { }
+
+    bool hasAllocationSite() const { return hasAddendum == HasAllocationSite; }
+    bool hasConstructor() const { return hasAddendum == HasConstructor; }
+};
+
+typedef Vector<IonTrackedTypeWithAddendum, 1, SystemAllocPolicy> IonTrackedTypeVector;
+
 class IonTrackedOptimizationsTypeInfo
 {
     const uint8_t *start_;
     const uint8_t *end_;
 
   public:
     IonTrackedOptimizationsTypeInfo(const uint8_t *start, const uint8_t *end)
       : start_(start), end_(end)
     {
         // Can be empty; i.e., no type info was tracked.
     }
 
     bool empty() const { return start_ == end_; }
 
-    template <class T>
-    bool readVector(T *types, const types::TypeSet::TypeList *allTypes) {
-        CompactBufferReader reader(start_, end_);
-        const uint8_t *cur = start_;
-        while (cur != end_) {
-            TrackedTypeInfo ty(reader);
-            if (!ty.readTypes(reader, allTypes))
-                return false;
-            if (!types->append(mozilla::Move(ty)))
-                return false;
-            cur = reader.currentPosition();
-            MOZ_ASSERT(cur <= end_);
-        }
-        return true;
-    }
+    // Unlike IonTrackedOptimizationAttempts,
+    // JS::ForEachTrackedOptimizaitonTypeInfoOp cannot be used directly. The
+    // internal API needs to deal with engine-internal data structures (e.g.,
+    // types::Type) directly.
+    struct ForEachOp
+    {
+        virtual void readType(const IonTrackedTypeWithAddendum &tracked) = 0;
+        virtual void operator()(JS::TrackedTypeSite site, MIRType mirType) = 0;
+    };
+
+    void forEach(ForEachOp &op, const IonTrackedTypeVector *allTypes);
 };
 
 template <class Entry>
 class IonTrackedOptimizationsOffsetsTable
 {
     uint32_t padding_;
     uint32_t numEntries_;
     uint32_t entryOffsets_[1];
@@ -758,19 +532,19 @@ class IonTrackedOptimizationsRegionTable
 typedef IonTrackedOptimizationsOffsetsTable<IonTrackedOptimizationsAttempts>
     IonTrackedOptimizationsAttemptsTable;
 
 typedef IonTrackedOptimizationsOffsetsTable<IonTrackedOptimizationsTypeInfo>
     IonTrackedOptimizationsTypesTable;
 
 bool
 WriteIonTrackedOptimizationsTable(JSContext *cx, CompactBufferWriter &writer,
-                                  const CodeGeneratorShared::NativeToTrackedOptimizations *start,
-                                  const CodeGeneratorShared::NativeToTrackedOptimizations *end,
+                                  const NativeToTrackedOptimizations *start,
+                                  const NativeToTrackedOptimizations *end,
                                   const UniqueTrackedOptimizations &unique,
                                   uint32_t *numRegions, uint32_t *regionTableOffsetp,
                                   uint32_t *typesTableOffsetp, uint32_t *attemptsTableOffsetp,
-                                  types::TypeSet::TypeList *allTypes);
+                                  IonTrackedTypeVector *allTypes);
 
 } // namespace jit
 } // namespace js
 
 #endif // jit_OptimizationTracking_h
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -772,17 +772,17 @@ CodeGeneratorShared::verifyCompactNative
             MOZ_ASSERT(curPcOffset < script->length());
         }
     }
 #endif // DEBUG
 }
 
 bool
 CodeGeneratorShared::generateCompactTrackedOptimizationsMap(JSContext *cx, JitCode *code,
-                                                            types::TypeSet::TypeList *allTypes)
+                                                            IonTrackedTypeVector *allTypes)
 {
     MOZ_ASSERT(trackedOptimizationsMap_ == nullptr);
     MOZ_ASSERT(trackedOptimizationsMapSize_ == 0);
     MOZ_ASSERT(trackedOptimizationsRegionTableOffset_ == 0);
     MOZ_ASSERT(trackedOptimizationsTypesTableOffset_ == 0);
     MOZ_ASSERT(trackedOptimizationsAttemptsTableOffset_ == 0);
 
     if (trackedOptimizations_.empty())
@@ -843,25 +843,67 @@ CodeGeneratorShared::generateCompactTrac
 
     verifyCompactTrackedOptimizationsMap(code, numRegions, unique, allTypes);
 
     JitSpew(JitSpew_OptimizationTracking,
             "== Compact Native To Optimizations Map [%p-%p] size %u",
             data, data + trackedOptimizationsMapSize_, trackedOptimizationsMapSize_);
     JitSpew(JitSpew_OptimizationTracking,
             "     with type list of length %u, size %u",
-            allTypes->length(), allTypes->length() * sizeof(types::Type));
+            allTypes->length(), allTypes->length() * sizeof(IonTrackedTypeWithAddendum));
 
     return true;
 }
 
+#ifdef DEBUG
+// Since this is a DEBUG-only verification, crash on OOM in the forEach ops
+// below.
+
+class ReadTempAttemptsVectorOp : public JS::ForEachTrackedOptimizationAttemptOp
+{
+    TempOptimizationAttemptsVector *attempts_;
+
+  public:
+    explicit ReadTempAttemptsVectorOp(TempOptimizationAttemptsVector *attempts)
+      : attempts_(attempts)
+    { }
+
+    void operator()(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome) MOZ_OVERRIDE {
+        MOZ_ALWAYS_TRUE(attempts_->append(OptimizationAttempt(strategy, outcome)));
+    }
+};
+
+struct ReadTempTypeInfoVectorOp : public IonTrackedOptimizationsTypeInfo::ForEachOp
+{
+    TempOptimizationTypeInfoVector *types_;
+    types::TypeSet::TypeList accTypes_;
+
+  public:
+    explicit ReadTempTypeInfoVectorOp(TempOptimizationTypeInfoVector *types)
+      : types_(types)
+    { }
+
+    void readType(const IonTrackedTypeWithAddendum &tracked) MOZ_OVERRIDE {
+        MOZ_ALWAYS_TRUE(accTypes_.append(tracked.type));
+    }
+
+    void operator()(JS::TrackedTypeSite site, MIRType mirType) MOZ_OVERRIDE {
+        OptimizationTypeInfo ty(site, mirType);
+        for (uint32_t i = 0; i < accTypes_.length(); i++)
+            MOZ_ALWAYS_TRUE(ty.trackType(accTypes_[i]));
+        MOZ_ALWAYS_TRUE(types_->append(mozilla::Move(ty)));
+        accTypes_.clear();
+    }
+};
+#endif // DEBUG
+
 void
 CodeGeneratorShared::verifyCompactTrackedOptimizationsMap(JitCode *code, uint32_t numRegions,
                                                           const UniqueTrackedOptimizations &unique,
-                                                          const types::TypeSet::TypeList *allTypes)
+                                                          const IonTrackedTypeVector *allTypes)
 {
 #ifdef DEBUG
     MOZ_ASSERT(trackedOptimizationsMap_ != nullptr);
     MOZ_ASSERT(trackedOptimizationsMapSize_ > 0);
     MOZ_ASSERT(trackedOptimizationsRegionTableOffset_ > 0);
     MOZ_ASSERT(trackedOptimizationsTypesTableOffset_ > 0);
     MOZ_ASSERT(trackedOptimizationsAttemptsTableOffset_ > 0);
 
@@ -907,26 +949,28 @@ CodeGeneratorShared::verifyCompactTracke
             uint32_t startOffset, endOffset;
             uint8_t index;
             iter.readNext(&startOffset, &endOffset, &index);
             NativeToTrackedOptimizations &entry = trackedOptimizations_[trackedIdx++];
             MOZ_ASSERT(startOffset == entry.startOffset.offset());
             MOZ_ASSERT(endOffset == entry.endOffset.offset());
             MOZ_ASSERT(index == unique.indexOf(entry.optimizations));
 
-            // Assert that the type info and attempts vector are correctly
-            // decoded. Since this is a DEBUG-only verification, crash on OOM.
+            // Assert that the type info and attempts vectors are correctly
+            // decoded.
             IonTrackedOptimizationsTypeInfo typeInfo = typesTable->entry(index);
-            TempTrackedTypeInfoVector tvec(alloc());
-            MOZ_ALWAYS_TRUE(typeInfo.readVector(&tvec, allTypes));
+            TempOptimizationTypeInfoVector tvec(alloc());
+            ReadTempTypeInfoVectorOp top(&tvec);
+            typeInfo.forEach(top, allTypes);
             MOZ_ASSERT(entry.optimizations->matchTypes(tvec));
 
             IonTrackedOptimizationsAttempts attempts = attemptsTable->entry(index);
-            TempAttemptsVector avec(alloc());
-            MOZ_ALWAYS_TRUE(attempts.readVector(&avec));
+            TempOptimizationAttemptsVector avec(alloc());
+            ReadTempAttemptsVectorOp aop(&avec);
+            attempts.forEach(aop);
             MOZ_ASSERT(entry.optimizations->matchAttempts(avec));
         }
     }
 #endif
 }
 
 void
 CodeGeneratorShared::markSafepoint(LInstruction *ins)
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -9,28 +9,28 @@
 
 #include "mozilla/Alignment.h"
 
 #include "jit/JitFrames.h"
 #include "jit/LIR.h"
 #include "jit/MacroAssembler.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MIRGraph.h"
+#include "jit/OptimizationTracking.h"
 #include "jit/Safepoints.h"
 #include "jit/Snapshots.h"
 #include "jit/VMFunctions.h"
 
 namespace js {
 namespace jit {
 
 class OutOfLineCode;
 class CodeGenerator;
 class MacroAssembler;
 class IonCache;
-class UniqueTrackedOptimizations;
 
 template <class ArgSeq, class StoreOutputTo>
 class OutOfLineCallVM;
 
 class OutOfLineTruncateSlow;
 
 struct PatchableBackedgeInfo
 {
@@ -43,16 +43,26 @@ struct PatchableBackedgeInfo
     {}
 };
 
 struct ReciprocalMulConstants {
     int32_t multiplier;
     int32_t shiftAmount;
 };
 
+// This should be nested in CodeGeneratorShared, but it is used in
+// optimization tracking implementation and nested classes cannot be
+// forward-declared.
+struct NativeToTrackedOptimizations {
+    // [startOffset, endOffset]
+    CodeOffsetLabel startOffset;
+    CodeOffsetLabel endOffset;
+    const TrackedOptimizations *optimizations;
+};
+
 class CodeGeneratorShared : public LElementVisitor
 {
     js::Vector<OutOfLineCode *, 0, SystemAllocPolicy> outOfLineCode_;
     OutOfLineCode *oolIns;
 
     MacroAssembler &ensureMasm(MacroAssembler *masm);
     mozilla::Maybe<MacroAssembler> maybeMasm_;
 
@@ -110,25 +120,16 @@ class CodeGeneratorShared : public LElem
 
     JSScript **nativeToBytecodeScriptList_;
     uint32_t nativeToBytecodeScriptListLength_;
 
     bool isProfilerInstrumentationEnabled() {
         return gen->isProfilerInstrumentationEnabled();
     }
 
-  public:
-    struct NativeToTrackedOptimizations {
-        // [startOffset, endOffset)
-        CodeOffsetLabel startOffset;
-        CodeOffsetLabel endOffset;
-        const TrackedOptimizations *optimizations;
-    };
-
-  protected:
     js::Vector<NativeToTrackedOptimizations, 0, SystemAllocPolicy> trackedOptimizations_;
     uint8_t *trackedOptimizationsMap_;
     uint32_t trackedOptimizationsMapSize_;
     uint32_t trackedOptimizationsRegionTableOffset_;
     uint32_t trackedOptimizationsTypesTableOffset_;
     uint32_t trackedOptimizationsAttemptsTableOffset_;
 
     bool isOptimizationTrackingEnabled() {
@@ -333,20 +334,20 @@ class CodeGeneratorShared : public LElem
     void encodeSafepoints();
 
     // Fixup offsets of native-to-bytecode map.
     bool createNativeToBytecodeScriptList(JSContext *cx);
     bool generateCompactNativeToBytecodeMap(JSContext *cx, JitCode *code);
     void verifyCompactNativeToBytecodeMap(JitCode *code);
 
     bool generateCompactTrackedOptimizationsMap(JSContext *cx, JitCode *code,
-                                                types::TypeSet::TypeList *allTypes);
+                                                IonTrackedTypeVector *allTypes);
     void verifyCompactTrackedOptimizationsMap(JitCode *code, uint32_t numRegions,
                                               const UniqueTrackedOptimizations &unique,
-                                              const types::TypeSet::TypeList *allTypes);
+                                              const IonTrackedTypeVector *allTypes);
 
     // Mark the safepoint on |ins| as corresponding to the current assembler location.
     // The location should be just after a call.
     void markSafepoint(LInstruction *ins);
     void markSafepointAt(uint32_t offset, LInstruction *ins);
 
     // Mark the OSI point |ins| as corresponding to the current
     // assembler location inside the |osiIndices_|. Return the assembler
--- a/js/src/jsapi-tests/testIntTypesABI.cpp
+++ b/js/src/jsapi-tests/testIntTypesABI.cpp
@@ -26,16 +26,17 @@
 /* LegacyIntTypes.h is deliberately exempted from this requirement */
 #include "js/MemoryMetrics.h"
 #include "js/ProfilingStack.h"
 #include "js/RequiredDefines.h"
 #include "js/RootingAPI.h"
 #include "js/SliceBudget.h"
 #include "js/StructuredClone.h"
 #include "js/TracingAPI.h"
+#include "js/TrackedOptimizationInfo.h"
 #include "js/TypeDecls.h"
 #include "js/UbiNode.h"
 #include "js/Utility.h"
 #include "js/Value.h"
 #include "js/Vector.h"
 #include "js/WeakMapPtr.h"
 #include "jsapi-tests/tests.h"
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -299,16 +299,20 @@ class ExclusiveContext : public ContextF
         MOZ_ASSERT_IF(compartment(), js::GetCompartmentZone(compartment()) == zone_);
         return zone_;
     }
 
     // Zone local methods that can be used freely from an ExclusiveContext.
     types::ObjectGroup *getNewGroup(const Class *clasp, TaggedProto proto,
                                     JSObject *associated = nullptr);
     types::ObjectGroup *getLazySingletonGroup(const Class *clasp, TaggedProto proto);
+
+    // Returns false if not found.
+    bool findAllocationSiteForType(types::Type ty, JSScript **script, uint32_t *offset) const;
+
     inline js::LifoAlloc &typeLifoAlloc();
 
     // Current global. This is only safe to use within the scope of the
     // AutoCompartment from which it's called.
     inline js::Handle<js::GlobalObject*> global() const;
 
     // Methods to access runtime data that must be protected by locks.
     frontend::ParseMapPool &parseMapPool() {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -92,16 +92,48 @@ types::TypeIdStringImpl(jsid id)
 }
 
 #endif
 
 /////////////////////////////////////////////////////////////////////
 // Logging
 /////////////////////////////////////////////////////////////////////
 
+const char *
+types::NonObjectTypeString(Type type)
+{
+    if (type.isPrimitive()) {
+        switch (type.primitive()) {
+          case JSVAL_TYPE_UNDEFINED:
+            return "void";
+          case JSVAL_TYPE_NULL:
+            return "null";
+          case JSVAL_TYPE_BOOLEAN:
+            return "bool";
+          case JSVAL_TYPE_INT32:
+            return "int";
+          case JSVAL_TYPE_DOUBLE:
+            return "float";
+          case JSVAL_TYPE_STRING:
+            return "string";
+          case JSVAL_TYPE_SYMBOL:
+            return "symbol";
+          case JSVAL_TYPE_MAGIC:
+            return "lazyargs";
+          default:
+            MOZ_CRASH("Bad type");
+        }
+    }
+    if (type.isUnknown())
+        return "unknown";
+
+    MOZ_ASSERT(type.isAnyObject());
+    return "object";
+}
+
 #ifdef DEBUG
 
 static bool InferSpewActive(SpewChannel channel)
 {
     static bool active[SPEW_COUNT];
     static bool checked = false;
     if (!checked) {
         checked = true;
@@ -167,42 +199,18 @@ types::InferSpewColor(TypeSet *types)
     if (!InferSpewColorable())
         return "";
     return colors[DefaultHasher<TypeSet *>::hash(types) % 7];
 }
 
 const char *
 types::TypeString(Type type)
 {
-    if (type.isPrimitive()) {
-        switch (type.primitive()) {
-          case JSVAL_TYPE_UNDEFINED:
-            return "void";
-          case JSVAL_TYPE_NULL:
-            return "null";
-          case JSVAL_TYPE_BOOLEAN:
-            return "bool";
-          case JSVAL_TYPE_INT32:
-            return "int";
-          case JSVAL_TYPE_DOUBLE:
-            return "float";
-          case JSVAL_TYPE_STRING:
-            return "string";
-          case JSVAL_TYPE_SYMBOL:
-            return "symbol";
-          case JSVAL_TYPE_MAGIC:
-            return "lazyargs";
-          default:
-            MOZ_CRASH("Bad type");
-        }
-    }
-    if (type.isUnknown())
-        return "unknown";
-    if (type.isAnyObject())
-        return " object";
+    if (type.isPrimitive() || type.isUnknown() || type.isAnyObject())
+        return NonObjectTypeString(type);
 
     static char bufs[4][40];
     static unsigned which = 0;
     which = (which + 1) & 3;
 
     if (type.isSingleton())
         JS_snprintf(bufs[which], 40, "<0x%p>", (void *) type.singleton());
     else
@@ -4705,16 +4713,40 @@ ExclusiveContext::getLazySingletonGroup(
     ObjectGroupTablePostBarrier(this, &table, clasp, proto, nullptr);
 
     group->initSingleton((JSObject *) ObjectGroup::LAZY_SINGLETON);
     MOZ_ASSERT(group->singleton(), "created group must be a proper singleton");
 
     return group;
 }
 
+bool
+ExclusiveContext::findAllocationSiteForType(Type type, JSScript **script, uint32_t *offset) const
+{
+    *script = nullptr;
+    *offset = 0;
+
+    if (type.isUnknown() || type.isAnyObject() || !type.isGroup())
+        return false;
+    ObjectGroup *obj = type.group();
+
+    const AllocationSiteTable *table = compartment()->types.allocationSiteTable;
+    if (!table)
+        return false;
+
+    for (AllocationSiteTable::Range r = table->all(); !r.empty(); r.popFront()) {
+        if (obj == r.front().value()) {
+            *script = r.front().key().script;
+            *offset = r.front().key().offset;
+            return true;
+        }
+    }
+    return false;
+}
+
 /////////////////////////////////////////////////////////////////////
 // Tracing
 /////////////////////////////////////////////////////////////////////
 
 void
 ConstraintTypeSet::sweep(Zone *zone, AutoClearTypeInferenceStateOnOOM &oom)
 {
     MOZ_ASSERT(zone->isGCSweepingOrCompacting());
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -1785,16 +1785,18 @@ struct TypeZone
 };
 
 enum SpewChannel {
     ISpewOps,      /* ops: New constraints and types. */
     ISpewResult,   /* result: Final type sets. */
     SPEW_COUNT
 };
 
+const char *NonObjectTypeString(Type type);
+
 #ifdef DEBUG
 
 const char * InferSpewColorReset();
 const char * InferSpewColor(TypeConstraint *constraint);
 const char * InferSpewColor(TypeSet *types);
 
 void InferSpew(SpewChannel which, const char *fmt, ...);
 const char * TypeString(Type type);
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -79,16 +79,17 @@ EXPORTS.js += [
     '../public/Principals.h',
     '../public/ProfilingFrameIterator.h',
     '../public/ProfilingStack.h',
     '../public/RequiredDefines.h',
     '../public/RootingAPI.h',
     '../public/SliceBudget.h',
     '../public/StructuredClone.h',
     '../public/TracingAPI.h',
+    '../public/TrackedOptimizationInfo.h',
     '../public/TypeDecls.h',
     '../public/UbiNode.h',
     '../public/UbiNodeTraverse.h',
     '../public/Utility.h',
     '../public/Value.h',
     '../public/Vector.h',
     '../public/WeakMapPtr.h',
 ]