if (parallel_) { if (outstanding_) {
AutoLockHelperThreadState lock;
// Remove any pending compilation tasks from the worklist.
size_t removed =
RemovePendingWasmCompileTasks(taskState_, compileState_, lock);
MOZ_ASSERT(outstanding_ >= removed);
outstanding_ -= removed;
// Wait until all active compilation tasks have finished. while (true) {
MOZ_ASSERT(outstanding_ >= taskState_.finished().length());
outstanding_ -= taskState_.finished().length();
taskState_.finished().clear();
staticbool InRange(uint32_t caller, uint32_t callee) { // We assume JumpImmediateRange is defined conservatively enough that the // slight difference between 'caller' (which is really the return address // offset) and the actual base of the relative displacement computation // isn't significant.
uint32_t range = std::min(JitOptions.jumpThreshold, JumpImmediateRange); if (caller < callee) { return callee - caller < range;
} return caller - callee < range;
}
using OffsetMap =
HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy>;
// Create far jumps for calls that have relative offsets that may otherwise // go out of range. This method is called both between function bodies (at a // frequency determined by the ISA's jump range) and once at the very end of // a module's codegen after all possible calls/traps have been emitted.
OffsetMap existingCallFarJumps; for (; lastPatchedCallSite_ < codeBlock_->callSites.length();
lastPatchedCallSite_++) {
CallSiteKind kind = codeBlock_->callSites.kind(lastPatchedCallSite_);
uint32_t callerOffset =
codeBlock_->callSites.returnAddressOffset(lastPatchedCallSite_); const CallSiteTarget& target = callSiteTargets_[lastPatchedCallSite_]; switch (kind) { case CallSiteKind::Import: case CallSiteKind::Indirect: case CallSiteKind::IndirectFast: case CallSiteKind::Symbolic: case CallSiteKind::Breakpoint: case CallSiteKind::EnterFrame: case CallSiteKind::LeaveFrame: case CallSiteKind::CollapseFrame: case CallSiteKind::FuncRef: case CallSiteKind::FuncRefFast: case CallSiteKind::ReturnStub: case CallSiteKind::StackSwitch: case CallSiteKind::RequestTierUp: break; case CallSiteKind::ReturnFunc: case CallSiteKind::Func: { auto patch = [this, kind](uint32_t callerOffset,
uint32_t calleeOffset) { if (kind == CallSiteKind::ReturnFunc) {
masm_->patchFarJump(CodeOffset(callerOffset), calleeOffset);
} else {
MOZ_ASSERT(kind == CallSiteKind::Func);
masm_->patchCall(callerOffset, calleeOffset);
}
}; if (funcIsCompiledInBlock(target.funcIndex())) {
uint32_t calleeOffset =
funcCodeRangeInBlock(target.funcIndex()).funcUncheckedCallEntry(); if (InRange(callerOffset, calleeOffset)) {
patch(callerOffset, calleeOffset); break;
}
}
OffsetMap::AddPtr p =
existingCallFarJumps.lookupForAdd(target.funcIndex()); if (!p) {
Offsets offsets;
offsets.begin = masm_->currentOffset(); if (!callFarJumps_.emplaceBack(target.funcIndex(),
masm_->farJumpWithPatch().offset())) { returnfalse;
}
offsets.end = masm_->currentOffset(); if (masm_->oom()) { returnfalse;
} if (!codeBlock_->codeRanges.emplaceBack(CodeRange::FarJumpIsland,
offsets)) { returnfalse;
} if (!existingCallFarJumps.add(p, target.funcIndex(), offsets.begin)) { returnfalse;
}
}
patch(callerOffset, p->value()); break;
}
}
}
masm_->flushBuffer(); return !masm_->oom();
}
void ModuleGenerator::noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRange) { switch (codeRange.kind()) { case CodeRange::Function:
MOZ_ASSERT(codeBlock_->funcToCodeRange[codeRange.funcIndex()] ==
BAD_CODE_RANGE);
codeBlock_->funcToCodeRange.insertInfallible(codeRange.funcIndex(),
codeRangeIndex); break; case CodeRange::InterpEntry:
codeBlock_->lookupFuncExport(codeRange.funcIndex())
.initEagerInterpEntryOffset(codeRange.begin()); break; case CodeRange::JitEntry: // Nothing to do: jit entries are linked in the jump tables. break; case CodeRange::ImportJitExit:
funcImports_[codeRange.funcIndex()].initJitExitOffset(codeRange.begin()); break; case CodeRange::ImportInterpExit:
funcImports_[codeRange.funcIndex()].initInterpExitOffset(
codeRange.begin()); break; case CodeRange::DebugStub:
MOZ_ASSERT(!debugStubCodeOffset_);
debugStubCodeOffset_ = codeRange.begin(); break; case CodeRange::RequestTierUpStub:
MOZ_ASSERT(!requestTierUpStubCodeOffset_);
requestTierUpStubCodeOffset_ = codeRange.begin(); break; case CodeRange::UpdateCallRefMetricsStub:
MOZ_ASSERT(!updateCallRefMetricsStubCodeOffset_);
updateCallRefMetricsStubCodeOffset_ = codeRange.begin(); break; #ifdef ENABLE_WASM_JSPI case CodeRange::ContBaseFrame:
MOZ_ASSERT(!contBaseFrameOffset_);
contBaseFrameOffset_ = codeRange.begin(); break; #endif case CodeRange::TrapExit:
MOZ_ASSERT(!linkData_->trapOffset);
linkData_->trapOffset = codeRange.begin(); break; case CodeRange::Throw: // Jumped to by other stubs, so nothing to do. break; case CodeRange::FarJumpIsland: case CodeRange::BuiltinThunk:
MOZ_CRASH("Unexpected CodeRange kind");
}
}
// Append every element from `srcVec` where `filterOp(srcElem) == true`. // Applies `mutateOp(dstElem)` to every element that is appended. template <class Vec, class FilterOp, class MutateOp> staticbool AppendForEach(Vec* dstVec, const Vec& srcVec, FilterOp filterOp,
MutateOp mutateOp) { // Eagerly grow the vector to the whole src vector. Any filtered elements // will be trimmed later. if (!dstVec->growByUninitialized(srcVec.length())) { returnfalse;
}
// We appended srcVec.length() elements at the beginning, so we append // elements starting at the first uninitialized element.
T* dst = dstEnd - srcVec.length();
for (const T* src = srcVec.begin(); src != srcVec.end(); src++) { if (!filterOp(src)) { continue;
} new (dst) T(*src);
mutateOp(dst - dstBegin, dst);
dst++;
}
// Trim off the filtered out elements that were eagerly added at the // beginning
size_t newSize = dst - dstBegin; if (newSize != dstVec->length()) {
dstVec->shrinkTo(newSize);
}
// The same as the above `AppendForEach`, without performing any filtering. template <class Vec, class MutateOp> staticbool AppendForEach(Vec* dstVec, const Vec& srcVec, MutateOp mutateOp) { using T = typename Vec::ElementType; return AppendForEach(dstVec, srcVec, &FilterNothing<T>, mutateOp);
}
// Combine observed features from the compiled code into the metadata
featureUsage_ |= code.featureUsage;
// Fold in compilation stats from all compiled functions in this block
tierStats_.mergeCompileStats(code.compileStats);
if (compilingTier1() && mode() == CompileMode::LazyTiering) { // All the CallRefMetrics from this batch of functions will start indexing // at our current length of metrics.
uint32_t startOfCallRefMetrics = numCallRefMetrics_;
for (const FuncCompileOutput& func : code.funcs) { // We only compile defined functions, not imported functions
MOZ_ASSERT(func.index >= codeMeta_->numFuncImports);
uint32_t funcDefIndex = func.index - codeMeta_->numFuncImports;
// This function should only be compiled once
MOZ_ASSERT(funcDefFeatureUsages_[funcDefIndex] == FeatureUsage::None);
// Track the feature usage for this function
funcDefFeatureUsages_[funcDefIndex] = func.featureUsage;
// Record the range of CallRefMetrics this function owns. The metrics // will be processed below when we patch the offsets into code.
MOZ_ASSERT(func.callRefMetricsRange.begin +
func.callRefMetricsRange.length <=
code.callRefMetricsPatches.length());
funcDefCallRefMetrics_[funcDefIndex] = func.callRefMetricsRange;
funcDefCallRefMetrics_[funcDefIndex].offsetBy(startOfCallRefMetrics);
}
} else {
MOZ_ASSERT(funcDefFeatureUsages_.empty());
MOZ_ASSERT(funcDefCallRefMetrics_.empty());
MOZ_ASSERT(code.callRefMetricsPatches.empty()); #ifdef DEBUG for (const FuncCompileOutput& func : code.funcs) {
MOZ_ASSERT(func.callRefMetricsRange.length == 0);
} #endif
}
if (compilingTier1()) { // All the AllocSites from this batch of functions will start indexing // at our current length.
uint32_t startOfAllocSites = numAllocSites_;
for (const FuncCompileOutput& func : code.funcs) { // We only compile defined functions, not imported functions
MOZ_ASSERT(func.index >= codeMeta_->numFuncImports);
uint32_t funcDefIndex = func.index - codeMeta_->numFuncImports;
// Grab the perf spewers that were generated for these functions. if (!funcIonSpewers_.appendAll(std::move(code.funcIonSpewers)) ||
!funcBaselineSpewers_.appendAll(std::move(code.funcBaselineSpewers))) { returnfalse;
}
// Before merging in new code, if calls in a prior code range might go out of // range, insert far jumps to extend the range.
if (!InRange(startOfUnpatchedCallsites_,
masm_->size() + code.bytes.length())) {
startOfUnpatchedCallsites_ = masm_->size(); if (!linkCallSites()) { returnfalse;
}
}
// All code offsets in 'code' must be incremented by their position in the // overall module when the code was appended.
// Compute the offset of the metrics, and patch it. This may overflow, // in which case we report an OOM. We might need to do something smarter // here. if (callRefMetricOffset > (INT32_MAX / sizeof(CallRefMetrics))) { returnfalse;
}
// Use numAllocSites_ to patch bytecode specific AllocSite to its index in // the map. for (const AllocSitePatch& patch : code.allocSitesPatches) {
uint32_t index = numAllocSites_;
numAllocSites_ += 1; if (!patch.hasPatchOffset()) { continue;
}
// Compute the offset of the AllocSite, and patch it. This may overflow, // in which case we report an OOM. if (index > INT32_MAX / sizeof(gc::AllocSite)) { returnfalse;
}
uintptr_t allocSiteOffset = uintptr_t(index) * sizeof(gc::AllocSite);
masm_->patchMove32(offset, Imm32(allocSiteOffset));
}
// Transfer all stackmaps with the offset in module. if (!codeBlock_->stackMaps.appendAll(code.stackMaps, offsetInModule)) { returnfalse;
}
auto unwindInfoOp = [=](uint32_t, CodeRangeUnwindInfo* i) {
i->offsetBy(offsetInModule);
}; if (!AppendForEach(&codeBlock_->codeRangeUnwindInfos,
code.codeRangeUnwindInfos, unwindInfoOp)) { returnfalse;
}
auto tryNoteFilter = [](const TryNote* tn) { // Filter out all try notes that were never given a try body. This may // happen due to dead code elimination. return tn->hasTryBody();
}; auto tryNoteOp = [=](uint32_t, TryNote* tn) { tn->offsetBy(offsetInModule); }; return AppendForEach(&codeBlock_->tryNotes, code.tryNotes, tryNoteFilter,
tryNoteOp);
}
{
AutoUnlockHelperThreadState unlock(lock);
ok = ExecuteCompileTask(this, &error);
}
// Don't release the lock between updating our state and returning from this // method.
if (!ok || !state.finished().append(this)) {
state.numFailed()++; if (!state.errorMessage()) {
state.errorMessage() = std::move(error);
}
}
state.condVar().notify_one(); /* failed or finished */
}
ThreadType CompileTask::threadType() { switch (compileState) { case CompileState::Once: case CompileState::EagerTier1: case CompileState::LazyTier1: return ThreadType::THREAD_TYPE_WASM_COMPILE_TIER1; case CompileState::EagerTier2: case CompileState::LazyTier2: return ThreadType::THREAD_TYPE_WASM_COMPILE_TIER2; default:
MOZ_CRASH();
}
}
bool ModuleGenerator::initTasks() { // Determine whether parallel or sequential compilation is to be used and // initialize the CompileTasks that will be used in either mode.
MOZ_ASSERT(GetHelperThreadCount() > 1);
MOZ_ASSERT(!parallel_);
uint32_t numTasks = 1; if ( // "obvious" prerequisites for doing off-thread compilation
CanUseExtraThreads() && GetHelperThreadCPUCount() > 1 && // For lazy tier 2 compilations, the current thread -- running a // WasmPartialTier2CompileTask -- is already dedicated to compiling the // to-be-tiered-up function. So don't create a new task for it.
compileState_ != CompileState::LazyTier2) {
parallel_ = true;
numTasks = 2 * GetMaxWasmCompilationThreads();
}
// Do not go over the threshold if we can avoid it: spin off the compilation // before appending the function if we would go over. (Very large single // functions may still exceed the threshold but this is fine; it'll be very // uncommon and is in any case safely handled by the MacroAssembler's buffer // limit logic.)
if (currentTask_ && currentTask_->inputs.length() &&
batchedBytecode_ + funcBytecodeLength > threshold) { if (!launchBatchCompile()) { returnfalse;
}
}
if (!currentTask_) { if (freeTasks_.empty() && !finishOutstandingTask()) { returnfalse;
}
currentTask_ = freeTasks_.popCopy();
}
if (!currentTask_->inputs.emplaceBack(funcIndex, lineOrBytecode, begin, end,
std::move(lineNums))) { returnfalse;
}
last = 0; for (const CodeRangeUnwindInfo& info : codeBlock.codeRangeUnwindInfos) {
MOZ_ASSERT(info.offset() >= last);
last = info.offset();
}
// Try notes should be sorted so that the end of ranges are in rising order // so that the innermost catch handler is chosen.
last = 0; for (const wasm::TryNote& tryNote : codeBlock.tryNotes) {
MOZ_ASSERT(tryNote.tryBodyEnd() >= last);
MOZ_ASSERT(tryNote.tryBodyEnd() > tryNote.tryBodyBegin());
last = tryNote.tryBodyBegin();
}
bool ModuleGenerator::finishCodeBlock(CodeBlockResult* result) { // Now that all functions and stubs are generated and their CodeRanges // known, patch all calls (which can emit far jumps) and far jumps. Linking // can emit tiny far-jump stubs, so there is an ordering dependency here.
if (!linkCallSites()) { returnfalse;
}
for (CallFarJump far : callFarJumps_) { if (funcIsCompiledInBlock(far.targetFuncIndex)) {
masm_->patchFarJump(
jit::CodeOffset(far.jumpOffset),
funcCodeRangeInBlock(far.targetFuncIndex).funcUncheckedCallEntry());
} elseif (!linkData_->callFarJumps.append(far)) { returnfalse;
}
}
// Mark the inlining context as done.
codeBlock_->inliningContext.setImmutable();
// Allocate the code storage, copy/link the code from `masm_` into it, set up // `codeBlock_->segment / codeBase / codeLength`, and adjust the metadata // offsets on `codeBlock_` accordingly.
uint8_t* codeStart = nullptr;
uint32_t codeLength = 0; if (partialTieringCode_) { // We're compiling a single function during tiering. Place it in its own // hardware page, inside an existing CodeSegment if possible, or allocate a // new one and use that. Either way, the chosen CodeSegment will be owned // by Code::lazyFuncSegments.
MOZ_ASSERT(mode() == CompileMode::LazyTiering);
// Try to allocate from Code::lazyFuncSegments. We do not allow a last-ditch // GC here as we may be running in OOL-code that is not ready for a GC.
codeBlock_->segment = partialTieringCode_->createFuncCodeSegmentFromPool(
*masm_, *linkData_, /* allowLastDitchGC = */ false, &codeStart,
&codeLength);
} else { // Create a new CodeSegment for the code and use that.
CodeSource codeSource(*masm_, linkData_.get(), nullptr);
codeLength = codeSource.lengthBytes();
uint32_t allocationLength;
codeBlock_->segment = CodeSegment::allocate(codeSource, nullptr, /* allowLastDitchGC = */ true,
&codeStart, &allocationLength);
tierStats_.codeBytesUsed += codeLength;
tierStats_.codeBytesMapped += allocationLength;
}
if (!codeBlock_->segment) {
warnf("failed to allocate executable memory for module"); returnfalse;
}
bool ModuleGenerator::prepareTier1() { if (!startCodeBlock(CodeBlockKind::SharedStubs)) { returnfalse;
}
// Initialize function definition ranges if (!funcDefRanges_.reserve(codeMeta_->numFuncDefs())) { returnfalse;
}
// Initialize function definition feature usages (only used for lazy tiering // and inlining right now). if (mode() == CompileMode::LazyTiering &&
(!funcDefFeatureUsages_.resize(codeMeta_->numFuncDefs()) ||
!funcDefCallRefMetrics_.resize(codeMeta_->numFuncDefs()))) { returnfalse;
}
// Initialize function definition alloc site ranges if (!funcDefAllocSites_.resize(codeMeta_->numFuncDefs())) { returnfalse;
}
// Initialize function import metadata if (!funcImports_.resize(codeMeta_->numFuncImports)) { returnfalse;
}
// The shared stubs code will contains function definitions for each imported // function. if (!FuncToCodeRangeMap::createDense(0, codeMeta_->numFuncImports,
&codeBlock_->funcToCodeRange)) { returnfalse;
}
uint32_t exportedFuncCount = 0; for (uint32_t funcIndex = 0; funcIndex < codeMeta_->numFuncImports;
funcIndex++) { const FuncDesc& func = codeMeta_->funcs[funcIndex]; if (func.isExported()) {
exportedFuncCount++;
}
} if (!codeBlock_->funcExports.reserve(exportedFuncCount)) { returnfalse;
}
for (uint32_t funcIndex = 0; funcIndex < codeMeta_->numFuncImports;
funcIndex++) { const FuncDesc& func = codeMeta_->funcs[funcIndex]; if (!func.isExported()) { continue;
}
if (!startCodeBlock(CodeBlock::kindFromTier(tier()))) { returnfalse;
}
// funcToCodeRange maps function indices to code-range indices and all // elements will be initialized by the time module generation is finished.
if (!FuncToCodeRangeMap::createDense(
codeMeta_->numFuncImports,
codeMeta_->funcs.length() - codeMeta_->numFuncImports,
&codeBlock_->funcToCodeRange)) { returnfalse;
}
// Pre-reserve space for large Vectors to avoid the significant cost of the // final reallocs. In particular, the MacroAssembler can be enormous, so be // extra conservative. Since large over-reservations may fail when the // actual allocations will succeed, ignore OOM failures. Note, // shrinkStorageToFit calls at the end will trim off unneeded capacity.
// Accumulate all exported functions: // - explicitly marked as such; // - implicitly exported by being an element of function tables; // - implicitly exported by being the start function; // - implicitly exported by being used in global ref.func initializer // ModuleEnvironment accumulates this information for us during decoding, // transfer it to the FuncExportVector stored in Metadata.
uint32_t exportedFuncCount = 0; for (uint32_t funcIndex = codeMeta_->numFuncImports;
funcIndex < codeMeta_->funcs.length(); funcIndex++) { const FuncDesc& func = codeMeta_->funcs[funcIndex]; if (func.isExported()) {
exportedFuncCount++;
}
} if (!codeBlock_->funcExports.reserve(exportedFuncCount)) { returnfalse;
}
if (!GenerateEntryStubs(*codeMeta_, codeBlock_->funcExports, &stubCode)) { returnfalse;
}
if (!linkCompiledCode(stubCode)) { returnfalse;
}
// Return the tier statistics and clear them
*tierStats = tierStats_;
tierStats_.clear();
return finishCodeBlock(result);
}
// Complete all tier-1 construction and return the resulting Module. For this // we will need both codeMeta_ (and maybe codeMetaForAsmJS_) and moduleMeta_.
SharedModule ModuleGenerator::finishModule( const BytecodeBufferOrSource& bytecode, ModuleMetadata& moduleMeta,
JS::OptimizedEncodingListener* maybeCompleteTier2Listener) {
MOZ_ASSERT(compilingTier1());
// Record what features we encountered in this module
moduleMeta.featureUsage = featureUsage_;
// Copy over data from the Bytecode, which is going away at the end of // compilation. // // In particular, convert the data- and custom-section ranges in the // ModuleMetadata into their full-fat versions by copying the underlying // data blocks.
const BytecodeSource& bytecodeSource = bytecode.source();
MOZ_ASSERT(moduleMeta.dataSegments.empty()); if (!moduleMeta.dataSegments.reserve(moduleMeta.dataSegmentRanges.length())) { return nullptr;
} for (const DataSegmentRange& srcRange : moduleMeta.dataSegmentRanges) {
MutableDataSegment dstSeg = js_new<DataSegment>(); if (!dstSeg) { return nullptr;
} if (!dstSeg->init(bytecodeSource, srcRange)) { return nullptr;
}
moduleMeta.dataSegments.infallibleAppend(std::move(dstSeg));
}
// Allocate and initialize the code tail metadata now that we have seen the // entire module.
MutableCodeTailMetadata codeTailMeta =
js_new<CodeTailMetadata>(*moduleMeta.codeMeta); if (!codeTailMeta) { return nullptr;
}
moduleMeta.codeTailMeta = codeTailMeta;
// Transfer the function definition ranges
MOZ_ASSERT(funcDefRanges_.length() == codeMeta_->numFuncDefs());
codeTailMeta->funcDefRanges = std::move(funcDefRanges_);
// Transfer the function definition feature usages
codeTailMeta->funcDefFeatureUsages = std::move(funcDefFeatureUsages_);
codeTailMeta->funcDefCallRefs = std::move(funcDefCallRefMetrics_);
codeTailMeta->funcDefAllocSites = std::move(funcDefAllocSites_);
MOZ_ASSERT_IF(mode() != CompileMode::LazyTiering, numCallRefMetrics_ == 0);
codeTailMeta->numCallRefMetrics = numCallRefMetrics_;
if (tier() == Tier::Baseline) {
codeTailMeta->numAllocSites = numAllocSites_;
} else {
MOZ_ASSERT(numAllocSites_ == 0); // Even if funcDefAllocSites were not created, e.g. single tier of // optimized compilation, the AllocSite array will exist.
codeTailMeta->numAllocSites = codeMeta_->numTypes();
}
// Initialize debuggable module state if (debugEnabled()) { // We cannot use lazy or eager tiering with debugging
MOZ_ASSERT(mode() == CompileMode::Once);
// Mark the flag
codeTailMeta->debugEnabled = true;
// Grab or allocate a full copy of the bytecode of this module if (!bytecode.getOrCreateBuffer(&codeTailMeta->debugBytecode)) { return nullptr;
}
codeTailMeta->codeSectionBytecode =
codeTailMeta->debugBytecode.codeSection();
// Compute the hash for this module
static_assert(sizeof(ModuleHash) <= sizeof(mozilla::SHA1Sum::Hash), "The ModuleHash size shall not exceed the SHA1 hash size.");
mozilla::SHA1Sum::Hash hash;
bytecodeSource.computeHash(&hash);
memcpy(codeTailMeta->debugHash, hash, sizeof(ModuleHash));
}
// Initialize lazy tiering module state if (mode() == CompileMode::LazyTiering) { // We cannot debug and use lazy tiering
MOZ_ASSERT(!debugEnabled());
// Grab or allocate a reference to the code section for this module if (bytecodeSource.hasCodeSection()) {
codeTailMeta->codeSectionBytecode = bytecode.getOrCreateCodeSection(); if (!codeTailMeta->codeSectionBytecode) { return nullptr;
}
}
// Store a reference to the name section on the code metadata if (codeMeta_->nameSection) {
codeTailMeta->nameSectionPayload =
moduleMeta.customSections[codeMeta_->nameSection->customSectionIndex]
.payload;
} else {
MOZ_ASSERT(codeTailMeta->nameSectionPayload == nullptr);
}
// Now that we have the name section we can send our blocks to the profiler.
sharedStubs_.codeBlock->sendToProfiler(
*codeMeta_, *codeTailMeta, codeMetaForAsmJS_,
FuncIonPerfSpewerSpan(sharedStubs_.funcIonSpewers),
FuncBaselinePerfSpewerSpan(sharedStubs_.funcBaselineSpewers));
tier1Result.codeBlock->sendToProfiler(
*codeMeta_, *codeTailMeta, codeMetaForAsmJS_,
FuncIonPerfSpewerSpan(tier1Result.funcIonSpewers),
FuncBaselinePerfSpewerSpan(tier1Result.funcBaselineSpewers));
// Copy in a couple of offsets.
code->setDebugStubOffset(debugStubCodeOffset_);
code->setRequestTierUpStubOffset(requestTierUpStubCodeOffset_);
code->setUpdateCallRefMetricsStubOffset(updateCallRefMetricsStubCodeOffset_); #ifdef ENABLE_WASM_JSPI
code->setContBaseFrameOffset(contBaseFrameOffset_); #endif
// All the components are finished, so create the complete Module and start // tier-2 compilation if requested.
// If we can serialize (not asm.js), are not planning on serializing already // and are testing serialization, then do a roundtrip through serialization // to test it out. if (!isAsmJS() && compileArgs_->features.testSerialization &&
module->canSerialize()) {
MOZ_RELEASE_ASSERT(mode() == CompileMode::Once &&
tier() == Tier::Serialized);
Bytes serializedBytes; if (!module->serialize(&serializedBytes)) { return nullptr;
}
// Perform storeOptimizedEncoding here instead of below so we don't have to // re-serialize the module. if (maybeCompleteTier2Listener && module->canSerialize()) {
maybeCompleteTier2Listener->storeOptimizedEncoding(
serializedBytes.begin(), serializedBytes.length());
maybeCompleteTier2Listener = nullptr;
}
}
if (compileState_ == CompileState::EagerTier1) { // Grab or allocate a copy of the code section bytecode
SharedBytes codeSection; if (bytecodeSource.hasCodeSection()) {
codeSection = bytecode.getOrCreateCodeSection(); if (!codeSection) { return nullptr;
}
}
// Kick off a background tier-2 compile task
module->startTier2(codeSection, maybeCompleteTier2Listener);
} elseif (tier() == Tier::Serialized && maybeCompleteTier2Listener &&
module->canSerialize()) {
Bytes bytes; if (module->serialize(&bytes)) {
maybeCompleteTier2Listener->storeOptimizedEncoding(bytes.begin(),
bytes.length());
}
}
// Complete all tier-2 construction. This merely augments the existing Code // and does not require moduleMeta_. bool ModuleGenerator::finishTier2(const Module& module) {
MOZ_ASSERT(!compilingTier1());
MOZ_ASSERT(compileState_ == CompileState::EagerTier2);
MOZ_ASSERT(tier() == Tier::Optimized);
MOZ_ASSERT(!compilerEnv_->debugEnabled());
if (cancelled_ && *cancelled_) { returnfalse;
}
CodeBlockResult tier2Result;
CompileAndLinkStats tier2Stats; if (!finishTier(&tier2Stats, &tier2Result)) { returnfalse;
}
if (MOZ_UNLIKELY(JitOptions.wasmDelayTier2)) { // Introduce an artificial delay when testing wasmDelayTier2, since we // want to exercise both tier1 and tier2 code in this case.
ThisThread::SleepMilliseconds(500);
}
// While we still have the func spewers, send the code block to the profiler.
tier2Result.codeBlock->sendToProfiler(
*codeMeta_, module.codeTailMeta(), codeMetaForAsmJS_,
FuncIonPerfSpewerSpan(tier2Result.funcIonSpewers),
FuncBaselinePerfSpewerSpan(tier2Result.funcBaselineSpewers));
CodeBlockResult tier2Result;
CompileAndLinkStats tier2Stats; if (!finishTier(&tier2Stats, &tier2Result)) { returnfalse;
}
// While we still have the func spewers, send the code block to the profiler.
tier2Result.codeBlock->sendToProfiler(
*codeMeta_, partialTieringCode_->codeTailMeta(), codeMetaForAsmJS_,
FuncIonPerfSpewerSpan(tier2Result.funcIonSpewers),
FuncBaselinePerfSpewerSpan(tier2Result.funcBaselineSpewers));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.