• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- subzero/src/IceAssembler.h - Integrated assembler --------*- C++ -*-===//
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
4 // BSD-style license that can be found in the LICENSE file.
5 //
6 // Modified by the Subzero authors.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //                        The Subzero Code Generator
11 //
12 // This file is distributed under the University of Illinois Open Source
13 // License. See LICENSE.TXT for details.
14 //
15 //===----------------------------------------------------------------------===//
16 ///
17 /// \file
18 /// \brief Declares the Assembler base class.
19 ///
20 /// Instructions are assembled by architecture-specific assemblers that derive
21 /// from this base class. This base class manages buffers and fixups for
22 /// emitting code, etc.
23 ///
24 //===----------------------------------------------------------------------===//
25 
26 #ifndef SUBZERO_SRC_ICEASSEMBLER_H
27 #define SUBZERO_SRC_ICEASSEMBLER_H
28 
29 #include "IceDefs.h"
30 #include "IceFixups.h"
31 #include "IceStringPool.h"
32 #include "IceUtils.h"
33 
34 #include "llvm/Support/Allocator.h"
35 
36 namespace Ice {
37 
38 class Assembler;
39 
40 /// A Label can be in one of three states:
41 ///  - Unused.
42 ///  - Linked, unplaced and tracking the position of branches to the label.
43 ///  - Bound, placed and tracking its position.
44 class Label {
45   Label(const Label &) = delete;
46   Label &operator=(const Label &) = delete;
47 
48 public:
49   Label() = default;
50   virtual ~Label() = default;
51 
finalCheck()52   virtual void finalCheck() const {
53     // Assert if label is being destroyed with unresolved branches pending.
54     assert(!isLinked());
55   }
56 
57   /// Returns the encoded position stored in the label.
getEncodedPosition()58   intptr_t getEncodedPosition() const { return Position; }
59 
60   /// Returns the position for bound labels (branches that come after this are
61   /// considered backward branches). Cannot be used for unused or linked labels.
getPosition()62   intptr_t getPosition() const {
63     assert(isBound());
64     return -Position - kWordSize;
65   }
66 
67   /// Returns the position of an earlier branch instruction that was linked to
68   /// this label (branches that use this are considered forward branches). The
69   /// linked instructions form a linked list, of sorts, using the instruction's
70   /// displacement field for the location of the next instruction that is also
71   /// linked to this label.
getLinkPosition()72   intptr_t getLinkPosition() const {
73     assert(isLinked());
74     return Position - kWordSize;
75   }
76 
setPosition(intptr_t NewValue)77   void setPosition(intptr_t NewValue) { Position = NewValue; }
78 
isBound()79   bool isBound() const { return Position < 0; }
isLinked()80   bool isLinked() const { return Position > 0; }
81 
isUnused()82   virtual bool isUnused() const { return Position == 0; }
83 
bindTo(intptr_t position)84   void bindTo(intptr_t position) {
85     assert(!isBound());
86     Position = -position - kWordSize;
87     assert(isBound());
88   }
89 
90   void linkTo(const Assembler &Asm, intptr_t position);
91 
92 protected:
93   intptr_t Position = 0;
94 
95   // TODO(jvoung): why are labels offset by this?
96   static constexpr uint32_t kWordSize = sizeof(uint32_t);
97 };
98 
99 /// Assembler buffers are used to emit binary code. They grow on demand.
100 class AssemblerBuffer {
101   AssemblerBuffer(const AssemblerBuffer &) = delete;
102   AssemblerBuffer &operator=(const AssemblerBuffer &) = delete;
103 
104 public:
105   AssemblerBuffer(Assembler &);
106   ~AssemblerBuffer();
107 
108   /// \name Basic support for emitting, loading, and storing.
109   /// @{
110   // These use memcpy instead of assignment to avoid undefined behaviour of
111   // assigning to unaligned addresses. Since the size of the copy is known the
112   // compiler can inline the memcpy with simple moves.
emit(T Value)113   template <typename T> void emit(T Value) {
114     assert(hasEnsuredCapacity());
115     memcpy(reinterpret_cast<void *>(Cursor), &Value, sizeof(T));
116     Cursor += sizeof(T);
117   }
118 
load(intptr_t Position)119   template <typename T> T load(intptr_t Position) const {
120     assert(Position >= 0 &&
121            Position <= (size() - static_cast<intptr_t>(sizeof(T))));
122     T Value;
123     memcpy(&Value, reinterpret_cast<void *>(Contents + Position), sizeof(T));
124     return Value;
125   }
126 
store(intptr_t Position,T Value)127   template <typename T> void store(intptr_t Position, T Value) {
128     assert(Position >= 0 &&
129            Position <= (size() - static_cast<intptr_t>(sizeof(T))));
130     memcpy(reinterpret_cast<void *>(Contents + Position), &Value, sizeof(T));
131   }
132   /// @{
133 
134   /// Emit a fixup at the current location.
emitFixup(AssemblerFixup * Fixup)135   void emitFixup(AssemblerFixup *Fixup) { Fixup->set_position(size()); }
136 
137   /// Get the size of the emitted code.
size()138   intptr_t size() const { return Cursor - Contents; }
contents()139   uintptr_t contents() const { return Contents; }
140 
141   /// To emit an instruction to the assembler buffer, the EnsureCapacity helper
142   /// must be used to guarantee that the underlying data area is big enough to
143   /// hold the emitted instruction. Usage:
144   ///
145   ///     AssemblerBuffer buffer;
146   ///     AssemblerBuffer::EnsureCapacity ensured(&buffer);
147   ///     ... emit bytes for single instruction ...
148   class EnsureCapacity {
149     EnsureCapacity(const EnsureCapacity &) = delete;
150     EnsureCapacity &operator=(const EnsureCapacity &) = delete;
151 
152   public:
EnsureCapacity(AssemblerBuffer * Buffer)153     explicit EnsureCapacity(AssemblerBuffer *Buffer) : Buffer(Buffer) {
154       if (Buffer->cursor() >= Buffer->limit())
155         Buffer->extendCapacity();
156       if (BuildDefs::asserts())
157         validate(Buffer);
158     }
159     ~EnsureCapacity();
160 
161   private:
162     AssemblerBuffer *Buffer;
163     intptr_t Gap = 0;
164 
165     void validate(AssemblerBuffer *Buffer);
computeGap()166     intptr_t computeGap() { return Buffer->capacity() - Buffer->size(); }
167   };
168 
169   bool HasEnsuredCapacity;
hasEnsuredCapacity()170   bool hasEnsuredCapacity() const {
171     if (BuildDefs::asserts())
172       return HasEnsuredCapacity;
173     // Disable the actual check in non-debug mode.
174     return true;
175   }
176 
177   /// Returns the position in the instruction stream.
getPosition()178   intptr_t getPosition() const { return Cursor - Contents; }
179 
180   /// Create and track a fixup in the current function.
181   AssemblerFixup *createFixup(FixupKind Kind, const Constant *Value);
182 
183   /// Create and track a textual fixup in the current function.
184   AssemblerTextFixup *createTextFixup(const std::string &Text,
185                                       size_t BytesUsed);
186 
187   /// Mark that an attempt was made to emit, but failed. Hence, in order to
188   /// continue, one must emit a text fixup.
setNeedsTextFixup()189   void setNeedsTextFixup() { TextFixupNeeded = true; }
resetNeedsTextFixup()190   void resetNeedsTextFixup() { TextFixupNeeded = false; }
191 
192   /// Returns true if last emit failed and needs a text fixup.
needsTextFixup()193   bool needsTextFixup() const { return TextFixupNeeded; }
194 
195   /// Installs a created fixup, after it has been allocated.
196   void installFixup(AssemblerFixup *F);
197 
fixups()198   const FixupRefList &fixups() const { return Fixups; }
199 
setSize(intptr_t NewSize)200   void setSize(intptr_t NewSize) {
201     assert(NewSize <= size());
202     Cursor = Contents + NewSize;
203   }
204 
205 private:
206   /// The limit is set to kMinimumGap bytes before the end of the data area.
207   /// This leaves enough space for the longest possible instruction and allows
208   /// for a single, fast space check per instruction.
209   static constexpr intptr_t kMinimumGap = 32;
210 
211   uintptr_t Contents;
212   uintptr_t Cursor;
213   uintptr_t Limit;
214   // The member variable is named Assemblr to avoid hiding the class Assembler.
215   Assembler &Assemblr;
216   /// List of pool-allocated fixups relative to the current function.
217   FixupRefList Fixups;
218   // True if a textual fixup is needed, because the assembler was unable to
219   // emit the last request.
220   bool TextFixupNeeded;
221 
cursor()222   uintptr_t cursor() const { return Cursor; }
limit()223   uintptr_t limit() const { return Limit; }
capacity()224   intptr_t capacity() const {
225     assert(Limit >= Contents);
226     return (Limit - Contents) + kMinimumGap;
227   }
228 
229   /// Compute the limit based on the data area and the capacity. See description
230   /// of kMinimumGap for the reasoning behind the value.
computeLimit(uintptr_t Data,intptr_t Capacity)231   static uintptr_t computeLimit(uintptr_t Data, intptr_t Capacity) {
232     return Data + Capacity - kMinimumGap;
233   }
234 
235   void extendCapacity();
236 };
237 
238 class Assembler {
239   Assembler() = delete;
240   Assembler(const Assembler &) = delete;
241   Assembler &operator=(const Assembler &) = delete;
242 
243 public:
244   enum AssemblerKind {
245     Asm_ARM32,
246     Asm_MIPS32,
247     Asm_X8632,
248     Asm_X8664,
249   };
250 
251   virtual ~Assembler() = default;
252 
253   /// Allocate a chunk of bytes using the per-Assembler allocator.
allocateBytes(size_t bytes)254   uintptr_t allocateBytes(size_t bytes) {
255     // For now, alignment is not related to NaCl bundle alignment, since the
256     // buffer's GetPosition is relative to the base. So NaCl bundle alignment
257     // checks can be relative to that base. Later, the buffer will be copied
258     // out to a ".text" section (or an in memory-buffer that can be mprotect'ed
259     // with executable permission), and that second buffer should be aligned
260     // for NaCl.
261     const size_t Alignment = 16;
262     return reinterpret_cast<uintptr_t>(Allocator.Allocate(bytes, Alignment));
263   }
264 
265   /// Allocate data of type T using the per-Assembler allocator.
allocate()266   template <typename T> T *allocate() { return Allocator.Allocate<T>(); }
267 
268   /// Align the tail end of the function to the required target alignment.
269   virtual void alignFunction() = 0;
270   /// Align the tail end of the basic block to the required target alignment.
alignCfgNode()271   void alignCfgNode() {
272     const SizeT Align = 1 << getBundleAlignLog2Bytes();
273     padWithNop(Utils::OffsetToAlignment(Buffer.getPosition(), Align));
274   }
275 
276   /// Add nop padding of a particular width to the current bundle.
277   virtual void padWithNop(intptr_t Padding) = 0;
278 
279   virtual SizeT getBundleAlignLog2Bytes() const = 0;
280 
281   virtual const char *getAlignDirective() const = 0;
282   virtual llvm::ArrayRef<uint8_t> getNonExecBundlePadding() const = 0;
283 
284   /// Get the label for a CfgNode.
285   virtual Label *getCfgNodeLabel(SizeT NodeNumber) = 0;
286   /// Mark the current text location as the start of a CFG node.
287   virtual void bindCfgNodeLabel(const CfgNode *Node) = 0;
288 
289   virtual bool fixupIsPCRel(FixupKind Kind) const = 0;
290 
291   /// Return a view of all the bytes of code for the current function.
292   llvm::StringRef getBufferView() const;
293 
294   /// Return the value of the given type in the corresponding buffer.
load(intptr_t Position)295   template <typename T> T load(intptr_t Position) const {
296     return Buffer.load<T>(Position);
297   }
298 
store(intptr_t Position,T Value)299   template <typename T> void store(intptr_t Position, T Value) {
300     Buffer.store(Position, Value);
301   }
302 
303   /// Emit a fixup at the current location.
emitFixup(AssemblerFixup * Fixup)304   void emitFixup(AssemblerFixup *Fixup) { Buffer.emitFixup(Fixup); }
305 
fixups()306   const FixupRefList &fixups() const { return Buffer.fixups(); }
307 
createFixup(FixupKind Kind,const Constant * Value)308   AssemblerFixup *createFixup(FixupKind Kind, const Constant *Value) {
309     return Buffer.createFixup(Kind, Value);
310   }
311 
createTextFixup(const std::string & Text,size_t BytesUsed)312   AssemblerTextFixup *createTextFixup(const std::string &Text,
313                                       size_t BytesUsed) {
314     return Buffer.createTextFixup(Text, BytesUsed);
315   }
316 
317   void bindRelocOffset(RelocOffset *Offset);
318 
setNeedsTextFixup()319   void setNeedsTextFixup() { Buffer.setNeedsTextFixup(); }
resetNeedsTextFixup()320   void resetNeedsTextFixup() { Buffer.resetNeedsTextFixup(); }
321 
needsTextFixup()322   bool needsTextFixup() const { return Buffer.needsTextFixup(); }
323 
324   void emitIASBytes(GlobalContext *Ctx) const;
getInternal()325   bool getInternal() const { return IsInternal; }
setInternal(bool Internal)326   void setInternal(bool Internal) { IsInternal = Internal; }
getFunctionName()327   GlobalString getFunctionName() const { return FunctionName; }
setFunctionName(GlobalString NewName)328   void setFunctionName(GlobalString NewName) { FunctionName = NewName; }
getBufferSize()329   intptr_t getBufferSize() const { return Buffer.size(); }
330   /// Roll back to a (smaller) size.
setBufferSize(intptr_t NewSize)331   void setBufferSize(intptr_t NewSize) { Buffer.setSize(NewSize); }
setPreliminary(bool Value)332   void setPreliminary(bool Value) { Preliminary = Value; }
getPreliminary()333   bool getPreliminary() const { return Preliminary; }
334 
getKind()335   AssemblerKind getKind() const { return Kind; }
336 
337 protected:
Assembler(AssemblerKind Kind)338   explicit Assembler(AssemblerKind Kind)
339       : Kind(Kind), Allocator(), Buffer(*this) {}
340 
341 private:
342   const AssemblerKind Kind;
343 
344   using AssemblerAllocator =
345       llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator, /*SlabSize=*/32 * 1024>;
346   AssemblerAllocator Allocator;
347 
348   /// FunctionName and IsInternal are transferred from the original Cfg object,
349   /// since the Cfg object may be deleted by the time the assembler buffer is
350   /// emitted.
351   GlobalString FunctionName;
352   bool IsInternal = false;
353   /// Preliminary indicates whether a preliminary pass is being made for
354   /// calculating bundle padding (Preliminary=true), versus the final pass where
355   /// all changes to label bindings, label links, and relocation fixups are
356   /// fully committed (Preliminary=false).
357   bool Preliminary = false;
358 
359   /// Installs a created fixup, after it has been allocated.
installFixup(AssemblerFixup * F)360   void installFixup(AssemblerFixup *F) { Buffer.installFixup(F); }
361 
362 protected:
363   // Buffer's constructor uses the Allocator, so it needs to appear after it.
364   // TODO(jpp): dependencies on construction order are a nice way of shooting
365   // yourself in the foot. Fix this.
366   AssemblerBuffer Buffer;
367 };
368 
369 } // end of namespace Ice
370 
371 #endif // SUBZERO_SRC_ICEASSEMBLER_H_
372