diff options
Diffstat (limited to 'js/src/wasm/WasmCode.h')
-rw-r--r-- | js/src/wasm/WasmCode.h | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/js/src/wasm/WasmCode.h b/js/src/wasm/WasmCode.h new file mode 100644 index 000000000..db14ace40 --- /dev/null +++ b/js/src/wasm/WasmCode.h @@ -0,0 +1,554 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * 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 "wasm/WasmGeneratedSourceMap.h" +#include "wasm/WasmTypes.h" + +namespace js { + +struct AsmJSMetadata; + +namespace wasm { + +struct LinkData; +struct Metadata; + +// A wasm CodeSegment owns the allocated executable code for a wasm module. +// This allocation also currently includes the global data segment, which allows +// RIP-relative access to global data on some architectures, but this will +// change in the future to give global data its own allocation. + +class CodeSegment; +typedef UniquePtr<CodeSegment> UniqueCodeSegment; + +class CodeSegment +{ + // bytes_ points to a single allocation with two contiguous ranges: + // executable machine code in the range [0, codeLength) and global data in + // the range [codeLength, codeLength + globalDataLength). The range + // [0, functionCodeLength) is the subrange of [0, codeLength) which contains + // function code. + uint8_t* bytes_; + uint32_t functionCodeLength_; + uint32_t codeLength_; + uint32_t globalDataLength_; + + // These are pointers into code for stubs used for asynchronous + // signal-handler control-flow transfer. + uint8_t* interruptCode_; + uint8_t* outOfBoundsCode_; + uint8_t* unalignedAccessCode_; + + // The profiling mode may be changed dynamically. + bool profilingEnabled_; + + CodeSegment() { PodZero(this); } + template <class> friend struct js::MallocProvider; + + CodeSegment(const CodeSegment&) = delete; + CodeSegment(CodeSegment&&) = delete; + void operator=(const CodeSegment&) = delete; + void operator=(CodeSegment&&) = delete; + + public: + static UniqueCodeSegment create(JSContext* cx, + const Bytes& code, + const LinkData& linkData, + const Metadata& metadata, + HandleWasmMemoryObject memory); + ~CodeSegment(); + + uint8_t* base() const { return bytes_; } + uint8_t* globalData() const { return bytes_ + codeLength_; } + uint32_t codeLength() const { return codeLength_; } + uint32_t globalDataLength() const { return globalDataLength_; } + uint32_t totalLength() const { return codeLength_ + globalDataLength_; } + + uint8_t* interruptCode() const { return interruptCode_; } + uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; } + uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; } + + // The range [0, functionBytes) is a subrange of [0, codeBytes) that + // contains only function body code, not the stub code. This distinction is + // used by the async interrupt handler to only interrupt when the pc is in + // function code which, in turn, simplifies reasoning about how stubs + // enter/exit. + + bool containsFunctionPC(const void* pc) const { + return pc >= base() && pc < (base() + functionCodeLength_); + } + bool containsCodePC(const void* pc) const { + return pc >= base() && pc < (base() + codeLength_); + } + + // onMovingGrow must be called if the memory passed to 'create' performs a + // moving grow operation. + + void onMovingGrow(uint8_t* prevMemoryBase, const Metadata& metadata, ArrayBufferObject& buffer); +}; + +// ShareableBytes is a ref-counted vector of bytes which are incrementally built +// during compilation and then immutably shared. + +struct ShareableBytes : ShareableBase<ShareableBytes> +{ + // Vector is 'final', so instead make Vector a member and add boilerplate. + Bytes bytes; + size_t sizeOfExcludingThis(MallocSizeOf m) const { return bytes.sizeOfExcludingThis(m); } + const uint8_t* begin() const { return bytes.begin(); } + const uint8_t* end() const { return bytes.end(); } + size_t length() const { return bytes.length(); } + bool append(const uint8_t *p, uint32_t ct) { return bytes.append(p, ct); } +}; + +typedef RefPtr<ShareableBytes> MutableBytes; +typedef RefPtr<const ShareableBytes> SharedBytes; + +// 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 +{ + Sig sig_; + MOZ_INIT_OUTSIDE_CTOR struct CacheablePod { + uint32_t funcIndex_; + uint32_t codeRangeIndex_; + uint32_t entryOffset_; + } pod; + + public: + FuncExport() = default; + explicit FuncExport(Sig&& sig, + uint32_t funcIndex, + uint32_t codeRangeIndex) + : sig_(Move(sig)) + { + pod.funcIndex_ = funcIndex; + pod.codeRangeIndex_ = codeRangeIndex; + pod.entryOffset_ = UINT32_MAX; + } + void initEntryOffset(uint32_t entryOffset) { + MOZ_ASSERT(pod.entryOffset_ == UINT32_MAX); + pod.entryOffset_ = entryOffset; + } + + const Sig& sig() const { + return sig_; + } + uint32_t funcIndex() const { + return pod.funcIndex_; + } + uint32_t codeRangeIndex() const { + return pod.codeRangeIndex_; + } + uint32_t entryOffset() const { + MOZ_ASSERT(pod.entryOffset_ != UINT32_MAX); + return pod.entryOffset_; + } + + 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 +{ + Sig sig_; + struct CacheablePod { + uint32_t tlsDataOffset_; + uint32_t interpExitCodeOffset_; + uint32_t jitExitCodeOffset_; + } pod; + + public: + FuncImport() { + memset(&pod, 0, sizeof(CacheablePod)); + } + + FuncImport(Sig&& sig, uint32_t tlsDataOffset) + : sig_(Move(sig)) + { + 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 Sig& sig() const { + return sig_; + } + uint32_t tlsDataOffset() const { + return pod.tlsDataOffset_; + } + uint32_t interpExitCodeOffset() const { + return pod.interpExitCodeOffset_; + } + uint32_t jitExitCodeOffset() const { + return pod.jitExitCodeOffset_; + } + + WASM_DECLARE_SERIALIZABLE(FuncImport) +}; + +typedef Vector<FuncImport, 0, SystemAllocPolicy> FuncImportVector; + +// A CodeRange describes a single contiguous range of code within a wasm +// module's code segment. A CodeRange describes what the code does and, for +// function bodies, the name and source coordinates of the function. + +class CodeRange +{ + public: + enum Kind { + Function, // function definition + Entry, // calls into wasm from C++ + ImportJitExit, // fast-path calling from wasm into JIT code + ImportInterpExit, // slow-path calling from wasm into C++ interp + TrapExit, // calls C++ to report and jumps to throw stub + FarJumpIsland, // inserted to connect otherwise out-of-range insns + Inline // stub that is jumped-to, not called, and thus + // replaces/loses preceding innermost frame + }; + + private: + // All fields are treated as cacheable POD: + uint32_t begin_; + uint32_t profilingReturn_; + uint32_t end_; + uint32_t funcIndex_; + uint32_t funcLineOrBytecode_; + uint8_t funcBeginToTableEntry_; + uint8_t funcBeginToTableProfilingJump_; + uint8_t funcBeginToNonProfilingEntry_; + uint8_t funcProfilingJumpToProfilingReturn_; + uint8_t funcProfilingEpilogueToProfilingReturn_; + Kind kind_ : 8; + + public: + CodeRange() = default; + CodeRange(Kind kind, Offsets offsets); + CodeRange(Kind kind, ProfilingOffsets offsets); + CodeRange(uint32_t funcIndex, uint32_t lineOrBytecode, FuncOffsets offsets); + + // All CodeRanges have a begin and end. + + uint32_t begin() const { + return begin_; + } + uint32_t end() const { + return end_; + } + + // Other fields are only available for certain CodeRange::Kinds. + + Kind kind() const { + return kind_; + } + + bool isFunction() const { + return kind() == Function; + } + bool isImportExit() const { + return kind() == ImportJitExit || kind() == ImportInterpExit; + } + bool isTrapExit() const { + return kind() == TrapExit; + } + bool isInline() const { + return kind() == Inline; + } + + // Every CodeRange except entry and inline stubs has a profiling return + // which is used for asynchronous profiling to determine the frame pointer. + + uint32_t profilingReturn() const { + MOZ_ASSERT(isFunction() || isImportExit() || isTrapExit()); + return profilingReturn_; + } + + // Functions have offsets which allow patching to selectively execute + // profiling prologues/epilogues. + + uint32_t funcProfilingEntry() const { + MOZ_ASSERT(isFunction()); + return begin(); + } + uint32_t funcTableEntry() const { + MOZ_ASSERT(isFunction()); + return begin_ + funcBeginToTableEntry_; + } + uint32_t funcTableProfilingJump() const { + MOZ_ASSERT(isFunction()); + return begin_ + funcBeginToTableProfilingJump_; + } + uint32_t funcNonProfilingEntry() const { + MOZ_ASSERT(isFunction()); + return begin_ + funcBeginToNonProfilingEntry_; + } + uint32_t funcProfilingJump() const { + MOZ_ASSERT(isFunction()); + return profilingReturn_ - funcProfilingJumpToProfilingReturn_; + } + uint32_t funcProfilingEpilogue() const { + MOZ_ASSERT(isFunction()); + return profilingReturn_ - funcProfilingEpilogueToProfilingReturn_; + } + uint32_t funcIndex() const { + MOZ_ASSERT(isFunction()); + return funcIndex_; + } + uint32_t funcLineOrBytecode() const { + MOZ_ASSERT(isFunction()); + return funcLineOrBytecode_; + } + + // A sorted array of CodeRanges can be looked up via BinarySearch and PC. + + struct PC { + size_t offset; + explicit PC(size_t offset) : offset(offset) {} + bool operator==(const CodeRange& rhs) const { + return offset >= rhs.begin() && offset < rhs.end(); + } + bool operator<(const CodeRange& rhs) const { + return offset < rhs.begin(); + } + }; +}; + +WASM_DECLARE_POD_VECTOR(CodeRange, CodeRangeVector) + +// A CallThunk describes the offset and target of thunks so that they may be +// patched at runtime when profiling is toggled. Thunks are emitted to connect +// callsites that are too far away from callees to fit in a single call +// instruction's relative offset. + +struct CallThunk +{ + uint32_t offset; + union { + uint32_t funcIndex; + uint32_t codeRangeIndex; + } u; + + CallThunk(uint32_t offset, uint32_t funcIndex) : offset(offset) { u.funcIndex = funcIndex; } + CallThunk() = default; +}; + +WASM_DECLARE_POD_VECTOR(CallThunk, CallThunkVector) + +// A wasm module can either use no memory, a unshared memory (ArrayBuffer) or +// shared memory (SharedArrayBuffer). + +enum class MemoryUsage +{ + None = false, + Unshared = 1, + Shared = 2 +}; + +static inline bool +UsesMemory(MemoryUsage memoryUsage) +{ + return bool(memoryUsage); +} + +// NameInBytecode represents a name that is embedded in the wasm bytecode. +// The presence of NameInBytecode implies that bytecode has been kept. + +struct NameInBytecode +{ + uint32_t offset; + uint32_t length; + + NameInBytecode() = default; + NameInBytecode(uint32_t offset, uint32_t length) : offset(offset), length(length) {} +}; + +typedef Vector<NameInBytecode, 0, SystemAllocPolicy> NameInBytecodeVector; +typedef Vector<char16_t, 64> TwoByteName; + +// 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. + +struct MetadataCacheablePod +{ + ModuleKind kind; + MemoryUsage memoryUsage; + uint32_t minMemoryLength; + Maybe<uint32_t> maxMemoryLength; + Maybe<uint32_t> startFuncIndex; + + explicit MetadataCacheablePod(ModuleKind kind) + : kind(kind), + memoryUsage(MemoryUsage::None), + minMemoryLength(0) + {} +}; + +struct Metadata : ShareableBase<Metadata>, MetadataCacheablePod +{ + explicit Metadata(ModuleKind kind = ModuleKind::Wasm) : MetadataCacheablePod(kind) {} + virtual ~Metadata() {} + + MetadataCacheablePod& pod() { return *this; } + const MetadataCacheablePod& pod() const { return *this; } + + FuncImportVector funcImports; + FuncExportVector funcExports; + SigWithIdVector sigIds; + GlobalDescVector globals; + TableDescVector tables; + MemoryAccessVector memoryAccesses; + MemoryPatchVector memoryPatches; + BoundsCheckVector boundsChecks; + CodeRangeVector codeRanges; + CallSiteVector callSites; + CallThunkVector callThunks; + NameInBytecodeVector funcNames; + CacheableChars filename; + + 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 + // 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; + } + virtual bool getFuncName(JSContext* cx, const Bytes* maybeBytecode, uint32_t funcIndex, + TwoByteName* name) const; + + WASM_DECLARE_SERIALIZABLE_VIRTUAL(Metadata); +}; + +typedef RefPtr<Metadata> MutableMetadata; +typedef RefPtr<const Metadata> SharedMetadata; + +// Code objects own executable code and the metadata that describes it. At the +// moment, Code objects are owned uniquely by instances since CodeSegments are +// not shareable. However, once this restriction is removed, a single Code +// object will be shared between a module and all its instances. + +class Code +{ + const UniqueCodeSegment segment_; + const SharedMetadata metadata_; + const SharedBytes maybeBytecode_; + UniqueGeneratedSourceMap maybeSourceMap_; + CacheableCharsVector funcLabels_; + bool profilingEnabled_; + + public: + Code(UniqueCodeSegment segment, + const Metadata& metadata, + const ShareableBytes* maybeBytecode); + + CodeSegment& segment() { return *segment_; } + const CodeSegment& segment() const { return *segment_; } + const Metadata& metadata() const { return *metadata_; } + + // Frame iterator support: + + const CallSite* lookupCallSite(void* returnAddress) const; + const CodeRange* lookupRange(void* pc) const; + const MemoryAccess* lookupMemoryAccess(void* pc) const; + + // Return the name associated with a given function index, or generate one + // if none was given by the module. + + bool getFuncName(JSContext* cx, uint32_t funcIndex, TwoByteName* name) const; + JSAtom* getFuncAtom(JSContext* cx, uint32_t funcIndex) const; + + // If the source bytecode was saved when this Code was constructed, this + // method will render the binary as text. Otherwise, a diagnostic string + // will be returned. + + JSString* createText(JSContext* cx); + bool getLineOffsets(size_t lineno, Vector<uint32_t>& offsets) const; + + // Each Code has a profiling mode that is updated to match the runtime's + // profiling mode when there are no other activations of the code live on + // the stack. Once in profiling mode, ProfilingFrameIterator can be used to + // asynchronously walk the stack. Otherwise, the ProfilingFrameIterator will + // skip any activations of this code. + + MOZ_MUST_USE bool ensureProfilingState(JSContext* cx, bool enabled); + bool profilingEnabled() const { return profilingEnabled_; } + const char* profilingLabel(uint32_t funcIndex) const { return funcLabels_[funcIndex].get(); } + + // about:memory reporting: + + void addSizeOfMisc(MallocSizeOf mallocSizeOf, + Metadata::SeenSet* seenMetadata, + ShareableBytes::SeenSet* seenBytes, + size_t* code, + size_t* data) const; + + WASM_DECLARE_SERIALIZABLE(Code); +}; + +typedef UniquePtr<Code> UniqueCode; + +} // namespace wasm +} // namespace js + +#endif // wasm_code_h |