| chigraph master Systems programming language written for beginners in LLVM

FunctionCompiler.cpp
Go to the documentation of this file.
1
3 #include "chi/FunctionCompiler.hpp"
4 #include "chi/Context.hpp"
5 #include "chi/DataType.hpp"
6 #include "chi/FunctionValidator.hpp"
7 #include "chi/GraphFunction.hpp"
8 #include "chi/GraphModule.hpp"
9 #include "chi/LLVMVersion.hpp"
10 #include "chi/LangModule.hpp"
11 #include "chi/NameMangler.hpp"
12 #include "chi/NodeInstance.hpp"
13 #include "chi/NodeType.hpp"
14 #include "chi/Support/Result.hpp"
15
16 #include <boost/bimap.hpp>
17 #include <boost/dynamic_bitset.hpp>
18 #include <boost/range/join.hpp>
19 #include <boost/uuid/uuid_io.hpp>
20
21 #include <unordered_map>
22
23 #include <llvm/IR/DIBuilder.h>
24 #include <llvm/IR/IRBuilder.h>
25 #include <llvm/IR/Module.h>
26
27 namespace fs = boost::filesystem;
28
29 namespace chi {
30
31 FunctionCompiler::FunctionCompiler(const chi::GraphFunction& func, llvm::Module& moduleToGenInto,
32 llvm::DICompileUnit& debugCU, llvm::DIBuilder& debugBuilder)
33 : mModule{&moduleToGenInto}, mDIBuilder{&debugBuilder}, mDebugCU{&debugCU}, mFunction{&func} {}
34
35 Result FunctionCompiler::initialize(bool validate) {
36 assert(initialized() == false && "Cannot initialize a FunctionCompiler more than once");
37
38 mInitialized = true;
39
40 Result res;
41 auto compilerCtx = res.addScopedContext(
42 {{"Function", function().name()}, {"Module", function().module().fullName()}});
43
44 if (validate) {
45 res += validateFunction(function());
46 if (!res) { return res; }
47 }
48
49 // get the entry node
50 auto entry = function().entryNode();
51 if (entry == nullptr) {
52 res.addEntry("EUKN", "No entry node", {});
53 return res;
54 }
55
56 // create function
57 auto mangledName = mangleFunctionName(module().fullName(), function().name());
58 mLLFunction = llvm::cast<llvm::Function>(
59 llvmModule().getOrInsertFunction(mangledName, function().functionType()));
60
61 // create the debug file
62 mDIFile = diBuilder().createFile(debugCompileUnit()->getFilename(),
63 debugCompileUnit()->getDirectory());
64
65 auto subroutineType = createSubroutineType();
66
67 mNodeLocations = module().createLineNumberAssoc();
68 auto entryLN = nodeLineNumber(*entry);
69
70 // TODO(#65): line numbers?
71 mDebugFunc =
72 diBuilder().createFunction(mDIFile, module().fullName() + ":" + function().name(),
73 mangledName, mDIFile, entryLN, subroutineType, false, true, 0,
74 #if LLVM_VERSION_LESS_EQUAL(3, 6)
75 0,
76 #else
77 llvm::DINode::DIFlags{},
78 #endif
79 false);
80
81 #if LLVM_VERSION_LESS_EQUAL(3, 6)
82 mDebugFunc.replaceFunction(mLLFunction);
83 #elif LLVM_VERSION_LESS_EQUAL(3, 7)
84 mDebugFunc->replaceFunction(mLLFunction);
85 #else
86 mLLFunction->setSubprogram(mDebugFunc);
87 #endif
88
89 mAllocBlock = llvm::BasicBlock::Create(context().llvmContext(), "alloc", mLLFunction);
90
91 // set argument names
92 auto idx = 0ull;
93 for (auto& arg : mLLFunction->
94 #if LLVM_VERSION_AT_LEAST(5, 0)
95 args()
96 #else
97 getArgumentList()
98 #endif
99 ) {
100 // the first one is the input exec ID
101 if (idx == 0) {
102 arg.setName("inputexec_id");
103
104 // create debug info
105 DataType intDataType;
106 res += context().typeFromModule("lang", "i32", &intDataType);
107 assert(intDataType.valid());
108 auto debugParam = diBuilder().
109 #if LLVM_VERSION_LESS_EQUAL(3, 7)
110 createLocalVariable(llvm::dwarf::DW_TAG_arg_variable, mDebugFunc,
111 "inputexec_id", mDIFile, entryLN,
112 #if LLVM_VERSION_LESS_EQUAL(3, 6)
113 *
114 #endif
115 intDataType.debugType());
116 #else
117
118 createParameterVariable(mDebugFunc, "inputexec_id", 1, mDIFile,
119 entryLN, intDataType.debugType());
120 #endif
121 diBuilder()
122 .insertDeclare(&arg, debugParam,
123 #if LLVM_VERSION_AT_LEAST(3, 6)
124 diBuilder().createExpression(),
125 #if LLVM_VERSION_AT_LEAST(3, 7)
126 llvm::DebugLoc::get(entryLN, 1, mDebugFunc),
127 #endif
128 #endif
129 &allocBlock())
130 #if LLVM_VERSION_LESS_EQUAL(3, 6)
131 ->setDebugLoc(llvm::DebugLoc::get(entryLN, 1, mDebugFunc))
132 #endif
133 ; // TODO(#65): "line" numbers
134
135 ++idx;
136 continue;
137 }
138
139 NamedDataType tyAndName;
140 // all the - 1's is becaues the first is the inputexec_id
141 if (idx - 1 < function().dataInputs().size()) {
142 tyAndName = function().dataInputs()[idx - 1];
143 } else {
144 tyAndName = function().dataOutputs()[idx - 1 - entry->type().dataOutputs().size()];
145 }
146 arg.setName(tyAndName.name);
147
148 // create debug info
149
150 // create DIType*
151 llvm::DIType* dType = tyAndName.type.debugType();
152 auto debugParam = diBuilder().
153 #if LLVM_VERSION_LESS_EQUAL(3, 7)
154 createLocalVariable(llvm::dwarf::DW_TAG_arg_variable, mDebugFunc,
155 tyAndName.name, mDIFile, entryLN,
156 #if LLVM_VERSION_LESS_EQUAL(3, 6)
157 *
158 #endif
159 dType);
160 #else
161 createParameterVariable(mDebugFunc, tyAndName.name,
162 idx + 1, // + 1 because it starts at 1
163 mDIFile, entryLN, dType);
164 #endif
165 diBuilder()
166 .insertDeclare(&arg, debugParam,
167 #if LLVM_VERSION_AT_LEAST(3, 6)
168 diBuilder().createExpression(),
169 #if LLVM_VERSION_AT_LEAST(3, 7)
170 llvm::DebugLoc::get(entryLN, 1, mDebugFunc),
171 #endif
172 #endif
173 &allocBlock())
174 #if LLVM_VERSION_LESS_EQUAL(3, 6)
175 ->setDebugLoc(llvm::DebugLoc::get(entryLN, 1, mDebugFunc))
176 #endif
177 ; // TODO(#65): line numbers
178
179 ++idx;
180 }
181
182 // create mPostPureBreak
183 llvm::IRBuilder<> allocBuilder{&allocBlock()};
184 mPostPureBreak = allocBuilder.CreateAlloca(
185 llvm::IntegerType::getInt8PtrTy(context().llvmContext()), nullptr, "pure_jumpback");
186
187 // alloc local variables and zero them
188 for (const auto& localVar : function().localVariables()) {
189 mLocalVariables[localVar.name] =
190 allocBuilder.CreateAlloca(localVar.type.llvmType(), nullptr, "var_" + localVar.name);
191 allocBuilder.CreateStore(llvm::Constant::getNullValue(localVar.type.llvmType()),
192 mLocalVariables[localVar.name]);
193 }
194
195 return res;
196 }
197
198 Result FunctionCompiler::compile() {
199 assert(initialized() && "You must initialize a FunctionCompiler before you compile it");
200 assert(compiled() == false && "You cannot compile a FunctionCompiler twice");
201
202 // compile the entry
203 auto entry = function().entryNode();
204 assert(entry != nullptr);
205
206 std::deque<std::pair<NodeInstance*, size_t>> nodesToCompile;
207 nodesToCompile.emplace_back(entry, 0);
208
209 Result res;
210
211 auto compilePureDependencies = [this](NodeInstance& node) {
212 Result res;
213
214 auto depPures = dependentPuresRecursive(node);
215 for (auto pure : depPures) {
216 auto compiler = getOrCreateNodeCompiler(*pure);
217 res += compiler->compile_stage2({}, 0);
218
219 if (!res) { return res; }
220 }
221 return res;
222 };
223
224 while (!nodesToCompile.empty()) {
225 auto& node = *nodesToCompile[0].first;
226 auto inputExecID = nodesToCompile[0].second;
227
228 assert(!node.type().pure());
229
230 auto compiler = getOrCreateNodeCompiler(node);
231 if (compiler->compiled(inputExecID)) {
232 nodesToCompile.pop_front();
233
234 continue;
235 }
236
237 // compile dependent pures
238 res += compilePureDependencies(node);
239 if (!res) { return res; }
240
241 std::vector<llvm::BasicBlock*> outputBlocks;
242 // make sure the output nodes have done stage 1 and collect output blocks
243 for (const auto& conn : node.outputExecConnections) {
244 res += compilePureDependencies(*conn.first);
245 if (!res) { return res; }
246
247 auto depCompiler = getOrCreateNodeCompiler(*conn.first);
248 depCompiler->compile_stage1(conn.second);
249
250 outputBlocks.push_back(&depCompiler->firstBlock(conn.second));
251 }
252
253 // compile this one
254 res += compiler->compile_stage2(outputBlocks, inputExecID);
255 if (!res) { return res; }
256
257 // recurse
258 for (const auto& conn : node.outputExecConnections) {
259 // add them to the end
260 nodesToCompile.emplace_back(conn.first, conn.second);
261 }
262
263 // pop it off
264 nodesToCompile.pop_front();
265 }
266
267 if (!res) { return res; }
268
269 llvm::IRBuilder<> allocBuilder{&allocBlock()};
270 allocBuilder.CreateBr(&nodeCompiler(*entry)->firstBlock(0));
271
272 return res;
273 }
274
275 FunctionCompiler::DebugFunctionType FunctionCompiler::createSubroutineType() {
276 // create param list
277 std::vector<
278 #if LLVM_VERSION_LESS_EQUAL(3, 5)
279 llvm::Value*
280 #else
281 llvm::Metadata*
282 #endif
283 >
284 params;
285 {
286 // ret first
287 DataType intType = function().context().langModule()->typeFromName("i32");
288 assert(intType.valid());
289
290 params.push_back(
291 #if LLVM_VERSION_LESS_EQUAL(3, 6)
292 *
293 #endif
294 intType.debugType());
295
296 // then first in inputexec id
297 params.push_back(
298 #if LLVM_VERSION_LESS_EQUAL(3, 6)
299 *
300 #endif
301 intType.debugType());
302
303 // add paramters
304 for (const auto& dType :
305 boost::range::join(function().dataInputs(), function().dataOutputs())) {
306 params.push_back(
307 #if LLVM_VERSION_LESS_EQUAL(3, 6)
308 *
309 #endif
310 dType.type.debugType());
311 }
312 }
313
314 // create type
315 auto subroutineType = diBuilder().createSubroutineType(
316 #if LLVM_VERSION_LESS_EQUAL(3, 7)
317 mDIFile,
318 #endif
319 diBuilder().
320 #if LLVM_VERSION_LESS_EQUAL(3, 5)
321 getOrCreateArray
322 #else
323 getOrCreateTypeArray
324 #endif
325 (params));
326
327 return subroutineType;
328 }
329
330 llvm::Value* FunctionCompiler::localVariable(boost::string_view name) {
331 assert(initialized() &&
332 "Please initialize the function compiler before getting a local variable");
333
334 auto iter = mLocalVariables.find(name.to_string());
335 if (iter != mLocalVariables.end()) { return iter->second; }
336 return nullptr;
337 }
338
339 GraphModule& FunctionCompiler::module() const { return function().module(); }
340 Context& FunctionCompiler::context() const { return function().context(); }
341
342 int FunctionCompiler::nodeLineNumber(NodeInstance& node) {
343 assert(&node.function() == &function() &&
344 "Cannot get node line number for a node not in the function");
345
346 auto iter = mNodeLocations.right.find(&node);
347 if (iter == mNodeLocations.right.end()) {
348 return -1; // ?
349 }
350 return iter->second;
351 }
352
353 NodeCompiler* FunctionCompiler::nodeCompiler(NodeInstance& node) {
354 assert(&node.function() == &function() &&
355 "Cannot get a NodeCompiler for a node instance not in this function");
356
357 auto iter = mNodeCompilers.find(&node);
358 if (iter != mNodeCompilers.end()) { return &iter->second; }
359 return nullptr;
360 }
361
362 NodeCompiler* FunctionCompiler::getOrCreateNodeCompiler(NodeInstance& node) {
363 assert(&node.function() == &function() &&
364 "Cannot get a NodeCompiler for a node instance not in this function");
365
366 auto iter = mNodeCompilers.find(&node);
367 if (iter != mNodeCompilers.end()) { return &iter->second; }
368 return &mNodeCompilers.emplace(&node, NodeCompiler{*this, node}).first->second;
369 }
370
371 Result compileFunction(const GraphFunction& func, llvm::Module* mod, llvm::DICompileUnit* debugCU,
372 llvm::DIBuilder& debugBuilder) {
373 FunctionCompiler compiler{func, *mod, *debugCU, debugBuilder};
374
375 auto res = compiler.initialize();
376 if (!res) { return res; }
377
378 res += compiler.compile();
379
380 return res;
381 }
382
383 } // namespace chi
LLVMVersion.hpp
chi::mangleFunctionName
std::string mangleFunctionName(std::string fullModuleName, const std::string &name)
Mangle a function name.
Definition: NameMangler.cpp:9
chi::Context::langModule
LangModule * langModule() const
Get the LangModule, if it has been loaded.
Definition: Context.hpp:177
FunctionValidator.hpp
Defines functions for validating GraphFunction objects.
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::FunctionCompiler::nodeCompiler
NodeCompiler * nodeCompiler(NodeInstance &node)
Get a node compile for a certain node.
Definition: FunctionCompiler.cpp:353
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
chi::FunctionCompiler::llvmModule
llvm::Module & llvmModule() const
Get the module being generated.
Definition: FunctionCompiler.hpp:81
chi::NamedDataType::type
DataType type
The type.
Definition: DataType.hpp:86
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::validateFunction
Result validateFunction(const GraphFunction &func)
Validate that a function is compilable.
Definition: FunctionValidator.cpp:15
chi::NodeInstance
An instance of a node.
Definition: NodeInstance.hpp:19
FunctionCompiler.hpp
Defines functions for compiling GraphFunction objects.
NameMangler.hpp
Definitions for mangling functions.
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::NamedDataType
Basicaly a std::pair<std::string, DataType>, except it has nicer names.
Definition: DataType.hpp:72
chi::NodeCompiler
Helper to compile nodes.
Definition: NodeCompiler.hpp:24
chi::FunctionCompiler::compile
Result compile()
Generates the contents of the function.
Definition: FunctionCompiler.cpp:198
chi::ChiModule::fullName
std::string fullName() const
Get the full name of the module.
Definition: ChiModule.hpp:61
chi::Result::addScopedContext
ScopedContext addScopedContext(const nlohmann::json &data)
Add a context with a scope Example usage: chi::Result res; res.contextJson(); // returns {} { aut...
Definition: Result.hpp:123
chi::FunctionCompiler::debugCompileUnit
llvm::DICompileUnit * debugCompileUnit() const
The compile unit for the module.
Definition: FunctionCompiler.hpp:89
chi::Context
The class that handles the loading, creation, storing, and compilation of modules It also stores a LL...
Definition: Context.hpp:55
chi::FunctionCompiler
Class for compiling GraphFunctions into llvm::Functions.
Definition: FunctionCompiler.hpp:24
chi::FunctionCompiler::nodeLineNumber
int nodeLineNumber(NodeInstance &node)
Get the debug line number for the node instance Unique for each node.
Definition: FunctionCompiler.cpp:342
chi::FunctionCompiler::allocBlock
llvm::BasicBlock & allocBlock() const
The block for allocating variables at the beginning of the function.
Definition: FunctionCompiler.hpp:94
chi::FunctionCompiler::getOrCreateNodeCompiler
NodeCompiler * getOrCreateNodeCompiler(NodeInstance &node)
Get or create a node compiler for a node.
Definition: FunctionCompiler.cpp:362
chi::NodeInstance::function
GraphFunction & function() const
Get the containing GraphFunction.
Definition: NodeInstance.hpp:91
chi::FunctionCompiler::compiled
bool compiled() const
Get if the function has been compiled (compile() has been called)
Definition: FunctionCompiler.hpp:148
chi::FunctionCompiler::FunctionCompiler
FunctionCompiler(const GraphFunction &func, llvm::Module &moduleToGenInto, llvm::DICompileUnit &debugCU, llvm::DIBuilder &debugBuilder)
Constructor.
Definition: FunctionCompiler.cpp:31
chi::LangModule::typeFromName
DataType typeFromName(boost::string_view name) override
Get a DataType from the name.
Definition: LangModule.cpp:871
NodeType.hpp
Defines the NodeType class.
LangModule.hpp
Defines the LangModule class.
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
DataType.hpp
Defines the DataType class.
chi::DataType::debugType
llvm::DIType * debugType() const
Get the debug type.
Definition: DataType.hpp:41
chi::FunctionCompiler::localVariable
llvm::Value * localVariable(boost::string_view name)
Get the value for a local variable.
Definition: FunctionCompiler.cpp:330
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
chi::FunctionCompiler::context
Context & context() const
Get function().context()
Definition: FunctionCompiler.cpp:340
chi::DataType::valid
bool valid() const
Check if the DataType is valid (if it&#39;s actually bound to a type and module)
Definition: DataType.hpp:44
chi::FunctionCompiler::diBuilder
llvm::DIBuilder & diBuilder() const
The debug builder we&#39;re using for the module.
Definition: FunctionCompiler.hpp:85
chi::NamedDataType::name
std::string name
The name.
Definition: DataType.hpp:83
chi::GraphModule
Module that holds graph functions.
Definition: GraphModule.hpp:16
GraphModule.hpp
Defines the GraphModule class.
chi
The namespace where chigraph lives.
Definition: BitcodeParser.hpp:12
chi::FunctionCompiler::initialize
Result initialize(bool validate=true)
Creates the function, but don&#39;t actually generate into it.
Definition: FunctionCompiler.cpp:35
chi::FunctionCompiler::initialized
bool initialized() const
Get if the function is initialized (initialize() has been called)
Definition: FunctionCompiler.hpp:144
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::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
GraphFunction.hpp
Declares the GraphFunction class.
NodeInstance.hpp
Defines the NodeInstance class and related functions.
chi::FunctionCompiler::module
GraphModule & module() const
Get function().module()
Definition: FunctionCompiler.cpp:339
chi::FunctionCompiler::createSubroutineType
DebugFunctionType createSubroutineType()
Create the subroutine type for the function.
Definition: FunctionCompiler.cpp:275