Home | History | Annotate | Download | only in src
      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 
     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.
     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.
     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.
     72   intptr_t getLinkPosition() const {
     73     assert(isLinked());
     74     return Position - kWordSize;
     75   }
     76 
     77   void setPosition(intptr_t NewValue) { Position = NewValue; }
     78 
     79   bool isBound() const { return Position < 0; }
     80   bool isLinked() const { return Position > 0; }
     81 
     82   virtual bool isUnused() const { return Position == 0; }
     83 
     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.
    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 
    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 
    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.
    135   void emitFixup(AssemblerFixup *Fixup) { Fixup->set_position(size()); }
    136 
    137   /// Get the size of the emitted code.
    138   intptr_t size() const { return Cursor - 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:
    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);
    166     intptr_t computeGap() { return Buffer->capacity() - Buffer->size(); }
    167   };
    168 
    169   bool 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.
    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.
    189   void setNeedsTextFixup() { TextFixupNeeded = true; }
    190   void resetNeedsTextFixup() { TextFixupNeeded = false; }
    191 
    192   /// Returns true if last emit failed and needs a text fixup.
    193   bool needsTextFixup() const { return TextFixupNeeded; }
    194 
    195   /// Installs a created fixup, after it has been allocated.
    196   void installFixup(AssemblerFixup *F);
    197 
    198   const FixupRefList &fixups() const { return Fixups; }
    199 
    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 
    222   uintptr_t cursor() const { return Cursor; }
    223   uintptr_t limit() const { return Limit; }
    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.
    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.
    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.
    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.
    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.
    295   template <typename T> T load(intptr_t Position) const {
    296     return Buffer.load<T>(Position);
    297   }
    298 
    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.
    304   void emitFixup(AssemblerFixup *Fixup) { Buffer.emitFixup(Fixup); }
    305 
    306   const FixupRefList &fixups() const { return Buffer.fixups(); }
    307 
    308   AssemblerFixup *createFixup(FixupKind Kind, const Constant *Value) {
    309     return Buffer.createFixup(Kind, Value);
    310   }
    311 
    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 
    319   void setNeedsTextFixup() { Buffer.setNeedsTextFixup(); }
    320   void resetNeedsTextFixup() { Buffer.resetNeedsTextFixup(); }
    321 
    322   bool needsTextFixup() const { return Buffer.needsTextFixup(); }
    323 
    324   void emitIASBytes(GlobalContext *Ctx) const;
    325   bool getInternal() const { return IsInternal; }
    326   void setInternal(bool Internal) { IsInternal = Internal; }
    327   GlobalString getFunctionName() const { return FunctionName; }
    328   void setFunctionName(GlobalString NewName) { FunctionName = NewName; }
    329   intptr_t getBufferSize() const { return Buffer.size(); }
    330   /// Roll back to a (smaller) size.
    331   void setBufferSize(intptr_t NewSize) { Buffer.setSize(NewSize); }
    332   void setPreliminary(bool Value) { Preliminary = Value; }
    333   bool getPreliminary() const { return Preliminary; }
    334 
    335   AssemblerKind getKind() const { return Kind; }
    336 
    337 protected:
    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.
    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