Bug 1330891 - Baldr: simplify ModuleGenerator (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Tue, 17 Jan 2017 13:13:51 -0600
changeset 329685 b353d014c22bff5760fdb70948d9fa7dace2047b
parent 329684 89f920cb1ce2c4083cd999af18ea05506965f8d4
child 329686 26ed78caca3d0ffa819c31f066cfc5d03dde7a95
push id85779
push userlwagner@mozilla.com
push dateTue, 17 Jan 2017 19:14:12 +0000
treeherdermozilla-inbound@b353d014c22b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1330891
milestone53.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 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()) {