Bug 1316078 part 4 - Add a script decoder as a valid off-main-thread parse-task. r=bhackett
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Tue, 31 Jan 2017 20:03:57 +0000
changeset 331930 2793ad041967772d99bd0cce71ad0d8ff4f55744
parent 331929 37077abfae0610840639fea78b8222775990aca3
child 331931 6f9ff384f30d87fb5622f76dec76af5ed8de4eed
push id31291
push usercbook@mozilla.com
push dateWed, 01 Feb 2017 12:14:40 +0000
treeherdermozilla-central@9e7b1041929f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs1316078
milestone54.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 1316078 part 4 - Add a script decoder as a valid off-main-thread parse-task. r=bhackett
js/src/frontend/Parser.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3125,17 +3125,17 @@ Parser<ParseHandler>::functionDefinition
         if (!skipLazyInnerFunction(pn, kind, tryAnnexB))
             return null();
         return pn;
     }
 
     RootedObject proto(context);
     if (generatorKind == StarGenerator) {
         // If we are off the main thread, the generator meta-objects have
-        // already been created by js::StartOffThreadParseScript, so cx will not
+        // already been created by js::StartOffThreadParseTask, so cx will not
         // be necessary.
         JSContext* cx = context->maybeJSContext();
         proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
         if (!proto)
             return null();
     }
     RootedFunction fun(context, newFunction(funName, kind, generatorKind, asyncKind, proto));
     if (!fun)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4162,16 +4162,41 @@ JS_PUBLIC_API(void)
 JS::CancelOffThreadModule(JSContext* cx, void* token)
 {
     MOZ_ASSERT(cx);
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
     HelperThreadState().cancelParseTask(cx, ParseTaskKind::Module, token);
 }
 
 JS_PUBLIC_API(bool)
+JS::DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+                          mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
+                          OffThreadCompileCallback callback, void* callbackData)
+{
+    MOZ_ASSERT(CanCompileOffThread(cx, options, buffer.length() - cursor));
+    return StartOffThreadDecodeScript(cx, options, buffer, cursor, callback, callbackData);
+}
+
+JS_PUBLIC_API(JSScript*)
+JS::FinishOffThreadScriptDecoder(JSContext* cx, void* token)
+{
+    MOZ_ASSERT(cx);
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
+    return HelperThreadState().finishScriptDecodeTask(cx, token);
+}
+
+JS_PUBLIC_API(void)
+JS::CancelOffThreadScriptDecoder(JSContext* cx, void* token)
+{
+    MOZ_ASSERT(cx);
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
+    HelperThreadState().cancelParseTask(cx, ParseTaskKind::ScriptDecode, token);
+}
+
+JS_PUBLIC_API(bool)
 JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
                  const JS::CompileOptions& options, MutableHandleScript script)
 {
     return Compile(cx, options, ascii, length, script);
 }
 
 JS_PUBLIC_API(bool)
 JS_CompileUCScript(JSContext* cx, const char16_t* chars, size_t length,
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3767,17 +3767,17 @@ namespace JS {
  * immediately. The worker thread will come along at some later point, and use
  * the options.
  *
  * The compiler itself just needs to be able to access a collection of options;
  * it doesn't care who owns them, or what's keeping them alive. It does its own
  * addrefs/copies/tracing/etc.
  *
  * Furthermore, in some cases compile options are propagated from one entity to
- * another (e.g. from a scriipt to a function defined in that script).  This
+ * another (e.g. from a script to a function defined in that script).  This
  * involves copying over some, but not all, of the options.
  *
  * So, we have a class hierarchy that reflects these four use cases:
  *
  * - TransitiveCompileOptions is the common base class, representing options
  *   that should get propagated from a script to functions defined in that
  *   script.  This is never instantiated directly.
  *
@@ -4207,16 +4207,27 @@ CompileOffThreadModule(JSContext* cx, co
                        OffThreadCompileCallback callback, void* callbackData);
 
 extern JS_PUBLIC_API(JSObject*)
 FinishOffThreadModule(JSContext* cx, void* token);
 
 extern JS_PUBLIC_API(void)
 CancelOffThreadModule(JSContext* cx, void* token);
 
+extern JS_PUBLIC_API(bool)
+DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+                      mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
+                      OffThreadCompileCallback callback, void* callbackData);
+
+extern JS_PUBLIC_API(JSScript*)
+FinishOffThreadScriptDecoder(JSContext* cx, void* token);
+
+extern JS_PUBLIC_API(void)
+CancelOffThreadScriptDecoder(JSContext* cx, void* token);
+
 /**
  * Compile a function with envChain plus the global as its scope chain.
  * envChain must contain objects in the current compartment of cx.  The actual
  * scope chain used for the function will consist of With wrappers for those
  * objects, followed by the current global of the compartment cx is in.  This
  * global must not be explicitly included in the scope chain.
  */
 extern JS_PUBLIC_API(bool)
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -16,16 +16,17 @@
 #include "frontend/BytecodeCompiler.h"
 #include "gc/GCInternals.h"
 #include "jit/IonBuilder.h"
 #include "threading/CpuCount.h"
 #include "vm/Debugger.h"
 #include "vm/SharedImmutableStringsCache.h"
 #include "vm/Time.h"
 #include "vm/TraceLogging.h"
+#include "vm/Xdr.h"
 
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 
@@ -296,16 +297,28 @@ ParseTask::ParseTask(ParseTaskKind kind,
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     exclusiveContextGlobal(exclusiveContextGlobal),
     callback(callback), callbackData(callbackData),
     script(nullptr), sourceObject(nullptr),
     errors(cx), overRecursed(false), outOfMemory(false)
 {
 }
 
+ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
+                     JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
+                     JS::OffThreadCompileCallback callback, void* callbackData)
+  : kind(kind), cx(cx), options(initCx), buffer(&buffer), cursor(cursor),
+    alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
+    exclusiveContextGlobal(exclusiveContextGlobal),
+    callback(callback), callbackData(callbackData),
+    script(nullptr), sourceObject(nullptr),
+    errors(cx), overRecursed(false), outOfMemory(false)
+{
+}
+
 bool
 ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
 {
     if (!this->options.copy(cx, options))
         return false;
 
     return true;
 }
@@ -381,16 +394,39 @@ void
 ModuleParseTask::parse()
 {
     SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
     ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject);
     if (module)
         script = module->script();
 }
 
