Start the new jit

This commit is contained in:
Sameer Rahmani 2021-11-01 15:09:11 +00:00
parent fc11896c28
commit 34d3513426
22 changed files with 664 additions and 41 deletions

View File

@ -98,7 +98,11 @@ on ADF
- [[https://www.wilfred.me.uk/blog/2014/09/27/the-definitive-guide-to-syntax-highlighting/][The Definitive Guide To Syntax Highlighting]]
** Linker
- [[https://lwn.net/Articles/276782/][20 part linker essay]]
- [[https://lld.llvm.org/index.html][LLD Usage]]
** LLVM
- [[https://blog.yossarian.net/2021/07/19/LLVM-internals-part-1-bitcode-format][LLVM Internals]]
*** TableGen
- [[https://llvm.org/docs/TableGen/BackGuide.html#creating-a-new-backend][Create a backend]]
* Considerations
** Hashmaps

View File

@ -22,6 +22,8 @@
#include "serene/diagnostics.h"
#include "serene/environment.h"
#include "serene/export.h"
#include "serene/jit.h"
#include "serene/jit/engine.h"
#include "serene/namespace.h"
#include "serene/passes.h"
#include "serene/slir/dialect.h"
@ -77,6 +79,8 @@ public:
// order to destroy last. DO NOT change the order or add anything before
// them
// --------------------------------------------------------------------------
// TODO: Remove the llvmContext
llvm::LLVMContext llvmContext;
mlir::MLIRContext mlirContext;
@ -84,6 +88,8 @@ public:
std::unique_ptr<DiagnosticEngine> diagEngine;
std::unique_ptr<serene::jit::SereneJIT> jit;
/// The source manager is responsible for loading namespaces and practically
/// managing the source code in form of memory buffers.
SourceMgr sourceManager;
@ -137,6 +143,24 @@ public:
MaybeNS readNamespace(const std::string &name);
MaybeNS readNamespace(const std::string &name, reader::LocationRange loc);
static std::unique_ptr<llvm::LLVMContext> genLLVMContext() {
return std::make_unique<llvm::LLVMContext>();
};
static std::unique_ptr<SereneContext> make() {
auto ctx = std::make_unique<SereneContext>();
auto maybeJIT = serene::jit::makeSereneJIT(*ctx);
if (!maybeJIT) {
// TODO: Raise an error here
return nullptr;
}
ctx->jit.swap(*maybeJIT);
return ctx;
};
private:
CompilationPhase targetPhase;

View File

@ -34,8 +34,9 @@ class Environment {
Environment<K, V> *parent;
using StorageType = llvm::DenseMap<K, V>;
// The actual bindings storage
llvm::DenseMap<K, V> pairs;
StorageType pairs;
public:
Environment() : parent(nullptr) {}
@ -60,6 +61,17 @@ public:
pairs.insert(std::pair<K, V>(key, value));
return mlir::success();
};
inline typename StorageType::iterator begin() { return pairs.begin(); }
inline typename StorageType::iterator end() { return pairs.end(); }
inline typename StorageType::const_iterator begin() const {
return pairs.begin();
}
inline typename StorageType::const_iterator end() const {
return pairs.end();
}
};
} // namespace serene

View File

@ -0,0 +1,18 @@
#+TITLE: Serene JIT
#+AUTHOR: Sameer Rahmani
#+STARTUP: logdrawer logdone logreschedule indent content align constSI entitiespretty nolatexpreview
#+OPTIONS: tex:t
* SereneJIT
** Development Notes
*** Steps to take
You usually want a custom =MaterializationUnit= for your program representation, and a custom =Layer=. The Layer will have two
operations: =add= and =emit=. The =add= operation takes an instance of your program representation, builds one of your custom
=MaterializationUnits= to hold it, then adds it to a =JITDylib=. The =emit= operation takes a =MaterializationResponsibility=
object and an instance of your program representation and materializes it, usually by compiling it and handing the resulting
object off to an =ObjectLinkingLayer=.
Your custom =MaterializationUnit= will have two operations: =materialize= and =discard=. The =materialize= function will be
called for you when any symbol provided by the unit is looked up, and it should just call the =emit= function on your layer,
passing in the given =MaterializationResponsibility= and the wrapped program representation. The =discard= function will be
called if some weak symbol provided by your unit is not needed (because the JIT found an overriding definition).
You can use this to drop your definition early, or just ignore it and let the linker drop the definition later.

131
include/serene/jit/engine.h Normal file
View File

@ -0,0 +1,131 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Commentary:
*/
#ifndef SERENE_JIT_ENGINE_H
#define SERENE_JIT_ENGINE_H
#include "serene/jit/layers.h"
#include "serene/namespace.h"
#include <llvm/ExecutionEngine/Orc/CompileUtils.h> // for ConcurrentIRCompiler
#include <llvm/ExecutionEngine/Orc/Core.h>
#include <llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h>
#include <llvm/ExecutionEngine/Orc/ExecutionUtils.h> // for DynamicLibrarySearchGenerator
#include <llvm/ExecutionEngine/Orc/IRCompileLayer.h>
#include <llvm/ExecutionEngine/Orc/Mangling.h>
#include <llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <llvm/IR/DataLayout.h>
#include <memory>
namespace orc = llvm::orc;
namespace serene {
class SereneContext;
namespace jit {
class SereneJIT {
/// An ExecutionSession represents a running JIT program.
std::unique_ptr<orc::ExecutionSession> es;
std::unique_ptr<orc::EPCIndirectionUtils> epciu;
llvm::DataLayout dl;
/// Mangler in responsible for changing the symbol names based on our
/// naming convention.
orc::MangleAndInterner mangler;
// Layers -------------------------------------------------------------------
// Serene's JIT follows the same design as the ORC and uses the framework that
// it provides.
//
// Layers are the building blocks of the JIT and work on top of each other
// to add different functionalities to the JIT. Order is important here and
// layers call each other and pa
/// The object linking layer allows object files to be added to the JIT
orc::RTDyldObjectLinkingLayer objectLayer;
/// The compiler layer is responsible to compile the LLVMIR to target code
/// for us
orc::IRCompileLayer compileLayer;
/// Transform layaer is responsible for running a pass pipeline on the AST
/// and generate LLVM IR
// orc::IRTransformLayer transformLayer;
/// The AST Layer reads and import the Serene Ast directly to the JIT
// SereneAstLayer astLayer;
/// NS layer is responsible for adding namespace to the JIT by name.
/// It will import the entire namespace.
NSLayer nsLayer;
/// This a symbol table tha holds symbols from whatever code we execute
orc::JITDylib &mainJD;
serene::SereneContext &ctx;
public:
SereneJIT(serene::SereneContext &ctx,
std::unique_ptr<orc::ExecutionSession> es,
std::unique_ptr<orc::EPCIndirectionUtils> epciu,
orc::JITTargetMachineBuilder jtmb, llvm::DataLayout &&dl);
~SereneJIT() {
if (auto Err = es->endSession()) {
es->reportError(std::move(Err));
}
}
const llvm::DataLayout &getDataLayout() const { return dl; }
orc::JITDylib &getMainJITDylib() { return mainJD; }
llvm::Error addModule(orc::ThreadSafeModule tsm,
orc::ResourceTrackerSP rt = nullptr) {
if (!rt) {
rt = mainJD.getDefaultResourceTracker();
}
return compileLayer.add(rt, std::move(tsm));
}
llvm::Error addNS(llvm::StringRef nsname,
orc::ResourceTrackerSP rt = nullptr);
llvm::Expected<llvm::JITEvaluatedSymbol> lookup(llvm::StringRef name) {
return es->lookup({&mainJD}, mangler(name.str()));
}
};
llvm::Expected<std::unique_ptr<SereneJIT>>
makeSereneJIT(serene::SereneContext &ctx);
}; // namespace jit
}; // namespace serene
#endif

170
include/serene/jit/layers.h Normal file
View File

@ -0,0 +1,170 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SERENE_JIT_LAYERS_H
#define SERENE_JIT_LAYERS_H
#include "serene/reader/location.h"
#include "serene/utils.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/ADT/Twine.h>
#include <llvm/ExecutionEngine/JITSymbol.h>
#include <llvm/ExecutionEngine/Orc/Core.h>
#include <llvm/ExecutionEngine/Orc/Layer.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/Support/Error.h>
namespace orc = llvm::orc;
namespace serene {
class SereneContext;
class Namespace;
namespace exprs {
class Expression;
using Node = std::shared_ptr<Expression>;
using Ast = std::vector<Node>;
} // namespace exprs
namespace jit {
class SereneAstLayer;
/// This will compile the ast to llvm ir.
llvm::orc::ThreadSafeModule compileAst(serene::SereneContext &ctx,
exprs::Ast &ast);
class SerenAstMaterializationUnit : public orc::MaterializationUnit {
public:
SerenAstMaterializationUnit(SereneContext &ctx, SereneAstLayer &l,
exprs::Ast &ast);
llvm::StringRef getName() const override {
return "SereneAstMaterializationUnit";
}
void
materialize(std::unique_ptr<orc::MaterializationResponsibility> r) override;
private:
void discard(const orc::JITDylib &jd,
const orc::SymbolStringPtr &sym) override {
UNUSED(jd);
UNUSED(sym);
llvm_unreachable("Kaleidoscope functions are not overridable");
}
serene::SereneContext &ctx;
SereneAstLayer &astLayer;
exprs::Ast &ast;
};
// class SereneAstLayer {
// serene::SereneContexe &ctx;
// orc::IRLayer &baseLayer;
// orc::MangleAndInterner &mangler;
// const llvm::DataLayout &dl;
// public:
// SereneAstLayer(serene::SereneContexe &ctx, orc::IRLayer &baseLayer,
// orc::MangleAndInterner &mangler, const llvm::DataLayout &dl)
// : ctx(ctx), baseLayer(baseLayer), mangler(mangler), dl(dl){};
// llvm::Error add(orc::ResourceTrackerSP &rt, exprs::Ast &ast) {
// return rt->getJITDylib().define(
// std::make_unique<SerenAstMaterializationUnit>(ctx, *this, ast), rt);
// }
// void emit(std::unique_ptr<orc::MaterializationResponsibility> mr,
// exprs::Ast &e) {
// baseLayer.emit(std::move(mr), compileAst(ctx, e));
// }
// orc::SymbolFlagsMap getInterface(exprs::Ast &e) {
// orc::SymbolFlagsMap Symbols;
// Symbols[mangler(e.getName())] = llvm::JITSymbolFlags(
// llvm::JITSymbolFlags::Exported | llvm::JITSymbolFlags::Callable);
// return Symbols;
// }
// };
/// NS Layer ==================================================================
class NSLayer;
/// This will compile the NS to llvm ir.
llvm::orc::ThreadSafeModule compileNS(serene::SereneContext &ctx,
serene::Namespace &ns);
class NSMaterializationUnit : public orc::MaterializationUnit {
public:
NSMaterializationUnit(SereneContext &ctx, NSLayer &l, serene::Namespace &ns);
llvm::StringRef getName() const override { return "NSMaterializationUnit"; }
void
materialize(std::unique_ptr<orc::MaterializationResponsibility> r) override;
private:
void discard(const orc::JITDylib &jd,
const orc::SymbolStringPtr &sym) override {
UNUSED(jd);
UNUSED(sym);
UNUSED(ctx);
llvm_unreachable("discard");
}
serene::SereneContext &ctx;
NSLayer &nsLayer;
serene::Namespace &ns;
};
/// NS Layer is responsible for adding namespaces to the JIT
class NSLayer {
serene::SereneContext &ctx;
orc::IRLayer &baseLayer;
orc::MangleAndInterner &mangler;
const llvm::DataLayout &dl;
public:
NSLayer(serene::SereneContext &ctx, orc::IRLayer &baseLayer,
orc::MangleAndInterner &mangler, const llvm::DataLayout &dl)
: ctx(ctx), baseLayer(baseLayer), mangler(mangler), dl(dl){};
llvm::Error add(orc::ResourceTrackerSP &rt, llvm::StringRef nsname) {
auto loc = serene::reader::LocationRange::UnknownLocation(nsname);
return add(rt, nsname, loc);
}
llvm::Error add(orc::ResourceTrackerSP &rt, llvm::StringRef nsname,
serene::reader::LocationRange &loc);
void emit(std::unique_ptr<orc::MaterializationResponsibility> mr,
serene::Namespace &ns) {
// TODO: We need to pass dl to the compilerNS later to aviod recreating
// the data layout all the time
UNUSED(dl);
baseLayer.emit(std::move(mr), compileNS(ctx, ns));
}
orc::SymbolFlagsMap getInterface(serene::Namespace &ns);
};
} // namespace jit
} // namespace serene
#endif

View File

@ -38,6 +38,7 @@
#include <llvm/ADT/SmallString.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/ADT/Twine.h>
#include <llvm/ExecutionEngine/Orc/ThreadSafeModule.h>
#include <llvm/IR/Module.h>
#include <mlir/IR/Builders.h>
#include <mlir/IR/BuiltinOps.h>
@ -61,7 +62,7 @@ using Node = std::shared_ptr<Expression>;
using Ast = std::vector<Node>;
} // namespace exprs
using MaybeModule = llvm::Optional<std::unique_ptr<llvm::Module>>;
using MaybeModule = llvm::Optional<llvm::orc::ThreadSafeModule>;
using MaybeModuleOp = llvm::Optional<mlir::OwningOpRef<mlir::ModuleOp>>;
/// Serene's namespaces are the unit of compilation. Any code that needs to be

View File

@ -65,7 +65,7 @@ SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx, std::string &input);
/// In case of an error Serene will throw the error messages vis the diagnostic
/// engine as well and the error that this function returns will be the
/// generic error message.
// SERENE_EXPORT exprs::MaybeNode eval(SereneContext &ctx, exprs::Ast input);
SERENE_EXPORT exprs::MaybeNode eval(SereneContext &ctx, exprs::Ast &input);
// TODO: Return a Serene String type instead of the std::string
// TODO: Create an overload to get a stream instead of the result string

View File

@ -20,6 +20,7 @@
#include "serene/exprs/expression.h"
#include <llvm/ADT/Optional.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/MLIRContext.h>
@ -27,8 +28,11 @@
namespace serene {
namespace slir {
std::unique_ptr<llvm::Module> compileToLLVMIR(serene::SereneContext &ctx,
mlir::ModuleOp &module);
// std::unique_ptr<llvm::Module> compileToLLVMIR(serene::SereneContext &ctx,
// mlir::ModuleOp &module);
llvm::Optional<llvm::orc::ThreadSafeModule>
compileToLLVMIR(serene::SereneContext &ctx, mlir::ModuleOp &module);
} // namespace slir
} // namespace serene

View File

@ -103,7 +103,7 @@ private:
SrcBuffer &operator=(const SrcBuffer &) = delete;
~SrcBuffer();
};
using ErrorOrMemBufPtr = llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>;
using MemBufPtr = std::unique_ptr<llvm::MemoryBuffer>;
/// This is all of the buffers that we are reading from.
std::vector<SrcBuffer> buffers;
@ -119,8 +119,8 @@ private:
// a unique pointer to the memory buffer containing the content or an error.
// In the success case it will put the path of the file into the \p
// importedFile.
ErrorOrMemBufPtr findFileInLoadPath(const std::string &name,
std::string &importedFile);
MemBufPtr findFileInLoadPath(const std::string &name,
std::string &importedFile);
bool isValidBufferID(unsigned i) const;

View File

@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(libserene)
add_subdirectory(serenec)
#add_subdirectory(serenec)
add_subdirectory(serene-repl)
# Testing only available if this is the main app

View File

@ -41,10 +41,13 @@ add_library(serene
serene.cpp
context.cpp
namespace.cpp
jit.cpp
source_mgr.cpp
diagnostics.cpp
# jit.cpp
jit/engine.cpp
jit/layers.cpp
# Reader
reader/reader.cpp
reader/location.cpp

View File

@ -99,7 +99,7 @@ MaybeNS SereneContext::readNamespace(const std::string &name) {
MaybeNS SereneContext::readNamespace(const std::string &name,
reader::LocationRange loc) {
return sourceManager.readNamespace(*this, std::move(name), loc);
return sourceManager.readNamespace(*this, name, loc);
}
void terminate(SereneContext &ctx, int exitCode) {
@ -112,7 +112,7 @@ void terminate(SereneContext &ctx, int exitCode) {
}
std::unique_ptr<SereneContext> makeSereneContext() {
return std::make_unique<SereneContext>();
return SereneContext::make();
};
}; // namespace serene

View File

@ -59,7 +59,6 @@ MaybeNode Def::make(SereneContext &ctx, List *list) {
// Make sure that the list starts with a `def`
Symbol *defSym = llvm::dyn_cast<Symbol>(list->elements[0].get());
// TODO: Replace this one with a runtime check
assert((defSym && defSym->name == "def") &&
"The first element of the list should be a 'def'.");
@ -100,7 +99,6 @@ MaybeNode Def::make(SereneContext &ctx, List *list) {
tmp->setName(binding->name);
}
// auto analayzedValuePtr = analyzedValue;
auto result = ctx.getCurrentNS().semanticEnv.insert_symbol(binding->name,
analyzedValue);

View File

@ -0,0 +1,75 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "serene/jit/engine.h"
#include "serene/context.h"
#include "serene/jit/layers.h"
#include "serene/utils.h"
namespace serene::jit {
SereneJIT::SereneJIT(serene::SereneContext &ctx,
std::unique_ptr<orc::ExecutionSession> es,
std::unique_ptr<orc::EPCIndirectionUtils> epciu,
orc::JITTargetMachineBuilder jtmb, llvm::DataLayout &&dl)
: es(std::move(es)), epciu(std::move(epciu)), dl(dl),
mangler(*this->es, this->dl),
objectLayer(
*this->es,
[]() { return std::make_unique<llvm::SectionMemoryManager>(); }),
compileLayer(
*this->es, objectLayer,
std::make_unique<orc::ConcurrentIRCompiler>(std::move(jtmb))),
nsLayer(ctx, compileLayer, mangler, dl),
mainJD(this->es->createBareJITDylib(ctx.getCurrentNS().name)), ctx(ctx) {
UNUSED(this->ctx);
mainJD.addGenerator(
cantFail(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
dl.getGlobalPrefix())));
}
llvm::Error SereneJIT::addNS(llvm::StringRef nsname,
orc::ResourceTrackerSP rt) {
if (!rt) {
rt = mainJD.getDefaultResourceTracker();
}
return nsLayer.add(rt, nsname);
};
llvm::Expected<std::unique_ptr<SereneJIT>>
makeSereneJIT(serene::SereneContext &ctx) {
auto epc = orc::SelfExecutorProcessControl::Create();
if (!epc) {
return epc.takeError();
}
auto es = std::make_unique<orc::ExecutionSession>(std::move(*epc));
orc::JITTargetMachineBuilder jtmb(
es->getExecutorProcessControl().getTargetTriple());
auto dl = jtmb.getDefaultDataLayoutForTarget();
if (!dl) {
return dl.takeError();
}
return std::make_unique<SereneJIT>(ctx, std::move(es), std::move(jtmb),
std::move(*dl));
};
} // namespace serene::jit

View File

@ -0,0 +1,101 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2021 Sameer Rahmani <lxsameer@gnu.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "serene/jit/layers.h"
#include "serene/context.h"
#include <llvm/ExecutionEngine/Orc/ThreadSafeModule.h>
#include <llvm/Support/Error.h> // for report_fatal_error
namespace serene::jit {
// llvm::orc::ThreadSafeModule compileAst(serene::SereneContext &ctx,
// exprs::Ast &ast){
// };
// SerenAstMaterializationUnit::SerenAstMaterializationUnit(
// serene::SereneContext &ctx, SereneAstLayer &l, exprs::Ast &ast)
// : MaterializationUnit(l.getInterface(ast), nullptr), ctx(ctx),
// astLayer(l),
// ast(ast){};
// void SerenAstMaterializationUnit::materialize(
// std::unique_ptr<orc::MaterializationResponsibility> r) {
// astLayer.emit(std::move(r), ast);
// }
/// NS Layer ==================================================================
llvm::orc::ThreadSafeModule compileNS(serene::SereneContext &ctx,
serene::Namespace &ns) {
UNUSED(ctx);
auto maybeModule = ns.compileToLLVM();
if (!maybeModule) {
// TODO: Handle failure
llvm::report_fatal_error("Couldn't compile lazily JIT'd function");
}
return std::move(maybeModule.getValue());
};
NSMaterializationUnit::NSMaterializationUnit(SereneContext &ctx, NSLayer &l,
serene::Namespace &ns)
: MaterializationUnit(l.getInterface(ns), nullptr), ctx(ctx), nsLayer(l),
ns(ns){};
void NSMaterializationUnit::materialize(
std::unique_ptr<orc::MaterializationResponsibility> r) {
nsLayer.emit(std::move(r), ns);
}
llvm::Error NSLayer::add(orc::ResourceTrackerSP &rt, llvm::StringRef nsname,
reader::LocationRange &loc) {
auto maybeNS = ctx.sourceManager.readNamespace(ctx, nsname.str(), loc);
if (!maybeNS) {
// TODO: Fix this by making Serene errors compatible with llvm::Error
auto err = maybeNS.getError();
return llvm::make_error<llvm::StringError>(
llvm::Twine(err.front()->getMessage()),
std::make_error_code(std::errc::io_error));
}
auto ns = maybeNS.getValue();
return rt->getJITDylib().define(
std::make_unique<NSMaterializationUnit>(ctx, *this, *ns), rt);
}
orc::SymbolFlagsMap NSLayer::getInterface(serene::Namespace &ns) {
orc::SymbolFlagsMap Symbols;
for (auto &k : ns.semanticEnv) {
// llvm::JITSymbolFlags::Exported |
auto nnn = mangler(ns.name + "/" + k.getFirst());
llvm::outs() << "AAAAA \n" << nnn << "\n";
Symbols[nnn] = llvm::JITSymbolFlags(llvm::JITSymbolFlags::Callable);
}
return Symbols;
}
} // namespace serene::jit

View File

@ -146,7 +146,7 @@ MaybeModule Namespace::compileToLLVM() {
if (ctx.getTargetPhase() >= CompilationPhase::IR) {
mlir::ModuleOp module = maybeModule.getValue().get();
return MaybeModule(::serene::slir::compileToLLVMIR(ctx, module));
return ::serene::slir::compileToLLVMIR(ctx, module);
}
return llvm::None;

View File

@ -26,12 +26,18 @@
#include "serene/diagnostics.h"
#include "serene/exprs/expression.h"
// TODO: Remove it
#include "serene/exprs/number.h"
#include "serene/reader/reader.h"
#include <llvm/ADT/None.h>
#include <llvm/Support/Error.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/raw_ostream.h>
namespace serene {
using exprs::Number;
void initCompiler() {
llvm::InitializeAllTargetInfos();
@ -96,9 +102,32 @@ SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx, std::string &input) {
return reader::read(ctx, input, currentNS.name, filename);
};
// SERENE_EXPORT exprs::MaybeNode eval(SereneContext &ctx, exprs::Ast input){
SERENE_EXPORT exprs::MaybeNode eval(SereneContext &ctx, exprs::Ast &input) {
// };
// TODO: Fix the eval function
UNUSED(input);
auto loc = reader::LocationRange::UnknownLocation("nsname");
auto err = ctx.jit->addNS("docs.examples.hello_world");
if (err) {
llvm::errs() << err;
auto e = errors::makeErrorTree(loc, errors::NSLoadError);
return exprs::makeErrorNode(loc, errors::NSLoadError);
}
std::string tmp("docs.examples.hello_world/main");
llvm::ExitOnError e;
// Get the anonymous expression's JITSymbol.
auto sym = e(ctx.jit->lookup(tmp));
// Get the symbol's address and cast it to the right type (takes no
// arguments, returns a double) so we can call it as a native function.
auto *f = (int (*)())(intptr_t)sym.getAddress();
f();
return exprs::make<exprs::Number>(loc, "4", false, false);
};
SERENE_EXPORT void print(SereneContext &ctx, const exprs::Ast &input,
std::string &result) {

View File

@ -21,17 +21,53 @@
namespace serene {
namespace slir {
std::unique_ptr<llvm::Module> compileToLLVMIR(serene::SereneContext &ctx,
mlir::ModuleOp &module) {
// std::unique_ptr<llvm::Module> compileToLLVMIR(serene::SereneContext &ctx,
// mlir::ModuleOp &module) {
// // Register the translation to LLVM IR with the MLIR context.
// mlir::registerLLVMDialectTranslation(ctx.mlirContext);
// // Convert the module to LLVM IR in a new LLVM IR context.
// auto llvmModule = mlir::translateModuleToLLVMIR(module, ctx.llvmContext);
// if (!llvmModule) {
// // TODO: Return a Result type instead
// llvm::errs() << "Failed to emit LLVM IR\n";
// throw std::runtime_error("Failed to emit LLVM IR\n");
// }
// // Initialize LLVM targets.
// llvm::InitializeNativeTarget();
// llvm::InitializeNativeTargetAsmPrinter();
// // TODO: replace this call with our own version of setupTargetTriple
// mlir::ExecutionEngine::setupTargetTriple(llvmModule.get());
// /// Optionally run an optimization pipeline over the llvm module.
// auto optPipeline = mlir::makeOptimizingTransformer(
// /*optLevel=*/ctx.getOptimizatioLevel(), /*sizeLevel=*/0,
// /*targetMachine=*/nullptr);
// if (auto err = optPipeline(llvmModule.get())) {
// llvm::errs() << "Failed to optimize LLVM IR " << err << "\n";
// throw std::runtime_error("Failed to optimize LLVM IR");
// }
// return llvmModule;
// };
llvm::Optional<llvm::orc::ThreadSafeModule>
compileToLLVMIR(serene::SereneContext &ctx, mlir::ModuleOp &module) {
auto llvmContext = serene::SereneContext::genLLVMContext();
// Register the translation to LLVM IR with the MLIR context.
mlir::registerLLVMDialectTranslation(ctx.mlirContext);
mlir::registerLLVMDialectTranslation(*module.getContext());
// Convert the module to LLVM IR in a new LLVM IR context.
auto llvmModule = mlir::translateModuleToLLVMIR(module, ctx.llvmContext);
auto llvmModule = mlir::translateModuleToLLVMIR(module, *llvmContext);
if (!llvmModule) {
// TODO: Return a Result type instead
llvm::errs() << "Failed to emit LLVM IR\n";
throw std::runtime_error("Failed to emit LLVM IR\n");
module.emitError("Failed to emit LLVM IR\n");
return llvm::None;
}
// Initialize LLVM targets.
@ -45,12 +81,16 @@ std::unique_ptr<llvm::Module> compileToLLVMIR(serene::SereneContext &ctx,
auto optPipeline = mlir::makeOptimizingTransformer(
/*optLevel=*/ctx.getOptimizatioLevel(), /*sizeLevel=*/0,
/*targetMachine=*/nullptr);
if (auto err = optPipeline(llvmModule.get())) {
llvm::errs() << "Failed to optimize LLVM IR " << err << "\n";
throw std::runtime_error("Failed to optimize LLVM IR");
// TODO: Return a proper error
module.emitError("Failed to optimize LLVM IR\n");
return llvm::None;
}
return llvmModule;
auto tsm = llvm::orc::ThreadSafeModule(std::move(llvmModule),
std::move(llvmContext));
return tsm;
};
} // namespace slir

View File

@ -33,6 +33,7 @@
#include <system_error>
#include <llvm/Support/Error.h>
#include <llvm/Support/FormatVariadic.h>
#include <llvm/Support/Locale.h>
#include <llvm/Support/MemoryBufferRef.h>
@ -55,27 +56,30 @@ bool SourceMgr::isValidBufferID(unsigned i) const {
return i != 0 && i <= buffers.size();
};
SourceMgr::ErrorOrMemBufPtr
SourceMgr::findFileInLoadPath(const std::string &name,
std::string &importedFile) {
SourceMgr::MemBufPtr SourceMgr::findFileInLoadPath(const std::string &name,
std::string &importedFile) {
auto path = convertNamespaceToPath(name);
// TODO: Fix this to enqueue a proper error instead
ErrorOrMemBufPtr newBufOrErr(
std::make_error_code(std::errc::no_such_file_or_directory));
// If the file didn't exist directly, see if it's in an include path.
for (unsigned i = 0, e = loadPaths.size(); i != e && !newBufOrErr; ++i) {
for (unsigned i = 0, e = loadPaths.size(); i != e; ++i) {
// TODO: Ugh, Udgly, fix this using llvm::sys::path functions
importedFile = loadPaths[i] + llvm::sys::path::get_separator().data() +
path + "." + DEFAULT_SUFFIX;
SMGR_LOG("Try to load the ns from: " + importedFile);
newBufOrErr = llvm::MemoryBuffer::getFile(importedFile);
auto newBufOrErr = llvm::MemoryBuffer::getFile(importedFile);
if (auto err = newBufOrErr.getError()) {
llvm::consumeError(llvm::errorCodeToError(err));
continue;
}
return std::move(*newBufOrErr);
}
return newBufOrErr;
return nullptr;
};
MaybeNS SourceMgr::readNamespace(SereneContext &ctx, std::string name,
@ -83,16 +87,16 @@ MaybeNS SourceMgr::readNamespace(SereneContext &ctx, std::string name,
std::string importedFile;
SMGR_LOG("Attempt to load namespace: " + name);
ErrorOrMemBufPtr newBufOrErr(findFileInLoadPath(name, importedFile));
MemBufPtr newBufOrErr(findFileInLoadPath(name, importedFile));
if (!newBufOrErr) {
if (newBufOrErr == nullptr) {
auto msg = llvm::formatv("Couldn't find namespace '{0}'", name);
auto err = errors::makeErrorTree(importLoc, errors::NSLoadError,
llvm::StringRef(msg));
return MaybeNS::error(err);
}
auto bufferId = AddNewSourceBuffer(std::move(*newBufOrErr), importLoc);
auto bufferId = AddNewSourceBuffer(std::move(newBufOrErr), importLoc);
UNUSED(nsTable.insert_or_assign(name, bufferId));

View File

@ -85,6 +85,8 @@ int main(int argc, char *argv[]) {
continue;
}
auto x = serene::eval(*ctx, maybeAst.getValue());
serene::print(*ctx, maybeAst.getValue(), result);
llvm::outs() << result << "\n";

View File

@ -16,12 +16,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "serene/jit.h"
#include "serene/namespace.h"
#include "serene/reader/location.h"
#include "serene/reader/reader.h"
#include "serene/reader/semantics.h"
#include "serene/serene.h"
#include "serene/serene/engine.h"
#include "serene/slir/generatable.h"
#include "serene/slir/slir.h"
@ -32,6 +32,7 @@
#include <llvm/ADT/ArrayRef.h>
#include <llvm/ADT/SmallString.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/ExecutionEngine/Orc/ThreadSafeModule.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/FileSystem.h>
@ -116,12 +117,18 @@ int dumpAsObject(Namespace &ns) {
return -1;
}
auto module = std::move(maybeModule.getValue());
auto tsm = maybeModule.getValue();
auto tsc = tsm.getContext();
auto lock = tsc.getLock();
auto module = tsm.getModuleUnlocked();
auto &ctx = ns.getContext();
// TODO: We need to set the triple data layout and everything to that sort in
// one place. We want them for the JIT as well and also we're kinda
// duplicating what we're doing in `Namespace#compileToLLVM`.
module->setTargetTriple(ctx.targetTriple);
std::string Error;