Bug 1330891 - Baldr: simplify ModuleGenerator (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Tue, 17 Jan 2017 13:13:51 -0600
changeset 357732 b353d014c22bff5760fdb70948d9fa7dace2047b
parent 357731 89f920cb1ce2c4083cd999af18ea05506965f8d4
child 357733 26ed78caca3d0ffa819c31f066cfc5d03dde7a95
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)
reviewersbbouvier
bugs1330891
milestone53.0a1
Bug 1330891 - Baldr: simplify ModuleGenerator (r=bbouvier) MozReview-Commit-ID: LrBIuXsUhtX
js/src/jit-test/tests/asm.js/directives.txt
js/src/jit/JitOptions.cpp
js/src/jit/JitOptions.h
js/src/tests/lib/jittests.py
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmBaselineCompile.h
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmGenerator.h
js/src/wasm/WasmIonCompile.cpp
--- a/js/src/jit-test/tests/asm.js/directives.txt
+++ b/js/src/jit-test/tests/asm.js/directives.txt
@@ -1,1 +1,1 @@
-|jit-test| test-also-noasmjs; test-also-wasm-baseline
+|jit-test| test-also-noasmjs
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -233,24 +233,20 @@ DefaultJitOptions::DefaultJitOptions()
     // Test whether wasm bounds check should always be generated.
     SET_DEFAULT(wasmAlwaysCheckBounds, false);
 
     // Toggles the optimization whereby offsets are folded into loads and not
     // included in the bounds check.
     SET_DEFAULT(wasmFoldOffsets, true);
 
     // Until which wasm bytecode size should we accumulate functions, in order
-    // to compile efficiently on helper threads (see also bug 1320374).
-    SET_DEFAULT(wasmBatchThreshold, 10000);
-
-    // In order to have different batching thresholds for Ion and the wasm
-    // baseline, and since a same batch can contain both Ion and baseline
-    // compiled functions, we make Ion functions weight more by using a scaling
-    // factor.
-    SET_DEFAULT(wasmBatchIonScaleFactor, 9);
+    // to compile efficiently on helper threads. Baseline code compiles much
+    // faster than Ion code so use scaled thresholds (see also bug 1320374).
+    SET_DEFAULT(wasmBatchBaselineThreshold, 10000);
+    SET_DEFAULT(wasmBatchIonThreshold, 1100);
 
     // Determines whether we suppress using signal handlers
     // for interrupting jit-ed code. This is used only for testing.
     SET_DEFAULT(ionInterruptWithoutSignals, false);
 }
 
 bool
 DefaultJitOptions::isSmallFunction(JSScript* script) const
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -82,18 +82,18 @@ struct DefaultJitOptions
     uint32_t osrPcMismatchesBeforeRecompile;
     uint32_t smallFunctionMaxBytecodeLength_;
     uint32_t jumpThreshold;
     uint32_t branchPruningHitCountFactor;
     uint32_t branchPruningInstFactor;
     uint32_t branchPruningBlockSpanFactor;
     uint32_t branchPruningEffectfulInstFactor;
     uint32_t branchPruningThreshold;
-    uint32_t wasmBatchThreshold;
-    uint32_t wasmBatchIonScaleFactor;
+    uint32_t wasmBatchIonThreshold;
+    uint32_t wasmBatchBaselineThreshold;
     mozilla::Maybe<uint32_t> forcedDefaultIonWarmUpThreshold;
     mozilla::Maybe<uint32_t> forcedDefaultIonSmallFunctionWarmUpThreshold;
     mozilla::Maybe<IonRegisterAllocator> forcedRegisterAllocator;
 
     // The options below affect the rest of the VM, and not just the JIT.
     bool disableUnboxedObjects;
 
     DefaultJitOptions();
--- a/js/src/tests/lib/jittests.py
+++ b/js/src/tests/lib/jittests.py
@@ -133,17 +133,17 @@ class JitTest:
         t.jitflags = self.jitflags[:]
         t.slow = self.slow
         t.allow_oom = self.allow_oom
         t.allow_unhandlable_oom = self.allow_unhandlable_oom
         t.allow_overrecursed = self.allow_overrecursed
         t.valgrind = self.valgrind
         t.tz_pacific = self.tz_pacific
         t.test_also_noasmjs = self.test_also_noasmjs
