Bug 1329018 - Part 1: Wasm: Add plumbing for compile threads to report validation errors. r=luke
authorDavid Major <dmajor@mozilla.com>
Fri, 06 Jan 2017 17:15:23 -0600
changeset 356443 6aea4b362dc58a0764cee4e181b40e2c21a2b34e
parent 356442 f02e4baff85d7c3b7e745fce05ca419858b7142b
child 356444 b4d14dfa3cf892f0a61ff610fdc72adc1cea2296
push id10621
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 16:02:43 +0000
treeherdermozilla-aurora@dca7b42e6c67 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1329018
milestone53.0a1
Bug 1329018 - Part 1: Wasm: Add plumbing for compile threads to report validation errors. r=luke MozReview-Commit-ID: 2cS7skk5tdk
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmBaselineCompile.h
js/src/wasm/WasmCompile.cpp
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmGenerator.h
js/src/wasm/WasmIonCompile.cpp
js/src/wasm/WasmIonCompile.h
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1411,30 +1411,33 @@ HelperThread::ThreadMain(void* arg)
 void
 HelperThread::handleWasmWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(HelperThreadState().canStartWasmCompile(locked));
     MOZ_ASSERT(idle());
 
     currentTask.emplace(HelperThreadState().wasmWorklist(locked).popCopy());
     bool success = false;
+    UniqueChars error;
 
     wasm::CompileTask* task = wasmTask();
     {
         AutoUnlockHelperThreadState unlock(locked);
-        success = wasm::CompileFunction(task);
+        success = wasm::CompileFunction(task, &error);
     }
 
     // On success, try to move work to the finished list.
     if (success)
         success = HelperThreadState().wasmFinishedList(locked).append(task);
 
     // On failure, note the failure for harvesting by the parent.
-    if (!success)
+    if (!success) {
         HelperThreadState().noteWasmFailure(locked);
+        HelperThreadState().setWasmError(locked, Move(error));
+    }
 
     // Notify the main thread in case it's waiting.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
     currentTask.reset();
 }
 
 void
 HelperThread::handlePromiseTaskWorkload(AutoLockHelperThreadState& locked)
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -215,20 +215,27 @@ class GlobalHelperThreadState
         const AutoLockHelperThreadState& lock);
     HelperThread* highestPriorityPausedIonCompile(const AutoLockHelperThreadState& lock);
 
     uint32_t harvestFailedWasmJobs(const AutoLockHelperThreadState&) {
         uint32_t n = numWasmFailedJobs;
         numWasmFailedJobs = 0;
         return n;
     }
+    UniqueChars harvestWasmError(const AutoLockHelperThreadState&) {
+        return Move(firstWasmError);
+    }
     void noteWasmFailure(const AutoLockHelperThreadState&) {
         // Be mindful to signal the main thread after calling this function.
         numWasmFailedJobs++;
     }
