CGRA-ME
DFGGeneration.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * The software programs comprising "CGRA-ME" and the documentation provided
3  * with them are copyright by its authors and the University of Toronto. Only
4  * non-commercial, not-for-profit use of this software is permitted without ex-
5  * plicit permission. This software is provided "as is" with no warranties or
6  * guarantees of support. See the LICENCE for more details. You should have re-
7  * ceived a copy of the full licence along with this software. If not, see
8  * <http://cgra-me.ece.utoronto.ca/license/>.
9  ******************************************************************************/
10 
11 #include "llvm/IR/Function.h"
12 #include "llvm/Pass.h"
13 #include "llvm/Support/raw_ostream.h"
14 #include "llvm/Support/FileSystem.h"
15 #include "llvm/Support/CommandLine.h"
16 #include "llvm/Analysis/LoopPass.h"
17 #include "llvm/IR/Constants.h"
18 #include "llvm/IR/Instructions.h"
19 #include "llvm/IR/GetElementPtrTypeIterator.h"
20 #include "llvm/IR/GlobalVariable.h"
21 #include "llvm/IR/Module.h"
22 
23 #if LLVM_VERSION_MAJOR >= 14
24 #include "llvm/Passes/PassPlugin.h"
25 #include "llvm/Passes/PassBuilder.h"
26 #endif
27 
28 #include <string>
29 #include <sstream>
30 #include <list>
31 #include <map>
32 #include <algorithm>
33 #include <cctype>
34 #include <fstream>
35 
36 #include <CGRA/OpGraph.h>
37 #include <CGRA/OpGraphProcedures.h>
39 
40 using namespace llvm;
41 
42 namespace
43 {
44  // Used to represent the instructions that are commutative. Candidate for replacement is using llvm::Instruction::isCommutative
45  static const std::set<OpGraphOpCode> commutative_ops = { OpCode::ADD, OpCode::MUL, OpCode::AND, OpCode::OR, OpCode::XOR};
46 
47  cl::opt<std::string> inputTagPairs("in-tag-pairs", cl::Positional, cl::desc("<input file that contains tag number and string pairs>"));
48 
49  cl::opt<std::string> loopTags("loop-tags", cl::desc("Input a list of loop tag names to generate DFG for"));
50 
51  OpGraphOpCode LLVMtoOp(const Instruction& I)
52  {
53  switch(I.getOpcode())
54  {
55  case Instruction::PHI : return OpCode::PHI;
56  case Instruction::Select : return OpCode::PHI;
57  case Instruction::Trunc : return OpCode::TRUNC;
58  case Instruction::Add : return OpCode::ADD;
59  case Instruction::FAdd : errs() << "Note: converting FAdd to integer op\n"; return OpCode::ADD;
60  case Instruction::Sub : return OpCode::SUB;
61  case Instruction::FSub : errs() << "Note: converting FSub to integer op\n"; return OpCode::SUB;
62  case Instruction::Shl : return OpCode::SHL;
63  case Instruction::AShr : return OpCode::ASHR;
64  case Instruction::LShr : return OpCode::LSHR;
65  case Instruction::And : return OpCode::AND;
66  case Instruction::Or : return OpCode::OR;
67  case Instruction::Xor : return OpCode::XOR;
68  case Instruction::SRem : return OpCode::DIV;
69  case Instruction::URem : return OpCode::DIV;
70  case Instruction::FRem : errs() << "Note: converting FRem to integer op\n"; return OpCode::DIV;
71  case Instruction::SDiv : return OpCode::DIV;
72  case Instruction::UDiv : return OpCode::DIV;
73  case Instruction::FDiv : errs() << "Note: converting FDiv to integer op\n"; return OpCode::DIV;
74  case Instruction::Mul : return OpCode::MUL;
75  case Instruction::FMul : errs() << "Note: converting FMul to integer op\n"; return OpCode::MUL;
76  case Instruction::Load : return OpCode::LOAD;
77  case Instruction::Store : return OpCode::STORE;
78  case Instruction::GetElementPtr : return OpCode::GEP;
79  case Instruction::ICmp : return OpCode::ICMP;
80  case Instruction::Br : return OpCode::BR;
81 
82  // ignore these instructions (mostly casts)
83  case Instruction::SExt : return OpCode::NOP;
84  case Instruction::ZExt : return OpCode::NOP;
85  case Instruction::FPToUI : return OpCode::NOP;
86  case Instruction::FPToSI : return OpCode::NOP;
87  case Instruction::UIToFP : return OpCode::NOP;
88  case Instruction::SIToFP : return OpCode::NOP;
89  case Instruction::FPTrunc : return OpCode::NOP;
90  case Instruction::FPExt : return OpCode::NOP;
91  case Instruction::PtrToInt : return OpCode::NOP;
92  case Instruction::IntToPtr : return OpCode::NOP;
93  case Instruction::BitCast : return OpCode::NOP;
94 
95  default: errs() << "could not look up:" << I << "\n"; std::abort();
96  }
97  }
98 
111  struct OpNameMaker {
112  std::map<const Value*,std::string> instruction_unique_part = {};
113  std::map<std::string, int> other_category_counts = {};
114 
115  OpNameMaker(const Loop* L) {
116  for (const auto& bb : make_iterator_range(L->block_begin(), L->block_end())) {
117  for (const auto& inst : *bb) {
118  // get the name from the IR, or just use the number of instructions encountered so far
119  std::string name = static_cast<std::string>(inst.getName());
120  if (name == "") { name = 'i' + std::to_string(instruction_unique_part.size()) + '_'; }
121  instruction_unique_part.emplace(&inst,std::move(name));
122  }
123  }
124  }
125 
126  // base case
127  std::string operator()(const Value* val, std::string s_arg = "") {
128 
129  // is it one of the pre-generated names?
130  const auto lookup_result = instruction_unique_part.find(val);
131  if (lookup_result != instruction_unique_part.end()) {
132  return lookup_result->second + std::move(s_arg);
133  }
134 
135  // otherwise, try to get a name from the IR, or make a unique name
136  std::string result = static_cast<std::string>(val->getName());
137 
138  if (result.empty()) { // use string + number
139  std::string category_name = "input"; // default
140 
141  // some common cases
142  if (isa<GlobalValue>(val)) { category_name = "global"; }
143  else if (isa<Constant>(val)) { category_name = "const"; }
144  else if (isa<BasicBlock>(val)) { category_name = "bb"; }
145 
146  auto& category_count = other_category_counts[category_name];
147  result = category_name + std::to_string(category_count++);
148  }
149 
150  return result + s_arg;
151  }
152 
153  // special case
154  std::string operator()(const Value* inst, const char* s_arg) {
155  return (*this)(inst, std::string(s_arg));
156  }
157 
158  // special case
159  template<typename Arg1, typename... Args>
160  std::string operator()(const Value* inst, const std::string& s_arg, Arg1& arg1, Args&... args) {
161  return (*this)(inst, s_arg + std::to_string(arg1), args...);
162  }
163 
164  // special case
165  template<typename Arg1, typename... Args>
166  std::string operator()(const Value* inst, const char* s_arg, Arg1& arg1, Args&... args) {
167  return (*this)(inst, s_arg + std::to_string(arg1), args...);
168  }
169 
170  // general case, converts first arg to string
171  template<typename Arg1, typename... Args>
172  std::string operator()(const Value* inst, Arg1& arg1, Args&... args) {
173  return (*this)(inst, std::to_string(arg1), args...);
174  }
175  };
176 
177  std::string operandTypeFor(const OpGraphOp* op, int op_num) {
178  if (commutative_ops.find(op->opcode) != commutative_ops.end()) {
179  return Operands::BINARY_ANY;
180  }
181  else if (op->opcode == OpCode::LOAD) {
182  return Operands::MEM_ADDR;
183  }
184  else if (op->opcode == OpCode::STORE) {
185  if (op_num == 0) {
186  return Operands::MEM_DATA;
187  }
188  else if (op_num == 1) {
189  return Operands::MEM_ADDR;
190  }
191  }
192  else if (op->opcode == OpCode::GEP) {
193  return "gep_input" + std::to_string(op_num);
194  }
195  else if (op->opcode == OpCode::BR) {
196  switch (op_num) {
197  case 0: return "branch_cond";
198  case 1: return "branch_true";
199  case 2: return "branch_false";
200  }
201  }
202  else if (op_num == 0) {
203  return Operands::BINARY_LHS;
204  }
205  else if (op_num == 1) {
206  return Operands::BINARY_RHS;
207  }
208 
209  errs() << "Unhandled case for operand setting"; std::abort();
210  }
211 
216  std::pair<bool, uint64_t> try_extract_integral_constant(const Value& val) {
217  if(const auto& as_constant = dyn_cast<Constant>(&val)) {
218  const auto& stripped_const = as_constant->stripPointerCasts();
219 
220  if (const auto& as_const_int = dyn_cast<ConstantInt>(stripped_const)) {
221  return {true, as_const_int->getZExtValue()};
222 
223  } else if (const auto& as_global_var = dyn_cast<GlobalVariable>(stripped_const)) {
224  if (as_global_var->hasInitializer()) {
225  try_extract_integral_constant(*as_global_var->getInitializer());
226  }
227  } else if (const auto& as_constant_expr = dyn_cast<ConstantExpr>(stripped_const)) {
228  if (as_constant_expr->isCast()) {
229  return try_extract_integral_constant(*stripped_const->getOperand(0));
230  } else {
231  errs() << "Warning: unable to extract a value from a ConstantExpr: " << *stripped_const << "\n";
232  }
233  } else {
234  errs() << "Warning: unable to extract a value from a Constant: " << *stripped_const << "\n";
235  }
236  }
237  return {false, 0};
238  }
239 
240  struct DfgOutImpl {
241  std::vector<OpGraph*> graphs = {};
242  std::map<unsigned int, std::string> tag_pairs = {};
243  std::vector<std::string> loop_tags = {};
244  DfgOutImpl()
245  {
246  if(!inputTagPairs.empty())
247  {
248  std::ifstream in(inputTagPairs);
249  for(std::string line; std::getline(in, line); )
250  {
251  std::stringstream temp_sstream(line);
252  int temp_tag_num;
253  temp_sstream >> temp_tag_num;
254  std::string temp_tag_string;
255  temp_sstream >> temp_tag_string;
256  tag_pairs.emplace(temp_tag_num, std::move(temp_tag_string));
257  }
258  std::stringstream temp_sstream(loopTags);
259  std::string temp_tag_string;
260  while(temp_sstream >> temp_tag_string)
261  loop_tags.push_back(temp_tag_string);
262  }
263  else
264  errs() << "Warning: No tag pair is provided as input, no DFG will be generated." << "\n";
265  }
266 
267  bool runOnLoop(Loop* L)
268  {
269  // Loop tag is in ths first basic block of the loop
270  int found_tag_num = 0;
271  Instruction* the_loop_tag = nullptr;
272  for(auto it = L->getHeader()->begin(); it != L->getHeader()->end(); ++it)
273  {
274  if(isa<CallInst>(it))
275  {
276  Function * func = dyn_cast<CallInst>(it)->getCalledFunction();
277  if((func != nullptr) && (func->getName() == "DFGLOOP_TAG")) {
278  found_tag_num = cast<ConstantInt>(dyn_cast<CallInst>(it)->getArgOperand(0))->getValue().getZExtValue(); // found_tag_num has the the tag number
279  the_loop_tag = &*it;
280  }
281  }
282  }
283  if(!found_tag_num) // If there is no tag associated with this loop
284  return false;
285 
286  std::string tag_name;
287 
288  auto tag_pairs_it = tag_pairs.find(found_tag_num);
289  if(tag_pairs_it == tag_pairs.end())
290  {
291  errs() << "Error: Tag could not be found from the generated script, ignoring this loop." << "\n";
292  return false;
293  }
294  else
295  {
296  auto tag_string_it = std::find(loop_tags.begin(), loop_tags.end(), tag_pairs_it->second);
297  if(tag_string_it == loop_tags.end())
298  {
299  return false; // Do not process this loop
300  }
301  // Process the loop otherwise
302  tag_name = *tag_string_it;
303  }
304 
305  if (!L->getSubLoops().empty()) // only run on innermost loops
306  return false;
307 
308  unsigned int bb_count = L->getBlocks().size();
309  if(bb_count > 1) // More then one basic block within a loop
310  {
311  errs() << "Error: Loop with tag: " << tag_name << " is not supported. This loop is ignored." << "\n";
312  return false;
313  }
314 
315  // Heper for making node names unique
316  OpNameMaker makeOpName {L};
317 
318  // the opgraph
319  OpGraph og;
320 
321  // Make an op in the graph for every Instruction or external Value operand
322  // And, return a map from Value to Op
323  auto llvm_value_to_dfg_op = [&]() { // (immediately invoked)
324  std::map<const Value*, OpGraph::OpDescriptor> result;
325 
326  for(const auto& bb : L->blocks()) {
327  for(const auto& inst : *bb) {
328  if (&inst == the_loop_tag) { continue; }
329 
330  // make an op node for it
331  result[&inst] = og.emplace(makeOpName(&inst, inst.getOpcodeName()), 32, LLVMtoOp(inst));
332 
333  // make op nodes for any operands
334  for (const auto& operand : inst.operands()) {
335  const auto& operand_as_value = *operand;
336 
337  // skip if already handled
338  if (result.find(&operand_as_value) != result.end()) { continue; }
339 
340  // skip if is an Instruction and it is *in* this Loop
341  const auto* operand_as_inst_ptr = dyn_cast<Instruction>(&operand_as_value);
342  if (operand_as_inst_ptr && L->contains(operand_as_inst_ptr)) { continue; }
343 
344 
345  // Now, make an op for the operand...
346  result[&operand_as_value] = og.insert([&]() -> OpGraphOp { // (immediately invoked)
347  const auto op_name = makeOpName(operand);
348 
349  // try to handle various constants
350  const auto success_and_value = try_extract_integral_constant(*operand);
351  if (success_and_value.first) {
352  return {std::move(op_name), 32, OpCode::CONST, (std::int64_t)success_and_value.second};
353  }
354 
355  // else, assume it's some sort of input
356  return {std::move(op_name), 32, OpCode::INPUT};
357  }());
358  }
359  }
360  }
361  return result;
362  }();
363 
364  // Link everything together and create output nodes
365  for(const auto& bb : L->blocks()) {
366  for(const auto& inst : *bb) {
367  if (&inst == the_loop_tag) { continue; }
368  const auto& op_for_inst = llvm_value_to_dfg_op.at(&inst);
369 
370  int operand_num = -1;
371  for (const auto& operand : inst.operands()) {
372  operand_num += 1;
373  const auto& operand_as_value = *operand;
374  const auto& operand_op = llvm_value_to_dfg_op.at(&operand_as_value);
375  og.link(operand_op, op_for_inst, operandTypeFor(op_for_inst, operand_num));
376  }
377 
378  // if this Instruction has users outside of this loop, or non-Instruction users,
379  // make an output node connected to it.
380  if(std::any_of(inst.user_begin(), inst.user_end(), [&](const auto& user) {
381  const auto* user_as_instruction_ptr = dyn_cast<Instruction>(&*user);
382  return not user_as_instruction_ptr || not L->contains(user_as_instruction_ptr);
383  })) {
384  og.insert(op_for_inst, OpGraphOp(makeOpName(&inst,"output"), 32, OpCode::OUTPUT), "");
385  }
386  }
387  }
388 
389  // initial graph is constructed. validate it.
390  verifyAndPrintReport(og, std::cerr, true, true);
391 
392  // make reverse map
393  auto dfg_op_to_llvm_value = [&]() { // (immediately invoked)
394  std::map<OpGraph::OpDescriptor, const Value*> result;
395  for (const auto& value_and_op : llvm_value_to_dfg_op) {
396  result.emplace(value_and_op.second, value_and_op.first);
397  }
398  return result;
399  }();
400 
401  // lower gep nodes. Has to be done in this code, unless we want to save all relevant information on the GEP nodes...
402  const auto orig_geps = filter_collection(og.opNodes(), [&og](auto&& op) { return og.getNodeRef(op).getOpCode() == OpGraphOpCode::GEP; });
403  for (const auto& gep_op : orig_geps) {
404  const auto inst_ptr = dyn_cast<GetElementPtrInst>(dfg_op_to_llvm_value.at(gep_op));
405  if (not inst_ptr) {
406  errs() << "Warning: Instruction " << *dfg_op_to_llvm_value.at(gep_op)
407  << " for GEP op node " << og.getNodeRef(gep_op).getName() << " is not a GEP. Will not lower";
408  continue;
409  }
410  const auto& inst = *inst_ptr;
411 
412  // start with the 0th arg (guaranteed to be present)
413  OpGraph::OpDescriptor tip = llvm_value_to_dfg_op.at(inst.getOperand(0));
414 
415  // chain the rest together, and connect the inputs
416  {gep_type_iterator GTI = gep_type_begin(&inst);
417  int gep_operand_num = 1;
418  for(auto operand_it = std::next(inst.op_begin()); operand_it != inst.op_end(); ++operand_it, ++GTI, ++gep_operand_num) {
419  const auto data_size = inst.getParent()->getModule()->getDataLayout().getTypeAllocSize(GTI.getIndexedType());
420  const auto data_size_op = og.emplace(makeOpName(&inst, "data_size", gep_operand_num), 32, OpCode::CONST, data_size);
421  const auto mult_by_size = og.emplace(makeOpName(&inst, "mul", gep_operand_num), 32, OpCode::MUL);
422  const auto add_to_prev = og.emplace(makeOpName(&inst, "add", gep_operand_num), 32, OpCode::ADD);
423  og.link(data_size_op, mult_by_size, Operands::BINARY_ANY);
424  og.link(llvm_value_to_dfg_op.at(*operand_it), mult_by_size, Operands::BINARY_ANY);
425  og.link(tip, add_to_prev, Operands::BINARY_ANY);
426  og.link(mult_by_size, add_to_prev, Operands::BINARY_ANY);
427  tip = add_to_prev;
428  }}
429 
430  // make links from final tip to the original's fanout
431  for (const auto& edge : og.outEdges(gep_op)) {
432  og.link_like(tip, og.targetOfEdge(edge), edge);
433  }
434 
435  // update maps & erase original node
436  llvm_value_to_dfg_op[inst_ptr] = tip;
437  dfg_op_to_llvm_value[tip] = inst_ptr;
438  og.erase(gep_op);
439  }
440 
441  // validate after GEP lowering
442  verifyAndPrintReport(og, std::cerr, true, true);
443 
444  og = removeCastNodes(std::move(og));
445 
446  // ****
447  // all further optimization and transformation is done in CGRA-ME at runtime. Just print and return.
448  // ****
449 
450  // a final validation
451  verifyAndPrintReport(og, std::cerr, true, true);
452 
453  // create a ranking to keep the print order stable-ish
454  const auto op_print_ranking = [&]() { // (immediately invoked)
455  std::map<OpGraph::OpDescriptor, int> result;
456  for (const auto& bb : make_iterator_range(L->block_begin(), L->block_end())) {
457  for (const auto& inst : *bb) {
458  const auto lookup_result = llvm_value_to_dfg_op.find(&inst);
459  if (lookup_result == llvm_value_to_dfg_op.end()) { continue; }
460 
461  // find any fanin that won't be otherwise printed. A depth-first search
462  std::vector<OpGraph::OpDescriptor> to_visit = og.inputOps(lookup_result->second);
463  std::set<OpGraph::OpDescriptor> visited;
464  std::vector<OpGraph::OpDescriptor> dfs_order;
465  while (not to_visit.empty()) {
466  const auto curr = to_visit.back();
467  to_visit.pop_back();
468  if (not visited.insert(curr).second) { continue; }
469  const auto& inst_lookup = dfg_op_to_llvm_value.find(curr);
470  if (inst_lookup != dfg_op_to_llvm_value.end()) {
471  if (const auto inst = dyn_cast<Instruction>(inst_lookup->second)) {
472  if (L->contains(inst)) { continue; } // won't come up in the outer loops
473  }
474  }
475  const auto& inputs = og.inputOps(curr);
476  std::copy(inputs.begin(), inputs.end(), std::back_inserter(to_visit));
477  dfs_order.push_back(curr);
478  }
479 
480  // rank backward. DFS was a fanin DFS, and we want immediate fanins printed last.
481  for (auto it = dfs_order.rbegin(); it != dfs_order.rend(); ++it) {
482  result.insert({*it, static_cast<int>(result.size())});
483  }
484 
485  // now rank the current op
486  result.insert({lookup_result->second, static_cast<int>(result.size())});
487  }
488  }
489 
490  return result;
491  }();
492 
493  // now, actually print
494  std::ofstream f("graph_" + tag_name + ".dot", std::ios::out);
495  og.serialize(f, op_print_ranking);
496 
497  return false;
498  }
499  };
500 
501  struct LegacyDFGOut : public LoopPass
502  {
503  static char ID; // Pass identification, replacement for typeid
504  DfgOutImpl impl = {};
505 
506  LegacyDFGOut() : LoopPass(ID) {}
507 
508  virtual bool runOnLoop(Loop* L, LPPassManager&) {
509  return impl.runOnLoop(L);
510  }
511 
512  virtual bool doFinalization()
513  {
514  return false;
515  }
516  };
517 }
518 
519 
520 /* Legacy PM Registration */
521 char LegacyDFGOut::ID = 0;
522 static RegisterPass<LegacyDFGOut> X("dfg-out", "DFG(Data Flow Graph) Output Pass",
523  false /* Only looks at CFG */,
524  false /* Analysis Pass */);
525 
526 /* New PM Registration */
527 #if LLVM_VERSION_MAJOR >= 14
528 namespace {
529 struct DfgOut : PassInfoMixin<DfgOut> {
530  DfgOutImpl impl = {};
531  PreservedAnalyses run(Loop& L, LoopAnalysisManager& , LoopStandardAnalysisResults& , LPMUpdater&) {
532  return impl.runOnLoop(&L) ? PreservedAnalyses::none() : PreservedAnalyses::all();
533  }
534 };
535 } // namespace
536 
537 extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {
538  return {
539  LLVM_PLUGIN_API_VERSION,
540  "DfgOut",
541  LLVM_VERSION_STRING,
542  [](PassBuilder& PB) {
543  PB.registerPipelineParsingCallback([](StringRef Name, LoopPassManager& PM, ArrayRef<PassBuilder::PipelineElement>) {
544  if (Name == "dfg-out") {
545  PM.addPass(DfgOut());
546  return true;
547  }
548  return false;
549  });
550  }
551  };
552 }
553 #endif /* LLVM_VERSION_MAJOR >= 14 */
OpCode::BR
@ BR
OpCode::DIV
@ DIV
verifyAndPrintReport
bool verifyAndPrintReport(const MRRG &mrrg, std::ostream &os, bool silent_on_no_errors, bool throw_if_errors, const ConfigStore &extra_opts)
Definition: MRRG.cpp:473
OpGraph.h
OpCode::CONST
@ CONST
OpCode::PHI
@ PHI
OpGraphProcedures.h
OpGraph::getNodeRef
OpGraphOp & getNodeRef(OpDescriptor ndesc)
Definition: OpGraph.h:376
Operands::BINARY_LHS
static constexpr auto & BINARY_LHS
Definition: OpGraph.h:89
OpCode::ASHR
@ ASHR
OpGraph::targetOfEdge
OpDescriptor targetOfEdge(EdgeDescriptor ed) const
Definition: OpGraph.cpp:525
OpCode::OR
@ OR
removeCastNodes
OpGraph removeCastNodes(OpGraph og)
Definition: OpGraphProcedures.cpp:313
OpCode::STORE
@ STORE
OpGraph::inputOps
std::vector< OpDescriptor > inputOps(OpDescriptor op) const
Definition: OpGraph.cpp:497
OpCode::ADD
@ ADD
OpGraph::serialize
void serialize(std::ostream &s) const
Definition: OpGraph.cpp:1070
OpGraph::link_like
ValDescriptor link_like(OpDescriptor driver, OpDescriptor fanout, EdgeDescriptor base)
Definition: OpGraph.cpp:418
to_string
const std::string & to_string(const OpGraphOpCode &opcode)
Definition: OpGraph.cpp:111
OpGraphOpCode
signed char OpCode OpGraphOpCode
Definition: OpGraph.h:15
OpCode::LOAD
@ LOAD
make_iterator_range
auto make_iterator_range(BIter &&b, EIter &&e)
Definition: Collections.h:48
OpCode::LSHR
@ LSHR
OpCode::NOP
@ NOP
OpCode::XOR
@ XOR
Operands::BINARY_RHS
static constexpr auto & BINARY_RHS
Definition: OpGraph.h:90
OpCode::GEP
@ GEP
OpGraph::link
ValDescriptor link(OpDescriptor driver, OpDescriptor fanout, std::string operand_group, int bitwidth=32, int dist=0, EdgeKind kind=EdgeKind::kDataFlow, bool predicate=false)
Definition: OpGraph.cpp:364
OpCode::INPUT
@ INPUT
OpGraph::opNodes
auto & opNodes() const
Definition: OpGraph.h:313
OpCode::SHL
@ SHL
OpCode::TRUNC
@ TRUNC
Collections.h
OpGraphOp
Definition: OpGraph.h:131
OpCode::OUTPUT
@ OUTPUT
OpCode::AND
@ AND
OpGraph::insert
OpDescriptor insert(OpGraphOp op)
Definition: OpGraph.cpp:338
OpCode::SUB
@ SUB
OpCode::MUL
@ MUL
OpCode::ICMP
@ ICMP
Operands::MEM_DATA
static constexpr auto & MEM_DATA
Definition: OpGraph.h:94
OpGraph::outEdges
std::vector< EdgeDescriptor > outEdges(const OpDescriptor &op) const
Definition: OpGraph.cpp:530
Operands::BINARY_ANY
static constexpr auto & BINARY_ANY
Definition: OpGraph.h:91
OpGraph::emplace
OpDescriptor emplace(Args &&... args)
Definition: OpGraph.h:263
OpGraph::erase
void erase(OpDescriptor op)
Definition: OpGraph.cpp:453
filter_collection
auto filter_collection(const COLLECTION &base_container, UNARY_PREDICATE &&filter)
Definition: Collections.h:317
OpGraph
Definition: OpGraph.h:215
OpGraphOp::opcode
OpGraphOpCode opcode
Definition: OpGraph.h:158
X
static RegisterPass< LegacyDFGOut > X("dfg-out", "DFG(Data Flow Graph) Output Pass", false, false)
OpGraphNode::getName
const std::string & getName() const
Definition: OpGraph.h:121
Operands::MEM_ADDR
static constexpr auto & MEM_ADDR
Definition: OpGraph.h:93