Bug 1068697 - Add a compartment option to preserve JIT code in non-shrinking GCs r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 22 Sep 2014 08:43:36 +0100
changeset 206495 32c2b586c5721077a9fbb29744125db23d451345
parent 206494 73272a5342976fc555bd947849e81f9c4c5aa457
child 206496 3161ad541392fa48ff11ffea06dbbc2b17d8a02c
push id27529
push userryanvm@gmail.com
push dateMon, 22 Sep 2014 19:49:52 +0000
treeherdermozilla-central@f4037194394e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1068697
milestone35.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 1068697 - Add a compartment option to preserve JIT code in non-shrinking GCs r=terrence
js/src/jsapi-tests/moz.build
js/src/jsapi-tests/testPreserveJitCode.cpp
js/src/jsapi.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -52,16 +52,17 @@ UNIFIED_SOURCES += [
     'testNewObject.cpp',
     'testNullRoot.cpp',
     'testObjectEmulatingUndefined.cpp',
     'testOOM.cpp',
     'testOps.cpp',
     'testOriginPrincipals.cpp',
     'testParseJSON.cpp',
     'testPersistentRooted.cpp',
+    'testPreserveJitCode.cpp',
     'testProfileStrings.cpp',
     'testPropCache.cpp',
     'testRegExp.cpp',
     'testResolveRecursion.cpp',
     'tests.cpp',
     'testSameValue.cpp',
     'testSavedStacks.cpp',
     'testScriptInfo.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testPreserveJitCode.cpp
@@ -0,0 +1,83 @@
+/* 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/. */
+
+#include "jsapi-tests/tests.h"
+
+using namespace JS;
+
+static void
+ScriptCallback(JSRuntime *rt, void *data, JSScript *script)
+{
+    unsigned &count = *static_cast<unsigned *>(data);
+    if (script->hasIonScript())
+        ++count;
+}
+
+BEGIN_TEST(test_PreserveJitCode)
+{
+    CHECK(testPreserveJitCode(false, 0));
+    CHECK(testPreserveJitCode(true, 1));
+    return true;
+}
+
+unsigned
+countIonScripts(JSObject *global)
+{
+    unsigned count = 0;
+    js::IterateScripts(rt, global->compartment(), &count, ScriptCallback);
+    return count;
+}
+
+bool
+testPreserveJitCode(bool preserveJitCode, unsigned remainingIonScripts)
+{
+    rt->options().setBaseline(true);
+    rt->options().setIon(true);
+    rt->setOffthreadIonCompilationEnabled(false);
+
+    RootedObject global(cx, createTestGlobal(preserveJitCode));
+    CHECK(global);
+    JSAutoCompartment ac(cx, global);
+
+    CHECK_EQUAL(countIonScripts(global), 0u);
+
+    const char *source =
+        "var i = 0;\n"
+        "var sum = 0;\n"
+        "while (i < 10) {\n"
+        "    sum += i;\n"
+        "    ++i;\n"
+        "}\n"
+        "return sum;\n";
+    unsigned length = strlen(source);
+
+    JS::RootedFunction fun(cx);
+    JS::CompileOptions options(cx);
+    options.setFileAndLine(__FILE__, 1);
+    CHECK(JS_CompileFunction(cx, global, "f", 0, nullptr, source, length, options, &fun));
+
+    RootedValue value(cx);
+    for (unsigned i = 0; i < 1500; ++i)
+        CHECK(JS_CallFunction(cx, global, fun, JS::HandleValueArray::empty(), &value));
+    CHECK_EQUAL(value.toInt32(), 45);
+    CHECK_EQUAL(countIonScripts(global), 1u);
+
+    GCForReason(rt, gcreason::API);
+    CHECK_EQUAL(countIonScripts(global), remainingIonScripts);
+
+    ShrinkingGC(rt, gcreason::API);
+    CHECK_EQUAL(countIonScripts(global), 0u);
+
+    return true;
+}
+
+JSObject *
+createTestGlobal(bool preserveJitCode)
+{
+    JS::CompartmentOptions options;
+    options.setVersion(JSVERSION_LATEST);
+    options.setPreserveJitCode(preserveJitCode);
+    return JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook, options);
+}
+END_TEST(test_PreserveJitCode)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2557,16 +2557,17 @@ class JS_PUBLIC_API(CompartmentOptions)
       : version_(JSVERSION_UNKNOWN)
       , invisibleToDebugger_(false)
       , mergeable_(false)
       , discardSource_(false)
       , cloneSingletons_(false)
       , traceGlobal_(nullptr)
       , singletonsAsTemplates_(true)
       , addonId_(nullptr)
