| /* -*- 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 <limits.h> |
| |
| #include "mozilla/DebugOnly.h" |
| #include "mozilla/PodOperations.h" |
| |
| #include "jit/IonAllocPolicy.h" |
| #include "jit/Registers.h" |
| #include "jit/RegisterSets.h" |
| #if defined(JS_CPU_X64) || defined(JS_CPU_ARM) |
| // 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 { |
| |
| enum Scale { |
| TimesOne = 0, |
| TimesTwo = 1, |
| TimesFour = 2, |
| TimesEight = 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) |
| { |
| JS_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; |
| } |
| |
| JS_NOT_REACHED("Invalid scale"); |
| return TimesOne; |
| } |
| |
| // 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); |
| }; |
| JS_NOT_REACHED("Invalid scale"); |
| return Imm32(-1); |
| } |
| |
| static inline Imm32 FactorOf(enum Scale s) { |
| return Imm32(1 << ShiftOf(s).value); |
| } |
| }; |
| |
| // Pointer-sized immediate. |
| struct ImmWord |
| { |
| uintptr_t value; |
| |
| explicit ImmWord(uintptr_t value) : value(value) |
| { } |
| explicit ImmWord(const void *ptr) : value(reinterpret_cast<uintptr_t>(ptr)) |
| { } |
| |
| // Note - this constructor is not implemented as it should not be used. |
| explicit ImmWord(gc::Cell *cell); |
| |
| void *asPointer() { |
| return reinterpret_cast<void *>(value); |
| } |
| }; |
| |
| // Used for immediates which require relocation. |
| struct ImmGCPtr |
| { |
| uintptr_t value; |
| |
| explicit ImmGCPtr(const gc::Cell *ptr) : value(reinterpret_cast<uintptr_t>(ptr)) |
| { |
| JS_ASSERT(!IsPoisonedPtr(ptr)); |
| JS_ASSERT_IF(ptr, ptr->isTenured()); |
| } |
| |
| protected: |
| ImmGCPtr() : value(0) {} |
| }; |
| |
| // Used for immediates which require relocation and may be traced during minor GC. |
| struct ImmMaybeNurseryPtr : public ImmGCPtr |
| { |
| explicit ImmMaybeNurseryPtr(gc::Cell *ptr) |
| { |
| this->value = reinterpret_cast<uintptr_t>(ptr); |
| JS_ASSERT(!IsPoisonedPtr(ptr)); |
| } |
| }; |
| |
| // Specifies a hardcoded, absolute address. |
| struct AbsoluteAddress { |
| void *addr; |
| |
| explicit AbsoluteAddress(void *addr) |
| : addr(addr) |
| { } |
| |
| AbsoluteAddress offset(ptrdiff_t delta) { |
| return AbsoluteAddress(((uint8_t *) addr) + delta); |
| } |
| }; |
| |
| // 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); } |
| }; |
| |
| 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 an IonCode buffer, which must be traced |
| // during garbage collection. Relocations and patching may be needed. |
| IONCODE |
| }; |
| }; |
| |
| struct LabelBase |
| { |
| protected: |
| // offset_ >= 0 means that the label is either bound or has incoming |
| // uses and needs to be bound. |
| int32_t offset_ : 31; |
| bool bound_ : 1; |
| |
| // Disallow assignment. |
| void operator =(const LabelBase &label); |
| static int id_count; |
| public: |
| mozilla::DebugOnly <int> id; |
| static const int32_t INVALID_OFFSET = -1; |
| |
| LabelBase() : offset_(INVALID_OFFSET), bound_(false), id(id_count++) |
| { } |
| LabelBase(const LabelBase &label) |
| : offset_(label.offset_), |
| bound_(label.bound_), |
| id(id_count++) |
| { } |
| |
| // If the label is bound, all incoming edges have been patched and any |
| // future incoming edges will be immediately patched. |
| bool bound() const { |
| return bound_; |
| } |
| int32_t offset() const { |
| JS_ASSERT(bound() || used()); |
| return offset_; |
| } |
| // Returns whether the label is not bound, but has incoming uses. |
| bool used() const { |
| return !bound() && offset_ > INVALID_OFFSET; |
| } |
| // Binds the label, fixing its final position in the code stream. |
| void bind(int32_t offset) { |
| JS_ASSERT(!bound()); |
| offset_ = offset; |
| bound_ = true; |
| JS_ASSERT(offset_ == offset); |
| } |
| // Marks the label as neither bound nor used. |
| void reset() { |
| offset_ = INVALID_OFFSET; |
| bound_ = false; |
| } |
| // Sets the label's latest used position, returning the old use position in |
| // the process. |
| int32_t use(int32_t offset) { |
| JS_ASSERT(!bound()); |
| |
| int32_t old = offset_; |
| offset_ = offset; |
| JS_ASSERT(offset_ == offset); |
| |
| return old; |
| } |
| }; |
| |
| // A label represents a position in an assembly buffer that may or may not have |
| // already been generated. Labels can either be "bound" or "unbound", the |
| // former meaning that its position is known and the latter that its position |
| // is not yet known. |
| // |
| // A jump to an unbound label adds that jump to the label's incoming queue. A |
| // jump to a bound label automatically computes the jump distance. The process |
| // of binding a label automatically corrects all incoming jumps. |
| class Label : public LabelBase |
| { |
| public: |
| Label() |
| { } |
| Label(const Label &label) : LabelBase(label) |
| { } |
| ~Label() |
| { |
| #ifdef DEBUG |
| // Note: the condition is a hack to silence this assert when OOM testing, |
| // see bug 756614. |
| if (!js_IonOptions.parallelCompilation) |
| JS_ASSERT_IF(MaybeGetIonContext() && !GetIonContext()->runtime->hadOutOfMemory, !used()); |
| #endif |
| } |
| }; |
| |
| // Wrapper around Label, on the heap, to avoid a bogus assert with OOM. |
| struct HeapLabel |
| : public TempObject, |
| public Label |
| { |
| }; |
| |
| 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) { |
| JS_ASSERT(offset_ == INVALID_OFFSET); |
| JS_ASSERT(newOffset != (uint32_t)INVALID_OFFSET); |
| offset_ = newOffset; |
| } |
| bool bound() const { |
| return bound_; |
| } |
| void bind(int32_t dest) { |
| JS_ASSERT(!bound_); |
| JS_ASSERT(dest != INVALID_OFFSET); |
| offset_ = dest; |
| bound_ = true; |
| } |
| int32_t target() { |
| JS_ASSERT(bound()); |
| int32_t ret = offset_; |
| offset_ = INVALID_OFFSET; |
| return ret; |
| } |
| int32_t offset() { |
| JS_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 { |
| JS_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; |
| } |
| }; |
| |
| // A code label contains an absolute reference to a point in the code |
| // Thus, it cannot be patched until after linking |
| class CodeLabel |
| { |
| // The destination position, where the absolute reference should get patched into |
| AbsoluteLabel dest_; |
| |
| // The source label (relative) in the code to where the |
| // the destination should get patched to. |
| Label src_; |
| |
| public: |
| CodeLabel() |
| { } |
| CodeLabel(const AbsoluteLabel &dest) |
| : dest_(dest) |
| { } |
| AbsoluteLabel *dest() { |
| return &dest_; |
| } |
| Label *src() { |
| return &src_; |
| } |
| }; |
| |
| // Location of a jump or label in a generated IonCode 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 |
| CodeOffsetJump(size_t offset) : offset_(offset) {} |
| #endif |
| |
| CodeOffsetJump() { |
| mozilla::PodZero(this); |
| } |
| |
| size_t offset() const { |
| return offset_; |
| } |
| void fixup(MacroAssembler *masm); |
| }; |
| |
| class CodeOffsetLabel |
| { |
| size_t offset_; |
| |
| public: |
| CodeOffsetLabel(size_t offset) : offset_(offset) {} |
| CodeOffsetLabel() : offset_(0) {} |
| |
| size_t offset() const { |
| return offset_; |
| } |
| void fixup(MacroAssembler *masm); |
| |
| }; |
| |
| // Absolute location of a jump or a label in some generated IonCode 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 IonCode is |
| // allocated. |
| |
| class CodeLocationJump |
| { |
| uint8_t *raw_; |
| #ifdef DEBUG |
| bool absolute_; |
| void setAbsolute() { |
| absolute_ = true; |
| } |
| void setRelative() { |
| absolute_ = false; |
| } |
| #else |
| void setAbsolute() const { |
| } |
| void setRelative() const { |
| } |
| #endif |
| |
| #ifdef JS_SMALL_BRANCH |
| uint8_t *jumpTableEntry_; |
| #endif |
| |
| public: |
| CodeLocationJump() { |
| raw_ = (uint8_t *) 0xdeadc0de; |
| setAbsolute(); |
| #ifdef JS_SMALL_BRANCH |
| jumpTableEntry_ = (uint8_t *) 0xdeadab1e; |
| #endif |
| } |
| CodeLocationJump(IonCode *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(IonCode *code, MacroAssembler* masm = NULL); |
| |
| bool isSet() const { |
| return raw_ != (uint8_t *) 0xdeadc0de; |
| } |
| |
| uint8_t *raw() const { |
| JS_ASSERT(absolute_ && isSet()); |
| return raw_; |
| } |
| uint8_t *offset() const { |
| JS_ASSERT(!absolute_ && isSet()); |
| return raw_; |
| } |
| |
| #ifdef JS_SMALL_BRANCH |
| uint8_t *jumpTableEntry() { |
| JS_ASSERT(absolute_); |
| return jumpTableEntry_; |
| } |
| #endif |
| }; |
| |
| class CodeLocationLabel |
| { |
| uint8_t *raw_; |
| #ifdef DEBUG |
| bool absolute_; |
| void setAbsolute() { |
| absolute_ = true; |
| } |
| void setRelative() { |
| absolute_ = false; |
| } |
| #else |
| void setAbsolute() const { |
| } |
| void setRelative() const { |
| } |
| #endif |
| |
| public: |
| CodeLocationLabel() { |
| raw_ = (uint8_t *) 0xdeadc0de; |
| setAbsolute(); |
| } |
| CodeLocationLabel(IonCode *code, CodeOffsetLabel base) { |
| *this = base; |
| repoint(code); |
| } |
| CodeLocationLabel(IonCode *code) { |
| raw_ = code->raw(); |
| setAbsolute(); |
| } |
| CodeLocationLabel(uint8_t *raw) { |
| raw_ = raw; |
| setAbsolute(); |
| } |
| |
| void operator = (CodeOffsetLabel base) { |
| raw_ = (uint8_t *)base.offset(); |
| setRelative(); |
| } |
| ptrdiff_t operator - (const CodeLocationLabel &other) { |
| return raw_ - other.raw_; |
| } |
| |
| void repoint(IonCode *code, MacroAssembler *masm = NULL); |
| |
| bool isSet() { |
| return raw_ != (uint8_t *) 0xdeadc0de; |
| } |
| |
| uint8_t *raw() { |
| JS_ASSERT(absolute_ && isSet()); |
| return raw_; |
| } |
| uint8_t *offset() { |
| JS_ASSERT(!absolute_ && isSet()); |
| return raw_; |
| } |
| }; |
| |
| |
| } // namespace jit |
| } // namespace js |
| |
| #endif /* jit_shared_Assembler_shared_h */ |