Bug 794679 - Cache for GetPcScript(). r=pierron
authorSean Stangl <sstangl@mozilla.com>
Mon, 01 Oct 2012 16:59:11 -0700
changeset 108797 f625a0dc10524e71789677b09189eed777078333
parent 108796 c2c611cc8df4b44d2d9b33b1ec8e8ca7866af8ca
child 108798 377cda004bd74736636556724ce69bf27d9c31b6
push id15700
push usersean.stangl@gmail.com
push dateMon, 01 Oct 2012 23:59:30 +0000
treeherdermozilla-inbound@f625a0dc1052 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspierron
bugs794679
milestone18.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 794679 - Cache for GetPcScript(). r=pierron
js/src/ion/IonFrames.cpp
js/src/ion/PcScriptCache-inl.h
js/src/ion/PcScriptCache.h
js/src/jsapi.cpp
js/src/jscntxt.h
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -11,16 +11,18 @@
 #include "jsscript.h"
 #include "jsfun.h"
 #include "IonCompartment.h"
 #include "IonFrames-inl.h"
 #include "IonFrameIterator-inl.h"
 #include "Safepoints.h"
 #include "IonSpewer.h"
 #include "IonMacroAssembler.h"
+#include "PcScriptCache.h"
+#include "PcScriptCache-inl.h"
 #include "gc/Marking.h"
 #include "SnapshotReader.h"
 #include "Safepoints.h"
 #include "VMFunctions.h"
 
 using namespace js;
 using namespace js::ion;
 
@@ -673,25 +675,47 @@ ion::AutoTempAllocatorRooter::trace(JSTr
 }
 
 void
 ion::GetPcScript(JSContext *cx, MutableHandleScript scriptRes, jsbytecode **pcRes)
 {
     JS_ASSERT(cx->fp()->beginsIonActivation());
     IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame.");
 
-    // Recover the innermost inlined frame.
-    IonFrameIterator it(cx->runtime->ionTop);
-    ++it;
+    JSRuntime *rt = cx->runtime;
+
+    // Recover the return address.
+    IonFrameIterator it(rt->ionTop);
+    uint8_t *retAddr = it.returnAddress();
+    uint32_t hash = PcScriptCache::Hash(retAddr);
+    JS_ASSERT(retAddr != NULL);
+
+    // Lazily initialize the cache. The allocation may safely fail and will not GC.
+    if (JS_UNLIKELY(rt->ionPcScriptCache == NULL)) {
+        rt->ionPcScriptCache = (PcScriptCache *)js_malloc(sizeof(struct PcScriptCache));
+        if (rt->ionPcScriptCache)
+            rt->ionPcScriptCache->clear(rt->gcNumber);
+    }
+
+    // Attempt to lookup address in cache.
+    if (rt->ionPcScriptCache && rt->ionPcScriptCache->get(rt, hash, retAddr, scriptRes, pcRes))
+        return;
+
+    // Lookup failed: undertake expensive process to recover the innermost inlined frame.
+    ++it; // Skip exit frame.
     InlineFrameIterator ifi(&it);
 
     // Set the result.
     scriptRes.set(ifi.script());
     if (pcRes)
         *pcRes = ifi.pc();
+
+    // Add entry to cache.
+    if (rt->ionPcScriptCache)
+        rt->ionPcScriptCache->add(hash, retAddr, ifi.pc(), ifi.script());
 }
 
 void
 OsiIndex::fixUpOffset(MacroAssembler &masm)
 {
     callPointDisplacement_ = masm.actualOffset(callPointDisplacement_);
 }
 
