| //===- subzero/src/IceDefs.h - Common Subzero declarations ------*- C++ -*-===// | 
 | // | 
 | //                        The Subzero Code Generator | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | /// | 
 | /// \file | 
 | /// \brief Declares various useful types and classes that have widespread use | 
 | /// across Subzero. | 
 | /// | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #ifndef SUBZERO_SRC_ICEDEFS_H | 
 | #define SUBZERO_SRC_ICEDEFS_H | 
 |  | 
 | #include "IceBuildDefs.h" // TODO(stichnot): move into individual files | 
 | #include "IceMemory.h" | 
 | #include "IceTLS.h" | 
 |  | 
 | #include "llvm/ADT/ArrayRef.h" | 
 | #include "llvm/ADT/STLExtras.h" | 
 | #include "llvm/ADT/SmallVector.h" | 
 | #include "llvm/ADT/ilist.h" | 
 | #include "llvm/ADT/ilist_node.h" | 
 | #include "llvm/ADT/iterator_range.h" | 
 | #include "llvm/Support/Casting.h" | 
 | #include "llvm/Support/ELF.h" | 
 | #include "llvm/Support/raw_ostream.h" | 
 |  | 
 | #include <cassert> | 
 | #include <cstdint> | 
 | #include <cstdio>     // snprintf | 
 | #include <functional> // std::less | 
 | #include <limits> | 
 | #include <list> | 
 | #include <map> | 
 | #include <memory> | 
 | #include <mutex> | 
 | #include <set> | 
 | #include <string> | 
 | #include <system_error> | 
 | #include <unordered_map> | 
 | #include <unordered_set> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #define XSTRINGIFY(x) STRINGIFY(x) | 
 | #define STRINGIFY(x) #x | 
 |  | 
 | namespace Ice { | 
 |  | 
 | class Assembler; | 
 | template <template <typename> class> class BitVectorTmpl; | 
 | class Cfg; | 
 | class CfgNode; | 
 | class Constant; | 
 | class ELFFileStreamer; | 
 | class ELFObjectWriter; | 
 | class ELFStreamer; | 
 | class FunctionDeclaration; | 
 | class GlobalContext; | 
 | class GlobalDeclaration; | 
 | class Inst; | 
 | class InstAssign; | 
 | class InstJumpTable; | 
 | class InstPhi; | 
 | class InstSwitch; | 
 | class InstTarget; | 
 | class LiveRange; | 
 | class Liveness; | 
 | class Operand; | 
 | class TargetDataLowering; | 
 | class TargetLowering; | 
 | class Variable; | 
 | class VariableDeclaration; | 
 | class VariablesMetadata; | 
 |  | 
 | /// SizeT is for holding small-ish limits like number of source operands in an | 
 | /// instruction. It is used instead of size_t (which may be 64-bits wide) when | 
 | /// we want to save space. | 
 | using SizeT = uint32_t; | 
 |  | 
 | constexpr char GlobalOffsetTable[] = "_GLOBAL_OFFSET_TABLE_"; | 
 | // makeUnique should be used when memory is expected to be allocated from the | 
 | // heap (as opposed to allocated from some Allocator.) It is intended to be | 
 | // used instead of new. | 
 | // | 
 | // The expected usage is as follows | 
 | // | 
 | // class MyClass { | 
 | // public: | 
 | //   static std::unique_ptr<MyClass> create(<ctor_args>) { | 
 | //     return makeUnique<MyClass>(<ctor_args>); | 
 | //   } | 
 | // | 
 | // private: | 
 | //   ENABLE_MAKE_UNIQUE; | 
 | // | 
 | //   MyClass(<ctor_args>) ... | 
 | // } | 
 | // | 
 | // ENABLE_MAKE_UNIQUE is a trick that is necessary if MyClass' ctor is private. | 
 | // Private ctors are highly encouraged when you're writing a class that you'd | 
 | // like to have allocated with makeUnique as it would prevent users from | 
 | // declaring stack allocated variables. | 
 | namespace Internal { | 
 | struct MakeUniqueEnabler { | 
 |   template <class T, class... Args> | 
 |   static std::unique_ptr<T> create(Args &&... TheArgs) { | 
 |     std::unique_ptr<T> Unique(new T(std::forward<Args>(TheArgs)...)); | 
 |     return Unique; | 
 |   } | 
 | }; | 
 | } // end of namespace Internal | 
 |  | 
 | template <class T, class... Args> | 
 | static std::unique_ptr<T> makeUnique(Args &&... TheArgs) { | 
 |   return ::Ice::Internal::MakeUniqueEnabler::create<T>( | 
 |       std::forward<Args>(TheArgs)...); | 
 | } | 
 |  | 
 | #define ENABLE_MAKE_UNIQUE friend struct ::Ice::Internal::MakeUniqueEnabler | 
 |  | 
 | using InstList = llvm::ilist<Inst>; | 
 | // Ideally PhiList would be llvm::ilist<InstPhi>, and similar for AssignList, | 
 | // but this runs into issues with SFINAE. | 
 | using PhiList = InstList; | 
 | using AssignList = InstList; | 
 |  | 
 | // Standard library containers with CfgLocalAllocator. | 
 | template <typename T> using CfgList = std::list<T, CfgLocalAllocator<T>>; | 
 | template <typename T, typename H = std::hash<T>, typename Eq = std::equal_to<T>> | 
 | using CfgUnorderedSet = std::unordered_set<T, H, Eq, CfgLocalAllocator<T>>; | 
 | template <typename T, typename Cmp = std::less<T>> | 
 | using CfgSet = std::set<T, Cmp, CfgLocalAllocator<T>>; | 
 | template <typename T, typename U, typename H = std::hash<T>, | 
 |           typename Eq = std::equal_to<T>> | 
 | using CfgUnorderedMap = | 
 |     std::unordered_map<T, U, H, Eq, CfgLocalAllocator<std::pair<const T, U>>>; | 
 | template <typename T> using CfgVector = std::vector<T, CfgLocalAllocator<T>>; | 
 |  | 
 | // Containers that are arena-allocated from the Cfg's allocator. | 
 | using OperandList = CfgVector<Operand *>; | 
 | using VarList = CfgVector<Variable *>; | 
 | using NodeList = CfgVector<CfgNode *>; | 
 |  | 
 | // Containers that use the default (global) allocator. | 
 | using ConstantList = std::vector<Constant *>; | 
 | using FunctionDeclarationList = std::vector<FunctionDeclaration *>; | 
 |  | 
 | /// VariableDeclarationList is a container for holding VariableDeclarations -- | 
 | /// i.e., Global Variables. It is also used to create said variables, and their | 
 | /// initializers in an arena. | 
 | class VariableDeclarationList { | 
 |   VariableDeclarationList(const VariableDeclarationList &) = delete; | 
 |   VariableDeclarationList &operator=(const VariableDeclarationList &) = delete; | 
 |   VariableDeclarationList(VariableDeclarationList &&) = delete; | 
 |   VariableDeclarationList &operator=(VariableDeclarationList &&) = delete; | 
 |  | 
 | public: | 
 |   using VariableDeclarationArray = std::vector<VariableDeclaration *>; | 
 |  | 
 |   VariableDeclarationList() : Arena(new ArenaAllocator()) {} | 
 |  | 
 |   ~VariableDeclarationList() { clearAndPurge(); } | 
 |  | 
 |   template <typename T> T *allocate_initializer(SizeT Count = 1) { | 
 |     static_assert( | 
 |         std::is_trivially_destructible<T>::value, | 
 |         "allocate_initializer can only allocate trivially destructible types."); | 
 |     return Arena->Allocate<T>(Count); | 
 |   } | 
 |  | 
 |   template <typename T> T *allocate_variable_declaration() { | 
 |     static_assert(!std::is_trivially_destructible<T>::value, | 
 |                   "allocate_variable_declaration expects non-trivially " | 
 |                   "destructible types."); | 
 |     T *Ret = Arena->Allocate<T>(); | 
 |     Dtors.emplace_back([Ret]() { Ret->~T(); }); | 
 |     return Ret; | 
 |   } | 
 |  | 
 |   // This do nothing method is invoked when a global variable is created, but it | 
 |   // will not be emitted. If we ever need to track the created variable, having | 
 |   // this hook is handy. | 
 |   void willNotBeEmitted(VariableDeclaration *) {} | 
 |  | 
 |   /// Merges Other with this, effectively resetting Other to an empty state. | 
 |   void merge(VariableDeclarationList *Other) { | 
 |     assert(Other != nullptr); | 
 |     addArena(std::move(Other->Arena)); | 
 |     for (std::size_t i = 0; i < Other->MergedArenas.size(); ++i) { | 
 |       addArena(std::move(Other->MergedArenas[i])); | 
 |     } | 
 |     Other->MergedArenas.clear(); | 
 |  | 
 |     Dtors.insert(Dtors.end(), Other->Dtors.begin(), Other->Dtors.end()); | 
 |     Other->Dtors.clear(); | 
 |  | 
 |     Globals.insert(Globals.end(), Other->Globals.begin(), Other->Globals.end()); | 
 |     Other->Globals.clear(); | 
 |   } | 
 |  | 
 |   /// Destroys all GlobalVariables and initializers that this knows about | 
 |   /// (including those merged with it), and releases memory. | 
 |   void clearAndPurge() { | 
 |     if (Arena == nullptr) { | 
 |       // Arena is only null if this was merged, so we ensure there's no state | 
 |       // being held by this. | 
 |       assert(Dtors.empty()); | 
 |       assert(Globals.empty()); | 
 |       assert(MergedArenas.empty()); | 
 |       return; | 
 |     } | 
 |     // Invokes destructors in reverse creation order. | 
 |     for (auto Dtor = Dtors.rbegin(); Dtor != Dtors.rend(); ++Dtor) { | 
 |       (*Dtor)(); | 
 |     } | 
 |     Dtors.clear(); | 
 |     Globals.clear(); | 
 |     MergedArenas.clear(); | 
 |     Arena->Reset(); | 
 |   } | 
 |  | 
 |   /// Adapt the relevant parts of the std::vector<VariableDeclaration *> | 
 |   /// interface. | 
 |   /// @{ | 
 |   VariableDeclarationArray::iterator begin() { return Globals.begin(); } | 
 |  | 
 |   VariableDeclarationArray::iterator end() { return Globals.end(); } | 
 |  | 
 |   VariableDeclarationArray::const_iterator begin() const { | 
 |     return Globals.begin(); | 
 |   } | 
 |  | 
 |   VariableDeclarationArray::const_iterator end() const { return Globals.end(); } | 
 |  | 
 |   bool empty() const { return Globals.empty(); } | 
 |  | 
 |   VariableDeclarationArray::size_type size() const { return Globals.size(); } | 
 |  | 
 |   VariableDeclarationArray::reference | 
 |   at(VariableDeclarationArray::size_type Pos) { | 
 |     return Globals.at(Pos); | 
 |   } | 
 |  | 
 |   void push_back(VariableDeclaration *Global) { Globals.push_back(Global); } | 
 |  | 
 |   void reserve(VariableDeclarationArray::size_type Capacity) { | 
 |     Globals.reserve(Capacity); | 
 |   } | 
 |  | 
 |   void clear() { Globals.clear(); } | 
 |  | 
 |   VariableDeclarationArray::reference back() { return Globals.back(); } | 
 |   /// @} | 
 |  | 
 | private: | 
 |   using ArenaPtr = std::unique_ptr<ArenaAllocator>; | 
 |   using DestructorsArray = std::vector<std::function<void()>>; | 
 |  | 
 |   void addArena(ArenaPtr NewArena) { | 
 |     MergedArenas.emplace_back(std::move(NewArena)); | 
 |   } | 
 |  | 
 |   ArenaPtr Arena; | 
 |   VariableDeclarationArray Globals; | 
 |   DestructorsArray Dtors; | 
 |   std::vector<ArenaPtr> MergedArenas; | 
 | }; | 
 |  | 
 | /// InstNumberT is for holding an instruction number. Instruction numbers are | 
 | /// used for representing Variable live ranges. | 
 | using InstNumberT = int32_t; | 
 |  | 
 | /// A LiveBeginEndMapEntry maps a Variable::Number value to an Inst::Number | 
 | /// value, giving the instruction number that begins or ends a variable's live | 
 | /// range. | 
 | template <typename T> | 
 | using LivenessVector = std::vector<T, LivenessAllocator<T>>; | 
 | using LiveBeginEndMapEntry = std::pair<SizeT, InstNumberT>; | 
 | using LiveBeginEndMap = LivenessVector<LiveBeginEndMapEntry>; | 
 | using LivenessBV = BitVectorTmpl<LivenessAllocator>; | 
 |  | 
 | using TimerStackIdT = uint32_t; | 
 | using TimerIdT = uint32_t; | 
 |  | 
 | /// Use alignas(MaxCacheLineSize) to isolate variables/fields that might be | 
 | /// contended while multithreading. Assumes the maximum cache line size is 64. | 
 | enum { MaxCacheLineSize = 64 }; | 
 | // Use ICE_CACHELINE_BOUNDARY to force the next field in a declaration | 
 | // list to be aligned to the next cache line. | 
 | #if defined(_MSC_VER) | 
 | #define ICE_CACHELINE_BOUNDARY __declspec(align(MaxCacheLineSize)) int : 0; | 
 | #else // !defined(_MSC_VER) | 
 | // Note: zero is added to work around the following GCC 4.8 bug (fixed in 4.9): | 
 | //       https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55382 | 
 | #define ICE_CACHELINE_BOUNDARY                                                 \ | 
 |   __attribute__((aligned(MaxCacheLineSize + 0))) int : 0 | 
 | #endif // !defined(_MSC_VER) | 
 |  | 
 | using RelocOffsetT = int32_t; | 
 | enum { RelocAddrSize = 4 }; | 
 |  | 
 | enum LivenessMode { | 
 |   /// Basic version of live-range-end calculation. Marks the last uses of | 
 |   /// variables based on dataflow analysis. Records the set of live-in and | 
 |   /// live-out variables for each block. Identifies and deletes dead | 
 |   /// instructions (primarily stores). | 
 |   Liveness_Basic, | 
 |  | 
 |   /// In addition to Liveness_Basic, also calculate the complete live range for | 
 |   /// each variable in a form suitable for interference calculation and register | 
 |   /// allocation. | 
 |   Liveness_Intervals | 
 | }; | 
 |  | 
 | enum LCSEOptions { | 
 |   LCSE_Disabled, | 
 |   LCSE_EnabledSSA,  // Default Mode, assumes SSA. | 
 |   LCSE_EnabledNoSSA // Does not assume SSA, to be enabled if CSE is done later. | 
 | }; | 
 |  | 
 | enum RegAllocKind { | 
 |   RAK_Unknown, | 
 |   RAK_Global,       /// full, global register allocation | 
 |   RAK_SecondChance, /// second-chance bin-packing after full regalloc attempt | 
 |   RAK_Phi,          /// infinite-weight Variables with active spilling/filling | 
 |   RAK_InfOnly       /// allocation only for infinite-weight Variables | 
 | }; | 
 |  | 
 | enum VerboseItem { | 
 |   IceV_None = 0, | 
 |   IceV_Instructions = 1 << 0, | 
 |   IceV_Deleted = 1 << 1, | 
 |   IceV_InstNumbers = 1 << 2, | 
 |   IceV_Preds = 1 << 3, | 
 |   IceV_Succs = 1 << 4, | 
 |   IceV_Liveness = 1 << 5, | 
 |   IceV_RegOrigins = 1 << 6, | 
 |   IceV_LinearScan = 1 << 7, | 
 |   IceV_Frame = 1 << 8, | 
 |   IceV_AddrOpt = 1 << 9, | 
 |   IceV_Folding = 1 << 10, | 
 |   IceV_RMW = 1 << 11, | 
 |   IceV_Loop = 1 << 12, | 
 |   IceV_Mem = 1 << 13, | 
 |   // Leave some extra space to make it easier to add new per-pass items. | 
 |   IceV_NO_PER_PASS_DUMP_BEYOND = 1 << 19, | 
 |   // Items greater than IceV_NO_PER_PASS_DUMP_BEYOND don't by themselves trigger | 
 |   // per-pass Cfg dump output. | 
 |   IceV_Status = 1 << 20, | 
 |   IceV_AvailableRegs = 1 << 21, | 
 |   IceV_GlobalInit = 1 << 22, | 
 |   IceV_ConstPoolStats = 1 << 23, | 
 |   IceV_Wasm = 1 << 24, | 
 |   IceV_ShufMat = 1 << 25, | 
 |   IceV_All = ~IceV_None, | 
 |   IceV_Most = | 
 |       IceV_All & ~IceV_LinearScan & ~IceV_GlobalInit & ~IceV_ConstPoolStats | 
 | }; | 
 | using VerboseMask = uint32_t; | 
 |  | 
 | enum FileType { | 
 |   FT_Elf, /// ELF .o file | 
 |   FT_Asm, /// Assembly .s file | 
 |   FT_Iasm /// "Integrated assembler" .byte-style .s file | 
 | }; | 
 |  | 
 | using Ostream = llvm::raw_ostream; | 
 | using Fdstream = llvm::raw_fd_ostream; | 
 |  | 
 | using GlobalLockType = std::mutex; | 
 |  | 
 | /// LockedPtr is an RAII wrapper that allows automatically locked access to a | 
 | /// given pointer, automatically unlocking it when when the LockedPtr goes out | 
 | /// of scope. | 
 | template <typename T> class LockedPtr { | 
 |   LockedPtr() = delete; | 
 |   LockedPtr(const LockedPtr &) = delete; | 
 |   LockedPtr &operator=(const LockedPtr &) = delete; | 
 |  | 
 | public: | 
 |   LockedPtr(T *Value, GlobalLockType *Lock) : Value(Value), Lock(Lock) { | 
 |     Lock->lock(); | 
 |   } | 
 |   LockedPtr(LockedPtr &&Other) : Value(Other.Value), Lock(Other.Lock) { | 
 |     Other.Value = nullptr; | 
 |     Other.Lock = nullptr; | 
 |   } | 
 |   ~LockedPtr() { | 
 |     if (Lock != nullptr) | 
 |       Lock->unlock(); | 
 |   } | 
 |   T *operator->() const { return Value; } | 
 |   T &operator*() const { return *Value; } | 
 |   T *get() { return Value; } | 
 |  | 
 | private: | 
 |   T *Value; | 
 |   GlobalLockType *Lock; | 
 | }; | 
 |  | 
 | enum ErrorCodes { EC_None = 0, EC_Args, EC_Bitcode, EC_Translation }; | 
 |  | 
 | /// Wrapper around std::error_code for allowing multiple errors to be folded | 
 | /// into one. The current implementation keeps track of the first error, which | 
 | /// is likely to be the most useful one, and this could be extended to e.g. | 
 | /// collect a vector of errors. | 
 | class ErrorCode : public std::error_code { | 
 |   ErrorCode(const ErrorCode &) = delete; | 
 |   ErrorCode &operator=(const ErrorCode &) = delete; | 
 |  | 
 | public: | 
 |   ErrorCode() = default; | 
 |   void assign(ErrorCodes Code) { | 
 |     if (!HasError) { | 
 |       HasError = true; | 
 |       std::error_code::assign(Code, std::generic_category()); | 
 |     } | 
 |   } | 
 |   void assign(int Code) { assign(static_cast<ErrorCodes>(Code)); } | 
 |  | 
 | private: | 
 |   bool HasError = false; | 
 | }; | 
 |  | 
 | /// Reverse range adaptors written in terms of llvm::make_range(). | 
 | template <typename T> | 
 | llvm::iterator_range<typename T::const_reverse_iterator> | 
 | reverse_range(const T &Container) { | 
 |   return llvm::make_range(Container.rbegin(), Container.rend()); | 
 | } | 
 | template <typename T> | 
 | llvm::iterator_range<typename T::reverse_iterator> reverse_range(T &Container) { | 
 |   return llvm::make_range(Container.rbegin(), Container.rend()); | 
 | } | 
 |  | 
 | using RelocOffsetArray = llvm::SmallVector<class RelocOffset *, 4>; | 
 |  | 
 | } // end of namespace Ice | 
 |  | 
 | #endif // SUBZERO_SRC_ICEDEFS_H |