CGRA-ME
OpGraphProcedures.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 <CGRA/OpGraphProcedures.h>
12 
13 #include <CGRA/GraphAlgorithms.h>
15 
16 namespace {
17 
18 std::pair<OpGraph::Walk, OpGraph::Walk> reverse_and_canonicalize
19  (std::pair<OpGraph::Walk, OpGraph::Walk>&& line_trail_pair) {
20  std::reverse(line_trail_pair.first.begin(), line_trail_pair.first.end());
21  std::reverse(line_trail_pair.second.begin(), line_trail_pair.second.end());
22  if (line_trail_pair.second < line_trail_pair.first) {
23  std::swap(line_trail_pair.first, line_trail_pair.second);
24  }
25  return line_trail_pair;
26 }
27 
28 OpGraph::Walk reverse_and_canonicalize(OpGraph::Walk&& cyclic_trail, const OpGraph& opgraph) {
29  std::reverse(cyclic_trail.begin(), cyclic_trail.end());
30  auto new_begin = std::min_element(cyclic_trail.begin(), cyclic_trail.end(), [&](const auto& lhs, const auto& rhs) {
31  return opgraph.getNodeRef(lhs.val).getName() < opgraph.getNodeRef(rhs.val).getName();
32  });
33  std::rotate(cyclic_trail.begin(), new_begin, cyclic_trail.end());
34  return cyclic_trail;
35 }
36 
37 } // end anon namespace
38 
39 // see comment in header for algorithm overview
41  using EdgeDesc = OpGraph::EdgeDescriptor;
42  using OpDesc = OpGraph::OpDescriptor;
43 
44  // return value
45  TrailsToBalance result;
46 
47  // ops that have been visited by previous iterations
48  std::set<OpDesc> ops_visited;
49 
50  // non-tree and re-convergence edges that have been visited by previous iterations
51  // note: re-convergence edges are symmetric with non-tree ones, so we treat them the same here
52  std::set<EdgeDesc> nte_and_convergence_edges_found;
53 
54  // use (ordered) sets -- no hash defined
55  // also, we'll be treating hyper-edge output pins as vertices; as-if it were a multi-graph
56  const auto galgos = GraphAlgorithms<EdgeDesc,
58  >{};
59 
60  for (OpDesc op : opgraph.opNodes()) {
61  if (ops_visited.find(op) != ops_visited.end()) { continue; }
62 
63  // start as far up as possible, to make for fewer iterations.
64  // note: for an unvisited Op, all Ops reachable by fanin edges must have also not been visited.
65  {std::set<OpDesc> ancestors;
66  while (true) {
67  const auto& fanin = opgraph.inputVals(op);
68  if (fanin.empty() || not ancestors.insert(op).second) { break; }
69  op = opgraph.inputOp(*fanin.begin());
70  }}
71 
72  struct FindOpNonTreeEdgesVisitor : DoNothingGraphVisitor<EdgeDesc> {
73  const OpGraph* opgraph = nullptr;
74  std::set<OpDesc> examined_op_targets = {};
75  std::set<EdgeDesc> non_tree_edges = {};
76 
77  void onExamine(const EdgeDesc& e) {
78  // if this hyper-edge's target was already visited, save it
79  if (not examined_op_targets.insert(opgraph->targetOfEdge(e)).second) {
80  non_tree_edges.insert(e);
81  }
82  }
83  } visitor;
84  visitor.opgraph = &opgraph;
85  visitor.examined_op_targets.insert(op); // handle edges that loop around to op
86 
87  // a search with with the hyper-edge output pins as vertices
88  // returns a fanin graph representing the search, where fanin.front() defines a spanning tree
89  auto search_tree = galgos.wavedBreadthFirstVisit(opgraph, opgraph.outEdges(op), visitAllVertices(), visitor);
90 
91  // ancestors (including e), if you walk up the search tree. halts at a cycle.
92  const auto determineSearchTreeAncestors = [&](const auto& e) {
93  auto traceback = galgos.singleTraceback(std::set<EdgeDesc>(), e, search_tree);
94  std::reverse(traceback.path.begin(), traceback.path.end());
95  return std::move(traceback.path);
96  };
97 
98  // finds the single search tree edge that drives the target of `e`
99  const auto determineSearchTreeEdgeWithSameTarget = [&](const auto& e) {
100  for (const auto& edge_to_target : opgraph.inEdges(opgraph.targetOfEdge(e))) {
101  if (visitor.non_tree_edges.find(edge_to_target) != visitor.non_tree_edges.end()) { continue; }
102  if (search_tree.find(edge_to_target) == search_tree.end()) { continue; }
103  return edge_to_target;
104  }
105  throw make_from_stream<cgrame_error>([&](auto&& s) {
106  s << "couldn't find the search tree edge with same target as " << e;
107  });
108  };
109 
110  const auto same_driver = [&](auto&& e1, auto&& e2) { return opgraph.inputOp(e1.val) == opgraph.inputOp(e2.val); };
111 
112  // each non-tree edge represents one constraint.
113  // examine the spanning tree around the target and driver to determine type and members
114  for (const auto nte : visitor.non_tree_edges) {
115  // only one constraint per nte or convergence edge!
116  if (not nte_and_convergence_edges_found.insert(nte).second) { continue; }
117 
118  // if there is any tree or non-tree edge that is driven by the target of nte, then it's a cycle
119  EdgeDesc cycle_edge;
120  const auto reverse_search_tree = galgos.wavedBreadthFirstVisit(
121  makeReversedGraphFromFaninLists<EdgeDesc>(&search_tree), {nte},
122  // input_is(opgraph.targetOfEdge(nte))
123  [&](auto& e) {
124  const bool found = opgraph.targetOfEdge(nte) == opgraph.inputOp(e.val);
125  if (found) { cycle_edge = e; }
126  return found;
127  }
128  );
129  if (cycle_edge) {
130  auto traceback = galgos.singleTraceback(singleItemSet(nte), cycle_edge, reverse_search_tree);
131  if (not traceback.success) { throw make_from_stream<cgrame_error>([fname=__func__](auto&& s) {
132  s << fname << " couldn't find original non-tree edge when searching a reverse search tree";
133  });}
134  if (nte == cycle_edge) {
135  traceback.path.pop_back(); // remove duplicate
136  }
137  // if already inserted, then it's a re-convergence edge
138  if (result.cyclic_trails.insert(reverse_and_canonicalize(std::move(traceback.path), opgraph)).second) {
139  continue;
140  }
141  }
142 
143  // else, it's a re-convergence -- find the common ancestor
144  const auto convergence_edge = determineSearchTreeEdgeWithSameTarget(nte); // ce for short
145  // every re-convergence edge represents symmetry in the graph, so treat it the same as a non-tree edge
146  if (not nte_and_convergence_edges_found.insert(convergence_edge).second) { continue; }
147  const auto nte_ancestors = determineSearchTreeAncestors(nte);
148  const auto ce_ancestors = determineSearchTreeAncestors(convergence_edge);
149  const auto same_driver_search = find_first_of_and_matching(nte_ancestors, ce_ancestors, same_driver);
150  if (same_driver_search.first != nte_ancestors.end()) {
151  result.noncyclic_trail_pairs.insert(reverse_and_canonicalize({
152  {nte_ancestors.begin(), std::next(same_driver_search.first)},
153  {ce_ancestors.begin(), std::next(same_driver_search.second)},
154  }));
155  continue;
156  }
157 
158  throw make_from_stream<cgrame_error>([nte,fname=__func__](auto&& s) {
159  s << fname << " couldn't construct a constraint from the non-tree edge " << nte
160  << ". It is neither a cycle or a re-convergence!";
161  });
162  }
163 
164  // synchronize the global visited set with the one for this iteration
165  std::copy(visitor.examined_op_targets.begin(), visitor.examined_op_targets.end(), std::inserter(ops_visited, ops_visited.end()));
166  }
167 
168  return result;
169 }
170 
171 void TrailsToBalance::print_to(std::ostream& os) const {
172  os << "Trails to balance: {\n";
173  os << " Line trail pairs to be balanced: ";
174  print_container(os, noncyclic_trail_pairs, ",\n ", "{ ", " }", [](auto&& os, const auto& trail_pair) {
175  print_container(os, trail_pair.first);
176  os << " == ";
177  print_container(os, trail_pair.second);
178  });
179 
180  os << "\n Cycle trails to be balanced: ";
181  print_container(os, cyclic_trails, ",\n ", "{ ", " }", [](auto&& os, const auto& trail) {
182  print_container(os, trail);
183  });
184  os << "\n}";
185 }
186 
188  OpGraph result;
189  auto cg_vert_to_op = std::map<ConfigGraph::VertexID, OpGraph::OpDescriptor>{};
190  for (const auto& v : config.allNodes()) {
191  const auto& v_attrs = config.attributes(v);
192 
193  const bool has_input = v_attrs.hasKey("input");
194  const bool has_output = v_attrs.hasKey("output");
195  const bool has_opcode = v_attrs.hasKey("opcode");
196  const int bitwidth = v_attrs.getIntOr("bitwidth", 32);
197  const std::string node_config = v_attrs.getStringOr("config", "");
198  if ((int)has_input + (int)has_output + (int)has_opcode > 1) {
199  throw make_from_stream<cgrame_error>([&](auto&& s) {
200  s << "ambiguous: op " << config.name(v)
201  << " specifies more than one of 'input', 'output', and 'opcode'";
202  });
203  }
204 
205  const auto opcode =
206  has_opcode ? [&] { return opcode_from_string(v_attrs.getString("opcode")); }()
207  : has_input ? OpCode::INPUT
208  : has_output ? OpCode::OUTPUT
209  : throw make_from_stream<cgrame_error>([&](auto&& s) {
210  s << "op: " << config.name(v) << " does not specify an opcode!";
211  });
212 
213  if (opcode == OpCode::CONST) {
214  const auto src = cg_vert_to_op[v] = result.insert({
215  config.name(v),
216  bitwidth,
217  opcode,
218  v_attrs.getInt("constVal")
219  });
220  } else if (opcode == OpCode::CMP) {
221  const auto src = cg_vert_to_op[v] = result.insert({
222  config.name(v),
223  bitwidth,
224  opcode,
225  v_attrs.getString("cmpMode"),
226  true
227  });
228  } else if ((opcode == OpCode::LOAD || opcode == OpCode::STORE) & (!config.empty())) {
229  BitConfig* bitConfig = new BitConfig(1);
230  std::vector<BitSetting> bitSet;
231  for (char bit : node_config) {
232  bitSet.push_back(from_bool<BitSetting>(int(bit) - 48));
233  }
234  bitConfig->add(bitSet, 0);
235  const auto src = cg_vert_to_op[v] = result.insert({
236  config.name(v),
237  bitwidth,
238  opcode,
239  bitConfig
240  });
241  } else if (opcode == OpCode::LOAD || opcode == OpCode::STORE) {
242  const auto src = cg_vert_to_op[v] = result.insert({
243  config.name(v),
244  bitwidth,
245  opcode,
246  v_attrs.getStringOr("memName", ""),
247  false
248  });
249  } else {
250  const auto src = cg_vert_to_op[v] = result.insert({
251  config.name(v),
252  bitwidth,
253  opcode
254  });
255  }
256  }
257 
258  for (const auto& v : config.allNodes()) {
259  for (const auto& out_edge : config.outEdges(v)) {
260  const auto& e_attrs = config.attributes(out_edge);
261  result.link(
262  cg_vert_to_op.at(v), cg_vert_to_op.at(config.target(out_edge)),
263  e_attrs.getStringOr("operand", ""),
264  e_attrs.getIntOr("bitwidth", 32),
265  e_attrs.getIntOr("dist", 0),
266  ((e_attrs.getStringOr("kind", "").compare("alias") == 0) ? EdgeKind::kAlias : EdgeKind::kDataFlow),
267  e_attrs.getBoolOr("predicate", false));
268  }
269  }
270  return result;
271 }
272 
273 
274 namespace {
275 
279 OpGraph removeTheseNodesAndAnyNowUnusedFaninRecursively(OpGraph opgraph, std::vector<OpGraph::OpDescriptor> ops_to_erase) {
280  // don't erase the same thing twice
281  std::set<OpGraph::OpDescriptor> will_be_or_is_erased{ops_to_erase.begin(), ops_to_erase.end()};
282 
283  // while there are nodes to be erased
284  while (not ops_to_erase.empty()) {
285  // pop one
286  const auto op = ops_to_erase.back();
287  ops_to_erase.pop_back();
288  // save the fanins (undefined after erase)
289  const auto op_inputs = opgraph.inputOps(op);
290  // erase it
291  opgraph.erase(op);
292  // add any fanin that now have no fanout to the erase stack
293  for (const auto& input_op : op_inputs) {
294  if (not opgraph.outputOps(input_op).empty()) { continue; }
295  if (not will_be_or_is_erased.insert(input_op).second) { continue; }
296  ops_to_erase.push_back(input_op);
297  }
298  }
299 
300  return opgraph;
301 }
302 
303 } // end anon namespace
304 
306  // start with the br nodes
307  auto ops_to_erase = filter_collection(std::vector<OpGraph::OpDescriptor>{opgraph.opNodes().begin(), opgraph.opNodes().end()},
308  [&](const auto& opdesc) { return opgraph.getNodeRef(opdesc).getOpCode() == OpCode::BR; });
309 
310  return removeTheseNodesAndAnyNowUnusedFaninRecursively(std::move(opgraph), ops_to_erase);
311 }
312 
314  const auto orig_casts = filter_collection(og.opNodes(), [&og](auto&& op) {
315  const auto opcode = og.getNodeRef(op).getOpCode();
316  return og.inputVals(op).size() == 1 &&
317  (opcode == OpGraphOpCode::NOP
318  || opcode == OpGraphOpCode::SEXT
319  || opcode == OpGraphOpCode::ZEXT
320  || opcode == OpGraphOpCode::TRUNC);
321  });
322 
323  for (const auto& cast_op : orig_casts) {
324  const auto input_op = og.inputOp(og.inputVals(cast_op).front());
325  for (const auto& edge : og.outEdges(cast_op)) {
326  og.link_like(input_op, og.targetOfEdge(edge), edge);
327  }
328  og.erase(cast_op);
329  }
330 
331  return og;
332 }
333 
335  const auto initial_op_nodes = opgraph.opNodes();
336 
337  auto erased_opnodes = std::set<OpGraph::OpDescriptor>{};
338  const auto erase_op = [&](OpGraph::OpDescriptor op_desc) {
339  opgraph.erase(op_desc);
340  erased_opnodes.insert(op_desc);
341  };
342 
343  for (const auto& op_desc : initial_op_nodes) {
344  const auto& opnode = opgraph.getNodeRef(op_desc);
345  if (erased_opnodes.find(op_desc) != erased_opnodes.end()) { continue; } // skip if removed
346  if (opnode.getOpCode() != OpCode::PHI) { continue; } // only care about PHIs
347 
348  // const or input nodes
349  const auto is_external_edge = [&](const auto& in_edge) {
350  const auto& in_opcode = opgraph.getNodeRef(opgraph.inputOp(in_edge.val)).getOpCode();
351  return in_opcode == OpCode::CONST || in_opcode == OpCode::INPUT;
352  };
353  const auto external_in_edges = filter_collection(opgraph.inEdges(op_desc), is_external_edge);
354  const auto internal_in_edges = filter_collection(opgraph.inEdges(op_desc), [&](auto& e) { return !is_external_edge(e); });
355  if (external_in_edges.empty()) { throw std::logic_error("could not find external input edges to phi node '" + opnode.getName() + "'!"); }
356  if (internal_in_edges.empty()) { throw std::logic_error("could not find internal input edges to phi node '" + opnode.getName() + "'!"); }
357  if (internal_in_edges.size() > 1) { throw std::logic_error("multiple internal input edges to phi node '" + opnode.getName() + "'... does this loop have multiple basic blocks?"); }
358 
359  // make links to output. internal_in_edges is asserted to have size() == 1 above
360  for (const auto& int_in_edge : internal_in_edges) {
361  for (const auto& out_edge : opgraph.outEdges(op_desc)) {
362  opgraph.link_like(opgraph.inputOp(int_in_edge.val),opgraph.targetOfEdge(out_edge), out_edge);
363  }
364  }
365 
366  // erase phi & any now unused nodes
367  opgraph = removeTheseNodesAndAnyNowUnusedFaninRecursively(std::move(opgraph), {op_desc});
368  }
369 
370  return opgraph;
371 }
372 
374  bool made_changes = true;
375  while (made_changes) {
376  made_changes = false;
377  for (const auto& op : og.opNodes()) {
378  const auto& op_ref = og.getNodeRef(op);
379 
380  if (op_ref.getOpCode() == OpGraphOpCode::MUL) {
381  std::int64_t product_of_constants = 1;
382  int num_const_inputs = 0;
383 
384  // count const inputs and compute their product
385  for (const auto& in_edge : og.inEdges(op)) {
386  const auto& in_op_ref = og.getNodeRef(og.inputOp(in_edge.val));
387  if (in_op_ref.getOpCode() == OpGraphOpCode::CONST) {
388  product_of_constants *= in_op_ref.getConstValue();
389  num_const_inputs += 1;
390  }
391  }
392 
393  if (num_const_inputs == (std::ptrdiff_t)og.inEdges(op).size() || product_of_constants == 0) {
394  // if all inputs are consts, or one or more constant inputs are 0, replace the whole op.
395  const auto new_const = og.emplace(op_ref.getName() + "_as_const",
396  op_ref.bitwidth, OpGraphOpCode::CONST, product_of_constants);
397  for (const auto& out_edge : og.outEdges(op)) {
398  og.link_like(new_const, og.targetOfEdge(out_edge), out_edge);
399  }
400  og = removeTheseNodesAndAnyNowUnusedFaninRecursively(std::move(og), {op});
401  made_changes = true;
402  // } else if (num_const_inputs > 1) {
403  // if there are more than one constant, we could fold them
404  // but what about, say, a 3-input multiply, with 2 constant inputs?
405  // can that be reduced to just a two-input one?
406  // Or, what would the operand of the merged constant be?
407  }
408  }
409 
410  if (op_ref.getOpCode() == OpGraphOpCode::ADD) {
411  int64_t sum_of_constants = 0;
412  int num_const_inputs = 0;
413  OpGraph::OpDescriptor a_non_const_input = {};
414 
415  // count const inputs and compute their sum
416  for (const auto& in_edge : og.inEdges(op)) {
417  const auto& in_op_ref = og.getNodeRef(og.inputOp(in_edge.val));
418  if (in_op_ref.getOpCode() == OpGraphOpCode::CONST) {
419  sum_of_constants += in_op_ref.getConstValue();
420  num_const_inputs += 1;
421  } else {
422  a_non_const_input = og.inputOp(in_edge.val);
423  }
424  }
425 
426  OpGraph::OpDescriptor replacement_op = {};
427 
428  const auto num_op_inputs = (std::ptrdiff_t)og.inEdges(op).size();
429  if (num_const_inputs == num_op_inputs) {
430  // if all inputs are consts, replace the whole op
431  replacement_op = og.emplace(op_ref.getName() + "_as_const",
432  op_ref.bitwidth, OpGraphOpCode::CONST, sum_of_constants);
433  } else if (num_const_inputs + 1 == num_op_inputs && sum_of_constants == 0) {
434  // if adding to 0, and only 1 non-const input, replace the op with the input
435  replacement_op = a_non_const_input; // the only one
436  }
437 
438  if (replacement_op) {
439  for (const auto& out_edge : og.outEdges(op)) {
440  og.link_like(replacement_op, og.targetOfEdge(out_edge), out_edge);
441  }
442  og = removeTheseNodesAndAnyNowUnusedFaninRecursively(std::move(og), {op});
443  made_changes = true;
444  }
445  }
446 
447  // if any changes were made, start over to refresh the opNodes for-loop
448  if (made_changes) { break; }
449  }
450  }
451 
452  return og;
453 }
454 
456  for (const auto& op : og.opNodes()) {
457  const auto& op_ref = og.getNodeRef(op);
458  if (op_ref.getOpCode() != OpGraphOpCode::CONST) { continue; }
459 
460  int dup_number = -1;
461  for (;;) {
462  const auto out_edges = og.outEdges(op);
463  // don't touch consts with fanout-of-1, and leave original op attached to one fanout
464  if (out_edges.size() <= 1) { break; }
465 
466  const auto& out_edge = out_edges.back();
467  dup_number += 1;
468  auto new_name = op_ref.getName() + "_dup" + std::to_string(dup_number);
469  og.link_like(og.insert(op_ref.propertyClone(std::move(new_name))), og.targetOfEdge(out_edge), out_edge);
470  og.unLink(out_edge.val, og.targetOfEdge(out_edge));
471  }
472  }
473  return og;
474 }
OpCode::BR
@ BR
StandardMapMaker
Definition: Collections.h:148
computeTrailsToBalance
TrailsToBalance computeTrailsToBalance(const OpGraph &opgraph)
Definition: OpGraphProcedures.cpp:40
BitConfig
Definition: BitSetting.h:58
ConfigGraph::allNodes
auto allNodes() const
Definition: ConfigGraph.h:142
ConfigGraph::attributes
const ConfigStore & attributes(const VertexID &v) const
Definition: ConfigGraph.h:128
ConfigGraph.h
OpCode::CONST
@ CONST
GraphAlgorithms
Definition: GraphAlgorithms.h:72
OpCode::PHI
@ PHI
GraphAlgorithms.h
removeBranchComputation
OpGraph removeBranchComputation(OpGraph opgraph)
Definition: OpGraphProcedures.cpp:305
TrailsToBalance::noncyclic_trail_pairs
std::set< TrailPair > noncyclic_trail_pairs
Definition: OpGraphProcedures.h:73
OpGraphProcedures.h
OpGraph::getNodeRef
OpGraphOp & getNodeRef(OpDescriptor ndesc)
Definition: OpGraph.h:376
propagateConstants
OpGraph propagateConstants(OpGraph og)
Definition: OpGraphProcedures.cpp:373
OpGraph::targetOfEdge
OpDescriptor targetOfEdge(EdgeDescriptor ed) const
Definition: OpGraph.cpp:525
OpGraphOp::getConstValue
std::int64_t getConstValue() const
Definition: OpGraph.h:152
begin
auto begin(const SingleItemImmutableSet< VertexID > &siis)
Definition: Collections.h:137
removeCastNodes
OpGraph removeCastNodes(OpGraph og)
Definition: OpGraphProcedures.cpp:313
removePhiNodes
OpGraph removePhiNodes(OpGraph opgraph)
Definition: OpGraphProcedures.cpp:334
OpGraph::inputVals
const std::vector< OpGraphVal * > & inputVals(OpDescriptor op) const
Definition: OpGraph.cpp:551
OpCode::STORE
@ STORE
visitAllVertices
auto visitAllVertices()
Definition: GraphAlgorithmHelpers.h:38
ConfigGraph
Definition: ConfigGraph.h:72
BitConfig::add
void add(std::vector< BitSetting > bitsetting, int cycle)
Definition: BitSetting.h:63
OpGraph::inputOps
std::vector< OpDescriptor > inputOps(OpDescriptor op) const
Definition: OpGraph.cpp:497
OpCode::CMP
@ CMP
ConfigGraph::outEdges
auto outEdges(const VertexID &v) const
Definition: ConfigGraph.h:148
find_first_of_and_matching
std::pair< InputIt, ForwardIt > find_first_of_and_matching(InputIt first, InputIt last, ForwardIt s_first, ForwardIt s_last, BinaryPredicate p)
Definition: Collections.h:373
OpGraph::link_like
ValDescriptor link_like(OpDescriptor driver, OpDescriptor fanout, EdgeDescriptor base)
Definition: OpGraph.cpp:418
from_bool< BitSetting >
BitSetting from_bool< BitSetting >(bool b)
Definition: BitSetting.cpp:17
OpGraph::inputOp
OpDescriptor inputOp(ValDescriptor val) const
Definition: OpGraph.cpp:504
DoNothingGraphVisitor::onExamine
void onExamine(const VertexID &)
Definition: GraphAlgorithmHelpers.h:55
to_string
const std::string & to_string(const OpGraphOpCode &opcode)
Definition: OpGraph.cpp:111
ConfigGraph::name
const std::string & name(const VertexID &v) const
Definition: ConfigGraph.h:123
OpCode::LOAD
@ LOAD
TrailsToBalance
Definition: OpGraphProcedures.h:67
OpGraph::inEdges
std::vector< EdgeDescriptor > inEdges(const OpDescriptor &op) const
Definition: OpGraph.cpp:540
ConfigStore::hasKey
bool hasKey(const std::string &key) const
Definition: ConfigStore.h:209
TrailsToBalance::cyclic_trails
std::set< Trail > cyclic_trails
Definition: OpGraphProcedures.h:76
OpGraph::outputOps
const std::vector< OpGraphOp * > & outputOps(ValDescriptor op) const
Definition: OpGraph.cpp:488
createOpGraphFromConfig
OpGraph createOpGraphFromConfig(const ConfigGraph &config)
Definition: OpGraphProcedures.cpp:187
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
distributeConstants
OpGraph distributeConstants(OpGraph og)
Definition: OpGraphProcedures.cpp:455
DoNothingGraphVisitor
Definition: GraphAlgorithmHelpers.h:49
OpGraphOp
Definition: OpGraph.h:131
ConfigGraph::target
const VertexID & target(const EdgeID &e) const
Definition: ConfigGraph.h:134
EdgeKind::kAlias
@ kAlias
print_container
void print_container(OSTREAM &&os, const CONTAINER &c, const T1 &element_separator, const T2 &container_prefix, const T3 &container_suffix, PRINTER &&element_printer=PRINTER{})
Definition: Collections.h:435
OpGraphOp::getOpCode
auto getOpCode() const
Definition: OpGraph.h:156
ConfigGraph::empty
bool empty() const
Definition: ConfigGraph.h:240
EdgeKind::kDataFlow
@ kDataFlow
OpCode::OUTPUT
@ OUTPUT
OpGraph::EdgeDescriptor
Definition: OpGraph.h:224
OpGraph::insert
OpDescriptor insert(OpGraphOp op)
Definition: OpGraph.cpp:338
StandardSetMaker
Definition: Collections.h:156
singleItemSet
SingleItemImmutableSet< VertexID > singleItemSet(VertexID v)
Definition: Collections.h:143
opcode_from_string
OpGraphOpCode opcode_from_string(const std::string &str)
Definition: OpGraph.cpp:113
OpGraph::outEdges
std::vector< EdgeDescriptor > outEdges(const OpDescriptor &op) const
Definition: OpGraph.cpp:530
OpGraph::unLink
void unLink(ValDescriptor driver_val, OpDescriptor fanout)
Definition: OpGraph.cpp:422
OpGraph::emplace
OpDescriptor emplace(Args &&... args)
Definition: OpGraph.h:263
TrailsToBalance::print_to
void print_to(std::ostream &os) const
Definition: OpGraphProcedures.cpp:171
OpGraph::erase
void erase(OpDescriptor op)
Definition: OpGraph.cpp:453
OpGraph::Walk
std::vector< EdgeDescriptor > Walk
Definition: OpGraph.h:231
filter_collection
auto filter_collection(const COLLECTION &base_container, UNARY_PREDICATE &&filter)
Definition: Collections.h:317
OpGraph::OpDescriptor
const OpGraphOp * OpDescriptor
Definition: OpGraph.h:219
OpGraph
Definition: OpGraph.h:215