Move the semantic analysis process into the namespace

This commit is contained in:
Sameer Rahmani 2021-09-25 16:18:33 +01:00
parent dc051797ee
commit 6ef69d329a
10 changed files with 164 additions and 151 deletions

View File

@ -243,13 +243,6 @@ int main(int argc, char *argv[]) {
applyPassManagerCLOptions(ctx->pm);
ctx->sourceManager.setLoadPaths(loadPaths);
auto runLoc = reader::LocationRange::UnknownLocation(inputNS);
auto ns = ctx->sourceManager.readNamespace(*ctx, inputNS, runLoc);
if (!ns) {
return (int)std::errc::no_such_file_or_directory;
}
// TODO: handle the outputDir by not forcing it. it should be
// default to the current working dir
if (outputDir == "-") {
@ -268,23 +261,13 @@ int main(int argc, char *argv[]) {
// Just print out the raw AST
case Action::DumpAST: {
auto ast = ns->getTree();
llvm::outs() << exprs::astToString(&ast) << "\n";
return 0;
ctx->setOperationPhase(CompilationPhase::Parse);
break;
};
case Action::DumpSemantic: {
auto ast = ns->getTree();
auto afterAst = reader::analyze(*ctx, ast);
if (!afterAst) {
throw std::move(afterAst.getError());
}
ast = afterAst.getValue();
llvm::outs() << exprs::astToString(&ast) << "\n";
return 0;
ctx->setOperationPhase(CompilationPhase::Analysis);
break;
};
case Action::DumpSLIR: {
@ -323,61 +306,63 @@ int main(int argc, char *argv[]) {
}
}
// Perform the semantic analytics
auto afterAst = reader::analyze(*ctx, ns->getTree());
if (!afterAst) {
throw std::move(afterAst.getError());
auto runLoc = reader::LocationRange::UnknownLocation(inputNS);
auto ns = ctx->sourceManager.readNamespace(*ctx, inputNS, runLoc);
if (!ns) {
return (int)std::errc::no_such_file_or_directory;
}
auto isSet = ns->setTree(afterAst.getValue());
if (isSet.succeeded()) {
ctx->insertNS(ns);
ctx->insertNS(ns);
switch (emitAction) {
case Action::DumpSLIR:
case Action::DumpMLIR:
case Action::DumpLIR: {
ns->dump();
break;
};
case Action::DumpIR: {
auto maybeModule = ns->compileToLLVM();
switch (emitAction) {
case Action::DumpAST:
case Action::DumpSemantic: {
auto ast = ns->getTree();
llvm::outs() << exprs::astToString(&ast) << "\n";
return 0;
}
case Action::DumpSLIR:
case Action::DumpMLIR:
case Action::DumpLIR: {
ns->dump();
break;
};
case Action::DumpIR: {
auto maybeModule = ns->compileToLLVM();
if (!maybeModule) {
llvm::errs() << "Failed to generate the IR.\n";
return 1;
}
maybeModule.getValue()->dump();
break;
};
case Action::RunJIT: {
auto maybeJIT = JIT::make(*ns.get());
if (!maybeJIT) {
// TODO: panic in here: "Couldn't creat the JIT!"
return -1;
}
auto jit = std::move(maybeJIT.getValue());
if (jit->invoke("main")) {
llvm::errs() << "Faild to invoke the 'main' function.\n";
return 1;
}
llvm::outs() << "Done!";
break;
};
case Action::Compile:
case Action::CompileToObject: {
return dumpAsObject(*ns);
};
default: {
llvm::errs() << "Action is not supported yet!\n";
};
if (!maybeModule) {
llvm::errs() << "Failed to generate the IR.\n";
return 1;
}
} else {
llvm::errs() << "Can't set the tree of the namespace!\n";
maybeModule.getValue()->dump();
break;
};
case Action::RunJIT: {
auto maybeJIT = JIT::make(*ns.get());
if (!maybeJIT) {
// TODO: panic in here: "Couldn't creat the JIT!"
return -1;
}
auto jit = std::move(maybeJIT.getValue());
if (jit->invoke("main")) {
llvm::errs() << "Faild to invoke the 'main' function.\n";
return 1;
}
llvm::outs() << "Done!";
break;
};
case Action::Compile:
case Action::CompileToObject: {
return dumpAsObject(*ns);
};
default: {
llvm::errs() << "Action is not supported yet!\n";
};
}
return 0;

View File

@ -53,6 +53,8 @@ using Node = std::shared_ptr<Expression>;
} // namespace exprs
enum class CompilationPhase {
Parse,
Analysis,
SLIR,
MLIR, // Lowered slir to other dialects
LIR, // Lowered to the llvm ir dialect

View File

@ -83,6 +83,7 @@ class DiagnosticEngine {
public:
DiagnosticEngine(SereneContext &ctx);
void enqueueError(llvm::StringRef msg);
void emitSyntaxError(reader::LocationRange loc, errors::ErrorVariant &e,
llvm::StringRef msg = "");
};

View File

@ -22,6 +22,15 @@
* SOFTWARE.
*/
/**
* Commentary:
* Rules of a namespace:
* - A namespace has have a name and it has to own it.
* - A namespace may or may not be assiciated with a file
* - The internal AST of a namespace is an evergrowing tree which may expand at
* any given time. For example via iteration of a REPL
*/
#ifndef SERENE_NAMESPACE_H
#define SERENE_NAMESPACE_H
@ -33,6 +42,7 @@
#include <atomic>
#include <llvm/ADT/SmallString.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/ADT/Twine.h>
#include <llvm/IR/Module.h>
#include <memory>
#include <mlir/IR/Builders.h>
@ -63,17 +73,18 @@ using MaybeModuleOp = llvm::Optional<mlir::OwningOpRef<mlir::ModuleOp>>;
class Namespace {
private:
SereneContext &ctx;
bool initialized = false;
// Anonymous function counter. We need to assing a unique name to each
// anonymous function and we use this counter to generate those names
std::atomic<uint> fn_counter = 0;
// The content of the namespace
/// The content of the namespace. It should alway hold a semantically
/// correct AST. It means thet the AST that we want to stor here has
/// to pass the semantic analyzer.
exprs::Ast tree;
public:
mlir::StringRef name;
std::string name;
llvm::Optional<std::string> filename;
/// The root environment of the namespace on the semantic analysis phase.
@ -88,7 +99,11 @@ public:
llvm::Optional<llvm::StringRef> filename);
exprs::Ast &getTree();
mlir::LogicalResult setTree(exprs::Ast &);
/// Expand the current tree of the namespace with the given \p ast by
/// semantically analazing it first. If the give \p ast in not valid
/// it will emit an error.
mlir::LogicalResult expandTree(exprs::Ast &ast);
/// Increase the function counter by one
uint nextFnCounter();
@ -110,6 +125,8 @@ public:
/// Dumps the namespace with respect to the compilation phase
void dump();
void enqueueError(llvm::StringRef e);
~Namespace();
};

View File

@ -24,9 +24,10 @@
#ifndef SERENE_SLIR_DIALECT_H_
#define SERENE_SLIR_DIALECT_H_
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Dialect.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/Dialect.h>
#include <mlir/Interfaces/ControlFlowInterfaces.h>
#include <mlir/Interfaces/SideEffectInterfaces.h>
// Include the auto-generated header file containing the declaration of the
// serene's dialect.

View File

@ -2,10 +2,15 @@
#define SERENE_DIALECT
include "mlir/IR/OpBase.td"
include "mlir/IR/OpAsmInterface.td"
include "mlir/IR/SymbolInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
include "mlir/Interfaces/CallInterfaces.td"
include "mlir/Interfaces/CastInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/DataLayoutInterfaces.td"
include "mlir/Interfaces/VectorInterfaces.td"
// Dialect definition. It will directly generate the SereneDialect class
@ -32,49 +37,17 @@ class Serene_Op<string mnemonic, list<OpTrait> traits = []> :
class Serene_Type<string name> : TypeDef<Serene_Dialect, name> { }
// def SymbolType : Serene_Type<"Symbol"> {
// let mnemonic = "symbol";
// let summary = "A typical Lisp symbol";
// let description = [{
// A symbol is just a name and nothing more. Just a name
// to give to a value or to use it as it is.
// }];
// // let cppNamespace = "::serene::sir";
// let parameters = (ins "std::string":$name);
// // We define the printer inline.
// let printer = [{
// $_printer << "Symbol<" << getImpl()->name << ">";
// }];
// // The parser is defined here also.
// let parser = [{
// if ($_parser.parseLess())
// return Type();
// std::string name;
// if ($_parser.parseInteger(name))
// return Type();
// return get($_ctxt, name);
// }];
// }
def ValueOp: Serene_Op<"value"> {
let summary = "This operation represent a value";
let description = [{
some description
ValueOp
}];
let arguments = (ins I64Attr:$value);
let results = (outs I64);
// let verifier = [{ return serene::sir::verify(*this); }];
//let verifier = [{ return serene::sir::verify(*this); }];
let builders = [
OpBuilder<(ins "int":$value), [{
@ -85,9 +58,12 @@ def ValueOp: Serene_Op<"value"> {
];
}
// TODO: Add the FunctionLike trait here and include its header file in dialect.h
def FnOp: Serene_Op<"fn", [
AffineScope, AutomaticAllocationScope,
IsolatedFromAbove
IsolatedFromAbove,
]> {
let summary = "This operation is just a place holder for a function";
@ -102,23 +78,21 @@ def FnOp: Serene_Op<"fn", [
OptionalAttr<StrAttr>:$sym_visibility);
let regions = (region AnyRegion:$body);
let results = (outs NoneType);
let results = (outs I64);
// let builders = [
// OpBuilder<(ins
// "llvm::StringRef":$name, "mlir::FunctionType":$type,
// CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs,
// CArg<"llvm::ArrayRef<mlir::DictionaryAttr>", "{}">:$argAttrs)
// >];
// let extraClassDeclaration = [{
// static FnOp create(mlir::Location location, llvm::StringRef name, mlir::FunctionType type,
// llvm::ArrayRef<mlir::NamedAttribute> attrs = {});
// static FnOp create(mlir::Location location, llvm::StringRef name, mlir::FunctionType type,
// mlir::Operation::dialect_attr_range attrs);
// static FnOp create(mlir::Location location, llvm::StringRef name, mlir::FunctionType type,
// llvm::ArrayRef<mlir::NamedAttribute> attrs,
// llvm::ArrayRef<mlir::DictionaryAttr> argAttrs);
// }];
}
def ReturnOp: Serene_Op<"return", [NoSideEffect, HasParent<"FnOp">,
ReturnLike, Terminator]> {
let summary = "This operation marks the return value of a function";
let description = [{
ReturnOp
}];
let arguments = (ins AnyType:$operand);
let assemblyFormat =
[{ attr-dict $operand `:` type($operand) }];
}
#endif // SERENE_DIALECT

View File

@ -32,7 +32,7 @@
namespace serene {
void SereneContext::insertNS(std::shared_ptr<Namespace> ns) {
namespaces[ns->name.str()] = ns;
namespaces[ns->name] = ns;
};
std::shared_ptr<Namespace> SereneContext::getNS(llvm::StringRef ns_name) {

View File

@ -28,12 +28,11 @@
#include "serene/reader/location.h"
#include "serene/source_mgr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/FormatAdapters.h>
#include <llvm/Support/FormatVariadic.h>
#include <llvm/Support/WithColor.h>
#include <llvm/Support/raw_ostream.h>
#include <memory>
namespace serene {
@ -166,6 +165,11 @@ Diagnostic DiagnosticEngine::toDiagnostic(reader::LocationRange loc,
return Diagnostic(ctx, loc, &e, msg, fn);
};
void DiagnosticEngine::enqueueError(llvm::StringRef msg) {
llvm::errs() << llvm::formatv("FIX ME (better emit error): {0}\n", msg);
exit(1);
};
void DiagnosticEngine::emitSyntaxError(reader::LocationRange loc,
errors::ErrorVariant &e,
llvm::StringRef msg) {

View File

@ -28,6 +28,7 @@
#include "serene/errors/constants.h"
#include "serene/exprs/expression.h"
#include "serene/llvm/IR/Value.h"
#include "serene/reader/semantics.h"
#include "serene/slir/slir.h"
#include <llvm/ADT/StringRef.h>
@ -36,6 +37,8 @@
#include <memory>
#include <mlir/IR/Builders.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/Verifier.h>
#include <mlir/Support/LogicalResult.h>
#include <stdexcept>
#include <string>
@ -54,15 +57,31 @@ Namespace::Namespace(SereneContext &ctx, llvm::StringRef ns_name,
}
};
void Namespace::enqueueError(llvm::StringRef e) {
ctx.diagEngine->enqueueError(e);
}
exprs::Ast &Namespace::getTree() { return this->tree; }
mlir::LogicalResult Namespace::setTree(exprs::Ast &t) {
if (initialized) {
mlir::LogicalResult Namespace::expandTree(exprs::Ast &ast) {
if (ctx.getTargetPhase() == CompilationPhase::Parse) {
// we just want the raw AST
this->tree.insert(this->tree.end(), ast.begin(), ast.end());
return mlir::success();
}
// Run the semantic analyer on the ast and then if everything
// is ok expand the currnt tree by the semantically correct ast.
auto maybeAst = reader::analyze(ctx, ast);
if (!maybeAst) {
enqueueError("Semantic analysis failed!");
return mlir::failure();
}
this->tree = std::move(t);
this->initialized = true;
auto semanticAst = std::move(maybeAst.getValue());
this->tree.insert(this->tree.end(), semanticAst.begin(), semanticAst.end());
return mlir::success();
}
@ -73,7 +92,8 @@ SereneContext &Namespace::getContext() { return this->ctx; };
MaybeModuleOp Namespace::generate() {
mlir::OpBuilder builder(&ctx.mlirContext);
// TODO: Fix the unknown location by pointing to the `ns` form
auto module = mlir::ModuleOp::create(builder.getUnknownLoc(), name);
auto module = mlir::ModuleOp::create(builder.getUnknownLoc(),
llvm::Optional<llvm::StringRef>(name));
// Walk the AST and call the `generateIR` function of each node.
// Since nodes will have access to the a reference of the
@ -83,6 +103,12 @@ MaybeModuleOp Namespace::generate() {
x->generateIR(*this, module);
}
if (mlir::failed(mlir::verify(module))) {
module.emitError("Can't verify the module");
module.erase();
return llvm::None;
}
if (mlir::failed(runPasses(module))) {
// TODO: Report a proper error
module.emitError("Failure in passes!");
@ -105,7 +131,10 @@ void Namespace::dump() {
return;
}
maybeModuleOp.getValue()->dump();
mlir::OpPrintingFlags flags;
flags.enableDebugInfo();
maybeModuleOp.getValue()->print(llvm::outs(), flags);
};
MaybeModule Namespace::compileToLLVM() {

View File

@ -55,6 +55,7 @@ NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
std::string importedFile;
auto path = convertNamespaceToPath(name);
// TODO: Fix this to enqueue a proper error instead
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> newBufOrErr(
std::make_error_code(std::errc::no_such_file_or_directory));
@ -84,6 +85,7 @@ NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
if (bufferId == 0) {
auto msg = llvm::formatv("Couldn't add namespace '{0}'", name);
// TODO: enqueue the error here
ctx.diagEngine->emitSyntaxError(importLoc, errors::NSAddToSMError,
llvm::StringRef(msg));
@ -99,14 +101,9 @@ NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
llvm::Optional(llvm::StringRef(importedFile)));
if (!maybeAst) {
SMGR_LOG("Couldn't Read namespace: " + name)
return nullptr;
}
// Perform the semantic analytics
auto afterAst = reader::analyze(ctx, maybeAst.getValue());
if (!afterAst) {
// TODO: Do we need raise an error here too?
SMGR_LOG("Couldn't Read namespace: " + name);
// The code that is calling this function has to flush the dia engine, so we
// don't have to emit an error
return nullptr;
}
@ -114,8 +111,11 @@ NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
auto ns =
makeNamespace(ctx, name, llvm::Optional(llvm::StringRef(importedFile)));
if (mlir::failed(ns->setTree(afterAst.getValue()))) {
SMGR_LOG("Couldn't set the AST for namespace: " + name)
if (mlir::failed(ns->expandTree(maybeAst.getValue()))) {
SMGR_LOG("Couldn't set the AST for namespace: " + name);
// The code that is calling this function has to flush the dia engine, so we
// don't have to emit an error
return nullptr;
}