| 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"
7 #include "chi/FunctionCompiler.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
22 NodeCompiler::NodeCompiler(FunctionCompiler& functionCompiler, NodeInstance& inst)
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 =
40 funcCompiler().diBuilder().
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
chi::NodeCompiler::compile_stage2
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...
Definition: NodeCompiler.cpp:151
LLVMVersion.hpp
chi::NodeCompiler::codeBlock
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...
Definition: NodeCompiler.cpp:221
chi::NodeCompiler::llvmModule
llvm::Module & llvmModule() const
Get the module being generated.
Definition: NodeCompiler.cpp:226
chi::NodeCompiler::pure
bool pure() const
node().type().pure()
Definition: NodeCompiler.cpp:89
chi::NodeInstance::inputExecConnections
std::vector< std::vector< std::pair< NodeInstance *, size_t > > > inputExecConnections
The connections that lead to this node, exec.
Definition: NodeInstance.hpp:74
chi::FunctionCompiler::nodeCompiler
NodeCompiler * nodeCompiler(NodeInstance &node)
Get a node compile for a certain node.
Definition: FunctionCompiler.cpp:353
chi::NodeCompiler::funcCompiler
FunctionCompiler & funcCompiler() const
Get the function compiler.
Definition: NodeCompiler.hpp:42
chi::FunctionCompiler::llvmModule
llvm::Module & llvmModule() const
Get the module being generated.
Definition: FunctionCompiler.hpp:81
chi::NodeInstance::stringId
std::string stringId() const
Get the ID as a string.
Definition: NodeInstance.hpp:68
Result.hpp
Defines the Result class and related functions.
chi::NodeType::dataOutputs
const std::vector< NamedDataType > & dataOutputs() const
Get the data outputs for the node.
Definition: NodeType.hpp:76
chi::NodeInstance::type
NodeType & type()
Get the type of the instance.
Definition: NodeInstance.hpp:46
chi::NodeInstance
An instance of a node.
Definition: NodeInstance.hpp:19
chi::NodeInstance::inputDataConnections
std::vector< std::pair< NodeInstance *, size_t > > inputDataConnections
The connections that go into this node, data.
Definition: NodeInstance.hpp:77
FunctionCompiler.hpp
Defines functions for compiling GraphFunction objects.
chi::dependentPuresRecursive
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...
Definition: NodeCompiler.cpp:244
chi::NodeCompiler::inputExecs
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...
Definition: NodeCompiler.cpp:235
chi::Context
The class that handles the loading, creation, storing, and compilation of modules It also stores a LL...
Definition: Context.hpp:55
chi::NodeCompiler::node
NodeInstance & node() const
The node we&#39;re compiling.
Definition: NodeCompiler.hpp:46
chi::NodeCompiler::compile_stage1
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 ...
Definition: NodeCompiler.cpp:91
chi::FunctionCompiler
Class for compiling GraphFunctions into llvm::Functions.
Definition: FunctionCompiler.hpp:24
chi::NodeCompiler::returnValues
std::vector< llvm::Value * > returnValues() const
Get return values.
Definition: NodeCompiler.hpp:105
chi::NodeType::pure
bool pure()
Get if this node is pure.
Definition: NodeType.hpp:86
NodeType.hpp
Defines the NodeType class.
chi::NodeCompiler::context
Context & context() const
Just node().context()
Definition: NodeCompiler.cpp:233
DataType.hpp
Defines the DataType class.
chi::NodeInstance::outputExecConnections
std::vector< std::pair< NodeInstance *, size_t > > outputExecConnections
The connections that go out of this node, exec.
Definition: NodeInstance.hpp:80
chi::NodeCompiler::compiled
bool compiled(size_t inputExecID) const
Get if compile_stage2 has been called for a given inputExecID.
Definition: NodeCompiler.cpp:228
chi::NodeType::dataInputs
const std::vector< NamedDataType > & dataInputs() const
Get the data inputs for the node.
Definition: NodeType.hpp:73
chi::FunctionCompiler::diBuilder
llvm::DIBuilder & diBuilder() const
The debug builder we&#39;re using for the module.
Definition: FunctionCompiler.hpp:85
chi::NodeCompiler::NodeCompiler
NodeCompiler(FunctionCompiler &functionCompiler, NodeInstance &inst)
Constructor.
Definition: NodeCompiler.cpp:22
chi
The namespace where chigraph lives.
Definition: BitcodeParser.hpp:12
chi::NodeInstance::context
Context & context() const
Get the containing Context object.
Definition: NodeInstance.hpp:87
Context.hpp
Defines the Context class and related functions.
chi::Result
The result object, used for identifiying errors with good diagnostics.
Definition: Result.hpp:72
chi::NodeCompiler::firstBlock
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...
Definition: NodeCompiler.cpp:214
chi::NodeCompiler::jumpBackInst
llvm::IndirectBrInst & jumpBackInst() const
Get the IndirectBrInst* for the pure.
Definition: NodeCompiler.hpp:110
NodeInstance.hpp
Defines the NodeInstance class and related functions.
NodeCompiler.hpp
chi::NodeType::codegen
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.