//===- 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