Add Basic setup for lowering the llvm dialect to the LLVMIR

This commit is contained in:
Sameer Rahmani 2021-06-21 11:03:18 +01:00
parent bd4dc2301c
commit 7f7f49d3ac
10 changed files with 181 additions and 54 deletions

View File

@ -132,6 +132,11 @@ int main(int argc, char *argv[]) {
break;
}
case Action::DumpIR: {
ctx->setOperationPhase(CompilationPhase::IR);
break;
}
default: {
llvm::errs() << "No action specified. TODO: Print out help here\n";
return 1;
@ -143,6 +148,10 @@ int main(int argc, char *argv[]) {
if (isSet.succeeded()) {
ctx->insertNS(ns);
if (mlir::failed(serene::slir::generate<Namespace>(*ns))) {
llvm::errs() << "IR generation faild\n";
return 1;
}
serene::slir::dump<Namespace>(*ns);
} else {
llvm::outs() << "Can't set the tree of the namespace!\n";

View File

@ -102,6 +102,9 @@ on ADF
* TODOs
** Bootstrap
*** TODO Replace =llvm::outs()= with debug statements
*** TODO Error handling
Create proper error handling for the internal infra
*** TODO Language Spec :DOCS:
*** TODO A proper List implementation
*** TODO Vector implementation
@ -120,7 +123,6 @@ on ADF
*** TODO Enum implementation
*** TODO Protocol
*** TODO Struct implementation
*** TODO Error handling
*** TODO Multi arity functions
*** TODO QuasiQuotation
*** TODO Linter :Misc:

View File

@ -1 +1 @@
4 80
4

View File

@ -47,19 +47,24 @@ enum class CompilationPhase {
MLIR, // Lowered slir to other dialects
LIR, // Lowered to the llvm ir dialect
IR, // Lowered to the LLVMIR itself
O1
NoOptimization,
O1,
O2,
O3,
};
class SereneContext {
std::map<std::string, std::shared_ptr<Namespace>> namespaces;
// Why string vs pointer? We might rewrite the namespace and
// holding a pointer means that it might point to the old version
std::string current_ns;
public:
// --------------------------------------------------------------------------
// IMPORTANT:
// These two contextes have to be the very first members of the class in
// order to destroy last. DO NOT change the order or add anything before
// them
// --------------------------------------------------------------------------
llvm::LLVMContext llvmContext;
mlir::MLIRContext mlirContext;
mlir::PassManager pm;
/// Insert the given `ns` into the context. The Context object is
/// the owner of all the namespaces. The `ns` will overwrite any
/// namespace with the same name.
@ -74,11 +79,23 @@ public:
std::shared_ptr<Namespace> getNS(llvm::StringRef ns_name);
SereneContext() : pm(&mlirContext) {
SereneContext()
: pm(&mlirContext), targetPhase(CompilationPhase::NoOptimization) {
mlirContext.getOrLoadDialect<serene::slir::SereneDialect>();
pm.enableCrashReproducerGeneration("/home/lxsameer/mlir.mlir");
};
void setOperationPhase(CompilationPhase phase);
CompilationPhase getTargetPhase() { return targetPhase; };
int getOptimizatioLevel();
private:
CompilationPhase targetPhase;
std::map<std::string, std::shared_ptr<Namespace>> namespaces;
// Why string vs pointer? We might rewrite the namespace and
// holding a pointer means that it might point to the old version
std::string current_ns;
};
/// Creates a new context object. Contexts are used through out the compilation

View File

@ -30,6 +30,7 @@
#include "serene/traits.h"
#include "serene/utils.h"
#include <algorithm>
#include <atomic>
#include <llvm/ADT/SmallString.h>
#include <llvm/ADT/StringRef.h>
@ -62,11 +63,12 @@ private:
bool initialized = false;
std::atomic<uint> fn_counter = 0;
exprs::Ast tree;
std::unique_ptr<llvm::Module> llvmModule;
mlir::ModuleOp module;
public:
mlir::StringRef name;
llvm::Optional<std::string> filename;
mlir::ModuleOp module;
/// The root environment of the namespace on the semantic analysis phase.
/// Which is a mapping from names to AST nodes ( no evaluation ).
@ -82,11 +84,13 @@ public:
mlir::ModuleOp &getModule();
SereneContext &getContext();
void setLLVMModule(std::unique_ptr<llvm::Module>);
llvm::Module &getLLVMModule();
// Generatable Trait
/// Generate the IR of the namespace with respect to the compilation phase
mlir::ModuleOp &generate();
mlir::LogicalResult generate();
mlir::LogicalResult runPasses();
/// Dumps the namespace with respect to the compilation phase

View File

@ -25,17 +25,27 @@
#ifndef SERENE_SLIR_GENERATABLE_H
#define SERENE_SLIR_GENERATABLE_H
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Identifier.h"
#include "serene/reader/location.h"
#include "serene/slir/dialect.h"
#include "serene/traits.h"
#include <llvm/ADT/STLExtras.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/Casting.h>
#include <llvm/Support/TargetSelect.h>
#include <mlir/ExecutionEngine/ExecutionEngine.h>
#include <mlir/ExecutionEngine/OptUtils.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/MLIRContext.h>
#include <mlir/Support/LogicalResult.h>
#include <mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h>
#include <mlir/Target/LLVMIR/ModuleTranslation.h>
#include <stdexcept>
#include <utility>
namespace serene {
class Namespace;
}
class SereneContext;
} // namespace serene
namespace serene::slir {
@ -58,12 +68,52 @@ public:
Generatable(){};
Generatable(const Generatable &) = delete;
mlir::ModuleOp &generate();
mlir::LogicalResult runPasses();
mlir::LogicalResult generate() { return this->Object().generate(); };
mlir::LogicalResult runPasses() { return this->Object().runPasses(); };
mlir::ModuleOp &getModule() { return this->Object().getModule(); };
serene::SereneContext &getContext() { return this->Object().getContext(); };
void dump() { this->Object().dump(); };
};
template <typename T>
mlir::LogicalResult generate(Generatable<T> &t) {
return t.generate();
};
template <typename T>
std::unique_ptr<llvm::Module> toLLVMIR(Generatable<T> &t) {
auto &module = t.getModule();
auto &ctx = t.getContext();
// 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();
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 std::move(llvmModule);
};
template <typename T>
void dump(Generatable<T> &t) {
t.dump();

View File

@ -26,6 +26,7 @@
#include "serene/namespace.h"
#include "serene/passes.h"
#include "serene/slir/generatable.h"
namespace serene {
@ -61,6 +62,8 @@ std::shared_ptr<Namespace> SereneContext::getCurrentNS() {
};
void SereneContext::setOperationPhase(CompilationPhase phase) {
this->targetPhase = phase;
if (phase == CompilationPhase::SLIR) {
return;
}
@ -74,6 +77,20 @@ void SereneContext::setOperationPhase(CompilationPhase phase) {
}
};
int SereneContext::getOptimizatioLevel() {
if (targetPhase <= CompilationPhase::NoOptimization) {
return 0;
}
if (targetPhase == CompilationPhase::O1) {
return 1;
}
if (targetPhase == CompilationPhase::O2) {
return 2;
}
return 3;
}
std::unique_ptr<SereneContext> makeSereneContext() {
return std::make_unique<SereneContext>();
};

View File

@ -83,8 +83,16 @@ uint Namespace::nextFnCounter() { return fn_counter++; };
mlir::ModuleOp &Namespace::getModule() { return this->module; };
SereneContext &Namespace::getContext() { return this->ctx; };
void Namespace::setLLVMModule(std::unique_ptr<llvm::Module> m) {
this->llvmModule = std::move(m);
};
mlir::ModuleOp &Namespace::generate() {
llvm::Module &Namespace::getLLVMModule() {
// TODO: check the llvm module to make sure it is initialized
return *llvmModule;
};
mlir::LogicalResult Namespace::generate() {
for (auto &x : getTree()) {
x->generateIR(*this);
}
@ -92,19 +100,30 @@ mlir::ModuleOp &Namespace::generate() {
if (mlir::failed(runPasses())) {
// TODO: throw a proper errer
module.emitError("Failure in passes!");
return mlir::failure();
}
return module;
if (ctx.getTargetPhase() >= CompilationPhase::IR) {
auto llvmTmpModule = slir::toLLVMIR<Namespace>(*this);
this->llvmModule = std::move(llvmTmpModule);
}
return mlir::success();
}
mlir::LogicalResult Namespace::runPasses() { return ctx.pm.run(module); };
void Namespace::dump() {
// We don't want this module just yet
mlir::ModuleOp &m = generate();
m->dump();
llvm::outs() << "\nMLIR: \n";
module->dump();
if (llvmModule) {
llvm::outs() << "\nLLVM IR: \n";
llvmModule->dump();
}
};
Namespace::~Namespace() { this->module.erase(); }
namespace slir {
template class Generatable<Namespace>;
}
} // namespace serene

View File

@ -26,6 +26,8 @@
#include "serene/passes.h"
#include "serene/slir/dialect.h"
#include "llvm/Support/Casting.h"
#include <mlir/Dialect/Affine/IR/AffineOps.h>
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
#include <mlir/Dialect/MemRef/IR/MemRef.h>
@ -49,12 +51,29 @@ ValueOpLowering::matchAndRewrite(serene::slir::ValueOp op,
auto value = op.value();
mlir::Location loc = op.getLoc();
// auto std_const =
rewriter.create<mlir::ConstantIntOp>(loc, (int64_t)value,
rewriter.getI64Type());
// rewriter.replaceOpWithNewOp<typename OpTy>(Operation *op, Args &&args...)
// Replace this operation with the generated alloc.
// rewriter.replaceOp(op, alloc);
llvm::SmallVector<mlir::Type, 4> arg_types(0);
auto func_type = rewriter.getFunctionType(arg_types, rewriter.getI64Type());
auto fn = rewriter.create<mlir::FuncOp>(loc, "randomname", func_type);
if (!fn) {
llvm::outs() << "Value Rewrite fn is null\n";
return mlir::failure();
}
auto &entryBlock = *fn.addEntryBlock();
rewriter.setInsertionPointToStart(&entryBlock);
auto retVal = rewriter
.create<mlir::ConstantIntOp>(loc, (int64_t)value,
rewriter.getI64Type())
.getResult();
mlir::ReturnOp returnOp = rewriter.create<mlir::ReturnOp>(loc, retVal);
if (!returnOp) {
llvm::outs() << "Value Rewrite returnOp is null\n";
return mlir::failure();
}
fn.setPrivate();
rewriter.eraseOp(op);
return mlir::success();
}
@ -99,6 +118,7 @@ void SLIRToAffinePass::runOnModule() {
// to lower, `toy.print`, as `legal`.
target.addIllegalDialect<serene::slir::SereneDialect>();
// target.addLegalOp<serene::slir::PrintOp>();
target.addLegalOp<mlir::FuncOp>();
// Now that the conversion target has been defined, we just need to provide
// the set of patterns that will lower the Toy operations.

View File

@ -24,35 +24,24 @@
#include "serene/slir/generatable.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Identifier.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/Value.h"
#include "mlir/Support/LogicalResult.h"
#include "serene/exprs/traits.h"
#include "serene/namespace.h"
#include "serene/slir/dialect.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <llvm/ADT/STLExtras.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/Casting.h>
#include <llvm/Support/TargetSelect.h>
#include <mlir/ExecutionEngine/ExecutionEngine.h>
#include <mlir/ExecutionEngine/OptUtils.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/Support/LogicalResult.h>
#include <mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h>
#include <mlir/Target/LLVMIR/ModuleTranslation.h>
#include <stdexcept>
#include <utility>
namespace serene {
namespace slir {
template <typename T>
mlir::ModuleOp &Generatable<T>::generate() {
return this->Object().generate();
};
template <typename T>
mlir::LogicalResult Generatable<T>::runPasses() {
return this->Object().runPasses();
};
// mlir::Operation *Generatable::generate(exprs::Expression *x) {
// switch (x->getType()) {
// case SereneType::Number: {