+    void setWasmError(const AutoLockHelperThreadState&, UniqueChars error) {
+        if (!firstWasmError)
+          firstWasmError = Move(error);
+    }
     bool wasmFailed(const AutoLockHelperThreadState&) {
         return bool(numWasmFailedJobs);
     }
 
     JSScript* finishParseTask(JSContext* cx, ParseTaskKind kind, void* token);
     void cancelParseTask(JSContext* cx, ParseTaskKind kind, void* token);
 
     void mergeParseTaskCompartment(JSContext* cx, ParseTask* parseTask,
@@ -238,16 +245,21 @@ class GlobalHelperThreadState
     void trace(JSTracer* trc);
 
   private:
     /*
      * Number of wasm jobs that encountered failure for the active module.
      * Their parent is logically the main thread, and this number serves for harvesting.
      */
     uint32_t numWasmFailedJobs;
+    /*
+     * 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);
     JSObject* finishModuleParseTask(JSContext* cx, void* token);
     bool compressionInProgress(SourceCompressionTask* task, const AutoLockHelperThreadState& lock);
     SourceCompressionTask* compressionTaskForSource(ScriptSource* ss, const AutoLockHelperThreadState& lock);
 
     bool hasActiveThreads(const AutoLockHelperThreadState&);
@@ -399,17 +411,17 @@ PauseCurrentHelperThread();
 // Enqueues a wasm compilation task.
 bool
 StartOffThreadWasmCompile(wasm::CompileTask* task);
 
 namespace wasm {
 
 // Performs MIR optimization and LIR generation on one or several functions.
 MOZ_MUST_USE bool
-CompileFunction(CompileTask* task);
+CompileFunction(CompileTask* task, UniqueChars* error);
 
 }
 
 /*
  * If helper threads are available, start executing the given PromiseTask on a
  * helper thread, finishing back on the originating JSContext's owner thread. If
  * no helper threads are available, the PromiseTask is synchronously executed
  * and finished.
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -1719,16 +1719,17 @@ class MOZ_STACK_CLASS ModuleValidator
         functions_(cx),
         funcPtrTables_(cx),
         globalMap_(cx),
         sigMap_(cx),
         importMap_(cx),
         arrayViews_(cx),
         atomicsPresent_(false),
         simdPresent_(false),
+        mg_(nullptr),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false)
     {}
 
     ~ModuleValidator() {
         if (errorString_) {
             MOZ_ASSERT(errorOffset_ != UINT32_MAX);
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -7889,23 +7889,23 @@ js::wasm::BaselineCanCompile(const Funct
 
     return true;
 #else
     return false;
 #endif
 }
 
 bool
-js::wasm::BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit)
+js::wasm::BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* error)
 {
     MOZ_ASSERT(unit->mode() == CompileMode::Baseline);
 
     const FuncBytes& func = unit->func();
 
-    Decoder d(func.bytes());
+    Decoder d(func.bytes(), error);
 
     // Build the local types vector.
 
     ValTypeVector locals;
     if (!locals.appendAll(func.sig().args()))
         return false;
     if (!DecodeLocalEntries(d, task->env().kind, &locals))
         return false;
--- a/js/src/wasm/WasmBaselineCompile.h
+++ b/js/src/wasm/WasmBaselineCompile.h
@@ -14,16 +14,18 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef asmjs_wasm_baseline_compile_h
 #define asmjs_wasm_baseline_compile_h
 
+#include "wasm/WasmTypes.h"
+
 namespace js {
 namespace wasm {
 
 class FunctionGenerator;
 class CompileTask;
 class FuncCompileUnit;
 
 // Return true if BaselineCompileFunction can generate code for the
@@ -35,14 +37,14 @@ class FuncCompileUnit;
 // capabilities are missing.  The FunctionGenerator and other data
 // structures contain information about the capabilities that are
 // required to compile the function.
 bool
 BaselineCanCompile(const FunctionGenerator* fg);
 
 // Generate adequate code quickly.
 bool
-BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit);
+BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* error);
 
 } // namespace wasm
 } // namespace js
 
 #endif // asmjs_wasm_baseline_compile_h
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -112,17 +112,17 @@ wasm::Compile(const ShareableBytes& byte
 
     auto env = js::MakeUnique<ModuleEnvironment>();
     if (!env)
         return nullptr;
 
     if (!DecodeModuleEnvironment(d, env.get()))
         return nullptr;
 
-    ModuleGenerator mg;
+    ModuleGenerator mg(error);
     if (!mg.init(Move(env), args))
         return nullptr;
 
     if (!DecodeCodeSection(d, mg))
         return nullptr;
 
     if (!DecodeModuleTail(d, &mg.mutableEnv()))
         return nullptr;
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -39,18 +39,19 @@ using mozilla::MakeEnumeratedRange;
 
 // ****************************************************************************
 // ModuleGenerator
 
 static const unsigned GENERATOR_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
 static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
 static const uint32_t BAD_CODE_RANGE = UINT32_MAX;
 
-ModuleGenerator::ModuleGenerator()
+ModuleGenerator::ModuleGenerator(UniqueChars* error)
   : alwaysBaseline_(false),
+    error_(error),
     numSigs_(0),
     numTables_(0),
     lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
     masmAlloc_(&lifo_),
     masm_(MacroAssembler::WasmToken(), masmAlloc_),
     lastPatchedCallsite_(0),
     startOfUnpatchedCallsites_(0),
     parallel_(false),
@@ -237,18 +238,23 @@ ModuleGenerator::finishOutstandingTask()
     MOZ_ASSERT(parallel_);
 
     CompileTask* task = nullptr;
     {
         AutoLockHelperThreadState lock;
         while (true) {
             MOZ_ASSERT(outstanding_ > 0);
 
-            if (HelperThreadState().wasmFailed(lock))
+            if (HelperThreadState().wasmFailed(lock)) {
+                if (error_) {
+                  MOZ_ASSERT(!*error_, "Should have stopped earlier");
+                  *error_ = Move(HelperThreadState().harvestWasmError(lock));
+                }
                 return false;
+            }
 
             if (!HelperThreadState().wasmFinishedList(lock).empty()) {
                 outstanding_--;
                 task = HelperThreadState().wasmFinishedList(lock).popCopy();
                 break;
             }
 
             HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
@@ -912,17 +918,17 @@ ModuleGenerator::launchBatchCompile()
     size_t numBatchedFuncs = currentTask_->units().length();
     MOZ_ASSERT(numBatchedFuncs);
 
     if (parallel_) {
         if (!StartOffThreadWasmCompile(currentTask_))
             return false;
         outstanding_++;
     } else {
-        if (!CompileFunction(currentTask_))
+        if (!CompileFunction(currentTask_, error_))
             return false;
         if (!finishTask(currentTask_))
             return false;
     }
 
     currentTask_ = nullptr;
     batchedBytecode_ = 0;
 
@@ -1160,28 +1166,28 @@ ModuleGenerator::finish(const ShareableB
                                        Move(env_->exports),
                                        Move(env_->dataSegments),
                                        Move(env_->elemSegments),
                                        *metadata_,
                                        bytecode));
 }
 
 bool
-wasm::CompileFunction(CompileTask* task)
+wasm::CompileFunction(CompileTask* task, UniqueChars* error)
 {
     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
     AutoTraceLog logCompile(logger, TraceLogger_WasmCompilation);
 
     for (FuncCompileUnit& unit : task->units()) {
         switch (unit.mode()) {
           case CompileMode::Ion:
-            if (!IonCompileFunction(task, &unit))
+            if (!IonCompileFunction(task, &unit, error))
                 return false;
             break;
           case CompileMode::Baseline:
-            if (!BaselineCompileFunction(task, &unit))
+            if (!BaselineCompileFunction(task, &unit, error))
                 return false;
             break;
         }
     }
 
     return true;
 }
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -201,16 +201,17 @@ class MOZ_STACK_CLASS ModuleGenerator
 {
     typedef HashSet<uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy> Uint32Set;
     typedef Vector<CompileTask, 0, SystemAllocPolicy> CompileTaskVector;
     typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
     typedef EnumeratedArray<Trap, Trap::Limit, ProfilingOffsets> TrapExitOffsetArray;
 
     // Constant parameters
     bool                            alwaysBaseline_;
+    UniqueChars*                    error_;
 
     // Data that is moved into the result of finish()
     Assumptions                     assumptions_;
     LinkData                        linkData_;
     MutableMetadata                 metadata_;
 
     // Data scoped to the ModuleGenerator's lifetime
     UniqueModuleEnvironment         env_;
@@ -255,17 +256,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     MOZ_MUST_USE bool allocateGlobal(GlobalDesc* global);
 
     MOZ_MUST_USE bool launchBatchCompile();
 
     MOZ_MUST_USE bool initAsmJS(Metadata* asmJSMetadata);
     MOZ_MUST_USE bool initWasm();
 
   public:
-    explicit ModuleGenerator();
+    explicit ModuleGenerator(UniqueChars* error);
     ~ModuleGenerator();
 
     MOZ_MUST_USE bool init(UniqueModuleEnvironment env, const CompileArgs& args,
                            Metadata* maybeAsmJSMetadata = nullptr);
 
     const ModuleEnvironment& env() const { return *env_; }
     ModuleEnvironment& mutableEnv();
 
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -3650,24 +3650,24 @@ EmitExpr(FunctionCompiler& f)
         return EmitCurrentMemory(f);
       case Op::Limit:;
     }
 
     MOZ_CRASH("unexpected wasm opcode");
 }
 
 bool
-wasm::IonCompileFunction(CompileTask* task, FuncCompileUnit* unit)
+wasm::IonCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* error)
 {
     MOZ_ASSERT(unit->mode() == CompileMode::Ion);
 
     const FuncBytes& func = unit->func();
     const ModuleEnvironment& env = task->env();
 
-    Decoder d(func.bytes());
+    Decoder d(func.bytes(), error);
 
     // Build the local types vector.
 
     ValTypeVector locals;
     if (!locals.appendAll(func.sig().args()))
         return false;
     if (!DecodeLocalEntries(d, env.kind, &locals))
         return false;
--- a/js/src/wasm/WasmIonCompile.h
+++ b/js/src/wasm/WasmIonCompile.h
@@ -16,22 +16,24 @@
  * limitations under the License.
  */
 
 #ifndef wasm_ion_compile_h
 #define wasm_ion_compile_h
 
 #include "mozilla/Attributes.h"
 
+#include "wasm/WasmTypes.h"
+
 namespace js {
 namespace wasm {
 
 class CompileTask;
 class FuncCompileUnit;
 
 // Generates very fast code at the expense of compilation time.
 MOZ_MUST_USE bool
-IonCompileFunction(CompileTask* task, FuncCompileUnit* unit);
+IonCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* error);
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_ion_compile_h