1 //===- subzero/src/IceDefs.h - Common Subzero declarations ------*- C++ -*-===//
2 //
3 //                        The Subzero Code Generator
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief Declares various useful types and classes that have widespread use
12 /// across Subzero.
13 ///
14 //===----------------------------------------------------------------------===//
15 
16 #ifndef SUBZERO_SRC_ICEDEFS_H
17 #define SUBZERO_SRC_ICEDEFS_H
18 
19 #include "IceBuildDefs.h" // TODO(stichnot): move into individual files
20 #include "IceMemory.h"
21 #include "IceTLS.h"
22 
23 #include "llvm/ADT/ArrayRef.h"
24 #include "llvm/ADT/STLExtras.h"
25 #include "llvm/ADT/SmallVector.h"
26 #include "llvm/ADT/ilist.h"
27 #include "llvm/ADT/ilist_node.h"
28 #include "llvm/ADT/iterator_range.h"
29 #include "llvm/Support/Casting.h"
30 #include "llvm/Support/ELF.h"
31 #include "llvm/Support/raw_ostream.h"
32 
33 #include <cassert>
34 #include <cstdint>
35 #include <cstdio>     // snprintf
36 #include <functional> // std::less
37 #include <limits>
38 #include <list>
39 #include <map>
40 #include <memory>
41 #include <mutex>
42 #include <set>
43 #include <string>
44 #include <system_error>
45 #include <unordered_map>
46 #include <unordered_set>
47 #include <utility>
48 #include <vector>
49 
50 #define XSTRINGIFY(x) STRINGIFY(x)
51 #define STRINGIFY(x) #x
52 
53 namespace Ice {
54 
55 class Assembler;
56 template <template <typename> class> class BitVectorTmpl;
57 class Cfg;
58 class CfgNode;
59 class Constant;
60 class ELFFileStreamer;
61 class ELFObjectWriter;
62 class ELFStreamer;
63 class FunctionDeclaration;
64 class GlobalContext;
65 class GlobalDeclaration;
66 class Inst;
67 class InstAssign;
68 class InstJumpTable;
69 class InstPhi;
70 class InstSwitch;
71 class InstTarget;
72 class LiveRange;
73 class Liveness;
74 class Operand;
75 class TargetDataLowering;
76 class TargetLowering;
77 class Variable;
78 class VariableDeclaration;
79 class VariablesMetadata;
80 
81 /// SizeT is for holding small-ish limits like number of source operands in an
82 /// instruction. It is used instead of size_t (which may be 64-bits wide) when
83 /// we want to save space.
84 using SizeT = uint32_t;
85 
86 constexpr char GlobalOffsetTable[] = "_GLOBAL_OFFSET_TABLE_";
87 // makeUnique should be used when memory is expected to be allocated from the
88 // heap (as opposed to allocated from some Allocator.) It is intended to be
89 // used instead of new.
90 //
91 // The expected usage is as follows
92 //
93 // class MyClass {
94 // public:
95 //   static std::unique_ptr<MyClass> create(<ctor_args>) {
96 //     return makeUnique<MyClass>(<ctor_args>);
97 //   }
98 //
99 // private:
100 //   ENABLE_MAKE_UNIQUE;
101 //
102 //   MyClass(<ctor_args>) ...
103 // }
104 //
105 // ENABLE_MAKE_UNIQUE is a trick that is necessary if MyClass' ctor is private.
106 // Private ctors are highly encouraged when you're writing a class that you'd
107 // like to have allocated with makeUnique as it would prevent users from
108 // declaring stack allocated variables.
109 namespace Internal {
110 struct MakeUniqueEnabler {
111   template <class T, class... Args>
createMakeUniqueEnabler112   static std::unique_ptr<T> create(Args &&... TheArgs) {
113     std::unique_ptr<T> Unique(new T(std::forward<Args>(TheArgs)...));
114     return Unique;
115   }
116 };
117 } // end of namespace Internal
118 
119 template <class T, class... Args>
makeUnique(Args &&...TheArgs)120 static std::unique_ptr<T> makeUnique(Args &&... TheArgs) {
121   return ::Ice::Internal::MakeUniqueEnabler::create<T>(
122       std::forward<Args>(TheArgs)...);
123 }
124 
125 #define ENABLE_MAKE_UNIQUE friend struct ::Ice::Internal::MakeUniqueEnabler
126 
127 using InstList = llvm::ilist<Inst>;
128 // Ideally PhiList would be llvm::ilist<InstPhi>, and similar for AssignList,
129 // but this runs into issues with SFINAE.
130 using PhiList = InstList;
131 using AssignList = InstList;
132 
133 // Standard library containers with CfgLocalAllocator.
134 template <typename T> using CfgList = std::list<T, CfgLocalAllocator<T>>;
135 template <typename T, typename H = std::hash<T>, typename Eq = std::equal_to<T>>
136 using CfgUnorderedSet = std::unordered_set<T, H, Eq, CfgLocalAllocator<T>>;
137 template <typename T, typename Cmp = std::less<T>>
138 using CfgSet = std::set<T, Cmp, CfgLocalAllocator<T>>;
139 template <typename T, typename U, typename H = std::hash<T>,
140           typename Eq = std::equal_to<T>>
141 using CfgUnorderedMap =
142     std::unordered_map<T, U, H, Eq, CfgLocalAllocator<std::pair<const T, U>>>;
143 template <typename T> using CfgVector = std::vector<T, CfgLocalAllocator<T>>;
144 
145 // Containers that are arena-allocated from the Cfg's allocator.
146 using OperandList = CfgVector<Operand *>;
147 using VarList = CfgVector<Variable *>;
148 using NodeList = CfgVector<CfgNode *>;
149 
150 // Containers that use the default (global) allocator.
151 using ConstantList = std::vector<Constant *>;
152 using FunctionDeclarationList = std::vector<FunctionDeclaration *>;
153 
154 /// VariableDeclarationList is a container for holding VariableDeclarations --
155 /// i.e., Global Variables. It is also used to create said variables, and their
156 /// initializers in an arena.
157 class VariableDeclarationList {
158   VariableDeclarationList(const VariableDeclarationList &) = delete;
159   VariableDeclarationList &operator=(const VariableDeclarationList &) = delete;
160   VariableDeclarationList(VariableDeclarationList &&) = delete;
161   VariableDeclarationList &operator=(VariableDeclarationList &&) = delete;
162 
163 public:
164   using VariableDeclarationArray = std::vector<VariableDeclaration *>;
165 
VariableDeclarationList()166   VariableDeclarationList() : Arena(new ArenaAllocator()) {}
167 
~VariableDeclarationList()168   ~VariableDeclarationList() { clearAndPurge(); }
169 
170   template <typename T> T *allocate_initializer(SizeT Count = 1) {
171     static_assert(
172         std::is_trivially_destructible<T>::value,
173         "allocate_initializer can only allocate trivially destructible types.");
174     return Arena->Allocate<T>(Count);
175   }
176 
allocate_variable_declaration()177   template <typename T> T *allocate_variable_declaration() {
178     static_assert(!std::is_trivially_destructible<T>::value,
179                   "allocate_variable_declaration expects non-trivially "
180                   "destructible types.");
181     T *Ret = Arena->Allocate<T>();
182     Dtors.emplace_back([Ret]() { Ret->~T(); });
183     return Ret;
184   }
185 
186   // This do nothing method is invoked when a global variable is created, but it
187   // will not be emitted. If we ever need to track the created variable, having
188   // this hook is handy.
willNotBeEmitted(VariableDeclaration *)189   void willNotBeEmitted(VariableDeclaration *) {}
190 
191   /// Merges Other with this, effectively resetting Other to an empty state.
merge(VariableDeclarationList * Other)192   void merge(VariableDeclarationList *Other) {
193     assert(Other != nullptr);
194     addArena(std::move(Other->Arena));
195     for (std::size_t i = 0; i < Other->MergedArenas.size(); ++i) {
196       addArena(std::move(Other->MergedArenas[i]));
197     }
198     Other->MergedArenas.clear();
199 
200     Dtors.insert(Dtors.end(), Other->Dtors.begin(), Other->Dtors.end());
201     Other->Dtors.clear();
202 
203     Globals.insert(Globals.end(), Other->Globals.begin(), Other->Globals.end());
204     Other->Globals.clear();
205   }
206 
207   /// Destroys all GlobalVariables and initializers that this knows about
208   /// (including those merged with it), and releases memory.
clearAndPurge()209   void clearAndPurge() {
210     if (Arena == nullptr) {
211       // Arena is only null if this was merged, so we ensure there's no state
212       // being held by this.
213       assert(Dtors.empty());
214       assert(Globals.empty());
215       assert(MergedArenas.empty());
216       return;
217     }
218     // Invokes destructors in reverse creation order.
219     for (auto Dtor = Dtors.rbegin(); Dtor != Dtors.rend(); ++Dtor) {
220       (*Dtor)();
221     }
222     Dtors.clear();
223     Globals.clear();
224     MergedArenas.clear();
225     Arena->Reset();
226   }
227 
228   /// Adapt the relevant parts of the std::vector<VariableDeclaration *>
229   /// interface.
230   /// @{
begin()231   VariableDeclarationArray::iterator begin() { return Globals.begin(); }
232 
end()233   VariableDeclarationArray::iterator end() { return Globals.end(); }
234 
begin()235   VariableDeclarationArray::const_iterator begin() const {
236     return Globals.begin();
237   }
238 
end()239   VariableDeclarationArray::const_iterator end() const { return Globals.end(); }
240 
empty()241   bool empty() const { return Globals.empty(); }
242 
size()243   VariableDeclarationArray::size_type size() const { return Globals.size(); }
244 
245   VariableDeclarationArray::reference
at(VariableDeclarationArray::size_type Pos)246   at(VariableDeclarationArray::size_type Pos) {
247     return Globals.at(Pos);
248   }
249 
push_back(VariableDeclaration * Global)250   void push_back(VariableDeclaration *Global) { Globals.push_back(Global); }
251 
reserve(VariableDeclarationArray::size_type Capacity)252   void reserve(VariableDeclarationArray::size_type Capacity) {
253     Globals.reserve(Capacity);
254   }
255 
clear()256   void clear() { Globals.clear(); }
257 
back()258   VariableDeclarationArray::reference back() { return Globals.back(); }
259   /// @}
260 
261 private:
262   using ArenaPtr = std::unique_ptr<ArenaAllocator>;
263   using DestructorsArray = std::vector<std::function<void()>>;
264 
addArena(ArenaPtr NewArena)265   void addArena(ArenaPtr NewArena) {
266     MergedArenas.emplace_back(std::move(NewArena));
267   }
268 
269   ArenaPtr Arena;
270   VariableDeclarationArray Globals;
271   DestructorsArray Dtors;
272   std::vector<ArenaPtr> MergedArenas;
273 };
274 
275 /// InstNumberT is for holding an instruction number. Instruction numbers are
276 /// used for representing Variable live ranges.
277 using InstNumberT = int32_t;
278 
279 /// A LiveBeginEndMapEntry maps a Variable::Number value to an Inst::Number
280 /// value, giving the instruction number that begins or ends a variable's live
281 /// range.
282 template <typename T>
283 using LivenessVector = std::vector<T, LivenessAllocator<T>>;
284 using LiveBeginEndMapEntry = std::pair<SizeT, InstNumberT>;
285 using LiveBeginEndMap = LivenessVector<LiveBeginEndMapEntry>;
286 using LivenessBV = BitVectorTmpl<LivenessAllocator>;
287 
288 using TimerStackIdT = uint32_t;
289 using TimerIdT = uint32_t;
290 
291 /// Use alignas(MaxCacheLineSize) to isolate variables/fields that might be
292 /// contended while multithreading. Assumes the maximum cache line size is 64.
293 enum { MaxCacheLineSize = 64 };
294 // Use ICE_CACHELINE_BOUNDARY to force the next field in a declaration
295 // list to be aligned to the next cache line.
296 #if defined(_MSC_VER)
297 #define ICE_CACHELINE_BOUNDARY __declspec(align(MaxCacheLineSize)) int : 0;
298 #else // !defined(_MSC_VER)
299 // Note: zero is added to work around the following GCC 4.8 bug (fixed in 4.9):
300 //       https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55382
301 #define ICE_CACHELINE_BOUNDARY                                                 \
302   __attribute__((aligned(MaxCacheLineSize + 0))) int : 0
303 #endif // !defined(_MSC_VER)
304 
305 /// PNaCl is ILP32, so theoretically we should only need 32-bit offsets.
306 using RelocOffsetT = int32_t;
307 enum { RelocAddrSize = 4 };
308 
309 enum LivenessMode {
310   /// Basic version of live-range-end calculation. Marks the last uses of
311   /// variables based on dataflow analysis. Records the set of live-in and
312   /// live-out variables for each block. Identifies and deletes dead
313   /// instructions (primarily stores).
314   Liveness_Basic,
315 
316   /// In addition to Liveness_Basic, also calculate the complete live range for
317   /// each variable in a form suitable for interference calculation and register
318   /// allocation.
319   Liveness_Intervals
320 };
321 
322 enum LCSEOptions {
323   LCSE_Disabled,
324   LCSE_EnabledSSA,  // Default Mode, assumes SSA.
325   LCSE_EnabledNoSSA // Does not assume SSA, to be enabled if CSE is done later.
326 };
327 
328 enum RegAllocKind {
329   RAK_Unknown,
330   RAK_Global,       /// full, global register allocation
331   RAK_SecondChance, /// second-chance bin-packing after full regalloc attempt
332   RAK_Phi,          /// infinite-weight Variables with active spilling/filling
333   RAK_InfOnly       /// allocation only for infinite-weight Variables
334 };
335 
336 enum VerboseItem {
337   IceV_None = 0,
338   IceV_Instructions = 1 << 0,
339   IceV_Deleted = 1 << 1,
340   IceV_InstNumbers = 1 << 2,
341   IceV_Preds = 1 << 3,
342   IceV_Succs = 1 << 4,
343   IceV_Liveness = 1 << 5,
344   IceV_RegOrigins = 1 << 6,
345   IceV_LinearScan = 1 << 7,
346   IceV_Frame = 1 << 8,
347   IceV_AddrOpt = 1 << 9,
348   IceV_Folding = 1 << 10,
349   IceV_RMW = 1 << 11,
350   IceV_Loop = 1 << 12,
351   IceV_Mem = 1 << 13,
352   // Leave some extra space to make it easier to add new per-pass items.
353   IceV_NO_PER_PASS_DUMP_BEYOND = 1 << 19,
354   // Items greater than IceV_NO_PER_PASS_DUMP_BEYOND don't by themselves trigger
355   // per-pass Cfg dump output.
356   IceV_Status = 1 << 20,
357   IceV_AvailableRegs = 1 << 21,
358   IceV_GlobalInit = 1 << 22,
359   IceV_ConstPoolStats = 1 << 23,
360   IceV_Wasm = 1 << 24,
361   IceV_ShufMat = 1 << 25,
362   IceV_All = ~IceV_None,
363   IceV_Most =
364       IceV_All & ~IceV_LinearScan & ~IceV_GlobalInit & ~IceV_ConstPoolStats
365 };
366 using VerboseMask = uint32_t;
367 
368 enum FileType {
369   FT_Elf, /// ELF .o file
370   FT_Asm, /// Assembly .s file
371   FT_Iasm /// "Integrated assembler" .byte-style .s file
372 };
373 
374 enum ABI {
375   ABI_PNaCl,   /// x32 for unsandboxed 64-bit x86
376   ABI_Platform /// Native executable ABI
377 };
378 
379 using Ostream = llvm::raw_ostream;
380 using Fdstream = llvm::raw_fd_ostream;
381 
382 using GlobalLockType = std::mutex;
383 
384 /// LockedPtr is an RAII wrapper that allows automatically locked access to a
385 /// given pointer, automatically unlocking it when when the LockedPtr goes out
386 /// of scope.
387 template <typename T> class LockedPtr {
388   LockedPtr() = delete;
389   LockedPtr(const LockedPtr &) = delete;
390   LockedPtr &operator=(const LockedPtr &) = delete;
391 
392 public:
LockedPtr(T * Value,GlobalLockType * Lock)393   LockedPtr(T *Value, GlobalLockType *Lock) : Value(Value), Lock(Lock) {
394     Lock->lock();
395   }
LockedPtr(LockedPtr && Other)396   LockedPtr(LockedPtr &&Other) : Value(Other.Value), Lock(Other.Lock) {
397     Other.Value = nullptr;
398     Other.Lock = nullptr;
399   }
~LockedPtr()400   ~LockedPtr() {
401     if (Lock != nullptr)
402       Lock->unlock();
403   }
404   T *operator->() const { return Value; }
405   T &operator*() const { return *Value; }
get()406   T *get() { return Value; }
407 
408 private:
409   T *Value;
410   GlobalLockType *Lock;
411 };
412 
413 enum ErrorCodes { EC_None = 0, EC_Args, EC_Bitcode, EC_Translation };
414 
415 /// Wrapper around std::error_code for allowing multiple errors to be folded
416 /// into one. The current implementation keeps track of the first error, which
417 /// is likely to be the most useful one, and this could be extended to e.g.
418 /// collect a vector of errors.
419 class ErrorCode : public std::error_code {
420   ErrorCode(const ErrorCode &) = delete;
421   ErrorCode &operator=(const ErrorCode &) = delete;
422 
423 public:
424   ErrorCode() = default;
assign(ErrorCodes Code)425   void assign(ErrorCodes Code) {
426     if (!HasError) {
427       HasError = true;
428       std::error_code::assign(Code, std::generic_category());
429     }
430   }
assign(int Code)431   void assign(int Code) { assign(static_cast<ErrorCodes>(Code)); }
432 
433 private:
434   bool HasError = false;
435 };
436 
437 /// Reverse range adaptors written in terms of llvm::make_range().
438 template <typename T>
439 llvm::iterator_range<typename T::const_reverse_iterator>
reverse_range(const T & Container)440 reverse_range(const T &Container) {
441   return llvm::make_range(Container.rbegin(), Container.rend());
442 }
443 template <typename T>
reverse_range(T & Container)444 llvm::iterator_range<typename T::reverse_iterator> reverse_range(T &Container) {
445   return llvm::make_range(Container.rbegin(), Container.rend());
446 }
447 
448 using RelocOffsetArray = llvm::SmallVector<class RelocOffset *, 4>;
449 
450 } // end of namespace Ice
451 
452 #endif // SUBZERO_SRC_ICEDEFS_H
453