chigraph  master
Systems programming language written for beginners in LLVM
NodeCompiler.cpp
1 #include "chi/NodeCompiler.hpp"
2 
3 #include <cassert>
4 
5 #include "chi/Context.hpp"
6 #include "chi/DataType.hpp"
8 #include "chi/LLVMVersion.hpp"
9 #include "chi/NodeInstance.hpp"
10 #include "chi/NodeType.hpp"
11 #include "chi/Support/Result.hpp"
12 
13 #include <llvm/IR/DIBuilder.h>
14 #include <llvm/IR/DebugInfo.h>
15 #include <llvm/IR/Function.h>
16 #include <llvm/IR/IRBuilder.h>
17 
18 namespace fs = boost::filesystem;
19 
20 namespace chi {
21 
23  : mCompiler{&functionCompiler}, mNode{&inst} {
24  // alloca the outputs
25  llvm::IRBuilder<> allocBuilder(&funcCompiler().allocBlock());
26  for (auto idx = 0ull; idx < node().type().dataOutputs().size(); ++idx) {
27  const auto& namedType = node().type().dataOutputs()[idx];
28 
29  // alloca the outputs
30  auto alloca = allocBuilder.CreateAlloca(namedType.type.llvmType(), nullptr,
31  node().stringId() + "__" + std::to_string(idx));
32 
33  // create debug info for the alloca
34  {
35  // get type
36  llvm::DIType* dType = namedType.type.debugType();
37 
38  // TODO(#63): better names
39  auto debugVar =
41 #if LLVM_VERSION_LESS_EQUAL(3, 7)
42  createLocalVariable(
43  llvm::dwarf::DW_TAG_auto_variable,
44 #else
45  createAutoVariable(
46 #endif
47  funcCompiler().diFunction(), node().stringId() + "__" + std::to_string(idx),
48 #if LLVM_VERSION_LESS_EQUAL(3, 6)
49  funcCompiler().diBuilder().createFile(
50  fs::path(funcCompiler().diFunction().getFilename()).filename().string(),
51  fs::path(funcCompiler().diFunction().getFilename()).parent_path().string()),
52 #else
53  funcCompiler().diFunction()->getFile(),
54 #endif
55  1,
56 #if LLVM_VERSION_LESS_EQUAL(3, 6)
57  *
58 #endif
59  dType);
60 
61  funcCompiler()
62  .diBuilder()
63  .insertDeclare(alloca, debugVar,
64 #if LLVM_VERSION_AT_LEAST(3, 6)
65  funcCompiler().diBuilder().createExpression(),
66 #if LLVM_VERSION_AT_LEAST(3, 7)
67  llvm::DebugLoc::get(1, 1, funcCompiler().diFunction()),
68 #endif
69 #endif
70  &funcCompiler().allocBlock())
71 #if LLVM_VERSION_LESS_EQUAL(3, 6)
72  ->setDebugLoc(llvm::DebugLoc::get(1, 1, funcCompiler().diFunction()))
73 #endif
74  ;
75  }
76 
77  mReturnValues.push_back(alloca);
78  }
79 
80  auto size = inputExecs();
81 
82  // resize the inputexec specific variables
83  mPureBlocks.resize(size);
84  mCodeBlocks.resize(size, nullptr);
85 
86  mCompiledInputs.resize(size, false);
87 }
88 
89 bool NodeCompiler::pure() const { return node().type().pure(); }
90 
91 void NodeCompiler::compile_stage1(size_t inputExecID) {
92  assert(inputExecID < inputExecs() &&
93  "Cannot compile_stage1 for a inputexec that doesn't exist");
94 
95  // create the code block
96  auto& codeBlock = mCodeBlocks[inputExecID];
97 
98  // if we've already done stage 1 before, then just exit
99  if (codeBlock != nullptr) { return; }
100 
101  codeBlock = llvm::BasicBlock::Create(
102  context().llvmContext(), "node_" + node().stringId() + "__" + std::to_string(inputExecID),
103  &funcCompiler().llFunction());
104 
105  // only do this for non-pure nodes because pure nodes don't call their dependencies, they are
106  // called by the non-pure
107  if (!pure()) {
108  // generate code for all the dependent pures
109  auto depPures = dependentPuresRecursive(node());
110 
111  // set our vector to be the same length
112  auto& pureBlocks = mPureBlocks[inputExecID];
113  pureBlocks.resize(depPures.size());
114 
115  // create the first pure block--the loop only creates the next one
116  if (!depPures.empty()) {
117  pureBlocks[0] = llvm::BasicBlock::Create(context().llvmContext(),
118  "node_" + node().stringId() + "__" +
119  std::to_string(inputExecID) + "__" +
120  depPures[0]->stringId(),
121  &funcCompiler().llFunction());
122  }
123 
124  for (auto id = 0ull; id < depPures.size(); ++id) {
125  // create a BasicBlock for the next one to br to--if we're on the last one, use the code
126  // block
127  llvm::BasicBlock* nextBlock = [&] {
128  if (id == depPures.size() - 1) { return codeBlock; }
129  pureBlocks[id + 1] = llvm::BasicBlock::Create(
130  context().llvmContext(),
131  "node_" + node().stringId() + "__" + std::to_string(inputExecID) + "__" +
132  depPures[id + 1]->stringId(),
133  &funcCompiler().llFunction());
134  return pureBlocks[id + 1];
135  }();
136 
137  // add nextBlock to the list of possible locations for the indirectbr
138  funcCompiler().nodeCompiler(*depPures[id])->jumpBackInst().addDestination(nextBlock);
139 
140  // set post-pure break to go to the next one
141  llvm::IRBuilder<> irBuilder{pureBlocks[id]};
142  irBuilder.CreateStore(llvm::BlockAddress::get(nextBlock),
143  &funcCompiler().postPureBreak());
144 
145  // br to the pure, terminating that BasicBlock
146  irBuilder.CreateBr(&funcCompiler().nodeCompiler(*depPures[id])->firstBlock(0));
147  }
148  }
149 }
150 
151 Result NodeCompiler::compile_stage2(std::vector<llvm::BasicBlock*> trailingBlocks,
152  size_t inputExecID) {
153  assert((pure() || trailingBlocks.size() == node().outputExecConnections.size()) &&
154  "Trailing blocks is the wrong size");
155  assert(inputExecID < inputExecs());
156 
157  auto& codeBlock = mCodeBlocks[inputExecID];
158 
159  // skip if we've already compiled
160  if (compiled(inputExecID)) { return {}; }
161 
162  // if we haven't done stage 1, then do it
163  if (codeBlock == nullptr) { compile_stage1(inputExecID); }
164  llvm::IRBuilder<> codeBuilder{codeBlock};
165 
166  // inputs and outputs (inputs followed by outputs)
167  std::vector<llvm::Value*> io;
168 
169  // add inputs
170  for (auto idx = 0ull; idx < node().inputDataConnections.size(); ++idx) {
171  auto& connection = node().inputDataConnections[idx];
172  auto& remoteNode = *connection.first;
173  auto remoteID = connection.second;
174 
175  assert(remoteID < funcCompiler().nodeCompiler(remoteNode)->returnValues().size() &&
176  "Internal error: connection to a value doesn't exist");
177 
178  auto loaded = codeBuilder.CreateLoad(
179  funcCompiler().nodeCompiler(remoteNode)->returnValues()[remoteID]);
180  io.push_back(loaded);
181 
182  assert(io[io.size() - 1]->getType() == node().type().dataInputs()[idx].type.llvmType() &&
183  "Internal error: types do not match");
184  }
185 
186  // add outputs
187  std::copy(mReturnValues.begin(), mReturnValues.end(), std::back_inserter(io));
188 
189  // on pure, jump to the set loc
190  if (pure()) {
191  auto brBlock = llvm::BasicBlock::Create(context().llvmContext(),
192  "node_" + node().stringId() + "_jumpback",
193  &funcCompiler().llFunction());
194  llvm::IRBuilder<> builder(brBlock);
195 
196  mJumpBackInst =
197  builder.CreateIndirectBr(builder.CreateLoad(&funcCompiler().postPureBreak()));
198 
199  trailingBlocks.resize(1);
200  trailingBlocks[0] = brBlock;
201  }
202 
203  // codegen
204  Result res = node().type().codegen(
205  *this, *codeBlock, inputExecID,
206  llvm::DebugLoc::get(funcCompiler().nodeLineNumber(node()), 1, funcCompiler().diFunction()),
207  io, trailingBlocks);
208 
209  mCompiledInputs[inputExecID] = true;
210 
211  return res;
212 }
213 
214 llvm::BasicBlock& NodeCompiler::firstBlock(size_t inputExecID) const {
215  assert(inputExecID < inputExecs());
216 
217  if (mPureBlocks[inputExecID].empty()) { return codeBlock(inputExecID); }
218  return *mPureBlocks[inputExecID][0];
219 }
220 
221 llvm::BasicBlock& NodeCompiler::codeBlock(size_t inputExecID) const {
222  assert(inputExecID < inputExecs());
223  return *mCodeBlocks[inputExecID];
224 }
225 
226 llvm::Module& NodeCompiler::llvmModule() const { return funcCompiler().llvmModule(); }
227 
228 bool NodeCompiler::compiled(size_t inputExecID) const {
229  assert(inputExecID < inputExecs());
230  return mCompiledInputs[inputExecID];
231 }
232 
233 Context& NodeCompiler::context() const { return node().context(); }
234 
235 size_t NodeCompiler::inputExecs() const {
236  if (pure()) {
237  return 1;
238  } else if (node().type().qualifiedName() == "lang:entry") {
239  return 1;
240  }
241  return node().inputExecConnections.size();
242 }
243 
244 std::vector<NodeInstance*> dependentPuresRecursive(const NodeInstance& inst) {
245  std::vector<NodeInstance*> ret;
246 
247  // TODO: remove duplicates in some intellegent way
248  for (const auto& conn : inst.inputDataConnections) {
249  // if it isn't connected (this really shouldn't happen because that would fail validation),
250  // then skip.
251  if (conn.first == nullptr) { continue; }
252 
253  if (conn.first->type().pure()) {
254  auto deps = dependentPuresRecursive(*conn.first);
255  std::copy(deps.begin(), deps.end(), std::back_inserter(ret));
256 
257  ret.push_back(conn.first);
258  }
259  }
260 
261  return ret;
262 }
263 
264 } // namespace chi
Result compile_stage2(std::vector< llvm::BasicBlock *> trailingBlocks, size_t inputExecID)
Fill the codegen block If compile_stage1 hasn&#39;t been called for this inputExecID, then it will be cal...
llvm::BasicBlock & codeBlock(size_t inputExecID) const
Get the code block for a given inputExecID Requires that compile_stage1 has been called for this ID...
llvm::Module & llvmModule() const
Get the module being generated.
bool pure() const
node().type().pure()
std::vector< std::vector< std::pair< NodeInstance *, size_t > > > inputExecConnections
The connections that lead to this node, exec.
NodeCompiler * nodeCompiler(NodeInstance &node)
Get a node compile for a certain node.
FunctionCompiler & funcCompiler() const
Get the function compiler.
llvm::Module & llvmModule() const
Get the module being generated.
std::string stringId() const
Get the ID as a string.
Defines the Result class and related functions.
const std::vector< NamedDataType > & dataOutputs() const
Get the data outputs for the node.
Definition: NodeType.hpp:76
NodeType & type()
Get the type of the instance.
An instance of a node.
std::vector< std::pair< NodeInstance *, size_t > > inputDataConnections
The connections that go into this node, data.
Defines functions for compiling GraphFunction objects.
std::vector< NodeInstance * > dependentPuresRecursive(const NodeInstance &inst)
Get the pures a NodeInstance relies on These are all the dependent pures (it&#39;s fetched recursively) T...
size_t inputExecs() const
The number of input execs that we can compile If it&#39;s pure or an entry node, this is 1...
The class that handles the loading, creation, storing, and compilation of modules It also stores a LL...
Definition: Context.hpp:55
NodeInstance & node() const
The node we&#39;re compiling.
void compile_stage1(size_t inputExecID)
Add the basic blocks and fill the pure blocks, but don&#39;t fill the code block nop if its already been ...
Class for compiling GraphFunctions into llvm::Functions.
std::vector< llvm::Value * > returnValues() const
Get return values.
bool pure()
Get if this node is pure.
Definition: NodeType.hpp:86
Defines the NodeType class.
Context & context() const
Just node().context()
Defines the DataType class.
std::vector< std::pair< NodeInstance *, size_t > > outputExecConnections
The connections that go out of this node, exec.
bool compiled(size_t inputExecID) const
Get if compile_stage2 has been called for a given inputExecID.
const std::vector< NamedDataType > & dataInputs() const
Get the data inputs for the node.
Definition: NodeType.hpp:73
llvm::DIBuilder & diBuilder() const
The debug builder we&#39;re using for the module.
NodeCompiler(FunctionCompiler &functionCompiler, NodeInstance &inst)
Constructor.
The namespace where chigraph lives.
Context & context() const
Get the containing Context object.
Defines the Context class and related functions.
The result object, used for identifiying errors with good diagnostics.
Definition: Result.hpp:72
llvm::BasicBlock & firstBlock(size_t inputExecID) const
Get the first block to jump to for the node If there are dependent pures, it&#39;s the first pure block O...
llvm::IndirectBrInst & jumpBackInst() const
Get the IndirectBrInst* for the pure.
Defines the NodeInstance class and related functions.
virtual Result codegen(NodeCompiler &compiler, llvm::BasicBlock &codegenInto, size_t execInputID, const llvm::DebugLoc &nodeLocation, const std::vector< llvm::Value *> &io, const std::vector< llvm::BasicBlock *> &outputBlocks)=0
A virtual function that is called when this node needs to be called.