5 #include "chi/DefaultModuleCache.hpp" 17 #include <llvm/ExecutionEngine/ExecutionEngine.h> 18 #include <llvm/ExecutionEngine/GenericValue.h> 19 #include <llvm/ExecutionEngine/SectionMemoryManager.h> 20 #include <llvm/IR/Module.h> 21 #include <llvm/IR/Type.h> 22 #include <llvm/IR/Verifier.h> 23 #include <llvm/Linker/Linker.h> 24 #include <llvm/Support/TargetRegistry.h> 25 #include <llvm/Support/TargetSelect.h> 26 #include <llvm/Support/raw_ostream.h> 27 #include <llvm/Target/TargetMachine.h> 28 #include <llvm/Target/TargetOptions.h> 30 #include <boost/algorithm/string/replace.hpp> 31 #include <boost/filesystem.hpp> 32 #include <boost/range.hpp> 35 #include <unordered_set> 37 namespace fs = boost::filesystem;
43 mModuleCache = std::make_unique<DefaultModuleCache>(*this);
49 for (
auto& module : mModules) {
50 if (module->fullName() == fullModuleName) {
return module.get(); }
59 auto uMod = std::make_unique<GraphModule>(*
this, fullName);
69 std::vector<std::string> moduleList;
73 if (!fs::is_directory(srcDir)) {
return {}; }
75 for (
const auto& dirEntry : boost::make_iterator_range(
76 fs::recursive_directory_iterator{srcDir, fs::symlink_option::recurse}, {})) {
77 const fs::path& p = dirEntry;
80 if (fs::is_regular_file(p) && p.extension() ==
".chimod") {
81 fs::path relPath = fs::relative(p, srcDir);
83 relPath.replace_extension(
"");
84 moduleList.emplace_back(relPath.string());
92 assert(!name.empty() &&
"Name should not be empty when calling chi::Context::loadModule");
96 auto requestedModCtx = res.
addScopedContext({{
"Requested Module Name", name.generic_string()}});
101 if (toFill !=
nullptr) { *toFill =
langModule(); }
104 auto mod = std::make_unique<LangModule>(*this);
105 if (toFill !=
nullptr) { *toFill = mod.get(); }
113 if (mod !=
nullptr) {
114 if (toFill !=
nullptr) { *toFill = mod; }
120 res.addEntry(
"E52",
"Cannot load module without a workspace path", {});
126 fullPath.replace_extension(
".chimod");
128 if (!fs::is_regular_file(fullPath)) {
129 res.addEntry(
"EUKN",
"Failed to find module",
131 {
"Expected Path", fullPath.generic_string()}});
136 nlohmann::json readJson = {};
138 fs::ifstream inFile{fullPath};
141 }
catch (std::exception& e) {
142 res.addEntry(
"EUKN",
"Failed to parse json", {{
"Error", e.what()}});
148 if (toFill !=
nullptr) { *toFill = toFillJson; }
160 auto scopedCtx = res.
addScopedContext({{
"Requested Module Name", fullName.string()}});
165 if (mod !=
nullptr) {
166 if (toFill !=
nullptr) {
168 if (casted !=
nullptr) { *toFill = casted; }
177 if (toFill !=
nullptr) { *toFill = jMod; }
186 assert(modToAdd !=
nullptr);
190 if (ptr !=
nullptr) {
return false; }
192 if (modToAdd->fullName() ==
"lang") { mLangModule =
dynamic_cast<LangModule*
>(modToAdd.get()); }
195 for (
const auto& tyName : modToAdd->nodeTypeNames()) {
197 std::unique_ptr<NodeType> ty;
205 if (!ty->converter()) {
210 mTypeConverters[ty->dataInputs()[0].type.qualifiedName()][ty->dataOutputs()[0].type.qualifiedName()] = std::move(ty);
213 mModules.push_back(std::move(modToAdd));
221 for (
auto idx = 0ull; idx < mModules.size(); ++idx) {
222 if (mModules[idx]->fullName() == fullName) {
223 mModules.erase(mModules.begin() + idx);
234 assert(toFill !=
nullptr);
239 if (mod ==
nullptr) {
240 res.
addEntry(
"E36",
"Could not find module", {{
"module", module.generic_string()}});
245 if (!toFill->valid()) {
246 res.
addEntry(
"E37",
"Could not find type in module",
247 {{
"type", name.to_string()}, {
"module", module.generic_string()}});
254 const nlohmann::json& data,
255 std::unique_ptr<NodeType>* toFill) noexcept {
259 if (module ==
nullptr) {
260 res.
addEntry(
"E36",
"Could not find module", {{
"module", moduleName.generic_string()}});
264 res += module->nodeTypeFromName(typeName, data, toFill);
270 auto fromIter = mTypeConverters.find(fromType.
qualifiedName());
271 if (fromIter == mTypeConverters.end()) {
276 if (toIter == fromIter->second.end()) {
280 return toIter->second->clone();
286 std::unique_ptr<llvm::Module>* toFill) {
290 if (mod ==
nullptr) {
291 res.
addEntry(
"E36",
"Could not find module", {{
"module", fullName.generic_string()}});
299 std::unique_ptr<llvm::Module>* toFill) {
300 assert(toFill !=
nullptr);
307 std::unique_ptr<llvm::Module> llmod;
320 std::unordered_set<fs::path> added(mod.
dependencies().begin(),
322 std::deque<fs::path> depsToAdd(mod.
dependencies().begin(),
324 while (!depsToAdd.empty()) {
325 auto& depName = depsToAdd[0];
328 if (depMod ==
nullptr) {
329 res.
addEntry(
"E36",
"Could not find module",
330 {{
"module", depName.generic_string()}});
334 res += depMod->addForwardDeclarations(*llmod);
335 if (!res) {
return res; }
337 for (
const auto& depOfDep : depMod->dependencies()) {
338 if (added.find(depOfDep) == added.end()) {
339 depsToAdd.push_back(depOfDep);
340 added.insert(depOfDep);
343 depsToAdd.pop_front();
350 if (llmod->getModuleFlag(
"Debug Info Version") ==
nullptr) {
351 llmod->addModuleFlag(llvm::Module::Warning,
"Debug Info Version",
352 llvm::DEBUG_METADATA_VERSION);
358 if (!res) {
return res; }
366 llvm::raw_string_ostream os(err);
367 errored = llvm::verifyModule(*llmod, &os);
372 std::string moduleStr;
374 llvm::raw_string_ostream printerStr{moduleStr};
375 llmod->print(printerStr,
nullptr);
378 res.
addEntry(
"EINT",
"Internal compiler error: Invalid module created",
379 {{
"Error", err}, {
"Full Name", mod.
fullName()}, {
"Module", moduleStr}});
389 std::unique_ptr<llvm::Module> compiledDep;
393 if (!res) {
return res; }
396 #if LLVM_VERSION_LESS_EQUAL(3, 7) 397 llvm::Linker::LinkModules(llmod.get(), compiledDep.get()
398 #if LLVM_VERSION_LESS_EQUAL(3, 5) 400 llvm::Linker::DestroySource,
nullptr 404 llvm::Linker::linkModules(*llmod, std::move(compiledDep));
412 executablePath().parent_path().parent_path() /
"lib" /
"chigraph" /
"runtime.bc";
414 if (!fs::is_regular_file(runtimebc)) {
416 "EUKN",
"Failed to find runtime.bc in lib/chigraph/runtime.bc",
417 {{
"Install prefix",
executablePath().parent_path().parent_path().string()}});
421 std::unique_ptr<llvm::Module> runtimeMod;
423 if (!res) {
return res; }
426 #if LLVM_VERSION_LESS_EQUAL(3, 7) 427 llvm::Linker::LinkModules(llmod.get(), runtimeMod.get()
428 #if LLVM_VERSION_LESS_EQUAL(3, 5) 430 llvm::Linker::DestroySource,
nullptr 434 llvm::Linker::linkModules(*llmod, std::move(runtimeMod));
439 *toFill = std::move(llmod);
445 boost::string_view typeName)
const {
446 std::vector<NodeInstance*> ret;
448 for (
const auto& module : mModules) {
450 auto castedMod =
dynamic_cast<GraphModule*
>(module.get());
451 if (castedMod ==
nullptr) {
continue; }
453 for (
const auto& func : castedMod->functions()) {
454 auto vec = func->nodesWithType(moduleName, typeName);
455 std::copy(vec.begin(), vec.end(), std::back_inserter(ret));
467 while (!ret.empty() && !fs::is_regular_file(ret /
".chigraphworkspace")) {
468 ret = ret.parent_path();
475 assert(ty !=
nullptr);
479 llvm::raw_string_ostream stream{data};
487 std::unique_ptr<llvm::ExecutionEngine> createEE(std::unique_ptr<llvm::Module> mod,
488 llvm::CodeGenOpt::Level optLevel,
489 std::string& errMsg) {
490 llvm::InitializeNativeTarget();
491 llvm::InitializeNativeTargetAsmPrinter();
492 llvm::InitializeNativeTargetAsmParser();
494 llvm::EngineBuilder EEBuilder(
495 #
if LLVM_VERSION_LESS_EQUAL(3, 5)
503 EEBuilder.setEngineKind(llvm::EngineKind::JIT);
506 EEBuilder.setVerifyModules(
true);
509 EEBuilder.setOptLevel(optLevel);
511 EEBuilder.setErrorStr(&errMsg);
513 #if LLVM_VERSION_LESS_EQUAL(3, 5) 514 EEBuilder.setUseMCJIT(
true);
517 EEBuilder.setMCJITMemoryManager(
518 #
if LLVM_VERSION_AT_LEAST(3, 6)
519 std::unique_ptr<llvm::SectionMemoryManager>
521 (
new llvm::SectionMemoryManager()));
523 return std::unique_ptr<llvm::ExecutionEngine>(EEBuilder.create());
529 const std::vector<llvm::GenericValue>& args, llvm::Function* funcToRun,
530 llvm::GenericValue* ret) {
535 if (funcToRun ==
nullptr) {
536 funcToRun = mod->getFunction(
"main");
538 if (funcToRun ==
nullptr) {
539 res.
addEntry(
"EUKN",
"Failed to find main function in module",
540 {{
"Module Name", mod->getModuleIdentifier()}});
546 auto EE = createEE(std::move(mod), optLevel, errMsg);
547 EE->finalizeObject();
548 EE->runStaticConstructorsDestructors(
false);
551 res.
addEntry(
"EINT",
"Failed to create an LLVM ExecutionEngine", {{
"Error", errMsg}});
555 auto returnValue = EE->runFunction(funcToRun, args);
557 EE->runStaticConstructorsDestructors(
true);
559 if (ret !=
nullptr) { *ret = returnValue; }
565 const std::vector<std::string>& args, llvm::Function* funcToRun,
571 if (funcToRun ==
nullptr) {
572 funcToRun = mod->getFunction(
"main");
574 if (funcToRun ==
nullptr) {
575 res.
addEntry(
"EUKN",
"Failed to find main function in module",
576 {{
"Module Name", mod->getModuleIdentifier()}});
582 auto EE = createEE(std::move(mod), optLevel, errMsg);
583 EE->finalizeObject();
584 EE->runStaticConstructorsDestructors(
false);
587 res.
addEntry(
"EINT",
"Failed to create an LLVM ExecutionEngine", {{
"Error", errMsg}});
591 auto returnValue = EE->runFunctionAsMain(funcToRun, args,
nullptr);
593 EE->runStaticConstructorsDestructors(
true);
595 if (ret !=
nullptr) { *ret = returnValue; }
600 assert(newCache !=
nullptr &&
"Cannot set the modulecache to be nullptr");
602 mModuleCache = std::move(newCache);
Result parseBitcodeFile(const boost::filesystem::path &file, llvm::LLVMContext &ctx, std::unique_ptr< llvm::Module > *toFill)
Parse a bitcode file.
Result nodeTypeFromName(boost::string_view name, const nlohmann::json &jsonData, std::unique_ptr< NodeType > *toFill) override
Create a node type that is in the module from the name and json.
LangModule * langModule() const
Get the LangModule, if it has been loaded.
ChiModule * moduleByFullName(const boost::filesystem::path &fullModuleName) const noexcept
Gets the module by the full name.
boost::filesystem::path executablePath()
Get the path of the executable.
virtual Result generateModule(llvm::Module &module)=0
Generate a llvm::Module from the module.
void addEntry(const char *ec, const char *overview, nlohmann::json data)
Add a entry to the result, either a warning or an error.
Result nodeTypeFromModule(const boost::filesystem::path &moduleName, boost::string_view typeName, const nlohmann::json &data, std::unique_ptr< NodeType > *toFill) noexcept
Gets a NodeType from the JSON and name.
bool unloadModule(const boost::filesystem::path &fullName)
Unloads a module.
The module that provides built-in operations like literals, math operations, etc. ...
Defines the Result class and related functions.
bool addModule(std::unique_ptr< ChiModule > modToAdd) noexcept
Adds a custom module to the Context.
std::vector< std::string > listModulesInWorkspace() const noexcept
Get the list of modules in the workspace.
Result loadModule(const boost::filesystem::path &name, ChiModule **toFill=nullptr)
Load a module from disk, also loads dependencies.
std::vector< NodeInstance * > findInstancesOfType(const boost::filesystem::path &moduleName, boost::string_view typeName) const
Find all uses of a node type in all the loaded modules.
boost::filesystem::path workspacePath() const
Get the workspace path of the Context.
Context(const boost::filesystem::path &workPath={})
Creates a context with just the lang module.
Result jsonToGraphModule(Context &createInside, const nlohmann::json &input, const boost::filesystem::path &fullName, GraphModule **toFill=nullptr)
Load a GraphModule from json.
boost::filesystem::path workspaceFromChildPath(const boost::filesystem::path &path)
Get the workspace directory from a child of the workspace directory Example: say you have a workspace...
virtual Result cacheModule(const boost::filesystem::path &moduleName, llvm::Module &compiledModule, std::time_t timeAtFileRead)=0
Cache a module.
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...
virtual std::unique_ptr< llvm::Module > retrieveFromCache(const boost::filesystem::path &moduleName, std::time_t atLeastThisNew)=0
Retrieve a module from the cache.
An abstract class that represents a module of code in Chigraph Can be compiled to a llvm::Module...
Result interpretLLVMIRAsMain(std::unique_ptr< llvm::Module > mod, llvm::CodeGenOpt::Level optLevel=llvm::CodeGenOpt::Default, const std::vector< std::string > &args={}, llvm::Function *funcToRun=nullptr, int *ret=nullptr)
Interpret LLVM IR as if it were the main function.
std::unique_ptr< NodeType > createConverterNodeType(const DataType &fromType, const DataType &toType)
Create a converter node.
Result interpretLLVMIR(std::unique_ptr< llvm::Module > mod, llvm::CodeGenOpt::Level optLevel=llvm::CodeGenOpt::Default, const std::vector< llvm::GenericValue > &args={}, llvm::Function *funcToRun=nullptr, llvm::GenericValue *ret=nullptr)
Interpret LLVM IR, just a convenience function.
A template class for type-safe flags.
Defines the NodeType class.
llvm::LLVMContext & llvmContext()
Get the LLVMContext
std::string qualifiedName() const
Get the qualified name of the type (module().fullName() + ":" name())
const ModuleCache & moduleCache() const
Get the module cache.
const std::set< boost::filesystem::path > & dependencies() const
Get the dependencies.
Defines the LangModule class.
void updateLastEditTime(std::time_t newLastEditTime=std::time(nullptr))
Update the last edit time, signifying that it's been edited.
Result typeFromModule(const boost::filesystem::path &module, boost::string_view name, DataType *toFill) noexcept
Gets a DataType from a module.
Result compileModule(const boost::filesystem::path &fullName, Flags< CompileSettings > settings, std::unique_ptr< llvm::Module > *toFill)
Compile a module to a llvm::Module.
void setModuleCache(std::unique_ptr< ModuleCache > newCache)
Set the module cache.
Module that holds graph functions.
boost::filesystem::path fullNamePath() const
Get the full name of the module in a path.
Defines the GraphModule class.
virtual DataType typeFromName(boost::string_view name)=0
Get a DataType from the name.
The namespace where chigraph lives.
std::string stringifyLLVMType(llvm::Type *ty)
Turns a type into a string.
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...
GraphModule * newGraphModule(const boost::filesystem::path &fullName)
Create a new GraphModule with the given full name.
std::time_t lastEditTime() const
Get the time that this module was last edited.
Link in dependencies If this is set, it will be a ready to run module If not, it'll contain forward d...
std::string shortName() const
Get the short name of the module (the last bit)
The result object, used for identifiying errors with good diagnostics.
Declares the GraphFunction class.
Defines the NodeInstance class and related functions.
Define deserialization functions.
Result addModuleFromJson(const boost::filesystem::path &fullName, const nlohmann::json &json, GraphModule **toFill=nullptr)
Load a module from JSON – avoid this use the string overload.