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
--- 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);