chigraph  master
Systems programming language written for beginners in LLVM
GraphModule.cpp
Go to the documentation of this file.
1 
3 #include "chi/GraphModule.hpp"
4 #include "chi/CCompiler.hpp"
5 #include "chi/Context.hpp"
7 #include "chi/GraphFunction.hpp"
8 #include "chi/GraphStruct.hpp"
10 #include "chi/JsonSerializer.hpp"
11 #include "chi/LLVMVersion.hpp"
12 #include "chi/NameMangler.hpp"
13 #include "chi/NodeInstance.hpp"
14 #include "chi/NodeType.hpp"
16 #include "chi/Support/Result.hpp"
18 
19 #include <llvm/IR/DIBuilder.h>
20 #include <llvm/IR/IRBuilder.h>
21 #include <llvm/IR/Module.h>
22 #include <llvm/Linker/Linker.h>
23 #include <llvm/Support/Compiler.h>
24 #include <llvm/Support/FileSystem.h>
25 #include <llvm/Support/MemoryBuffer.h>
26 #include <llvm/Transforms/Utils/Cloning.h>
27 
28 #include <boost/filesystem.hpp>
29 #include <boost/range.hpp>
30 
31 #include <boost/uuid/uuid_io.hpp>
32 
33 namespace fs = boost::filesystem;
34 
35 namespace chi {
36 
37 namespace {
39 struct CFuncNode : NodeType {
40  CFuncNode(GraphModule& mod, std::string cCode, std::string functionName,
41  std::vector<std::string> extraArgs, std::vector<NamedDataType> inputs,
42  DataType output)
43  : NodeType{mod, "c-call", "call C code"},
44  mFunctionName{std::move(functionName)},
45  mCCode(std::move(cCode)),
46  mExtraArguments{std::move(extraArgs)},
47  mInputs{std::move(inputs)},
48  mOutput{std::move(output)},
49  mGraphModule{&mod} {
50  setExecInputs({""});
51  setExecOutputs({""});
52 
53  setDataInputs(mInputs);
54  if (mOutput.valid()) { setDataOutputs({{"", mOutput}}); }
55  }
56 
57  Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t /*execInputID*/,
58  const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
59  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
60  assert(io.size() == dataInputs().size() + dataOutputs().size() && outputBlocks.size() == 1);
61 
62  Result res;
63 
64  // compile the c code if it hasn't already been compiled
65  if (llcompiledmod == nullptr) {
66  auto args = mExtraArguments;
67 
68  // add -I for the .c dir
69  args.push_back("-I");
70  args.push_back(mGraphModule->pathToCSources().string());
71 
72  res += compileCToLLVM(
73  fs::path(llvm::sys::fs::getMainExecutable(nullptr, nullptr)).parent_path() /
74  "chi-ctollvm"
75 #ifdef WIN32
76  ".exe"
77 #endif
78  ,
79  context().llvmContext(), args, mCCode, &llcompiledmod);
80 
81  if (!res) { return res; }
82  }
83 
84  // create a copy of the module
85  auto copymod = llvm::CloneModule(llcompiledmod.get());
86 
87  // link it in
88 
89  auto parentModule = &compiler.llvmModule();
90 
91 #if LLVM_VERSION_LESS_EQUAL(3, 7)
92  llvm::Linker::LinkModules(parentModule, copymod
93 #if LLVM_VERSION_LESS_EQUAL(3, 5)
94  ,
95  llvm::Linker::DestroySource, nullptr
96 #endif
97  );
98 #else
99  llvm::Linker::linkModules(*parentModule, std::move(copymod));
100 #endif
101 
102  parentModule->setDataLayout("");
103 
104  auto llfunc = parentModule->getFunction(mFunctionName);
105  assert(llfunc != nullptr);
106 
107  llvm::IRBuilder<> builder(&codegenInto);
108 
109  size_t ioSize = io.size();
110 
111  std::string outputName;
112 
113  // remove the return type if there is one
114  if (!dataOutputs().empty()) {
115  --ioSize;
116  outputName = dataOutputs()[0].name;
117  }
118 
119  auto callinst = builder.CreateCall(llfunc, {io.data(), ioSize}, outputName);
120  callinst->setDebugLoc(nodeLocation);
121 
122  // store theoutput if there are any
123  if (!dataOutputs().empty()) {
124  auto stoInst = builder.CreateStore(callinst, io[dataInputs().size()]);
125  stoInst->setDebugLoc(nodeLocation);
126  }
127 
128  auto brInst = builder.CreateBr(outputBlocks[0]);
129  brInst->setDebugLoc(nodeLocation);
130 
131  return res;
132  }
133 
134  nlohmann::json toJSON() const override {
135  auto j = nlohmann::json{};
136 
137  j = nlohmann::json::object();
138  j["code"] = mCCode;
139  j["function"] = mFunctionName;
140 
141  auto& jsonExtraFlags = j["extraflags"];
142  jsonExtraFlags = nlohmann::json::array();
143  for (const auto& flag : mExtraArguments) { jsonExtraFlags.push_back(flag); }
144 
145  auto& jsonInputs = j["inputs"];
146  jsonInputs = nlohmann::json::array();
147  for (const auto& in : mInputs) {
148  jsonInputs.push_back({{in.name, in.type.qualifiedName()}});
149  }
150 
151  if (mOutput.valid()) {
152  j["output"] = mOutput.qualifiedName();
153  } else {
154  j["output"] = nlohmann::json();
155  }
156 
157  return j;
158  }
159 
160  std::unique_ptr<NodeType> clone() const override {
161  Result res;
162 
163  return std::make_unique<CFuncNode>(*mGraphModule, mCCode, mFunctionName, mExtraArguments,
164  mInputs, mOutput);
165  }
166 
167  std::string mFunctionName;
168  std::string mCCode;
169  std::vector<std::string> mExtraArguments;
170  std::vector<NamedDataType> mInputs;
171  DataType mOutput;
172  GraphModule* mGraphModule;
173 
174  std::unique_ptr<llvm::Module> llcompiledmod;
175 };
176 
177 struct GraphFuncCallType : public NodeType {
178  GraphFuncCallType(GraphModule& json_module, std::string funcname, Result* resPtr)
179  : NodeType(json_module, std::move(funcname), ""), JModule(&json_module) {
180  Result& res = *resPtr;
181 
182  auto* mygraph = JModule->functionFromName(name());
183  setDescription(mygraph->description());
184 
185  if (mygraph == nullptr) {
186  res.addEntry("EINT", "Graph doesn't exist in module",
187  {{"Module Name", JModule->fullName()}, {"Requested Name", name()}});
188  return;
189  }
190 
191  setDataOutputs(mygraph->dataOutputs());
192 
193  setDataInputs(mygraph->dataInputs());
194 
195  setExecInputs(mygraph->execInputs());
196  setExecOutputs(mygraph->execOutputs());
197  }
198 
199  Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t execInputID,
200  const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
201  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
202  Result res = {};
203 
204  llvm::IRBuilder<> builder(&codegenInto);
205  builder.SetCurrentDebugLocation(nodeLocation);
206 
207  auto func = compiler.funcCompiler().llvmModule().getFunction(
208  mangleFunctionName(module().fullName(), name()));
209 
210  if (func == nullptr) {
211  res.addEntry("EINT", "Could not find function in llvm module",
212  {{"Requested Function", name()}});
213  return res;
214  }
215 
216  // add the execInputID to the argument list
217  std::vector<llvm::Value*> passingIO;
218  passingIO.push_back(builder.getInt32(execInputID));
219 
220  std::copy(io.begin(), io.end(), std::back_inserter(passingIO));
221 
222  auto ret = builder.CreateCall(func, passingIO, "call_function");
223 
224  // create switch on return
225  auto switchInst = builder.CreateSwitch(ret, outputBlocks[0]); // TODO: better default
226 
227  auto id = 0ull;
228  for (auto out : outputBlocks) {
229  switchInst->addCase(builder.getInt32(id), out);
230  ++id;
231  }
232 
233  return res;
234  }
235 
236  nlohmann::json toJSON() const override { return {}; }
237  std::unique_ptr<NodeType> clone() const override {
238  Result res = {}; // there shouldn't be an error but check anyways
239  return std::make_unique<GraphFuncCallType>(*JModule, name(), &res);
240  }
241 
242  GraphModule* JModule;
243 };
244 
245 struct MakeStructNodeType : public NodeType {
246  MakeStructNodeType(GraphStruct& ty) : NodeType(ty.module()), mStruct{&ty} {
247  setName("_make_" + ty.name());
248  setDescription("Make a " + ty.name() + " structure");
249  makePure();
250 
251  // set inputs
252  setDataInputs(ty.types());
253 
254  // set output to just be the struct
255  setDataOutputs({{"", ty.dataType()}});
256  }
257 
258  Result codegen(NodeCompiler& /*compiler*/, llvm::BasicBlock& codegenInto,
259  size_t /*execInputID*/, const llvm::DebugLoc& nodeLocation,
260  const std::vector<llvm::Value*>& io,
261  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
262  llvm::IRBuilder<> builder{&codegenInto};
263  builder.SetCurrentDebugLocation(nodeLocation);
264 
265  llvm::Value* out = io[io.size() - 1]; // output goes last
266  for (auto id = 0ull; id < io.size() - 1; ++id) {
267  auto ptr = builder.CreateStructGEP(
268 #if LLVM_VERSION_AT_LEAST(3, 7)
269  mStruct->dataType().llvmType(),
270 #endif
271  out, id);
272  builder.CreateStore(io[id], ptr);
273  }
274 
275  builder.CreateBr(outputBlocks[0]);
276 
277  return {};
278  }
279 
280  nlohmann::json toJSON() const override { return {}; }
281  std::unique_ptr<NodeType> clone() const override {
282  return std::make_unique<MakeStructNodeType>(*mStruct);
283  }
284 
285  GraphStruct* mStruct;
286 };
287 
288 struct BreakStructNodeType : public NodeType {
289  BreakStructNodeType(GraphStruct& ty) : NodeType(ty.module()), mStruct{&ty} {
290  setName("_break_" + ty.name());
291  setDescription("Break a " + ty.name() + " structure");
292  makePure();
293 
294  // set input to just be the struct
295  setDataInputs({{"", ty.dataType()}});
296 
297  // set outputs
298  setDataOutputs(ty.types());
299  }
300 
301  Result codegen(NodeCompiler& /*compiler*/, llvm::BasicBlock& codegenInto,
302  size_t /*execInputID*/, const llvm::DebugLoc& nodeLocation,
303  const std::vector<llvm::Value*>& io,
304  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
305  llvm::IRBuilder<> builder{&codegenInto};
306  builder.SetCurrentDebugLocation(nodeLocation);
307 
308  // create temp struct
309  auto tempStruct = builder.CreateAlloca(mStruct->dataType().llvmType());
310  builder.CreateStore(io[0], tempStruct);
311 
312  for (auto id = 1ull; id < io.size(); ++id) {
313  auto ptr = builder.CreateStructGEP(
314 #if LLVM_VERSION_AT_LEAST(3, 7)
315  nullptr,
316 #endif
317  tempStruct, id - 1);
318  std::string s = stringifyLLVMType(ptr->getType());
319 
320  auto val = builder.CreateLoad(ptr);
321  builder.CreateStore(val, io[id]);
322  }
323 
324  builder.CreateBr(outputBlocks[0]);
325 
326  return {};
327  }
328 
329  nlohmann::json toJSON() const override { return {}; }
330  std::unique_ptr<NodeType> clone() const override {
331  return std::make_unique<BreakStructNodeType>(*mStruct);
332  }
333 
334  GraphStruct* mStruct;
335 };
336 
337 struct SetLocalNodeType : public NodeType {
338  SetLocalNodeType(ChiModule& mod, NamedDataType ty) : NodeType(mod), mDataType{std::move(ty)} {
339  setName("_set_" + mDataType.name);
340  setDescription("Set " + mDataType.name);
341 
342  setDataInputs({{"", mDataType.type}});
343 
344  setExecInputs({""});
345  setExecOutputs({""});
346  }
347 
348  Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t /*execInputID*/,
349  const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
350  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
351  llvm::IRBuilder<> builder{&codegenInto};
352  builder.SetCurrentDebugLocation(nodeLocation);
353 
354  auto value = compiler.funcCompiler().localVariable(mDataType.name);
355  assert(value != nullptr);
356 
357  // set the value!
358  builder.CreateStore(io[0], value);
359 
360  builder.CreateBr(outputBlocks[0]);
361 
362  return {};
363  }
364 
365  nlohmann::json toJSON() const override { return mDataType.type.qualifiedName(); }
366  std::unique_ptr<NodeType> clone() const override {
367  return std::make_unique<SetLocalNodeType>(module(), mDataType);
368  }
369 
370  NamedDataType mDataType;
371 };
372 
373 struct GetLocalNodeType : public NodeType {
374  GetLocalNodeType(ChiModule& mod, NamedDataType ty) : NodeType(mod), mDataType{std::move(ty)} {
375  setName("_get_" + mDataType.name);
376  setDescription("Get " + mDataType.name);
377 
378  setDataOutputs({{"", mDataType.type}});
379 
380  makePure();
381  }
382 
383  Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t execInputID,
384  const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
385  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
386  llvm::IRBuilder<> builder{&codegenInto};
387  builder.SetCurrentDebugLocation(nodeLocation);
388 
389  auto value = compiler.funcCompiler().localVariable(mDataType.name);
390  assert(value != nullptr);
391 
392  builder.CreateStore(builder.CreateLoad(value), io[0]);
393 
394  builder.CreateBr(outputBlocks[0]);
395 
396  return {};
397  }
398 
399  nlohmann::json toJSON() const override { return mDataType.type.qualifiedName(); }
400  std::unique_ptr<NodeType> clone() const override {
401  return std::make_unique<GetLocalNodeType>(module(), mDataType);
402  }
403 
404  NamedDataType mDataType;
405 };
406 
407 } // anon namespace
408 
409 GraphModule::GraphModule(Context& cont, boost::filesystem::path fullName,
410  const std::vector<boost::filesystem::path>& dependencies)
411  : ChiModule(cont, fullName) {
412  // load the dependencies from the context
413  for (const auto& dep : dependencies) { addDependency(dep); }
414 }
415 
416 std::vector<std::string> GraphModule::typeNames() const {
417  std::vector<std::string> ret;
418  ret.reserve(structs().size());
419 
420  for (const auto& ty : structs()) { ret.push_back(ty->name()); }
421 
422  return ret;
423 }
424 
425 Result GraphModule::addForwardDeclarations(llvm::Module& module) const {
426  // create prototypes
427  for (auto& graph : mFunctions) {
428  module.getOrInsertFunction(mangleFunctionName(fullName(), graph->name()),
429  graph->functionType());
430  }
431 
432  return {};
433 }
434 
435 Result GraphModule::generateModule(llvm::Module& module) {
436  Result res = {};
437 
438  // if C support was enabled, compile the C files
439  if (cEnabled()) {
440  fs::path cPath = pathToCSources();
441  if (fs::is_directory(cPath)) {
442  // compile the files
443  for (auto direntry : boost::make_iterator_range(
444  fs::recursive_directory_iterator{cPath, fs::symlink_option::recurse}, {})) {
445  const fs::path& CFile = direntry;
446 
447  if (!fs::is_regular_file(CFile) ||
448  !(CFile.extension() == ".c" || CFile.extension() == ".C" ||
449  CFile.extension() == ".cpp" || CFile.extension() == ".cxx" ||
450  CFile.extension() == ".c++" || CFile.extension() == ".cc")) {
451  continue;
452  }
453 
454  // compile it
455  std::unique_ptr<llvm::Module> generatedModule;
456  res += compileCToLLVM(
457  fs::path(llvm::sys::fs::getMainExecutable(nullptr, nullptr)).parent_path() /
458  "chi-ctollvm"
459 #ifdef WIN32
460  ".exe"
461 #endif
462  ,
463  context().llvmContext(), {CFile.string()}, "", &generatedModule);
464 
465  if (!res) { return res; }
466 
467 // link it
468 
469 #if LLVM_VERSION_LESS_EQUAL(3, 7)
470  llvm::Linker::LinkModules(&module, generatedModule.get()
471 #if LLVM_VERSION_LESS_EQUAL(3, 5)
472  ,
473  llvm::Linker::DestroySource, nullptr
474 #endif
475  );
476 #else
477  llvm::Linker::linkModules(module, std::move(generatedModule));
478 #endif
479  }
480  }
481  }
482 
483  // debug info
484  llvm::DIBuilder debugBuilder(module);
485 
486  auto compileUnit = debugBuilder.createCompileUnit(llvm::dwarf::DW_LANG_C,
487 #if LLVM_VERSION_LESS_EQUAL(3, 9)
488  sourceFilePath().filename().string(),
489  sourceFilePath().parent_path().string(),
490 #else
491  debugBuilder.createFile(
492  sourceFilePath().filename().string(),
493  sourceFilePath().parent_path().string()),
494 #endif
495  "Chigraph Compiler", false, "", 0);
496 
497  // create prototypes
498  addForwardDeclarations(module);
499 
500  for (auto& graph : mFunctions) {
501  res += compileFunction(*graph, &module,
502 #if LLVM_VERSION_LESS_EQUAL(3, 6)
503  &
504 #endif
505  compileUnit,
506  debugBuilder);
507  }
508 
509  debugBuilder.finalize();
510 
511  return res;
512 }
513 
515  Result res;
516 
517  // can't serialize without a workspace...
518  if (!context().hasWorkspace()) {
519  res.addEntry("EUKN", "Cannot serialize without a worksapce", {});
520  return res;
521  }
522 
523  auto modulePath = sourceFilePath();
524 
525  try {
526  // create directories that conatain the path
527  fs::create_directories(modulePath.parent_path());
528 
529  } catch (std::exception& e) {
530  res.addEntry("EUKN", "Failed to create directoires in workspace",
531  {{"Module File", modulePath.string()}});
532  return res;
533  }
534 
535  // serialize
536  nlohmann::json toFill = graphModuleToJson(*this);
537 
538  // save
539  fs::ofstream ostr(modulePath);
540  ostr << toFill.dump(2);
541 
542  return res;
543 }
544 
546  std::vector<NamedDataType> dataIns,
547  std::vector<NamedDataType> dataOuts,
548  std::vector<std::string> execIns,
549  std::vector<std::string> execOuts, bool* inserted) {
550  // make sure there already isn't one by this name
551  auto foundFunc = functionFromName(name);
552  if (foundFunc != nullptr) {
553  if (inserted != nullptr) { *inserted = false; }
554  return foundFunc;
555  }
556 
557  // invalidate the cache
559 
560  mFunctions.push_back(std::make_unique<GraphFunction>(*this, std::move(name), std::move(dataIns),
561  std::move(dataOuts), std::move(execIns),
562  std::move(execOuts)));
563 
564  if (inserted != nullptr) { *inserted = true; }
565  return mFunctions[mFunctions.size() - 1].get();
566 }
567 
568 bool GraphModule::removeFunction(boost::string_view name, bool deleteReferences) {
569  // invalidate the cache
571 
572  auto funcPtr = functionFromName(name);
573 
574  if (funcPtr == nullptr) { return false; }
575 
576  removeFunction(*funcPtr, deleteReferences);
577 
578  return true;
579 }
580 
581 void GraphModule::removeFunction(GraphFunction& func, bool deleteReferences) {
582  // invalidate the cache
584 
585  if (deleteReferences) {
586  auto references = context().findInstancesOfType(fullName(), func.name());
587 
588  for (auto node : references) { node->function().removeNode(*node); }
589  }
590 
591  auto iter = std::find_if(mFunctions.begin(), mFunctions.end(),
592  [&func](auto& uPtr) { return uPtr.get() == &func; });
593  if (iter == mFunctions.end()) { return; }
594 
595  mFunctions.erase(iter);
596 }
597 
598 GraphFunction* GraphModule::functionFromName(boost::string_view name) const {
599  auto iter = std::find_if(mFunctions.begin(), mFunctions.end(),
600  [&](auto& ptr) { return ptr->name() == name; });
601 
602  if (iter != mFunctions.end()) { return iter->get(); }
603  return nullptr;
604 }
605 
606 Result GraphModule::nodeTypeFromName(boost::string_view name, const nlohmann::json& jsonData,
607  std::unique_ptr<NodeType>* toFill) {
608  Result res = {};
609 
610  // see if it's a C call
611  if (cEnabled() && name == "c-call") {
612  if (!jsonData.is_object()) {
613  res.addEntry("WUKN", "Data for c-call must be an object", {{"Given Data", jsonData}});
614  }
615 
616  std::string code;
617  if (jsonData.is_object() && jsonData.find("code") != jsonData.end() &&
618  jsonData["code"].is_string()) {
619  code = jsonData["code"];
620  } else {
621  res.addEntry(
622  "WUKN",
623  "Data for c-call must have a pair with the key of code and that the data is a "
624  "string",
625  {{"Given Data", jsonData}});
626  }
627 
628  std::string function;
629  if (jsonData.is_object() && jsonData.find("function") != jsonData.end() &&
630  jsonData["function"].is_string()) {
631  function = jsonData["function"];
632  } else {
633  res.addEntry(
634  "WUKN",
635  "Data for c-call must have a pair with the key of function and that the data is a "
636  "string",
637  {{"Given Data", jsonData}});
638  }
639 
640  std::vector<std::string> extraFlags;
641  if (jsonData.is_object() && jsonData.find("extraflags") != jsonData.end() &&
642  jsonData["extraflags"].is_array()) {
643  for (const auto& flag : jsonData["extraflags"]) {
644  std::string str = flag;
645  extraFlags.emplace_back(std::move(str));
646  }
647  } else {
648  res.addEntry("WUKN", "Data for c-call must have an extraflags array",
649  {{"Given Data", jsonData}});
650  }
651 
652  std::vector<NamedDataType> inputs;
653  if (jsonData.is_object() && jsonData.find("inputs") != jsonData.end() &&
654  jsonData["inputs"].is_array()) {
655  inputs.reserve(jsonData["inputs"].size());
656  for (const auto& jsonArg : jsonData["inputs"]) {
657  std::string docstring, qualifiedName;
658  std::tie(docstring, qualifiedName) = parseObjectPair(jsonArg);
659 
660  std::string moduleName, typeName;
661  std::tie(moduleName, typeName) = parseColonPair(qualifiedName);
662 
663  DataType ty;
664  res += context().typeFromModule(moduleName, typeName, &ty);
665 
666  inputs.emplace_back(docstring, ty);
667  }
668 
669  } else {
670  res.addEntry("WUKN", "Data for c-call must have an inputs array",
671  {{"Given Data", jsonData}});
672  }
673 
674  DataType output;
675  if (jsonData.is_object() && jsonData.find("output") != jsonData.end()) {
676  auto& outputJson = jsonData["output"];
677 
678  // if it's a string, there's a return type
679  if (outputJson.is_string()) {
680  std::string moduleName, typeName;
681  std::tie(moduleName, typeName) = parseColonPair(outputJson);
682 
683  res += context().typeFromModule(moduleName, typeName, &output);
684  }
685  // now if it isn't null, then there was an error
686  else if (!outputJson.is_null()) {
687  res.addEntry("WUKN",
688  R"("output" element in c-call must be either null or a string)",
689  {{"Given Data", jsonData}});
690  }
691  } else {
692  res.addEntry(
693  "WUKN",
694  "Data for c-call must have an output element that is either null or a string",
695  {{"Given Data", jsonData}});
696  }
697 
698  *toFill = std::make_unique<CFuncNode>(*this, code, function, extraFlags, inputs, output);
699  return res;
700 
701  } else if (name == "c-call") {
702  res.addEntry("EUKN", "Tried to use a c-call node without c support enabled in the module",
703  {});
704  return res;
705  }
706 
707  auto graph = functionFromName(name);
708 
709  if (graph == nullptr) {
710  // if it wasn't found, then see if it's a struct breaker or maker
711  std::string nameStr{name.to_string()};
712  if (nameStr.substr(0, 6) == "_make_") {
713  auto str = structFromName(nameStr.substr(6));
714  if (str != nullptr) {
715  *toFill = std::make_unique<MakeStructNodeType>(*str);
716  return res;
717  }
718  }
719  if (nameStr.substr(0, 7) == "_break_") {
720  auto str = structFromName(nameStr.substr(7));
721  if (str != nullptr) {
722  *toFill = std::make_unique<BreakStructNodeType>(*str);
723  return res;
724  }
725  }
726  if (nameStr.substr(0, 5) == "_get_") {
727  if (jsonData.is_string()) {
728  std::string module, typeName;
729 
730  std::tie(module, typeName) = parseColonPair(jsonData);
731 
732  DataType ty;
733  res += context().typeFromModule(module, typeName, &ty);
734 
735  *toFill =
736  std::make_unique<GetLocalNodeType>(*this, NamedDataType{nameStr.substr(5), ty});
737  } else {
738  res.addEntry("EUKN", "Json data for _get_ node type isn't a string",
739  {{"Given Data", jsonData}});
740  }
741  return res;
742  }
743  if (nameStr.substr(0, 5) == "_set_") {
744  if (jsonData.is_string()) {
745  std::string module, typeName;
746 
747  std::tie(module, typeName) = parseColonPair(jsonData);
748 
749  DataType ty;
750  res += context().typeFromModule(module, typeName, &ty);
751 
752  *toFill = std::make_unique<SetLocalNodeType>(
753  *this, NamedDataType{nameStr.substr(5), std::move(ty)});
754  } else {
755  res.addEntry("EUKN", "Json data for _set_ node type isn't a string",
756  {{"Given Data", jsonData}});
757  }
758  return res;
759  }
760 
761  // if we get here than it's for sure not a thing
762  res.addEntry("EUKN", "Graph not found in module",
763  {{"Module Name", fullName()}, {"Requested Graph", name.to_string()}});
764  return res;
765  }
766 
767  *toFill = std::make_unique<GraphFuncCallType>(*this, name.to_string(), &res);
768  return res;
769 }
770 
771 DataType GraphModule::typeFromName(boost::string_view name) {
772  auto func = structFromName(name);
773 
774  if (func == nullptr) { return {}; }
775 
776  return func->dataType();
777 }
778 
779 std::vector<std::string> GraphModule::nodeTypeNames() const {
780  std::vector<std::string> ret;
781  std::transform(mFunctions.begin(), mFunctions.end(), std::back_inserter(ret),
782  [](auto& gPtr) { return gPtr->name(); });
783 
784  for (const auto& str : structs()) {
785  ret.push_back("_make_" + str->name());
786  ret.push_back("_break_" + str->name());
787  }
788 
789  if (cEnabled()) { ret.push_back("c-call"); }
790 
791  return ret;
792 }
793 
794 boost::bimap<unsigned int, NodeInstance*> GraphModule::createLineNumberAssoc() const {
795  // create a sorted list of GraphFunctions
796  std::vector<NodeInstance*> nodes;
797  for (const auto& f : functions()) {
798  for (const auto& node : f->nodes()) {
799  assert(node.second != nullptr);
800  nodes.push_back(node.second.get());
801  }
802  }
803 
804  std::sort(nodes.begin(), nodes.end(), [](const auto& lhs, const auto& rhs) {
805  return (lhs->function().name() + ":" + boost::uuids::to_string(lhs->id())) <
806  (rhs->function().name() + ":" + boost::uuids::to_string(rhs->id()));
807  });
808 
809  boost::bimap<unsigned, NodeInstance*> ret;
810  for (unsigned i = 0; i < nodes.size(); ++i) {
811  ret.left.insert({i + 1, nodes[i]}); // + 1 because line numbers start at 1
812  }
813 
814  return ret;
815 }
816 
817 GraphStruct* GraphModule::structFromName(boost::string_view name) const {
818  for (const auto& str : structs()) {
819  if (str->name() == name) { return str.get(); }
820  }
821  return nullptr;
822 }
823 
824 GraphStruct* GraphModule::getOrCreateStruct(std::string name, bool* inserted) {
825  auto str = structFromName(name);
826 
827  if (str != nullptr) {
828  if (inserted != nullptr) { *inserted = false; }
829  return str;
830  }
831  // invalidate the cache
833 
834  mStructs.push_back(std::make_unique<GraphStruct>(*this, std::move(name)));
835 
836  if (inserted != nullptr) { *inserted = true; }
837  return mStructs[mStructs.size() - 1].get();
838 }
839 
840 bool GraphModule::removeStruct(boost::string_view name) {
841  // invalidate the cache
843 
844  for (auto iter = structs().begin(); iter != structs().end(); ++iter) {
845  if ((*iter)->name() == name) {
846  mStructs.erase(iter);
847  return true;
848  }
849  }
850 
851  // TODO: remove referencing nodes
852  return false;
853 }
854 
856  assert(&tyToDel.module() == this);
857 
858  LLVM_ATTRIBUTE_UNUSED bool succeeded = removeStruct(tyToDel.name());
859  assert(succeeded);
860 }
861 
862 boost::filesystem::path GraphModule::sourceFilePath() const {
863  return context().workspacePath() / "src" / (fullName() + ".chimod");
864 }
865 
867  boost::string_view functionName,
868  std::vector<std::string> clangArgs,
869  std::unique_ptr<NodeType>* toFill) {
870  assert(toFill != nullptr);
871 
872  Result res;
873 
874  // add -I for the .c dir
875  clangArgs.push_back("-I");
876  clangArgs.push_back(pathToCSources().string());
877 
878  std::unique_ptr<llvm::Module> mod;
879  res +=
880  compileCToLLVM(fs::path(llvm::sys::fs::getMainExecutable(nullptr, nullptr)).parent_path() /
881  "chi-ctollvm"
882 #ifdef WIN32
883  ".exe"
884 #endif
885  ,
886  context().llvmContext(), clangArgs, code, &mod);
887 
888  if (!res) { return res; }
889 
890  auto llFunc = mod->getFunction(functionName.to_string());
891 
892  if (llFunc == nullptr) {
893  res.addEntry("EUKN", "Failed to find function in C code",
894  {{"Function Name", functionName.to_string()}, {"C Code", code.to_string()}});
895  return res;
896  }
897 
898  std::vector<NamedDataType> dInputs;
899  for (const auto& argument : llFunc->args()) {
900  DataType ty;
901  context().typeFromModule("lang", stringifyLLVMType(argument.getType()), &ty);
902  dInputs.emplace_back(argument.getName(), ty);
903  }
904 
905  DataType output;
906  auto ret = llFunc->getReturnType();
907 
908  if (!ret->isVoidTy()) { context().typeFromModule("lang", stringifyLLVMType(ret), &output); }
909 
910  *toFill = std::make_unique<CFuncNode>(*this, code.to_string(), functionName.to_string(),
911  clangArgs, dInputs, output);
912 
913  return res;
914 }
915 
916 } // namespace chi
Result saveToDisk() const
Serialize to disk in the context.
std::string mangleFunctionName(std::string fullModuleName, const std::string &name)
Mangle a function name.
Definition: NameMangler.cpp:9
Define json serialization functions.
this is an AST-like representation of a function in a graph It is used for IDE-like behavior...
Result generateModule(llvm::Module &module) override
Generate a llvm::Module from the module.
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
const std::string & name() const
Get the name of the type.
Definition: GraphStruct.hpp:43
boost::filesystem::path pathToCSources() const
Get the path to the .c directory. It is not garunteed to exist, even if cEnabled() is true...
A class holding a compound type defined in a GraphModule.
Definition: GraphStruct.hpp:18
std::pair< std::string, std::string > parseColonPair(const std::string &in)
Parse a colonated pair Example: lang:i32 would turn into {lang, i32}.
boost::bimap< unsigned, NodeInstance * > createLineNumberAssoc() const
Create the associations from line number and function in debug info.
Defines the Result class and related functions.
const std::vector< std::unique_ptr< GraphFunction > > & functions() const
Get functions.
Definition: GraphModule.hpp:95
GraphFunction * functionFromName(boost::string_view name) const
Get a function from the name.
STL namespace.
GraphModule & module() const
Get the module.
Definition: GraphStruct.hpp:30
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
Defines functions for compiling GraphFunction objects.
boost::filesystem::path workspacePath() const
Get the workspace path of the Context.
Definition: Context.hpp:138
Result addDependency(boost::filesystem::path newDepFullPath)
Add a dependency to the module Loads the module from context() if it isn&#39;t already loaded...
Definition: ChiModule.cpp:13
Definitions for mangling functions.
GraphStruct * structFromName(boost::string_view name) const
Get a struct by name.
Define GraphStruct.
std::string name() const
Get the name of the function.
bool removeStruct(boost::string_view name)
Remove a struct from the module by name.
Basicaly a std::pair<std::string, DataType>, except it has nicer names.
Definition: DataType.hpp:72
GraphFunction * getOrCreateFunction(std::string name, std::vector< NamedDataType > dataIns, std::vector< NamedDataType > dataOuts, std::vector< std::string > execIns, std::vector< std::string > execOuts, bool *inserted=nullptr)
Create a new function if it does&#39;t already exist.
std::string fullName() const
Get the full name of the module.
Definition: ChiModule.hpp:61
The class that handles the loading, creation, storing, and compilation of modules It also stores a LL...
Definition: Context.hpp:55
An abstract class that represents a module of code in Chigraph Can be compiled to a llvm::Module...
Definition: ChiModule.hpp:24
boost::filesystem::path sourceFilePath() const
Get the path to the source file It&#39;s not garunteed to exist, because it could have not been saved...
bool cEnabled() const
Gets if C support is enabled.
std::vector< std::string > nodeTypeNames() const override
Get the possible node type names.
GraphStruct * getOrCreateStruct(std::string name, bool *inserted=nullptr)
Create a new struct in the module.
Defines the NodeType class.
nlohmann::json graphModuleToJson(const GraphModule &mod)
Serialize a JsonModule to json.
Result compileCToLLVM(const boost::filesystem::path &ctollvmPath, llvm::LLVMContext &llvmContext, std::vector< std::string > arguments, boost::string_view inputCCode, std::unique_ptr< llvm::Module > *toFill)
Use chi-ctollvm to compile C source code to a llvm module It also uses stdCIncludePaths to find basic...
Definition: CCompiler.cpp:17
Result compileFunction(const GraphFunction &func, llvm::Module *mod, llvm::DICompileUnit *debugCU, llvm::DIBuilder &debugBuilder)
Compile the graph to an llvm::Function (usually called from JsonModule::generateModule) ...
Result addForwardDeclarations(llvm::Module &module) const override
Adds forward declartions for the functions in this module.
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< std::string > typeNames() const override
Get the possible DataType names.
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.
GraphModule(Context &cont, boost::filesystem::path fullName, const std::vector< boost::filesystem::path > &dependencies={})
Construct a GraphModule.
DataType typeFromName(boost::string_view name) override
Get a DataType from the name.
Result typeFromModule(const boost::filesystem::path &module, boost::string_view name, DataType *toFill) noexcept
Gets a DataType from a module.
Definition: Context.cpp:232
Defines the GraphModule class.
The namespace where chigraph lives.
Context & context() const
Get the Context that this module belongs to.
Definition: ChiModule.hpp:68
std::string stringifyLLVMType(llvm::Type *ty)
Turns a type into a string.
Definition: Context.cpp:474
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
The result object, used for identifiying errors with good diagnostics.
Definition: Result.hpp:72
std::pair< std::string, std::string > parseObjectPair(const nlohmann::json &object)
Parse something that looks like: {"hello": "there"} into {"hello", "there"}.
Declares the GraphFunction class.
Defines the NodeInstance class and related functions.
Result createNodeTypeFromCCode(boost::string_view code, boost::string_view functionName, std::vector< std::string > clangArgs, std::unique_ptr< NodeType > *toFill)
From C code, create a NodeType.
bool removeFunction(boost::string_view name, bool deleteReferences=true)
Remove a function from the module.
Define deserialization functions.