chigraph  master
Systems programming language written for beginners in LLVM
GraphFunction.cpp
Go to the documentation of this file.
1 
3 #include "chi/GraphFunction.hpp"
4 #include "chi/Context.hpp"
5 #include "chi/DataType.hpp"
7 #include "chi/GraphModule.hpp"
8 #include "chi/NameMangler.hpp"
9 #include "chi/NodeInstance.hpp"
10 #include "chi/NodeType.hpp"
11 #include "chi/Support/Result.hpp"
12 
13 #include <llvm/AsmParser/Parser.h>
14 #include <llvm/IR/DIBuilder.h>
15 #include <llvm/IR/DerivedTypes.h>
16 #include <llvm/IR/Verifier.h>
17 #include <llvm/Support/SourceMgr.h>
18 
19 #include <boost/dynamic_bitset.hpp>
20 #include <boost/range/counting_range.hpp>
21 #include <boost/range/join.hpp>
22 #include <boost/uuid/uuid_io.hpp>
23 
24 namespace chi {
25 GraphFunction::GraphFunction(GraphModule& mod, std::string name, std::vector<NamedDataType> dataIns,
26  std::vector<NamedDataType> dataOuts, std::vector<std::string> execIns,
27  std::vector<std::string> execOuts)
28  : mModule{&mod},
29  mContext{&mod.context()},
30  mName{std::move(name)},
31  mDataInputs(std::move(dataIns)),
32  mDataOutputs(std::move(dataOuts)),
33  mExecInputs(std::move(execIns)),
34  mExecOutputs(std::move(execOuts)) {
35  // TODO(#66): check that it has at least 1 exec input and output
36 }
37 
38 NodeInstance* GraphFunction::nodeByID(const boost::uuids::uuid& id) const {
39  auto iter = nodes().find(id);
40  if (iter != nodes().end()) { return iter->second.get(); }
41  return nullptr;
42 }
43 
45  auto matching = nodesWithType("lang", "entry");
46 
47  if (matching.size() == 1) {
48  auto& vec = matching[0]->type().dataOutputs();
49  // make sure it has the same signature as the method
50  if (!std::equal(dataInputs().begin(), dataInputs().end(), vec.begin(), vec.end())) {
51  return nullptr;
52  }
53  // make sure it has the same exec names and size
54  auto foundExecOutputs = matching[0]->type().execOutputs();
55  if (!std::equal(execInputs().begin(), execInputs().end(), foundExecOutputs.begin(),
56  foundExecOutputs.end())) {
57  return nullptr;
58  }
59  return matching[0];
60  }
61  return nullptr;
62 }
63 
64 Result GraphFunction::insertNode(std::unique_ptr<NodeType> type, float x, float y,
65  boost::uuids::uuid id, NodeInstance** toFill) {
66  // invalidate the cache
68 
69  Result res;
70 
71  // make sure the ID doesn't exist
72  if (nodes().find(id) != nodes().end()) {
73  res.addEntry("E47", "Cannot have two nodes with the same ID",
74  {{"Requested ID", boost::uuids::to_string(id)}});
75  return res;
76  }
77 
78  auto ptr = std::make_unique<NodeInstance>(this, std::move(type), x, y, id);
79 
80  auto emplaced = mNodes.emplace(id, std::move(ptr)).first;
81 
82  if (toFill != nullptr) { *toFill = emplaced->second.get(); }
83 
84  return res;
85 }
86 
87 std::vector<NodeInstance*> GraphFunction::nodesWithType(const boost::filesystem::path& module,
88  boost::string_view name) const noexcept {
89  std::vector<NodeInstance*> ret;
90  for (const auto& node : mNodes) {
91  if (node.second->type().module().fullName() == module &&
92  node.second->type().name() == name) {
93  ret.push_back(node.second.get());
94  }
95  }
96 
97  return ret;
98 }
99 
100 Result GraphFunction::insertNode(const boost::filesystem::path& moduleName,
101  boost::string_view typeName, const nlohmann::json& typeJSON,
102  float x, float y, boost::uuids::uuid id, NodeInstance** toFill) {
103  // invalidate the cache
105 
106  std::unique_ptr<NodeType> nodeType;
107  Result res = context().nodeTypeFromModule(moduleName, typeName, typeJSON, &nodeType);
108 
109  if (!res) { return res; }
110  res += insertNode(std::move(nodeType), x, y, id, toFill);
111  return res;
112 }
113 
115  // invalidate the cache
117 
118  Result res;
119 
120  // disconnect it's connections
121 
122  // disconnect input exec
123  for (const auto& execSlot : nodeToRemove.inputExecConnections) {
124  for (const auto& pair : execSlot) {
125  if (pair.first != nullptr) { res += disconnectExec(*pair.first, pair.second); }
126  }
127  }
128  // disconnect output exec
129  auto ID = 0ull;
130  for (const auto& pair : nodeToRemove.outputExecConnections) {
131  if (pair.first != nullptr) { res += disconnectExec(nodeToRemove, ID); }
132  ++ID;
133  }
134 
135  // disconnect input data
136  for (const auto& pair : nodeToRemove.inputDataConnections) {
137  if (pair.first != nullptr) {
138  res += disconnectData(*pair.first, pair.second, nodeToRemove);
139  }
140  }
141 
142  // disconnect output data
143  ID = 0ull;
144  for (const auto& dataSlot : nodeToRemove.outputDataConnections) {
145  for (const auto& pair : dataSlot) {
146  if (pair.first != nullptr) { disconnectData(nodeToRemove, ID, *pair.first); }
147  }
148  ++ID;
149  }
150  // then delete the node
151  nodes().erase(nodeToRemove.id());
152 
153  return res;
154 }
155 
156 Result GraphFunction::createEntryNodeType(std::unique_ptr<NodeType>* toFill) const {
157  Result res;
158 
159  nlohmann::json entry = nlohmann::json::object();
160 
161  auto& data = entry["data"];
162  data = nlohmann::json::array();
163  for (auto in : dataInputs()) { data.push_back({{in.name, in.type.qualifiedName()}}); }
164 
165  auto& exec = entry["exec"];
166  exec = nlohmann::json::array();
167  for (const auto& in : execInputs()) { exec.push_back(in); }
168 
169  res += context().nodeTypeFromModule("lang", "entry", entry, toFill);
170 
171  return res;
172 }
173 
174 Result GraphFunction::createExitNodeType(std::unique_ptr<NodeType>* toFill) const {
175  Result res;
176 
177  nlohmann::json exit = nlohmann::json::object();
178 
179  auto& data = exit["data"];
180  data = nlohmann::json::array();
181  for (auto out : dataOutputs()) { data.push_back({{out.name, out.type.qualifiedName()}}); }
182 
183  auto& exec = exit["exec"];
184  exec = nlohmann::json::array();
185  for (const auto& out : execOutputs()) { exec.push_back(out); }
186 
187  res += context().nodeTypeFromModule("lang", "exit", exit, toFill);
188 
189  return res;
190 }
191 
192 Result GraphFunction::getOrInsertEntryNode(float x, float y, boost::uuids::uuid id,
193  NodeInstance** toFill) {
194  Result res;
195 
196  if (auto ent = entryNode()) {
197  if (toFill != nullptr) { *toFill = ent; }
198  return res;
199  }
200 
201  // invalidate the cache
203 
204  std::unique_ptr<NodeType> entryNodeType;
205  res += createEntryNodeType(&entryNodeType);
206 
207  res += insertNode(std::move(entryNodeType), x, y, id, toFill);
208 
209  return res;
210 }
211 
212 void GraphFunction::addDataInput(const DataType& type, std::string name, size_t addBefore) {
213  // invalidate the cache
215 
216  if (addBefore < mDataInputs.size()) {
217  mDataInputs.emplace(mDataInputs.cbegin() + addBefore, std::move(name), type);
218  } else {
219  mDataInputs.emplace_back(std::move(name), type);
220  }
221  updateEntries();
222 }
223 
225  // invalidate the cache
227 
228  if (idx < mDataInputs.size()) { mDataInputs.erase(mDataInputs.begin() + idx); }
229  updateEntries();
230 }
231 
232 void GraphFunction::renameDataInput(size_t idx, std::string newName) {
233  // invalidate the cache
235 
236  if (idx < mDataInputs.size()) { mDataInputs[idx].name = std::move(newName); }
237  updateEntries();
238 }
239 
240 void GraphFunction::retypeDataInput(size_t idx, DataType newType) {
241  // invalidate the cache
243 
244  if (idx < mDataInputs.size()) { mDataInputs[idx].type = std::move(newType); }
245  updateEntries();
246 }
247 
248 void GraphFunction::addDataOutput(const DataType& type, std::string name, size_t addBefore) {
249  // invalidate the cache
251 
252  if (addBefore < mDataOutputs.size()) {
253  mDataOutputs.emplace(mDataOutputs.cbegin() + addBefore, std::move(name), type);
254  } else {
255  mDataOutputs.emplace_back(std::move(name), type);
256  }
257  updateExits();
258 }
259 
261  // invalidate the cache
263 
264  if (idx < mDataOutputs.size()) { mDataOutputs.erase(mDataOutputs.begin() + idx); }
265  updateExits();
266 }
267 
268 void GraphFunction::renameDataOutput(size_t idx, std::string newName) {
269  // invalidate the cache
271 
272  if (idx < mDataOutputs.size()) { mDataOutputs[idx].name = std::move(newName); }
273  updateExits();
274 }
275 
276 void GraphFunction::retypeDataOutput(size_t idx, DataType newType) {
277  if (idx < mDataOutputs.size()) { mDataOutputs[idx].type = std::move(newType); }
278  updateExits();
279 }
280 
281 llvm::FunctionType* GraphFunction::functionType() const {
282  std::vector<llvm::Type*> arguments;
283  arguments.reserve(1 + dataInputs().size() + dataOutputs().size());
284 
285  // this is for which exec input
286  arguments.push_back(llvm::IntegerType::getInt32Ty(context().llvmContext()));
287 
288  for (const auto& p : dataInputs()) { arguments.push_back(p.type.llvmType()); }
289 
290  // make these pointers
291  for (const auto& p : dataOutputs()) {
292  arguments.push_back(llvm::PointerType::get(p.type.llvmType(), 0));
293  }
294 
295  return llvm::FunctionType::get(llvm::IntegerType::getInt32Ty(context().llvmContext()),
296  arguments, false);
297 }
298 
299 void GraphFunction::addExecInput(std::string name, size_t addBefore) {
300  // invalidate the cache
302 
303  if (addBefore < mExecInputs.size()) {
304  // +1 because emplace adds before
305  mExecInputs.emplace(mExecInputs.cbegin() + addBefore, std::move(name));
306  } else {
307  mExecInputs.emplace_back(std::move(name));
308  }
309  updateEntries();
310 }
311 
313  // invalidate the cache
315 
316  if (idx < mExecInputs.size()) { mExecInputs.erase(mExecInputs.begin() + idx); }
317  updateEntries();
318 }
319 
320 void GraphFunction::renameExecInput(size_t idx, std::string name) {
321  // invalidate the cache
323 
324  if (idx < mExecInputs.size()) { mExecInputs[idx] = std::move(name); }
325  updateEntries();
326 }
327 
328 void GraphFunction::addExecOutput(std::string name, size_t addBefore) {
329  // invalidate the cache
331 
332  if (addBefore < mExecOutputs.size()) {
333  // +1 because emplace adds before
334  mExecOutputs.emplace(mExecOutputs.cbegin() + addBefore, std::move(name));
335  } else {
336  mExecOutputs.emplace_back(std::move(name));
337  }
338  updateExits();
339 }
340 
342  // invalidate the cache
344 
345  if (idx < mExecOutputs.size()) { mExecOutputs.erase(mExecOutputs.begin() + idx); }
346  updateExits();
347 }
348 
349 void GraphFunction::renameExecOutput(size_t idx, std::string name) {
350  // invalidate the cache
352 
353  if (idx < mExecOutputs.size()) { mExecOutputs[idx] = std::move(name); }
354  updateExits();
355 }
356 
357 void GraphFunction::updateEntries() {
358  auto matching = nodesWithType("lang", "entry");
359 
360  for (auto entry : matching) {
361  if (entry == nullptr) { return; }
362 
363  std::unique_ptr<NodeType> entryNodeType;
364  createEntryNodeType(&entryNodeType);
365 
366  entry->setType(std::move(entryNodeType));
367  }
368 }
369 
370 void GraphFunction::updateExits() {
371  for (const auto& exitNode : nodesWithType("lang", "exit")) {
372  std::unique_ptr<NodeType> exitNodeType;
373  createExitNodeType(&exitNodeType);
374 
375  exitNode->setType(std::move(exitNodeType));
376  }
377 }
378 
380  for (auto local : mLocalVariables) {
381  if (local.name == name) { return local; }
382  }
383 
384  return {};
385 }
386 
388  bool* inserted) {
389  auto local = localVariableFromName(name);
390 
391  if (local.valid()) {
392  if (inserted != nullptr) { *inserted = false; }
393  return local;
394  }
395 
396  // invalidate the cache
398 
399  mLocalVariables.emplace_back(name, type);
400  if (inserted != nullptr) { *inserted = true; }
401 
402  return mLocalVariables[mLocalVariables.size() - 1];
403 }
404 
405 bool GraphFunction::removeLocalVariable(boost::string_view name) {
406  auto iter = std::find_if(mLocalVariables.begin(), mLocalVariables.end(),
407  [&](auto& toTest) { return toTest.name == name; });
408 
409  bool erased = false;
410  if (iter != mLocalVariables.end()) {
411  mLocalVariables.erase(iter);
412  erased = true;
413  }
414  if (!erased) { return false; }
415 
416  // invalidate the cache
418 
419  // remove set and get nodes
420  auto setNodes = nodesWithType(module().fullName(), "_set_" + name.to_string());
421  for (const auto& node : setNodes) { removeNode(*node); }
422  auto getNodes = nodesWithType(module().fullName(), "_get_" + name.to_string());
423  for (const auto& node : getNodes) { removeNode(*node); }
424 
425  return true;
426 }
427 
428 void GraphFunction::renameLocalVariable(std::string oldName, std::string newName) {
429  // invalidate the cache
431 
432  bool setanything = false;
433  for (auto& var : mLocalVariables) {
434  if (var.name == oldName) {
435  var.name = newName;
436 
437  setanything = true;
438  break;
439  }
440  }
441 
442  // if it wans't found, then we don't have to update
443  if (!setanything) { return; }
444 
445  // update existing nodes
446  auto setNodes = nodesWithType(module().fullName(), "_set_" + oldName);
447  for (const auto& node : setNodes) {
448  // create a new node type
449  std::unique_ptr<NodeType> ty;
450 
451  auto res = module().nodeTypeFromName("_set_" + newName, node->type().toJSON(), &ty);
452  assert(!!res);
453 
454  node->setType(std::move(ty));
455  }
456 
457  auto getNodes = nodesWithType(module().fullName(), "_get_" + oldName);
458  for (const auto& node : getNodes) {
459  // create a new node type
460  std::unique_ptr<NodeType> ty;
461 
462  auto res = module().nodeTypeFromName("_get_" + newName, node->type().toJSON(), &ty);
463  assert(!!res);
464 
465  node->setType(std::move(ty));
466  }
467 }
468 
469 void GraphFunction::retypeLocalVariable(boost::string_view name, DataType newType) {
470  // invalidate the cache
472 
473  std::string qualifiedName = newType.qualifiedName();
474 
475  for (auto& var : mLocalVariables) {
476  if (var.name == name) {
477  var.type = std::move(newType);
478  break;
479  }
480  }
481 
482  // update existing nodes
483  auto setNodes = nodesWithType(module().fullName(), "_set_" + name.to_string());
484  for (const auto& node : setNodes) {
485  // create a new node type
486  std::unique_ptr<NodeType> ty;
487 
488  auto res = module().nodeTypeFromName("_set_" + name.to_string(), qualifiedName, &ty);
489  assert(!!res);
490 
491  node->setType(std::move(ty));
492  }
493 
494  auto getNodes = nodesWithType(module().fullName(), "_get_" + name.to_string());
495  for (const auto& node : getNodes) {
496  // create a new node type
497  std::unique_ptr<NodeType> ty;
498 
499  auto res = module().nodeTypeFromName("_get_" + name.to_string(), qualifiedName, &ty);
500  assert(!!res);
501 
502  node->setType(std::move(ty));
503  }
504 }
505 
506 std::vector<NodeInstance*> GraphFunction::setName(boost::string_view newName,
507  bool updateReferences) {
508  // invalidate the cache
510 
511  auto oldName = mName;
512  mName = newName.to_string();
513 
514  if (updateReferences) {
515  auto toUpdate = context().findInstancesOfType(module().fullName(), oldName);
516 
517  for (auto node : toUpdate) {
518  std::unique_ptr<NodeType> ty;
519  auto res = context().nodeTypeFromModule(module().fullName(), name(), {}, &ty);
520  if (!res) { return {}; }
521 
522  node->setType(std::move(ty));
523  }
524 
525  return toUpdate;
526  }
527  return {};
528 }
529 
530 std::string GraphFunction::qualifiedName() const { return module().fullName() + ":" + name(); }
531 
532 } // namespace chi
void addDataOutput(const DataType &type, std::string name, size_t addBefore=(std::numeric_limits< size_t >::max)())
Add an data output to the end of the argument list.
GraphFunction(GraphModule &mod, std::string name, std::vector< NamedDataType > dataIns, std::vector< NamedDataType > dataOuts, std::vector< std::string > execIns, std::vector< std::string > execOuts)
Construct a graph–don&#39;t call this directly use GraphModule::getorCreateFunction. ...
void addDataInput(const DataType &type, std::string name, size_t addBefore=(std::numeric_limits< size_t >::max)())
Add an input to the end of the argument list.
Result getOrInsertEntryNode(float x, float y, boost::uuids::uuid id=boost::uuids::random_generator()(), NodeInstance **toFill=nullptr)
Creates an entry node if it doesn&#39;t already exist, else just return it.
std::vector< std::vector< std::pair< NodeInstance *, size_t > > > inputExecConnections
The connections that lead to this node, exec.
const std::vector< NamedDataType > & dataOutputs() const
Get the function data outputs in the format {type, docstring}.
Defines functions for validating GraphFunction objects.
void addEntry(const char *ec, const char *overview, nlohmann::json data)
Add a entry to the result, either a warning or an error.
Definition: Result.cpp:52
Result nodeTypeFromModule(const boost::filesystem::path &moduleName, boost::string_view typeName, const nlohmann::json &data, std::unique_ptr< NodeType > *toFill) noexcept
Gets a NodeType from the JSON and name.
Definition: Context.cpp:253
Result removeNode(NodeInstance &nodeToRemove)
Remove a node from the function.
Result createEntryNodeType(std::unique_ptr< NodeType > *toFill) const
Create a fresh NodeType for an entry.
void retypeDataInput(size_t idx, DataType newType)
Change the type of a data input This also updates the entry node and disconnects invalid connections...
const std::vector< std::string > & execInputs() const
Get the function exec inputs.
Result insertNode(std::unique_ptr< NodeType > type, float x, float y, boost::uuids::uuid id=boost::uuids::random_generator()(), NodeInstance **toFill=nullptr)
Add a node to the graph.
NamedDataType localVariableFromName(boost::string_view name) const
Get a local varaible by name.
const std::vector< std::string > & execOutputs() const
Get the function exec outputs.
Defines the Result class and related functions.
void removeDataInput(size_t idx)
Remove an input from the argument list Also removes invalid connections If idx is out of range...
std::vector< std::vector< std::pair< NodeInstance *, size_t > > > outputDataConnections
The connections that lead out of this node, data.
Context & context() const
Get the context.
void retypeLocalVariable(boost::string_view name, DataType newType)
Set a new type to a local variable.
std::string qualifiedName() const
Get the qualified name of the function Same as module().fullName() + ":" + name();.
An instance of a node.
std::vector< std::pair< NodeInstance *, size_t > > inputDataConnections
The connections that go into this node, data.
std::vector< NodeInstance * > findInstancesOfType(const boost::filesystem::path &moduleName, boost::string_view typeName) const
Find all uses of a node type in all the loaded modules.
Definition: Context.cpp:444
void removeExecOutput(size_t idx)
Remove an exec output from the argument list If idx is out of range, this function does nothing...
Definitions for mangling functions.
GraphModule & module() const
Get the GraphModule that contains this GraphFunction.
void removeDataOutput(size_t idx)
Remove an data output from the argument list Also removes invalid connections If idx is out of range...
Result createExitNodeType(std::unique_ptr< NodeType > *toFill) const
Create a fresh NodeType for an exit.
std::string name() const
Get the name of the function.
Basicaly a std::pair<std::string, DataType>, except it has nicer names.
Definition: DataType.hpp:72
std::string fullName() const
Get the full name of the module.
Definition: ChiModule.hpp:61
void removeExecInput(size_t idx)
Remove an exec input from the argument list If idx is out of range, this function does nothing...
void renameExecInput(size_t idx, std::string name)
Change the name for an exec input If idx is out of range, this function does nothing.
NodeInstance * nodeByID(const boost::uuids::uuid &id) const
Get a node with a given ID.
std::vector< NodeInstance * > setName(boost::string_view newName, bool updateReferences=true)
Set the name of the function.
NodeInstance * entryNode() const noexcept
Gets the node with type lang:entry returns nullptr on failure Also returns nullptr if there are two e...
Result disconnectData(NodeInstance &lhs, size_t lhsConnID, NodeInstance &rhs)
Disconnect a data connection.
void renameExecOutput(size_t idx, std::string name)
Rename an exec output If idx is out of range, this function does nothing.
std::unordered_map< boost::uuids::uuid, std::unique_ptr< NodeInstance > > & nodes()
Get the nodes in the function Usually called by connectData or connectExec or GraphFunction.
void retypeDataOutput(size_t idx, DataType newType)
Change the type of a data output This also updates all exit nodes and disconnects invalid connections...
void renameLocalVariable(std::string oldName, std::string newName)
Rename a local variable.
Defines the NodeType class.
std::string qualifiedName() const
Get the qualified name of the type (module().fullName() + ":" name())
Definition: DataType.cpp:8
bool removeLocalVariable(boost::string_view name)
Remove a local variable from the function by name.
Defines the DataType class.
void updateLastEditTime(std::time_t newLastEditTime=std::time(nullptr))
Update the last edit time, signifying that it&#39;s been edited.
Definition: ChiModule.hpp:105
std::vector< NodeInstance * > nodesWithType(const boost::filesystem::path &module, boost::string_view name) const noexcept
Gets the nodes with a given type.
Result nodeTypeFromName(boost::string_view name, const nlohmann::json &jsonData, std::unique_ptr< NodeType > *toFill) override
Create a node type that is in the module from the name and json.
std::vector< std::pair< NodeInstance *, size_t > > outputExecConnections
The connections that go out of this node, exec.
Module that holds graph functions.
Definition: GraphModule.hpp:16
Result disconnectExec(NodeInstance &lhs, size_t lhsConnID)
Disconnect a exec connection.
Defines the GraphModule class.
void renameDataOutput(size_t idx, std::string newName)
Modify an data output (change it&#39;s type and docstring)
The namespace where chigraph lives.
void addExecOutput(std::string name, size_t addBefore=(std::numeric_limits< size_t >::max)())
Add an exec output to the end of the argument list.
void addExecInput(std::string name, size_t addBefore=(std::numeric_limits< size_t >::max)())
Add an exec input to the end of the argument list.
Defines the Context class and related functions.
A type of data Loose wrapper around llvm::Type*, except it knows which ChiModule it&#39;s in and it embed...
Definition: DataType.hpp:17
const std::vector< NamedDataType > & dataInputs() const
Get the function data inputs in the format {type, docstring}.
llvm::FunctionType * functionType() const
Get the LLVM function type for the function.
boost::uuids::uuid id() const
Get the ID of the instance, unique to the graph.
The result object, used for identifiying errors with good diagnostics.
Definition: Result.hpp:72
Declares the GraphFunction class.
NamedDataType getOrCreateLocalVariable(std::string name, DataType type, bool *inserted=nullptr)
Create a new local varaible in the module.
Defines the NodeInstance class and related functions.
void renameDataInput(size_t idx, std::string newName)
Rename a data input This also updates the entry node If idx is out of range, this function does nothi...