chigraph  master
Systems programming language written for beginners in LLVM
LangModule.cpp
Go to the documentation of this file.
1 
3 #include "chi/LangModule.hpp"
4 #include "chi/Context.hpp"
5 #include "chi/DataType.hpp"
6 #include "chi/LLVMVersion.hpp"
7 #include "chi/NodeType.hpp"
8 #include "chi/Support/Result.hpp"
9 
10 #include <llvm/AsmParser/Parser.h>
11 #include <llvm/IR/DebugInfo.h>
12 #include <llvm/IR/IRBuilder.h>
13 #include <llvm/IR/Module.h>
14 #include <llvm/Support/SourceMgr.h>
15 
16 #if LLVM_VERSION_LESS_EQUAL(3, 6)
17 #include <llvm/IR/DIBuilder.h>
18 #endif
19 
20 namespace chi {
21 
22 namespace {
24 struct IfNodeType : NodeType {
25  IfNodeType(LangModule& mod) : NodeType(mod, "if", "If") {
26  setExecInputs({""});
27  setExecOutputs({"True", "False"});
28 
29  setDataInputs({{"condition", mod.typeFromName("i1")}});
30  }
31 
32  Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t execInputID,
33  const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
34  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
35  assert(io.size() == 1 && outputBlocks.size() == 2);
36 
37  llvm::IRBuilder<> builder(&codegenInto);
38  builder.SetCurrentDebugLocation(nodeLocation);
39 
40  builder.CreateCondBr(io[0], outputBlocks[0], outputBlocks[1]);
41 
42  return {};
43  }
44 
45  std::unique_ptr<NodeType> clone() const override { return std::make_unique<IfNodeType>(*this); }
46 };
47 
48 struct EntryNodeType : NodeType {
49  EntryNodeType(LangModule& mod, std::vector<NamedDataType> dataInputs,
50  std::vector<std::string> execInputs)
51  : NodeType(mod, "entry", "Entry") {
52  setExecOutputs(std::move(execInputs));
53 
54  setDataOutputs(std::move(dataInputs));
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() == dataOutputs().size() && outputBlocks.size() == execOutputs().size());
61 
62  llvm::IRBuilder<> builder(&codegenInto);
63  builder.SetCurrentDebugLocation(nodeLocation);
64 
65  // store the arguments
66  auto arg_iter = codegenInto.getParent()->arg_begin();
67  ++arg_iter; // skip the first argument, which is the input exec ID
68  for (const auto& iovalue : io) {
69  builder.CreateStore(&*arg_iter, iovalue);
70 
71  ++arg_iter;
72  }
73 
74  auto inExecID = &*codegenInto.getParent()->arg_begin();
75  auto switchInst = builder.CreateSwitch(inExecID, outputBlocks[0], execOutputs().size());
76 
77  for (auto id = 0ull; id < execOutputs().size(); ++id) {
78  switchInst->addCase(builder.getInt32(id), outputBlocks[id]);
79  }
80  return {};
81  }
82 
83  std::unique_ptr<NodeType> clone() const override {
84  return std::make_unique<EntryNodeType>(*this);
85  }
86 
87  nlohmann::json toJSON() const override {
88  nlohmann::json ret = nlohmann::json::object();
89 
90  auto& data = ret["data"];
91  data = nlohmann::json::array();
92  for (auto& pair : dataOutputs()) {
93  data.push_back({{pair.name, pair.type.qualifiedName()}});
94  }
95 
96  auto& exec = ret["exec"];
97  exec = nlohmann::json::array();
98  for (auto& name : execOutputs()) { exec.push_back(name); }
99 
100  return ret;
101  }
102 };
103 
104 struct ConstIntNodeType : NodeType {
105  ConstIntNodeType(LangModule& mod, int num)
106  : NodeType(mod, "const-int", "Integer"), number(num) {
107  makePure();
108 
109  setDataOutputs({{"", mod.typeFromName("i32")}});
110  }
111 
112  Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t execInputID,
113  const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
114  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
115  assert(io.size() == 1 && outputBlocks.size() == 1);
116 
117  llvm::IRBuilder<> builder(&codegenInto);
118  builder.SetCurrentDebugLocation(nodeLocation);
119 
120  builder.CreateStore(builder.getInt32(number), io[0], false);
121  builder.CreateBr(outputBlocks[0]);
122 
123  return {};
124  }
125 
126  std::unique_ptr<NodeType> clone() const override {
127  return std::make_unique<ConstIntNodeType>(*this);
128  }
129 
130  nlohmann::json toJSON() const override { return number; }
131  int number;
132 };
133 
134 struct ConstFloatNodeType : NodeType {
135  ConstFloatNodeType(LangModule& mod, double num)
136  : NodeType(mod, "const-float", "Float"), number(num) {
137  makePure();
138 
139  setDataOutputs({{"", mod.typeFromName("float")}});
140  }
141 
142  Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t execInputID,
143  const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
144  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
145  assert(io.size() == 1 && outputBlocks.size() == 1);
146 
147  llvm::IRBuilder<> builder(&codegenInto);
148  builder.SetCurrentDebugLocation(nodeLocation);
149 
150  builder.CreateStore(llvm::ConstantFP::get(builder.getFloatTy(), number), io[0]);
151  builder.CreateBr(outputBlocks[0]);
152 
153  return {};
154  }
155 
156  std::unique_ptr<NodeType> clone() const override {
157  return std::make_unique<ConstFloatNodeType>(*this);
158  }
159 
160  nlohmann::json toJSON() const override { return number; }
161  double number;
162 };
163 
164 struct ConstBoolNodeType : NodeType {
165  ConstBoolNodeType(LangModule& mod, bool num)
166  : NodeType{mod, "const-bool", "Boolean literal"}, value{num} {
167  makePure();
168 
169  setDataOutputs({{"", mod.typeFromName("i1")}});
170  }
171 
172  Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t execInputID,
173  const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
174  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
175  assert(io.size() == 1 && outputBlocks.size() == 1);
176 
177  llvm::IRBuilder<> builder(&codegenInto);
178  builder.SetCurrentDebugLocation(nodeLocation);
179 
180  builder.CreateStore(builder.getInt1(value), io[0], false);
181  builder.CreateBr(outputBlocks[0]);
182 
183  return {};
184  }
185 
186  std::unique_ptr<NodeType> clone() const override {
187  return std::make_unique<ConstBoolNodeType>(*this);
188  }
189 
190  nlohmann::json toJSON() const override { return value; }
191  bool value;
192 };
193 
194 struct ExitNodeType : NodeType {
195  ExitNodeType(LangModule& mod, std::vector<NamedDataType> dataOutputs,
196  std::vector<std::string> execOutputs)
197  : NodeType{mod, "exit", "Return from a function"} {
198  // outputs to the function are inputs to the node
199  setExecInputs(std::move(execOutputs));
200 
201  setDataInputs(std::move(dataOutputs));
202  }
203 
204  Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t execInputID,
205  const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
206  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
207  assert(execInputID < execInputs().size() && io.size() == dataInputs().size());
208 
209  // assign the return types
210  llvm::IRBuilder<> builder(&codegenInto);
211  builder.SetCurrentDebugLocation(nodeLocation);
212 
213  llvm::Function* f = codegenInto.getParent();
214  size_t ret_start =
215  f->arg_size() - io.size(); // returns are after args, find where returns start
216  auto arg_iter = f->arg_begin();
217  std::advance(arg_iter, ret_start);
218  for (auto& value : io) {
219  builder.CreateStore(value, &*arg_iter, false); // TODO: volitility?
220  ++arg_iter;
221  }
222 
223  builder.CreateRet(builder.getInt32(execInputID));
224 
225  return {};
226  }
227 
228  std::unique_ptr<NodeType> clone() const override {
229  return std::make_unique<ExitNodeType>(*this);
230  }
231 
232  nlohmann::json toJSON() const override {
233  nlohmann::json ret = nlohmann::json::object();
234 
235  auto& data = ret["data"];
236  data = nlohmann::json::array();
237  for (auto& pair : dataInputs()) {
238  data.push_back({{pair.name, pair.type.qualifiedName()}});
239  }
240 
241  auto& exec = ret["exec"];
242  exec = nlohmann::json::array();
243  for (auto& name : execInputs()) { exec.push_back(name); }
244 
245  return ret;
246  }
247 };
248 
249 struct StringLiteralNodeType : NodeType {
250  StringLiteralNodeType(LangModule& mod, std::string str)
251  : NodeType(mod, "strliteral", "String"), literalString(std::move(str)) {
252  makePure();
253 
254  setDataOutputs({{"", mod.typeFromName("i8*")}});
255  }
256 
257  Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t execInputID,
258  const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
259  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
260  assert(io.size() == 1 && outputBlocks.size() == 1);
261 
262  llvm::IRBuilder<> builder(&codegenInto);
263  builder.SetCurrentDebugLocation(nodeLocation);
264 
265  auto global = builder.CreateGlobalString(literalString);
266 
267  auto const0ID = llvm::ConstantInt::get(context().llvmContext(), llvm::APInt(32, 0, false));
268  auto gep = builder.CreateGEP(global,
269 // LLVM 3.6- doesn't have std::initializer_list constructor to llvm::ArrayRef
270 #if LLVM_VERSION_LESS_EQUAL(3, 6)
271  std::vector<llvm::Value*> {
272  { const0ID, const0ID }
273  }
274 #else
275  { const0ID, const0ID }
276 #endif
277  );
278  builder.CreateStore(gep, io[0], false);
279 
280  builder.CreateBr(outputBlocks[0]);
281 
282  return {};
283  }
284 
285  std::unique_ptr<NodeType> clone() const override {
286  return std::make_unique<StringLiteralNodeType>(*this);
287  }
288 
289  nlohmann::json toJSON() const override { return literalString; }
290  std::string literalString;
291 };
292 
293 struct IntToFloatNodeType : NodeType {
294  IntToFloatNodeType(LangModule& mod) : NodeType(mod, "inttofloat", "Float -> Integer") {
295  makePure();
296 
297  setDataInputs({{"", mod.typeFromName("i32")}});
298  setDataOutputs({{"", mod.typeFromName("float")}});
299 
300  makeConverter();
301  }
302 
303  Result codegen(NodeCompiler& /*compiler*/, llvm::BasicBlock& codegenInto,
304  size_t /*execInputID*/, const llvm::DebugLoc& nodeLocation,
305  const std::vector<llvm::Value*>& io,
306  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
307  assert(io.size() == 2 && outputBlocks.size() == 1);
308 
309  llvm::IRBuilder<> builder(&codegenInto);
310  builder.SetCurrentDebugLocation(nodeLocation);
311 
312  auto casted =
313  builder.CreateCast(llvm::Instruction::CastOps::SIToFP, io[0], builder.getFloatTy());
314  builder.CreateStore(casted, io[1]);
315 
316  builder.CreateBr(outputBlocks[0]);
317 
318  return {};
319  }
320 
321  std::unique_ptr<NodeType> clone() const override {
322  return std::make_unique<IntToFloatNodeType>(*this);
323  }
324 };
325 
326 struct FloatToIntNodeType : NodeType {
327  FloatToIntNodeType(LangModule& mod) : NodeType(mod, "floattoint", "Float -> Integer") {
328  makePure();
329 
330  setDataInputs({{"", mod.typeFromName("float")}});
331  setDataOutputs({{"", mod.typeFromName("i32")}});
332 
333  makeConverter();
334  }
335 
336  Result codegen(NodeCompiler& compiler, llvm::BasicBlock& codegenInto, size_t execInputID,
337  const llvm::DebugLoc& nodeLocation, const std::vector<llvm::Value*>& io,
338  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
339  assert(io.size() == 2 && outputBlocks.size() == 1);
340 
341  llvm::IRBuilder<> builder(&codegenInto);
342  builder.SetCurrentDebugLocation(nodeLocation);
343 
344  auto casted =
345  builder.CreateCast(llvm::Instruction::CastOps::FPToSI, io[0], builder.getInt32Ty());
346  builder.CreateStore(casted, io[1]);
347 
348  builder.CreateBr(outputBlocks[0]);
349 
350  return {};
351  }
352 
353  std::unique_ptr<NodeType> clone() const override {
354  return std::make_unique<FloatToIntNodeType>(*this);
355  }
356 };
357 
359 enum class BinOp { Add, Subtract, Multiply, Divide };
360 
361 struct BinaryOperationNodeType : NodeType {
362  BinaryOperationNodeType(LangModule& mod, DataType ty, BinOp binaryOperation)
363  : NodeType(mod), mBinOp(binaryOperation), mType{ty} {
364  makePure();
365 
366  std::string opStr = [](BinOp b) {
367  switch (b) {
368  case BinOp::Add: return "+";
369  case BinOp::Subtract: return "-";
370  case BinOp::Multiply: return "*";
371  case BinOp::Divide: return "/";
372  default: return "";
373  }
374  return "";
375  }(binaryOperation);
376 
377  setName(ty.unqualifiedName() + opStr + ty.unqualifiedName());
378 
379  std::string opVerb = [](BinOp b) {
380  switch (b) {
381  case BinOp::Add: return "Add";
382  case BinOp::Subtract: return "Subtract";
383  case BinOp::Multiply: return "Multiply";
384  case BinOp::Divide: return "Divide";
385  default: return "";
386  }
387  return "";
388  }(binaryOperation);
389 
390  setDescription(opVerb + " two " + ty.unqualifiedName() + "s");
391 
392  setDataInputs({{"a", ty}, {"b", ty}});
393  setDataOutputs({{"", ty}});
394  }
395 
396  Result codegen(NodeCompiler& /*compiler*/, llvm::BasicBlock& codegenInto,
397  size_t /*execInputID*/, const llvm::DebugLoc& nodeLocation,
398  const std::vector<llvm::Value*>& io,
399  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
400  assert(io.size() == 3 && outputBlocks.size() == 1);
401 
402  llvm::IRBuilder<> builder(&codegenInto);
403  builder.SetCurrentDebugLocation(nodeLocation);
404 
405  llvm::Value* result = nullptr;
406  if (mType.unqualifiedName() == "i32") {
407  result = [&](BinOp b) {
408  switch (b) {
409  case BinOp::Add: return builder.CreateAdd(io[0], io[1]);
410  case BinOp::Subtract: return builder.CreateSub(io[0], io[1]);
411  case BinOp::Multiply: return builder.CreateMul(io[0], io[1]);
412  case BinOp::Divide: return builder.CreateSDiv(io[0], io[1]);
413  default: return (llvm::Value*)nullptr;
414  }
415  return (llvm::Value*)nullptr;
416  }(mBinOp);
417 
418  } else {
419  result = [&](BinOp b) {
420  switch (b) {
421  case BinOp::Add: return builder.CreateFAdd(io[0], io[1]);
422  case BinOp::Subtract: return builder.CreateFSub(io[0], io[1]);
423  case BinOp::Multiply: return builder.CreateFMul(io[0], io[1]);
424  case BinOp::Divide: return builder.CreateFDiv(io[0], io[1]);
425  default: return (llvm::Value*)nullptr;
426  }
427  return (llvm::Value*)nullptr;
428  }(mBinOp);
429  }
430 
431  builder.CreateStore(result, io[2]);
432 
433  builder.CreateBr(outputBlocks[0]);
434 
435  return {};
436  }
437 
438  std::unique_ptr<NodeType> clone() const override {
439  return std::make_unique<BinaryOperationNodeType>(*this);
440  }
441 
442  BinOp mBinOp;
443  DataType mType;
444 };
445 
446 enum class CmpOp { Lt, Gt, Let, Get, Eq, Neq };
447 
448 struct CompareNodeType : NodeType {
449  CompareNodeType(LangModule& mod, DataType ty, CmpOp op)
450  : NodeType(mod), mCompOp(op), mType{ty} {
451  makePure();
452 
453  std::string opStr = [](CmpOp b) {
454  switch (b) {
455  case CmpOp::Lt: return "<";
456  case CmpOp::Gt: return ">";
457  case CmpOp::Let: return "<=";
458  case CmpOp::Get: return ">=";
459  case CmpOp::Eq: return "==";
460  case CmpOp::Neq: return "!=";
461  default: return "";
462  }
463  return "";
464  }(mCompOp);
465 
466  setName(ty.unqualifiedName() + opStr + ty.unqualifiedName());
467  setDescription(ty.unqualifiedName() + opStr + ty.unqualifiedName());
468 
469  setDataInputs({{"a", ty}, {"b", ty}});
470  setDataOutputs({{"", mod.typeFromName("i1")}});
471  }
472 
473  Result codegen(NodeCompiler& /*compiler*/, llvm::BasicBlock& codegenInto,
474  size_t /*execInputID*/, const llvm::DebugLoc& nodeLocation,
475  const std::vector<llvm::Value*>& io,
476  const std::vector<llvm::BasicBlock*>& outputBlocks) override {
477  assert(io.size() == 3 && outputBlocks.size() == 1);
478 
479  llvm::IRBuilder<> builder(&codegenInto);
480  builder.SetCurrentDebugLocation(nodeLocation);
481 
482  llvm::Value* result = nullptr;
483  if (mType.unqualifiedName() == "i32") {
484  result = [&](CmpOp b) -> llvm::Value* {
485  switch (b) {
486  case CmpOp::Lt: return builder.CreateICmpSLT(io[0], io[1]);
487  case CmpOp::Gt: return builder.CreateICmpSGT(io[0], io[1]);
488  case CmpOp::Let: return builder.CreateICmpSLE(io[0], io[1]);
489  case CmpOp::Get: return builder.CreateICmpSGE(io[0], io[1]);
490  case CmpOp::Eq: return builder.CreateICmpEQ(io[0], io[1]);
491  case CmpOp::Neq: return builder.CreateICmpNE(io[0], io[1]);
492  default: return nullptr;
493  }
494  return nullptr;
495  }(mCompOp);
496 
497  } else {
498  result = [&](CmpOp b) -> llvm::Value* {
499  switch (b) {
500  case CmpOp::Lt: return builder.CreateFCmpULT(io[0], io[1]);
501  case CmpOp::Gt: return builder.CreateFCmpUGT(io[0], io[1]);
502  case CmpOp::Let: return builder.CreateFCmpULE(io[0], io[1]);
503  case CmpOp::Get: return builder.CreateFCmpUGE(io[0], io[1]);
504  case CmpOp::Eq: return builder.CreateFCmpUEQ(io[0], io[1]);
505  case CmpOp::Neq: return builder.CreateFCmpUNE(io[0], io[1]);
506  default: return nullptr;
507  }
508  return nullptr;
509  }(mCompOp);
510  }
511 
512  builder.CreateStore(result, io[2]);
513 
514  builder.CreateBr(outputBlocks[0]);
515 
516  return {};
517  }
518 
519  std::unique_ptr<NodeType> clone() const override {
520  return std::make_unique<CompareNodeType>(*this);
521  }
522 
523  CmpOp mCompOp;
524  DataType mType;
525 };
526 
527 } // anonymous namespace
528 
530  using namespace std::string_literals;
531 
532  // populate them
533  nodes = {
534  {"if"s,
535  [this](const nlohmann::json&, Result&) { return std::make_unique<IfNodeType>(*this); }},
536  {"i32+i32"s,
537  [this](const nlohmann::json&, Result&) {
538  return std::make_unique<BinaryOperationNodeType>(
539  *this, LangModule::typeFromName("i32"), BinOp::Add);
540  }},
541  {"i32-i32"s,
542  [this](const nlohmann::json&, Result&) {
543  return std::make_unique<BinaryOperationNodeType>(
544  *this, LangModule::typeFromName("i32"), BinOp::Subtract);
545  }},
546  {"i32*i32"s,
547  [this](const nlohmann::json&, Result&) {
548  return std::make_unique<BinaryOperationNodeType>(
549  *this, LangModule::typeFromName("i32"), BinOp::Multiply);
550  }},
551  {"i32/i32"s,
552  [this](const nlohmann::json&, Result&) {
553  return std::make_unique<BinaryOperationNodeType>(
554  *this, LangModule::typeFromName("i32"), BinOp::Divide);
555  }},
556  {"float+float"s,
557  [this](const nlohmann::json&, Result&) {
558  return std::make_unique<BinaryOperationNodeType>(
559  *this, LangModule::typeFromName("float"), BinOp::Add);
560  }},
561  {"float-float"s,
562  [this](const nlohmann::json&, Result&) {
563  return std::make_unique<BinaryOperationNodeType>(
564  *this, LangModule::typeFromName("float"), BinOp::Subtract);
565  }},
566  {"float*float"s,
567  [this](const nlohmann::json&, Result&) {
568  return std::make_unique<BinaryOperationNodeType>(
569  *this, LangModule::typeFromName("float"), BinOp::Multiply);
570  }},
571  {"float/float"s,
572  [this](const nlohmann::json&, Result&) {
573  return std::make_unique<BinaryOperationNodeType>(
574  *this, LangModule::typeFromName("float"), BinOp::Divide);
575  }},
576  {"i32<i32"s,
577  [this](const nlohmann::json&, Result&) {
578  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("i32"),
579  CmpOp::Lt);
580  }},
581  {"i32>i32"s,
582  [this](const nlohmann::json&, Result&) {
583  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("i32"),
584  CmpOp::Gt);
585  }},
586  {"i32<=i32"s,
587  [this](const nlohmann::json&, Result&) {
588  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("i32"),
589  CmpOp::Let);
590  }},
591  {"i32>=i32"s,
592  [this](const nlohmann::json&, Result&) {
593  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("i32"),
594  CmpOp::Get);
595  }},
596  {"i32==i32"s,
597  [this](const nlohmann::json&, Result&) {
598  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("i32"),
599  CmpOp::Eq);
600  }},
601  {"i32!=i32"s,
602  [this](const nlohmann::json&, Result&) {
603  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("i32"),
604  CmpOp::Neq);
605  }},
606  {"float<float"s,
607  [this](const nlohmann::json&, Result&) {
608  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("float"),
609  CmpOp::Lt);
610  }},
611  {"float>float"s,
612  [this](const nlohmann::json&, Result&) {
613  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("float"),
614  CmpOp::Gt);
615  }},
616  {"float<=float"s,
617  [this](const nlohmann::json&, Result&) {
618  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("float"),
619  CmpOp::Let);
620  }},
621  {"float>=float"s,
622  [this](const nlohmann::json&, Result&) {
623  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("float"),
624  CmpOp::Get);
625  }},
626  {"float==float"s,
627  [this](const nlohmann::json&, Result&) {
628  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("float"),
629  CmpOp::Eq);
630  }},
631  {"float!=float"s,
632  [this](const nlohmann::json&, Result&) {
633  return std::make_unique<CompareNodeType>(*this, LangModule::typeFromName("float"),
634  CmpOp::Neq);
635  }},
636  {"inttofloat"s, [this](const nlohmann::json&,
637  Result&) { return std::make_unique<IntToFloatNodeType>(*this); }},
638  {"floattoint"s, [this](const nlohmann::json&,
639  Result&) { return std::make_unique<FloatToIntNodeType>(*this); }},
640  {"entry"s,
641  [this](const nlohmann::json& injson, Result& res) {
642 
643  // transform the JSON data into this data structure
644  std::vector<NamedDataType> dataInputs;
645 
646  if (injson.find("data") != injson.end()) {
647  auto& data = injson["data"];
648 
649  if (data.is_array()) {
650  for (const auto& input : data) {
651  std::string docString;
652  std::string qualifiedType;
653  for (auto iter = input.begin(); iter != input.end(); ++iter) {
654  docString = iter.key();
655  qualifiedType = iter.value();
656  }
657 
658  std::string module = qualifiedType.substr(0, qualifiedType.find(':'));
659  std::string type = qualifiedType.substr(qualifiedType.find(':') + 1);
660 
661  DataType cty;
662  // TODO: maybe not discard res
663  res += context().typeFromModule(module, type, &cty);
664 
665  if (!res) { continue; }
666 
667  dataInputs.emplace_back(docString, cty);
668  }
669 
670  } else {
671  res.addEntry("WUKN", "Data for lang:entry must be an array",
672  {{"Given Data", data}});
673  }
674 
675  } else {
676  res.addEntry("WUKN", "Data for lang:entry must have a data element",
677  {{"Data JSON", injson}});
678  }
679 
680  std::vector<std::string> execInputs;
681 
682  if (injson.find("exec") != injson.end()) {
683  auto& exec = injson["exec"];
684  if (exec.is_array()) {
685  for (const auto& output : exec) { execInputs.push_back(output); }
686  }
687  } else {
688  res.addEntry("WUKN", "Data for lang:entry must have a exec element",
689  {{"Data JSON", injson}});
690  }
691 
692  if (res) {
693  return std::make_unique<EntryNodeType>(*this, std::move(dataInputs),
694  std::move(execInputs));
695  }
696  return std::unique_ptr<EntryNodeType>();
697  }},
698  {"exit"s,
699  [this](const nlohmann::json& injson, Result& res) {
700  // transform the JSON data into this data structure
701  std::vector<NamedDataType> dataOutputs;
702 
703  if (injson.find("data") != injson.end()) {
704  auto& data = injson["data"];
705 
706  if (data.is_array()) {
707  for (const auto& output : data) {
708  std::string docString;
709  std::string qualifiedType;
710  for (auto iter = output.begin(); iter != output.end(); ++iter) {
711  docString = iter.key();
712  qualifiedType = iter.value();
713  }
714 
715  std::string module = qualifiedType.substr(0, qualifiedType.find(':'));
716  std::string type = qualifiedType.substr(qualifiedType.find(':') + 1);
717 
718  DataType cty;
719  // TODO: maybe not discard res
720  context().typeFromModule(module, type, &cty);
721 
722  dataOutputs.emplace_back(docString, cty);
723  }
724 
725  } else {
726  res.addEntry("WUKN", "Data element for lang:exit must be an array",
727  {{"Data JSON", injson}});
728  }
729 
730  } else {
731  res.addEntry("WUKN", "Data for lang:exit must have a data element",
732  {{"Data JSON", injson}});
733  }
734  std::vector<std::string> execOutputs;
735 
736  if (injson.find("exec") != injson.end()) {
737  auto& exec = injson["exec"];
738  if (exec.is_array()) {
739  for (const auto& output : exec) { execOutputs.push_back(output); }
740  } else {
741  res.addEntry("WUKN", "Exec element for lang:exit must be an array",
742  {{"Data JSON", injson}});
743  }
744 
745  } else {
746  res.addEntry("WUKN", "Data for lang:exit must have a exec element",
747  {{"Data JSON", injson}});
748  }
749 
750  return std::make_unique<ExitNodeType>(*this, std::move(dataOutputs),
751  std::move(execOutputs));
752 
753  }},
754  {"const-int"s,
755  [this](const nlohmann::json& data, Result& res) {
756 
757  int num = 0;
758 
759  if (data.is_number_integer()) {
760  num = data;
761  } else {
762  res.addEntry("WUKN", "Data for lang:const-int must be an integer",
763  {{"Given Data", data}});
764  }
765 
766  return std::make_unique<ConstIntNodeType>(*this, num);
767  }},
768  {"const-float"s,
769  [this](const nlohmann::json& data, Result& res) {
770 
771  float num = 0;
772 
773  if (data.is_number()) {
774  num = data;
775  } else {
776  res.addEntry("WUKN", "Data for lang:const-float must be a number",
777  {{"Given Data", data}});
778  }
779 
780  return std::make_unique<ConstFloatNodeType>(*this, num);
781  }},
782  {"const-bool"s,
783  [this](const nlohmann::json& data, Result& res) {
784 
785  bool val = false;
786 
787  if (data.is_boolean()) {
788  val = data;
789  } else {
790  res.addEntry("WUKN", "Data for lang:const-bool must be a boolean",
791  {{"Given Data", data}});
792  }
793 
794  return std::make_unique<ConstBoolNodeType>(*this, val);
795  }},
796  {"strliteral"s, [this](const nlohmann::json& data, Result& res) {
797  std::string str;
798  if (data.is_string()) {
799  str = data;
800  } else {
801  res.addEntry("WUKN", "Data for lang:strliteral must be a string",
802  {{"Given Data", data}});
803  }
804 
805  return std::make_unique<StringLiteralNodeType>(*this, str);
806  }}};
807 #if LLVM_VERSION_LESS_EQUAL(3, 6)
808  // create a temp module so we can make a DIBuilder
809  auto mod = std::make_unique<llvm::Module>("tmp", context().llvmContext());
810 
811  auto builder = std::make_unique<llvm::DIBuilder>(*mod);
812  mDebugTypes["i32"] =
813  new llvm::DIType(builder->createBasicType("lang:i32", 32, 32, llvm::dwarf::DW_ATE_signed));
814  mDebugTypes["i1"] =
815  new llvm::DIType(builder->createBasicType("lang:i1", 8, 7, llvm::dwarf::DW_ATE_boolean));
816  mDebugTypes["float"] =
817  new llvm::DIType(builder->createBasicType("lang:float", 64, 64, llvm::dwarf::DW_ATE_float));
818 
819  auto charType = builder->createBasicType("lang:i8", 8, 8, llvm::dwarf::DW_ATE_unsigned_char);
820 
821  mDebugTypes["i8*"] = new llvm::DIType(builder->createPointerType(charType, 64, 64, "lang:i8*"));
822 
823 #else
824  // create debug types
825  mDebugTypes["i32"] =
826  llvm::DIBasicType::get(context().llvmContext(), llvm::dwarf::DW_TAG_base_type, "lang:i32",
827  32, 32, llvm::dwarf::DW_ATE_signed);
828  mDebugTypes["i1"] =
829  llvm::DIBasicType::get(context().llvmContext(), llvm::dwarf::DW_TAG_base_type, "lang:i1", 8,
830  8, llvm::dwarf::DW_ATE_boolean);
831  mDebugTypes["float"] =
832  llvm::DIBasicType::get(context().llvmContext(), llvm::dwarf::DW_TAG_base_type, "lang:float",
833  64, 64, llvm::dwarf::DW_ATE_float);
834  auto charType = llvm::DIBasicType::get(context().llvmContext(), llvm::dwarf::DW_TAG_base_type,
835  "lang:i8", 8, 8, llvm::dwarf::DW_ATE_unsigned_char);
836 
837  mDebugTypes["i8*"] =
838  llvm::DIDerivedType::get(context().llvmContext(), llvm::dwarf::DW_TAG_pointer_type, nullptr,
839  nullptr, 0, nullptr, charType, 64, 64, 0,
840 #if LLVM_VERSION_AT_LEAST(5, 0)
841  llvm::None,
842 #endif
843  llvm::DINode::DIFlags()); // TODO: 32bit support?
844 #endif
845 }
846 
847 Result LangModule::nodeTypeFromName(boost::string_view name, const nlohmann::json& jsonData,
848  std::unique_ptr<NodeType>* toFill) {
849  Result res;
850 
851  auto iter = nodes.find(name.to_string());
852  if (iter != nodes.end()) {
853  *toFill = iter->second(jsonData, res);
854  return res;
855  }
856 
857  res.addEntry("E37", "Failed to find node in module",
858  {{"Module", "lang"}, {"Requested Node Type", name.to_string()}});
859 
860  return res;
861 }
862 
864 #if LLVM_VERSION_LESS_EQUAL(3, 6)
865  // free those newed pointers
866  for (const auto& ptr : mDebugTypes) { delete ptr.second; }
867 #endif
868 }
869 
870 // the lang module just has the basic llvm types.
871 DataType LangModule::typeFromName(boost::string_view name) {
872  using namespace std::string_literals;
873 
874  llvm::Type* ty;
875 
876  auto err = llvm::SMDiagnostic();
877 #if LLVM_VERSION_LESS_EQUAL(3, 8)
878  // just parse the type
879  auto IR = "@G = external global "s + name.to_string();
880 
881  auto tmpModule = llvm::
882 #if LLVM_VERSION_LESS_EQUAL(3, 5)
883  ParseAssemblyString(IR.c_str(), new llvm::Module("tmp", context().llvmContext()), err,
884  context().llvmContext());
885 #else
886  parseAssemblyString(IR, err, context().llvmContext());
887 #endif
888 
889  if (!tmpModule) { return nullptr; }
890 
891  ty = tmpModule->getNamedValue("G")->getType()->getContainedType(0);
892 #else
893  {
894  llvm::Module tMod("tmp", context().llvmContext());
895  ty = llvm::parseType(name.to_string(), err, tMod, nullptr);
896  }
897 #endif
898 
899  // get debug type
900  auto iter = mDebugTypes.find(name.to_string());
901  if (iter == mDebugTypes.end()) { return {}; }
902 
903 #if LLVM_VERSION_LESS_EQUAL(3, 5)
904  delete tmpModule;
905 #endif
906 
907  return DataType{this, name.to_string(), ty, iter->second};
908 }
909 
910 Result LangModule::addForwardDeclarations(llvm::Module&) const { return {}; }
911 
912 Result LangModule::generateModule(llvm::Module&) { return {}; }
913 } // namespace chi
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: LangModule.cpp:847
Definition: Fwd.hpp:29
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
Defines the Result class and related functions.
STL namespace.
~LangModule()
Destructor.
Definition: LangModule.cpp:863
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
Result addForwardDeclarations(llvm::Module &module) const override
Adds forward declartions for the functions in this module.
Definition: LangModule.cpp:910
DataType typeFromName(boost::string_view name) override
Get a DataType from the name.
Definition: LangModule.cpp:871
Defines the NodeType class.
llvm::LLVMContext & llvmContext()
Get the LLVMContext
Definition: Context.hpp:173
Defines the LangModule class.
Result generateModule(llvm::Module &) override
Generate a llvm::Module from the module.
Definition: LangModule.cpp:912
Defines the DataType class.
Result typeFromModule(const boost::filesystem::path &module, boost::string_view name, DataType *toFill) noexcept
Gets a DataType from a module.
Definition: Context.cpp:232
The namespace where chigraph lives.
Context & context() const
Get the Context that this module belongs to.
Definition: ChiModule.hpp:68
LangModule(Context &ctx)
Default constructor, usually called from Context::loadModule("lang")
Definition: LangModule.cpp:529
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