summaryrefslogtreecommitdiffstats
path: root/js/src/wasm/WasmGenerator.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/wasm/WasmGenerator.cpp1362
1 files changed, 1362 insertions, 0 deletions
diff --git a/js/src/wasm/WasmGenerator.cpp b/js/src/wasm/WasmGenerator.cpp
new file mode 100644
index 0000000000..d477e001b7
--- /dev/null
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -0,0 +1,1362 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ *
+ * Copyright 2015 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wasm/WasmGenerator.h"
+
+#include "mozilla/CheckedInt.h"
+#include "mozilla/EnumeratedRange.h"
+#include "mozilla/SHA1.h"
+#include "mozilla/Unused.h"
+
+#include <algorithm>
+#include <thread>
+
+#include "util/Memory.h"
+#include "util/Text.h"
+#include "vm/HelperThreadState.h"
+#include "vm/Time.h"
+#include "vm/TraceLogging.h"
+#include "vm/TraceLoggingTypes.h"
+#include "wasm/WasmBaselineCompile.h"
+#include "wasm/WasmCompile.h"
+#include "wasm/WasmCraneliftCompile.h"
+#include "wasm/WasmIonCompile.h"
+#include "wasm/WasmStubs.h"
+
+#include "jit/MacroAssembler-inl.h"
+
+using namespace js;
+using namespace js::jit;
+using namespace js::wasm;
+
+using mozilla::CheckedInt;
+using mozilla::MakeEnumeratedRange;
+using mozilla::Unused;
+
+bool CompiledCode::swap(MacroAssembler& masm) {
+ MOZ_ASSERT(bytes.empty());
+ if (!masm.swapBuffer(bytes)) {
+ return false;
+ }
+
+ callSites.swap(masm.callSites());
+ callSiteTargets.swap(masm.callSiteTargets());
+ trapSites.swap(masm.trapSites());
+ symbolicAccesses.swap(masm.symbolicAccesses());
+ codeLabels.swap(masm.codeLabels());
+ return true;
+}
+
+bool CompiledCode::swapCranelift(MacroAssembler& masm,
+ CraneliftReusableData& data) {
+ if (!swap(masm)) {
+ return false;
+ }
+ std::swap(data, craneliftReusableData);
+ return true;
+}
+
+// ****************************************************************************
+// 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(const CompileArgs& args,
+ ModuleEnvironment* moduleEnv,
+ CompilerEnvironment* compilerEnv,
+ const Atomic<bool>* cancelled,
+ UniqueChars* error)
+ : compileArgs_(&args),
+ error_(error),
+ cancelled_(cancelled),
+ moduleEnv_(moduleEnv),
+ compilerEnv_(compilerEnv),
+ linkData_(nullptr),
+ metadataTier_(nullptr),
+ lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
+ masmAlloc_(&lifo_),
+ masm_(masmAlloc_, *moduleEnv, /* limitedSize= */ false),
+ debugTrapCodeOffset_(),
+ lastPatchedCallSite_(0),
+ startOfUnpatchedCallsites_(0),
+ parallel_(false),
+ outstanding_(0),
+ currentTask_(nullptr),
+ batchedBytecode_(0),
+ finishedFuncDefs_(false) {
+ MOZ_ASSERT(IsCompilingWasm());
+}
+
+ModuleGenerator::~ModuleGenerator() {
+ MOZ_ASSERT_IF(finishedFuncDefs_, !batchedBytecode_);
+ MOZ_ASSERT_IF(finishedFuncDefs_, !currentTask_);
+
+ if (parallel_) {
+ if (outstanding_) {
+ AutoLockHelperThreadState lock;
+
+ // Remove any pending compilation tasks from the worklist.
+ size_t removed = RemovePendingWasmCompileTasks(taskState_, mode(), 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();
+
+ MOZ_ASSERT(outstanding_ >= taskState_.numFailed());
+ outstanding_ -= taskState_.numFailed();
+ taskState_.numFailed() = 0;
+
+ if (!outstanding_) {
+ break;
+ }
+
+ taskState_.condVar().wait(lock); /* failed or finished */
+ }
+ }
+ } else {
+ MOZ_ASSERT(!outstanding_);
+ }
+
+ // Propagate error state.
+ if (error_ && !*error_) {
+ AutoLockHelperThreadState lock;
+ *error_ = std::move(taskState_.errorMessage());
+ }
+}
+
+bool ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align,
+ uint32_t* globalDataOffset) {
+ CheckedInt<uint32_t> newGlobalDataLength(metadata_->globalDataLength);
+
+ newGlobalDataLength +=
+ ComputeByteAlignment(newGlobalDataLength.value(), align);
+ if (!newGlobalDataLength.isValid()) {
+ return false;
+ }
+
+ *globalDataOffset = newGlobalDataLength.value();
+ newGlobalDataLength += bytes;
+
+ if (!newGlobalDataLength.isValid()) {
+ return false;
+ }
+
+ metadata_->globalDataLength = newGlobalDataLength.value();
+ return true;
+}
+
+bool ModuleGenerator::init(Metadata* maybeAsmJSMetadata,
+ JSTelemetrySender telemetrySender) {
+ // Perform fallible metadata, linkdata, assumption allocations.
+
+ telemetrySender_ = telemetrySender;
+
+ MOZ_ASSERT(isAsmJS() == !!maybeAsmJSMetadata);
+ if (maybeAsmJSMetadata) {
+ metadata_ = maybeAsmJSMetadata;
+ } else {
+ metadata_ = js_new<Metadata>();
+ if (!metadata_) {
+ return false;
+ }
+ }
+
+ if (compileArgs_->scriptedCaller.filename) {
+ metadata_->filename =
+ DuplicateString(compileArgs_->scriptedCaller.filename.get());
+ if (!metadata_->filename) {
+ return false;
+ }
+
+ metadata_->filenameIsURL = compileArgs_->scriptedCaller.filenameIsURL;
+ } else {
+ MOZ_ASSERT(!compileArgs_->scriptedCaller.filenameIsURL);
+ }
+
+ if (compileArgs_->sourceMapURL) {
+ metadata_->sourceMapURL = DuplicateString(compileArgs_->sourceMapURL.get());
+ if (!metadata_->sourceMapURL) {
+ return false;
+ }
+ }
+
+ linkData_ = js::MakeUnique<LinkData>(tier());
+ if (!linkData_) {
+ return false;
+ }
+
+ metadataTier_ = js::MakeUnique<MetadataTier>(tier());
+ if (!metadataTier_) {
+ return false;
+ }
+
+ // funcToCodeRange maps function indices to code-range indices and all
+ // elements will be initialized by the time module generation is finished.
+
+ if (!metadataTier_->funcToCodeRange.appendN(BAD_CODE_RANGE,
+ moduleEnv_->funcs.length())) {
+ return false;
+ }
+
+ // 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.
+
+ size_t codeSectionSize =
+ moduleEnv_->codeSection ? moduleEnv_->codeSection->size : 0;
+
+ size_t estimatedCodeSize =
+ 1.2 * EstimateCompiledCodeSize(tier(), codeSectionSize);
+ Unused << masm_.reserve(std::min(estimatedCodeSize, MaxCodeBytesPerProcess));
+
+ Unused << metadataTier_->codeRanges.reserve(2 * moduleEnv_->numFuncDefs());
+
+ const size_t ByteCodesPerCallSite = 50;
+ Unused << metadataTier_->callSites.reserve(codeSectionSize /
+ ByteCodesPerCallSite);
+
+ const size_t ByteCodesPerOOBTrap = 10;
+ Unused << metadataTier_->trapSites[Trap::OutOfBounds].reserve(
+ codeSectionSize / ByteCodesPerOOBTrap);
+
+ // Allocate space in TlsData for declarations that need it.
+
+ MOZ_ASSERT(metadata_->globalDataLength == 0);
+
+ for (size_t i = 0; i < moduleEnv_->funcImportGlobalDataOffsets.length();
+ i++) {
+ uint32_t globalDataOffset;
+ if (!allocateGlobalBytes(sizeof(FuncImportTls), sizeof(void*),
+ &globalDataOffset)) {
+ return false;
+ }
+
+ moduleEnv_->funcImportGlobalDataOffsets[i] = globalDataOffset;
+
+ FuncType copy;
+ if (!copy.clone(*moduleEnv_->funcs[i].type)) {
+ return false;
+ }
+ if (!metadataTier_->funcImports.emplaceBack(std::move(copy),
+ globalDataOffset)) {
+ return false;
+ }
+ }
+
+ for (TableDesc& table : moduleEnv_->tables) {
+ if (!allocateGlobalBytes(sizeof(TableTls), sizeof(void*),
+ &table.globalDataOffset)) {
+ return false;
+ }
+ }
+
+ if (!isAsmJS()) {
+ // Copy type definitions to metadata that are required at runtime,
+ // allocating global data so that codegen can find the type id's at
+ // runtime.
+ for (uint32_t typeIndex = 0; typeIndex < moduleEnv_->types.length();
+ typeIndex++) {
+ const TypeDef& typeDef = moduleEnv_->types[typeIndex];
+ TypeIdDesc& typeId = moduleEnv_->typeIds[typeIndex];
+
+ if (TypeIdDesc::isGlobal(typeDef)) {
+ uint32_t globalDataOffset;
+ if (!allocateGlobalBytes(sizeof(void*), sizeof(void*),
+ &globalDataOffset)) {
+ return false;
+ }
+
+ typeId = TypeIdDesc::global(typeDef, globalDataOffset);
+
+ TypeDef copy;
+ if (!copy.clone(typeDef)) {
+ return false;
+ }
+
+ if (!metadata_->types.emplaceBack(std::move(copy), typeId)) {
+ return false;
+ }
+ } else {
+ typeId = TypeIdDesc::immediate(typeDef);
+ }
+ }
+
+ // If we allow type indices, then we need to rewrite the index space to
+ // account for types that are omitted from metadata, such as function
+ // types that fit in an immediate.
+ if (moduleEnv_->functionReferencesEnabled()) {
+ // Do a linear pass to create a map from src index to dest index.
+ RenumberMap map;
+ for (uint32_t srcIndex = 0, destIndex = 0;
+ srcIndex < moduleEnv_->types.length(); srcIndex++) {
+ const TypeDef& typeDef = moduleEnv_->types[srcIndex];
+ if (!TypeIdDesc::isGlobal(typeDef)) {
+ continue;
+ }
+ if (!map.put(srcIndex, destIndex++)) {
+ return false;
+ }
+ }
+
+ // Apply the map
+ for (TypeDefWithId& typeDef : metadata_->types) {
+ typeDef.renumber(map);
+ }
+ }
+ }
+
+ for (GlobalDesc& global : moduleEnv_->globals) {
+ if (global.isConstant()) {
+ continue;
+ }
+
+ uint32_t width =
+ global.isIndirect() ? sizeof(void*) : SizeOf(global.type());
+
+ uint32_t globalDataOffset;
+ if (!allocateGlobalBytes(width, width, &globalDataOffset)) {
+ return false;
+ }
+
+ global.setOffset(globalDataOffset);
+ }
+
+ // Accumulate all exported functions:
+ // - explicitly marked as such;
+ // - implicitly exported by being an element of function tables;
+ // - implicitly exported by being the start function;
+ // The FuncExportVector stored in Metadata needs to be sorted (to allow
+ // O(log(n)) lookup at runtime) and deduplicated. Use a vector with invalid
+ // entries for every single function, that we'll fill as we go through the
+ // exports, and in which we'll remove invalid entries after the fact.
+
+ static_assert(((uint64_t(MaxFuncs) << 1) | 1) < uint64_t(UINT32_MAX),
+ "bit packing won't work in ExportedFunc");
+
+ class ExportedFunc {
+ uint32_t value;
+
+ public:
+ ExportedFunc() : value(UINT32_MAX) {}
+ ExportedFunc(uint32_t index, bool isExplicit)
+ : value((index << 1) | (isExplicit ? 1 : 0)) {}
+ uint32_t index() const { return value >> 1; }
+ bool isExplicit() const { return value & 0x1; }
+ bool operator<(const ExportedFunc& other) const {
+ return index() < other.index();
+ }
+ bool operator==(const ExportedFunc& other) const {
+ return index() == other.index();
+ }
+ bool isInvalid() const { return value == UINT32_MAX; }
+ void mergeExplicit(bool explicitBit) {
+ if (!isExplicit() && explicitBit) {
+ value |= 0x1;
+ }
+ }
+ };
+
+ Vector<ExportedFunc, 8, SystemAllocPolicy> exportedFuncs;
+ if (!exportedFuncs.resize(moduleEnv_->numFuncs())) {
+ return false;
+ }
+
+ auto addOrMerge = [&exportedFuncs](ExportedFunc newEntry) {
+ uint32_t index = newEntry.index();
+ if (exportedFuncs[index].isInvalid()) {
+ exportedFuncs[index] = newEntry;
+ } else {
+ exportedFuncs[index].mergeExplicit(newEntry.isExplicit());
+ }
+ };
+
+ for (const Export& exp : moduleEnv_->exports) {
+ if (exp.kind() == DefinitionKind::Function) {
+ addOrMerge(ExportedFunc(exp.funcIndex(), true));
+ }
+ }
+
+ if (moduleEnv_->startFuncIndex) {
+ addOrMerge(ExportedFunc(*moduleEnv_->startFuncIndex, true));
+ }
+
+ for (const ElemSegment* seg : moduleEnv_->elemSegments) {
+ // For now, the segments always carry function indices regardless of the
+ // segment's declared element type; this works because the only legal
+ // element types are funcref and externref and the only legal values are
+ // functions and null. We always add functions in segments as exported
+ // functions, regardless of the segment's type. In the future, if we make
+ // the representation of AnyRef segments different, we will have to consider
+ // function values in those segments specially.
+ bool isAsmJS = seg->active() && moduleEnv_->tables[seg->tableIndex].isAsmJS;
+ if (!isAsmJS) {
+ for (uint32_t funcIndex : seg->elemFuncIndices) {
+ if (funcIndex != NullFuncIndex) {
+ addOrMerge(ExportedFunc(funcIndex, false));
+ }
+ }
+ }
+ }
+
+ for (const GlobalDesc& global : moduleEnv_->globals) {
+ if (global.isVariable() &&
+ global.initExpr().kind() == InitExpr::Kind::RefFunc) {
+ addOrMerge(ExportedFunc(global.initExpr().refFuncIndex(), false));
+ }
+ }
+
+ auto* newEnd =
+ std::remove_if(exportedFuncs.begin(), exportedFuncs.end(),
+ [](const ExportedFunc& exp) { return exp.isInvalid(); });
+ exportedFuncs.erase(newEnd, exportedFuncs.end());
+
+ if (!metadataTier_->funcExports.reserve(exportedFuncs.length())) {
+ return false;
+ }
+
+ for (const ExportedFunc& funcIndex : exportedFuncs) {
+ FuncType funcType;
+ if (!funcType.clone(*moduleEnv_->funcs[funcIndex.index()].type)) {
+ return false;
+ }
+ metadataTier_->funcExports.infallibleEmplaceBack(
+ std::move(funcType), funcIndex.index(), funcIndex.isExplicit());
+ }
+
+ // Determine whether parallel or sequential compilation is to be used and
+ // initialize the CompileTasks that will be used in either mode.
+
+ GlobalHelperThreadState& threads = HelperThreadState();
+ MOZ_ASSERT(threads.threadCount > 1);
+
+ uint32_t numTasks;
+ if (CanUseExtraThreads() && threads.cpuCount > 1) {
+ parallel_ = true;
+ numTasks = 2 * threads.maxWasmCompilationThreads();
+ } else {
+ numTasks = 1;
+ }
+
+ if (!tasks_.initCapacity(numTasks)) {
+ return false;
+ }
+ for (size_t i = 0; i < numTasks; i++) {
+ tasks_.infallibleEmplaceBack(*moduleEnv_, *compilerEnv_, taskState_,
+ COMPILATION_LIFO_DEFAULT_CHUNK_SIZE,
+ telemetrySender);
+ }
+
+ if (!freeTasks_.reserve(numTasks)) {
+ return false;
+ }
+ for (size_t i = 0; i < numTasks; i++) {
+ freeTasks_.infallibleAppend(&tasks_[i]);
+ }
+
+ // Fill in function stubs for each import so that imported functions can be
+ // used in all the places that normal function definitions can (table
+ // elements, export calls, etc).
+
+ CompiledCode& importCode = tasks_[0].output;
+ MOZ_ASSERT(importCode.empty());
+
+ if (!GenerateImportFunctions(*moduleEnv_, metadataTier_->funcImports,
+ &importCode)) {
+ return false;
+ }
+
+ if (!linkCompiledCode(importCode)) {
+ return false;
+ }
+
+ importCode.clear();
+ return true;
+}
+
+bool ModuleGenerator::funcIsCompiled(uint32_t funcIndex) const {
+ return metadataTier_->funcToCodeRange[funcIndex] != BAD_CODE_RANGE;
+}
+
+const CodeRange& ModuleGenerator::funcCodeRange(uint32_t funcIndex) const {
+ MOZ_ASSERT(funcIsCompiled(funcIndex));
+ const CodeRange& cr =
+ metadataTier_->codeRanges[metadataTier_->funcToCodeRange[funcIndex]];
+ MOZ_ASSERT(cr.isFunction());
+ return cr;
+}
+
+static bool 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;
+}
+
+typedef HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy>
+ OffsetMap;
+typedef EnumeratedArray<Trap, Trap::Limit, Maybe<uint32_t>>
+ TrapMaybeOffsetArray;
+
+bool ModuleGenerator::linkCallSites() {
+ masm_.haltingAlign(CodeAlignment);
+
+ // 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_ < metadataTier_->callSites.length();
+ lastPatchedCallSite_++) {
+ const CallSite& callSite = metadataTier_->callSites[lastPatchedCallSite_];
+ const CallSiteTarget& target = callSiteTargets_[lastPatchedCallSite_];
+ uint32_t callerOffset = callSite.returnAddressOffset();
+ switch (callSite.kind()) {
+ case CallSiteDesc::Dynamic:
+ case CallSiteDesc::Symbolic:
+ break;
+ case CallSiteDesc::Func: {
+ if (funcIsCompiled(target.funcIndex())) {
+ uint32_t calleeOffset =
+ funcCodeRange(target.funcIndex()).funcUncheckedCallEntry();
+ if (InRange(callerOffset, calleeOffset)) {
+ masm_.patchCall(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())) {
+ return false;
+ }
+ offsets.end = masm_.currentOffset();
+ if (masm_.oom()) {
+ return false;
+ }
+ if (!metadataTier_->codeRanges.emplaceBack(CodeRange::FarJumpIsland,
+ offsets)) {
+ return false;
+ }
+ if (!existingCallFarJumps.add(p, target.funcIndex(), offsets.begin)) {
+ return false;
+ }
+ }
+
+ masm_.patchCall(callerOffset, p->value());
+ break;
+ }
+ case CallSiteDesc::Breakpoint:
+ case CallSiteDesc::EnterFrame:
+ case CallSiteDesc::LeaveFrame: {
+ Uint32Vector& jumps = metadataTier_->debugTrapFarJumpOffsets;
+ if (jumps.empty() || !InRange(jumps.back(), callerOffset)) {
+ Offsets offsets;
+ offsets.begin = masm_.currentOffset();
+ CodeOffset jumpOffset = masm_.farJumpWithPatch();
+ offsets.end = masm_.currentOffset();
+ if (masm_.oom()) {
+ return false;
+ }
+ if (!metadataTier_->codeRanges.emplaceBack(CodeRange::FarJumpIsland,
+ offsets)) {
+ return false;
+ }
+ if (!debugTrapFarJumps_.emplaceBack(jumpOffset)) {
+ return false;
+ }
+ if (!jumps.emplaceBack(offsets.begin)) {
+ return false;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ masm_.flushBuffer();
+ return !masm_.oom();
+}
+
+void ModuleGenerator::noteCodeRange(uint32_t codeRangeIndex,
+ const CodeRange& codeRange) {
+ switch (codeRange.kind()) {
+ case CodeRange::Function:
+ MOZ_ASSERT(metadataTier_->funcToCodeRange[codeRange.funcIndex()] ==
+ BAD_CODE_RANGE);
+ metadataTier_->funcToCodeRange[codeRange.funcIndex()] = codeRangeIndex;
+ break;
+ case CodeRange::InterpEntry:
+ metadataTier_->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:
+ metadataTier_->funcImports[codeRange.funcIndex()].initJitExitOffset(
+ codeRange.begin());
+ break;
+ case CodeRange::ImportInterpExit:
+ metadataTier_->funcImports[codeRange.funcIndex()].initInterpExitOffset(
+ codeRange.begin());
+ break;
+ case CodeRange::DebugTrap:
+ MOZ_ASSERT(!debugTrapCodeOffset_);
+ debugTrapCodeOffset_ = codeRange.begin();
+ break;
+ 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");
+ }
+}
+
+template <class Vec, class Op>
+static bool AppendForEach(Vec* dstVec, const Vec& srcVec, Op op) {
+ if (!dstVec->growByUninitialized(srcVec.length())) {
+ return false;
+ }
+
+ using T = typename Vec::ElementType;
+
+ const T* src = srcVec.begin();
+
+ T* dstBegin = dstVec->begin();
+ T* dstEnd = dstVec->end();
+ T* dstStart = dstEnd - srcVec.length();
+
+ for (T* dst = dstStart; dst != dstEnd; dst++, src++) {
+ new (dst) T(*src);
+ op(dst - dstBegin, dst);
+ }
+
+ return true;
+}
+
+bool ModuleGenerator::linkCompiledCode(CompiledCode& code) {
+ // 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()) {
+ return false;
+ }
+ }
+
+ // All code offsets in 'code' must be incremented by their position in the
+ // overall module when the code was appended.
+
+ masm_.haltingAlign(CodeAlignment);
+ const size_t offsetInModule = masm_.size();
+ if (!masm_.appendRawCode(code.bytes.begin(), code.bytes.length())) {
+ return false;
+ }
+
+ auto codeRangeOp = [=](uint32_t codeRangeIndex, CodeRange* codeRange) {
+ codeRange->offsetBy(offsetInModule);
+ noteCodeRange(codeRangeIndex, *codeRange);
+ };
+ if (!AppendForEach(&metadataTier_->codeRanges, code.codeRanges,
+ codeRangeOp)) {
+ return false;
+ }
+
+ auto callSiteOp = [=](uint32_t, CallSite* cs) {
+ cs->offsetBy(offsetInModule);
+ };
+ if (!AppendForEach(&metadataTier_->callSites, code.callSites, callSiteOp)) {
+ return false;
+ }
+
+ if (!callSiteTargets_.appendAll(code.callSiteTargets)) {
+ return false;
+ }
+
+ for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
+ auto trapSiteOp = [=](uint32_t, TrapSite* ts) {
+ ts->offsetBy(offsetInModule);
+ };
+ if (!AppendForEach(&metadataTier_->trapSites[trap], code.trapSites[trap],
+ trapSiteOp)) {
+ return false;
+ }
+ }
+
+ for (const SymbolicAccess& access : code.symbolicAccesses) {
+ uint32_t patchAt = offsetInModule + access.patchAt.offset();
+ if (!linkData_->symbolicLinks[access.target].append(patchAt)) {
+ return false;
+ }
+ }
+
+ for (const CodeLabel& codeLabel : code.codeLabels) {
+ LinkData::InternalLink link;
+ link.patchAtOffset = offsetInModule + codeLabel.patchAt().offset();
+ link.targetOffset = offsetInModule + codeLabel.target().offset();
+#ifdef JS_CODELABEL_LINKMODE
+ link.mode = codeLabel.linkMode();
+#endif
+ if (!linkData_->internalLinks.append(link)) {
+ return false;
+ }
+ }
+
+ for (size_t i = 0; i < code.stackMaps.length(); i++) {
+ StackMaps::Maplet maplet = code.stackMaps.move(i);
+ maplet.offsetBy(offsetInModule);
+ if (!metadataTier_->stackMaps.add(maplet)) {
+ // This function is now the only owner of maplet.map, so we'd better
+ // free it right now.
+ maplet.map->destroy();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool ExecuteCompileTask(CompileTask* task, UniqueChars* error) {
+ MOZ_ASSERT(task->lifo.isEmpty());
+ MOZ_ASSERT(task->output.empty());
+
+#ifdef ENABLE_SPIDERMONKEY_TELEMETRY
+ int64_t startTime = PRMJ_Now();
+ int compileTimeTelemetryID;
+#endif
+
+ switch (task->compilerEnv.tier()) {
+ case Tier::Optimized:
+ switch (task->compilerEnv.optimizedBackend()) {
+ case OptimizedBackend::Cranelift:
+ if (!CraneliftCompileFunctions(task->moduleEnv, task->compilerEnv,
+ task->lifo, task->inputs,
+ &task->output, error)) {
+ return false;
+ }
+#ifdef ENABLE_SPIDERMONKEY_TELEMETRY
+ compileTimeTelemetryID = JS_TELEMETRY_WASM_COMPILE_TIME_CRANELIFT_US;
+#endif
+ break;
+ case OptimizedBackend::Ion:
+ if (!IonCompileFunctions(task->moduleEnv, task->compilerEnv,
+ task->lifo, task->inputs, &task->output,
+ error)) {
+ return false;
+ }
+#ifdef ENABLE_SPIDERMONKEY_TELEMETRY
+ compileTimeTelemetryID = JS_TELEMETRY_WASM_COMPILE_TIME_ION_US;
+#endif
+ break;
+ }
+ break;
+ case Tier::Baseline:
+ if (!BaselineCompileFunctions(task->moduleEnv, task->compilerEnv,
+ task->lifo, task->inputs, &task->output,
+ error)) {
+ return false;
+ }
+#ifdef ENABLE_SPIDERMONKEY_TELEMETRY
+ compileTimeTelemetryID = JS_TELEMETRY_WASM_COMPILE_TIME_BASELINE_US;
+#endif
+ break;
+ }
+
+#ifdef ENABLE_SPIDERMONKEY_TELEMETRY
+ int64_t endTime = PRMJ_Now();
+ int64_t compileTimeMicros = endTime - startTime;
+
+ task->telemetrySender.addTelemetry(compileTimeTelemetryID, compileTimeMicros);
+#endif
+
+ MOZ_ASSERT(task->lifo.isEmpty());
+ MOZ_ASSERT(task->inputs.length() == task->output.codeRanges.length());
+ task->inputs.clear();
+ return true;
+}
+
+void CompileTask::runHelperThreadTask(AutoLockHelperThreadState& lock) {
+ TraceLoggerThread* logger = TraceLoggerForCurrentThread();
+ AutoTraceLog logCompile(logger, TraceLogger_WasmCompilation);
+
+ UniqueChars error;
+ bool ok;
+
+ {
+ 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 */
+}
+
+bool ModuleGenerator::locallyCompileCurrentTask() {
+ if (!ExecuteCompileTask(currentTask_, error_)) {
+ return false;
+ }
+ if (!finishTask(currentTask_)) {
+ return false;
+ }
+ currentTask_ = nullptr;
+ batchedBytecode_ = 0;
+ return true;
+}
+
+bool ModuleGenerator::finishTask(CompileTask* task) {
+ masm_.haltingAlign(CodeAlignment);
+
+ if (!linkCompiledCode(task->output)) {
+ return false;
+ }
+
+ task->output.clear();
+
+ MOZ_ASSERT(task->inputs.empty());
+ MOZ_ASSERT(task->output.empty());
+ MOZ_ASSERT(task->lifo.isEmpty());
+ freeTasks_.infallibleAppend(task);
+ return true;
+}
+
+bool ModuleGenerator::launchBatchCompile() {
+ MOZ_ASSERT(currentTask_);
+
+ if (cancelled_ && *cancelled_) {
+ return false;
+ }
+
+ if (!parallel_) {
+ return locallyCompileCurrentTask();
+ }
+
+ if (!StartOffThreadWasmCompile(currentTask_, mode())) {
+ return false;
+ }
+ outstanding_++;
+ currentTask_ = nullptr;
+ batchedBytecode_ = 0;
+ return true;
+}
+
+bool ModuleGenerator::finishOutstandingTask() {
+ MOZ_ASSERT(parallel_);
+
+ CompileTask* task = nullptr;
+ {
+ AutoLockHelperThreadState lock;
+ while (true) {
+ MOZ_ASSERT(outstanding_ > 0);
+
+ if (taskState_.numFailed() > 0) {
+ return false;
+ }
+
+ if (!taskState_.finished().empty()) {
+ outstanding_--;
+ task = taskState_.finished().popCopy();
+ break;
+ }
+
+ taskState_.condVar().wait(lock); /* failed or finished */
+ }
+ }
+
+ // Call outside of the compilation lock.
+ return finishTask(task);
+}
+
+bool ModuleGenerator::compileFuncDef(uint32_t funcIndex,
+ uint32_t lineOrBytecode,
+ const uint8_t* begin, const uint8_t* end,
+ Uint32Vector&& lineNums) {
+ MOZ_ASSERT(!finishedFuncDefs_);
+ MOZ_ASSERT(funcIndex < moduleEnv_->numFuncs());
+
+ uint32_t threshold;
+ switch (tier()) {
+ case Tier::Baseline:
+ threshold = JitOptions.wasmBatchBaselineThreshold;
+ break;
+ case Tier::Optimized:
+ switch (compilerEnv_->optimizedBackend()) {
+ case OptimizedBackend::Ion:
+ threshold = JitOptions.wasmBatchIonThreshold;
+ break;
+ case OptimizedBackend::Cranelift:
+ threshold = JitOptions.wasmBatchCraneliftThreshold;
+ break;
+ default:
+ MOZ_CRASH("Invalid optimizedBackend value");
+ }
+ break;
+ default:
+ MOZ_CRASH("Invalid tier value");
+ break;
+ }
+
+ uint32_t funcBytecodeLength = end - begin;
+
+ // 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()) {
+ return false;
+ }
+ }
+
+ if (!currentTask_) {
+ if (freeTasks_.empty() && !finishOutstandingTask()) {
+ return false;
+ }
+ currentTask_ = freeTasks_.popCopy();
+ }
+
+ if (!currentTask_->inputs.emplaceBack(funcIndex, lineOrBytecode, begin, end,
+ std::move(lineNums))) {
+ return false;
+ }
+
+ batchedBytecode_ += funcBytecodeLength;
+ MOZ_ASSERT(batchedBytecode_ <= MaxCodeSectionBytes);
+ return true;
+}
+
+bool ModuleGenerator::finishFuncDefs() {
+ MOZ_ASSERT(!finishedFuncDefs_);
+
+ if (currentTask_ && !locallyCompileCurrentTask()) {
+ return false;
+ }
+
+ finishedFuncDefs_ = true;
+ return true;
+}
+
+bool ModuleGenerator::finishCodegen() {
+ // 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()) {
+ return false;
+ }
+
+ for (CallFarJump far : callFarJumps_) {
+ masm_.patchFarJump(far.jump,
+ funcCodeRange(far.funcIndex).funcUncheckedCallEntry());
+ }
+
+ for (CodeOffset farJump : debugTrapFarJumps_) {
+ masm_.patchFarJump(farJump, debugTrapCodeOffset_);
+ }
+
+ // None of the linking or far-jump operations should emit masm metadata.
+
+ MOZ_ASSERT(masm_.callSites().empty());
+ MOZ_ASSERT(masm_.callSiteTargets().empty());
+ MOZ_ASSERT(masm_.trapSites().empty());
+ MOZ_ASSERT(masm_.symbolicAccesses().empty());
+ MOZ_ASSERT(masm_.codeLabels().empty());
+
+ masm_.finish();
+ return !masm_.oom();
+}
+
+bool ModuleGenerator::finishMetadataTier() {
+ // The stack maps aren't yet sorted. Do so now, since we'll need to
+ // binary-search them at GC time.
+ metadataTier_->stackMaps.sort();
+
+#ifdef DEBUG
+ // Check that the stack map contains no duplicates, since that could lead to
+ // ambiguities about stack slot pointerness.
+ uint8_t* previousNextInsnAddr = nullptr;
+ for (size_t i = 0; i < metadataTier_->stackMaps.length(); i++) {
+ const StackMaps::Maplet& maplet = metadataTier_->stackMaps.get(i);
+ MOZ_ASSERT_IF(i > 0, uintptr_t(maplet.nextInsnAddr) >
+ uintptr_t(previousNextInsnAddr));
+ previousNextInsnAddr = maplet.nextInsnAddr;
+ }
+
+ // Assert all sorted metadata is sorted.
+ uint32_t last = 0;
+ for (const CodeRange& codeRange : metadataTier_->codeRanges) {
+ MOZ_ASSERT(codeRange.begin() >= last);
+ last = codeRange.end();
+ }
+
+ last = 0;
+ for (const CallSite& callSite : metadataTier_->callSites) {
+ MOZ_ASSERT(callSite.returnAddressOffset() >= last);
+ last = callSite.returnAddressOffset();
+ }
+
+ for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
+ last = 0;
+ for (const TrapSite& trapSite : metadataTier_->trapSites[trap]) {
+ MOZ_ASSERT(trapSite.pcOffset >= last);
+ last = trapSite.pcOffset;
+ }
+ }
+
+ last = 0;
+ for (uint32_t debugTrapFarJumpOffset :
+ metadataTier_->debugTrapFarJumpOffsets) {
+ MOZ_ASSERT(debugTrapFarJumpOffset >= last);
+ last = debugTrapFarJumpOffset;
+ }
+#endif
+
+ // These Vectors can get large and the excess capacity can be significant,
+ // so realloc them down to size.
+
+ metadataTier_->funcToCodeRange.shrinkStorageToFit();
+ metadataTier_->codeRanges.shrinkStorageToFit();
+ metadataTier_->callSites.shrinkStorageToFit();
+ metadataTier_->trapSites.shrinkStorageToFit();
+ metadataTier_->debugTrapFarJumpOffsets.shrinkStorageToFit();
+ for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
+ metadataTier_->trapSites[trap].shrinkStorageToFit();
+ }
+
+ return true;
+}
+
+UniqueCodeTier ModuleGenerator::finishCodeTier() {
+ MOZ_ASSERT(finishedFuncDefs_);
+
+ while (outstanding_ > 0) {
+ if (!finishOutstandingTask()) {
+ return nullptr;
+ }
+ }
+
+#ifdef DEBUG
+ for (uint32_t codeRangeIndex : metadataTier_->funcToCodeRange) {
+ MOZ_ASSERT(codeRangeIndex != BAD_CODE_RANGE);
+ }
+#endif
+
+ // Now that all imports/exports are known, we can generate a special
+ // CompiledCode containing stubs.
+
+ CompiledCode& stubCode = tasks_[0].output;
+ MOZ_ASSERT(stubCode.empty());
+
+ if (!GenerateStubs(*moduleEnv_, metadataTier_->funcImports,
+ metadataTier_->funcExports, &stubCode)) {
+ return nullptr;
+ }
+
+ if (!linkCompiledCode(stubCode)) {
+ return nullptr;
+ }
+
+ // Finish linking and metadata.
+
+ if (!finishCodegen()) {
+ return nullptr;
+ }
+
+ if (!finishMetadataTier()) {
+ return nullptr;
+ }
+
+ UniqueModuleSegment segment =
+ ModuleSegment::create(tier(), masm_, *linkData_);
+ if (!segment) {
+ return nullptr;
+ }
+
+ metadataTier_->stackMaps.offsetBy(uintptr_t(segment->base()));
+
+#ifdef DEBUG
+ // Check that each stack map is associated with a plausible instruction.
+ for (size_t i = 0; i < metadataTier_->stackMaps.length(); i++) {
+ MOZ_ASSERT(IsValidStackMapKey(compilerEnv_->debugEnabled(),
+ metadataTier_->stackMaps.get(i).nextInsnAddr),
+ "wasm stack map does not reference a valid insn");
+ }
+#endif
+
+ return js::MakeUnique<CodeTier>(std::move(metadataTier_), std::move(segment));
+}
+
+SharedMetadata ModuleGenerator::finishMetadata(const Bytes& bytecode) {
+ // Finish initialization of Metadata, which is only needed for constructing
+ // the initial Module, not for tier-2 compilation.
+ MOZ_ASSERT(mode() != CompileMode::Tier2);
+
+ // Copy over data from the ModuleEnvironment.
+
+ metadata_->memoryUsage = moduleEnv_->memoryUsage;
+ metadata_->minMemoryLength = moduleEnv_->minMemoryLength;
+ metadata_->maxMemoryLength = moduleEnv_->maxMemoryLength;
+ metadata_->startFuncIndex = moduleEnv_->startFuncIndex;
+ metadata_->tables = std::move(moduleEnv_->tables);
+ metadata_->globals = std::move(moduleEnv_->globals);
+#ifdef ENABLE_WASM_EXCEPTIONS
+ metadata_->events = std::move(moduleEnv_->events);
+#endif
+ metadata_->nameCustomSectionIndex = moduleEnv_->nameCustomSectionIndex;
+ metadata_->moduleName = moduleEnv_->moduleName;
+ metadata_->funcNames = std::move(moduleEnv_->funcNames);
+ metadata_->omitsBoundsChecks = moduleEnv_->hugeMemoryEnabled();
+ metadata_->v128Enabled = moduleEnv_->v128Enabled();
+ metadata_->usesDuplicateImports = moduleEnv_->usesDuplicateImports;
+
+ // Copy over additional debug information.
+
+ if (compilerEnv_->debugEnabled()) {
+ metadata_->debugEnabled = true;
+
+ const size_t numFuncs = moduleEnv_->funcs.length();
+ if (!metadata_->debugFuncArgTypes.resize(numFuncs)) {
+ return nullptr;
+ }
+ if (!metadata_->debugFuncReturnTypes.resize(numFuncs)) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < numFuncs; i++) {
+ if (!metadata_->debugFuncArgTypes[i].appendAll(
+ moduleEnv_->funcs[i].type->args())) {
+ return nullptr;
+ }
+ if (!metadata_->debugFuncReturnTypes[i].appendAll(
+ moduleEnv_->funcs[i].type->results())) {
+ return nullptr;
+ }
+ }
+
+ static_assert(sizeof(ModuleHash) <= sizeof(mozilla::SHA1Sum::Hash),
+ "The ModuleHash size shall not exceed the SHA1 hash size.");
+ mozilla::SHA1Sum::Hash hash;
+ mozilla::SHA1Sum sha1Sum;
+ sha1Sum.update(bytecode.begin(), bytecode.length());
+ sha1Sum.finish(hash);
+ memcpy(metadata_->debugHash, hash, sizeof(ModuleHash));
+ }
+
+ MOZ_ASSERT_IF(moduleEnv_->nameCustomSectionIndex, !!metadata_->namePayload);
+
+ // Metadata shouldn't be mutably modified after finishMetadata().
+ SharedMetadata metadata = metadata_;
+ metadata_ = nullptr;
+ return metadata;
+}
+
+SharedModule ModuleGenerator::finishModule(
+ const ShareableBytes& bytecode,
+ JS::OptimizedEncodingListener* maybeTier2Listener) {
+ MOZ_ASSERT(mode() == CompileMode::Once || mode() == CompileMode::Tier1);
+
+ UniqueCodeTier codeTier = finishCodeTier();
+ if (!codeTier) {
+ return nullptr;
+ }
+
+ JumpTables jumpTables;
+ if (!jumpTables.init(mode(), codeTier->segment(),
+ codeTier->metadata().codeRanges)) {
+ return nullptr;
+ }
+
+ // Copy over data from the Bytecode, which is going away at the end of
+ // compilation.
+
+ DataSegmentVector dataSegments;
+ if (!dataSegments.reserve(moduleEnv_->dataSegments.length())) {
+ return nullptr;
+ }
+ for (const DataSegmentEnv& srcSeg : moduleEnv_->dataSegments) {
+ MutableDataSegment dstSeg = js_new<DataSegment>(srcSeg);
+ if (!dstSeg) {
+ return nullptr;
+ }
+ if (!dstSeg->bytes.append(bytecode.begin() + srcSeg.bytecodeOffset,
+ srcSeg.length)) {
+ return nullptr;
+ }
+ dataSegments.infallibleAppend(std::move(dstSeg));
+ }
+
+ CustomSectionVector customSections;
+ if (!customSections.reserve(moduleEnv_->customSections.length())) {
+ return nullptr;
+ }
+ for (const CustomSectionEnv& srcSec : moduleEnv_->customSections) {
+ CustomSection sec;
+ if (!sec.name.append(bytecode.begin() + srcSec.nameOffset,
+ srcSec.nameLength)) {
+ return nullptr;
+ }
+ MutableBytes payload = js_new<ShareableBytes>();
+ if (!payload) {
+ return nullptr;
+ }
+ if (!payload->append(bytecode.begin() + srcSec.payloadOffset,
+ srcSec.payloadLength)) {
+ return nullptr;
+ }
+ sec.payload = std::move(payload);
+ customSections.infallibleAppend(std::move(sec));
+ }
+
+ if (moduleEnv_->nameCustomSectionIndex) {
+ metadata_->namePayload =
+ customSections[*moduleEnv_->nameCustomSectionIndex].payload;
+ }
+
+ SharedMetadata metadata = finishMetadata(bytecode.bytes);
+ if (!metadata) {
+ return nullptr;
+ }
+
+ MutableCode code =
+ js_new<Code>(std::move(codeTier), *metadata, std::move(jumpTables));
+ if (!code || !code->initialize(*linkData_)) {
+ return nullptr;
+ }
+
+ // See Module debugCodeClaimed_ comments for why we need to make a separate
+ // debug copy.
+
+ UniqueBytes debugUnlinkedCode;
+ UniqueLinkData debugLinkData;
+ const ShareableBytes* debugBytecode = nullptr;
+ if (compilerEnv_->debugEnabled()) {
+ MOZ_ASSERT(mode() == CompileMode::Once);
+ MOZ_ASSERT(tier() == Tier::Debug);
+
+ debugUnlinkedCode = js::MakeUnique<Bytes>();
+ if (!debugUnlinkedCode || !debugUnlinkedCode->resize(masm_.bytesNeeded())) {
+ return nullptr;
+ }
+
+ masm_.executableCopy(debugUnlinkedCode->begin());
+
+ debugLinkData = std::move(linkData_);
+ debugBytecode = &bytecode;
+ }
+
+ // All the components are finished, so create the complete Module and start
+ // tier-2 compilation if requested.
+
+ MutableModule module = js_new<Module>(
+ *code, std::move(moduleEnv_->imports), std::move(moduleEnv_->exports),
+ std::move(dataSegments), std::move(moduleEnv_->elemSegments),
+ std::move(customSections), std::move(debugUnlinkedCode),
+ std::move(debugLinkData), debugBytecode);
+ if (!module) {
+ return nullptr;
+ }
+
+ if (mode() == CompileMode::Tier1) {
+ module->startTier2(*compileArgs_, bytecode, maybeTier2Listener,
+ telemetrySender_);
+ } else if (tier() == Tier::Serialized && maybeTier2Listener) {
+ module->serialize(*linkData_, *maybeTier2Listener);
+ }
+
+ return module;
+}
+
+bool ModuleGenerator::finishTier2(const Module& module) {
+ MOZ_ASSERT(mode() == CompileMode::Tier2);
+ MOZ_ASSERT(tier() == Tier::Optimized);
+ MOZ_ASSERT(!compilerEnv_->debugEnabled());
+
+ if (cancelled_ && *cancelled_) {
+ return false;
+ }
+
+ UniqueCodeTier codeTier = finishCodeTier();
+ if (!codeTier) {
+ return false;
+ }
+
+ 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.
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ }
+
+ return module.finishTier2(*linkData_, std::move(codeTier));
+}
+
+size_t CompiledCode::sizeOfExcludingThis(
+ mozilla::MallocSizeOf mallocSizeOf) const {
+ size_t trapSitesSize = 0;
+ for (const TrapSiteVector& vec : trapSites) {
+ trapSitesSize += vec.sizeOfExcludingThis(mallocSizeOf);
+ }
+
+ return bytes.sizeOfExcludingThis(mallocSizeOf) +
+ codeRanges.sizeOfExcludingThis(mallocSizeOf) +
+ callSites.sizeOfExcludingThis(mallocSizeOf) +
+ callSiteTargets.sizeOfExcludingThis(mallocSizeOf) + trapSitesSize +
+ symbolicAccesses.sizeOfExcludingThis(mallocSizeOf) +
+ codeLabels.sizeOfExcludingThis(mallocSizeOf);
+}
+
+size_t CompileTask::sizeOfExcludingThis(
+ mozilla::MallocSizeOf mallocSizeOf) const {
+ return lifo.sizeOfExcludingThis(mallocSizeOf) +
+ inputs.sizeOfExcludingThis(mallocSizeOf) +
+ output.sizeOfExcludingThis(mallocSizeOf);
+}