16 #include <boost/bimap.hpp> 17 #include <boost/dynamic_bitset.hpp> 18 #include <boost/range/join.hpp> 19 #include <boost/uuid/uuid_io.hpp> 21 #include <unordered_map> 23 #include <llvm/IR/DIBuilder.h> 24 #include <llvm/IR/IRBuilder.h> 25 #include <llvm/IR/Module.h> 27 namespace fs = boost::filesystem;
32 llvm::DICompileUnit& debugCU, llvm::DIBuilder& debugBuilder)
33 : mModule{&moduleToGenInto}, mDIBuilder{&debugBuilder}, mDebugCU{&debugCU}, mFunction{&func} {}
36 assert(
initialized() ==
false &&
"Cannot initialize a FunctionCompiler more than once");
42 {{
"Function",
function().name()}, {
"Module",
function().
module().
fullName()}});
46 if (!res) {
return res; }
50 auto entry =
function().entryNode();
51 if (entry ==
nullptr) {
52 res.
addEntry(
"EUKN",
"No entry node", {});
58 mLLFunction = llvm::cast<llvm::Function>(
59 llvmModule().getOrInsertFunction(mangledName,
function().functionType()));
72 diBuilder().createFunction(mDIFile,
module().fullName() +
":" +
function().name(),
73 mangledName, mDIFile, entryLN, subroutineType,
false,
true, 0,
74 #
if LLVM_VERSION_LESS_EQUAL(3, 6)
77 llvm::DINode::DIFlags{},
81 #if LLVM_VERSION_LESS_EQUAL(3, 6) 82 mDebugFunc.replaceFunction(mLLFunction);
83 #elif LLVM_VERSION_LESS_EQUAL(3, 7) 84 mDebugFunc->replaceFunction(mLLFunction);
86 mLLFunction->setSubprogram(mDebugFunc);
89 mAllocBlock = llvm::BasicBlock::Create(
context().llvmContext(),
"alloc", mLLFunction);
93 for (
auto& arg : mLLFunction->
94 #
if LLVM_VERSION_AT_LEAST(5, 0)
102 arg.setName(
"inputexec_id");
107 assert(intDataType.
valid());
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)
118 createParameterVariable(mDebugFunc,
"inputexec_id", 1, mDIFile,
122 .insertDeclare(&arg, debugParam,
123 #
if LLVM_VERSION_AT_LEAST(3, 6)
125 #
if LLVM_VERSION_AT_LEAST(3, 7)
126 llvm::DebugLoc::get(entryLN, 1, mDebugFunc),
130 #if LLVM_VERSION_LESS_EQUAL(3, 6) 131 ->setDebugLoc(llvm::DebugLoc::get(entryLN, 1, mDebugFunc))
141 if (idx - 1 <
function().dataInputs().size()) {
142 tyAndName =
function().dataInputs()[idx - 1];
144 tyAndName =
function().dataOutputs()[idx - 1 - entry->type().dataOutputs().size()];
146 arg.setName(tyAndName.
name);
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)
161 createParameterVariable(mDebugFunc, tyAndName.
name,
163 mDIFile, entryLN, dType);
166 .insertDeclare(&arg, debugParam,
167 #
if LLVM_VERSION_AT_LEAST(3, 6)
169 #
if LLVM_VERSION_AT_LEAST(3, 7)
170 llvm::DebugLoc::get(entryLN, 1, mDebugFunc),
174 #if LLVM_VERSION_LESS_EQUAL(3, 6) 175 ->setDebugLoc(llvm::DebugLoc::get(entryLN, 1, mDebugFunc))
183 llvm::IRBuilder<> allocBuilder{&
allocBlock()};
184 mPostPureBreak = allocBuilder.CreateAlloca(
185 llvm::IntegerType::getInt8PtrTy(
context().llvmContext()),
nullptr,
"pure_jumpback");
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]);
199 assert(
initialized() &&
"You must initialize a FunctionCompiler before you compile it");
200 assert(
compiled() ==
false &&
"You cannot compile a FunctionCompiler twice");
203 auto entry =
function().entryNode();
204 assert(entry !=
nullptr);
206 std::deque<std::pair<NodeInstance*, size_t>> nodesToCompile;
207 nodesToCompile.emplace_back(entry, 0);
211 auto compilePureDependencies = [
this](
NodeInstance& node) {
215 for (
auto pure : depPures) {
217 res += compiler->compile_stage2({}, 0);
219 if (!res) {
return res; }
224 while (!nodesToCompile.empty()) {
225 auto& node = *nodesToCompile[0].first;
226 auto inputExecID = nodesToCompile[0].second;
228 assert(!node.type().pure());
231 if (compiler->compiled(inputExecID)) {
232 nodesToCompile.pop_front();
238 res += compilePureDependencies(node);
239 if (!res) {
return res; }
241 std::vector<llvm::BasicBlock*> outputBlocks;
243 for (
const auto& conn : node.outputExecConnections) {
244 res += compilePureDependencies(*conn.first);
245 if (!res) {
return res; }
248 depCompiler->compile_stage1(conn.second);
250 outputBlocks.push_back(&depCompiler->firstBlock(conn.second));
254 res += compiler->compile_stage2(outputBlocks, inputExecID);
255 if (!res) {
return res; }
258 for (
const auto& conn : node.outputExecConnections) {
260 nodesToCompile.emplace_back(conn.first, conn.second);
264 nodesToCompile.pop_front();
267 if (!res) {
return res; }
269 llvm::IRBuilder<> allocBuilder{&
allocBlock()};
278 #if LLVM_VERSION_LESS_EQUAL(3, 5) 288 assert(intType.
valid());
291 #
if LLVM_VERSION_LESS_EQUAL(3, 6)
298 #
if LLVM_VERSION_LESS_EQUAL(3, 6)
304 for (
const auto& dType :
305 boost::range::join(
function().dataInputs(),
function().dataOutputs())) {
307 #
if LLVM_VERSION_LESS_EQUAL(3, 6)
310 dType.type.debugType());
315 auto subroutineType =
diBuilder().createSubroutineType(
316 #
if LLVM_VERSION_LESS_EQUAL(3, 7)
320 #
if LLVM_VERSION_LESS_EQUAL(3, 5)
327 return subroutineType;
332 "Please initialize the function compiler before getting a local variable");
334 auto iter = mLocalVariables.find(name.to_string());
335 if (iter != mLocalVariables.end()) {
return iter->second; }
343 assert(&node.
function() == &
function() &&
344 "Cannot get node line number for a node not in the function");
346 auto iter = mNodeLocations.right.find(&node);
347 if (iter == mNodeLocations.right.end()) {
354 assert(&node.
function() == &
function() &&
355 "Cannot get a NodeCompiler for a node instance not in this function");
357 auto iter = mNodeCompilers.find(&node);
358 if (iter != mNodeCompilers.end()) {
return &iter->second; }
363 assert(&node.
function() == &
function() &&
364 "Cannot get a NodeCompiler for a node instance not in this function");
366 auto iter = mNodeCompilers.find(&node);
367 if (iter != mNodeCompilers.end()) {
return &iter->second; }
368 return &mNodeCompilers.emplace(&node,
NodeCompiler{*
this, node}).first->second;
372 llvm::DIBuilder& debugBuilder) {
375 auto res = compiler.initialize();
376 if (!res) {
return res; }
378 res += compiler.compile();
std::string mangleFunctionName(std::string fullModuleName, const std::string &name)
Mangle a function name.
LangModule * langModule() const
Get the LangModule, if it has been loaded.
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.
llvm::Module & llvmModule() const
Get the module being generated.
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.
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's fetched recursively) T...
Basicaly a std::pair<std::string, DataType>, except it has nicer names.
Result compile()
Generates the contents of the function.
std::string fullName() const
Get the full name of the module.
ScopedContext addScopedContext(const nlohmann::json &data)
Add a context with a scope Example usage: chi::Result res; res.contextJson(); // returns {} { aut...
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...
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.
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.
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.
Context & context() const
Get function().context()
bool valid() const
Check if the DataType is valid (if it's actually bound to a type and module)
llvm::DIBuilder & diBuilder() const
The debug builder we're using for the module.
std::string name
The name.
Module that holds graph functions.
Defines the GraphModule class.
The namespace where chigraph lives.
Result initialize(bool validate=true)
Creates the function, but don'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's in and it embed...
The result object, used for identifiying errors with good diagnostics.
llvm::BasicBlock & firstBlock(size_t inputExecID) const
Get the first block to jump to for the node If there are dependent pures, it'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.