llir-opt  0.0.1
Low-Level Post-Link Optimiser for OCaml and C
kildall.h
1 // This file if part of the llir-opt project.
2 // Licensing information can be found in the LICENSE file.
3 // (C) 2018 Nandor Licker. All rights reserved.
4 
5 #pragma once
6 
7 #include <llvm/ADT/PostOrderIterator.h>
8 #include <queue>
9 #include "core/cfg.h"
10 
11 
12 
14 enum class Direction {
15  FORWARD,
16  BACKWARD,
17 };
18 
22 template <typename FlowSet, typename GenSet, typename KillSet, Direction Dir>
24 protected:
25  struct InstInfo {
27  Inst *I;
28 
30  GenSet Gen;
32  KillSet Kill;
33 
34  InstInfo(Inst *I) : I(I) {}
35  };
36 
37 public:
39  KillGenSolver(Func &func);
40 
42  void Solve();
43 
44 protected:
46  virtual void Build(Inst &inst) = 0;
47 
49  virtual void Traverse(Inst *inst, const FlowSet &set) = 0;
50 
53  {
54  return blocks_[blockToIndex_[I->getParent()]].Insts.emplace_back(I);
55  }
56 
57 private:
59  struct BlockInfo {
61  Block *B;
63  llvm::SmallVector<uint64_t, 5> Preds;
65  llvm::SmallVector<uint64_t, 5> Succs;
67  std::vector<InstInfo> Insts;
69  GenSet Gen;
71  KillSet Kill;
73  FlowSet Flow;
74 
75  BlockInfo(Block *B) : B(B) {}
76  };
77 
78 protected:
81 
82 private:
84  std::vector<BlockInfo> blocks_;
86  std::unordered_map<const Block *, uint64_t> blockToIndex_;
87 };
88 
89 template <typename FlowSet, typename GenSet, typename KillSet, Direction Dir>
91  : func_(func)
92 {
93  // Generate unique block IDs.
94  for (Block &block : func) {
95  blockToIndex_.insert({ &block, blocks_.size() });
96  blocks_.push_back(&block);
97  }
98 
99  // Build the graph.
100  for (Block &block : func) {
101  BlockInfo &blockInfo = blocks_[blockToIndex_[&block]];
102 
103  // Construct fast pred/succ information.
104  for (const Block *pred : block.predecessors()) {
105  blockInfo.Preds.push_back(blockToIndex_[pred]);
106  }
107  for (const Block *succ : block.successors()) {
108  blockInfo.Succs.push_back(blockToIndex_[succ]);
109  }
110  }
111 }
112 
113 template <typename FlowSet, typename GenSet, typename KillSet, Direction Dir>
115 {
116  // Build constraints.
117  for (Block &block : func_) {
118  for (Inst &inst : block) {
119  Build(inst);
120  }
121  }
122 
123  // Construct per-block kill/gen.
124  for (BlockInfo &block : blocks_) {
125  auto Step = [&block](InstInfo &inst) {
126  // kill' = kill U killNew
127  block.Kill.Union(inst.Kill);
128  // gen' = (gen - killNew) U genNew
129  block.Gen.Minus(inst.Kill);
130  block.Gen.Union(inst.Gen);
131  };
132 
133  switch (Dir) {
134  case Direction::FORWARD: {
135  for (auto it = block.Insts.begin(); it != block.Insts.end(); ++it) {
136  Step(*it);
137  }
138  break;
139  }
140  case Direction::BACKWARD: {
141  for (auto it = block.Insts.rbegin(); it != block.Insts.rend(); ++it) {
142  Step(*it);
143  }
144  break;
145  }
146  }
147  }
148 
149  // Populate the worklist.
150  std::queue<BlockInfo *> queue_;
151  std::set<BlockInfo *> inQueue_;
152  std::vector<llvm::GraphTraits<Func *>::NodeRef> blockOrder_;
153  {
154  auto Add = [&queue_, &inQueue_] (BlockInfo *info) {
155  inQueue_.insert(info);
156  queue_.push(info);
157  };
158 
159  // Find the order of the nodes.
160  auto Entry = llvm::GraphTraits<Func *>::getEntryNode(&func_);
161  std::copy(po_begin(Entry), po_end(Entry), std::back_inserter(blockOrder_));
162 
163  // Add nodes to queue.
164  switch (Dir) {
165  case Direction::FORWARD: {
166  for (auto it = blockOrder_.rbegin(); it != blockOrder_.rend(); ++it) {
167  auto *block = &blocks_[blockToIndex_[*it]];
168 
169  std::optional<FlowSet> init;
170  for (unsigned index : block->Preds) {
171  auto *pred = &blocks_[index];
172 
173  FlowSet out = pred->Flow;
174  out.Minus(pred->Kill);
175  out.Union(pred->Gen);
176  if (init) {
177  init->Union(out);
178  } else {
179  init = out;
180  }
181  }
182 
183  if (init) {
184  block->Flow = *init;
185  }
186 
187  Add(block);
188  }
189  break;
190  }
191  case Direction::BACKWARD: {
192  for (auto it = blockOrder_.begin(); it != blockOrder_.end(); ++it) {
193  auto *block = &blocks_[blockToIndex_[*it]];
194 
195  std::optional<FlowSet> init;
196  for (unsigned index : block->Preds) {
197  auto *succ = &blocks_[index];
198 
199  FlowSet out = succ->Flow;
200  out.Minus(succ->Kill);
201  out.Union(succ->Gen);
202  if (init) {
203  init->Union(out);
204  } else {
205  init = out;
206  }
207  }
208 
209  if (init) {
210  block->Flow = *init;
211  }
212 
213  Add(block);
214  }
215  break;
216  }
217  }
218  }
219 
220  // Iterate until worklist has elements.
221  while (!queue_.empty()) {
222  BlockInfo *block = queue_.front();
223  queue_.pop();
224  inQueue_.erase(block);
225 
227  FlowSet out = block->Flow;
228  out.Minus(block->Kill);
229  out.Union(block->Gen);
230 
231  // If inputs to a block change, continue.
232  auto Transfer = [&out, &inQueue_, &queue_] (BlockInfo *next) {
233  FlowSet in = next->Flow;
234  in.Union(out);
235  if (!(in == next->Flow)) {
236  next->Flow = in;
237  if (inQueue_.count(next) == 0) {
238  queue_.push(next);
239  }
240  }
241  };
242 
244  switch (Dir) {
245  case Direction::FORWARD: {
246  for (unsigned succ : block->Succs) {
247  Transfer(&blocks_[succ]);
248  }
249  break;
250  }
251  case Direction::BACKWARD: {
252  for (unsigned pred : block->Preds) {
253  Transfer(&blocks_[pred]);
254  }
255  break;
256  }
257  }
258  }
259 
260  // Traverse nodes, applying the transformation.
261  for (Block *block : blockOrder_) {
262  BlockInfo &info = blocks_[blockToIndex_[block]];
263 
264  FlowSet set = info.Flow;
265 
266  auto Step = [this, &set] (InstInfo &i) {
267  set.Minus(i.Kill);
268  set.Union(i.Gen);
269  Traverse(i.I, set);
270  };
271 
272  switch (Dir) {
273  case Direction::FORWARD: {
274  for (auto it = info.Insts.begin(); it != info.Insts.end(); ++it) {
275  Step(*it);
276  }
277  break;
278  }
279  case Direction::BACKWARD: {
280  for (auto it = info.Insts.rbegin(); it != info.Insts.rend(); ++it) {
281  Step(*it);
282  }
283  break;
284  }
285  }
286  }
287 }
KillGenSolver::func_
Func & func_
Reference to the function.
Definition: kildall.h:80
Inst
Definition: inst.h:53
Func
Definition: func.h:30
KillGenSolver::InstInfo::I
Inst * I
Instruction which generates or clobbers.
Definition: kildall.h:27
KillGenSolver::Info
InstInfo & Info(Inst *I)
Returns a kill-gen set for an instruction.
Definition: kildall.h:52
KillGenSolver::KillGenSolver
KillGenSolver(Func &func)
Initialises the solver.
Definition: kildall.h:90
KillGenSolver::InstInfo::Gen
GenSet Gen
Liveness gen.
Definition: kildall.h:30
KillGenSolver::Build
virtual void Build(Inst &inst)=0
Callback to generate constraints.
KillGenSolver::Traverse
virtual void Traverse(Inst *inst, const FlowSet &set)=0
Solve all the constraints.
Inst::getParent
Block * getParent() const
Returns the parent node.
Definition: inst.h:86
KillGenSolver::InstInfo
Definition: kildall.h:25
KillGenSolver
Definition: kildall.h:23
Block
Definition: block.h:29
KillGenSolver::Solve
void Solve()
Builds and solves constraints, invokes callback.
Definition: kildall.h:114
KillGenSolver::InstInfo::Kill
KillSet Kill
Liveness kill.
Definition: kildall.h:32