OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 // |
| 5 // Modified by the Subzero authors. |
| 6 // |
| 7 //===- subzero/src/assembler.h - Integrated assembler -----------*- C++ -*-===// |
| 8 // |
| 9 // The Subzero Code Generator |
| 10 // |
| 11 // This file is distributed under the University of Illinois Open Source |
| 12 // License. See LICENSE.TXT for details. |
| 13 // |
| 14 //===----------------------------------------------------------------------===// |
| 15 // |
| 16 // This file declares the Assembler base class. Instructions are assembled |
| 17 // by architecture-specific assemblers that derive from this base class. |
| 18 // This base class manages buffers and fixups for emitting code, etc. |
| 19 // |
| 20 //===----------------------------------------------------------------------===// |
| 21 |
| 22 #ifndef SUBZERO_SRC_ASSEMBLER_H |
| 23 #define SUBZERO_SRC_ASSEMBLER_H |
| 24 |
| 25 #include "IceDefs.h" |
| 26 |
| 27 #include "IceFixups.h" |
| 28 #include "llvm/Support/Allocator.h" |
| 29 |
| 30 namespace Ice { |
| 31 |
| 32 // Forward declarations. |
| 33 class Assembler; |
| 34 class AssemblerFixup; |
| 35 class AssemblerBuffer; |
| 36 class ConstantRelocatable; |
| 37 class MemoryRegion; |
| 38 |
| 39 // Assembler fixups are positions in generated code that hold relocation |
| 40 // information that needs to be processed before finalizing the code |
| 41 // into executable memory. |
| 42 class AssemblerFixup { |
| 43 public: |
| 44 virtual void Process(const MemoryRegion ®ion, intptr_t position) = 0; |
| 45 |
| 46 // It would be ideal if the destructor method could be made private, |
| 47 // but the g++ compiler complains when this is subclassed. |
| 48 virtual ~AssemblerFixup() { llvm_unreachable("~AssemblerFixup used"); } |
| 49 |
| 50 intptr_t position() const { return position_; } |
| 51 |
| 52 FixupKind kind() const { return kind_; } |
| 53 |
| 54 const ConstantRelocatable *value() const { return value_; } |
| 55 |
| 56 protected: |
| 57 AssemblerFixup(FixupKind Kind, const ConstantRelocatable *Value) |
| 58 : position_(0), kind_(Kind), value_(Value) {} |
| 59 |
| 60 private: |
| 61 intptr_t position_; |
| 62 FixupKind kind_; |
| 63 const ConstantRelocatable *value_; |
| 64 |
| 65 void set_position(intptr_t position) { position_ = position; } |
| 66 |
| 67 AssemblerFixup(const AssemblerFixup &) LLVM_DELETED_FUNCTION; |
| 68 AssemblerFixup &operator=(const AssemblerFixup &) LLVM_DELETED_FUNCTION; |
| 69 friend class AssemblerBuffer; |
| 70 }; |
| 71 |
| 72 // Assembler buffers are used to emit binary code. They grow on demand. |
| 73 class AssemblerBuffer { |
| 74 public: |
| 75 AssemblerBuffer(Assembler &); |
| 76 ~AssemblerBuffer(); |
| 77 |
| 78 // Basic support for emitting, loading, and storing. |
| 79 template <typename T> void Emit(T value) { |
| 80 assert(HasEnsuredCapacity()); |
| 81 *reinterpret_cast<T *>(cursor_) = value; |
| 82 cursor_ += sizeof(T); |
| 83 } |
| 84 |
| 85 template <typename T> T Load(intptr_t position) const { |
| 86 assert(position >= 0 && |
| 87 position <= (Size() - static_cast<intptr_t>(sizeof(T)))); |
| 88 return *reinterpret_cast<T *>(contents_ + position); |
| 89 } |
| 90 |
| 91 template <typename T> void Store(intptr_t position, T value) { |
| 92 assert(position >= 0 && |
| 93 position <= (Size() - static_cast<intptr_t>(sizeof(T)))); |
| 94 *reinterpret_cast<T *>(contents_ + position) = value; |
| 95 } |
| 96 |
| 97 // Emit a fixup at the current location. |
| 98 void EmitFixup(AssemblerFixup *fixup) { |
| 99 fixup->set_position(Size()); |
| 100 fixups_.push_back(fixup); |
| 101 } |
| 102 |
| 103 // Get the size of the emitted code. |
| 104 intptr_t Size() const { return cursor_ - contents_; } |
| 105 uintptr_t contents() const { return contents_; } |
| 106 |
| 107 // Copy the assembled instructions into the specified memory block |
| 108 // and apply all fixups. |
| 109 // TODO(jvoung): This will be different. We'll be writing the text |
| 110 // and reloc section to a file? |
| 111 void FinalizeInstructions(const MemoryRegion ®ion); |
| 112 |
| 113 // To emit an instruction to the assembler buffer, the EnsureCapacity helper |
| 114 // must be used to guarantee that the underlying data area is big enough to |
| 115 // hold the emitted instruction. Usage: |
| 116 // |
| 117 // AssemblerBuffer buffer; |
| 118 // AssemblerBuffer::EnsureCapacity ensured(&buffer); |
| 119 // ... emit bytes for single instruction ... |
| 120 |
| 121 #if defined(DEBUG) |
| 122 class EnsureCapacity { |
| 123 public: |
| 124 explicit EnsureCapacity(AssemblerBuffer *buffer); |
| 125 ~EnsureCapacity(); |
| 126 |
| 127 private: |
| 128 AssemblerBuffer *buffer_; |
| 129 intptr_t gap_; |
| 130 |
| 131 intptr_t ComputeGap() { return buffer_->Capacity() - buffer_->Size(); } |
| 132 }; |
| 133 |
| 134 bool has_ensured_capacity_; |
| 135 bool HasEnsuredCapacity() const { return has_ensured_capacity_; } |
| 136 #else |
| 137 class EnsureCapacity { |
| 138 public: |
| 139 explicit EnsureCapacity(AssemblerBuffer *buffer) { |
| 140 if (buffer->cursor() >= buffer->limit()) |
| 141 buffer->ExtendCapacity(); |
| 142 } |
| 143 }; |
| 144 |
| 145 // When building the C++ tests, assertion code is enabled. To allow |
| 146 // asserting that the user of the assembler buffer has ensured the |
| 147 // capacity needed for emitting, we add a dummy method in non-debug mode. |
| 148 bool HasEnsuredCapacity() const { return true; } |
| 149 #endif |
| 150 |
| 151 // Returns the position in the instruction stream. |
| 152 intptr_t GetPosition() const { return cursor_ - contents_; } |
| 153 |
| 154 // For bringup only. |
| 155 AssemblerFixup *GetLatestFixup() const; |
| 156 |
| 157 private: |
| 158 // The limit is set to kMinimumGap bytes before the end of the data area. |
| 159 // This leaves enough space for the longest possible instruction and allows |
| 160 // for a single, fast space check per instruction. |
| 161 static const intptr_t kMinimumGap = 32; |
| 162 |
| 163 uintptr_t contents_; |
| 164 uintptr_t cursor_; |
| 165 uintptr_t limit_; |
| 166 Assembler &assembler_; |
| 167 std::vector<AssemblerFixup *> fixups_; |
| 168 #if defined(DEBUG) |
| 169 bool fixups_processed_; |
| 170 #endif |
| 171 |
| 172 uintptr_t cursor() const { return cursor_; } |
| 173 uintptr_t limit() const { return limit_; } |
| 174 intptr_t Capacity() const { |
| 175 assert(limit_ >= contents_); |
| 176 return (limit_ - contents_) + kMinimumGap; |
| 177 } |
| 178 |
| 179 // Process the fixup chain. |
| 180 void ProcessFixups(const MemoryRegion ®ion); |
| 181 |
| 182 // Compute the limit based on the data area and the capacity. See |
| 183 // description of kMinimumGap for the reasoning behind the value. |
| 184 static uintptr_t ComputeLimit(uintptr_t data, intptr_t capacity) { |
| 185 return data + capacity - kMinimumGap; |
| 186 } |
| 187 |
| 188 void ExtendCapacity(); |
| 189 |
| 190 friend class AssemblerFixup; |
| 191 }; |
| 192 |
| 193 class Assembler { |
| 194 public: |
| 195 Assembler() {} |
| 196 ~Assembler() {} |
| 197 |
| 198 // Allocate a chunk of bytes using the per-Assembler allocator. |
| 199 uintptr_t AllocateBytes(size_t bytes) { |
| 200 // For now, alignment is not related to NaCl bundle alignment, since |
| 201 // the buffer's GetPosition is relative to the base. So NaCl bundle |
| 202 // alignment checks can be relative to that base. Later, the buffer |
| 203 // will be copied out to a ".text" section (or an in memory-buffer |
| 204 // that can be mprotect'ed with executable permission), and that |
| 205 // second buffer should be aligned for NaCl. |
| 206 const size_t Alignment = 16; |
| 207 return reinterpret_cast<uintptr_t>(Allocator.Allocate(bytes, Alignment)); |
| 208 } |
| 209 |
| 210 // Allocate data of type T using the per-Assembler allocator. |
| 211 template <typename T> T *Allocate() { return Allocator.Allocate<T>(); } |
| 212 |
| 213 private: |
| 214 llvm::BumpPtrAllocator Allocator; |
| 215 |
| 216 Assembler(const Assembler &) LLVM_DELETED_FUNCTION; |
| 217 Assembler &operator=(const Assembler &) LLVM_DELETED_FUNCTION; |
| 218 }; |
| 219 |
| 220 } // end of namespace Ice |
| 221 |
| 222 #endif // SUBZERO_SRC_ASSEMBLER_H_ |
OLD | NEW |