chigraph  master
Systems programming language written for beginners in LLVM
FunctionCompiler.cpp
Go to the documentation of this file.
1 
4 #include "chi/Context.hpp"
5 #include "chi/DataType.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 
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 
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 
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 
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 
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
std::string mangleFunctionName(std::string fullModuleName, const std::string &name)
Mangle a function name.
Definition: NameMangler.cpp:9
LangModule * langModule() const
Get the LangModule, if it has been loaded.
Definition: Context.hpp:177
Defines functions for validating GraphFunction objects.
this is an AST-like representation of a function in a graph It is used for IDE-like behavior...
NodeCompiler * nodeCompiler(NodeInstance &node)
Get a node compile for a certain node.
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
llvm::Module & llvmModule() const
Get the module being generated.
DataType type
The type.
Definition: DataType.hpp:86
boost::bimap< unsigned, NodeInstance * > createLineNumberAssoc() const
Create the associations from line number and function in debug info.
Defines the Result class and related functions.
Result validateFunction(const GraphFunction &func)
Validate that a function is compilable.
An instance of a node.
Defines functions for compiling GraphFunction objects.
Definitions for mangling functions.
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...
Basicaly a std::pair<std::string, DataType>, except it has nicer names.
Definition: DataType.hpp:72
Helper to compile nodes.
Result compile()
Generates the contents of the function.
std::string fullName() const
Get the full name of the module.
Definition: ChiModule.hpp:61
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
llvm::DICompileUnit * debugCompileUnit() const
The compile unit for the module.
The class that handles the loading, creation, storing, and compilation of modules It also stores a LL...
Definition: Context.hpp:55
Class for compiling GraphFunctions into llvm::Functions.
int nodeLineNumber(NodeInstance &node)
Get the debug line number for the node instance Unique for each node.
llvm::BasicBlock & allocBlock() const
The block for allocating variables at the beginning of the function.
NodeCompiler * getOrCreateNodeCompiler(NodeInstance &node)
Get or create a node compiler for a node.
GraphFunction & function() const
Get the containing GraphFunction.
bool compiled() const
Get if the function has been compiled (compile() has been called)
FunctionCompiler(const GraphFunction &func, llvm::Module &moduleToGenInto, llvm::DICompileUnit &debugCU, llvm::DIBuilder &debugBuilder)
Constructor.
DataType typeFromName(boost::string_view name) override
Get a DataType from the name.
Definition: LangModule.cpp:871
Defines the NodeType class.
Defines the LangModule class.
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) ...
Defines the DataType class.
llvm::DIType * debugType() const
Get the debug type.
Definition: DataType.hpp:41
llvm::Value * localVariable(boost::string_view name)
Get the value for a local variable.
Result typeFromModule(const boost::filesystem::path &module, boost::string_view name, DataType *toFill) noexcept
Gets a DataType from a module.
Definition: Context.cpp:232
Context & context() const
Get function().context()
bool valid() const
Check if the DataType is valid (if it&#39;s actually bound to a type and module)
Definition: DataType.hpp:44
llvm::DIBuilder & diBuilder() const
The debug builder we&#39;re using for the module.
std::string name
The name.
Definition: DataType.hpp:83
Module that holds graph functions.
Definition: GraphModule.hpp:16
Defines the GraphModule class.
The namespace where chigraph lives.
Result initialize(bool validate=true)
Creates the function, but don&#39;t actually generate into it.
bool initialized() const
Get if the function is initialized (initialize() has been called)
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
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...
Declares the GraphFunction class.
Defines the NodeInstance class and related functions.
GraphModule & module() const
Get function().module()
DebugFunctionType createSubroutineType()
Create the subroutine type for the function.