Joos1W Compiler Framework
All Classes Functions Typedefs Pages
Constant.h
1 #pragma once
2 
3 #include <forward_list>
4 #include <string_view>
5 
6 #include "tir/Instructions.h"
7 #include "tir/Type.h"
8 #include "tir/Value.h"
9 #include "utils/Generator.h"
10 #include "utils/Utils.h"
11 
12 namespace tir {
13 
14 class BasicBlock;
15 class CompilationUnit;
16 class Function;
17 class ConstantInt;
18 
19 /**
20  * @brief
21  */
22 class Constant : public User {
23 protected:
24  Constant(Context& ctx, Type* type) : User{ctx, type} {}
25  virtual bool isNullPointer() const { return false; }
26  virtual bool isNumeric() const { return false; }
27  virtual bool isBoolean() const { return false; }
28 
29 public:
30  static ConstantInt* CreateInt(Context& ctx, uint8_t bits, uint32_t value);
31  static ConstantInt* CreateBool(Context& ctx, bool value) {
32  return CreateInt(ctx, 1, value ? 1 : 0);
33  }
34  static ConstantInt* CreateInt32(Context& ctx, uint32_t value) {
35  return CreateInt(ctx, 32, value);
36  }
37  static ConstantNullPointer* CreateNullPointer(Context& ctx);
38 };
39 
40 /**
41  * @brief
42  */
43 class ConstantInt final : public Constant {
44 private:
45  ConstantInt(Context& ctx, Type* type, uint64_t value)
46  : Constant{ctx, type}, value_{value} {}
47 
48 public:
49  static ConstantInt* Create(Context& ctx, Type* type, uint64_t value) {
50  assert(type->isIntegerType() && "Type must be an integer type");
51  auto* buf =
52  ctx.alloc().allocate_bytes(sizeof(ConstantInt), alignof(ConstantInt));
53  return new(buf) ConstantInt{ctx, type, value};
54  }
55  static ConstantInt* AllOnes(Context& ctx, Type* type) {
56  return Create(ctx, type, ~0ULL);
57  }
58  static ConstantInt* Zero(Context& ctx, Type* type) {
59  return Create(ctx, type, 0);
60  }
61 
62 public:
63  std::ostream& print(std::ostream& os) const override;
64  uint64_t zextValue() const {
65  return value_ & cast<IntegerType>(type())->getMask();
66  }
67  uint64_t sextValue() const {
68  auto mask = cast<IntegerType>(type())->getMask();
69  return (value_ & mask) |
70  ((value_ & (1ULL << (type()->getSizeInBits() - 1))) ? ~mask : 0);
71  }
72  bool isNumeric() const override { return true; }
73  bool isBoolean() const override { return type()->isBooleanType(); }
74 
75 private:
76  uint64_t value_;
77 };
78 
79 /**
80  * @brief
81  */
82 class ConstantNullPointer final : public Constant {
83 private:
84  friend class Context;
85  ConstantNullPointer(Context& ctx, Type* ty) : Constant{ctx, ty} {}
86 
87 public:
88  static ConstantNullPointer* Create(Context& ctx) {
89  return ctx.pimpl().nullPointer;
90  }
91 
92 public:
93  std::ostream& print(std::ostream& os) const override;
94  bool isNullPointer() const override { return true; }
95 };
96 
97 /**
98  * @brief
99  */
100 class GlobalObject : public Constant {
101 protected:
102  GlobalObject(Context& ctx, Type* type) : Constant{ctx, type} {}
103 
104 public:
105  virtual bool isExternalLinkage() const = 0;
106 };
107 
108 /**
109  * @brief
110  */
111 class GlobalVariable final : public GlobalObject {
112  friend class CompilationUnit;
113 
114 private:
115  GlobalVariable(Context& ctx, Type* type) : GlobalObject{ctx, type} {}
116 
117 public:
118  std::ostream& print(std::ostream& os) const override;
119  bool isExternalLinkage() const override { return false; }
120 };
121 
122 /**
123  * @brief
124  */
125 class Argument final : public Value {
126  friend class Function;
127 
128 private:
129  Argument(Function* parent, Type* type, unsigned index);
130 
131 public:
132  std::ostream& print(std::ostream& os) const override;
133  auto* parent() const { return parent_; }
134  auto index() const { return index_; }
135 
136 private:
137  Function* parent_;
138  unsigned index_;
139 };
140 
141 /**
142  * @brief
143  */
144 class Function final : public GlobalObject {
145  friend class CompilationUnit;
146  friend class BasicBlock;
147 
148 private:
149  Function(Context& ctx, CompilationUnit* parent, FunctionType* type,
150  const std::string_view name)
151  : GlobalObject{ctx, type}, body_{ctx.alloc()}, parent_{parent} {
152  // Set name of the function
153  setName(name);
154  // Build the argument list
155  for(unsigned i = 0; i < type->numParams(); ++i) {
156  auto* buf =
157  ctx.alloc().allocate_bytes(sizeof(Argument), alignof(Argument));
158  auto* arg = new(buf) Argument{this, type->getParamType(i), i};
159  addChild(arg);
160  }
161  }
162 
163 public:
164  std::ostream& print(std::ostream& os) const override;
165  auto* parent() const { return parent_; }
166  auto args() const {
167  return std::views::transform(
168  children(), [](Value* val) { return static_cast<Argument*>(val); });
169  }
170  auto numParams() const { return cast<FunctionType>(type())->numParams(); }
171  auto getParamType(int index) const {
172  return cast<FunctionType>(type())->getParamType(index);
173  }
174  auto getReturnType() const {
175  return cast<FunctionType>(type())->getReturnType();
176  }
177  bool hasBody() const { return !body_.empty(); }
178  /// @brief Gets the entry BB of the function or nullptr if it doesn't exist.
179  BasicBlock* getEntryBlock() const { return entryBB_; }
180  /// @brief Create a new basic block and add it to the function.
181  auto body() const { return std::views::all(body_); }
182  void removeBlock(BasicBlock* block) {
183  assert(block->parent() == this && "Block does not belong to this function");
184  assert(block != entryBB_ && "Cannot remove the entry block");
185  body_.remove(block);
186  }
187 
188  /**
189  * @brief Create an alloca instruction for the given type. This will be
190  * at the start of the entry BB of the function. If no entry BB exists,
191  * this will fail.
192  *
193  * @param type The type of the alloca instruction.
194  * @return AllocaInst* The alloca instruction created.
195  */
196  AllocaInst* createAlloca(Type* type) {
197  assert(getEntryBlock());
198  auto* inst = AllocaInst::Create(ctx(), type);
199  getEntryBlock()->insertBeforeBegin(inst);
200  return inst;
201  }
202  // Get the function attribute "noreturn"
203  auto isNoReturn() const { return noReturn_; }
204  // Set the function attribute "noreturn"
205  void setNoReturn() { noReturn_ = true; }
206  // Set the function attribute "external"
207  void setExternalLinkage() { external_ = true; }
208  // Get the function attribute "external"
209  bool isExternalLinkage() const override {
210  if(!external_) return !entryBB_;
211  return true;
212  }
213  // Print the function in DOT format
214  void printDot(std::ostream& os) const;
215  // Get the reverse post order of the basic blocks
216  utils::Generator<BasicBlock*> reversePostOrder() const;
217  // Iterates through the set of allocas
218  utils::Generator<AllocaInst*> allocas() const {
219  if(entryBB_) {
220  for(auto* inst : *entryBB_) {
221  if(auto* alloca = dyn_cast<AllocaInst>(inst)) {
222  co_yield alloca;
223  }
224  }
225  }
226  }
227 
228 private:
229  void addBlock(BasicBlock* block) {
230  if(!entryBB_) entryBB_ = block;
231  body_.push_front(block);
232  }
233 
234 private:
235  std::pmr::forward_list<BasicBlock*> body_;
236  BasicBlock* entryBB_ = nullptr;
237  tir::CompilationUnit* parent_;
238  bool noReturn_ = false;
239  bool external_ = false;
240 };
241 
242 } // namespace tir