|  | //===- 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/ilist.h" | 
|  | #include "llvm/ADT/ilist_node.h" | 
|  | #include "llvm/ADT/iterator_range.h" | 
|  | #include "llvm/ADT/SmallVector.h" | 
|  | #include "llvm/ADT/STLExtras.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) | 
|  |  | 
|  | /// PNaCl is ILP32, so theoretically we should only need 32-bit offsets. | 
|  | 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_Random = 1 << 10, | 
|  | IceV_Folding = 1 << 11, | 
|  | IceV_RMW = 1 << 12, | 
|  | IceV_Loop = 1 << 13, | 
|  | IceV_Mem = 1 << 14, | 
|  | // 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 | 
|  | }; | 
|  |  | 
|  | enum ABI { | 
|  | ABI_PNaCl,   /// x32 for unsandboxed 64-bit x86 | 
|  | ABI_Platform /// Native executable ABI | 
|  | }; | 
|  |  | 
|  | 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()); | 
|  | } | 
|  |  | 
|  | /// Options for pooling and randomization of immediates. | 
|  | enum RandomizeAndPoolImmediatesEnum { RPI_None, RPI_Randomize, RPI_Pool }; | 
|  |  | 
|  | /// Salts for Random number generator for different randomization passes. | 
|  | enum RandomizationPassesEnum { | 
|  | RPE_BasicBlockReordering, | 
|  | RPE_ConstantBlinding, | 
|  | RPE_FunctionReordering, | 
|  | RPE_GlobalVariableReordering, | 
|  | RPE_NopInsertion, | 
|  | RPE_PooledConstantReordering, | 
|  | RPE_RegAllocRandomization, | 
|  | RPE_num | 
|  | }; | 
|  |  | 
|  | using RelocOffsetArray = llvm::SmallVector<class RelocOffset *, 4>; | 
|  |  | 
|  | } // end of namespace Ice | 
|  |  | 
|  | #endif // SUBZERO_SRC_ICEDEFS_H |