| OLD | NEW |
| 1 //===- subzero/src/IceAssembler.h - Integrated assembler --------*- C++ -*-===// | 1 //===- subzero/src/IceAssembler.h - Integrated assembler --------*- C++ -*-===// |
| 2 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 2 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 3 // for details. All rights reserved. Use of this source code is governed by a | 3 // for details. All rights reserved. Use of this source code is governed by a |
| 4 // BSD-style license that can be found in the LICENSE file. | 4 // BSD-style license that can be found in the LICENSE file. |
| 5 // | 5 // |
| 6 // Modified by the Subzero authors. | 6 // Modified by the Subzero authors. |
| 7 // | 7 // |
| 8 //===----------------------------------------------------------------------===// | 8 //===----------------------------------------------------------------------===// |
| 9 // | 9 // |
| 10 // The Subzero Code Generator | 10 // The Subzero Code Generator |
| 11 // | 11 // |
| 12 // This file is distributed under the University of Illinois Open Source | 12 // This file is distributed under the University of Illinois Open Source |
| 13 // License. See LICENSE.TXT for details. | 13 // License. See LICENSE.TXT for details. |
| 14 // | 14 // |
| 15 //===----------------------------------------------------------------------===// | 15 //===----------------------------------------------------------------------===// |
| 16 // | 16 /// |
| 17 // This file declares the Assembler base class. Instructions are assembled | 17 /// \file |
| 18 // by architecture-specific assemblers that derive from this base class. | 18 /// This file declares the Assembler base class. Instructions are assembled |
| 19 // This base class manages buffers and fixups for emitting code, etc. | 19 /// by architecture-specific assemblers that derive from this base class. |
| 20 // | 20 /// This base class manages buffers and fixups for emitting code, etc. |
| 21 /// |
| 21 //===----------------------------------------------------------------------===// | 22 //===----------------------------------------------------------------------===// |
| 22 | 23 |
| 23 #ifndef SUBZERO_SRC_ICEASSEMBLER_H | 24 #ifndef SUBZERO_SRC_ICEASSEMBLER_H |
| 24 #define SUBZERO_SRC_ICEASSEMBLER_H | 25 #define SUBZERO_SRC_ICEASSEMBLER_H |
| 25 | 26 |
| 26 #include "IceDefs.h" | 27 #include "IceDefs.h" |
| 27 #include "IceFixups.h" | 28 #include "IceFixups.h" |
| 28 | 29 |
| 29 namespace Ice { | 30 namespace Ice { |
| 30 | 31 |
| 31 // Assembler buffers are used to emit binary code. They grow on demand. | 32 /// Assembler buffers are used to emit binary code. They grow on demand. |
| 32 class AssemblerBuffer { | 33 class AssemblerBuffer { |
| 33 AssemblerBuffer(const AssemblerBuffer &) = delete; | 34 AssemblerBuffer(const AssemblerBuffer &) = delete; |
| 34 AssemblerBuffer &operator=(const AssemblerBuffer &) = delete; | 35 AssemblerBuffer &operator=(const AssemblerBuffer &) = delete; |
| 35 | 36 |
| 36 public: | 37 public: |
| 37 AssemblerBuffer(Assembler &); | 38 AssemblerBuffer(Assembler &); |
| 38 ~AssemblerBuffer(); | 39 ~AssemblerBuffer(); |
| 39 | 40 |
| 40 // Basic support for emitting, loading, and storing. | 41 /// Basic support for emitting, loading, and storing. |
| 41 template <typename T> void emit(T Value) { | 42 template <typename T> void emit(T Value) { |
| 42 assert(hasEnsuredCapacity()); | 43 assert(hasEnsuredCapacity()); |
| 43 *reinterpret_cast<T *>(Cursor) = Value; | 44 *reinterpret_cast<T *>(Cursor) = Value; |
| 44 Cursor += sizeof(T); | 45 Cursor += sizeof(T); |
| 45 } | 46 } |
| 46 | 47 |
| 47 template <typename T> T load(intptr_t Position) const { | 48 template <typename T> T load(intptr_t Position) const { |
| 48 assert(Position >= 0 && | 49 assert(Position >= 0 && |
| 49 Position <= (size() - static_cast<intptr_t>(sizeof(T)))); | 50 Position <= (size() - static_cast<intptr_t>(sizeof(T)))); |
| 50 return *reinterpret_cast<T *>(Contents + Position); | 51 return *reinterpret_cast<T *>(Contents + Position); |
| 51 } | 52 } |
| 52 | 53 |
| 53 template <typename T> void store(intptr_t Position, T Value) { | 54 template <typename T> void store(intptr_t Position, T Value) { |
| 54 assert(Position >= 0 && | 55 assert(Position >= 0 && |
| 55 Position <= (size() - static_cast<intptr_t>(sizeof(T)))); | 56 Position <= (size() - static_cast<intptr_t>(sizeof(T)))); |
| 56 *reinterpret_cast<T *>(Contents + Position) = Value; | 57 *reinterpret_cast<T *>(Contents + Position) = Value; |
| 57 } | 58 } |
| 58 | 59 |
| 59 // Emit a fixup at the current location. | 60 /// Emit a fixup at the current location. |
| 60 void emitFixup(AssemblerFixup *Fixup) { Fixup->set_position(size()); } | 61 void emitFixup(AssemblerFixup *Fixup) { Fixup->set_position(size()); } |
| 61 | 62 |
| 62 // Get the size of the emitted code. | 63 /// Get the size of the emitted code. |
| 63 intptr_t size() const { return Cursor - Contents; } | 64 intptr_t size() const { return Cursor - Contents; } |
| 64 uintptr_t contents() const { return Contents; } | 65 uintptr_t contents() const { return Contents; } |
| 65 | 66 |
| 66 // To emit an instruction to the assembler buffer, the EnsureCapacity helper | 67 /// To emit an instruction to the assembler buffer, the EnsureCapacity helper |
| 67 // must be used to guarantee that the underlying data area is big enough to | 68 /// must be used to guarantee that the underlying data area is big enough to |
| 68 // hold the emitted instruction. Usage: | 69 /// hold the emitted instruction. Usage: |
| 69 // | 70 /// |
| 70 // AssemblerBuffer buffer; | 71 /// AssemblerBuffer buffer; |
| 71 // AssemblerBuffer::EnsureCapacity ensured(&buffer); | 72 /// AssemblerBuffer::EnsureCapacity ensured(&buffer); |
| 72 // ... emit bytes for single instruction ... | 73 /// ... emit bytes for single instruction ... |
| 73 | |
| 74 class EnsureCapacity { | 74 class EnsureCapacity { |
| 75 EnsureCapacity(const EnsureCapacity &) = delete; | 75 EnsureCapacity(const EnsureCapacity &) = delete; |
| 76 EnsureCapacity &operator=(const EnsureCapacity &) = delete; | 76 EnsureCapacity &operator=(const EnsureCapacity &) = delete; |
| 77 | 77 |
| 78 public: | 78 public: |
| 79 explicit EnsureCapacity(AssemblerBuffer *Buffer) : Buffer(Buffer) { | 79 explicit EnsureCapacity(AssemblerBuffer *Buffer) : Buffer(Buffer) { |
| 80 if (Buffer->cursor() >= Buffer->limit()) | 80 if (Buffer->cursor() >= Buffer->limit()) |
| 81 Buffer->extendCapacity(); | 81 Buffer->extendCapacity(); |
| 82 if (BuildDefs::asserts()) | 82 if (BuildDefs::asserts()) |
| 83 validate(Buffer); | 83 validate(Buffer); |
| 84 } | 84 } |
| 85 ~EnsureCapacity(); | 85 ~EnsureCapacity(); |
| 86 | 86 |
| 87 private: | 87 private: |
| 88 AssemblerBuffer *Buffer; | 88 AssemblerBuffer *Buffer; |
| 89 intptr_t Gap = 0; | 89 intptr_t Gap = 0; |
| 90 | 90 |
| 91 void validate(AssemblerBuffer *Buffer); | 91 void validate(AssemblerBuffer *Buffer); |
| 92 intptr_t computeGap() { return Buffer->capacity() - Buffer->size(); } | 92 intptr_t computeGap() { return Buffer->capacity() - Buffer->size(); } |
| 93 }; | 93 }; |
| 94 | 94 |
| 95 bool HasEnsuredCapacity; | 95 bool HasEnsuredCapacity; |
| 96 bool hasEnsuredCapacity() const { | 96 bool hasEnsuredCapacity() const { |
| 97 if (BuildDefs::asserts()) | 97 if (BuildDefs::asserts()) |
| 98 return HasEnsuredCapacity; | 98 return HasEnsuredCapacity; |
| 99 // Disable the actual check in non-debug mode. | 99 // Disable the actual check in non-debug mode. |
| 100 return true; | 100 return true; |
| 101 } | 101 } |
| 102 | 102 |
| 103 // Returns the position in the instruction stream. | 103 /// Returns the position in the instruction stream. |
| 104 intptr_t getPosition() const { return Cursor - Contents; } | 104 intptr_t getPosition() const { return Cursor - Contents; } |
| 105 | 105 |
| 106 // Create and track a fixup in the current function. | 106 /// Create and track a fixup in the current function. |
| 107 AssemblerFixup *createFixup(FixupKind Kind, const Constant *Value); | 107 AssemblerFixup *createFixup(FixupKind Kind, const Constant *Value); |
| 108 | 108 |
| 109 const FixupRefList &fixups() const { return Fixups; } | 109 const FixupRefList &fixups() const { return Fixups; } |
| 110 | 110 |
| 111 void setSize(intptr_t NewSize) { | 111 void setSize(intptr_t NewSize) { |
| 112 assert(NewSize <= size()); | 112 assert(NewSize <= size()); |
| 113 Cursor = Contents + NewSize; | 113 Cursor = Contents + NewSize; |
| 114 } | 114 } |
| 115 | 115 |
| 116 private: | 116 private: |
| 117 // The limit is set to kMinimumGap bytes before the end of the data area. | 117 /// The limit is set to kMinimumGap bytes before the end of the data area. |
| 118 // This leaves enough space for the longest possible instruction and allows | 118 /// This leaves enough space for the longest possible instruction and allows |
| 119 // for a single, fast space check per instruction. | 119 /// for a single, fast space check per instruction. |
| 120 static constexpr intptr_t kMinimumGap = 32; | 120 static constexpr intptr_t kMinimumGap = 32; |
| 121 | 121 |
| 122 uintptr_t Contents; | 122 uintptr_t Contents; |
| 123 uintptr_t Cursor; | 123 uintptr_t Cursor; |
| 124 uintptr_t Limit; | 124 uintptr_t Limit; |
| 125 // The member variable is named Assemblr to avoid hiding the class Assembler. | 125 // The member variable is named Assemblr to avoid hiding the class Assembler. |
| 126 Assembler &Assemblr; | 126 Assembler &Assemblr; |
| 127 // List of pool-allocated fixups relative to the current function. | 127 /// List of pool-allocated fixups relative to the current function. |
| 128 FixupRefList Fixups; | 128 FixupRefList Fixups; |
| 129 | 129 |
| 130 uintptr_t cursor() const { return Cursor; } | 130 uintptr_t cursor() const { return Cursor; } |
| 131 uintptr_t limit() const { return Limit; } | 131 uintptr_t limit() const { return Limit; } |
| 132 intptr_t capacity() const { | 132 intptr_t capacity() const { |
| 133 assert(Limit >= Contents); | 133 assert(Limit >= Contents); |
| 134 return (Limit - Contents) + kMinimumGap; | 134 return (Limit - Contents) + kMinimumGap; |
| 135 } | 135 } |
| 136 | 136 |
| 137 // Compute the limit based on the data area and the capacity. See | 137 /// Compute the limit based on the data area and the capacity. See |
| 138 // description of kMinimumGap for the reasoning behind the value. | 138 /// description of kMinimumGap for the reasoning behind the value. |
| 139 static uintptr_t computeLimit(uintptr_t Data, intptr_t Capacity) { | 139 static uintptr_t computeLimit(uintptr_t Data, intptr_t Capacity) { |
| 140 return Data + Capacity - kMinimumGap; | 140 return Data + Capacity - kMinimumGap; |
| 141 } | 141 } |
| 142 | 142 |
| 143 void extendCapacity(); | 143 void extendCapacity(); |
| 144 }; | 144 }; |
| 145 | 145 |
| 146 class Assembler { | 146 class Assembler { |
| 147 Assembler() = delete; | 147 Assembler() = delete; |
| 148 Assembler(const Assembler &) = delete; | 148 Assembler(const Assembler &) = delete; |
| 149 Assembler &operator=(const Assembler &) = delete; | 149 Assembler &operator=(const Assembler &) = delete; |
| 150 | 150 |
| 151 public: | 151 public: |
| 152 enum AssemblerKind { | 152 enum AssemblerKind { |
| 153 Asm_ARM32, | 153 Asm_ARM32, |
| 154 Asm_MIPS32, | 154 Asm_MIPS32, |
| 155 Asm_X8632, | 155 Asm_X8632, |
| 156 Asm_X8664, | 156 Asm_X8664, |
| 157 }; | 157 }; |
| 158 | 158 |
| 159 virtual ~Assembler() = default; | 159 virtual ~Assembler() = default; |
| 160 | 160 |
| 161 // Allocate a chunk of bytes using the per-Assembler allocator. | 161 /// Allocate a chunk of bytes using the per-Assembler allocator. |
| 162 uintptr_t allocateBytes(size_t bytes) { | 162 uintptr_t allocateBytes(size_t bytes) { |
| 163 // For now, alignment is not related to NaCl bundle alignment, since | 163 // For now, alignment is not related to NaCl bundle alignment, since |
| 164 // the buffer's GetPosition is relative to the base. So NaCl bundle | 164 // the buffer's GetPosition is relative to the base. So NaCl bundle |
| 165 // alignment checks can be relative to that base. Later, the buffer | 165 // alignment checks can be relative to that base. Later, the buffer |
| 166 // will be copied out to a ".text" section (or an in memory-buffer | 166 // will be copied out to a ".text" section (or an in memory-buffer |
| 167 // that can be mprotect'ed with executable permission), and that | 167 // that can be mprotect'ed with executable permission), and that |
| 168 // second buffer should be aligned for NaCl. | 168 // second buffer should be aligned for NaCl. |
| 169 const size_t Alignment = 16; | 169 const size_t Alignment = 16; |
| 170 return reinterpret_cast<uintptr_t>(Allocator.Allocate(bytes, Alignment)); | 170 return reinterpret_cast<uintptr_t>(Allocator.Allocate(bytes, Alignment)); |
| 171 } | 171 } |
| 172 | 172 |
| 173 // Allocate data of type T using the per-Assembler allocator. | 173 /// Allocate data of type T using the per-Assembler allocator. |
| 174 template <typename T> T *allocate() { return Allocator.Allocate<T>(); } | 174 template <typename T> T *allocate() { return Allocator.Allocate<T>(); } |
| 175 | 175 |
| 176 // Align the tail end of the function to the required target alignment. | 176 /// Align the tail end of the function to the required target alignment. |
| 177 virtual void alignFunction() = 0; | 177 virtual void alignFunction() = 0; |
| 178 | 178 |
| 179 // Add nop padding of a particular width to the current bundle. | 179 /// Add nop padding of a particular width to the current bundle. |
| 180 virtual void padWithNop(intptr_t Padding) = 0; | 180 virtual void padWithNop(intptr_t Padding) = 0; |
| 181 | 181 |
| 182 virtual SizeT getBundleAlignLog2Bytes() const = 0; | 182 virtual SizeT getBundleAlignLog2Bytes() const = 0; |
| 183 | 183 |
| 184 virtual const char *getNonExecPadDirective() const = 0; | 184 virtual const char *getNonExecPadDirective() const = 0; |
| 185 virtual llvm::ArrayRef<uint8_t> getNonExecBundlePadding() const = 0; | 185 virtual llvm::ArrayRef<uint8_t> getNonExecBundlePadding() const = 0; |
| 186 | 186 |
| 187 // Mark the current text location as the start of a CFG node | 187 /// Mark the current text location as the start of a CFG node |
| 188 // (represented by NodeNumber). | 188 /// (represented by NodeNumber). |
| 189 virtual void bindCfgNodeLabel(SizeT NodeNumber) = 0; | 189 virtual void bindCfgNodeLabel(SizeT NodeNumber) = 0; |
| 190 | 190 |
| 191 virtual bool fixupIsPCRel(FixupKind Kind) const = 0; | 191 virtual bool fixupIsPCRel(FixupKind Kind) const = 0; |
| 192 | 192 |
| 193 // Return a view of all the bytes of code for the current function. | 193 // Return a view of all the bytes of code for the current function. |
| 194 llvm::StringRef getBufferView() const; | 194 llvm::StringRef getBufferView() const; |
| 195 | 195 |
| 196 const FixupRefList &fixups() const { return Buffer.fixups(); } | 196 const FixupRefList &fixups() const { return Buffer.fixups(); } |
| 197 | 197 |
| 198 AssemblerFixup *createFixup(FixupKind Kind, const Constant *Value) { | 198 AssemblerFixup *createFixup(FixupKind Kind, const Constant *Value) { |
| 199 return Buffer.createFixup(Kind, Value); | 199 return Buffer.createFixup(Kind, Value); |
| 200 } | 200 } |
| 201 | 201 |
| 202 void emitIASBytes(GlobalContext *Ctx) const; | 202 void emitIASBytes(GlobalContext *Ctx) const; |
| 203 bool getInternal() const { return IsInternal; } | 203 bool getInternal() const { return IsInternal; } |
| 204 void setInternal(bool Internal) { IsInternal = Internal; } | 204 void setInternal(bool Internal) { IsInternal = Internal; } |
| 205 const IceString &getFunctionName() { return FunctionName; } | 205 const IceString &getFunctionName() { return FunctionName; } |
| 206 void setFunctionName(const IceString &NewName) { FunctionName = NewName; } | 206 void setFunctionName(const IceString &NewName) { FunctionName = NewName; } |
| 207 intptr_t getBufferSize() const { return Buffer.size(); } | 207 intptr_t getBufferSize() const { return Buffer.size(); } |
| 208 // Roll back to a (smaller) size. | 208 /// Roll back to a (smaller) size. |
| 209 void setBufferSize(intptr_t NewSize) { Buffer.setSize(NewSize); } | 209 void setBufferSize(intptr_t NewSize) { Buffer.setSize(NewSize); } |
| 210 void setPreliminary(bool Value) { Preliminary = Value; } | 210 void setPreliminary(bool Value) { Preliminary = Value; } |
| 211 bool getPreliminary() const { return Preliminary; } | 211 bool getPreliminary() const { return Preliminary; } |
| 212 | 212 |
| 213 AssemblerKind getKind() const { return Kind; } | 213 AssemblerKind getKind() const { return Kind; } |
| 214 | 214 |
| 215 protected: | 215 protected: |
| 216 explicit Assembler(AssemblerKind Kind) | 216 explicit Assembler(AssemblerKind Kind) |
| 217 : Kind(Kind), Allocator(), Buffer(*this) {} | 217 : Kind(Kind), Allocator(), Buffer(*this) {} |
| 218 | 218 |
| 219 private: | 219 private: |
| 220 const AssemblerKind Kind; | 220 const AssemblerKind Kind; |
| 221 | 221 |
| 222 ArenaAllocator<32 * 1024> Allocator; | 222 ArenaAllocator<32 * 1024> Allocator; |
| 223 // FunctionName and IsInternal are transferred from the original Cfg | 223 /// FunctionName and IsInternal are transferred from the original Cfg |
| 224 // object, since the Cfg object may be deleted by the time the | 224 /// object, since the Cfg object may be deleted by the time the |
| 225 // assembler buffer is emitted. | 225 /// assembler buffer is emitted. |
| 226 IceString FunctionName = ""; | 226 IceString FunctionName = ""; |
| 227 bool IsInternal = false; | 227 bool IsInternal = false; |
| 228 // Preliminary indicates whether a preliminary pass is being made | 228 /// Preliminary indicates whether a preliminary pass is being made |
| 229 // for calculating bundle padding (Preliminary=true), versus the | 229 /// for calculating bundle padding (Preliminary=true), versus the |
| 230 // final pass where all changes to label bindings, label links, and | 230 /// final pass where all changes to label bindings, label links, and |
| 231 // relocation fixups are fully committed (Preliminary=false). | 231 /// relocation fixups are fully committed (Preliminary=false). |
| 232 bool Preliminary = false; | 232 bool Preliminary = false; |
| 233 | 233 |
| 234 protected: | 234 protected: |
| 235 // Buffer's constructor uses the Allocator, so it needs to appear after it. | 235 // Buffer's constructor uses the Allocator, so it needs to appear after it. |
| 236 // TODO(jpp): dependencies on construction order are a nice way of shooting | 236 // TODO(jpp): dependencies on construction order are a nice way of shooting |
| 237 // yourself in the foot. Fix this. | 237 // yourself in the foot. Fix this. |
| 238 AssemblerBuffer Buffer; | 238 AssemblerBuffer Buffer; |
| 239 }; | 239 }; |
| 240 | 240 |
| 241 } // end of namespace Ice | 241 } // end of namespace Ice |
| 242 | 242 |
| 243 #endif // SUBZERO_SRC_ICEASSEMBLER_H_ | 243 #endif // SUBZERO_SRC_ICEASSEMBLER_H_ |
| OLD | NEW |