diff --git a/js/src/ion/IonFrames.cpp b/js/src/ion/IonFrames.cpp index 1e1734d98c20..9f735c9e9e5c 100644 --- a/js/src/ion/IonFrames.cpp +++ b/js/src/ion/IonFrames.cpp @@ -16,6 +16,8 @@ #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" @@ -678,15 +680,37 @@ ion::GetPcScript(JSContext *cx, MutableHandleScript scriptRes, jsbytecode **pcRe 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 diff --git a/js/src/ion/PcScriptCache-inl.h b/js/src/ion/PcScriptCache-inl.h new file mode 100644 index 000000000000..a5674af7acca --- /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__ diff --git a/js/src/ion/PcScriptCache.h b/js/src/ion/PcScriptCache.h new file mode 100644 index 000000000000..29abbc108afc --- /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__ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 1b4845ed7168..7cb4d9f1d934 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -859,6 +859,7 @@ JSRuntime::JSRuntime() ionJSContext(NULL), ionStackLimit(0), ionActivation(NULL), + ionPcScriptCache(NULL), ionReturnOverride_(MagicValue(JS_ARG_POISON)) { /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ @@ -1006,6 +1007,9 @@ JSRuntime::~JSRuntime() js_delete(jaegerRuntime_); #endif js_delete(execAlloc_); /* Delete after jaegerRuntime_. */ + + if (ionPcScriptCache) + js_delete(ionPcScriptCache); } #ifdef JS_THREADSAFE diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index c2f4588881b6..8dddc1e1e56b 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -33,6 +33,8 @@ #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 */ @@ -947,6 +949,9 @@ struct JSRuntime : js::RuntimeFriendFields // 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,