Joos1W Compiler Framework
All Classes Functions Typedefs Pages
Instructions.h
1 #pragma once
2 
3 #include <variant>
4 
5 #include "tir/BasicBlock.h"
6 #include "tir/Context.h"
7 #include "tir/Type.h"
8 #include "tir/Value.h"
9 #include "utils/EnumMacros.h"
10 #include "utils/Utils.h"
11 
12 namespace tir {
13 
14 class Function;
15 
16 /* ===--------------------------------------------------------------------=== */
17 // Instruction base class
18 /* ===--------------------------------------------------------------------=== */
19 
20 /**
21  * @brief Base class for all instructions in the TIR. Instructions are
22  * also Values, but they are not constants. Instructions are stored as a linked
23  * list, possibly belonging to a BasicBlock, or not. Instructions are also
24  * Users, meaning they can have other Values as operands (children).
25  */
26 class Instruction : public User {
27  friend class BasicBlock;
28  // Public enum definitions //////////////////////////////////////////////////
29 public:
30 #define BINOP_KINDS(F)
31  F(None)
32  F(Add)
33  F(Sub)
34  F(Mul)
35  F(Div)
36  F(Rem)
37  F(And)
38  F(Or)
39  F(Xor)
40  DECLARE_ENUM(BinOp, BINOP_KINDS)
41 protected:
42  DECLARE_STRING_TABLE(BinOp, binop_strtab_, BINOP_KINDS)
43 #undef BINOP_KINDS
44 
45 public:
46 #define PREDICATE_KINDS(F)
47  F(EQ)
48  F(NE)
49  F(LT)
50  F(GT)
51  F(LE)
52  F(GE)
53  DECLARE_ENUM(Predicate, PREDICATE_KINDS)
54 protected:
55  DECLARE_STRING_TABLE(Predicate, predicate_strtab_, PREDICATE_KINDS)
56 #undef PREDICATE_KINDS
57 
58 public:
59 #define CAST_KINDS(F)
60  F(Trunc)
61  F(ZExt)
62  F(SExt)
63  DECLARE_ENUM(CastOp, CAST_KINDS)
64 protected:
65  DECLARE_STRING_TABLE(CastOp, castop_strtab_, CAST_KINDS)
66 #undef CAST_KINDS
67 
68  // Protected data type and getter ///////////////////////////////////////////
69 protected:
70  using DataType = std::variant<BinOp, Predicate, CastOp, Type*, StructType*>;
71  template <typename T>
72  constexpr T const& get() const {
73  return std::get<T>(data_);
74  }
75 
76  // Constructors and public member functions /////////////////////////////////
77 public:
78  Instruction(Context& ctx, tir::Type* resultTy)
79  : Instruction{ctx, resultTy, BinOp::None} {}
80  Instruction(Context& ctx, tir::Type* resultTy, DataType data)
81  : User{ctx, resultTy},
82  next_{nullptr},
83  prev_{nullptr},
84  data_{data},
85  parent_{nullptr} {}
86  Instruction(const Instruction&) = delete;
87  Instruction(Instruction&&) = delete;
88  Instruction& operator=(const Instruction&) = delete;
89  Instruction& operator=(Instruction&&) = delete;
90  // Returns true if this instruction is a terminator instruction
91  virtual bool isTerminator() const { return false; }
92  // Inserts this before the given instruction, setting the parent of this
93  void insertBefore(Instruction* inst) {
94  assert(!isDestroyed() && "Instruction is already destroyed");
95  prev_ = inst->prev_;
96  next_ = inst;
97  if(prev_) {
98  prev_->next_ = this;
99  }
100  inst->prev_ = this;
101  parent_ = inst->parent_;
102  // If this is the first instruction in the BB, update the BB's first pointer
103  if(!prev_ && parent_) {
104  parent_->first_ = this;
105  }
106  }
107  // Inserts this after the given instruction, setting the parent of this
108  void insertAfter(Instruction* inst) {
109  assert(!isDestroyed() && "Instruction is already destroyed");
110  next_ = inst->next_;
111  prev_ = inst;
112  if(next_) {
113  next_->prev_ = this;
114  }
115  inst->next_ = this;
116  parent_ = inst->parent_;
117  // If this is the last instruction in the BB, update the BB's last pointer
118  if(!next_ && parent_) {
119  parent_->last_ = this;
120  }
121  }
122  // Gets the parent BB of this instruction, or nullptr if it has no parent
123  auto* parent() const { return parent_; }
124  // Gets the next instruction in the BB, or nullptr if this is the last
125  Instruction* next() const { return next_; }
126  // Gets the previous instruction in the BB, or nullptr if this is the first
127  Instruction* prev() const { return prev_; }
128  // Gets the iterator to the next instruction in the BB
129  auto nextIter() {
130  return BasicBlock::iterator{
131  next_ ? next_ : this, this->parent_, !next_, false};
132  }
133  // Gets the iterator to the previous instruction in the BB
134  auto prevIter() {
135  return BasicBlock::iterator{
136  prev_ ? prev_ : this, this->parent_, false, !prev_};
137  }
138  // Gets the current iterator to this instruction
139  auto iter() { return BasicBlock::iterator{this, this->parent_, false, false}; }
140  /**
141  * @brief Removes this instruction from its parent BB if it exists. Also
142  * will unlink this instruction from the list, re-linking the previous and
143  * next instructions.
144  */
145  void eraseFromParent(bool keep = false) {
146  assert(!isDestroyed() && "Instruction is already destroyed");
147  if(prev_) {
148  prev_->next_ = next_;
149  }
150  if(next_) {
151  next_->prev_ = prev_;
152  }
153  if(parent_) {
154  if(parent_->first_ == this) {
155  parent_->first_ = next_;
156  }
157  if(parent_->last_ == this) {
158  parent_->last_ = prev_;
159  }
160  }
161  // Destroy all references to the parent BB
162  next_ = prev_ = nullptr;
163  if(!keep) destroy();
164  }
165  // Sets the parent BB of this instruction
166  void setParent(BasicBlock* parent) { parent_ = parent; }
167 
168  // Private data members /////////////////////////////////////////////////////
169 private:
170  Instruction* next_;
171  Instruction* prev_;
172  const DataType data_;
173  BasicBlock* parent_;
174 };
175 
176 /* ===--------------------------------------------------------------------=== */
177 // Terminal instructions
178 /* ===--------------------------------------------------------------------=== */
179 
180 /**
181  * @brief Conditional branch instruction. This instruction is a terminator
182  * and branches to one of two basic blocks based on the condition. The
183  * condition value must be an i1 type.
184  */
185 class BranchInst final : public Instruction {
186 private:
187  BranchInst(Context& ctx, Value* cond, BasicBlock* trueBB, BasicBlock* falseBB);
188 
189 public:
190  static BranchInst* Create(Context& ctx, Value* cond, BasicBlock* trueBB,
191  BasicBlock* falseBB) {
192  auto buf =
193  ctx.alloc().allocate_bytes(sizeof(BranchInst), alignof(BranchInst));
194  return new(buf) BranchInst{ctx, cond, trueBB, falseBB};
195  }
196 
197 public:
198  bool isTerminator() const override { return true; }
199  BasicBlock* getSuccessor(unsigned idx) const;
200  std::ostream& print(std::ostream& os) const override;
201  void replaceSuccessor(unsigned idx, BasicBlock* newBB) {
202  assert(idx < 2 && "Index out of bounds");
203  replaceChild(idx+1, newBB);
204  }
205 };
206 
207 /**
208  * @brief Return instruction. This instruction is a terminator and returns
209  * either a value or nothing (void).
210  */
211 class ReturnInst final : public Instruction {
212 private:
213  ReturnInst(Context& ctx, Value* ret);
214 
215 public:
216  static ReturnInst* Create(Context& ctx, Value* ret) {
217  auto buf =
218  ctx.alloc().allocate_bytes(sizeof(ReturnInst), alignof(ReturnInst));
219  return new(buf) ReturnInst{ctx, ret};
220  }
221 
222 public:
223  bool isReturnVoid() const { return numChildren() == 0; }
224  bool isTerminator() const override { return true; }
225  std::ostream& print(std::ostream& os) const override;
226 };
227 
228 /* ===--------------------------------------------------------------------=== */
229 // Memory instructions
230 /* ===--------------------------------------------------------------------=== */
231 
232 /**
233  * @brief Store instruction. This instruction stores a value to a pointer.
234  */
235 class StoreInst final : public Instruction {
236 private:
237  StoreInst(Context& ctx, Value* val, Value* ptr);
238 
239 public:
240  static StoreInst* Create(Context& ctx, Value* val, Value* ptr) {
241  auto buf = ctx.alloc().allocate_bytes(sizeof(StoreInst), alignof(StoreInst));
242  return new(buf) StoreInst{ctx, val, ptr};
243  }
244 
245 public:
246  std::ostream& print(std::ostream& os) const override;
247 };
248 
249 /**
250  * @brief Load instruction. This instruction loads a value from a pointer.
251  * The size of the value loaded is determined by the type of the load instr,
252  * not by the type of the pointer. Pointer types are opaque.
253  */
254 class LoadInst final : public Instruction {
255 private:
256  LoadInst(Context& ctx, Type* type, Value* ptr);
257 
258 public:
259  static LoadInst* Create(Context& ctx, Type* type, Value* ptr) {
260  auto buf = ctx.alloc().allocate_bytes(sizeof(LoadInst), alignof(LoadInst));
261  return new(buf) LoadInst{ctx, type, ptr};
262  }
263 
264 public:
265  std::ostream& print(std::ostream& os) const override;
266 };
267 
268 /* ===--------------------------------------------------------------------=== */
269 // Function call instruction
270 /* ===--------------------------------------------------------------------=== */
271 
272 /**
273  * @brief Call instruction. This instruction calls a function with the given
274  * arguments. It returns the result of the function call.
275  */
276 class CallInst final : public Instruction {
277 private:
278  CallInst(Context& ctx, Value* callee, utils::range_ref<Value*> args);
279 
280 public:
281  static CallInst* Create(Context& ctx, Value* callee,
282  utils::range_ref<Value*> args) {
283  auto buf = ctx.alloc().allocate_bytes(sizeof(CallInst), alignof(CallInst));
284  return new(buf) CallInst{ctx, callee, args};
285  }
286 
287 public:
288  std::ostream& print(std::ostream& os) const override;
289  bool isTerminator() const override;
290  Function* getCallee() const;
291 };
292 
293 /* ===--------------------------------------------------------------------=== */
294 // Arithmetic and logic operators
295 /* ===--------------------------------------------------------------------=== */
296 
297 /**
298  * @brief Binary instruction. This instruction performs a binary operation
299  * on two values. The type of the result is the same as the type of the
300  * two operands (which must be the same type also).
301  */
302 class BinaryInst final : public Instruction {
303 private:
304  BinaryInst(Context& ctx, BinOp binop, Value* lhs, Value* rhs);
305 
306 public:
307  static BinaryInst* Create(Context& ctx, BinOp binop, Value* lhs, Value* rhs) {
308  auto buf =
309  ctx.alloc().allocate_bytes(sizeof(BinaryInst), alignof(BinaryInst));
310  return new(buf) BinaryInst{ctx, binop, lhs, rhs};
311  }
312 
313 public:
314  std::ostream& print(std::ostream& os) const override;
315  BinOp binop() const { return get<BinOp>(); }
316 };
317 
318 /**
319  * @brief Compare instruction. This instruction compares two values (of the same
320  * type) and returns a boolean value (i1) based on the comparison.
321  */
322 class CmpInst final : public Instruction {
323 private:
324  CmpInst(Context& ctx, Predicate pred, Value* lhs, Value* rhs);
325 
326 public:
327  static CmpInst* Create(Context& ctx, Predicate pred, Value* lhs, Value* rhs) {
328  auto buf = ctx.alloc().allocate_bytes(sizeof(CmpInst), alignof(CmpInst));
329  return new(buf) CmpInst{ctx, pred, lhs, rhs};
330  }
331 
332 public:
333  std::ostream& print(std::ostream& os) const override;
334  Predicate predicate() const { return get<Predicate>(); }
335 };
336 
337 /**
338  * @brief Integer cast instruction. This instruction can either truncate,
339  * zero-extend, or sign-extend an integer value to a different integer type.
340  */
341 class ICastInst final : public Instruction {
342 private:
343  ICastInst(Context& ctx, CastOp op, Value* val, Type* destTy);
344 
345 public:
346  static ICastInst* Create(Context& ctx, CastOp op, Value* val, Type* destTy) {
347  auto buf = ctx.alloc().allocate_bytes(sizeof(ICastInst), alignof(ICastInst));
348  return new(buf) ICastInst{ctx, op, val, destTy};
349  }
350 
351 public:
352  std::ostream& print(std::ostream& os) const override;
353  CastOp castop() const { return get<CastOp>(); }
354 };
355 
356 /* ===--------------------------------------------------------------------=== */
357 // Alloca instruction
358 /* ===--------------------------------------------------------------------=== */
359 
360 /**
361  * @brief Alloca instruction. This instruction allocates memory on the stack.
362  * This is equivalent to the TIR TEMP() node.
363  */
364 class AllocaInst final : public Instruction {
365 private:
366  AllocaInst(Context& ctx, Type* type);
367 
368 public:
369  static AllocaInst* Create(Context& ctx, Type* type) {
370  auto buf =
371  ctx.alloc().allocate_bytes(sizeof(AllocaInst), alignof(AllocaInst));
372  return new(buf) AllocaInst{ctx, type};
373  }
374 
375 public:
376  std::ostream& print(std::ostream& os) const override;
377 };
378 
379 /* ===--------------------------------------------------------------------=== */
380 // GetElementPtr instruction
381 /* ===--------------------------------------------------------------------=== */
382 
383 class GetElementPtrInst final : public Instruction {
384 private:
385  GetElementPtrInst(Context& ctx, Value* ptr, StructType* structTy,
386  utils::range_ref<Value*> indices);
387 
388 public:
389  static GetElementPtrInst* Create(Context& ctx, Value* ptr, StructType* structTy,
390  utils::range_ref<Value*> indices) {
391  auto buf = ctx.alloc().allocate_bytes(sizeof(GetElementPtrInst),
392  alignof(GetElementPtrInst));
393  return new(buf) GetElementPtrInst{ctx, ptr, structTy, indices};
394  }
395 
396 public:
397  std::ostream& print(std::ostream& os) const override;
398  Type* getIndexedType(utils::range_ref<Value*> indices) const {
399  auto structTy = get<StructType*>();
400  return structTy->getIndexedType(indices);
401  }
402  auto getStructType() const { return get<StructType*>(); }
403 };
404 
405 } // namespace tir