| 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"
6 #include "chi/FunctionCompiler.hpp"
7 #include "chi/GraphFunction.hpp"
8 #include "chi/GraphStruct.hpp"
9 #include "chi/JsonDeserializer.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"
15 #include "chi/Support/LibCLocator.hpp"
16 #include "chi/Support/Result.hpp"
17 #include "chi/Support/Subprocess.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
514 Result GraphModule::saveToDisk() const {
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
545 GraphFunction* GraphModule::getOrCreateFunction(std::string name,
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
558 updateLastEditTime();
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
570 updateLastEditTime();
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
583 updateLastEditTime();
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
832 updateLastEditTime();
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
842 updateLastEditTime();
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
855 void GraphModule::removeStruct(GraphStruct& tyToDel) {
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
866 Result GraphModule::createNodeTypeFromCCode(boost::string_view code,
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
chi::GraphModule::saveToDisk
Result saveToDisk() const
Serialize to disk in the context.
Definition: GraphModule.cpp:514
LLVMVersion.hpp
chi::mangleFunctionName
std::string mangleFunctionName(std::string fullModuleName, const std::string &name)
Mangle a function name.
Definition: NameMangler.cpp:9
JsonSerializer.hpp
Define json serialization functions.
chi::GraphFunction
this is an AST-like representation of a function in a graph It is used for IDE-like behavior...
Definition: GraphFunction.hpp:23
chi::GraphModule::generateModule
Result generateModule(llvm::Module &module) override
Generate a llvm::Module from the module.
Definition: GraphModule.cpp:435
chi::Result::addEntry
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
Subprocess.hpp
chi::GraphStruct::name
const std::string & name() const
Get the name of the type.
Definition: GraphStruct.hpp:43
chi::GraphModule::pathToCSources
boost::filesystem::path pathToCSources() const
Get the path to the .c directory. It is not garunteed to exist, even if cEnabled() is true...
Definition: GraphModule.hpp:150
chi::GraphStruct
A class holding a compound type defined in a GraphModule.
Definition: GraphStruct.hpp:18
chi::parseColonPair
std::pair< std::string, std::string > parseColonPair(const std::string &in)
Parse a colonated pair Example: lang:i32 would turn into {lang, i32}.
Definition: GraphFunction.hpp:355
chi::GraphModule::createLineNumberAssoc
boost::bimap< unsigned, NodeInstance * > createLineNumberAssoc() const
Create the associations from line number and function in debug info.
Definition: GraphModule.cpp:794
Result.hpp
Defines the Result class and related functions.
chi::GraphModule::functions
const std::vector< std::unique_ptr< GraphFunction > > & functions() const
Get functions.
Definition: GraphModule.hpp:95
chi::GraphModule::functionFromName
GraphFunction * functionFromName(boost::string_view name) const
Get a function from the name.
Definition: GraphModule.cpp:598
std
STL namespace.
chi::GraphStruct::module
GraphModule & module() const
Get the module.
Definition: GraphStruct.hpp:30
chi::Context::findInstancesOfType
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
FunctionCompiler.hpp
Defines functions for compiling GraphFunction objects.
chi::Context::workspacePath
boost::filesystem::path workspacePath() const
Get the workspace path of the Context.
Definition: Context.hpp:138
chi::ChiModule::addDependency
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
NameMangler.hpp
Definitions for mangling functions.
chi::GraphModule::structFromName
GraphStruct * structFromName(boost::string_view name) const
Get a struct by name.
Definition: GraphModule.cpp:817
GraphStruct.hpp
Define GraphStruct.
chi::GraphFunction::name
std::string name() const
Get the name of the function.
Definition: GraphFunction.hpp:313
chi::GraphModule::removeStruct
bool removeStruct(boost::string_view name)
Remove a struct from the module by name.
Definition: GraphModule.cpp:840
chi::NamedDataType
Basicaly a std::pair<std::string, DataType>, except it has nicer names.
Definition: DataType.hpp:72
chi::GraphModule::getOrCreateFunction
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.
Definition: GraphModule.cpp:545
chi::ChiModule::fullName
std::string fullName() const
Get the full name of the module.
Definition: ChiModule.hpp:61
chi::Context
The class that handles the loading, creation, storing, and compilation of modules It also stores a LL...
Definition: Context.hpp:55
chi::ChiModule
An abstract class that represents a module of code in Chigraph Can be compiled to a llvm::Module...
Definition: ChiModule.hpp:24
chi::GraphModule::sourceFilePath
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...
Definition: GraphModule.cpp:862
chi::GraphModule::cEnabled
bool cEnabled() const
Gets if C support is enabled.
Definition: GraphModule.hpp:136
chi::GraphModule::nodeTypeNames
std::vector< std::string > nodeTypeNames() const override
Get the possible node type names.
Definition: GraphModule.cpp:779
chi::GraphModule::getOrCreateStruct
GraphStruct * getOrCreateStruct(std::string name, bool *inserted=nullptr)
Create a new struct in the module.
Definition: GraphModule.cpp:824
LibCLocator.hpp
NodeType.hpp
Defines the NodeType class.
chi::graphModuleToJson
nlohmann::json graphModuleToJson(const GraphModule &mod)
Serialize a JsonModule to json.
Definition: JsonSerializer.cpp:97
chi::compileCToLLVM
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
chi::compileFunction
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) ...
Definition: FunctionCompiler.cpp:371
chi::GraphModule::addForwardDeclarations
Result addForwardDeclarations(llvm::Module &module) const override
Adds forward declartions for the functions in this module.
Definition: GraphModule.cpp:425
chi::ChiModule::updateLastEditTime
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
chi::GraphModule::typeNames
std::vector< std::string > typeNames() const override
Get the possible DataType names.
Definition: GraphModule.cpp:416
chi::GraphModule::nodeTypeFromName
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.
Definition: GraphModule.cpp:606
chi::GraphModule::GraphModule
GraphModule(Context &cont, boost::filesystem::path fullName, const std::vector< boost::filesystem::path > &dependencies={})
Construct a GraphModule.
Definition: GraphModule.cpp:409
chi::GraphModule::typeFromName
DataType typeFromName(boost::string_view name) override
Get a DataType from the name.
Definition: GraphModule.cpp:771
chi::Context::typeFromModule
Result typeFromModule(const boost::filesystem::path &module, boost::string_view name, DataType *toFill) noexcept
Gets a DataType from a module.
Definition: Context.cpp:232
GraphModule.hpp
Defines the GraphModule class.
CCompiler.hpp
chi
The namespace where chigraph lives.
Definition: BitcodeParser.hpp:12
chi::ChiModule::context
Context & context() const
Get the Context that this module belongs to.
Definition: ChiModule.hpp:68
chi::stringifyLLVMType
std::string stringifyLLVMType(llvm::Type *ty)
Turns a type into a string.
Definition: Context.cpp:474
Context.hpp
Defines the Context class and related functions.
chi::DataType
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
chi::Result
The result object, used for identifiying errors with good diagnostics.
Definition: Result.hpp:72
chi::parseObjectPair
std::pair< std::string, std::string > parseObjectPair(const nlohmann::json &object)
Parse something that looks like: {"hello": "there"} into {"hello", "there"}.
Definition: JsonDeserializer.cpp:491
GraphFunction.hpp
Declares the GraphFunction class.
NodeInstance.hpp
Defines the NodeInstance class and related functions.
chi::GraphModule::createNodeTypeFromCCode
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.
Definition: GraphModule.cpp:866
chi::GraphModule::removeFunction
bool removeFunction(boost::string_view name, bool deleteReferences=true)
Remove a function from the module.
Definition: GraphModule.cpp:568
JsonDeserializer.hpp
Define deserialization functions.