+ScriptDecodeTask::ScriptDecodeTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
+                                   JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
+                                   JS::OffThreadCompileCallback callback, void* callbackData)
+  : ParseTask(ParseTaskKind::ScriptDecode, cx, exclusiveContextGlobal, initCx,
+              buffer, cursor, callback, callbackData)
+{
+}
+
+void
+ScriptDecodeTask::parse()
+{
+    RootedScript resultScript(cx);
+    XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject,
+                                *buffer, cursor);
+    decoder.codeScript(&resultScript);
+    MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
+    if (decoder.resultCode() == JS::TranscodeResult_Ok) {
+        script = resultScript.get();
+    } else {
+        sourceObject = nullptr;
+    }
+}
+
 void
 js::CancelOffThreadParses(JSRuntime* rt)
 {
     AutoLockHelperThreadState lock;
 
     if (!HelperThreadState().threads)
         return;
 
@@ -609,16 +645,28 @@ js::StartOffThreadParseModule(JSContext*
 {
     auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ModuleParseTask* {
         return cx->new_<ModuleParseTask>(helpercx, global, cx, chars, length,
                                          callback, callbackData);
     };
     return StartOffThreadParseTask(cx, options, ParseTaskKind::Module, functor);
 }
 
+bool
+js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+                               JS::TranscodeBuffer& buffer, size_t cursor,
+                               JS::OffThreadCompileCallback callback, void* callbackData)
+{
+    auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ScriptDecodeTask* {
+        return cx->new_<ScriptDecodeTask>(helpercx, global, cx, buffer, cursor,
+                                          callback, callbackData);
+    };
+    return StartOffThreadParseTask(cx, options, ParseTaskKind::ScriptDecode, functor);
+}
+
 void
 js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
 {
     MOZ_ASSERT(!OffThreadParsingMustWaitForGC(rt));
 
     GlobalHelperThreadState::ParseTaskVector newTasks;
     {
         AutoLockHelperThreadState lock;
@@ -1256,16 +1304,24 @@ GlobalHelperThreadState::finishParseTask
 JSScript*
 GlobalHelperThreadState::finishScriptParseTask(JSContext* cx, void* token)
 {
     JSScript* script = finishParseTask(cx, ParseTaskKind::Script, token);
     MOZ_ASSERT_IF(script, script->isGlobalCode());
     return script;
 }
 
+JSScript*
+GlobalHelperThreadState::finishScriptDecodeTask(JSContext* cx, void* token)
+{
+    JSScript* script = finishParseTask(cx, ParseTaskKind::ScriptDecode, token);
+    MOZ_ASSERT_IF(script, script->isGlobalCode());
+    return script;
+}
+
 JSObject*
 GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
 {
     JSScript* script = finishParseTask(cx, ParseTaskKind::Module, token);
     if (!script)
         return nullptr;
 
     MOZ_ASSERT(script->module());
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -14,16 +14,17 @@
 #define vm_HelperThreads_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Variant.h"
 
+#include "jsapi.h"
 #include "jscntxt.h"
 
 #include "frontend/TokenStream.h"
 #include "jit/Ion.h"
 #include "threading/ConditionVariable.h"
 #include "vm/MutexIDs.h"
 
 namespace JS {
@@ -45,17 +46,18 @@ namespace wasm {
   class FunctionCompileResults;
   class CompileTask;
   typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
 } // namespace wasm
 
 enum class ParseTaskKind
 {
     Script,
-    Module
+    Module,
+    ScriptDecode
 };
 
 // Per-process state for off thread work items.
 class GlobalHelperThreadState
 {
     friend class AutoLockHelperThreadState;
     friend class AutoUnlockHelperThreadState;
 
@@ -253,16 +255,17 @@ class GlobalHelperThreadState
     /*
      * Error string from wasm validation. Arbitrarily choose to keep the first one that gets
      * reported. Nondeterministic if multiple threads have errors.
      */
     UniqueChars firstWasmError;
 
   public:
     JSScript* finishScriptParseTask(JSContext* cx, void* token);
+    JSScript* finishScriptDecodeTask(JSContext* cx, void* token);
     JSObject* finishModuleParseTask(JSContext* cx, void* token);
     bool compressionInProgress(SourceCompressionTask* task, const AutoLockHelperThreadState& lock);
     SourceCompressionTask* compressionTaskForSource(ScriptSource* ss, const AutoLockHelperThreadState& lock);
 
     bool hasActiveThreads(const AutoLockHelperThreadState&);
     void waitForAllThreads();
 
     template <typename T>
@@ -499,16 +502,21 @@ StartOffThreadParseScript(JSContext* cx,
                           const char16_t* chars, size_t length,
                           JS::OffThreadCompileCallback callback, void* callbackData);
 
 bool
 StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
                           const char16_t* chars, size_t length,
                           JS::OffThreadCompileCallback callback, void* callbackData);
 
+bool
+StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+                           JS::TranscodeBuffer& buffer, size_t cursor,
+                           JS::OffThreadCompileCallback callback, void* callbackData);
+
 /*
  * Called at the end of GC to enqueue any Parse tasks that were waiting on an
  * atoms-zone GC to finish.
  */
 void
 EnqueuePendingParseTasksAfterGC(JSRuntime* rt);
 
 struct AutoEnqueuePendingParseTasksAfterGC {
@@ -551,18 +559,31 @@ class MOZ_RAII AutoUnlockHelperThreadSta
     }
 };
 
 struct ParseTask
 {
     ParseTaskKind kind;
     ExclusiveContext* cx;
     OwningCompileOptions options;
-    const char16_t* chars;
-    size_t length;
+    // Anonymous union, the only correct interpretation is provided by the
+    // ParseTaskKind value, or from the virtual parse function.
+    union {
+        struct {
+            const char16_t* chars;
+            size_t length;
+        };
+        struct {
+            // This should be a reference, but C++ prevents us from using union
+            // with references as it assumes the reference constness might be
+            // violated.
+            JS::TranscodeBuffer* const buffer;
+            size_t cursor;
+        };
+    };
     LifoAlloc alloc;
 
     // Rooted pointer to the global object used by 'cx'.
     JSObject* exclusiveContextGlobal;
 
     // Callback invoked off the main thread when the parse finishes.
     JS::OffThreadCompileCallback callback;
     void* callbackData;
@@ -579,16 +600,19 @@ struct ParseTask
     // when finishing the script.
     Vector<frontend::CompileError*> errors;
     bool overRecursed;
     bool outOfMemory;
 
     ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
               JSContext* initCx, const char16_t* chars, size_t length,
               JS::OffThreadCompileCallback callback, void* callbackData);
+    ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
+              JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
+              JS::OffThreadCompileCallback callback, void* callbackData);
     bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
 
     void activate(JSRuntime* rt);
     virtual void parse() = 0;
     bool finish(JSContext* cx);
 
     bool runtimeMatches(JSRuntime* rt) {
         return cx->runtimeMatches(rt);
@@ -610,16 +634,24 @@ struct ScriptParseTask : public ParseTas
 struct ModuleParseTask : public ParseTask
 {
     ModuleParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
                     JSContext* initCx, const char16_t* chars, size_t length,
                     JS::OffThreadCompileCallback callback, void* callbackData);
     void parse() override;
 };
 
+struct ScriptDecodeTask : public ParseTask
+{
+    ScriptDecodeTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
+                     JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
+                     JS::OffThreadCompileCallback callback, void* callbackData);
+    void parse() override;
+};
+
 // Return whether, if a new parse task was started, it would need to wait for
 // an in-progress GC to complete before starting.
 extern bool
 OffThreadParsingMustWaitForGC(JSRuntime* rt);
 
 // Compression tasks are allocated on the stack by their triggering thread,
 // which will block on the compression completing as the task goes out of scope
 // to ensure it completes at the required time.