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 using RelocOffsetT = int32_t;
306 enum { RelocAddrSize = 4 };
307
308 enum LivenessMode {
309 /// Basic version of live-range-end calculation. Marks the last uses of
310 /// variables based on dataflow analysis. Records the set of live-in and
311 /// live-out variables for each block. Identifies and deletes dead
312 /// instructions (primarily stores).
313 Liveness_Basic,
314
315 /// In addition to Liveness_Basic, also calculate the complete live range for
316 /// each variable in a form suitable for interference calculation and register
317 /// allocation.
318 Liveness_Intervals
319 };
320
321 enum LCSEOptions {
322 LCSE_Disabled,
323 LCSE_EnabledSSA, // Default Mode, assumes SSA.
324 LCSE_EnabledNoSSA // Does not assume SSA, to be enabled if CSE is done later.
325 };
326
327 enum RegAllocKind {
328 RAK_Unknown,
329 RAK_Global, /// full, global register allocation
330 RAK_SecondChance, /// second-chance bin-packing after full regalloc attempt
331 RAK_Phi, /// infinite-weight Variables with active spilling/filling
332 RAK_InfOnly /// allocation only for infinite-weight Variables
333 };
334
335 enum VerboseItem {
336 IceV_None = 0,
337 IceV_Instructions = 1 << 0,
338 IceV_Deleted = 1 << 1,
339 IceV_InstNumbers = 1 << 2,
340 IceV_Preds = 1 << 3,
341 IceV_Succs = 1 << 4,
342 IceV_Liveness = 1 << 5,
343 IceV_RegOrigins = 1 << 6,
344 IceV_LinearScan = 1 << 7,
345 IceV_Frame = 1 << 8,
346 IceV_AddrOpt = 1 << 9,
347 IceV_Folding = 1 << 10,
348 IceV_RMW = 1 << 11,
349 IceV_Loop = 1 << 12,
350 IceV_Mem = 1 << 13,
351 // Leave some extra space to make it easier to add new per-pass items.
352 IceV_NO_PER_PASS_DUMP_BEYOND = 1 << 19,
353 // Items greater than IceV_NO_PER_PASS_DUMP_BEYOND don't by themselves trigger
354 // per-pass Cfg dump output.
355 IceV_Status = 1 << 20,
356 IceV_AvailableRegs = 1 << 21,
357 IceV_GlobalInit = 1 << 22,
358 IceV_ConstPoolStats = 1 << 23,
359 IceV_Wasm = 1 << 24,
360 IceV_ShufMat = 1 << 25,
361 IceV_All = ~IceV_None,
362 IceV_Most =
363 IceV_All & ~IceV_LinearScan & ~IceV_GlobalInit & ~IceV_ConstPoolStats
364 };
365 using VerboseMask = uint32_t;
366
367 enum FileType {
368 FT_Elf, /// ELF .o file
369 FT_Asm, /// Assembly .s file
370 FT_Iasm /// "Integrated assembler" .byte-style .s file
371 };
372
373 using Ostream = llvm::raw_ostream;
374 using Fdstream = llvm::raw_fd_ostream;
375
376 using GlobalLockType = std::mutex;
377
378 /// LockedPtr is an RAII wrapper that allows automatically locked access to a
379 /// given pointer, automatically unlocking it when when the LockedPtr goes out
380 /// of scope.
381 template <typename T> class LockedPtr {
382 LockedPtr() = delete;
383 LockedPtr(const LockedPtr &) = delete;
384 LockedPtr &operator=(const LockedPtr &) = delete;
385
386 public:
LockedPtr(T * Value,GlobalLockType * Lock)387 LockedPtr(T *Value, GlobalLockType *Lock) : Value(Value), Lock(Lock) {
388 Lock->lock();
389 }
LockedPtr(LockedPtr && Other)390 LockedPtr(LockedPtr &&Other) : Value(Other.Value), Lock(Other.Lock) {
391 Other.Value = nullptr;
392 Other.Lock = nullptr;
393 }
~LockedPtr()394 ~LockedPtr() {
395 if (Lock != nullptr)
396 Lock->unlock();
397 }
398 T *operator->() const { return Value; }
399 T &operator*() const { return *Value; }
get()400 T *get() { return Value; }
401
402 private:
403 T *Value;
404 GlobalLockType *Lock;
405 };
406
407 enum ErrorCodes { EC_None = 0, EC_Args, EC_Bitcode, EC_Translation };
408
409 /// Wrapper around std::error_code for allowing multiple errors to be folded
410 /// into one. The current implementation keeps track of the first error, which
411 /// is likely to be the most useful one, and this could be extended to e.g.
412 /// collect a vector of errors.
413 class ErrorCode : public std::error_code {
414 ErrorCode(const ErrorCode &) = delete;
415 ErrorCode &operator=(const ErrorCode &) = delete;
416
417 public:
418 ErrorCode() = default;
assign(ErrorCodes Code)419 void assign(ErrorCodes Code) {
420 if (!HasError) {
421 HasError = true;
422 std::error_code::assign(Code, std::generic_category());
423 }
424 }
assign(int Code)425 void assign(int Code) { assign(static_cast<ErrorCodes>(Code)); }
426
427 private:
428 bool HasError = false;
429 };
430
431 /// Reverse range adaptors written in terms of llvm::make_range().
432 template <typename T>
433 llvm::iterator_range<typename T::const_reverse_iterator>
reverse_range(const T & Container)434 reverse_range(const T &Container) {
435 return llvm::make_range(Container.rbegin(), Container.rend());
436 }
437 template <typename T>
reverse_range(T & Container)438 llvm::iterator_range<typename T::reverse_iterator> reverse_range(T &Container) {
439 return llvm::make_range(Container.rbegin(), Container.rend());
440 }
441
442 using RelocOffsetArray = llvm::SmallVector<class RelocOffset *, 4>;
443
444 } // end of namespace Ice
445
446 #endif // SUBZERO_SRC_ICEDEFS_H
447