new file mode 100644
--- /dev/null
+++ b/js/src/ion/PcScriptCache-inl.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et 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 pcscriptcache_inl_h__
+#define pcscriptcache_inl_h__
+
+#include "PcScriptCache.h"
+
+namespace js {
+namespace ion {
+
+// Get a value from the cache. May perform lazy allocation.
+bool
+PcScriptCache::get(JSRuntime *rt, uint32_t hash, uint8_t *addr,
+                   MutableHandleScript scriptRes, jsbytecode **pcRes)
+{
+    // If a GC occurred, lazily clear the cache now.
+    if (gcNumber != rt->gcNumber) {
+        clear(rt->gcNumber);
+        return false;
+    }
+
+    if (entries[hash].returnAddress != addr)
+        return false;
+
+    scriptRes.set(entries[hash].script);
+    if (pcRes)
+        *pcRes = entries[hash].pc;
+
+    return true;
+}
+
+} // namespace ion
+} // namespace js
+
+#endif // pcscriptcache_inl_h__
new file mode 100644
--- /dev/null
+++ b/js/src/ion/PcScriptCache.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et 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 pcscriptcache_h__
+#define pcscriptcache_h__
+
+// Defines a fixed-size hash table solely for the purpose of caching ion::GetPcScript().
+// One cache is attached to each JSRuntime; it functions as if cleared on GC.
+
+struct JSRuntime;
+
+namespace js {
+namespace ion {
+
+struct PcScriptCacheEntry
+{
+    uint8_t *returnAddress; // Key into the hash table.
+    jsbytecode *pc;         // Cached PC.
+    JSScript *script;       // Cached script.
+};
+
+struct PcScriptCache
+{
+    static const uint32_t Length = 73;
+
+    // GC number at the time the cache was filled or created.
+    // Storing and checking against this number allows us to not bother
+    // clearing this cache on every GC -- only when actually necessary.
+    uint64_t gcNumber;
+
+    // List of cache entries.
+    PcScriptCacheEntry entries[Length];
+
+    void clear(uint64_t gcNumber) {
+        for (uint32_t i = 0; i < Length; i++)
+            entries[i].returnAddress = NULL;
+        this->gcNumber = gcNumber;
+    }
+
+    // Get a value from the cache. May perform lazy allocation.
+    // Defined in PcScriptCache-inl.h.
+    bool get(JSRuntime *rt, uint32_t hash, uint8_t *addr,
+             MutableHandleScript scriptRes, jsbytecode **pcRes);
+
+    void add(uint32_t hash, uint8_t *addr, jsbytecode *pc, JSScript *script) {
+        entries[hash].returnAddress = addr;
+        entries[hash].pc = pc;
+        entries[hash].script = script;
+    }
+
+    static uint32_t Hash(uint8_t *addr) {
+        uint32_t key = (uint32_t)((uintptr_t)addr);
+        return ((key >> 3) * 2654435761) % Length;
+    }
+};
+
+} // namespace ion
+} // namespace js
+
+#endif // pcscriptcache_h__
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -854,16 +854,17 @@ JSRuntime::JSRuntime()
     noGCOrAllocationCheck(0),
 #endif
     inOOMReport(0),
     jitHardening(false),
     ionTop(NULL),
     ionJSContext(NULL),
     ionStackLimit(0),
     ionActivation(NULL),
+    ionPcScriptCache(NULL),
     ionReturnOverride_(MagicValue(JS_ARG_POISON))
 {
     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
     JS_INIT_CLIST(&contextList);
     JS_INIT_CLIST(&debuggerList);
 
     PodZero(&debugHooks);
     PodZero(&atomState);
@@ -1001,16 +1002,19 @@ JSRuntime::~JSRuntime()
 #endif
 
     js_delete(bumpAlloc_);
     js_delete(mathCache_);
 #ifdef JS_METHODJIT
     js_delete(jaegerRuntime_);
 #endif
     js_delete(execAlloc_);  /* Delete after jaegerRuntime_. */
+
+    if (ionPcScriptCache)
+        js_delete(ionPcScriptCache);
 }
 
 #ifdef JS_THREADSAFE
 void
 JSRuntime::setOwnerThread()
 {
     JS_ASSERT(ownerThread_ == (void *)0xc1ea12);  /* "clear" */
     JS_ASSERT(requestDepth == 0);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -28,16 +28,18 @@
 
 #include "ds/LifoAlloc.h"
 #include "gc/Statistics.h"
 #include "js/HashTable.h"
 #include "js/Vector.h"
 #include "vm/Stack.h"
 #include "vm/SPSProfiler.h"
 
+#include "ion/PcScriptCache.h"
+
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #pragma warning(push)
 #pragma warning(disable:4355) /* Silence warning about "this" used in base member initializer list */
 #endif
 
 JS_BEGIN_EXTERN_C
@@ -942,16 +944,19 @@ struct JSRuntime : js::RuntimeFriendFiel
 
     void resetIonStackLimit() {
         ionStackLimit = nativeStackLimit;
     }
 
     // This points to the most recent Ion activation running on the thread.
     js::ion::IonActivation  *ionActivation;
 
+    // Cache for ion::GetPcScript().
+    js::ion::PcScriptCache *ionPcScriptCache;
+
   private:
     // In certain cases, we want to optimize certain opcodes to typed instructions,
     // to avoid carrying an extra register to feed into an unbox. Unfortunately,
     // that's not always possible. For example, a GetPropertyCacheT could return a
     // typed double, but if it takes its out-of-line path, it could return an
     // object, and trigger invalidation. The invalidation bailout will consider the
     // return value to be a double, and create a garbage Value.
     //