Bug 794679 - Cache for GetPcScript(). r=pierron
authorSean Stangl <sstangl@mozilla.com>
Mon, 01 Oct 2012 16:59:11 -0700
changeset 108949 f625a0dc10524e71789677b09189eed777078333
parent 108948 c2c611cc8df4b44d2d9b33b1ec8e8ca7866af8ca
child 108950 377cda004bd74736636556724ce69bf27d9c31b6
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewerspierron
bugs794679
milestone18.0a1
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.
     //