michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jit_shared_Assembler_shared_h michael@0: #define jit_shared_Assembler_shared_h michael@0: michael@0: #include "mozilla/PodOperations.h" michael@0: michael@0: #include michael@0: michael@0: #include "jsworkers.h" michael@0: michael@0: #include "jit/IonAllocPolicy.h" michael@0: #include "jit/Registers.h" michael@0: #include "jit/RegisterSets.h" michael@0: michael@0: #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) michael@0: // JS_SMALL_BRANCH means the range on a branch instruction michael@0: // is smaller than the whole address space michael@0: # define JS_SMALL_BRANCH michael@0: #endif michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: enum Scale { michael@0: TimesOne = 0, michael@0: TimesTwo = 1, michael@0: TimesFour = 2, michael@0: TimesEight = 3 michael@0: }; michael@0: michael@0: static inline unsigned michael@0: ScaleToShift(Scale scale) michael@0: { michael@0: return unsigned(scale); michael@0: } michael@0: michael@0: static inline bool michael@0: IsShiftInScaleRange(int i) michael@0: { michael@0: return i >= TimesOne && i <= TimesEight; michael@0: } michael@0: michael@0: static inline Scale michael@0: ShiftToScale(int i) michael@0: { michael@0: JS_ASSERT(IsShiftInScaleRange(i)); michael@0: return Scale(i); michael@0: } michael@0: michael@0: static inline Scale michael@0: ScaleFromElemWidth(int shift) michael@0: { michael@0: switch (shift) { michael@0: case 1: michael@0: return TimesOne; michael@0: case 2: michael@0: return TimesTwo; michael@0: case 4: michael@0: return TimesFour; michael@0: case 8: michael@0: return TimesEight; michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid scale"); michael@0: } michael@0: michael@0: // Used for 32-bit immediates which do not require relocation. michael@0: struct Imm32 michael@0: { michael@0: int32_t value; michael@0: michael@0: explicit Imm32(int32_t value) : value(value) michael@0: { } michael@0: michael@0: static inline Imm32 ShiftOf(enum Scale s) { michael@0: switch (s) { michael@0: case TimesOne: michael@0: return Imm32(0); michael@0: case TimesTwo: michael@0: return Imm32(1); michael@0: case TimesFour: michael@0: return Imm32(2); michael@0: case TimesEight: michael@0: return Imm32(3); michael@0: }; michael@0: MOZ_ASSUME_UNREACHABLE("Invalid scale"); michael@0: } michael@0: michael@0: static inline Imm32 FactorOf(enum Scale s) { michael@0: return Imm32(1 << ShiftOf(s).value); michael@0: } michael@0: }; michael@0: michael@0: // Pointer-sized integer to be embedded as an immediate in an instruction. michael@0: struct ImmWord michael@0: { michael@0: uintptr_t value; michael@0: michael@0: explicit ImmWord(uintptr_t value) : value(value) michael@0: { } michael@0: }; michael@0: michael@0: #ifdef DEBUG michael@0: static inline bool michael@0: IsCompilingAsmJS() michael@0: { michael@0: // asm.js compilation pushes an IonContext with a null JSCompartment. michael@0: IonContext *ictx = MaybeGetIonContext(); michael@0: return ictx && ictx->compartment == nullptr; michael@0: } michael@0: #endif michael@0: michael@0: // Pointer to be embedded as an immediate in an instruction. michael@0: struct ImmPtr michael@0: { michael@0: void *value; michael@0: michael@0: explicit ImmPtr(const void *value) : value(const_cast(value)) michael@0: { michael@0: // To make code serialization-safe, asm.js compilation should only michael@0: // compile pointer immediates using AsmJSImmPtr. michael@0: JS_ASSERT(!IsCompilingAsmJS()); michael@0: } michael@0: michael@0: template michael@0: explicit ImmPtr(R (*pf)()) michael@0: : value(JS_FUNC_TO_DATA_PTR(void *, pf)) michael@0: { michael@0: JS_ASSERT(!IsCompilingAsmJS()); michael@0: } michael@0: michael@0: template michael@0: explicit ImmPtr(R (*pf)(A1)) michael@0: : value(JS_FUNC_TO_DATA_PTR(void *, pf)) michael@0: { michael@0: JS_ASSERT(!IsCompilingAsmJS()); michael@0: } michael@0: michael@0: template michael@0: explicit ImmPtr(R (*pf)(A1, A2)) michael@0: : value(JS_FUNC_TO_DATA_PTR(void *, pf)) michael@0: { michael@0: JS_ASSERT(!IsCompilingAsmJS()); michael@0: } michael@0: michael@0: template michael@0: explicit ImmPtr(R (*pf)(A1, A2, A3)) michael@0: : value(JS_FUNC_TO_DATA_PTR(void *, pf)) michael@0: { michael@0: JS_ASSERT(!IsCompilingAsmJS()); michael@0: } michael@0: michael@0: template michael@0: explicit ImmPtr(R (*pf)(A1, A2, A3, A4)) michael@0: : value(JS_FUNC_TO_DATA_PTR(void *, pf)) michael@0: { michael@0: JS_ASSERT(!IsCompilingAsmJS()); michael@0: } michael@0: michael@0: }; michael@0: michael@0: // The same as ImmPtr except that the intention is to patch this michael@0: // instruction. The initial value of the immediate is 'addr' and this value is michael@0: // either clobbered or used in the patching process. michael@0: struct PatchedImmPtr { michael@0: void *value; michael@0: michael@0: explicit PatchedImmPtr() michael@0: : value(nullptr) michael@0: { } michael@0: explicit PatchedImmPtr(const void *value) michael@0: : value(const_cast(value)) michael@0: { } michael@0: }; michael@0: michael@0: // Used for immediates which require relocation. michael@0: struct ImmGCPtr michael@0: { michael@0: uintptr_t value; michael@0: michael@0: explicit ImmGCPtr(const gc::Cell *ptr) : value(reinterpret_cast(ptr)) michael@0: { michael@0: JS_ASSERT(!IsPoisonedPtr(ptr)); michael@0: JS_ASSERT_IF(ptr, ptr->isTenured()); michael@0: michael@0: // asm.js shouldn't be creating GC things michael@0: JS_ASSERT(!IsCompilingAsmJS()); michael@0: } michael@0: michael@0: protected: michael@0: ImmGCPtr() : value(0) {} michael@0: }; michael@0: michael@0: // Used for immediates which require relocation and may be traced during minor GC. michael@0: struct ImmMaybeNurseryPtr : public ImmGCPtr michael@0: { michael@0: explicit ImmMaybeNurseryPtr(gc::Cell *ptr) michael@0: { michael@0: this->value = reinterpret_cast(ptr); michael@0: JS_ASSERT(!IsPoisonedPtr(ptr)); michael@0: michael@0: // asm.js shouldn't be creating GC things michael@0: JS_ASSERT(!IsCompilingAsmJS()); michael@0: } michael@0: }; michael@0: michael@0: // Pointer to be embedded as an immediate that is loaded/stored from by an michael@0: // instruction. michael@0: struct AbsoluteAddress { michael@0: void *addr; michael@0: michael@0: explicit AbsoluteAddress(const void *addr) michael@0: : addr(const_cast(addr)) michael@0: { michael@0: // asm.js shouldn't be creating GC things michael@0: JS_ASSERT(!IsCompilingAsmJS()); michael@0: } michael@0: michael@0: AbsoluteAddress offset(ptrdiff_t delta) { michael@0: return AbsoluteAddress(((uint8_t *) addr) + delta); michael@0: } michael@0: }; michael@0: michael@0: // The same as AbsoluteAddress except that the intention is to patch this michael@0: // instruction. The initial value of the immediate is 'addr' and this value is michael@0: // either clobbered or used in the patching process. michael@0: struct PatchedAbsoluteAddress { michael@0: void *addr; michael@0: michael@0: explicit PatchedAbsoluteAddress() michael@0: : addr(nullptr) michael@0: { } michael@0: explicit PatchedAbsoluteAddress(const void *addr) michael@0: : addr(const_cast(addr)) michael@0: { } michael@0: }; michael@0: michael@0: // Specifies an address computed in the form of a register base and a constant, michael@0: // 32-bit offset. michael@0: struct Address michael@0: { michael@0: Register base; michael@0: int32_t offset; michael@0: michael@0: Address(Register base, int32_t offset) : base(base), offset(offset) michael@0: { } michael@0: michael@0: Address() { mozilla::PodZero(this); } michael@0: }; michael@0: michael@0: // Specifies an address computed in the form of a register base, a register michael@0: // index with a scale, and a constant, 32-bit offset. michael@0: struct BaseIndex michael@0: { michael@0: Register base; michael@0: Register index; michael@0: Scale scale; michael@0: int32_t offset; michael@0: michael@0: BaseIndex(Register base, Register index, Scale scale, int32_t offset = 0) michael@0: : base(base), index(index), scale(scale), offset(offset) michael@0: { } michael@0: michael@0: BaseIndex() { mozilla::PodZero(this); } michael@0: }; michael@0: michael@0: class Relocation { michael@0: public: michael@0: enum Kind { michael@0: // The target is immovable, so patching is only needed if the source michael@0: // buffer is relocated and the reference is relative. michael@0: HARDCODED, michael@0: michael@0: // The target is the start of a JitCode buffer, which must be traced michael@0: // during garbage collection. Relocations and patching may be needed. michael@0: JITCODE michael@0: }; michael@0: }; michael@0: michael@0: struct LabelBase michael@0: { michael@0: protected: michael@0: // offset_ >= 0 means that the label is either bound or has incoming michael@0: // uses and needs to be bound. michael@0: int32_t offset_ : 31; michael@0: bool bound_ : 1; michael@0: michael@0: // Disallow assignment. michael@0: void operator =(const LabelBase &label); michael@0: public: michael@0: static const int32_t INVALID_OFFSET = -1; michael@0: michael@0: LabelBase() : offset_(INVALID_OFFSET), bound_(false) michael@0: { } michael@0: LabelBase(const LabelBase &label) michael@0: : offset_(label.offset_), michael@0: bound_(label.bound_) michael@0: { } michael@0: michael@0: // If the label is bound, all incoming edges have been patched and any michael@0: // future incoming edges will be immediately patched. michael@0: bool bound() const { michael@0: return bound_; michael@0: } michael@0: int32_t offset() const { michael@0: JS_ASSERT(bound() || used()); michael@0: return offset_; michael@0: } michael@0: // Returns whether the label is not bound, but has incoming uses. michael@0: bool used() const { michael@0: return !bound() && offset_ > INVALID_OFFSET; michael@0: } michael@0: // Binds the label, fixing its final position in the code stream. michael@0: void bind(int32_t offset) { michael@0: JS_ASSERT(!bound()); michael@0: offset_ = offset; michael@0: bound_ = true; michael@0: JS_ASSERT(offset_ == offset); michael@0: } michael@0: // Marks the label as neither bound nor used. michael@0: void reset() { michael@0: offset_ = INVALID_OFFSET; michael@0: bound_ = false; michael@0: } michael@0: // Sets the label's latest used position, returning the old use position in michael@0: // the process. michael@0: int32_t use(int32_t offset) { michael@0: JS_ASSERT(!bound()); michael@0: michael@0: int32_t old = offset_; michael@0: offset_ = offset; michael@0: JS_ASSERT(offset_ == offset); michael@0: michael@0: return old; michael@0: } michael@0: }; michael@0: michael@0: // A label represents a position in an assembly buffer that may or may not have michael@0: // already been generated. Labels can either be "bound" or "unbound", the michael@0: // former meaning that its position is known and the latter that its position michael@0: // is not yet known. michael@0: // michael@0: // A jump to an unbound label adds that jump to the label's incoming queue. A michael@0: // jump to a bound label automatically computes the jump distance. The process michael@0: // of binding a label automatically corrects all incoming jumps. michael@0: class Label : public LabelBase michael@0: { michael@0: public: michael@0: Label() michael@0: { } michael@0: Label(const Label &label) : LabelBase(label) michael@0: { } michael@0: ~Label() michael@0: { michael@0: #ifdef DEBUG michael@0: // The assertion below doesn't hold if an error occurred. michael@0: if (OOM_counter > OOM_maxAllocations) michael@0: return; michael@0: if (MaybeGetIonContext() && GetIonContext()->runtime->hadOutOfMemory()) michael@0: return; michael@0: michael@0: MOZ_ASSERT(!used()); michael@0: #endif michael@0: } michael@0: }; michael@0: michael@0: // Label's destructor asserts that if it has been used it has also been bound. michael@0: // In the case long-lived labels, however, failed compilation (e.g. OOM) will michael@0: // trigger this failure innocuously. This Label silences the assertion. michael@0: class NonAssertingLabel : public Label michael@0: { michael@0: public: michael@0: ~NonAssertingLabel() michael@0: { michael@0: #ifdef DEBUG michael@0: if (used()) michael@0: bind(0); michael@0: #endif michael@0: } michael@0: }; michael@0: michael@0: class RepatchLabel michael@0: { michael@0: static const int32_t INVALID_OFFSET = 0xC0000000; michael@0: int32_t offset_ : 31; michael@0: uint32_t bound_ : 1; michael@0: public: michael@0: michael@0: RepatchLabel() : offset_(INVALID_OFFSET), bound_(0) {} michael@0: michael@0: void use(uint32_t newOffset) { michael@0: JS_ASSERT(offset_ == INVALID_OFFSET); michael@0: JS_ASSERT(newOffset != (uint32_t)INVALID_OFFSET); michael@0: offset_ = newOffset; michael@0: } michael@0: bool bound() const { michael@0: return bound_; michael@0: } michael@0: void bind(int32_t dest) { michael@0: JS_ASSERT(!bound_); michael@0: JS_ASSERT(dest != INVALID_OFFSET); michael@0: offset_ = dest; michael@0: bound_ = true; michael@0: } michael@0: int32_t target() { michael@0: JS_ASSERT(bound()); michael@0: int32_t ret = offset_; michael@0: offset_ = INVALID_OFFSET; michael@0: return ret; michael@0: } michael@0: int32_t offset() { michael@0: JS_ASSERT(!bound()); michael@0: return offset_; michael@0: } michael@0: bool used() const { michael@0: return !bound() && offset_ != (INVALID_OFFSET); michael@0: } michael@0: michael@0: }; michael@0: // An absolute label is like a Label, except it represents an absolute michael@0: // reference rather than a relative one. Thus, it cannot be patched until after michael@0: // linking. michael@0: struct AbsoluteLabel : public LabelBase michael@0: { michael@0: public: michael@0: AbsoluteLabel() michael@0: { } michael@0: AbsoluteLabel(const AbsoluteLabel &label) : LabelBase(label) michael@0: { } michael@0: int32_t prev() const { michael@0: JS_ASSERT(!bound()); michael@0: if (!used()) michael@0: return INVALID_OFFSET; michael@0: return offset(); michael@0: } michael@0: void setPrev(int32_t offset) { michael@0: use(offset); michael@0: } michael@0: void bind() { michael@0: bound_ = true; michael@0: michael@0: // These labels cannot be used after being bound. michael@0: offset_ = -1; michael@0: } michael@0: }; michael@0: michael@0: // A code label contains an absolute reference to a point in the code michael@0: // Thus, it cannot be patched until after linking michael@0: class CodeLabel michael@0: { michael@0: // The destination position, where the absolute reference should get patched into michael@0: AbsoluteLabel dest_; michael@0: michael@0: // The source label (relative) in the code to where the michael@0: // the destination should get patched to. michael@0: Label src_; michael@0: michael@0: public: michael@0: CodeLabel() michael@0: { } michael@0: CodeLabel(const AbsoluteLabel &dest) michael@0: : dest_(dest) michael@0: { } michael@0: AbsoluteLabel *dest() { michael@0: return &dest_; michael@0: } michael@0: Label *src() { michael@0: return &src_; michael@0: } michael@0: }; michael@0: michael@0: // Location of a jump or label in a generated JitCode block, relative to the michael@0: // start of the block. michael@0: michael@0: class CodeOffsetJump michael@0: { michael@0: size_t offset_; michael@0: michael@0: #ifdef JS_SMALL_BRANCH michael@0: size_t jumpTableIndex_; michael@0: #endif michael@0: michael@0: public: michael@0: michael@0: #ifdef JS_SMALL_BRANCH michael@0: CodeOffsetJump(size_t offset, size_t jumpTableIndex) michael@0: : offset_(offset), jumpTableIndex_(jumpTableIndex) michael@0: {} michael@0: size_t jumpTableIndex() const { michael@0: return jumpTableIndex_; michael@0: } michael@0: #else michael@0: CodeOffsetJump(size_t offset) : offset_(offset) {} michael@0: #endif michael@0: michael@0: CodeOffsetJump() { michael@0: mozilla::PodZero(this); michael@0: } michael@0: michael@0: size_t offset() const { michael@0: return offset_; michael@0: } michael@0: void fixup(MacroAssembler *masm); michael@0: }; michael@0: michael@0: class CodeOffsetLabel michael@0: { michael@0: size_t offset_; michael@0: michael@0: public: michael@0: CodeOffsetLabel(size_t offset) : offset_(offset) {} michael@0: CodeOffsetLabel() : offset_(0) {} michael@0: michael@0: size_t offset() const { michael@0: return offset_; michael@0: } michael@0: void fixup(MacroAssembler *masm); michael@0: michael@0: }; michael@0: michael@0: // Absolute location of a jump or a label in some generated JitCode block. michael@0: // Can also encode a CodeOffset{Jump,Label}, such that the offset is initially michael@0: // set and the absolute location later filled in after the final JitCode is michael@0: // allocated. michael@0: michael@0: class CodeLocationJump michael@0: { michael@0: uint8_t *raw_; michael@0: #ifdef DEBUG michael@0: enum State { Uninitialized, Absolute, Relative }; michael@0: State state_; michael@0: void setUninitialized() { michael@0: state_ = Uninitialized; michael@0: } michael@0: void setAbsolute() { michael@0: state_ = Absolute; michael@0: } michael@0: void setRelative() { michael@0: state_ = Relative; michael@0: } michael@0: #else michael@0: void setUninitialized() const { michael@0: } michael@0: void setAbsolute() const { michael@0: } michael@0: void setRelative() const { michael@0: } michael@0: #endif michael@0: michael@0: #ifdef JS_SMALL_BRANCH michael@0: uint8_t *jumpTableEntry_; michael@0: #endif michael@0: michael@0: public: michael@0: CodeLocationJump() { michael@0: raw_ = nullptr; michael@0: setUninitialized(); michael@0: #ifdef JS_SMALL_BRANCH michael@0: jumpTableEntry_ = (uint8_t *) 0xdeadab1e; michael@0: #endif michael@0: } michael@0: CodeLocationJump(JitCode *code, CodeOffsetJump base) { michael@0: *this = base; michael@0: repoint(code); michael@0: } michael@0: michael@0: void operator = (CodeOffsetJump base) { michael@0: raw_ = (uint8_t *) base.offset(); michael@0: setRelative(); michael@0: #ifdef JS_SMALL_BRANCH michael@0: jumpTableEntry_ = (uint8_t *) base.jumpTableIndex(); michael@0: #endif michael@0: } michael@0: michael@0: void repoint(JitCode *code, MacroAssembler* masm = nullptr); michael@0: michael@0: uint8_t *raw() const { michael@0: JS_ASSERT(state_ == Absolute); michael@0: return raw_; michael@0: } michael@0: uint8_t *offset() const { michael@0: JS_ASSERT(state_ == Relative); michael@0: return raw_; michael@0: } michael@0: michael@0: #ifdef JS_SMALL_BRANCH michael@0: uint8_t *jumpTableEntry() const { michael@0: JS_ASSERT(state_ == Absolute); michael@0: return jumpTableEntry_; michael@0: } michael@0: #endif michael@0: }; michael@0: michael@0: class CodeLocationLabel michael@0: { michael@0: uint8_t *raw_; michael@0: #ifdef DEBUG michael@0: enum State { Uninitialized, Absolute, Relative }; michael@0: State state_; michael@0: void setUninitialized() { michael@0: state_ = Uninitialized; michael@0: } michael@0: void setAbsolute() { michael@0: state_ = Absolute; michael@0: } michael@0: void setRelative() { michael@0: state_ = Relative; michael@0: } michael@0: #else michael@0: void setUninitialized() const { michael@0: } michael@0: void setAbsolute() const { michael@0: } michael@0: void setRelative() const { michael@0: } michael@0: #endif michael@0: michael@0: public: michael@0: CodeLocationLabel() { michael@0: raw_ = nullptr; michael@0: setUninitialized(); michael@0: } michael@0: CodeLocationLabel(JitCode *code, CodeOffsetLabel base) { michael@0: *this = base; michael@0: repoint(code); michael@0: } michael@0: CodeLocationLabel(JitCode *code) { michael@0: raw_ = code->raw(); michael@0: setAbsolute(); michael@0: } michael@0: CodeLocationLabel(uint8_t *raw) { michael@0: raw_ = raw; michael@0: setAbsolute(); michael@0: } michael@0: michael@0: void operator = (CodeOffsetLabel base) { michael@0: raw_ = (uint8_t *)base.offset(); michael@0: setRelative(); michael@0: } michael@0: ptrdiff_t operator - (const CodeLocationLabel &other) { michael@0: return raw_ - other.raw_; michael@0: } michael@0: michael@0: void repoint(JitCode *code, MacroAssembler *masm = nullptr); michael@0: michael@0: #ifdef DEBUG michael@0: bool isSet() const { michael@0: return state_ != Uninitialized; michael@0: } michael@0: #endif michael@0: michael@0: uint8_t *raw() const { michael@0: JS_ASSERT(state_ == Absolute); michael@0: return raw_; michael@0: } michael@0: uint8_t *offset() const { michael@0: JS_ASSERT(state_ == Relative); michael@0: return raw_; michael@0: } michael@0: }; michael@0: michael@0: // Describes the user-visible properties of a callsite. michael@0: // michael@0: // A few general notes about the stack-walking supported by CallSite(Desc): michael@0: // - This information facilitates stack-walking performed by FrameIter which michael@0: // is used by Error.stack and other user-visible stack-walking functions. michael@0: // - Ion/asm.js calling conventions do not maintain a frame-pointer so michael@0: // stack-walking must lookup the stack depth based on the PC. michael@0: // - Stack-walking only occurs from C++ after a synchronous calls (JS-to-JS and michael@0: // JS-to-C++). Thus, we do not need to map arbitrary PCs to stack-depths, michael@0: // just the return address at callsites. michael@0: // - An exception to the above rule is the interrupt callback which can happen michael@0: // at arbitrary PCs. In such cases, we drop frames from the stack-walk. In michael@0: // the future when a full PC->stack-depth map is maintained, we handle this michael@0: // case. michael@0: class CallSiteDesc michael@0: { michael@0: uint32_t line_; michael@0: uint32_t column_; michael@0: uint32_t functionNameIndex_; michael@0: michael@0: static const uint32_t sEntryTrampoline = UINT32_MAX; michael@0: static const uint32_t sExit = UINT32_MAX - 1; michael@0: michael@0: public: michael@0: static const uint32_t FUNCTION_NAME_INDEX_MAX = UINT32_MAX - 2; michael@0: michael@0: CallSiteDesc() {} michael@0: michael@0: CallSiteDesc(uint32_t line, uint32_t column, uint32_t functionNameIndex) michael@0: : line_(line), column_(column), functionNameIndex_(functionNameIndex) michael@0: {} michael@0: michael@0: static CallSiteDesc Entry() { return CallSiteDesc(0, 0, sEntryTrampoline); } michael@0: static CallSiteDesc Exit() { return CallSiteDesc(0, 0, sExit); } michael@0: michael@0: bool isEntry() const { return functionNameIndex_ == sEntryTrampoline; } michael@0: bool isExit() const { return functionNameIndex_ == sExit; } michael@0: bool isNormal() const { return !(isEntry() || isExit()); } michael@0: michael@0: uint32_t line() const { JS_ASSERT(isNormal()); return line_; } michael@0: uint32_t column() const { JS_ASSERT(isNormal()); return column_; } michael@0: uint32_t functionNameIndex() const { JS_ASSERT(isNormal()); return functionNameIndex_; } michael@0: }; michael@0: michael@0: // Adds to CallSiteDesc the metadata necessary to walk the stack given an michael@0: // initial stack-pointer. michael@0: struct CallSite : public CallSiteDesc michael@0: { michael@0: uint32_t returnAddressOffset_; michael@0: uint32_t stackDepth_; michael@0: michael@0: public: michael@0: CallSite() {} michael@0: michael@0: CallSite(CallSiteDesc desc, uint32_t returnAddressOffset, uint32_t stackDepth) michael@0: : CallSiteDesc(desc), michael@0: returnAddressOffset_(returnAddressOffset), michael@0: stackDepth_(stackDepth) michael@0: { } michael@0: michael@0: void setReturnAddressOffset(uint32_t r) { returnAddressOffset_ = r; } michael@0: uint32_t returnAddressOffset() const { return returnAddressOffset_; } michael@0: michael@0: // The stackDepth measures the amount of stack space pushed since the michael@0: // function was called. In particular, this includes the word pushed by the michael@0: // call instruction on x86/x64. michael@0: uint32_t stackDepth() const { JS_ASSERT(!isEntry()); return stackDepth_; } michael@0: }; michael@0: michael@0: typedef Vector CallSiteVector; michael@0: michael@0: // Summarizes a heap access made by asm.js code that needs to be patched later michael@0: // and/or looked up by the asm.js signal handlers. Different architectures need michael@0: // to know different things (x64: offset and length, ARM: where to patch in michael@0: // heap length, x86: where to patch in heap length and base) hence the massive michael@0: // #ifdefery. michael@0: class AsmJSHeapAccess michael@0: { michael@0: uint32_t offset_; michael@0: #if defined(JS_CODEGEN_X86) michael@0: uint8_t cmpDelta_; // the number of bytes from the cmp to the load/store instruction michael@0: #endif michael@0: #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) michael@0: uint8_t opLength_; // the length of the load/store instruction michael@0: uint8_t isFloat32Load_; michael@0: AnyRegister::Code loadedReg_ : 8; michael@0: #endif michael@0: michael@0: JS_STATIC_ASSERT(AnyRegister::Total < UINT8_MAX); michael@0: michael@0: public: michael@0: AsmJSHeapAccess() {} michael@0: #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) michael@0: // If 'cmp' equals 'offset' or if it is not supplied then the michael@0: // cmpDelta_ is zero indicating that there is no length to patch. michael@0: AsmJSHeapAccess(uint32_t offset, uint32_t after, ArrayBufferView::ViewType vt, michael@0: AnyRegister loadedReg, uint32_t cmp = UINT32_MAX) michael@0: : offset_(offset), michael@0: # if defined(JS_CODEGEN_X86) michael@0: cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp), michael@0: # endif michael@0: opLength_(after - offset), michael@0: isFloat32Load_(vt == ArrayBufferView::TYPE_FLOAT32), michael@0: loadedReg_(loadedReg.code()) michael@0: {} michael@0: AsmJSHeapAccess(uint32_t offset, uint8_t after, uint32_t cmp = UINT32_MAX) michael@0: : offset_(offset), michael@0: # if defined(JS_CODEGEN_X86) michael@0: cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp), michael@0: # endif michael@0: opLength_(after - offset), michael@0: isFloat32Load_(false), michael@0: loadedReg_(UINT8_MAX) michael@0: {} michael@0: #elif defined(JS_CODEGEN_ARM) michael@0: explicit AsmJSHeapAccess(uint32_t offset) michael@0: : offset_(offset) michael@0: {} michael@0: #endif michael@0: michael@0: uint32_t offset() const { return offset_; } michael@0: void setOffset(uint32_t offset) { offset_ = offset; } michael@0: #if defined(JS_CODEGEN_X86) michael@0: bool hasLengthCheck() const { return cmpDelta_ > 0; } michael@0: void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); } michael@0: void *patchOffsetAt(uint8_t *code) const { return code + (offset_ + opLength_); } michael@0: #endif michael@0: #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) michael@0: unsigned opLength() const { return opLength_; } michael@0: bool isLoad() const { return loadedReg_ != UINT8_MAX; } michael@0: bool isFloat32Load() const { return isFloat32Load_; } michael@0: AnyRegister loadedReg() const { return AnyRegister::FromCode(loadedReg_); } michael@0: #endif michael@0: }; michael@0: michael@0: typedef Vector AsmJSHeapAccessVector; michael@0: michael@0: struct AsmJSGlobalAccess michael@0: { michael@0: CodeOffsetLabel patchAt; michael@0: unsigned globalDataOffset; michael@0: michael@0: AsmJSGlobalAccess(CodeOffsetLabel patchAt, unsigned globalDataOffset) michael@0: : patchAt(patchAt), globalDataOffset(globalDataOffset) michael@0: {} michael@0: }; michael@0: michael@0: // Describes the intended pointee of an immediate to be embedded in asm.js michael@0: // code. By representing the pointee as a symbolic enum, the pointee can be michael@0: // patched after deserialization when the address of global things has changed. michael@0: enum AsmJSImmKind michael@0: { michael@0: AsmJSImm_Runtime, michael@0: AsmJSImm_StackLimit, michael@0: AsmJSImm_ReportOverRecursed, michael@0: AsmJSImm_HandleExecutionInterrupt, michael@0: AsmJSImm_InvokeFromAsmJS_Ignore, michael@0: AsmJSImm_InvokeFromAsmJS_ToInt32, michael@0: AsmJSImm_InvokeFromAsmJS_ToNumber, michael@0: AsmJSImm_CoerceInPlace_ToInt32, michael@0: AsmJSImm_CoerceInPlace_ToNumber, michael@0: AsmJSImm_ToInt32, michael@0: #if defined(JS_CODEGEN_ARM) michael@0: AsmJSImm_aeabi_idivmod, michael@0: AsmJSImm_aeabi_uidivmod, michael@0: #endif michael@0: AsmJSImm_ModD, michael@0: AsmJSImm_SinD, michael@0: AsmJSImm_CosD, michael@0: AsmJSImm_TanD, michael@0: AsmJSImm_ASinD, michael@0: AsmJSImm_ACosD, michael@0: AsmJSImm_ATanD, michael@0: AsmJSImm_CeilD, michael@0: AsmJSImm_CeilF, michael@0: AsmJSImm_FloorD, michael@0: AsmJSImm_FloorF, michael@0: AsmJSImm_ExpD, michael@0: AsmJSImm_LogD, michael@0: AsmJSImm_PowD, michael@0: AsmJSImm_ATan2D, michael@0: #ifdef DEBUG michael@0: AsmJSImm_AssumeUnreachable, michael@0: #endif michael@0: AsmJSImm_Invalid michael@0: }; michael@0: michael@0: // Pointer to be embedded as an immediate in asm.js code. michael@0: class AsmJSImmPtr michael@0: { michael@0: AsmJSImmKind kind_; michael@0: public: michael@0: AsmJSImmKind kind() const { return kind_; } michael@0: AsmJSImmPtr(AsmJSImmKind kind) : kind_(kind) { JS_ASSERT(IsCompilingAsmJS()); } michael@0: AsmJSImmPtr() {} michael@0: }; michael@0: michael@0: // Pointer to be embedded as an immediate that is loaded/stored from by an michael@0: // instruction in asm.js code. michael@0: class AsmJSAbsoluteAddress michael@0: { michael@0: AsmJSImmKind kind_; michael@0: public: michael@0: AsmJSImmKind kind() const { return kind_; } michael@0: AsmJSAbsoluteAddress(AsmJSImmKind kind) : kind_(kind) { JS_ASSERT(IsCompilingAsmJS()); } michael@0: AsmJSAbsoluteAddress() {} michael@0: }; michael@0: michael@0: // Represents an instruction to be patched and the intended pointee. These michael@0: // links are accumulated in the MacroAssembler, but patching is done outside michael@0: // the MacroAssembler (in AsmJSModule::staticallyLink). michael@0: struct AsmJSAbsoluteLink michael@0: { michael@0: AsmJSAbsoluteLink(CodeOffsetLabel patchAt, AsmJSImmKind target) michael@0: : patchAt(patchAt), target(target) {} michael@0: CodeOffsetLabel patchAt; michael@0: AsmJSImmKind target; michael@0: }; michael@0: michael@0: // The base class of all Assemblers for all archs. michael@0: class AssemblerShared michael@0: { michael@0: Vector callsites_; michael@0: Vector asmJSHeapAccesses_; michael@0: Vector asmJSGlobalAccesses_; michael@0: Vector asmJSAbsoluteLinks_; michael@0: michael@0: public: michael@0: bool append(CallSite callsite) { return callsites_.append(callsite); } michael@0: CallSiteVector &&extractCallSites() { return Move(callsites_); } michael@0: michael@0: bool append(AsmJSHeapAccess access) { return asmJSHeapAccesses_.append(access); } michael@0: AsmJSHeapAccessVector &&extractAsmJSHeapAccesses() { return Move(asmJSHeapAccesses_); } michael@0: michael@0: bool append(AsmJSGlobalAccess access) { return asmJSGlobalAccesses_.append(access); } michael@0: size_t numAsmJSGlobalAccesses() const { return asmJSGlobalAccesses_.length(); } michael@0: AsmJSGlobalAccess asmJSGlobalAccess(size_t i) const { return asmJSGlobalAccesses_[i]; } michael@0: michael@0: bool append(AsmJSAbsoluteLink link) { return asmJSAbsoluteLinks_.append(link); } michael@0: size_t numAsmJSAbsoluteLinks() const { return asmJSAbsoluteLinks_.length(); } michael@0: AsmJSAbsoluteLink asmJSAbsoluteLink(size_t i) const { return asmJSAbsoluteLinks_[i]; } michael@0: }; michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif /* jit_shared_Assembler_shared_h */