diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/jit/shared/Assembler-shared.h | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/jit/shared/Assembler-shared.h')
-rw-r--r-- | js/src/jit/shared/Assembler-shared.h | 991 |
1 files changed, 991 insertions, 0 deletions
diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h new file mode 100644 index 000000000..aac9687b8 --- /dev/null +++ b/js/src/jit/shared/Assembler-shared.h @@ -0,0 +1,991 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_shared_Assembler_shared_h +#define jit_shared_Assembler_shared_h + +#include "mozilla/PodOperations.h" + +#include <limits.h> + +#include "jit/AtomicOp.h" +#include "jit/JitAllocPolicy.h" +#include "jit/Label.h" +#include "jit/Registers.h" +#include "jit/RegisterSets.h" +#include "vm/HelperThreads.h" +#include "wasm/WasmTypes.h" + +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ + defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) +// Push return addresses callee-side. +# define JS_USE_LINK_REGISTER +#endif + +#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) +// JS_SMALL_BRANCH means the range on a branch instruction +// is smaller than the whole address space +# define JS_SMALL_BRANCH +#endif + +namespace js { +namespace jit { + +namespace Disassembler { +class HeapAccess; +} // namespace Disassembler + +static const uint32_t Simd128DataSize = 4 * sizeof(int32_t); +static_assert(Simd128DataSize == 4 * sizeof(int32_t), "SIMD data should be able to contain int32x4"); +static_assert(Simd128DataSize == 4 * sizeof(float), "SIMD data should be able to contain float32x4"); +static_assert(Simd128DataSize == 2 * sizeof(double), "SIMD data should be able to contain float64x2"); + +enum Scale { + TimesOne = 0, + TimesTwo = 1, + TimesFour = 2, + TimesEight = 3 +}; + +static_assert(sizeof(JS::Value) == 8, + "required for TimesEight and 3 below to be correct"); +static const Scale ValueScale = TimesEight; +static const size_t ValueShift = 3; + +static inline unsigned +ScaleToShift(Scale scale) +{ + return unsigned(scale); +} + +static inline bool +IsShiftInScaleRange(int i) +{ + return i >= TimesOne && i <= TimesEight; +} + +static inline Scale +ShiftToScale(int i) +{ + MOZ_ASSERT(IsShiftInScaleRange(i)); + return Scale(i); +} + +static inline Scale +ScaleFromElemWidth(int shift) +{ + switch (shift) { + case 1: + return TimesOne; + case 2: + return TimesTwo; + case 4: + return TimesFour; + case 8: + return TimesEight; + } + + MOZ_CRASH("Invalid scale"); +} + +// Used for 32-bit immediates which do not require relocation. +struct Imm32 +{ + int32_t value; + + explicit Imm32(int32_t value) : value(value) + { } + + static inline Imm32 ShiftOf(enum Scale s) { + switch (s) { + case TimesOne: + return Imm32(0); + case TimesTwo: + return Imm32(1); + case TimesFour: + return Imm32(2); + case TimesEight: + return Imm32(3); + }; + MOZ_CRASH("Invalid scale"); + } + + static inline Imm32 FactorOf(enum Scale s) { + return Imm32(1 << ShiftOf(s).value); + } +}; + +// Pointer-sized integer to be embedded as an immediate in an instruction. +struct ImmWord +{ + uintptr_t value; + + explicit ImmWord(uintptr_t value) : value(value) + { } +}; + +// Used for 64-bit immediates which do not require relocation. +struct Imm64 +{ + uint64_t value; + + explicit Imm64(int64_t value) : value(value) + { } + + Imm32 low() const { + return Imm32(int32_t(value)); + } + + Imm32 hi() const { + return Imm32(int32_t(value >> 32)); + } + + inline Imm32 firstHalf() const; + inline Imm32 secondHalf() const; +}; + +#ifdef DEBUG +static inline bool +IsCompilingWasm() +{ + // wasm compilation pushes a JitContext with a null JSCompartment. + return GetJitContext()->compartment == nullptr; +} +#endif + +// Pointer to be embedded as an immediate in an instruction. +struct ImmPtr +{ + void* value; + + explicit ImmPtr(const void* value) : value(const_cast<void*>(value)) + { + // To make code serialization-safe, wasm compilation should only + // compile pointer immediates using a SymbolicAddress. + MOZ_ASSERT(!IsCompilingWasm()); + } + + template <class R> + explicit ImmPtr(R (*pf)()) + : value(JS_FUNC_TO_DATA_PTR(void*, pf)) + { + MOZ_ASSERT(!IsCompilingWasm()); + } + + template <class R, class A1> + explicit ImmPtr(R (*pf)(A1)) + : value(JS_FUNC_TO_DATA_PTR(void*, pf)) + { + MOZ_ASSERT(!IsCompilingWasm()); + } + + template <class R, class A1, class A2> + explicit ImmPtr(R (*pf)(A1, A2)) + : value(JS_FUNC_TO_DATA_PTR(void*, pf)) + { + MOZ_ASSERT(!IsCompilingWasm()); + } + + template <class R, class A1, class A2, class A3> + explicit ImmPtr(R (*pf)(A1, A2, A3)) + : value(JS_FUNC_TO_DATA_PTR(void*, pf)) + { + MOZ_ASSERT(!IsCompilingWasm()); + } + + template <class R, class A1, class A2, class A3, class A4> + explicit ImmPtr(R (*pf)(A1, A2, A3, A4)) + : value(JS_FUNC_TO_DATA_PTR(void*, pf)) + { + MOZ_ASSERT(!IsCompilingWasm()); + } + +}; + +// The same as ImmPtr except that the intention is to patch this +// instruction. The initial value of the immediate is 'addr' and this value is +// either clobbered or used in the patching process. +struct PatchedImmPtr { + void* value; + + explicit PatchedImmPtr() + : value(nullptr) + { } + explicit PatchedImmPtr(const void* value) + : value(const_cast<void*>(value)) + { } +}; + +class AssemblerShared; +class ImmGCPtr; + +// Used for immediates which require relocation. +class ImmGCPtr +{ + public: + const gc::Cell* value; + + explicit ImmGCPtr(const gc::Cell* ptr) : value(ptr) + { + // Nursery pointers can't be used if the main thread might be currently + // performing a minor GC. + MOZ_ASSERT_IF(ptr && !ptr->isTenured(), + !CurrentThreadIsIonCompilingSafeForMinorGC()); + + // wasm shouldn't be creating GC things + MOZ_ASSERT(!IsCompilingWasm()); + } + + private: + ImmGCPtr() : value(0) {} +}; + +// Pointer to be embedded as an immediate that is loaded/stored from by an +// instruction. +struct AbsoluteAddress +{ + void* addr; + + explicit AbsoluteAddress(const void* addr) + : addr(const_cast<void*>(addr)) + { + MOZ_ASSERT(!IsCompilingWasm()); + } + + AbsoluteAddress offset(ptrdiff_t delta) { + return AbsoluteAddress(((uint8_t*) addr) + delta); + } +}; + +// The same as AbsoluteAddress except that the intention is to patch this +// instruction. The initial value of the immediate is 'addr' and this value is +// either clobbered or used in the patching process. +struct PatchedAbsoluteAddress +{ + void* addr; + + explicit PatchedAbsoluteAddress() + : addr(nullptr) + { } + explicit PatchedAbsoluteAddress(const void* addr) + : addr(const_cast<void*>(addr)) + { } + explicit PatchedAbsoluteAddress(uintptr_t addr) + : addr(reinterpret_cast<void*>(addr)) + { } +}; + +// Specifies an address computed in the form of a register base and a constant, +// 32-bit offset. +struct Address +{ + Register base; + int32_t offset; + + Address(Register base, int32_t offset) : base(base), offset(offset) + { } + + Address() { mozilla::PodZero(this); } +}; + +// Specifies an address computed in the form of a register base, a register +// index with a scale, and a constant, 32-bit offset. +struct BaseIndex +{ + Register base; + Register index; + Scale scale; + int32_t offset; + + BaseIndex(Register base, Register index, Scale scale, int32_t offset = 0) + : base(base), index(index), scale(scale), offset(offset) + { } + + BaseIndex() { mozilla::PodZero(this); } +}; + +// A BaseIndex used to access Values. Note that |offset| is *not* scaled by +// sizeof(Value). Use this *only* if you're indexing into a series of Values +// that aren't object elements or object slots (for example, values on the +// stack, values in an arguments object, &c.). If you're indexing into an +// object's elements or slots, don't use this directly! Use +// BaseObject{Element,Slot}Index instead. +struct BaseValueIndex : BaseIndex +{ + BaseValueIndex(Register base, Register index, int32_t offset = 0) + : BaseIndex(base, index, ValueScale, offset) + { } +}; + +// Specifies the address of an indexed Value within object elements from a +// base. The index must not already be scaled by sizeof(Value)! +struct BaseObjectElementIndex : BaseValueIndex +{ + BaseObjectElementIndex(Register base, Register index, int32_t offset = 0) + : BaseValueIndex(base, index, offset) + { + NativeObject::elementsSizeMustNotOverflow(); + } +}; + +// Like BaseObjectElementIndex, except for object slots. +struct BaseObjectSlotIndex : BaseValueIndex +{ + BaseObjectSlotIndex(Register base, Register index) + : BaseValueIndex(base, index) + { + NativeObject::slotsSizeMustNotOverflow(); + } +}; + +class Relocation { + public: + enum Kind { + // The target is immovable, so patching is only needed if the source + // buffer is relocated and the reference is relative. + HARDCODED, + + // The target is the start of a JitCode buffer, which must be traced + // during garbage collection. Relocations and patching may be needed. + JITCODE + }; +}; + +class RepatchLabel +{ + static const int32_t INVALID_OFFSET = 0xC0000000; + int32_t offset_ : 31; + uint32_t bound_ : 1; + public: + + RepatchLabel() : offset_(INVALID_OFFSET), bound_(0) {} + + void use(uint32_t newOffset) { + MOZ_ASSERT(offset_ == INVALID_OFFSET); + MOZ_ASSERT(newOffset != (uint32_t)INVALID_OFFSET); + offset_ = newOffset; + } + bool bound() const { + return bound_; + } + void bind(int32_t dest) { + MOZ_ASSERT(!bound_); + MOZ_ASSERT(dest != INVALID_OFFSET); + offset_ = dest; + bound_ = true; + } + int32_t target() { + MOZ_ASSERT(bound()); + int32_t ret = offset_; + offset_ = INVALID_OFFSET; + return ret; + } + int32_t offset() { + MOZ_ASSERT(!bound()); + return offset_; + } + bool used() const { + return !bound() && offset_ != (INVALID_OFFSET); + } + +}; +// An absolute label is like a Label, except it represents an absolute +// reference rather than a relative one. Thus, it cannot be patched until after +// linking. +struct AbsoluteLabel : public LabelBase +{ + public: + AbsoluteLabel() + { } + AbsoluteLabel(const AbsoluteLabel& label) : LabelBase(label) + { } + int32_t prev() const { + MOZ_ASSERT(!bound()); + if (!used()) + return INVALID_OFFSET; + return offset(); + } + void setPrev(int32_t offset) { + use(offset); + } + void bind() { + bound_ = true; + + // These labels cannot be used after being bound. + offset_ = -1; + } +}; + +class CodeOffset +{ + size_t offset_; + + static const size_t NOT_BOUND = size_t(-1); + + public: + explicit CodeOffset(size_t offset) : offset_(offset) {} + CodeOffset() : offset_(NOT_BOUND) {} + + size_t offset() const { + MOZ_ASSERT(bound()); + return offset_; + } + + void bind(size_t offset) { + MOZ_ASSERT(!bound()); + offset_ = offset; + MOZ_ASSERT(bound()); + } + bool bound() const { + return offset_ != NOT_BOUND; + } + + void offsetBy(size_t delta) { + MOZ_ASSERT(bound()); + MOZ_ASSERT(offset_ + delta >= offset_, "no overflow"); + offset_ += delta; + } +}; + +// A code label contains an absolute reference to a point in the code. Thus, it +// cannot be patched until after linking. +// When the source label is resolved into a memory address, this address is +// patched into the destination address. +class CodeLabel +{ + // The destination position, where the absolute reference should get + // patched into. + CodeOffset patchAt_; + + // The source label (relative) in the code to where the destination should + // get patched to. + CodeOffset target_; + + public: + CodeLabel() + { } + explicit CodeLabel(const CodeOffset& patchAt) + : patchAt_(patchAt) + { } + CodeLabel(const CodeOffset& patchAt, const CodeOffset& target) + : patchAt_(patchAt), + target_(target) + { } + CodeOffset* patchAt() { + return &patchAt_; + } + CodeOffset* target() { + return &target_; + } + void offsetBy(size_t delta) { + patchAt_.offsetBy(delta); + target_.offsetBy(delta); + } +}; + +// Location of a jump or label in a generated JitCode block, relative to the +// start of the block. + +class CodeOffsetJump +{ + size_t offset_; + +#ifdef JS_SMALL_BRANCH + size_t jumpTableIndex_; +#endif + + public: + +#ifdef JS_SMALL_BRANCH + CodeOffsetJump(size_t offset, size_t jumpTableIndex) + : offset_(offset), jumpTableIndex_(jumpTableIndex) + {} + size_t jumpTableIndex() const { + return jumpTableIndex_; + } +#else + explicit CodeOffsetJump(size_t offset) : offset_(offset) {} +#endif + + CodeOffsetJump() { + mozilla::PodZero(this); + } + + size_t offset() const { + return offset_; + } + void fixup(MacroAssembler* masm); +}; + +// Absolute location of a jump or a label in some generated JitCode block. +// Can also encode a CodeOffset{Jump,Label}, such that the offset is initially +// set and the absolute location later filled in after the final JitCode is +// allocated. + +class CodeLocationJump +{ + uint8_t* raw_; +#ifdef DEBUG + enum State { Uninitialized, Absolute, Relative }; + State state_; + void setUninitialized() { + state_ = Uninitialized; + } + void setAbsolute() { + state_ = Absolute; + } + void setRelative() { + state_ = Relative; + } +#else + void setUninitialized() const { + } + void setAbsolute() const { + } + void setRelative() const { + } +#endif + +#ifdef JS_SMALL_BRANCH + uint8_t* jumpTableEntry_; +#endif + + public: + CodeLocationJump() { + raw_ = nullptr; + setUninitialized(); +#ifdef JS_SMALL_BRANCH + jumpTableEntry_ = (uint8_t*) uintptr_t(0xdeadab1e); +#endif + } + CodeLocationJump(JitCode* code, CodeOffsetJump base) { + *this = base; + repoint(code); + } + + void operator = (CodeOffsetJump base) { + raw_ = (uint8_t*) base.offset(); + setRelative(); +#ifdef JS_SMALL_BRANCH + jumpTableEntry_ = (uint8_t*) base.jumpTableIndex(); +#endif + } + + void repoint(JitCode* code, MacroAssembler* masm = nullptr); + + uint8_t* raw() const { + MOZ_ASSERT(state_ == Absolute); + return raw_; + } + uint8_t* offset() const { + MOZ_ASSERT(state_ == Relative); + return raw_; + } + +#ifdef JS_SMALL_BRANCH + uint8_t* jumpTableEntry() const { + MOZ_ASSERT(state_ == Absolute); + return jumpTableEntry_; + } +#endif +}; + +class CodeLocationLabel +{ + uint8_t* raw_; +#ifdef DEBUG + enum State { Uninitialized, Absolute, Relative }; + State state_; + void setUninitialized() { + state_ = Uninitialized; + } + void setAbsolute() { + state_ = Absolute; + } + void setRelative() { + state_ = Relative; + } +#else + void setUninitialized() const { + } + void setAbsolute() const { + } + void setRelative() const { + } +#endif + + public: + CodeLocationLabel() { + raw_ = nullptr; + setUninitialized(); + } + CodeLocationLabel(JitCode* code, CodeOffset base) { + *this = base; + repoint(code); + } + explicit CodeLocationLabel(JitCode* code) { + raw_ = code->raw(); + setAbsolute(); + } + explicit CodeLocationLabel(uint8_t* raw) { + raw_ = raw; + setAbsolute(); + } + + void operator = (CodeOffset base) { + raw_ = (uint8_t*)base.offset(); + setRelative(); + } + ptrdiff_t operator - (const CodeLocationLabel& other) { + return raw_ - other.raw_; + } + + void repoint(JitCode* code, MacroAssembler* masm = nullptr); + +#ifdef DEBUG + bool isSet() const { + return state_ != Uninitialized; + } +#endif + + uint8_t* raw() const { + MOZ_ASSERT(state_ == Absolute); + return raw_; + } + uint8_t* offset() const { + MOZ_ASSERT(state_ == Relative); + return raw_; + } +}; + +} // namespace jit + +namespace wasm { + +// As an invariant across architectures, within wasm code: +// $sp % WasmStackAlignment = (sizeof(wasm::Frame) + masm.framePushed) % WasmStackAlignment +// Thus, wasm::Frame represents the bytes pushed after the call (which occurred +// with a WasmStackAlignment-aligned StackPointer) that are not included in +// masm.framePushed. + +struct Frame +{ + // The caller's saved frame pointer. In non-profiling mode, internal + // wasm-to-wasm calls don't update fp and thus don't save the caller's + // frame pointer; the space is reserved, however, so that profiling mode can + // reuse the same function body without recompiling. + uint8_t* callerFP; + + // The return address pushed by the call (in the case of ARM/MIPS the return + // address is pushed by the first instruction of the prologue). + void* returnAddress; +}; + +static_assert(sizeof(Frame) == 2 * sizeof(void*), "?!"); +static const uint32_t FrameBytesAfterReturnAddress = sizeof(void*); + +// Represents an instruction to be patched and the intended pointee. These +// links are accumulated in the MacroAssembler, but patching is done outside +// the MacroAssembler (in Module::staticallyLink). + +struct SymbolicAccess +{ + SymbolicAccess(jit::CodeOffset patchAt, SymbolicAddress target) + : patchAt(patchAt), target(target) {} + + jit::CodeOffset patchAt; + SymbolicAddress target; +}; + +typedef Vector<SymbolicAccess, 0, SystemAllocPolicy> SymbolicAccessVector; + +// Describes a single wasm or asm.js memory access for the purpose of generating +// code and metadata. + +class MemoryAccessDesc +{ + uint32_t offset_; + uint32_t align_; + Scalar::Type type_; + unsigned numSimdElems_; + jit::MemoryBarrierBits barrierBefore_; + jit::MemoryBarrierBits barrierAfter_; + mozilla::Maybe<wasm::TrapOffset> trapOffset_; + + public: + explicit MemoryAccessDesc(Scalar::Type type, uint32_t align, uint32_t offset, + mozilla::Maybe<TrapOffset> trapOffset, + unsigned numSimdElems = 0, + jit::MemoryBarrierBits barrierBefore = jit::MembarNobits, + jit::MemoryBarrierBits barrierAfter = jit::MembarNobits) + : offset_(offset), + align_(align), + type_(type), + numSimdElems_(numSimdElems), + barrierBefore_(barrierBefore), + barrierAfter_(barrierAfter), + trapOffset_(trapOffset) + { + MOZ_ASSERT(Scalar::isSimdType(type) == (numSimdElems > 0)); + MOZ_ASSERT(numSimdElems <= jit::ScalarTypeToLength(type)); + MOZ_ASSERT(mozilla::IsPowerOfTwo(align)); + MOZ_ASSERT_IF(isSimd(), hasTrap()); + MOZ_ASSERT_IF(isAtomic(), hasTrap()); + } + + uint32_t offset() const { return offset_; } + uint32_t align() const { return align_; } + Scalar::Type type() const { return type_; } + unsigned byteSize() const { + return Scalar::isSimdType(type()) + ? Scalar::scalarByteSize(type()) * numSimdElems() + : Scalar::byteSize(type()); + } + unsigned numSimdElems() const { MOZ_ASSERT(isSimd()); return numSimdElems_; } + jit::MemoryBarrierBits barrierBefore() const { return barrierBefore_; } + jit::MemoryBarrierBits barrierAfter() const { return barrierAfter_; } + bool hasTrap() const { return !!trapOffset_; } + TrapOffset trapOffset() const { return *trapOffset_; } + bool isAtomic() const { return (barrierBefore_ | barrierAfter_) != jit::MembarNobits; } + bool isSimd() const { return Scalar::isSimdType(type_); } + bool isUnaligned() const { return align() && align() < byteSize(); } + bool isPlainAsmJS() const { return !hasTrap(); } + + void clearOffset() { offset_ = 0; } +}; + +// Summarizes a global access for a mutable (in asm.js) or immutable value (in +// asm.js or the wasm MVP) that needs to get patched later. + +struct GlobalAccess +{ + GlobalAccess(jit::CodeOffset patchAt, unsigned globalDataOffset) + : patchAt(patchAt), globalDataOffset(globalDataOffset) + {} + + jit::CodeOffset patchAt; + unsigned globalDataOffset; +}; + +typedef Vector<GlobalAccess, 0, SystemAllocPolicy> GlobalAccessVector; + +// The TrapDesc struct describes a wasm trap that is about to be emitted. This +// includes the logical wasm bytecode offset to report, the kind of instruction +// causing the trap, and the stack depth right before control is transferred to +// the trap out-of-line path. + +struct TrapDesc : TrapOffset +{ + enum Kind { Jump, MemoryAccess }; + Kind kind; + Trap trap; + uint32_t framePushed; + + TrapDesc(TrapOffset offset, Trap trap, uint32_t framePushed, Kind kind = Jump) + : TrapOffset(offset), kind(kind), trap(trap), framePushed(framePushed) + {} +}; + +// A TrapSite captures all relevant information at the point of emitting the +// in-line trapping instruction for the purpose of generating the out-of-line +// trap code (at the end of the function). + +struct TrapSite : TrapDesc +{ + uint32_t codeOffset; + + TrapSite(TrapDesc trap, uint32_t codeOffset) + : TrapDesc(trap), codeOffset(codeOffset) + {} +}; + +typedef Vector<TrapSite, 0, SystemAllocPolicy> TrapSiteVector; + +// A TrapFarJump records the offset of a jump that needs to be patched to a trap +// exit at the end of the module when trap exits are emitted. + +struct TrapFarJump +{ + Trap trap; + jit::CodeOffset jump; + + TrapFarJump(Trap trap, jit::CodeOffset jump) + : trap(trap), jump(jump) + {} + + void offsetBy(size_t delta) { + jump.offsetBy(delta); + } +}; + +typedef Vector<TrapFarJump, 0, SystemAllocPolicy> TrapFarJumpVector; + +} // namespace wasm + +namespace jit { + +// The base class of all Assemblers for all archs. +class AssemblerShared +{ + wasm::CallSiteAndTargetVector callSites_; + wasm::TrapSiteVector trapSites_; + wasm::TrapFarJumpVector trapFarJumps_; + wasm::MemoryAccessVector memoryAccesses_; + wasm::MemoryPatchVector memoryPatches_; + wasm::BoundsCheckVector boundsChecks_; + wasm::GlobalAccessVector globalAccesses_; + wasm::SymbolicAccessVector symbolicAccesses_; + + protected: + Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_; + + bool enoughMemory_; + bool embedsNurseryPointers_; + + public: + AssemblerShared() + : enoughMemory_(true), + embedsNurseryPointers_(false) + {} + + void propagateOOM(bool success) { + enoughMemory_ &= success; + } + + void setOOM() { + enoughMemory_ = false; + } + + bool oom() const { + return !enoughMemory_; + } + + bool embedsNurseryPointers() const { + return embedsNurseryPointers_; + } + + template <typename... Args> + void append(const wasm::CallSiteDesc& desc, CodeOffset retAddr, size_t framePushed, + Args&&... args) + { + // framePushed does not include sizeof(wasm:Frame), so add it in explicitly when + // setting the CallSite::stackDepth. + wasm::CallSite cs(desc, retAddr.offset(), framePushed + sizeof(wasm::Frame)); + enoughMemory_ &= callSites_.emplaceBack(cs, mozilla::Forward<Args>(args)...); + } + wasm::CallSiteAndTargetVector& callSites() { return callSites_; } + + void append(wasm::TrapSite trapSite) { + enoughMemory_ &= trapSites_.append(trapSite); + } + const wasm::TrapSiteVector& trapSites() const { return trapSites_; } + void clearTrapSites() { trapSites_.clear(); } + + void append(wasm::TrapFarJump jmp) { + enoughMemory_ &= trapFarJumps_.append(jmp); + } + const wasm::TrapFarJumpVector& trapFarJumps() const { return trapFarJumps_; } + + void append(wasm::MemoryAccess access) { enoughMemory_ &= memoryAccesses_.append(access); } + wasm::MemoryAccessVector&& extractMemoryAccesses() { return Move(memoryAccesses_); } + + void append(const wasm::MemoryAccessDesc& access, size_t codeOffset, size_t framePushed) { + if (access.hasTrap()) { + // If a memory access is trapping (wasm, SIMD.js, Atomics), create a + // TrapSite now which will generate a trap out-of-line path at the end + // of the function which will *then* append a MemoryAccess. + wasm::TrapDesc trap(access.trapOffset(), wasm::Trap::OutOfBounds, framePushed, + wasm::TrapSite::MemoryAccess); + append(wasm::TrapSite(trap, codeOffset)); + } else { + // Otherwise, this is a plain asm.js access. On WASM_HUGE_MEMORY + // platforms, asm.js uses signal handlers to remove bounds checks + // and thus requires a MemoryAccess. + MOZ_ASSERT(access.isPlainAsmJS()); +#ifdef WASM_HUGE_MEMORY + append(wasm::MemoryAccess(codeOffset)); +#endif + } + } + + void append(wasm::MemoryPatch patch) { enoughMemory_ &= memoryPatches_.append(patch); } + wasm::MemoryPatchVector&& extractMemoryPatches() { return Move(memoryPatches_); } + + void append(wasm::BoundsCheck check) { enoughMemory_ &= boundsChecks_.append(check); } + wasm::BoundsCheckVector&& extractBoundsChecks() { return Move(boundsChecks_); } + + void append(wasm::GlobalAccess access) { enoughMemory_ &= globalAccesses_.append(access); } + const wasm::GlobalAccessVector& globalAccesses() const { return globalAccesses_; } + + void append(wasm::SymbolicAccess access) { enoughMemory_ &= symbolicAccesses_.append(access); } + size_t numSymbolicAccesses() const { return symbolicAccesses_.length(); } + wasm::SymbolicAccess symbolicAccess(size_t i) const { return symbolicAccesses_[i]; } + + static bool canUseInSingleByteInstruction(Register reg) { return true; } + + void addCodeLabel(CodeLabel label) { + propagateOOM(codeLabels_.append(label)); + } + size_t numCodeLabels() const { + return codeLabels_.length(); + } + CodeLabel codeLabel(size_t i) { + return codeLabels_[i]; + } + + // Merge this assembler with the other one, invalidating it, by shifting all + // offsets by a delta. + bool asmMergeWith(size_t delta, const AssemblerShared& other) { + size_t i = callSites_.length(); + enoughMemory_ &= callSites_.appendAll(other.callSites_); + for (; i < callSites_.length(); i++) + callSites_[i].offsetReturnAddressBy(delta); + + MOZ_ASSERT(other.trapSites_.empty(), "should have been cleared by wasmEmitTrapOutOfLineCode"); + + i = trapFarJumps_.length(); + enoughMemory_ &= trapFarJumps_.appendAll(other.trapFarJumps_); + for (; i < trapFarJumps_.length(); i++) + trapFarJumps_[i].offsetBy(delta); + + i = memoryAccesses_.length(); + enoughMemory_ &= memoryAccesses_.appendAll(other.memoryAccesses_); + for (; i < memoryAccesses_.length(); i++) + memoryAccesses_[i].offsetBy(delta); + + i = memoryPatches_.length(); + enoughMemory_ &= memoryPatches_.appendAll(other.memoryPatches_); + for (; i < memoryPatches_.length(); i++) + memoryPatches_[i].offsetBy(delta); + + i = boundsChecks_.length(); + enoughMemory_ &= boundsChecks_.appendAll(other.boundsChecks_); + for (; i < boundsChecks_.length(); i++) + boundsChecks_[i].offsetBy(delta); + + i = globalAccesses_.length(); + enoughMemory_ &= globalAccesses_.appendAll(other.globalAccesses_); + for (; i < globalAccesses_.length(); i++) + globalAccesses_[i].patchAt.offsetBy(delta); + + i = symbolicAccesses_.length(); + enoughMemory_ &= symbolicAccesses_.appendAll(other.symbolicAccesses_); + for (; i < symbolicAccesses_.length(); i++) + symbolicAccesses_[i].patchAt.offsetBy(delta); + + i = codeLabels_.length(); + enoughMemory_ &= codeLabels_.appendAll(other.codeLabels_); + for (; i < codeLabels_.length(); i++) + codeLabels_[i].offsetBy(delta); + + return !oom(); + } +}; + +} // namespace jit +} // namespace js + +#endif /* jit_shared_Assembler_shared_h */ |