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