-        t.test_also_wasm_baseline = self.test_also_noasmjs
+        t.test_also_wasm_baseline = self.test_also_wasm_baseline
         t.test_also = self.test_also
         t.test_join = self.test_join
         t.expect_error = self.expect_error
         t.expect_status = self.expect_status
         t.expect_crash = self.expect_crash
         t.test_reflect_stringify = self.test_reflect_stringify
         t.enable = True
         t.is_module = self.is_module
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -7897,17 +7897,17 @@ volatileReturnGPR()
 }
 
 LiveRegisterSet BaseCompiler::VolatileReturnGPR = volatileReturnGPR();
 
 } // wasm
 } // js
 
 bool
-js::wasm::BaselineCanCompile(const FunctionGenerator* fg)
+js::wasm::BaselineCanCompile()
 {
     // On all platforms we require signals for AsmJS/Wasm.
     // If we made it this far we must have signals.
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
 
 #if defined(JS_CODEGEN_ARM)
     // Simplifying assumption: require SDIV and UDIV.
     //
@@ -7915,32 +7915,26 @@ js::wasm::BaselineCanCompile(const Funct
     // X% of devices in the market implement SDIV and UDIV.  However,
     // they are definitely implemented on the Cortex-A7 and Cortex-A15
     // and on all ARMv8 systems.
     if (!HasIDIV())
         return false;
 #endif
 
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
-    // AsmJS code may use SIMD or atomics, which Baseline doesn't currently
-    // handle. Since we haven't yet validated the function, we don't know
-    // whether it actually uses those features. Assume the worst.
-    if (fg->isAsmJS())
-        return false;
-
     return true;
 #else
     return false;
 #endif
 }
 
 bool
 js::wasm::BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars *error)
 {
-    MOZ_ASSERT(unit->mode() == CompileMode::Baseline);
+    MOZ_ASSERT(task->mode() == CompileMode::Baseline);
 
     const FuncBytes& func = unit->func();
     uint32_t bodySize = func.bytes().length();
 
     Decoder d(func.bytes(), error);
 
     if (!ValidateFunctionBody(task->env(), func.index(), bodySize, d))
         return false;
--- a/js/src/wasm/WasmBaselineCompile.h
+++ b/js/src/wasm/WasmBaselineCompile.h
@@ -19,31 +19,23 @@
 #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
-// function held in the FunctionGenerator.  If false is returned a
-// different compilation strategy must be chosen.
-//
-// This allows the baseline compiler to have different capabilities on
-// different platforms and defer to the full Ion compiler if
-// capabilities are missing.  The FunctionGenerator and other data
-// structures contain information about the capabilities that are
-// required to compile the function.
+// Return whether BaselineCompileFunction can generate code on the current device.
+// Note: asm.js is also currently not supported due to Atomics and SIMD.
 bool
-BaselineCanCompile(const FunctionGenerator* fg);
+BaselineCanCompile();
 
 // Generate adequate code quickly.
 bool
 BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* error);
 
 } // namespace wasm
 } // namespace js
 
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -40,18 +40,17 @@ 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(UniqueChars* error)
-  : alwaysBaseline_(false),
-    debugEnabled_(false),
+  : compileMode_(CompileMode(-1)),
     error_(error),
     numSigs_(0),
     numTables_(0),
     lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
     masmAlloc_(&lifo_),
     masm_(MacroAssembler::WasmToken(), masmAlloc_),
     lastPatchedCallsite_(0),
     startOfUnpatchedCallsites_(0),
