summaryrefslogtreecommitdiffstats
path: root/js/src/wasm/WasmCode.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/wasm/WasmCode.h767
1 files changed, 767 insertions, 0 deletions
diff --git a/js/src/wasm/WasmCode.h b/js/src/wasm/WasmCode.h
new file mode 100644
index 0000000000..29315e9ae3
--- /dev/null
+++ b/js/src/wasm/WasmCode.h
@@ -0,0 +1,767 @@
+/* -*- 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 2016 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.
+ */
+
+#ifndef wasm_code_h
+#define wasm_code_h
+
+#include "gc/Memory.h"
+#include "jit/JitOptions.h"
+#include "jit/shared/Assembler-shared.h"
+#include "js/HashTable.h"
+#include "threading/ExclusiveData.h"
+#include "util/Memory.h"
+#include "vm/MutexIDs.h"
+#include "wasm/WasmGC.h"
+#include "wasm/WasmTypes.h"
+
+namespace js {
+
+struct AsmJSMetadata;
+
+namespace wasm {
+
+struct MetadataTier;
+struct Metadata;
+
+// LinkData contains all the metadata necessary to patch all the locations
+// that depend on the absolute address of a ModuleSegment. This happens in a
+// "linking" step after compilation and after the module's code is serialized.
+// The LinkData is serialized along with the Module but does not (normally, see
+// Module::debugLinkData_ comment) persist after (de)serialization, which
+// distinguishes it from Metadata, which is stored in the Code object.
+
+struct LinkDataCacheablePod {
+ uint32_t trapOffset = 0;
+
+ LinkDataCacheablePod() = default;
+};
+
+struct LinkData : LinkDataCacheablePod {
+ const Tier tier;
+
+ explicit LinkData(Tier tier) : tier(tier) {}
+
+ LinkDataCacheablePod& pod() { return *this; }
+ const LinkDataCacheablePod& pod() const { return *this; }
+
+ struct InternalLink {
+ uint32_t patchAtOffset;
+ uint32_t targetOffset;
+#ifdef JS_CODELABEL_LINKMODE
+ uint32_t mode;
+#endif
+ };
+ typedef Vector<InternalLink, 0, SystemAllocPolicy> InternalLinkVector;
+
+ struct SymbolicLinkArray
+ : EnumeratedArray<SymbolicAddress, SymbolicAddress::Limit, Uint32Vector> {
+ WASM_DECLARE_SERIALIZABLE(SymbolicLinkArray)
+ };
+
+ InternalLinkVector internalLinks;
+ SymbolicLinkArray symbolicLinks;
+
+ WASM_DECLARE_SERIALIZABLE(LinkData)
+};
+
+using UniqueLinkData = UniquePtr<LinkData>;
+
+// Executable code must be deallocated specially.
+
+struct FreeCode {
+ uint32_t codeLength;
+ FreeCode() : codeLength(0) {}
+ explicit FreeCode(uint32_t codeLength) : codeLength(codeLength) {}
+ void operator()(uint8_t* codeBytes);
+};
+
+using UniqueCodeBytes = UniquePtr<uint8_t, FreeCode>;
+
+class Code;
+class CodeTier;
+class ModuleSegment;
+class LazyStubSegment;
+
+// CodeSegment contains common helpers for determining the base and length of a
+// code segment and if a pc belongs to this segment. It is inherited by:
+// - ModuleSegment, i.e. the code segment of a Module, generated
+// eagerly when a Module is instanciated.
+// - LazyStubSegment, i.e. the code segment of entry stubs that are lazily
+// generated.
+
+class CodeSegment {
+ protected:
+ static UniqueCodeBytes AllocateCodeBytes(uint32_t codeLength);
+
+ enum class Kind { LazyStubs, Module };
+
+ CodeSegment(UniqueCodeBytes bytes, uint32_t length, Kind kind)
+ : bytes_(std::move(bytes)),
+ length_(length),
+ kind_(kind),
+ codeTier_(nullptr),
+ unregisterOnDestroy_(false) {}
+
+ bool initialize(const CodeTier& codeTier);
+
+ private:
+ const UniqueCodeBytes bytes_;
+ const uint32_t length_;
+ const Kind kind_;
+ const CodeTier* codeTier_;
+ bool unregisterOnDestroy_;
+
+ public:
+ bool initialized() const { return !!codeTier_; }
+ ~CodeSegment();
+
+ bool isLazyStubs() const { return kind_ == Kind::LazyStubs; }
+ bool isModule() const { return kind_ == Kind::Module; }
+ const ModuleSegment* asModule() const {
+ MOZ_ASSERT(isModule());
+ return (ModuleSegment*)this;
+ }
+ const LazyStubSegment* asLazyStub() const {
+ MOZ_ASSERT(isLazyStubs());
+ return (LazyStubSegment*)this;
+ }
+
+ uint8_t* base() const { return bytes_.get(); }
+ uint32_t length() const {
+ MOZ_ASSERT(length_ != UINT32_MAX);
+ return length_;
+ }
+
+ bool containsCodePC(const void* pc) const {
+ return pc >= base() && pc < (base() + length_);
+ }
+
+ const CodeTier& codeTier() const {
+ MOZ_ASSERT(initialized());
+ return *codeTier_;
+ }
+ const Code& code() const;
+
+ void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code) const;
+};
+
+// A wasm ModuleSegment owns the allocated executable code for a wasm module.
+
+using UniqueModuleSegment = UniquePtr<ModuleSegment>;
+
+enum IsTier2 { Tier2, NotTier2 };
+
+class ModuleSegment : public CodeSegment {
+ const Tier tier_;
+ uint8_t* const trapCode_;
+
+ public:
+ ModuleSegment(Tier tier, UniqueCodeBytes codeBytes, uint32_t codeLength,
+ const LinkData& linkData);
+
+ static UniqueModuleSegment create(Tier tier, jit::MacroAssembler& masm,
+ const LinkData& linkData);
+ static UniqueModuleSegment create(Tier tier, const Bytes& unlinkedBytes,
+ const LinkData& linkData);
+
+ bool initialize(IsTier2 compileMode, const CodeTier& codeTier,
+ const LinkData& linkData, const Metadata& metadata,
+ const MetadataTier& metadataTier);
+
+ Tier tier() const { return tier_; }
+
+ // Pointers to stubs to which PC is redirected from the signal-handler.
+
+ uint8_t* trapCode() const { return trapCode_; }
+
+ // Structured clone support:
+
+ size_t serializedSize() const;
+ uint8_t* serialize(uint8_t* cursor, const LinkData& linkData) const;
+ static const uint8_t* deserialize(const uint8_t* cursor,
+ const LinkData& linkData,
+ UniqueModuleSegment* segment);
+
+ const CodeRange* lookupRange(const void* pc) const;
+
+ void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code,
+ size_t* data) const;
+};
+
+// A FuncExport represents a single function definition inside a wasm Module
+// that has been exported one or more times. A FuncExport represents an
+// internal entry point that can be called via function definition index by
+// Instance::callExport(). To allow O(log(n)) lookup of a FuncExport by
+// function definition index, the FuncExportVector is stored sorted by
+// function definition index.
+
+class FuncExport {
+ FuncType funcType_;
+ MOZ_INIT_OUTSIDE_CTOR struct CacheablePod {
+ uint32_t funcIndex_;
+ uint32_t eagerInterpEntryOffset_; // Machine code offset
+ bool hasEagerStubs_;
+ } pod;
+
+ public:
+ FuncExport() = default;
+ explicit FuncExport(FuncType&& funcType, uint32_t funcIndex,
+ bool hasEagerStubs)
+ : funcType_(std::move(funcType)) {
+ pod.funcIndex_ = funcIndex;
+ pod.eagerInterpEntryOffset_ = UINT32_MAX;
+ pod.hasEagerStubs_ = hasEagerStubs;
+ }
+ void initEagerInterpEntryOffset(uint32_t entryOffset) {
+ MOZ_ASSERT(pod.eagerInterpEntryOffset_ == UINT32_MAX);
+ MOZ_ASSERT(hasEagerStubs());
+ pod.eagerInterpEntryOffset_ = entryOffset;
+ }
+
+ bool hasEagerStubs() const { return pod.hasEagerStubs_; }
+ const FuncType& funcType() const { return funcType_; }
+ uint32_t funcIndex() const { return pod.funcIndex_; }
+ uint32_t eagerInterpEntryOffset() const {
+ MOZ_ASSERT(pod.eagerInterpEntryOffset_ != UINT32_MAX);
+ MOZ_ASSERT(hasEagerStubs());
+ return pod.eagerInterpEntryOffset_;
+ }
+
+ bool canHaveJitEntry() const {
+ return !funcType_.hasUnexposableArgOrRet() &&
+ !funcType_.temporarilyUnsupportedReftypeForEntry() &&
+ !funcType_.temporarilyUnsupportedResultCountForJitEntry() &&
+ JitOptions.enableWasmJitEntry;
+ }
+
+ bool clone(const FuncExport& src) {
+ mozilla::PodAssign(&pod, &src.pod);
+ return funcType_.clone(src.funcType_);
+ }
+
+ WASM_DECLARE_SERIALIZABLE(FuncExport)
+};
+
+typedef Vector<FuncExport, 0, SystemAllocPolicy> FuncExportVector;
+
+// An FuncImport contains the runtime metadata needed to implement a call to an
+// imported function. Each function import has two call stubs: an optimized path
+// into JIT code and a slow path into the generic C++ js::Invoke and these
+// offsets of these stubs are stored so that function-import callsites can be
+// dynamically patched at runtime.
+
+class FuncImport {
+ FuncType funcType_;
+ struct CacheablePod {
+ uint32_t tlsDataOffset_;
+ uint32_t interpExitCodeOffset_; // Machine code offset
+ uint32_t jitExitCodeOffset_; // Machine code offset
+ } pod;
+
+ public:
+ FuncImport() { memset(&pod, 0, sizeof(CacheablePod)); }
+
+ FuncImport(FuncType&& funcType, uint32_t tlsDataOffset)
+ : funcType_(std::move(funcType)) {
+ pod.tlsDataOffset_ = tlsDataOffset;
+ pod.interpExitCodeOffset_ = 0;
+ pod.jitExitCodeOffset_ = 0;
+ }
+
+ void initInterpExitOffset(uint32_t off) {
+ MOZ_ASSERT(!pod.interpExitCodeOffset_);
+ pod.interpExitCodeOffset_ = off;
+ }
+ void initJitExitOffset(uint32_t off) {
+ MOZ_ASSERT(!pod.jitExitCodeOffset_);
+ pod.jitExitCodeOffset_ = off;
+ }
+
+ const FuncType& funcType() const { return funcType_; }
+ uint32_t tlsDataOffset() const { return pod.tlsDataOffset_; }
+ uint32_t interpExitCodeOffset() const { return pod.interpExitCodeOffset_; }
+ uint32_t jitExitCodeOffset() const { return pod.jitExitCodeOffset_; }
+
+ bool clone(const FuncImport& src) {
+ mozilla::PodAssign(&pod, &src.pod);
+ return funcType_.clone(src.funcType_);
+ }
+
+ WASM_DECLARE_SERIALIZABLE(FuncImport)
+};
+
+typedef Vector<FuncImport, 0, SystemAllocPolicy> FuncImportVector;
+
+// Metadata holds all the data that is needed to describe compiled wasm code
+// at runtime (as opposed to data that is only used to statically link or
+// instantiate a module).
+//
+// Metadata is built incrementally by ModuleGenerator and then shared immutably
+// between modules.
+//
+// The Metadata structure is split into tier-invariant and tier-variant parts;
+// the former points to instances of the latter. Additionally, the asm.js
+// subsystem subclasses the Metadata, adding more tier-invariant data, some of
+// which is serialized. See AsmJS.cpp.
+
+struct MetadataCacheablePod {
+ ModuleKind kind;
+ MemoryUsage memoryUsage;
+ uint64_t minMemoryLength;
+ uint32_t globalDataLength;
+ Maybe<uint64_t> maxMemoryLength;
+ Maybe<uint32_t> startFuncIndex;
+ Maybe<uint32_t> nameCustomSectionIndex;
+ bool filenameIsURL;
+ bool v128Enabled;
+ bool omitsBoundsChecks;
+ bool usesDuplicateImports;
+
+ explicit MetadataCacheablePod(ModuleKind kind)
+ : kind(kind),
+ memoryUsage(MemoryUsage::None),
+ minMemoryLength(0),
+ globalDataLength(0),
+ filenameIsURL(false),
+ v128Enabled(false),
+ omitsBoundsChecks(false),
+ usesDuplicateImports(false) {}
+};
+
+typedef uint8_t ModuleHash[8];
+typedef Vector<ValTypeVector, 0, SystemAllocPolicy> FuncArgTypesVector;
+typedef Vector<ValTypeVector, 0, SystemAllocPolicy> FuncReturnTypesVector;
+
+struct Metadata : public ShareableBase<Metadata>, public MetadataCacheablePod {
+ TypeDefWithIdVector types;
+ GlobalDescVector globals;
+ TableDescVector tables;
+#ifdef ENABLE_WASM_EXCEPTIONS
+ EventDescVector events;
+#endif
+ CacheableChars filename;
+ CacheableChars sourceMapURL;
+
+ // namePayload points at the name section's CustomSection::payload so that
+ // the Names (which are use payload-relative offsets) can be used
+ // independently of the Module without duplicating the name section.
+ SharedBytes namePayload;
+ Maybe<Name> moduleName;
+ NameVector funcNames;
+
+ // Debug-enabled code is not serialized.
+ bool debugEnabled;
+ FuncArgTypesVector debugFuncArgTypes;
+ FuncReturnTypesVector debugFuncReturnTypes;
+ ModuleHash debugHash;
+
+ explicit Metadata(ModuleKind kind = ModuleKind::Wasm)
+ : MetadataCacheablePod(kind), debugEnabled(false), debugHash() {}
+ virtual ~Metadata() = default;
+
+ MetadataCacheablePod& pod() { return *this; }
+ const MetadataCacheablePod& pod() const { return *this; }
+
+ bool usesMemory() const { return memoryUsage != MemoryUsage::None; }
+ bool usesSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
+
+ // Invariant: The result of getFuncResultType can only be used as long as
+ // MetaData is live, because the returned ResultType may encode a pointer to
+ // debugFuncReturnTypes.
+ ResultType getFuncResultType(uint32_t funcIndex) const {
+ return ResultType::Vector(debugFuncReturnTypes[funcIndex]);
+ };
+
+ // 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
+ // easily encapsulated by AsmJS.cpp.
+
+ bool isAsmJS() const { return kind == ModuleKind::AsmJS; }
+ const AsmJSMetadata& asAsmJS() const {
+ MOZ_ASSERT(isAsmJS());
+ return *(const AsmJSMetadata*)this;
+ }
+ virtual bool mutedErrors() const { return false; }
+ virtual const char16_t* displayURL() const { return nullptr; }
+ virtual ScriptSource* maybeScriptSource() const { return nullptr; }
+
+ // The Developer-Facing Display Conventions section of the WebAssembly Web
+ // API spec defines two cases for displaying a wasm function name:
+ // 1. the function name stands alone
+ // 2. the function name precedes the location
+
+ enum NameContext { Standalone, BeforeLocation };
+
+ virtual bool getFuncName(NameContext ctx, uint32_t funcIndex,
+ UTF8Bytes* name) const;
+
+ bool getFuncNameStandalone(uint32_t funcIndex, UTF8Bytes* name) const {
+ return getFuncName(NameContext::Standalone, funcIndex, name);
+ }
+ bool getFuncNameBeforeLocation(uint32_t funcIndex, UTF8Bytes* name) const {
+ return getFuncName(NameContext::BeforeLocation, funcIndex, name);
+ }
+
+ WASM_DECLARE_SERIALIZABLE(Metadata);
+};
+
+using MutableMetadata = RefPtr<Metadata>;
+using SharedMetadata = RefPtr<const Metadata>;
+
+struct MetadataTier {
+ explicit MetadataTier(Tier tier) : tier(tier) {}
+
+ const Tier tier;
+
+ Uint32Vector funcToCodeRange;
+ CodeRangeVector codeRanges;
+ CallSiteVector callSites;
+ TrapSiteVectorArray trapSites;
+ FuncImportVector funcImports;
+ FuncExportVector funcExports;
+ StackMaps stackMaps;
+
+ // Debug information, not serialized.
+ Uint32Vector debugTrapFarJumpOffsets;
+
+ FuncExport& lookupFuncExport(uint32_t funcIndex,
+ size_t* funcExportIndex = nullptr);
+ const FuncExport& lookupFuncExport(uint32_t funcIndex,
+ size_t* funcExportIndex = nullptr) const;
+
+ const CodeRange& codeRange(const FuncExport& funcExport) const {
+ return codeRanges[funcToCodeRange[funcExport.funcIndex()]];
+ }
+
+ bool clone(const MetadataTier& src);
+
+ WASM_DECLARE_SERIALIZABLE(MetadataTier);
+};
+
+using UniqueMetadataTier = UniquePtr<MetadataTier>;
+
+// LazyStubSegment is a code segment lazily generated for function entry stubs
+// (both interpreter and jit ones).
+//
+// Because a stub is usually small (a few KiB) and an executable code segment
+// isn't (64KiB), a given stub segment can contain entry stubs of many
+// functions.
+
+using UniqueLazyStubSegment = UniquePtr<LazyStubSegment>;
+using LazyStubSegmentVector =
+ Vector<UniqueLazyStubSegment, 0, SystemAllocPolicy>;
+
+class LazyStubSegment : public CodeSegment {
+ CodeRangeVector codeRanges_;
+ size_t usedBytes_;
+
+ public:
+ LazyStubSegment(UniqueCodeBytes bytes, size_t length)
+ : CodeSegment(std::move(bytes), length, CodeSegment::Kind::LazyStubs),
+ usedBytes_(0) {}
+
+ static UniqueLazyStubSegment create(const CodeTier& codeTier,
+ size_t codeLength);
+
+ static size_t AlignBytesNeeded(size_t bytes) {
+ return AlignBytes(bytes, gc::SystemPageSize());
+ }
+
+ bool hasSpace(size_t bytes) const;
+ bool addStubs(size_t codeLength, const Uint32Vector& funcExportIndices,
+ const FuncExportVector& funcExports,
+ const CodeRangeVector& codeRanges, uint8_t** codePtr,
+ size_t* indexFirstInsertedCodeRange);
+
+ const CodeRangeVector& codeRanges() const { return codeRanges_; }
+ const CodeRange* lookupRange(const void* pc) const;
+
+ void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code,
+ size_t* data) const;
+};
+
+// LazyFuncExport helps to efficiently lookup a CodeRange from a given function
+// index. It is inserted in a vector sorted by function index, to perform
+// binary search on it later.
+
+struct LazyFuncExport {
+ size_t funcIndex;
+ size_t lazyStubSegmentIndex;
+ size_t funcCodeRangeIndex;
+ LazyFuncExport(size_t funcIndex, size_t lazyStubSegmentIndex,
+ size_t funcCodeRangeIndex)
+ : funcIndex(funcIndex),
+ lazyStubSegmentIndex(lazyStubSegmentIndex),
+ funcCodeRangeIndex(funcCodeRangeIndex) {}
+};
+
+using LazyFuncExportVector = Vector<LazyFuncExport, 0, SystemAllocPolicy>;
+
+// LazyStubTier contains all the necessary information for lazy function entry
+// stubs that are generated at runtime. None of its data is ever serialized.
+//
+// It must be protected by a lock, because the main thread can both read and
+// write lazy stubs at any time while a background thread can regenerate lazy
+// stubs for tier2 at any time.
+
+class LazyStubTier {
+ LazyStubSegmentVector stubSegments_;
+ LazyFuncExportVector exports_;
+ size_t lastStubSegmentIndex_;
+
+ bool createMany(const Uint32Vector& funcExportIndices,
+ const CodeTier& codeTier, bool flushAllThreadsIcaches,
+ size_t* stubSegmentIndex);
+
+ public:
+ LazyStubTier() : lastStubSegmentIndex_(0) {}
+
+ bool empty() const { return stubSegments_.empty(); }
+ bool hasStub(uint32_t funcIndex) const;
+
+ // Returns a pointer to the raw interpreter entry of a given function which
+ // stubs have been lazily generated.
+ void* lookupInterpEntry(uint32_t funcIndex) const;
+
+ // Creates one lazy stub for the exported function, for which the jit entry
+ // will be set to the lazily-generated one.
+ bool createOne(uint32_t funcExportIndex, const CodeTier& codeTier);
+
+ // Create one lazy stub for all the functions in funcExportIndices, putting
+ // them in a single stub. Jit entries won't be used until
+ // setJitEntries() is actually called, after the Code owner has committed
+ // tier2.
+ bool createTier2(const Uint32Vector& funcExportIndices,
+ const CodeTier& codeTier, Maybe<size_t>* stubSegmentIndex);
+ void setJitEntries(const Maybe<size_t>& stubSegmentIndex, const Code& code);
+
+ void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code,
+ size_t* data) const;
+};
+
+// CodeTier contains all the data related to a given compilation tier. It is
+// built during module generation and then immutably stored in a Code.
+
+using UniqueCodeTier = UniquePtr<CodeTier>;
+using UniqueConstCodeTier = UniquePtr<const CodeTier>;
+
+class CodeTier {
+ const Code* code_;
+
+ // Serialized information.
+ const UniqueMetadataTier metadata_;
+ const UniqueModuleSegment segment_;
+
+ // Lazy stubs, not serialized.
+ ExclusiveData<LazyStubTier> lazyStubs_;
+
+ static const MutexId& mutexForTier(Tier tier) {
+ if (tier == Tier::Baseline) {
+ return mutexid::WasmLazyStubsTier1;
+ }
+ MOZ_ASSERT(tier == Tier::Optimized);
+ return mutexid::WasmLazyStubsTier2;
+ }
+
+ public:
+ CodeTier(UniqueMetadataTier metadata, UniqueModuleSegment segment)
+ : code_(nullptr),
+ metadata_(std::move(metadata)),
+ segment_(std::move(segment)),
+ lazyStubs_(mutexForTier(segment_->tier())) {}
+
+ bool initialized() const { return !!code_ && segment_->initialized(); }
+ bool initialize(IsTier2 isTier2, const Code& code, const LinkData& linkData,
+ const Metadata& metadata);
+
+ Tier tier() const { return segment_->tier(); }
+ const ExclusiveData<LazyStubTier>& lazyStubs() const { return lazyStubs_; }
+ const MetadataTier& metadata() const { return *metadata_.get(); }
+ const ModuleSegment& segment() const { return *segment_.get(); }
+ const Code& code() const {
+ MOZ_ASSERT(initialized());
+ return *code_;
+ }
+
+ const CodeRange* lookupRange(const void* pc) const;
+
+ size_t serializedSize() const;
+ uint8_t* serialize(uint8_t* cursor, const LinkData& linkData) const;
+ static const uint8_t* deserialize(const uint8_t* cursor,
+ const LinkData& linkData,
+ UniqueCodeTier* codeTier);
+ void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code,
+ size_t* data) const;
+};
+
+// Jump tables to take tiering into account, when calling either from wasm to
+// wasm (through rabaldr) or from jit to wasm (jit entry).
+
+class JumpTables {
+ using TablePointer = mozilla::UniquePtr<void*[], JS::FreePolicy>;
+
+ CompileMode mode_;
+ TablePointer tiering_;
+ TablePointer jit_;
+ size_t numFuncs_;
+
+ public:
+ bool init(CompileMode mode, const ModuleSegment& ms,
+ const CodeRangeVector& codeRanges);
+
+ void setJitEntry(size_t i, void* target) const {
+ // Make sure that write is atomic; see comment in wasm::Module::finishTier2
+ // to that effect.
+ MOZ_ASSERT(i < numFuncs_);
+ jit_.get()[i] = target;
+ }
+ void setJitEntryIfNull(size_t i, void* target) const {
+ // Make sure that compare-and-write is atomic; see comment in
+ // wasm::Module::finishTier2 to that effect.
+ MOZ_ASSERT(i < numFuncs_);
+ void* expected = nullptr;
+ (void)__atomic_compare_exchange_n(&jit_.get()[i], &expected, target,
+ /*weak=*/false, __ATOMIC_RELAXED,
+ __ATOMIC_RELAXED);
+ }
+ void** getAddressOfJitEntry(size_t i) const {
+ MOZ_ASSERT(i < numFuncs_);
+ MOZ_ASSERT(jit_.get()[i]);
+ return &jit_.get()[i];
+ }
+ size_t funcIndexFromJitEntry(void** target) const {
+ MOZ_ASSERT(target >= &jit_.get()[0]);
+ MOZ_ASSERT(target <= &(jit_.get()[numFuncs_ - 1]));
+ return (intptr_t*)target - (intptr_t*)&jit_.get()[0];
+ }
+
+ void setTieringEntry(size_t i, void* target) const {
+ MOZ_ASSERT(i < numFuncs_);
+ // See comment in wasm::Module::finishTier2.
+ if (mode_ == CompileMode::Tier1) {
+ tiering_.get()[i] = target;
+ }
+ }
+ void** tiering() const { return tiering_.get(); }
+
+ size_t sizeOfMiscExcludingThis() const {
+ // 2 words per function for the jit entry table, plus maybe 1 per
+ // function if we're tiering.
+ return sizeof(void*) * (2 + (tiering_ ? 1 : 0)) * numFuncs_;
+ }
+};
+
+// Code objects own executable code and the metadata that describe it. A single
+// Code object is normally shared between a module and all its instances.
+//
+// profilingLabels_ is lazily initialized, but behind a lock.
+
+using SharedCode = RefPtr<const Code>;
+using MutableCode = RefPtr<Code>;
+
+class Code : public ShareableBase<Code> {
+ UniqueCodeTier tier1_;
+ mutable UniqueConstCodeTier tier2_; // Access only when hasTier2() is true
+ mutable Atomic<bool> hasTier2_;
+ SharedMetadata metadata_;
+ ExclusiveData<CacheableCharsVector> profilingLabels_;
+ JumpTables jumpTables_;
+
+ public:
+ Code(UniqueCodeTier tier1, const Metadata& metadata,
+ JumpTables&& maybeJumpTables);
+ bool initialized() const { return tier1_->initialized(); }
+
+ bool initialize(const LinkData& linkData);
+
+ void setTieringEntry(size_t i, void* target) const {
+ jumpTables_.setTieringEntry(i, target);
+ }
+ void** tieringJumpTable() const { return jumpTables_.tiering(); }
+
+ void setJitEntry(size_t i, void* target) const {
+ jumpTables_.setJitEntry(i, target);
+ }
+ void setJitEntryIfNull(size_t i, void* target) const {
+ jumpTables_.setJitEntryIfNull(i, target);
+ }
+ void** getAddressOfJitEntry(size_t i) const {
+ return jumpTables_.getAddressOfJitEntry(i);
+ }
+ uint32_t getFuncIndex(JSFunction* fun) const;
+
+ bool setTier2(UniqueCodeTier tier2, const LinkData& linkData) const;
+ void commitTier2() const;
+
+ bool hasTier2() const { return hasTier2_; }
+ Tiers tiers() const;
+ bool hasTier(Tier t) const;
+
+ Tier stableTier() const; // This is stable during a run
+ Tier bestTier()
+ const; // This may transition from Baseline -> Ion at any time
+
+ const CodeTier& codeTier(Tier tier) const;
+ const Metadata& metadata() const { return *metadata_; }
+
+ const ModuleSegment& segment(Tier iter) const {
+ return codeTier(iter).segment();
+ }
+ const MetadataTier& metadata(Tier iter) const {
+ return codeTier(iter).metadata();
+ }
+
+ // Metadata lookup functions:
+
+ const CallSite* lookupCallSite(void* returnAddress) const;
+ const CodeRange* lookupFuncRange(void* pc) const;
+ const StackMap* lookupStackMap(uint8_t* nextPC) const;
+ bool containsCodePC(const void* pc) const;
+ bool lookupTrap(void* pc, Trap* trap, BytecodeOffset* bytecode) const;
+
+ // To save memory, profilingLabels_ are generated lazily when profiling mode
+ // is enabled.
+
+ void ensureProfilingLabels(bool profilingEnabled) const;
+ const char* profilingLabel(uint32_t funcIndex) const;
+
+ // about:memory reporting:
+
+ void addSizeOfMiscIfNotSeen(MallocSizeOf mallocSizeOf,
+ Metadata::SeenSet* seenMetadata,
+ Code::SeenSet* seenCode, size_t* code,
+ size_t* data) const;
+
+ // A Code object is serialized as the length and bytes of the machine code
+ // after statically unlinking it; the Code is then later recreated from the
+ // machine code and other parts.
+
+ size_t serializedSize() const;
+ uint8_t* serialize(uint8_t* cursor, const LinkData& linkData) const;
+ static const uint8_t* deserialize(const uint8_t* cursor,
+ const LinkData& linkData,
+ Metadata& metadata, SharedCode* code);
+};
+
+void PatchDebugSymbolicAccesses(uint8_t* codeBase, jit::MacroAssembler& masm);
+
+} // namespace wasm
+} // namespace js
+
+#endif // wasm_code_h