Bug 1286948 - Adds debug mode for WASM baseline compiler. r=luke draft
authorYury Delendik <ydelendik@mozilla.com>
Sat, 07 Jan 2017 10:34:09 -0600
changeset 457372 851685cbd4397aca314382094f89a76ae16d0792
parent 457371 e9d16569a7b4518e3a90a0512128b838118dea55
child 457373 9ffdce038fd7497e9d5ac2e82ffab6856cdc2288
push id40734
push userydelendik@mozilla.com
push dateSat, 07 Jan 2017 16:43:50 +0000
reviewersluke
bugs1286948
milestone53.0a1
Bug 1286948 - Adds debug mode for WASM baseline compiler. r=luke Changes logic when debug mode is turned off -- when debug tab is opened. MozReview-Commit-ID: G7aNqyxA8Rx
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmCode.cpp
js/src/wasm/WasmCode.h
js/src/wasm/WasmCompile.cpp
js/src/wasm/WasmCompile.h
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmGenerator.h
js/src/wasm/WasmModule.cpp
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -492,16 +492,17 @@ class BaseCompiler
     size_t                      lastReadCallSite_;
     TempAllocator&              alloc_;
     const ValTypeVector&        locals_;         // Types of parameters and locals
     int32_t                     localSize_;      // Size of local area in bytes (stable after beginFunction)
     int32_t                     varLow_;         // Low byte offset of local area for true locals (not parameters)
     int32_t                     varHigh_;        // High byte offset + 1 of local area for true locals
     int32_t                     maxFramePushed_; // Max value of masm.framePushed() observed
     bool                        deadCode_;       // Flag indicating we should decode & discard the opcode
+    bool                        debugEnabled_;
     ValTypeVector               SigI64I64_;
     ValTypeVector               SigDD_;
     ValTypeVector               SigD_;
     ValTypeVector               SigF_;
     ValTypeVector               SigI_;
     ValTypeVector               Sig_;
     Label                       returnLabel_;
     Label                       stackOverflowLabel_;
@@ -563,16 +564,17 @@ class BaseCompiler
 
     // More members: see the stk_ and ctl_ vectors, defined below.
 
   public:
     BaseCompiler(const ModuleEnvironment& env,
                  Decoder& decoder,
                  const FuncBytes& func,
                  const ValTypeVector& locals,
+                 bool debugEnabled,
                  TempAllocator* alloc,
                  MacroAssembler* masm);
 
     MOZ_MUST_USE bool init();
 
     FuncOffsets finish();
 
     MOZ_MUST_USE bool emitFunction();
@@ -7671,29 +7673,31 @@ BaseCompiler::emitFunction()
 
     return true;
 }
 
 BaseCompiler::BaseCompiler(const ModuleEnvironment& env,
                            Decoder& decoder,
                            const FuncBytes& func,
                            const ValTypeVector& locals,
+                           bool debugEnabled,
                            TempAllocator* alloc,
                            MacroAssembler* masm)
     : env_(env),
       iter_(decoder, func.lineOrBytecode()),
       func_(func),
       lastReadCallSite_(0),
       alloc_(*alloc),
       locals_(locals),
       localSize_(0),
       varLow_(0),
       varHigh_(0),
       maxFramePushed_(0),
       deadCode_(false),
+      debugEnabled_(debugEnabled),
       prologueTrapOffset_(trapOffset()),
       stackAddOffset_(0),
       latentOp_(LatentOp::None),
       latentType_(ValType::I32),
       latentIntCmp_(Assembler::Equal),
       latentDoubleCmp_(Assembler::DoubleEqual),
       masm(*masm),
       availGPR_(GeneralRegisterSet::All()),
