Cerata
A library to generate structural hardware designs
node.cc
1 // Copyright 2018-2019 Delft University of Technology
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "cerata/node.h"
16 
17 #include <optional>
18 #include <utility>
19 #include <vector>
20 #include <memory>
21 #include <string>
22 
23 #include "cerata/utils.h"
24 #include "cerata/edge.h"
25 #include "cerata/pool.h"
26 #include "cerata/graph.h"
27 #include "cerata/expression.h"
28 #include "cerata/parameter.h"
29 
30 namespace cerata {
31 
32 Node::Node(std::string name, Node::NodeID id, std::shared_ptr<Type> type)
33  : Object(std::move(name), Object::NODE), node_id_(id), type_(std::move(type)) {}
34 
35 std::string Node::ToString() const {
36  return name();
37 }
38 
39 void ImplicitlyRebindNodes(Graph *dst, const std::vector<Node *> &nodes, NodeMap *rebinding) {
40  for (const auto &n : nodes) {
41  if (rebinding->count(n) > 0) {
42  // If it's already in the map, skip it.
43  continue;
44  } else if (dst->Has(n->name())) {
45  // If it already has a node with that name, select that one for rebinding. This is the implicit part.
46  (*rebinding)[n] = dst->Get<Node>(n->name());
47  } else if (!n->IsLiteral()) {
48  // We skip literals. Any other nodes we copy onto the destination graph.
49  n->CopyOnto(dst, n->name(), rebinding);
50  }
51  }
52 }
53 
54 Node *Node::CopyOnto(Graph *dst, const std::string &name, NodeMap *rebinding) const {
55  // Make a normal copy (that does not rebind the type generics).
56  auto result = std::dynamic_pointer_cast<Node>(this->Copy());
57  result->SetName(name);
58  // Default node only has a type in which other nodes could be referenced through the type's generics.
59  auto generics = this->type()->GetGenerics();
60  // If it has any generics, we might need to rebind them.
61  if (!generics.empty()) {
62  // Resolve the rebinding.
63  ImplicitlyRebindNodes(dst, generics, rebinding);
64  // Make a copy of the type, rebinding the generic nodes.
65  auto rebound_type = result->type()->Copy(*rebinding);
66  // Set the type of the new node to this new type.
67  result->SetType(rebound_type);
68  }
69  // Append this node to the rebind map.
70  (*rebinding)[this] = result.get();
71  // It is now possible to add the copy onto the graph.
72  dst->Add(result);
73  return result.get();
74 }
75 
76 Node *Node::Replace(Node *replacement) {
77  // Iterate over all sourcing edges of the original node.
78  for (const auto &e : this->sources()) {
79  auto src = e->src();
80  src->RemoveEdge(e);
81  this->RemoveEdge(e);
82  Connect(replacement, src);
83  }
84  // Iterate over all sinking edges of the original node.
85  for (const auto &e : this->sinks()) {
86  auto dst = e->src();
87  dst->RemoveEdge(e);
88  this->RemoveEdge(e);
89  Connect(dst, replacement);
90  }
91  // Remove the node from its parent, if it has one.
92  if (this->parent()) {
93  this->parent().value()->Remove(this);
94  this->parent().value()->Add(this->shared_from_this());
95  }
96  // Set array size node, if this is one.
97  if (this->IsParameter()) {
98  auto param = this->AsParameter();
99  if (param->node_array_parent) {
100  param->node_array_parent.value()->SetSize(replacement->shared_from_this());
101  }
102  }
103  return replacement;
104 }
105 
106 std::vector<Edge *> Node::edges() const {
107  auto snk = sinks();
108  auto src = sources();
109  std::vector<Edge *> edges;
110  edges.insert(edges.end(), snk.begin(), snk.end());
111  edges.insert(edges.end(), src.begin(), src.end());
112  return edges;
113 }
114 
115 Node *Node::SetType(const std::shared_ptr<Type> &type) {
116  type_ = type;
117  return this;
118 }
119 
120 void Node::AppendReferences(std::vector<Object *> *out) const {
121  for (const auto &g : type()->GetGenerics()) {
122  out->push_back(g);
123  // A type generic may refer to objects itself, append that as well.
124  g->AppendReferences(out);
125  }
126 }
127 
128 // Generate node casting convenience functions.
129 #ifndef NODE_CAST_IMPL_FACTORY
130 #define NODE_CAST_IMPL_FACTORY(NODENAME) \
131 NODENAME * Node::As##NODENAME() { auto result = dynamic_cast<NODENAME*>(this); \
132  if (result != nullptr) { \
133  return result; \
134  } else { \
135  CERATA_LOG(FATAL, "Node is not " + std::string(#NODENAME)); \
136 }} \
137 const NODENAME* Node::As##NODENAME() const { auto result = dynamic_cast<const NODENAME*>(this); \
138  if (result != nullptr) { \
139  return result; \
140  } else { \
141  CERATA_LOG(FATAL, "Node is not " + std::string(#NODENAME)); \
142  } \
143 }
144 #endif
145 
146 NODE_CAST_IMPL_FACTORY(Port)
147 NODE_CAST_IMPL_FACTORY(Signal)
148 NODE_CAST_IMPL_FACTORY(Parameter)
149 NODE_CAST_IMPL_FACTORY(Literal)
150 NODE_CAST_IMPL_FACTORY(Expression)
151 
152 #ifndef TYPE_STRINGIFICATION_FACTORY
153 #define TYPE_STRINGIFICATION_FACTORY(NODE_TYPE) \
154  template<> \
155  std::string ToString<NODE_TYPE>() { return #NODE_TYPE; }
156 #endif
157 TYPE_STRINGIFICATION_FACTORY(Port)
158 TYPE_STRINGIFICATION_FACTORY(Signal)
159 TYPE_STRINGIFICATION_FACTORY(Literal)
160 TYPE_STRINGIFICATION_FACTORY(Parameter)
161 TYPE_STRINGIFICATION_FACTORY(Expression)
162 
163 bool MultiOutputNode::AddEdge(const std::shared_ptr<Edge> &edge) {
164  bool success = false;
165  // Check if the source is this node
166  if (edge->src() == this) {
167  // Check if this node does not already contain this edge
168  if (!Contains(outputs_, edge)) {
169  // Add the edge to this node
170  outputs_.push_back(edge);
171  success = true;
172  }
173  }
174  return success;
175 }
176 
178  if (edge->src() == this) {
179  // This node sources the edge.
180  for (auto i = outputs_.begin(); i < outputs_.end(); i++) {
181  if (i->get() == edge) {
182  outputs_.erase(i);
183  return true;
184  }
185  }
186  }
187  return false;
188 }
189 
190 std::optional<Edge *> NormalNode::input() const {
191  if (input_ != nullptr) {
192  return input_.get();
193  }
194  return {};
195 }
196 
197 std::vector<Edge *> NormalNode::sources() const {
198  if (input_ != nullptr) {
199  return {input_.get()};
200  } else {
201  return {};
202  }
203 }
204 
205 bool NormalNode::AddEdge(const std::shared_ptr<Edge> &edge) {
206  // first attempt to add the edge as an output.
207  bool success = MultiOutputNode::AddEdge(edge);
208  // If we couldn't add the edge as output, it must be input.
209  if (!success) {
210  // Check if the edge has a destination.
211  if (edge->dst()) {
212  // Check if this is the destination.
213  if (edge->dst() == this) {
214  // Set this node source to the edge.
215  input_ = edge;
216  success = true;
217  }
218  }
219  }
220  return success;
221 }
222 
224  // First remove the edge from any outputs
225  bool success = MultiOutputNode::RemoveEdge(edge);
226  // Check if the edge is an input to this node
227  if ((edge->dst() != nullptr) && !success) {
228  if ((edge->dst() == this) && (input_.get() == edge)) {
229  input_.reset();
230  success = true;
231  }
232  }
233  return success;
234 }
235 
236 std::string ToString(Node::NodeID id) {
237  switch (id) {
238  case Node::NodeID::PORT:return "Port";
239  case Node::NodeID::SIGNAL:return "Signal";
240  case Node::NodeID::LITERAL:return "Literal";
241  case Node::NodeID::PARAMETER:return "Parameter";
242  case Node::NodeID::EXPRESSION:return "Expression";
243  }
244  throw std::runtime_error("Corrupted node type.");
245 }
246 
247 void GetObjectReferences(const Object &obj, std::vector<Object *> *out) {
248  if (obj.IsNode()) {
249  // If the object is a node, its type may be a generic type.
250  auto &node = dynamic_cast<const Node &>(obj);
251  // Obtain any potential type generic nodes.
252  auto params = node.type()->GetGenerics();
253  for (const auto &p : params) {
254  out->push_back(p);
255  }
256  // If the node is an expression, we need to grab every object from the tree.
257 
258  } else if (obj.IsArray()) {
259  // If the object is an array, we can obtain the generic nodes from the base type.
260  auto &array = dynamic_cast<const NodeArray &>(obj);
261  // Add the type generic nodes of the base node first.
262  GetObjectReferences(*array.base(), out);
263  // An array has a size node. Add that as well.
264  out->push_back(array.size());
265  }
266 }
267 
268 } // namespace cerata
cerata::Node::sinks
virtual std::vector< Edge * > sinks() const
Get the output edges of this Node.
Definition: node.h:88
cerata::MultiOutputNode::AddEdge
bool AddEdge(const std::shared_ptr< Edge > &edge) override
Add an output edge to this node.
Definition: node.cc:163
cerata::Node::SetType
Node * SetType(const std::shared_ptr< Type > &type)
Set the node Type.
Definition: node.cc:115
cerata::Node::sources
virtual std::vector< Edge * > sources() const
Get the input edges of this Node.
Definition: node.h:86
cerata::Graph::Add
virtual Graph & Add(const std::shared_ptr< Object > &object)
Add an object to the component.
Definition: graph.cc:32
cerata::NormalNode::AddEdge
bool AddEdge(const std::shared_ptr< Edge > &edge) override
Add an edge to this node.
Definition: node.cc:205
cerata::Node::NodeID::LITERAL
@ LITERAL
No-input AND multi-output node with storage type and storage value.
cerata::Edge::dst
Node * dst() const
Return the destination node.
Definition: edge.h:44
cerata::Literal
A Literal Node.
Definition: literal.h:36
cerata::Object::IsNode
bool IsNode() const
Return true if this object is a node.
Definition: object.h:52
cerata::Edge::src
Node * src() const
Return the source node.
Definition: edge.h:46
cerata::NormalNode::input_
std::shared_ptr< Edge > input_
The incoming Edge that sources this Node.
Definition: node.h:169
cerata::NormalNode::sources
std::vector< Edge * > sources() const override
Return the incoming edges (in this case just the single input edge).
Definition: node.cc:197
cerata::MultiOutputNode::outputs_
std::vector< std::shared_ptr< Edge > > outputs_
The outgoing Edges that sink this Node.
Definition: node.h:142
cerata::Expression
A node representing a binary tree of other nodes.
Definition: expression.h:34
cerata::Graph
A graph representing a hardware structure.
Definition: graph.h:37
cerata::NodeArray
An array of nodes.
Definition: array.h:31
cerata::Node::Replace
Node * Replace(Node *replacement)
Replace some node with another node, reconnecting all original edges. Returns the replaced node.
Definition: node.cc:76
cerata::Signal
A Signal Node.
Definition: signal.h:30
cerata
Contains every Cerata class, function, etc...
Definition: api.h:41
cerata::Node::type_
std::shared_ptr< Type > type_
The Type of this Node.
Definition: node.h:126
cerata::Node
A node.
Definition: node.h:42
cerata::Node::Node
Node(std::string name, NodeID id, std::shared_ptr< Type > type)
Node constructor.
Definition: node.cc:32
cerata::NodeMap
std::unordered_map< const Node *, Node * > NodeMap
A mapping from one object to another object, used in e.g. type generic rebinding.
Definition: node.h:135
cerata::Object::IsArray
bool IsArray() const
Return true if this object is an array.
Definition: object.h:54
cerata::Object::Copy
virtual std::shared_ptr< Object > Copy() const =0
Deep-copy the object.
cerata::Node::NodeID::PARAMETER
@ PARAMETER
Single-input AND multi-output node with default value.
cerata::Graph::Get
T * Get(const std::string &name) const
Get one object of a specific type.
Definition: graph.h:76
cerata::Named::name
std::string name() const
Return the name of the object.
Definition: utils.h:45
cerata::Node::NodeID
NodeID
Node type IDs with different properties.
Definition: node.h:45
cerata::ToString
std::string ToString(Expression::Op operation)
Human-readable expression operator.
Definition: expression.cc:149
cerata::Node::RemoveEdge
virtual bool RemoveEdge(Edge *edge)=0
Remove an edge of this node.
cerata::Node::edges
virtual std::vector< Edge * > edges() const
Return all edges this Node is on.
Definition: node.cc:106
cerata::Edge
A directed edge between two nodes.
Definition: edge.h:35
cerata::Node::NodeID::EXPRESSION
@ EXPRESSION
No-input AND multi-output node that forms a binary tree with operations and nodes.
cerata::GetObjectReferences
void GetObjectReferences(const Object &obj, std::vector< Object * > *out)
Get any sub-objects that are used by an object, e.g. type generic nodes or array size nodes.
Definition: node.cc:247
cerata::Object
A Cerata Object on a graph.
Definition: object.h:34
cerata::Node::NodeID::SIGNAL
@ SIGNAL
Single-input AND multi-output node.
cerata::Parameter
A Parameter node.
Definition: parameter.h:29
cerata::Contains
bool Contains(const std::vector< std::shared_ptr< T >> &list, const std::shared_ptr< T > &item)
Return true if vector contains item, false otherwise.
Definition: utils.h:58
cerata::Graph::Has
bool Has(const std::string &name)
Return true if object with name already exists on graph.
Definition: graph.cc:220
cerata::MultiOutputNode::RemoveEdge
bool RemoveEdge(Edge *edge) override
Remove an edge from this node.
Definition: node.cc:177
cerata::Node::ToString
virtual std::string ToString() const
Return a human-readable string of this node.
Definition: node.cc:35
cerata::Type::GetGenerics
virtual std::vector< Node * > GetGenerics() const
Obtain any nodes that this type uses as generics.
Definition: type.h:126
cerata::Node::AppendReferences
void AppendReferences(std::vector< Object * > *out) const override
Return all objects referenced by this node. For default nodes, these are type generics only.
Definition: node.cc:120
cerata::NormalNode::input
std::optional< Edge * > input() const
Return the single incoming edge.
Definition: node.cc:190
cerata::Node::NodeID::PORT
@ PORT
Single-input AND multi-output node with direction.
cerata::Node::CopyOnto
virtual Node * CopyOnto(Graph *dst, const std::string &name, NodeMap *rebinding) const
Copy node onto a graph, implicitly copying over and rebinding e.g. type generics of referenced nodes.
Definition: node.cc:54
cerata::Object::parent
virtual std::optional< Graph * > parent() const
Return the parent graph of this object, if any. Returns empty option otherwise.
Definition: object.cc:33
cerata::Port
A port is a terminator node on a graph.
Definition: port.h:57
cerata::Node::type
Type * type() const
Return the node Type.
Definition: node.h:57
cerata::NormalNode::RemoveEdge
bool RemoveEdge(Edge *edge) override
Remove an edge from this node.
Definition: node.cc:223
cerata::ImplicitlyRebindNodes
void ImplicitlyRebindNodes(Graph *dst, const std::vector< Node * > &nodes, NodeMap *rebinding)
Make sure that the NodeMap contains all nodes to be rebound onto the destination graph.
Definition: node.cc:39
cerata::MultiOutputNode
A no-input, multiple-outputs node.
Definition: node.h:140
cerata::Connect
std::shared_ptr< Edge > Connect(Node *dst, Node *src)
Connect two nodes, returns the corresponding edge.
Definition: edge.cc:68