@@ -107,38 +106,49 @@ ModuleGenerator::~ModuleGenerator()
 bool
 ModuleGenerator::initAsmJS(Metadata* asmJSMetadata)
 {
     MOZ_ASSERT(env_->isAsmJS());
 
     metadata_ = asmJSMetadata;
     MOZ_ASSERT(isAsmJS());
 
+    // Enabling debugging requires baseline and baseline is only enabled for
+    // wasm (since the baseline does not currently support Atomics or SIMD).
+
+    metadata_->debugEnabled = false;
+    compileMode_ = CompileMode::Ion;
+
     // For asm.js, the Vectors in ModuleEnvironment are max-sized reservations
     // and will be initialized in a linear order via init* functions as the
     // module is generated.
 
     MOZ_ASSERT(env_->sigs.length() == AsmJSMaxTypes);
     MOZ_ASSERT(env_->tables.length() == AsmJSMaxTables);
     MOZ_ASSERT(env_->asmJSSigToTableIndex.length() == AsmJSMaxTypes);
 
     return true;
 }
 
 bool
-ModuleGenerator::initWasm()
+ModuleGenerator::initWasm(const CompileArgs& args)
 {
     MOZ_ASSERT(!env_->isAsmJS());
 
     metadata_ = js_new<Metadata>();
     if (!metadata_)
         return false;
 
     MOZ_ASSERT(!isAsmJS());
 
+    metadata_->debugEnabled = args.debugEnabled && BaselineCanCompile();
+    compileMode_ = args.alwaysBaseline || metadata_->debugEnabled
+                   ? CompileMode::Baseline
+                   : CompileMode::Ion;
+
     // For wasm, the Vectors are correctly-sized and already initialized.
 
     numSigs_ = env_->sigs.length();
     numTables_ = env_->tables.length();
 
     for (size_t i = 0; i < env_->funcImportGlobalDataOffsets.length(); i++) {
         env_->funcImportGlobalDataOffsets[i] = linkData_.globalDataLength;
         linkData_.globalDataLength += sizeof(FuncImportTls);
@@ -197,29 +207,26 @@ ModuleGenerator::initWasm()
 bool
 ModuleGenerator::init(UniqueModuleEnvironment env, const CompileArgs& args,
                       Metadata* maybeAsmJSMetadata)
 {
     env_ = Move(env);
 
     linkData_.globalDataLength = AlignBytes(InitialGlobalDataBytes, sizeof(void*));
 
-    alwaysBaseline_ = args.alwaysBaseline;
-    debugEnabled_ = args.debugEnabled;
-
     if (!funcToCodeRange_.appendN(BAD_CODE_RANGE, env_->funcSigs.length()))
         return false;
 
     if (!assumptions_.clone(args.assumptions))
         return false;
 
     if (!exportedFuncs_.init())
         return false;
 
-    if (env_->isAsmJS() ? !initAsmJS(maybeAsmJSMetadata) : !initWasm())
+    if (env_->isAsmJS() ? !initAsmJS(maybeAsmJSMetadata) : !initWasm(args))
         return false;
 
     if (args.scriptedCaller.filename) {
         metadata_->filename = DuplicateString(args.scriptedCaller.filename.get());
         if (!metadata_->filename)
             return false;
     }
 
@@ -898,17 +905,17 @@ ModuleGenerator::startFuncDefs()
         numTasks = 2 * threads.maxWasmCompilationThreads();
     } else {
         numTasks = 1;
     }
 
     if (!tasks_.initCapacity(numTasks))
         return false;
     for (size_t i = 0; i < numTasks; i++)
-        tasks_.infallibleEmplaceBack(*env_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
+        tasks_.infallibleEmplaceBack(*env_, compileMode_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
 
     if (!freeTasks_.reserve(numTasks))
         return false;
     for (size_t i = 0; i < numTasks; i++)
         freeTasks_.infallibleAppend(&tasks_[i]);
 
     startedFuncDefs_ = true;
     MOZ_ASSERT(!finishedFuncDefs_);
@@ -943,17 +950,17 @@ ModuleGenerator::startFuncDef(uint32_t l
     return true;
 }
 
 bool
 ModuleGenerator::launchBatchCompile()
 {
     MOZ_ASSERT(currentTask_);
 
-    currentTask_->setDebugEnabled(debugEnabled_);
+    currentTask_->setDebugEnabled(metadata_->debugEnabled);
 
     size_t numBatchedFuncs = currentTask_->units().length();
     MOZ_ASSERT(numBatchedFuncs);
 
     if (parallel_) {
         if (!StartOffThreadWasmCompile(currentTask_))
             return false;
         outstanding_++;
@@ -972,40 +979,30 @@ ModuleGenerator::launchBatchCompile()
 }
 
 bool
 ModuleGenerator::finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg)
 {
     MOZ_ASSERT(activeFuncDef_ == fg);
 
     UniqueFuncBytes func = Move(fg->funcBytes_);
-
     func->setFunc(funcIndex, &funcSig(funcIndex));
+    uint32_t funcBytecodeLength = func->bytes().length();
+    if (!currentTask_->units().emplaceBack(Move(func)))
+        return false;
 
-    CompileMode mode;
-    if ((alwaysBaseline_ || debugEnabled_) && BaselineCanCompile(fg)) {
-      mode = CompileMode::Baseline;
-    } else {
-      mode = CompileMode::Ion;
-      // Ion does not support debugging -- reset debugEnabled_ flags to avoid
-      // turning debugging for wasm::Code.
-      debugEnabled_ = false;
+    uint32_t threshold;
+    switch (compileMode_) {
+      case CompileMode::Baseline: threshold = JitOptions.wasmBatchBaselineThreshold; break;
+      case CompileMode::Ion:      threshold = JitOptions.wasmBatchIonThreshold;      break;
     }
 
-    CheckedInt<uint32_t> newBatched = func->bytes().length();
-    if (mode == CompileMode::Ion)
-        newBatched *= JitOptions.wasmBatchIonScaleFactor;
-    newBatched += batchedBytecode_;
-
-    if (!currentTask_->units().emplaceBack(Move(func), mode))
-        return false;
-
-    if (newBatched.isValid() && newBatched.value() < JitOptions.wasmBatchThreshold)
-        batchedBytecode_ = newBatched.value();
-    else if (!launchBatchCompile())
+    batchedBytecode_ += funcBytecodeLength;
+    MOZ_ASSERT(batchedBytecode_ <= MaxModuleBytes);
+    if (batchedBytecode_ > threshold && !launchBatchCompile())
         return false;
 
     fg->m_ = nullptr;
     activeFuncDef_ = nullptr;
     return true;
 }
 
 bool
@@ -1184,18 +1181,16 @@ ModuleGenerator::finish(const ShareableB
     metadata_->callThunks.podResizeToFit();
     metadata_->debugTrapFarJumpOffsets.podResizeToFit();
 
     // For asm.js, the tables vector is over-allocated (to avoid resize during
     // parallel copilation). Shrink it back down to fit.
     if (isAsmJS() && !metadata_->tables.resize(numTables_))
         return nullptr;
 
-    metadata_->debugEnabled = debugEnabled_;
-
     // Assert CodeRanges are sorted.
 #ifdef DEBUG
     uint32_t lastEnd = 0;
     for (const CodeRange& codeRange : metadata_->codeRanges) {
         MOZ_ASSERT(codeRange.begin() >= lastEnd);
         lastEnd = codeRange.end();
     }
 #endif
@@ -1224,23 +1219,25 @@ ModuleGenerator::finish(const ShareableB
 }
 
 bool
 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:
+    switch (task->mode()) {
+      case CompileMode::Ion:
+        for (FuncCompileUnit& unit : task->units()) {
             if (!IonCompileFunction(task, &unit, error))
                 return false;
-            break;
-          case CompileMode::Baseline:
+        }
+        break;
+      case CompileMode::Baseline:
+        for (FuncCompileUnit& unit : task->units()) {
             if (!BaselineCompileFunction(task, &unit, error))
                 return false;
-            break;
         }
+        break;
     }
 
     return true;
 }
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -96,29 +96,26 @@ enum class CompileMode
 };
 
 // FuncCompileUnit contains all the data necessary to produce and store the
 // results of a single function's compilation.
 
 class FuncCompileUnit
 {
     UniqueFuncBytes func_;
-    CompileMode mode_;
     FuncOffsets offsets_;
     DebugOnly<bool> finished_;
 
   public:
-    FuncCompileUnit(UniqueFuncBytes func, CompileMode mode)
+    explicit FuncCompileUnit(UniqueFuncBytes func)
       : func_(Move(func)),
-        mode_(mode),
         finished_(false)
     {}
 
     const FuncBytes& func() const { return *func_; }
-    CompileMode mode() const { return mode_; }
     FuncOffsets offsets() const { MOZ_ASSERT(finished_); return offsets_; }
 
     void finish(FuncOffsets offsets) {
         MOZ_ASSERT(!finished_);
         offsets_ = offsets;
         finished_ = true;
     }
 
@@ -135,34 +132,36 @@ typedef Vector<FuncCompileUnit, 8, Syste
 // filled with a certain number of function's bodies that are sent off to a
 // compilation helper thread, which fills in the resulting code offsets, and
 // finally sent back to the validation thread. To save time allocating and
 // freeing memory, CompileTasks are reset() and reused.
 
 class CompileTask
 {
     const ModuleEnvironment&   env_;
+    CompileMode                mode_;
     LifoAlloc                  lifo_;
     Maybe<jit::TempAllocator>  alloc_;
     Maybe<jit::MacroAssembler> masm_;
     FuncCompileUnitVector      units_;
     bool                       debugEnabled_;
 
     CompileTask(const CompileTask&) = delete;
     CompileTask& operator=(const CompileTask&) = delete;
 
     void init() {
         alloc_.emplace(&lifo_);
         masm_.emplace(jit::MacroAssembler::WasmToken(), *alloc_);
         debugEnabled_ = false;
     }
 
   public:
-    CompileTask(const ModuleEnvironment& env, size_t defaultChunkSize)
+    CompileTask(const ModuleEnvironment& env, CompileMode mode, size_t defaultChunkSize)
       : env_(env),
+        mode_(mode),
         lifo_(defaultChunkSize)
     {
         init();
     }
     LifoAlloc& lifo() {
         return lifo_;
     }
     jit::TempAllocator& alloc() {
@@ -172,16 +171,19 @@ class CompileTask
         return env_;
     }
     jit::MacroAssembler& masm() {
         return *masm_;
     }
     FuncCompileUnitVector& units() {
         return units_;
     }
+    CompileMode mode() const {
+        return mode_;
+    }
     bool debugEnabled() const {
         return debugEnabled_;
     }
     void setDebugEnabled(bool enabled) {
         debugEnabled_ = enabled;
     }
     bool reset(UniqueFuncBytesVector* freeFuncBytes) {
         for (FuncCompileUnit& unit : units_) {
@@ -208,18 +210,17 @@ class CompileTask
 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_;
-    bool                            debugEnabled_;
+    CompileMode                     compileMode_;
     UniqueChars*                    error_;
 
     // Data that is moved into the result of finish()
     Assumptions                     assumptions_;
     LinkData                        linkData_;
     MutableMetadata                 metadata_;
 
     // Data scoped to the ModuleGenerator's lifetime
@@ -263,17 +264,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     MOZ_MUST_USE bool finishLinkData(Bytes& code);
     MOZ_MUST_USE bool addFuncImport(const Sig& sig, uint32_t globalDataOffset);
     MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
     MOZ_MUST_USE bool allocateGlobal(GlobalDesc* global);
 
     MOZ_MUST_USE bool launchBatchCompile();
 
     MOZ_MUST_USE bool initAsmJS(Metadata* asmJSMetadata);
-    MOZ_MUST_USE bool initWasm();
+    MOZ_MUST_USE bool initWasm(const CompileArgs& args);
 
   public:
     explicit ModuleGenerator(UniqueChars* error);
     ~ModuleGenerator();
 
     MOZ_MUST_USE bool init(UniqueModuleEnvironment env, const CompileArgs& args,
                            Metadata* maybeAsmJSMetadata = nullptr);
 
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -3652,17 +3652,17 @@ EmitExpr(FunctionCompiler& f)
     }
 
     MOZ_CRASH("unexpected wasm opcode");
 }
 
 bool
 wasm::IonCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* error)
 {
-    MOZ_ASSERT(unit->mode() == CompileMode::Ion);
+    MOZ_ASSERT(task->mode() == CompileMode::Ion);
 
     const FuncBytes& func = unit->func();
     const ModuleEnvironment& env = task->env();
     uint32_t bodySize = func.bytes().length();
 
     Decoder d(func.bytes(), error);
 
     if (!env.isAsmJS()) {