@@ -7917,17 +7921,17 @@ js::wasm::BaselineCompileFunction(Compil
         return false;
 
     // The MacroAssembler will sometimes access the jitContext.
 
     JitContext jitContext(&task->alloc());
 
     // One-pass baseline compilation.
 
-    BaseCompiler f(task->env(), d, func, locals, &task->alloc(), &task->masm());
+    BaseCompiler f(task->env(), d, func, locals, task->debugEnabled(), &task->alloc(), &task->masm());
     if (!f.init())
         return false;
 
     if (!f.emitFunction())
         return false;
 
     unit->finish(f.finish());
     return true;
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -453,16 +453,17 @@ Metadata::serializedSize() const
            SerializedPodVectorSize(funcNames) +
            SerializedPodVectorSize(customSections) +
            filename.serializedSize();
 }
 
 uint8_t*
 Metadata::serialize(uint8_t* cursor) const
 {
+    MOZ_ASSERT(!debugEnabled);
     cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
     cursor = SerializeVector(cursor, funcImports);
     cursor = SerializeVector(cursor, funcExports);
     cursor = SerializeVector(cursor, sigIds);
     cursor = SerializePodVector(cursor, globals);
     cursor = SerializePodVector(cursor, tables);
     cursor = SerializePodVector(cursor, memoryAccesses);
     cursor = SerializePodVector(cursor, memoryPatches);
@@ -489,16 +490,17 @@ Metadata::deserialize(const uint8_t* cur
     (cursor = DeserializePodVector(cursor, &memoryPatches)) &&
     (cursor = DeserializePodVector(cursor, &boundsChecks)) &&
     (cursor = DeserializePodVector(cursor, &codeRanges)) &&
     (cursor = DeserializePodVector(cursor, &callSites)) &&
     (cursor = DeserializePodVector(cursor, &callThunks)) &&
     (cursor = DeserializePodVector(cursor, &funcNames)) &&
     (cursor = DeserializePodVector(cursor, &customSections)) &&
     (cursor = filename.deserialize(cursor));
+    debugEnabled = false;
     return cursor;
 }
 
 size_t
 Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
            SizeOfVectorExcludingThis(funcExports, mallocSizeOf) +
@@ -567,17 +569,19 @@ Metadata::getFuncName(const Bytes* maybe
 
 Code::Code(UniqueCodeSegment segment,
            const Metadata& metadata,
            const ShareableBytes* maybeBytecode)
   : segment_(Move(segment)),
     metadata_(&metadata),
     maybeBytecode_(maybeBytecode),
     profilingEnabled_(false)
-{}
+{
+    MOZ_ASSERT_IF(metadata_->debugEnabled, maybeBytecode);
+}
 
 struct CallSiteRetAddrOffset
 {
     const CallSiteVector& callSites;
     explicit CallSiteRetAddrOffset(const CallSiteVector& callSites) : callSites(callSites) {}
     uint32_t operator[](size_t index) const {
         return callSites[index].returnAddressOffset();
     }
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -462,16 +462,19 @@ struct Metadata : ShareableBase<Metadata
     BoundsCheckVector     boundsChecks;
     CodeRangeVector       codeRanges;
     CallSiteVector        callSites;
     CallThunkVector       callThunks;
     NameInBytecodeVector  funcNames;
     CustomSectionVector   customSections;
     CacheableChars        filename;
 
+    // Debug-enabled code is not serialized.
+    bool                  debugEnabled;
+
     bool usesMemory() const { return UsesMemory(memoryUsage); }
     bool hasSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
 
     const FuncExport& lookupFuncExport(uint32_t funcIndex) const;
 
     // AsmJSMetadata derives Metadata iff isAsmJS(). Mostly this distinction is
     // encapsulated within AsmJS.cpp, but the additional virtual functions allow
     // asm.js to override wasm behavior in the handful of cases that can't be
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -89,16 +89,23 @@ DecodeCodeSection(Decoder& d, ModuleGene
 
     return mg.finishFuncDefs();
 }
 
 bool
 CompileArgs::initFromContext(ExclusiveContext* cx, ScriptedCaller&& scriptedCaller)
 {
     alwaysBaseline = cx->options().wasmAlwaysBaseline();
+
+    // Debug information such as source view or debug traps will require
+    // additional memory and permanently stay in baseline code, so we try to
+    // only enable it when a developer actually cares: when the debugger tab
+    // is open.
+    debugEnabled = cx->compartment()->debuggerObservesAsmJS();
+
     this->scriptedCaller = Move(scriptedCaller);
     return assumptions.initBuildIdFromContext(cx);
 }
 
 SharedModule
 wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueChars* error)
 {
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
--- a/js/src/wasm/WasmCompile.h
+++ b/js/src/wasm/WasmCompile.h
@@ -35,21 +35,23 @@ struct ScriptedCaller
 
 // Describes all the parameters that control wasm compilation.
 
 struct CompileArgs
 {
     Assumptions assumptions;
     ScriptedCaller scriptedCaller;
     bool alwaysBaseline;
+    bool debugEnabled;
 
     CompileArgs(Assumptions&& assumptions, ScriptedCaller&& scriptedCaller)
       : assumptions(Move(assumptions)),
         scriptedCaller(Move(scriptedCaller)),
-        alwaysBaseline(false)
+        alwaysBaseline(false),
+        debugEnabled(false)
     {}
 
     // If CompileArgs is constructed without arguments, initFromContext() must
     // be called to complete initialization.
     CompileArgs() = default;
     bool initFromContext(ExclusiveContext* cx, ScriptedCaller&& scriptedCaller);
 };
 
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -41,16 +41,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),
     error_(error),
     numSigs_(0),
     numTables_(0),
     lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
     masmAlloc_(&lifo_),
     masm_(MacroAssembler::WasmToken(), masmAlloc_),
     lastPatchedCallsite_(0),
     startOfUnpatchedCallsites_(0),
@@ -197,16 +198,17 @@ 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())
@@ -910,16 +912,18 @@ ModuleGenerator::startFuncDef(uint32_t l
     return true;
 }
 
 bool
 ModuleGenerator::launchBatchCompile()
 {
     MOZ_ASSERT(currentTask_);
 
+    currentTask_->setDebugEnabled(debugEnabled_);
+
     size_t numBatchedFuncs = currentTask_->units().length();
     MOZ_ASSERT(numBatchedFuncs);
 
     if (parallel_) {
         if (!StartOffThreadWasmCompile(currentTask_))
             return false;
         outstanding_++;
     } else {
@@ -940,19 +944,25 @@ bool
 ModuleGenerator::finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg)
 {
     MOZ_ASSERT(activeFuncDef_ == fg);
 
     UniqueFuncBytes func = Move(fg->funcBytes_);
 
     func->setFunc(funcIndex, &funcSig(funcIndex));
 
-    auto mode = alwaysBaseline_ && BaselineCanCompile(fg)
-                ? CompileMode::Baseline
-                : CompileMode::Ion;
+    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;
+    }
 
     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;
@@ -1142,16 +1152,18 @@ ModuleGenerator::finish(const ShareableB
     metadata_->callSites.podResizeToFit();
     metadata_->callThunks.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
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -139,23 +139,25 @@ typedef Vector<FuncCompileUnit, 8, Syste
 
 class CompileTask
 {
     const ModuleEnvironment&   env_;
     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)
       : env_(env),
         lifo_(defaultChunkSize)
     {
         init();
@@ -170,16 +172,22 @@ class CompileTask
         return env_;
     }
     jit::MacroAssembler& masm() {
         return *masm_;
     }
     FuncCompileUnitVector& units() {
         return units_;
     }
+    bool debugEnabled() const {
+        return debugEnabled_;
+    }
+    void setDebugEnabled(bool enabled) {
+        debugEnabled_ = enabled;
+    }
     bool reset(UniqueFuncBytesVector* freeFuncBytes) {
         for (FuncCompileUnit& unit : units_) {
             if (!freeFuncBytes->emplaceBack(Move(unit.recycle())))
                 return false;
         }
 
         units_.clear();
         masm_.reset();
@@ -201,16 +209,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_;
+    bool                            debugEnabled_;
     UniqueChars*                    error_;
 
     // Data that is moved into the result of finish()
     Assumptions                     assumptions_;
     LinkData                        linkData_;
     MutableMetadata                 metadata_;
 
     // Data scoped to the ModuleGenerator's lifetime
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -138,17 +138,23 @@ LinkData::sizeOfExcludingThis(MallocSize
 }
 
 /* virtual */ void
 Module::serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const
 {
     if (maybeBytecodeSize)
         *maybeBytecodeSize = bytecode_->bytes.length();
 
-    if (maybeCompiledSize) {
+    // The compiled debug code must not be saved, set compiled size to 0,
+    // so Module::assumptionsMatch will return false during assumptions
+    // deserialization.
+    if (maybeCompiledSize && metadata_->debugEnabled)
+        *maybeCompiledSize = 0;
+
+    if (maybeCompiledSize && !metadata_->debugEnabled) {
         *maybeCompiledSize = assumptions_.serializedSize() +
                              SerializedPodVectorSize(code_) +
                              linkData_.serializedSize() +
                              SerializedVectorSize(imports_) +
                              SerializedVectorSize(exports_) +
                              SerializedPodVectorSize(dataSegments_) +
                              SerializedVectorSize(elemSegments_) +
                              metadata_->serializedSize();
@@ -169,17 +175,19 @@ Module::serialize(uint8_t* maybeBytecode
         // compatibility is ensured by backwards compatibility of the wasm
         // binary format).
 
         const Bytes& bytes = bytecode_->bytes;
         uint8_t* bytecodeEnd = WriteBytes(maybeBytecodeBegin, bytes.begin(), bytes.length());
         MOZ_RELEASE_ASSERT(bytecodeEnd == maybeBytecodeBegin + maybeBytecodeSize);
     }
 
-    if (maybeCompiledBegin) {
+    MOZ_ASSERT_IF(maybeCompiledBegin && metadata_->debugEnabled, maybeCompiledSize == 0);
+
+    if (maybeCompiledBegin && !metadata_->debugEnabled) {
         // Assumption must be serialized at the beginning of the compiled bytes so
         // that compiledAssumptionsMatch can detect a build-id mismatch before any
         // other decoding occurs.
 
         uint8_t* cursor = maybeCompiledBegin;
         cursor = assumptions_.serialize(cursor);
         cursor = SerializePodVector(cursor, code_);
         cursor = linkData_.serialize(cursor);