Bug 1004110 - Memoize PCToLineNumber lookups in SavedStacks. r=terrence
authorNick Fitzgerald <fitzgen@mozilla.com>
Tue, 17 Jun 2014 16:32:00 -0400
changeset 189457 466812b8348144eb4f0371ac92b47826fcbbc63f
parent 189456 2c7d100d0595b5bc9e24acad3a9591c0a9db02fb
child 189458 516a5bdb4f16edd14bbd19cfdc7505cf6914c72b
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersterrence
bugs1004110
milestone33.0a1
Bug 1004110 - Memoize PCToLineNumber lookups in SavedStacks. r=terrence
js/src/gc/Barrier.h
js/src/vm/SavedStacks.cpp
js/src/vm/SavedStacks.h
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -824,16 +824,17 @@ typedef ReadBarriered<DebugScopeObject*>
 typedef ReadBarriered<GlobalObject*> ReadBarrieredGlobalObject;
 typedef ReadBarriered<JSFunction*> ReadBarrieredFunction;
 typedef ReadBarriered<JSObject*> ReadBarrieredObject;
 typedef ReadBarriered<ScriptSourceObject*> ReadBarrieredScriptSourceObject;
 typedef ReadBarriered<Shape*> ReadBarrieredShape;
 typedef ReadBarriered<UnownedBaseShape*> ReadBarrieredUnownedBaseShape;
 typedef ReadBarriered<jit::JitCode*> ReadBarrieredJitCode;
 typedef ReadBarriered<types::TypeObject*> ReadBarrieredTypeObject;
+typedef ReadBarriered<JSAtom*> ReadBarrieredAtom;
 
 typedef ReadBarriered<Value> ReadBarrieredValue;
 
 // A pre- and post-barriered Value that is specialized to be aware that it
 // resides in a slots or elements vector. This allows it to be relocated in
 // memory, but with substantially less overhead than a RelocatablePtr.
 class HeapSlot : public BarrieredBase<Value>
 {
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -344,16 +344,19 @@ SavedFrame::toStringMethod(JSContext *cx
     JS_FN("constructor", SavedFrame::construct, 0, 0),
     JS_FN("toString", SavedFrame::toStringMethod, 0, 0),
     JS_FS_END
 };
 
 bool
 SavedStacks::init()
 {
+    if (!pcLocationMap.init())
+        return false;
+
     return frames.init();
 }
 
 bool
 SavedStacks::saveCurrentStack(JSContext *cx, MutableHandle<SavedFrame*> frame)
 {
     JS_ASSERT(initialized());
     JS_ASSERT(&cx->compartment()->savedStacks() == this);
@@ -389,16 +392,18 @@ SavedStacks::sweep(JSRuntime *rt)
                                                     parent,
                                                     frame->getPrincipals()),
                                  ReadBarriered<SavedFrame *>(frame));
                 }
             }
         }
     }
 
