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_IonAssemblerBuffer_h michael@0: #define jit_shared_IonAssemblerBuffer_h michael@0: michael@0: // needed for the definition of Label :( michael@0: #include "jit/shared/Assembler-shared.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: // This should theoretically reside inside of AssemblerBuffer, but that won't be nice michael@0: // AssemblerBuffer is templated, BufferOffset would be indirectly. michael@0: // A BufferOffset is the offset into a buffer, expressed in bytes of instructions. michael@0: michael@0: class BufferOffset michael@0: { michael@0: int offset; michael@0: public: michael@0: friend BufferOffset nextOffset(); michael@0: explicit BufferOffset(int offset_) : offset(offset_) {} michael@0: // Return the offset as a raw integer. michael@0: int getOffset() const { return offset; } michael@0: michael@0: // A BOffImm is a Branch Offset Immediate. It is an architecture-specific michael@0: // structure that holds the immediate for a pc relative branch. michael@0: // diffB takes the label for the destination of the branch, and encodes michael@0: // the immediate for the branch. This will need to be fixed up later, since michael@0: // A pool may be inserted between the branch and its destination michael@0: template michael@0: BOffImm diffB(BufferOffset other) const { michael@0: return BOffImm(offset - other.offset); michael@0: } michael@0: michael@0: template michael@0: BOffImm diffB(Label *other) const { michael@0: JS_ASSERT(other->bound()); michael@0: return BOffImm(offset - other->offset()); michael@0: } michael@0: michael@0: explicit BufferOffset(Label *l) : offset(l->offset()) { michael@0: } michael@0: explicit BufferOffset(RepatchLabel *l) : offset(l->offset()) { michael@0: } michael@0: michael@0: BufferOffset() : offset(INT_MIN) {} michael@0: bool assigned() const { return offset != INT_MIN; }; michael@0: }; michael@0: michael@0: template michael@0: struct BufferSlice { michael@0: protected: michael@0: BufferSlice *prev; michael@0: BufferSlice *next; michael@0: // How much data has been added to the current node. michael@0: uint32_t nodeSize; michael@0: public: michael@0: BufferSlice *getNext() { return this->next; } michael@0: BufferSlice *getPrev() { return this->prev; } michael@0: void setNext(BufferSlice *next_) { michael@0: JS_ASSERT(this->next == nullptr); michael@0: JS_ASSERT(next_->prev == nullptr); michael@0: this->next = next_; michael@0: next_->prev = this; michael@0: } michael@0: michael@0: mozilla::Array instructions; michael@0: unsigned int size() { michael@0: return nodeSize; michael@0: } michael@0: BufferSlice() : prev(nullptr), next(nullptr), nodeSize(0) {} michael@0: void putBlob(uint32_t instSize, uint8_t* inst) { michael@0: if (inst != nullptr) michael@0: memcpy(&instructions[size()], inst, instSize); michael@0: nodeSize += instSize; michael@0: } michael@0: }; michael@0: michael@0: template michael@0: struct AssemblerBuffer michael@0: { michael@0: public: michael@0: AssemblerBuffer() : head(nullptr), tail(nullptr), m_oom(false), m_bail(false), bufferSize(0), LifoAlloc_(8192) {} michael@0: protected: michael@0: typedef BufferSlice Slice; michael@0: typedef AssemblerBuffer AssemblerBuffer_; michael@0: Slice *head; michael@0: Slice *tail; michael@0: public: michael@0: bool m_oom; michael@0: bool m_bail; michael@0: // How much data has been added to the buffer thusfar. michael@0: uint32_t bufferSize; michael@0: uint32_t lastInstSize; michael@0: bool isAligned(int alignment) const { michael@0: // make sure the requested alignment is a power of two. michael@0: JS_ASSERT((alignment & (alignment-1)) == 0); michael@0: return !(size() & (alignment - 1)); michael@0: } michael@0: virtual Slice *newSlice(LifoAlloc &a) { michael@0: Slice *tmp = static_cast(a.alloc(sizeof(Slice))); michael@0: if (!tmp) { michael@0: m_oom = true; michael@0: return nullptr; michael@0: } michael@0: new (tmp) Slice; michael@0: return tmp; michael@0: } michael@0: bool ensureSpace(int size) { michael@0: if (tail != nullptr && tail->size()+size <= SliceSize) michael@0: return true; michael@0: Slice *tmp = newSlice(LifoAlloc_); michael@0: if (tmp == nullptr) michael@0: return false; michael@0: if (tail != nullptr) { michael@0: bufferSize += tail->size(); michael@0: tail->setNext(tmp); michael@0: } michael@0: tail = tmp; michael@0: if (head == nullptr) { michael@0: finger = tmp; michael@0: finger_offset = 0; michael@0: head = tmp; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: BufferOffset putByte(uint8_t value) { michael@0: return putBlob(sizeof(value), (uint8_t*)&value); michael@0: } michael@0: michael@0: BufferOffset putShort(uint16_t value) { michael@0: return putBlob(sizeof(value), (uint8_t*)&value); michael@0: } michael@0: michael@0: BufferOffset putInt(uint32_t value) { michael@0: return putBlob(sizeof(value), (uint8_t*)&value); michael@0: } michael@0: BufferOffset putBlob(uint32_t instSize, uint8_t *inst) { michael@0: if (!ensureSpace(instSize)) michael@0: return BufferOffset(); michael@0: BufferOffset ret = nextOffset(); michael@0: tail->putBlob(instSize, inst); michael@0: return ret; michael@0: } michael@0: unsigned int size() const { michael@0: int executableSize; michael@0: if (tail != nullptr) michael@0: executableSize = bufferSize + tail->size(); michael@0: else michael@0: executableSize = bufferSize; michael@0: return executableSize; michael@0: } michael@0: unsigned int uncheckedSize() const { michael@0: return size(); michael@0: } michael@0: bool oom() const { michael@0: return m_oom || m_bail; michael@0: } michael@0: bool bail() const { michael@0: return m_bail; michael@0: } michael@0: void fail_oom() { michael@0: m_oom = true; michael@0: } michael@0: void fail_bail() { michael@0: m_bail = true; michael@0: } michael@0: // finger for speeding up accesses michael@0: Slice *finger; michael@0: unsigned int finger_offset; michael@0: Inst *getInst(BufferOffset off) { michael@0: int local_off = off.getOffset(); michael@0: // don't update the structure's finger in place, so there is the option michael@0: // to not update it. michael@0: Slice *cur = nullptr; michael@0: int cur_off; michael@0: // get the offset that we'd be dealing with by walking through backwards michael@0: int end_off = bufferSize - local_off; michael@0: // If end_off is negative, then it is in the last chunk, and there is no michael@0: // real work to be done. michael@0: if (end_off <= 0) { michael@0: return (Inst*)&tail->instructions[-end_off]; michael@0: } michael@0: bool used_finger = false; michael@0: int finger_off = abs((int)(local_off - finger_offset)); michael@0: if (finger_off < Min(local_off, end_off)) { michael@0: // The finger offset is minimal, use the finger. michael@0: cur = finger; michael@0: cur_off = finger_offset; michael@0: used_finger = true; michael@0: } else if (local_off < end_off) { michael@0: // it is closest to the start michael@0: cur = head; michael@0: cur_off = 0; michael@0: } else { michael@0: // it is closest to the end michael@0: cur = tail; michael@0: cur_off = bufferSize; michael@0: } michael@0: int count = 0; michael@0: if (local_off < cur_off) { michael@0: for (; cur != nullptr; cur = cur->getPrev(), cur_off -= cur->size()) { michael@0: if (local_off >= cur_off) { michael@0: local_off -= cur_off; michael@0: break; michael@0: } michael@0: count++; michael@0: } michael@0: JS_ASSERT(cur != nullptr); michael@0: } else { michael@0: for (; cur != nullptr; cur = cur->getNext()) { michael@0: int cur_size = cur->size(); michael@0: if (local_off < cur_off + cur_size) { michael@0: local_off -= cur_off; michael@0: break; michael@0: } michael@0: cur_off += cur_size; michael@0: count++; michael@0: } michael@0: JS_ASSERT(cur != nullptr); michael@0: } michael@0: if (count > 2 || used_finger) { michael@0: finger = cur; michael@0: finger_offset = cur_off; michael@0: } michael@0: // the offset within this node should not be larger than the node itself. michael@0: JS_ASSERT(local_off < (int)cur->size()); michael@0: return (Inst*)&cur->instructions[local_off]; michael@0: } michael@0: BufferOffset nextOffset() const { michael@0: if (tail != nullptr) michael@0: return BufferOffset(bufferSize + tail->size()); michael@0: else michael@0: return BufferOffset(bufferSize); michael@0: } michael@0: BufferOffset prevOffset() const { michael@0: MOZ_ASSUME_UNREACHABLE("Don't current record lastInstSize"); michael@0: } michael@0: michael@0: // Break the instruction stream so we can go back and edit it at this point michael@0: void perforate() { michael@0: Slice *tmp = newSlice(LifoAlloc_); michael@0: if (!tmp) michael@0: m_oom = true; michael@0: bufferSize += tail->size(); michael@0: tail->setNext(tmp); michael@0: tail = tmp; michael@0: } michael@0: michael@0: class AssemblerBufferInstIterator { michael@0: private: michael@0: BufferOffset bo; michael@0: AssemblerBuffer_ *m_buffer; michael@0: public: michael@0: AssemblerBufferInstIterator(BufferOffset off, AssemblerBuffer_ *buff) : bo(off), m_buffer(buff) {} michael@0: Inst *next() { michael@0: Inst *i = m_buffer->getInst(bo); michael@0: bo = BufferOffset(bo.getOffset()+i->size()); michael@0: return cur(); michael@0: }; michael@0: Inst *cur() { michael@0: return m_buffer->getInst(bo); michael@0: } michael@0: }; michael@0: public: michael@0: LifoAlloc LifoAlloc_; michael@0: }; michael@0: michael@0: } // ion michael@0: } // js michael@0: #endif /* jit_shared_IonAssemblerBuffer_h */