+      , preserveJitCode_(false)
     {
         zone_.spec = JS::FreshZone;
     }
 
     JSVersion version() const { return version_; }
     CompartmentOptions &setVersion(JSVersion aVersion) {
         MOZ_ASSERT(aVersion != JSVERSION_UNKNOWN);
         version_ = aVersion;
@@ -2637,16 +2638,22 @@ class JS_PUBLIC_API(CompartmentOptions)
     CompartmentOptions &setTrace(JSTraceOp op) {
         traceGlobal_ = op;
         return *this;
     }
     JSTraceOp getTrace() const {
         return traceGlobal_;
     }
 
+    bool preserveJitCode() const { return preserveJitCode_; }
+    CompartmentOptions &setPreserveJitCode(bool flag) {
+        preserveJitCode_ = flag;
+        return *this;
+    }
+
   private:
     JSVersion version_;
     bool invisibleToDebugger_;
     bool mergeable_;
     bool discardSource_;
     bool cloneSingletons_;
     Override extraWarningsOverride_;
     union {
@@ -2656,16 +2663,17 @@ class JS_PUBLIC_API(CompartmentOptions)
     JSTraceOp traceGlobal_;
 
     // To XDR singletons, we need to ensure that all singletons are all used as
     // templates, by making JSOP_OBJECT return a clone of the JSScript
     // singleton, instead of returning the value which is baked in the JSScript.
     bool singletonsAsTemplates_;
 
     JSAddonId *addonId_;
+    bool preserveJitCode_;
 };
 
 JS_PUBLIC_API(CompartmentOptions &)
 CompartmentOptionsRef(JSCompartment *compartment);
 
 JS_PUBLIC_API(CompartmentOptions &)
 CompartmentOptionsRef(JSObject *obj);
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -54,16 +54,17 @@ JSCompartment::JSCompartment(Zone *zone,
     objectMetadataCallback(nullptr),
     lastAnimationTime(0),
     regExps(runtime_),
     globalWriteBarriered(false),
     propertyTree(thisForCtor()),
     selfHostingScriptSource(nullptr),
     gcIncomingGrayPointers(nullptr),
     gcWeakMapList(nullptr),
+    gcPreserveJitCode(options.preserveJitCode()),
     debugModeBits(0),
     rngState(0),
     watchpointMap(nullptr),
     scriptCountsMap(nullptr),
     debugScriptMap(nullptr),
     debugScopes(nullptr),
     enumerators(nullptr),
     compartmentStats(nullptr),
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -290,16 +290,19 @@ struct JSCompartment
      * slot for the former, or a special slot for the latter.
      */
     JSObject                     *gcIncomingGrayPointers;
 
     /* Linked list of live weakmaps in this compartment. */
     js::WeakMapBase              *gcWeakMapList;
 
   private:
+    /* Whether to preserve JIT code on non-shrinking GCs. */
+    bool                         gcPreserveJitCode;
+
     enum {
         DebugMode = 1 << 0,
         DebugNeedDelazification = 1 << 1
     };
 
     unsigned                     debugModeBits;
 
   public:
@@ -342,17 +345,17 @@ struct JSCompartment
     }
 
     struct WrapperEnum : public js::WrapperMap::Enum {
         explicit WrapperEnum(JSCompartment *c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {}
     };
 
     void trace(JSTracer *trc);
     void markRoots(JSTracer *trc);
-    bool isDiscardingJitCode(JSTracer *trc);
+    bool preserveJitCode() { return gcPreserveJitCode; }
     void sweep(js::FreeOp *fop, bool releaseTypes);
     void sweepCrossCompartmentWrappers();
     void purge();
     void clearTables();
 
 #ifdef JSGC_COMPACTING
     void fixupInitialShapeTable();
     void fixupNewTypeObjectTable(js::types::TypeObjectWithNewScriptSet &table);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3554,16 +3554,18 @@ bool
 GCRuntime::shouldPreserveJITCode(JSCompartment *comp, int64_t currentTime,
                                  JS::gcreason::Reason reason)
 {
     if (cleanUpEverything)
         return false;
 
     if (alwaysPreserveCode)
         return true;
+    if (comp->preserveJitCode())
+        return true;
     if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime)
         return true;
     if (reason == JS::gcreason::DEBUG_GC)
         return true;
 
     if (comp->jitCompartment() && comp->jitCompartment()->hasRecentParallelActivity())
         return true;