+    sweepPCLocationMap();
+
     if (savedFrameProto && IsObjectAboutToBeFinalized(&savedFrameProto)) {
         savedFrameProto = nullptr;
     }
 }
 
 uint32_t
 SavedStacks::count()
 {
@@ -434,30 +439,24 @@ SavedStacks::insertFrames(JSContext *cx,
     // in js/src/jit-test/tests/saved-stacks/bug-1006876-too-much-recursion.js).
     JS_CHECK_RECURSION_DONT_REPORT(cx, return false);
 
     ScriptFrameIter thisFrame(iter);
     Rooted<SavedFrame*> parentFrame(cx);
     if (!insertFrames(cx, ++iter, &parentFrame))
         return false;
 
-    RootedScript script(cx, thisFrame.script());
-    RootedFunction callee(cx, thisFrame.maybeCallee());
-    const char *filename = script->filename();
-    if (!filename)
-        filename = "";
-    RootedAtom source(cx, Atomize(cx, filename, strlen(filename)));
-    if (!source)
+    LocationValue location;
+    if (!getLocation(cx, thisFrame.script(), thisFrame.pc(), &location))
         return false;
-    uint32_t column;
-    uint32_t line = PCToLineNumber(script, thisFrame.pc(), &column);
 
-    SavedFrame::Lookup lookup(source,
-                              line,
-                              column,
+    JSFunction *callee = thisFrame.maybeCallee();
+    SavedFrame::Lookup lookup(location.source,
+                              location.line,
+                              location.column,
                               callee ? callee->displayAtom() : nullptr,
                               parentFrame,
                               thisFrame.compartment()->principals);
 
     frame.set(getOrCreateSavedFrame(cx, lookup));
     return frame.get() != nullptr;
 }
 
@@ -523,16 +522,59 @@ SavedStacks::createFrameFromLookup(JSCon
         return nullptr;
 
     SavedFrame &f = frameObj->as<SavedFrame>();
     f.initFromLookup(lookup);
 
     return &f;
 }
 
+/*
+ * Remove entries from the table whose JSScript is being collected.
+ */
+void
+SavedStacks::sweepPCLocationMap()
+{
+    for (PCLocationMap::Enum e(pcLocationMap); !e.empty(); e.popFront()) {
+        PCKey key = e.front().key();
+        JSScript *script = key.script.get();
+        if (IsScriptAboutToBeFinalized(&script)) {
+            e.removeFront();
+        } else if (script != key.script.get()) {
+            key.script = script;
+            e.rekeyFront(key);
+        }
+    }
+}
+
+bool
+SavedStacks::getLocation(JSContext *cx, JSScript *script, jsbytecode *pc,
+                         LocationValue *locationp)
+{
+    PCKey key(script, pc);
+    PCLocationMap::AddPtr p = pcLocationMap.lookupForAdd(key);
+
+    if (!p) {
+        const char *filename = script->filename() ? script->filename() : "";
+        RootedAtom source(cx, Atomize(cx, filename, strlen(filename)));
+        if (!source)
+            return false;
+
+        uint32_t column;
+        uint32_t line = PCToLineNumber(script, pc, &column);
+
+        LocationValue value(source, line, column);
+        if (!pcLocationMap.add(p, key, value))
+            return false;
+    }
+
+    *locationp = p->value();
+    return true;
+}
+
 bool
 SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata)
 {
     Rooted<SavedFrame *> frame(cx);
     if (!cx->compartment()->savedStacks().saveCurrentStack(cx, &frame))
         return false;
     *pmetadata = frame;
     return true;
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -76,33 +76,33 @@ class SavedFrame : public JSObject {
     bool         parentMoved();
     void         updatePrivateParent();
 
     static SavedFrame *checkThis(JSContext *cx, CallArgs &args, const char *fnName);
 };
 
 struct SavedFrame::Lookup {
     Lookup(JSAtom *source, size_t line, size_t column, JSAtom *functionDisplayName,
-           Handle<SavedFrame*> parent, JSPrincipals *principals)
+           SavedFrame *parent, JSPrincipals *principals)
         : source(source),
           line(line),
           column(column),
           functionDisplayName(functionDisplayName),
           parent(parent),
           principals(principals)
     {
         JS_ASSERT(source);
     }
 
-    JSAtom              *source;
-    size_t              line;
-    size_t              column;
-    JSAtom              *functionDisplayName;
-    Handle<SavedFrame*> parent;
-    JSPrincipals        *principals;
+    JSAtom       *source;
+    size_t       line;
+    size_t       column;
+    JSAtom       *functionDisplayName;
+    SavedFrame   *parent;
+    JSPrincipals *principals;
 };
 
 struct SavedFrame::HashPolicy
 {
     typedef SavedFrame::Lookup               Lookup;
     typedef PointerHasher<SavedFrame *, 3>   SavedFramePtrHasher;
     typedef PointerHasher<JSPrincipals *, 3> JSPrincipalsPtrHasher;
 
@@ -131,15 +131,58 @@ class SavedStacks {
     JSObject                 *savedFrameProto;
 
     bool       insertFrames(JSContext *cx, ScriptFrameIter &iter, MutableHandle<SavedFrame*> frame);
     SavedFrame *getOrCreateSavedFrame(JSContext *cx, SavedFrame::Lookup &lookup);
     // |SavedFrame.prototype| is created lazily and held weakly. It should only
     // be accessed through this method.
     JSObject   *getOrCreateSavedFramePrototype(JSContext *cx);
     SavedFrame *createFrameFromLookup(JSContext *cx, SavedFrame::Lookup &lookup);
+
+    // Cache for memoizing PCToLineNumber lookups.
+
+    struct PCKey {
+        PCKey(JSScript *script, jsbytecode *pc) : script(script), pc(pc) { }
+
+        PreBarrieredScript script;
+        jsbytecode         *pc;
+    };
+
+    struct LocationValue {
+        LocationValue() : source(nullptr), line(0), column(0) { }
+        LocationValue(JSAtom *source, size_t line, size_t column)
+            : source(source),
+              line(line),
+              column(column)
+        { }
+
+        ReadBarrieredAtom source;
+        size_t            line;
+        size_t            column;
+    };
+
+    struct PCLocationHasher : public DefaultHasher<PCKey> {
+        typedef PointerHasher<JSScript *, 3>   ScriptPtrHasher;
+        typedef PointerHasher<jsbytecode *, 3> BytecodePtrHasher;
+
+        static HashNumber hash(const PCKey &key) {
+            return mozilla::AddToHash(ScriptPtrHasher::hash(key.script),
+                                      BytecodePtrHasher::hash(key.pc));
+        }
+
+        static bool match(const PCKey &l, const PCKey &k) {
+            return l.script == k.script && l.pc == k.pc;
+        }
+    };
+
+    typedef HashMap<PCKey, LocationValue, PCLocationHasher, SystemAllocPolicy> PCLocationMap;
+
+    PCLocationMap pcLocationMap;
+
+    void sweepPCLocationMap();
+    bool getLocation(JSContext *cx, JSScript *script, jsbytecode *pc, LocationValue *locationp);
 };
 
 bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata);
 
 } /* namespace js */
 
 #endif /* vm_SavedStacks_h */