Remove the old libserene implementations
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/manual/docs Pipeline failed Details
ci/woodpecker/manual/build Pipeline failed Details

This commit is contained in:
Sameer Rahmani 2023-07-30 15:12:56 +01:00
parent 1ef8a5f0ca
commit d54835a961
Signed by: lxsameer
GPG Key ID: B0A4AF28AB9FD90B
115 changed files with 0 additions and 12487 deletions

View File

@ -1,81 +0,0 @@
# Serene Programming Language
#
# Copyright (c) 2019-2023 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_directories(${INCLUDE_DIR})
add_subdirectory(include)
add_subdirectory(lib)
# Install rules for libserene target
install(TARGETS serene
EXPORT SereneExports
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
# Install rules for the public header files.
install(DIRECTORY ${INCLUDE_DIR}/serene
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN *.h
PATTERN *.td
PATTERN "CMake*" EXCLUDE)
# Install rule for the public generated header files
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN *.h
PATTERN *.td
PATTERN *.h.inc
PATTERN "CMake*" EXCLUDE)
include(CMakePackageConfigHelpers)
# Package config file let us use find_package with serene
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
)
write_basic_package_version_file(
"${PROJECT_NAME}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
)
# Install the package exports
install(EXPORT SereneExports
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
NAMESPACE serene::)
# Testing only available if this is the main app
# Emergency override SERENE_CMAKE_BUILD_TESTING provided as well
if(SERENE_BUILD_TESTING)
message("Build the test binary")
add_subdirectory(tests)
endif()

View File

@ -1,21 +0,0 @@
# Serene Programming Language
#
# Copyright (c) 2019-2023 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/>.
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Exports.cmake")
check_required_components("@PROJECT_NAME@")

View File

@ -1,3 +0,0 @@
add_subdirectory("serene/slir/")
add_subdirectory("serene/passes/")
add_subdirectory("serene/errors/")

View File

@ -1,16 +0,0 @@
#ifndef CONFIG_H
#define CONFIG_H
// the configured options and settings
#define SERENE_VERSION "@PROJECT_VERSION@"
// Why so obvious? to make the linter shutup :))
#define I8_SIZE 8
#define I32_SIZE 32
#define I64_SIZE 64
// Should we build the support for MLIR CL OPTIONS?
#cmakedefine SERENE_WITH_MLIR_CL_OPTION
#endif

View File

@ -1,285 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_CONTEXT_H
#define SERENE_CONTEXT_H
#include "serene/diagnostics.h"
#include "serene/environment.h"
#include "serene/export.h"
#include "serene/jit/halley.h"
#include "serene/namespace.h"
#include "serene/passes.h"
#include "serene/slir/dialect.h"
#include "serene/source_mgr.h"
#include <llvm/ADT/DenseMap.h>
#include <llvm/ADT/None.h>
#include <llvm/ADT/Optional.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/ADT/Triple.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/Support/Host.h>
#include <mlir/Dialect/Func/IR/FuncOps.h>
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
#include <mlir/IR/MLIRContext.h>
#include <mlir/Pass/PassManager.h>
#include <memory>
#define DEFAULT_NS_NAME "serene.user"
#define INTERNAL_NS "serene.internal"
namespace serene {
class SereneContext;
namespace reader {
class LocationRange;
} // namespace reader
namespace exprs {
class Expression;
using Node = std::shared_ptr<Expression>;
} // namespace exprs
/// This enum describes the different operational phases for the compiler
/// in order. Anything below `NoOptimization` is considered only for debugging
enum class CompilationPhase {
Parse,
Analysis,
SLIR,
MLIR, // Lowered slir to other dialects
LIR, // Lowered to the llvm ir dialect
IR, // Lowered to the LLVMIR itself
NoOptimization,
O1,
O2,
O3,
};
/// Terminates the serene compiler process in a thread safe manner
SERENE_EXPORT void terminate(SereneContext &ctx, int exitCode);
/// Options describes the compiler options that can be passed to the
/// compiler via command line. Anything that user should be able to
/// tweak about the compiler has to end up here regardless of the
/// different subsystem that might use it.
struct SERENE_EXPORT Options {
/// Whether to use colors for the output or not
bool withColors = true;
// JIT related flags
bool JITenableObjectCache = true;
bool JITenableGDBNotificationListener = true;
bool JITenablePerfNotificationListener = true;
bool JITLazy = false;
Options() = default;
};
class SERENE_EXPORT SereneContext {
public:
template <typename T>
using CurrentNSFn = std::function<T()>;
mlir::MLIRContext mlirContext;
mlir::PassManager pm;
std::unique_ptr<DiagnosticEngine> diagEngine;
std::unique_ptr<serene::jit::Halley> jit;
/// The source manager is responsible for loading namespaces and practically
/// managing the source code in form of memory buffers.
SourceMgr sourceManager;
/// The set of options to change the compilers behaivoirs
Options opts;
const llvm::Triple triple;
/// 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.
void insertNS(NSPtr &ns);
/// Execute the given function \p f by setting the `currentNS`
/// to the given \p nsName. It will restore the value of `currentNS`
/// after \p f returned.
template <typename T>
T withCurrentNS(llvm::StringRef nsName, CurrentNSFn<T> f) {
assert(!currentNS.empty() && "The currentNS is not initialized!");
auto tmp = this->currentNS;
this->currentNS = nsName.str();
T res = f();
this->currentNS = tmp;
return res;
};
// void specialization
template <>
void withCurrentNS(llvm::StringRef nsName, CurrentNSFn<void> f) {
assert(!currentNS.empty() && "The currentNS is not initialized!");
auto tmp = this->currentNS;
this->currentNS = nsName.str();
f();
this->currentNS = tmp;
}
/// Return the current namespace that is being processed at the moment
Namespace &getCurrentNS();
/// Lookup the namespace with the give name in the current context and
/// return a pointer to it or a `nullptr` in it doesn't exist.
Namespace *getNS(llvm::StringRef nsName);
/// Lookup and return a shared pointer to the given \p ns_name. This
/// method should be used only if you need to own the namespace as well
/// and want to keep it long term (like the JIT).
NSPtr getSharedPtrToNS(llvm::StringRef nsName);
SereneContext(Options &options)
: pm(&mlirContext), diagEngine(makeDiagnosticEngine(*this)),
opts(options), triple(llvm::sys::getDefaultTargetTriple()),
targetPhase(CompilationPhase::NoOptimization) {
mlirContext.getOrLoadDialect<serene::slir::SereneDialect>();
mlirContext.getOrLoadDialect<mlir::func::FuncDialect>();
mlirContext.getOrLoadDialect<mlir::LLVM::LLVMDialect>();
// We need to create one empty namespace, so that the JIT can
// start it's operation.
auto ns = Namespace::make(*this, DEFAULT_NS_NAME, std::nullopt);
insertNS(ns);
currentNS = ns->name;
// TODO: Get the crash report path dynamically from the cli
// pm.enableCrashReproducerGeneration("/home/lxsameer/mlir.mlir");
};
/// Set the target compilation phase of the compiler. The compilation
/// phase dictates the behavior and the output type of the compiler.
void setOperationPhase(CompilationPhase phase);
CompilationPhase getTargetPhase() { return targetPhase; };
int getOptimizatioLevel();
// Namespace stuff ---
/// Create an empty namespace with the given \p name and optional \p filename
/// and then insert it into the context
NSPtr makeNamespace(llvm::StringRef name,
std::optional<llvm::StringRef> filename);
/// Read a namespace with the given \p name and returns a share pointer
/// to the name or an Error.
///
/// It just `read` the namespace by parsing it and running the semantic
/// analyzer on it.
MaybeNS readNamespace(const std::string &name);
MaybeNS readNamespace(const std::string &name, reader::LocationRange loc);
/// Reads and add the namespace with the given \p name to the context. The
/// namespace will be added to the context and the JIT engine as well.
///
/// It will \r a shared pointer to the namespace or an error tree.
MaybeNS importNamespace(const std::string &name);
MaybeNS importNamespace(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(Options &options) {
auto ctx = std::make_unique<SereneContext>(options);
auto *ns = ctx->getNS(DEFAULT_NS_NAME);
assert(ns != nullptr && "Default ns doesn't exit!");
auto maybeJIT = serene::jit::makeHalleyJIT(*ctx);
if (!maybeJIT) {
auto err = maybeJIT.takeError();
panic(*ctx, err);
}
ctx->jit.swap(*maybeJIT);
// Make serene.user which is the defult NS available on the
// JIT
auto loc = reader::LocationRange::UnknownLocation(INTERNAL_NS);
if (auto err = ctx->jit->addNS(*ns, loc)) {
panic(*ctx, err);
}
return ctx;
};
// JIT JITDylib related functions ---
// TODO: For Dylib related functions, make sure that the namespace in questoin
// is aleady registered in the context
/// Return a pointer to the most registered JITDylib of the given \p ns
////name
llvm::orc::JITDylib *getLatestJITDylib(Namespace &ns);
/// Register the given pointer to a `JITDylib` \p l, with the give \p ns.
void pushJITDylib(Namespace &ns, llvm::orc::JITDylib *l);
/// Returns the number of registered `JITDylib` for the given \p ns.
size_t getNumberOfJITDylibs(Namespace &ns);
private:
CompilationPhase targetPhase;
// TODO: We need to keep different instances of the namespace
// because if any one of them gets cleaned up via reference
// count (if we are still using shared ptr for namespaces if not
// remove this todo) then we will end up with dangling references
// it the JIT
/// The namespace table. Every namespace that needs to be compiled has
/// to register itself with the context and appear on this table.
/// This table acts as a cache as well.
std::map<std::string, NSPtr> 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 currentNS;
/// A vector of pointers to all the jitDylibs for namespaces. Usually
/// There will be only one pre NS but in case of forceful reloads of a
/// namespace there will be more.
llvm::StringMap<llvm::SmallVector<llvm::orc::JITDylib *, 1>> jitDylibs;
};
/// Creates a new context object. Contexts are used through out the compilation
/// process to store the state.
///
/// \p opts is an instance of \c Options that can be used to set options of
/// of the compiler.
SERENE_EXPORT std::unique_ptr<SereneContext>
makeSereneContext(Options opts = Options());
} // namespace serene
#endif

View File

@ -1,39 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_CONVENTIONS_H
#define SERENE_CONVENTIONS_H
#include "serene/config.h"
#include <llvm/ADT/StringRef.h>
#include <string>
namespace serene {
// static std::string mangleInternalStringName(llvm::StringRef str) {
// return "__serene__internal__str__" + str.str();
// }
// static std::string mangleInternalSymName(llvm::StringRef str) {
// return "__serene__symbol__" + str.str();
// }
} // namespace serene
#endif

View File

@ -1,127 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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:
* `DiagEngine` is in charge of error handling of the compiler. It receives
* the incoming errors (`llvm::Error`) and print them out to stderr.
*
* Errors might raise from different contextes and the incoming channel might
* vary. For exmaple in many case we just return `llvm::Error` from functions
* and propagate them to the highest level call site and deal with them there
* or during the pass management we call the `emit()` function on operations
* to report back an error.
*
* Serene extends `llvm::Error`. For more info have a look at
* `serene/errors/base.h` and the `serene/errors/errors.td`.
*/
#ifndef SERENE_DIAGNOSTICS_H
#define SERENE_DIAGNOSTICS_H
#include "serene/errors.h"
#include "serene/export.h"
#include "serene/reader/location.h"
#include "serene/source_mgr.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/raw_ostream.h>
#include <mlir/IR/Diagnostics.h>
#include <memory>
namespace serene {
class SereneContext;
class DiagnosticEngine;
// TODO: Finish up the Diagnostic interface and utility functions
// to build diagnostics from Errors
class Diagnostic {
// TODO: Add support for llvm::SMFixIt
friend DiagnosticEngine;
enum Type {
Error,
// TODO: Add support for remarks and notes
Remark,
Note,
};
SereneContext &ctx;
reader::LocationRange loc;
std::string fn;
llvm::Error *err = nullptr;
Type type = Type::Error;
std::string message, lineContents;
std::string getPrefix(llvm::StringRef prefix = "");
public:
Diagnostic(SereneContext &ctx, reader::LocationRange loc, llvm::Error *e,
llvm::StringRef msg, llvm::StringRef fn = "")
: ctx(ctx), loc(loc), fn(fn), err(e), message(msg){};
protected:
void print(llvm::raw_ostream &os, llvm::StringRef prefix = "") const;
void writeColorByType(llvm::raw_ostream &os, llvm::StringRef str);
};
/// DiagnosticEngine is the central hub for dealing with errors in Serene. It
/// integrates with MLIR's diag engine and LLVM's error system to handle error
/// reporting for Serene's compiler
class DiagnosticEngine {
SereneContext &ctx;
mlir::DiagnosticEngine &diagEngine;
Diagnostic toDiagnostic(reader::LocationRange loc, llvm::Error &e,
llvm::StringRef msg, llvm::StringRef fn = "");
void print(llvm::raw_ostream &os, Diagnostic &d);
public:
DiagnosticEngine(SereneContext &ctx);
void enqueueError(llvm::StringRef msg);
void emitSyntaxError(reader::LocationRange loc, llvm::Error &e,
llvm::StringRef msg = "");
void emit(const llvm::Error &err);
};
/// Create a new instance of the `DiagnosticEngine` from the give
/// `SereneContext`
std::unique_ptr<DiagnosticEngine> makeDiagnosticEngine(SereneContext &ctx);
// ----------------------------------------------------------------------------
// Public API
// ----------------------------------------------------------------------------
/// Throw out an error with the given \p and terminate the execution.
SERENE_EXPORT void panic(SereneContext &ctx, llvm::Twine msg);
/// Throw out the give error \p err and stop the execution.
SERENE_EXPORT void panic(SereneContext &ctx, const llvm::Error &err);
/// Throw the give `llvm::Error` \p errs to the stderr.
SERENE_EXPORT void throwErrors(SereneContext &ctx, const llvm::Error &err);
} // namespace serene
#endif

View File

@ -1,80 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_TEST_ENVIRONMENT_H
#define SERENE_TEST_ENVIRONMENT_H
#include "serene/utils.h"
#include <llvm/ADT/StringMap.h>
#include <mlir/Support/LogicalResult.h>
namespace serene {
/// This class represents a classic lisp environment (or scope) that holds the
/// bindings from type `K` to type `V`. For example an environment of symbols
/// to expressions would be `Environment<Symbol, Node>`
template <typename V>
class Environment {
Environment<V> *parent;
using StorageType = llvm::StringMap<V>;
// The actual bindings storage
StorageType pairs;
public:
Environment() : parent(nullptr) {}
Environment(Environment *parent) : parent(parent){};
/// Look up the given `key` in the environment and return it.
std::optional<V> lookup(llvm::StringRef key) {
if (auto value = pairs.lookup(key)) {
return value;
}
if (parent) {
return parent->lookup(key);
}
return std::nullopt;
};
/// Insert the given `key` with the given `value` into the storage. This
/// operation will shadow an aleady exist `key` in the parent environment
mlir::LogicalResult insert_symbol(llvm::StringRef key, V value) {
auto result = pairs.insert_or_assign(key, value);
UNUSED(result);
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
#endif

View File

@ -1,82 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_ERRORS_H
#define SERENE_ERRORS_H
#include "serene/export.h"
#include "serene/reader/location.h"
#define GET_CLASS_DEFS
#include "serene/errors/errs.h.inc"
#include <llvm/Support/Casting.h>
#include <llvm/Support/Error.h>
namespace serene {
class SereneContext;
} // namespace serene
namespace serene::errors {
class SERENE_EXPORT SereneError : public llvm::ErrorInfo<SereneError> {
public:
static char ID;
ErrorType errorType;
SereneContext &ctx;
reader::LocationRange location;
std::string msg;
void log(llvm::raw_ostream &os) const override { os << msg; }
std::error_code convertToErrorCode() const override {
// TODO: Fix this by creating a mapping from ErrorType to standard
// errc or return the ErrorType number instead
return std::make_error_code(std::errc::io_error);
}
SereneError(SereneContext &ctx, ErrorType errtype, reader::LocationRange &loc)
: errorType(errtype), ctx(ctx), location(loc){};
SereneError(SereneContext &ctx, ErrorType errtype, reader::LocationRange &loc,
llvm::StringRef msg)
: errorType(errtype), ctx(ctx), location(loc), msg(msg.str()){};
reader::LocationRange &where() { return location; };
};
/// Create and return a Serene flavored `llvm::Error` by passing the parameters
/// directly to the constructor of type `E`.
///
/// This is the official way of creating error objects in Serene.
template <typename... Args>
SERENE_EXPORT llvm::Error makeError(SereneContext &ctx, ErrorType errtype,
Args &&...args) {
return llvm::make_error<SereneError>(ctx, errtype,
std::forward<Args>(args)...);
};
/// Returns the messange that the given error \p e is holding. It doesn't cast
/// the error to a concrete error type.
SERENE_EXPORT std::string getMessage(const llvm::Error &e);
SERENE_EXPORT const ErrorVariant *getVariant(ErrorType t);
} // namespace serene::errors
#endif

View File

@ -1,5 +0,0 @@
set(LLVM_TARGET_DEFINITIONS errors.td)
serene_tablegen(errs.h.inc -errors-backend)
add_public_tablegen_target(SereneErrorGen)

View File

@ -1,65 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_ERRORS_BASE_H
#define SERENE_ERRORS_BASE_H
#include "serene/export.h"
#include "serene/reader/location.h"
#define GET_CLASS_DEFS
#include "serene/errors/errs.h.inc"
#include <system_error>
#include <llvm/Support/Error.h>
#include <llvm/Support/FormatVariadic.h>
namespace serene::errors {
class SERENE_EXPORT Error {};
class SERENE_EXPORT SereneError : public llvm::ErrorInfo<SereneError> {
public:
static char ID;
ErrorType errorType;
SereneContext &ctx;
reader::LocationRange location;
std::string msg;
void log(llvm::raw_ostream &os) const override { os << msg; }
std::error_code convertToErrorCode() const override {
// TODO: Fix this by creating a mapping from ErrorType to standard
// errc or return the ErrorType number instead
return std::make_error_code(std::errc::io_error);
}
SereneError(SereneContext &ctx, ErrorType errtype, reader::LocationRange &loc)
: errorType(errtype), ctx(ctx), location(loc){};
SereneError(SereneContext &ctx, ErrorType errtype, reader::LocationRange &loc,
llvm::StringRef msg)
: errorType(errtype), ctx(ctx), location(loc), msg(msg.str()){};
reader::LocationRange &where() { return location; };
};
}; // namespace serene::errors
#endif

View File

@ -1,76 +0,0 @@
class Error<string _desc = "", string _help = ""> {
string desc = _desc;
string help = _help;
}
// Examples of how to define a new error
// def Err : Error<"Err1 titel"> {
// let description = [{
// err1
// multiline
// desc}];
// }
// def Err2 : Error {
// let title = "err 2 titel";
// let description = "err2 desc";
// }
// def Err3 : Error<"err3", [{
// err3
// multiline
// desc
// }]>;
def UnknownError: Error<"Can't find any description for this error.">;
def DefExpectSymbol: Error<"The first argument to 'def' has to be a Symbol.">;
def DefWrongNumberOfArgs: Error<"Wrong number of arguments is passed to the 'def' form.">;
def FnNoArgsList: Error<"'fn' form requires an argument list.">;
def FnArgsMustBeList: Error<"'fn' arguments should be a list.">;
def CantResolveSymbol: Error<"Can't resolve the given name.">;
def DontKnowHowToCallNode: Error<"Don't know how to call the given expression.">;
def PassFailureError: Error<"Pass Failure.">;
def NSLoadError: Error<"Faild to find a namespace.">;
def NSAddToSMError: Error<"Faild to add the namespace to the source manager.">;
def
EOFWhileScaningAList: Error<"EOF reached before closing of list">;
def InvalidDigitForNumber: Error<"Invalid digit for a number.">;
def
TwoFloatPoints: Error<"Two or more float point characters in a number">;
def
InvalidCharacterForSymbol: Error<"Invalid character for a symbol">;
def CompilationError: Error<"Compilation error!">;
defvar errorsIndex = [
UnknownError,
DefExpectSymbol,
DefWrongNumberOfArgs,
FnNoArgsList,
FnArgsMustBeList,
CantResolveSymbol,
DontKnowHowToCallNode,
PassFailureError,
NSLoadError,
NSAddToSMError,
EOFWhileScaningAList,
InvalidDigitForNumber,
TwoFloatPoints,
InvalidCharacterForSymbol,
CompilationError,
];

View File

@ -1,43 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_ERRORS_VARIANT_H
#define SERENE_ERRORS_VARIANT_H
#include <string>
namespace serene::errors {
// This class is used in the generated code
struct ErrorVariant {
const int id;
const std::string title;
const std::string desc;
const std::string help;
static ErrorVariant make(const int id, const char *t, const char *d,
const char *h) {
return ErrorVariant(id, t, d, h);
};
private:
ErrorVariant(const int id, const char *t, const char *d, const char *h)
: id(id), title(t), desc(d), help(h){};
};
} // namespace serene::errors
#endif

View File

@ -1,76 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_EXPRS_CALL_H
#define SERENE_EXPRS_CALL_H
#include "serene/context.h"
#include "serene/errors.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/list.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/Error.h>
#include <memory>
#include <string>
namespace serene {
namespace exprs {
class List;
/// This data structure represents a function. with a collection of
/// arguments and the ast of a body
class Call : public Expression {
public:
Node target;
Ast params;
Call(reader::LocationRange &loc, Node &target, Ast &params)
: Expression(loc), target(target), params(params){};
Call(Call &) = delete;
ExprType getType() const override;
std::string toString() const override;
MaybeNode analyze(semantics::AnalysisState &state) override;
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override {
UNUSED(ns);
UNUSED(m);
};
static bool classof(const Expression *e);
/// Creates a call node out of a list.
/// For exmaple: `(somefn (param1 param2) param3)`. This function
/// is supposed to be used in the semantic analysis phase.
///
/// \param ctx The semantic analysis context object.
/// \param env The environment that this node exists in.
/// \param list the list in question.
static MaybeNode make(semantics::AnalysisState &state, List *list);
~Call() = default;
};
} // namespace exprs
} // namespace serene
#endif

View File

@ -1,72 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_EXPRS_DEF_H
#define SERENE_EXPRS_DEF_H
#include "serene/context.h"
#include "serene/errors.h"
#include "serene/exprs/expression.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/Error.h>
#include <memory>
#include <string>
namespace serene {
namespace exprs {
class List;
/// This data structure represents the operation to define a new binding via
/// the `def` special form.
class Def : public Expression {
public:
std::string binding;
Node value;
Def(reader::LocationRange &loc, llvm::StringRef binding, Node &v)
: Expression(loc), binding(binding), value(v){};
Def(Def &d) = delete;
ExprType getType() const override;
std::string toString() const override;
MaybeNode analyze(semantics::AnalysisState &state) override;
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override;
static bool classof(const Expression *e);
/// Create a Def node out a list. The list should contain the
/// correct `def` form like `(def blah value)`. This function
/// is supposed to be used in the semantic analysis phase.
///
/// \param state is the semantic analysis state to use in creation time.
/// \param list the list containing the `def` form
static MaybeNode make(semantics::AnalysisState &state, List *list);
~Def() = default;
};
} // namespace exprs
} // namespace serene
#endif

View File

@ -1,148 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_EXPRS_EXPRESSION_H
#define SERENE_EXPRS_EXPRESSION_H
#include "serene/context.h"
#include "serene/errors.h"
#include "serene/exprs/traits.h"
#include "serene/namespace.h"
#include "serene/reader/location.h"
#include "serene/semantics.h"
#include "serene/utils.h"
#include <llvm/Support/Error.h>
#include <mlir/IR/BuiltinOps.h>
#include <memory>
namespace serene {
/// Contains all the builtin AST expressions including those which do not appear
/// in the syntax directly. Like function definitions.
namespace exprs {
class Expression;
using Node = std::shared_ptr<Expression>;
using MaybeNode = llvm::Expected<Node>;
using Ast = std::vector<Node>;
using MaybeAst = llvm::Expected<Ast>;
constexpr static auto EmptyNode = nullptr;
/// The base class of the expressions which provides the common interface for
/// the expressions to implement.
class SERENE_EXPORT Expression {
public:
/// The location range provide information regarding to where in the input
/// string the current expression is used.
reader::LocationRange location;
Expression(const reader::LocationRange &loc) : location(loc){};
virtual ~Expression() = default;
/// Returns the type of the expression. We need this funciton to perform
/// dynamic casting of expression object to implementations such as lisp or
/// symbol.
virtual ExprType getType() const = 0;
/// The AST representation of an expression
virtual std::string toString() const = 0;
/// Analyzes the semantics of current node and return a new node in case
/// that we need to semantically rewrite the current node and replace it with
/// another node. For example to change from a List containing `(def a b)`
/// to a `Def` node that represents defining a new binding.
///
/// \param state is the analysis state object of the semantic analyzer.
virtual MaybeNode analyze(semantics::AnalysisState &state) = 0;
/// Genenates the correspondig SLIR of the expressoin and attach it to the
/// given module.
///
/// \param ns The namespace that current expression is in it.
/// \param m The target MLIR moduleOp to attach the operations to
virtual void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) = 0;
};
/// Create a new `node` of type `T` and forwards any given parameter
/// to the constructor of type `T`. This is the **official way** to create
/// a new `Expression`. Here is an example:
/// \code
/// auto list = make<List>();
/// \endcode
///
/// \param[args] Any argument with any type passed to this function will be
/// passed to the constructor of type T.
/// \return A unique pointer to an Expression
template <typename T, typename... Args>
Node make(Args &&...args) {
return std::make_shared<T>(std::forward<Args>(args)...);
};
/// Create a new `node` of type `T` and forwards any given parameter
/// to the constructor of type `T`. This is the **official way** to create
/// a new `Expression`. Here is an example:
/// \code
/// auto list = makeAndCast<List>();
/// \endcode
///
/// \param[args] Any argument with any type passed to this function will be
/// passed to the constructor of type T.
/// \return A unique pointer to a value of type T.
template <typename T, typename... Args>
std::shared_ptr<T> makeAndCast(Args &&...args) {
return std::make_shared<T>(std::forward<Args>(args)...);
};
/// The helper function to create a new `Node` and returnsit. It should be useds
/// where every we want to return a `MaybeNode` successfully.
template <typename T, typename... Args>
MaybeNode makeSuccessfulNode(Args &&...args) {
return make<T>(std::forward<Args>(args)...);
};
/// The hlper function to creates an Error (`llvm::Error`) by passing all
/// the given arguments to the constructor of the template param `E`.
template <typename E, typename T = Node, typename... Args>
llvm::Expected<T> makeErrorful(Args &&...args) {
return llvm::make_error<E>(std::forward<Args>(args)...);
};
/// The hlper function to creates an Error (`llvm::Error`) by passing all
/// the given arguments to the constructor of the template param `E`.
template <typename E, typename... Args>
MaybeNode makeErrorNode(Args &&...args) {
return makeErrorful<E, Node>(std::forward<Args>(args)...);
};
/// Convert the given AST to string by calling the `toString` method
/// of each node.
SERENE_EXPORT std::string astToString(const Ast *);
/// Converts the given ExprType to string.
std::string stringifyExprType(ExprType);
/// Converts the given AST to string and prints it out
void dump(Ast &);
} // namespace exprs
} // namespace serene
#endif

View File

@ -1,77 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_EXPRS_FN_H
#define SERENE_EXPRS_FN_H
#include "serene/context.h"
#include "serene/errors.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/list.h"
#include "serene/namespace.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/Error.h>
#include <memory>
#include <string>
namespace serene {
namespace exprs {
class List;
/// This data structure represents a function. with a collection of
/// arguments and the ast of a body
class Fn : public Expression {
public:
std::string name;
// TODO: Use a coll type instead of a list here
List args;
Ast body;
Fn(SereneContext &ctx, reader::LocationRange &loc, List &args, Ast body);
Fn(Fn &f) = delete;
ExprType getType() const override;
std::string toString() const override;
MaybeNode analyze(semantics::AnalysisState &state) override;
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override;
static bool classof(const Expression *e);
/// Creates a function node out of a function definition
/// in a list. the list has to contain the correct definition
/// of a function, for exmaple: `(fn (args1 arg2) body)`.This function
/// is supposed to be used in the semantic analysis phase.
///
/// \param state is the semantic analysis state to use.
/// \param list the list containing the `fn` form
static MaybeNode make(semantics::AnalysisState &state, List *list);
void setName(std::string);
~Fn() = default;
};
} // namespace exprs
} // namespace serene
#endif

View File

@ -1,88 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_EXPRS_LIST_H
#define SERENE_EXPRS_LIST_H
#include "serene/context.h"
#include "serene/export.h"
#include "serene/exprs/expression.h"
#include <llvm/ADT/Optional.h>
#include <string>
namespace serene {
namespace exprs {
/// This class represents a List in the AST level and not the List as the data
/// type.
class SERENE_EXPORT List : public Expression {
public:
// Internal elements of the lest (small vector of shared pointers to
// expressions)
Ast elements;
List(const List &l); // Copy ctor
List(List &&e) noexcept = default; // Move ctor
List(const reader::LocationRange &loc) : Expression(loc){};
List(const reader::LocationRange &loc, Node &e);
List(const reader::LocationRange &loc, Ast elems);
ExprType getType() const override;
std::string toString() const override;
void append(Node);
size_t count() const;
Ast from(uint index);
std::optional<Expression *> at(uint index);
/// Return an iterator to be used with the `for` loop. It's implicitly called
/// by the for loop.
std::vector<Node>::const_iterator cbegin();
/// Return an iterator to be used with the `for` loop. It's implicitly called
/// by the for loop.
std::vector<Node>::const_iterator cend();
/// Return an iterator to be used with the `for` loop. It's implicitly called
/// by the for loop.
std::vector<Node>::iterator begin();
/// Return an iterator to be used with the `for` loop. It's implicitly called
/// by the for loop.
std::vector<Node>::iterator end();
MaybeNode analyze(semantics::AnalysisState &state) override;
// NOLINTNEXTLINE(readability-named-parameter)
void generateIR(serene::Namespace &, mlir::ModuleOp &) override{};
~List() = default;
static bool classof(const Expression *e);
};
} // namespace exprs
} // namespace serene
#endif

View File

@ -1,65 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_EXPRS_NUMBER_H
#define SERENE_EXPRS_NUMBER_H
#include "serene/context.h"
#include "serene/export.h"
#include "serene/exprs/expression.h"
#include "serene/namespace.h"
#include <llvm/Support/FormatVariadic.h>
namespace serene {
namespace exprs {
/// This data structure represent a number. I handles float points, integers,
/// positive and negative numbers. This is not a data type representative.
/// So it won't cast to actual numeric types and it has a string container
/// to hold the parsed value.
struct SERENE_EXPORT Number : public Expression {
// TODO: Use a variant here instead to store different number types
std::string value;
bool isNeg;
bool isFloat;
Number(reader::LocationRange &loc, const std::string &num, bool isNeg,
bool isFloat)
: Expression(loc), value(num), isNeg(isNeg), isFloat(isFloat){};
ExprType getType() const override;
std::string toString() const override;
MaybeNode analyze(semantics::AnalysisState &state) override;
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override;
// TODO: This is horrible, we need to fix it after the mvp
int toI64() const;
~Number() = default;
static bool classof(const Expression *e);
};
} // namespace exprs
} // namespace serene
#endif

View File

@ -1,81 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_EXPRS_SYMBOL_H
#define SERENE_EXPRS_SYMBOL_H
#include "serene/context.h"
#include "serene/export.h"
#include "serene/exprs/expression.h"
#include "serene/namespace.h"
#include <llvm/ADT/StringRef.h>
#include <string>
namespace serene {
namespace exprs {
/// This data structure represent the Lisp symbol. Just a symbol
/// in the context of the AST and nothing else.
class SERENE_EXPORT Symbol : public Expression {
public:
std::string name;
std::string nsName;
Symbol(const reader::LocationRange &loc, llvm::StringRef name,
llvm::StringRef currentNS)
: Expression(loc) {
// IMPORTANT NOTE: the `name` and `currentNS` should be valid string and
// already validated.
auto partDelimiter = name.find('/');
if (partDelimiter == std::string::npos) {
nsName = currentNS.str();
this->name = name.str();
} else {
this->name = name.substr(partDelimiter + 1, name.size()).str();
nsName = name.substr(0, partDelimiter).str();
}
};
Symbol(Symbol &s) : Expression(s.location) {
this->name = s.name;
this->nsName = s.nsName;
}
ExprType getType() const override;
std::string toString() const override;
MaybeNode analyze(semantics::AnalysisState &state) override;
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override {
UNUSED(ns);
UNUSED(m);
};
~Symbol() = default;
static bool classof(const Expression *e);
};
} // namespace exprs
} // namespace serene
#endif

View File

@ -1,47 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_EXPRS_TRAITS_H
#define SERENE_EXPRS_TRAITS_H
#include "serene/context.h"
#include "serene/reader/location.h"
#include "serene/reader/traits.h"
#include "serene/traits.h"
#include "serene/utils.h"
namespace serene::exprs {
/// This enum represent the expression type and **not** the value type.
enum class ExprType {
Symbol,
List,
Number,
Def,
Error,
Fn,
Call,
};
/// The string represantion of built in expr types (NOT DATATYPES).
static const char *exprTypes[] = {
"Symbol", "List", "Number", "Def", "Error", "Fn", "Call",
};
}; // namespace serene::exprs
#endif

View File

@ -1,18 +0,0 @@
#+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.

View File

@ -1,180 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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:
*/
// TODO: Look at the LLJITBuilderState::prepareForConstruction to setup the
// object linking layer
#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 <llvm/Support/raw_ostream.h>
#include <memory>
#define JIT_LOG(...) \
DEBUG_WITH_TYPE("jit", llvm::dbgs() << "[JIT]: " << __VA_ARGS__ << "\n");
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;
// TODO: Enable these two layers when we add the `Platform support`
// std::unique_ptr<orc::IRTransformLayer> transformLayer;
// std::unique_ptr<orc::IRTransformLayer> initHelperTransformLayer;
// orc::CompileOnDemandLayer compileOnDemandLayer;
/// 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
AstLayer 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;
/// The name of the NS which we are in it at the moment.
std::string inNS;
static llvm::Expected<orc::ThreadSafeModule>
optimizeModule(orc::ThreadSafeModule tsm,
const orc::MaterializationResponsibility &r) {
// TSM.withModuleDo([](Module &M) {
// // Create a function pass manager.
// auto FPM = std::make_unique<legacy::FunctionPassManager>(&M);
// // Add some optimizations.
// FPM->add(createInstructionCombiningPass());
// FPM->add(createReassociatePass());
// FPM->add(createGVNPass());
// FPM->add(createCFGSimplificationPass());
// FPM->doInitialization();
// // Run the optimizations over all functions in the module being added
// to
// // the JIT.
// for (auto &F : M)
// FPM->run(F);
// });
UNUSED(r);
return std::move(tsm);
}
Namespace &currentNS;
public:
SereneJIT(Namespace &entryNS, std::unique_ptr<orc::ExecutionSession> es,
std::unique_ptr<orc::EPCIndirectionUtils> epciu,
orc::JITTargetMachineBuilder jtmb, llvm::DataLayout &&dl);
~SereneJIT() {
// if (compileThreads) {
// compileThreads->wait();
// }
if (auto err = es->endSession()) {
es->reportError(std::move(err));
}
};
Namespace &getCurrentNS() { return currentNS; }
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::Error addAst(exprs::Ast &ast, orc::ResourceTrackerSP rt = nullptr);
llvm::Expected<llvm::JITEvaluatedSymbol> lookup(llvm::StringRef name) {
JIT_LOG("Looking up symbol: " + name);
return es->lookup({&mainJD}, mangler(name.str()));
}
};
llvm::Expected<std::unique_ptr<SereneJIT>> makeSereneJIT(Namespace &ns);
}; // namespace jit
}; // namespace serene
#endif

View File

@ -1,196 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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:
This is the first working attempt on building a JIT engine for Serene
and named after Edmond Halley.
- It supports both ASTs and Namespaces
- Every Namespace might have one or more JITDylibs. Depends on the method
of the compilation.
- It operates in lazy (for REPL) and non-lazy mode and wraps LLJIT
and LLLazyJIT
- It uses an object cache layer to cache module (not NSs) objects.
*/
#ifndef SERENE_JIT_HALLEY_H
#define SERENE_JIT_HALLEY_H
#include "serene/errors.h"
#include "serene/export.h"
#include "serene/namespace.h"
#include "serene/utils.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/ExecutionEngine/JITEventListener.h>
#include <llvm/ExecutionEngine/ObjectCache.h>
#include <llvm/ExecutionEngine/Orc/LLJIT.h>
#include <llvm/Support/CodeGen.h>
#include <llvm/Support/Debug.h>
#include <llvm/Support/SmallVectorMemoryBuffer.h>
#include <mlir/Support/LLVM.h>
#include <memory>
#define HALLEY_LOG(...) \
DEBUG_WITH_TYPE("halley", llvm::dbgs() \
<< "[HALLEY]: " << __VA_ARGS__ << "\n");
namespace serene {
class SereneContext;
class Namespace;
namespace exprs {
class Symbol;
} // namespace exprs
namespace jit {
class Halley;
using MaybeJIT = llvm::Expected<std::unique_ptr<Halley>>;
using MaybeJITPtr = llvm::Expected<void (*)(void **)>;
/// A simple object cache following Lang's LLJITWithObjectCache example and
/// MLIR's SimpelObjectCache.
class ObjectCache : public llvm::ObjectCache {
public:
/// Cache the given `objBuffer` for the given module `m`. The buffer contains
/// the combiled objects of the module
void notifyObjectCompiled(const llvm::Module *m,
llvm::MemoryBufferRef objBuffer) override;
// Lookup the cache for the given module `m` or returen a nullptr.
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module *m) override;
/// Dump cached object to output file `filename`.
void dumpToObjectFile(llvm::StringRef filename);
private:
llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> cachedObjects;
};
class SERENE_EXPORT Halley {
// TODO: Replace this with a variant of LLJIT and LLLazyJIT
std::unique_ptr<llvm::orc::LLJIT> engine;
std::unique_ptr<ObjectCache> cache;
/// GDB notification listener.
llvm::JITEventListener *gdbListener;
/// Perf notification listener.
llvm::JITEventListener *perfListener;
llvm::orc::JITTargetMachineBuilder jtmb;
llvm::DataLayout &dl;
SereneContext &ctx;
std::shared_ptr<Namespace> activeNS;
bool isLazy = false;
public:
Halley(serene::SereneContext &ctx, llvm::orc::JITTargetMachineBuilder &&jtmb,
llvm::DataLayout &&dl);
static MaybeJIT make(serene::SereneContext &ctx,
llvm::orc::JITTargetMachineBuilder &&jtmb);
void setEngine(std::unique_ptr<llvm::orc::LLJIT> e, bool isLazy);
/// Looks up a packed-argument function with the given sym name and returns a
/// pointer to it. Propagates errors in case of failure.
MaybeJITPtr lookup(exprs::Symbol &sym) const;
/// Invokes the function with the given name passing it the list of opaque
/// pointers to the actual arguments.
// llvm::Error
// invokePacked(llvm::StringRef name,
// llvm::MutableArrayRef<void *> args = std::nullopt) const;
/// Trait that defines how a given type is passed to the JIT code. This
/// defaults to passing the address but can be specialized.
template <typename T>
struct Argument {
static void pack(llvm::SmallVectorImpl<void *> &args, T &val) {
args.push_back(&val);
}
};
/// Tag to wrap an output parameter when invoking a jitted function.
template <typename T>
struct FnResult {
FnResult(T &result) : value(result) {}
T &value;
};
/// Helper function to wrap an output operand when using
/// ExecutionEngine::invoke.
template <typename T>
static FnResult<T> result(T &t) {
return FnResult<T>(t);
}
// Specialization for output parameter: their address is forwarded directly to
// the native code.
template <typename T>
struct Argument<Result<T>> {
static void pack(llvm::SmallVectorImpl<void *> &args, FnResult<T> &result) {
args.push_back(&result.value);
}
};
/// Invokes the function with the given name passing it the list of arguments
/// by value. Function result can be obtain through output parameter using the
/// `FnResult` wrapper defined above. For example:
///
/// func @foo(%arg0 : i32) -> i32 attributes { llvm.emit_c_interface }
///
/// can be invoked:
///
/// int32_t result = 0;
/// llvm::Error error = jit->invoke("foo", 42,
/// result(result));
// template <typename... Args>
// llvm::Error invoke(llvm::StringRef funcName, Args... args) {
// const std::string adapterName = std::string("") + funcName.str();
// llvm::SmallVector<void *> argsArray;
// // Pack every arguments in an array of pointers. Delegate the packing to
// a
// // trait so that it can be overridden per argument type.
// // TODO: replace with a fold expression when migrating to C++17.
// int dummy[] = {0, ((void)Argument<Args>::pack(argsArray, args), 0)...};
// (void)dummy;
// return invokePacked(adapterName, argsArray);
// };
void dumpToObjectFile(llvm::StringRef filename);
llvm::Error addNS(Namespace &ns, reader::LocationRange &loc);
llvm::Error addAST(exprs::Ast &ast);
Namespace &getActiveNS();
};
llvm::Expected<std::unique_ptr<Halley>> makeHalleyJIT(SereneContext &ctx);
} // namespace jit
} // namespace serene
#endif

View File

@ -1,164 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/namespace.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>
#define LAYER_LOG(...) \
DEBUG_WITH_TYPE("layer", llvm::dbgs() << "[Layer]: " << __VA_ARGS__ << "\n");
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 AstLayer;
/// This will compile the ast to llvm ir.
llvm::orc::ThreadSafeModule compileAst(Namespace &ns, exprs::Ast &ast);
class AstMaterializationUnit : public orc::MaterializationUnit {
public:
AstMaterializationUnit(Namespace &ns, AstLayer &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("Serene functions are not overridable");
}
Namespace &ns;
AstLayer &astLayer;
exprs::Ast &ast;
};
class AstLayer {
orc::IRLayer &baseLayer;
orc::MangleAndInterner &mangler;
public:
AstLayer(orc::IRLayer &baseLayer, orc::MangleAndInterner &mangler)
: baseLayer(baseLayer), mangler(mangler){};
llvm::Error add(orc::ResourceTrackerSP &rt, Namespace &ns, exprs::Ast &ast) {
return rt->getJITDylib().define(
std::make_unique<AstMaterializationUnit>(ns, *this, ast), rt);
}
void emit(std::unique_ptr<orc::MaterializationResponsibility> mr,
Namespace &ns, exprs::Ast &e) {
baseLayer.emit(std::move(mr), compileAst(ns, e));
}
orc::MaterializationUnit::Interface getInterface(Namespace &ns,
exprs::Ast &e);
};
/// NS Layer ==================================================================
class NSLayer;
/// This will compile the NS to llvm ir.
llvm::orc::ThreadSafeModule compileNS(Namespace &ns);
class NSMaterializationUnit : public orc::MaterializationUnit {
public:
NSMaterializationUnit(NSLayer &l, 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);
llvm_unreachable("Serene functions are not overridable");
// TODO: Check the ctx to see whether we need to remove the sym or not
}
NSLayer &nsLayer;
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(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);
LAYER_LOG("Emit namespace");
baseLayer.emit(std::move(mr), compileNS(ns));
}
orc::MaterializationUnit::Interface getInterface(serene::Namespace &ns);
};
} // namespace jit
} // namespace serene
#endif

View File

@ -1,7 +0,0 @@
#ifndef LLVM_IR_VALUE_H
#define LLVM_IR_VALUE_H
#pragma clang diagnostic ignored "-Wunused-parameter"
#include <llvm/IR/Value.h>
#endif

View File

@ -1,57 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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 LLVM_PATCHES_H
#define LLVM_PATCHES_H
#include <llvm/ADT/DenseMap.h>
#include <llvm/ADT/Hashing.h>
namespace llvm {
// Our specialization of DensMapInfo for string type. This will allow use to use
// string
template <>
struct DenseMapInfo<std::string> {
static inline std::string getEmptyKey() { return ""; }
static inline std::string getTombstoneKey() {
// Maybe we need to use something else beside strings ????
return "0TOMBED";
}
static unsigned getHashValue(const std::string &Val) {
assert(Val != getEmptyKey() && "Cannot hash the empty key!");
assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
return (unsigned)(llvm::hash_value(Val));
}
static bool isEqual(const std::string &LHS, const std::string &RHS) {
if (RHS == getEmptyKey()) {
return LHS == getEmptyKey();
}
if (RHS == getTombstoneKey()) {
return LHS == getTombstoneKey();
}
return LHS == RHS;
}
};
} // namespace llvm
#endif

View File

@ -1,178 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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:
* Rules of a namespace:
* - A namespace has have a name and it has to own it.
* - A namespace may or may not be associated 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
* - `environments` vector is the owner of all the semantic envs
* - The first env in the `environments` is the root env.
*
* How to create a namespace ?
* The official way to create a namespace object is to use the `SereneContext`
* object and call `readNamespace`, `importNamespace` or `makeNamespace`.
*/
// TODO: Add a mechanism to figure out whether a namespace has changed or not
// either on memory or disk
#ifndef SERENE_NAMESPACE_H
#define SERENE_NAMESPACE_H
#include "serene/environment.h"
#include "serene/errors.h"
#include "serene/export.h"
#include "serene/slir/generatable.h"
#include "serene/traits.h"
#include "serene/utils.h"
#include <llvm/ADT/SmallString.h>
#include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/ADT/Twine.h>
#include <llvm/ExecutionEngine/Orc/Core.h>
#include <llvm/ExecutionEngine/Orc/ThreadSafeModule.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/Error.h>
#include <mlir/IR/Builders.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/OwningOpRef.h>
#include <mlir/IR/Value.h>
#include <mlir/Support/LogicalResult.h>
#include <atomic>
#include <cstddef>
#include <memory>
#include <string>
#define NAMESPACE_LOG(...) \
DEBUG_WITH_TYPE("NAMESPACE", llvm::dbgs() << __VA_ARGS__ << "\n");
namespace serene {
class SereneContext;
class Namespace;
namespace exprs {
class Expression;
using Node = std::shared_ptr<Expression>;
using Ast = std::vector<Node>;
} // namespace exprs
using NSPtr = std::shared_ptr<Namespace>;
using MaybeNS = llvm::Expected<NSPtr>;
using MaybeModule = std::optional<llvm::orc::ThreadSafeModule>;
using MaybeModuleOp = std::optional<mlir::OwningOpRef<mlir::ModuleOp>>;
using SemanticEnv = Environment<exprs::Node>;
using SemanticEnvPtr = std::unique_ptr<SemanticEnv>;
using SemanticEnvironments = std::vector<SemanticEnvPtr>;
using Form = std::pair<SemanticEnv, exprs::Ast>;
using Forms = std::vector<Form>;
/// Serene's namespaces are the unit of compilation. Any code that needs to be
/// compiled has to be in a namespace. The official way to create a new
/// namespace is to use the `readNamespace`, `importNamespace` and
/// `makeNamespace` member functions of `SereneContext`.
class SERENE_EXPORT Namespace {
friend SereneContext;
private:
SereneContext &ctx;
// 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. It should alway hold a semantically
/// correct AST. It means thet the AST that we want to store here has
/// to pass the semantic analyzer checks.
exprs::Ast tree;
SemanticEnvironments environments;
std::vector<llvm::StringRef> symbolList;
public:
std::string name;
std::optional<std::string> filename;
/// Create a naw namespace with the given `name` and optional `filename` and
/// return a shared pointer to it in the given Serene context.
static NSPtr make(SereneContext &ctx, llvm::StringRef name,
std::optional<llvm::StringRef> filename);
Namespace(SereneContext &ctx, llvm::StringRef ns_name,
std::optional<llvm::StringRef> filename);
/// Create a new environment with the give \p parent as the parent,
/// push the environment to the internal environment storage and
/// return a reference to it. The namespace itself is the owner of
/// environments.
SemanticEnv &createEnv(SemanticEnv *parent);
/// Return a referenece to the top level (root) environment of ns.
SemanticEnv &getRootEnv();
/// Define a new binding in the root environment with the given \p name
/// and the given \p node. Defining a new binding with a name that
/// already exists in legal and will overwrite the previous binding and
/// the given name will point to a new value from now on.
mlir::LogicalResult define(std::string &name, exprs::Node &node);
/// Add the given \p ast to the namespace and return any possible error.
/// The given \p ast will be added to a vector of ASTs by expanding
/// the tree vector to contain \p ast.
///
/// This function runs the semantic analyzer on the \p ast as well.
llvm::Error addTree(exprs::Ast &ast);
exprs::Ast &getTree();
const std::vector<llvm::StringRef> &getSymList() { return symbolList; };
/// Increase the function counter by one
uint nextFnCounter();
SereneContext &getContext();
/// Generate and return a MLIR ModuleOp tha contains the IR of the namespace
/// with respect to the compilation phase
MaybeModuleOp generate(unsigned offset = 0);
/// Compile the namespace to a llvm module. It will call the
/// `generate` method of the namespace to generate the IR.
MaybeModule compileToLLVM();
/// Compile the given namespace from the given \p offset of AST till the end
/// of the trees.
MaybeModule compileToLLVMFromOffset(unsigned offset);
/// Run all the passes specified in the context on the given MLIR ModuleOp.
mlir::LogicalResult runPasses(mlir::ModuleOp &m);
/// Dumps the namespace with respect to the compilation phase
void dump();
~Namespace();
};
} // namespace serene
#endif

View File

@ -1,44 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_PASSES_H
#define SERENE_PASSES_H
#include "serene/export.h"
#include <mlir/Pass/Pass.h>
#define PASS_LOG(...) \
DEBUG_WITH_TYPE("PASSES", llvm::dbgs() << __VA_ARGS__ << "\n");
namespace serene::passes {
/// Return a pass to lower the serene.symbol op
SERENE_EXPORT std::unique_ptr<mlir::Pass> createLowerSymbol();
SERENE_EXPORT void registerAllPasses();
/// Return a pass to convert SLIR dialect to built-in dialects
/// of MLIR.
std::unique_ptr<mlir::Pass> createSLIRLowerToMLIRPass();
/// Return a pass to convert different dialects of MLIR to LLVM dialect.
std::unique_ptr<mlir::Pass> createSLIRLowerToLLVMDialectPass();
} // namespace serene::passes
#endif

View File

@ -1,9 +0,0 @@
set(LLVM_TARGET_DEFINITIONS passes.td)
mlir_tablegen(passes.h.inc -gen-pass-decls)
if(SERENE_ENABLE_DOCS)
mlir_tablegen(passes.md.inc -gen-pass-doc)
endif()
add_public_tablegen_target(SerenePassGen)

View File

@ -1,43 +0,0 @@
/*
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_PASSES_TD
#define SERENE_PASSES_TD
include "mlir/Pass/PassBase.td"
// TODO: When we add and utilize namespace operations we need
// use NsOp instead of the ModuleOp for root of lowering
def LowerSLIR : Pass<"lower-slir", "mlir::ModuleOp"> {
let summary = "Lowers the SLIR dialect to LLVM IR indirectly";
let description = [{
Lowers the SLIR dialect to LLVM IR indirectly meaning
that it will lower SLIR to other dialects that in turn will
eventually lower to llvm ir.
}];
// This is how we can create the default instance of this pass.
// via `createLowerSymbol` function
let constructor = "serene::passes::createLowerSLIR()";
let dependentDialects = ["mlir::func::FuncDialect", "mlir::arith::ArithmeticDialect"];
}
#endif // SERENE_PASSES_TD

View File

@ -1,92 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_READER_LOCATION_H
#define SERENE_READER_LOCATION_H
#include <mlir/IR/Diagnostics.h>
#include <mlir/IR/Location.h>
#include <string>
namespace serene {
class SereneContext;
namespace reader {
/// It represents a location in the input string to the parser via `line`,
struct Location {
/// Since namespaces are our unit of compilation, we need to have
/// a namespace in hand
llvm::StringRef ns;
std::optional<llvm::StringRef> filename = std::nullopt;
/// A pointer to the character that this location is pointing to
/// it the input buffer
const char *c = nullptr;
/// At this stage we only support 65535 lines of code in each file
unsigned short int line = 0;
/// At this stage we only support 65535 chars in each line
unsigned short int col = 0;
bool knownLocation = true;
::std::string toString() const;
Location() = default;
Location(llvm::StringRef ns,
std::optional<llvm::StringRef> fname = std::nullopt,
const char *c = nullptr, unsigned short int line = 0,
unsigned short int col = 0, bool knownLocation = true)
: ns(ns), filename(fname), c(c), line(line), col(col),
knownLocation(knownLocation){};
Location clone() const;
mlir::Location toMLIRLocation(SereneContext &ctx);
/// Returns an unknown location for the given \p ns.
static Location UnknownLocation(llvm::StringRef ns) {
return Location(ns, std::nullopt, nullptr, 0, 0, false);
}
};
class LocationRange {
public:
Location start;
Location end;
LocationRange() = default;
LocationRange(Location _start) : start(_start), end(_start){};
LocationRange(Location _start, Location _end) : start(_start), end(_end){};
// LocationRange(const LocationRange &);
bool isKnownLocation() const { return start.knownLocation; };
static LocationRange UnknownLocation(llvm::StringRef ns) {
return LocationRange(Location::UnknownLocation(ns));
}
};
void incLocation(Location &, const char *);
void decLocation(Location &, const char *);
} // namespace reader
} // namespace serene
#endif

View File

@ -1,137 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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:
* `Reader` is the base parser class and accepts a buffer like objenct (usually
* `llvm::StringRef`) as the input and parsess it to create an AST (look at the
* `serene::exprs::Expression` class).
*
* The parsing algorithm is quite simple and it is a LL(2). It means that, we
* start parsing the input from the very first character and parse the input
* one char at a time till we reach the end of the input. Please note that
* when we call the `advance` function to move forward in the buffer, we
* can't go back. In order to look ahead in the buffer without moving in the
* buffer we use the `nextChar` method.
*
* We have dedicated methods to read different forms like `list`, `symbol`
* `number` and etc. Each of them return a `MaybeNode` that in the success
* case contains the node and an `Error` on the failure case.
*/
#ifndef SERENE_READER_READER_H
#define SERENE_READER_READER_H
#include "serene/errors.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/list.h"
#include "serene/exprs/symbol.h"
#include "serene/reader/location.h"
#include "serene/serene.h"
#include <serene/export.h>
#include <system_error>
#include <llvm/Support/Debug.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/MemoryBufferRef.h>
#include <llvm/Support/raw_ostream.h>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#define READER_LOG(...) \
DEBUG_WITH_TYPE("READER", llvm::dbgs() \
<< "[READER]: " << __VA_ARGS__ << "\n");
namespace serene::reader {
/// Base reader class which reads from a string directly.
class Reader {
private:
SereneContext &ctx;
llvm::StringRef ns;
std::optional<llvm::StringRef> filename;
const char *currentChar = NULL;
llvm::StringRef buf;
/// The position tracker that we will use to determine the end of the
/// buffer since the buffer might not be null terminated
size_t currentPos = -1;
Location currentLocation;
bool readEOL = false;
/// Returns a clone of the current location
Location getCurrentLocation();
/// Returns the next character from the stream.
/// @param skip_whitespace An indicator to whether skip white space like chars
/// or not
void advance(bool skipWhitespace = false);
void advanceByOne();
const char *nextChar(bool skipWhitespace = false, unsigned count = 1);
/// Returns a boolean indicating whether the given input character is valid
/// for an identifier or not.
static bool isValidForIdentifier(char c);
// The property to store the ast tree
exprs::Ast ast;
exprs::MaybeNode readSymbol();
exprs::MaybeNode readNumber(bool);
exprs::MaybeNode readList();
exprs::MaybeNode readExpr();
bool isEndOfBuffer(const char *);
public:
Reader(SereneContext &ctx, llvm::StringRef buf, llvm::StringRef ns,
std::optional<llvm::StringRef> filename);
Reader(SereneContext &ctx, llvm::MemoryBufferRef buf, llvm::StringRef ns,
std::optional<llvm::StringRef> filename);
// void setInput(const llvm::StringRef string);
/// Parses the the input and creates a possible AST out of it or errors
/// otherwise.
exprs::MaybeAst read();
~Reader();
};
/// Parses the given `input` string and returns a `Result<ast>`
/// which may contains an AST or an `llvm::Error`
SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx, llvm::StringRef input,
llvm::StringRef ns,
std::optional<llvm::StringRef> filename);
SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx,
llvm::MemoryBufferRef input,
llvm::StringRef ns,
std::optional<llvm::StringRef> filename);
} // namespace serene::reader
#endif

View File

@ -1,40 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_READER_TRAITS_H
#define SERENE_READER_TRAITS_H
#include "serene/reader/location.h"
#include "serene/traits.h"
namespace serene::reader {
template <typename ConcreteType>
class ILocatable : public TraitBase<ConcreteType, ILocatable> {
public:
ILocatable(){};
ILocatable(const ILocatable &) = delete;
serene::reader::LocationRange &where() const {
return this->Object().where();
}
};
template <typename T>
serene::reader::LocationRange &where(ILocatable<T> &);
} // namespace serene::reader
#endif

View File

@ -1,75 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_SEMANTICS_H
#define SERENE_SEMANTICS_H
#include "serene/environment.h"
#include "serene/errors.h"
#include "serene/export.h"
#include "serene/utils.h"
#include <llvm/ADT/StringRef.h>
#include <memory>
namespace serene {
namespace exprs {
class Expression;
using Node = std::shared_ptr<Expression>;
using Ast = std::vector<Node>;
}; // namespace exprs
class Namespace;
using SemanticEnv = Environment<exprs::Node>;
namespace semantics {
using AnalyzeResult = llvm::Expected<exprs::Ast>;
/// This struct represent the state necessary for each analysis job.
struct AnalysisState {
Namespace &ns;
SemanticEnv &env;
AnalysisState(Namespace &ns, SemanticEnv &env) : ns(ns), env(env){};
std::unique_ptr<AnalysisState> moveToNewEnv();
};
/// Create an new `AnalysisState` by forwarding all parameters off this
/// function directly to the ctor of `AnalysisState` and returns a
/// unique pointer to the state.
template <typename... Args>
std::unique_ptr<AnalysisState> makeAnalysisState(Args &&...args) {
return std::make_unique<AnalysisState>(std::forward<Args>(args)...);
};
/// The entry point to the Semantic analysis phase. It calls the `analyze`
/// method of each node in the given \p form and creates a new AST that
/// contains a more comprehensive set of nodes in a semantically correct
/// AST. If the `analyze` method of a node return a `nullptr` value then the
/// original node will be used instead. Any possible error will be returned.
///
/// \param state The semantic analysis state that keep track of the envs.
/// \param form The actual AST in question.
SERENE_EXPORT AnalyzeResult analyze(AnalysisState &state, exprs::Ast &forms);
} // namespace semantics
} // namespace serene
#endif

View File

@ -1,79 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_SERENE_H
#define SERENE_SERENE_H
#include "serene/config.h"
#include "serene/context.h"
#include "serene/export.h"
#include "serene/exprs/expression.h"
#include "serene/source_mgr.h"
namespace serene {
/// Clinet applications have to call this function before any interaction with
/// the Serene's compiler API.
SERENE_EXPORT void initCompiler();
/// Register the global CLI options of the serene compiler. If the client
/// application needs to setup the compilers options automatically use this
/// function in conjunction with `applySereneCLOptions`.
SERENE_EXPORT void registerSereneCLOptions();
/// Applies the global compiler options on the give \p SereneContext. This
/// function has to be called after `llvm::cl::ParseCommandLineOptions`.
SERENE_EXPORT void applySereneCLOptions(SereneContext &ctx);
/// \brief Reads the the given \p input as Serene source code in the given
/// \c SereneContext \p ctx and returns the possible AST tree of the input or an
/// error otherwise.
///
/// If any thing goes wrong it will return an \return \c llvm::Error describing
/// the issue.
///
/// Be aware than this function reads the input in the context of the current
/// namespace. So for example if the input is somthing like:
///
/// (ns example.code) ....
///
/// and the current ns is `user` then if there is a syntax error in the input
/// the error will be reported under the `user` ns. This is logical because
/// this function reads the code and not evaluate it. the `ns` form has to be
/// evaluated in order to change the ns.
SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx, std::string &input);
/// Evaluates the given AST form \p input in the given \c SereneContext \p ctx
/// and retuns a new expression as the result or a possible error.
///
/// 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);
// TODO: Return a Serene String type instead of the std::string
// TODO: Create an overload to get a stream instead of the result string
/// Prints the given AST form \p input in the given \c SereneContext \p ctx
/// into the given \p result.
/// Note: print is a lisp action. Don't confuse it with a print function such
/// as `println`.
SERENE_EXPORT void print(SereneContext &ctx, const exprs::Ast &input,
std::string &result);
} // namespace serene
#endif

View File

@ -1,12 +0,0 @@
set(LLVM_TARGET_DEFINITIONS dialect.td)
mlir_tablegen(ops.h.inc -gen-op-decls)
mlir_tablegen(ops.cpp.inc -gen-op-defs)
mlir_tablegen(attrs.h.inc -gen-attrdef-decls)
mlir_tablegen(attrs.cpp.inc -gen-attrdef-defs)
mlir_tablegen(types.h.inc -gen-typedef-decls )
mlir_tablegen(types.cpp.inc -gen-typedef-defs)
mlir_tablegen(dialect.h.inc -gen-dialect-decls)
mlir_tablegen(dialect.cpp.inc -gen-dialect-defs)
add_public_tablegen_target(SereneDialectGen)

View File

@ -1,45 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_SLIR_DIALECT_H
#define SERENE_SLIR_DIALECT_H
#include <serene/export.h>
#include <llvm/ADT/TypeSwitch.h>
#include <mlir/Dialect/Arithmetic/IR/Arithmetic.h>
#include <mlir/Dialect/Func/IR/FuncOps.h>
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/Dialect.h>
#include <mlir/IR/DialectRegistry.h>
#include <mlir/IR/FunctionInterfaces.h>
#include <mlir/Interfaces/CallInterfaces.h>
#include <mlir/Interfaces/ControlFlowInterfaces.h>
#include <mlir/Interfaces/InferTypeOpInterface.h>
#include <mlir/Interfaces/SideEffectInterfaces.h>
// Include the auto-generated header file containing the declaration of the
// serene's dialect.
#include "serene/slir/dialect.h.inc"
#include "serene/slir/types.h"
namespace serene::slir {
SERENE_EXPORT void registerTo(mlir::DialectRegistry &registry);
} // namespace serene::slir
//
#endif // SERENE_SLIR_DIALECT_H

View File

@ -1,126 +0,0 @@
/*
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_DIALECT_TD
#define SERENE_DIALECT_TD
include "mlir/IR/OpBase.td"
include "mlir/IR/OpAsmInterface.td"
include "mlir/IR/AttrTypeBase.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/InferTypeOpInterface.td"
include "mlir/Interfaces/DataLayoutInterfaces.td"
include "mlir/Interfaces/VectorInterfaces.td"
// Dialect definition. It will directly generate the SereneDialect class
def Serene_Dialect : Dialect {
let name = "serene";
let cppNamespace = "::serene::slir";
let summary = "Primary IR of serene language.";
let description = [{
This dialect tries to map the special forms of a lisp into
IR level operations.
}];
// All the types have to define a mnemonic
let useDefaultTypePrinterParser = 1;
// TODO: Uncomment this when ever we have custom attrs
// All the attributes have to define a mnemonic
// let useDefaultAttributePrinterParser = 1;
let dependentDialects = [
"mlir::arith::ArithmeticDialect",
"mlir::func::FuncDialect",
"mlir::LLVM::LLVMDialect"
];
let extraClassDeclaration = [{
/// Register all the Serene types
void registerType();
}];
}
include "types.td"
include "ops.td"
// ============================================================================
// Old operations that are we need to replace
// ============================================================================
def Value1Op: Serene_Op<"value1"> {
let summary = "This operation represent a compile time value";
let description = [{
ValueOp represent a value in compile time. For example:
```mlir
%0 = "serene.value"(){value = 3} : () -> i64
```
}];
let arguments = (ins I64Attr:$value);
let results = (outs I64);
//let verifier = [{ return serene::sir::verify(*this); }];
let builders = [
OpBuilder<(ins "int":$value), [{
// Build from fix 64 bit int
build(odsBuilder, odsState, odsBuilder.getI64Type(), (uint64_t) value);
}]>,
];
}
def Fn1Op: Serene_Op<"fn1", [
AffineScope, AutomaticAllocationScope,
IsolatedFromAbove,
]> {
let summary = "This operation is just a place holder for a function";
let description = [{
A place holder for an anonymous function. For example consider an expression
like `(def a (fn (x) x))`, in this case we don't immediately create an anonymous
function since we need to set the name and create the function later.
}];
let arguments = (ins StrAttr:$name,
DictionaryAttr:$args,
OptionalAttr<StrAttr>:$sym_visibility);
let regions = (region AnyRegion:$body);
let results = (outs FnType);
}
def Return1Op: Serene_Op<"return", [NoSideEffect, HasParent<"Fn1Op">,
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

@ -1,121 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_SLIR_GENERATABLE_H
#define SERENE_SLIR_GENERATABLE_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 {
template <typename T>
class GeneratableUnit : public TraitBase<T, GeneratableUnit> {
public:
GeneratableUnit(){};
GeneratableUnit(const GeneratableUnit &) = delete;
void generate(serene::Namespace &ns) {
// TODO: should we return any status or possible error here or
// should we just populate them in a ns wide state?
this->Object().generateIR(ns);
};
};
template <typename T>
class Generatable : public TraitBase<T, Generatable> {
public:
Generatable(){};
Generatable(const Generatable &) = delete;
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();
// 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 std::move(llvmModule);
};
template <typename T>
void dump(Generatable<T> &t) {
t.dump();
};
} // namespace serene::slir
#endif

View File

@ -1,40 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_DIALECT_OPS_H
#define SERENE_DIALECT_OPS_H
#include "serene/slir/types.h"
#include <llvm/ADT/TypeSwitch.h>
#include <mlir/Dialect/Arithmetic/IR/Arithmetic.h>
#include <mlir/Dialect/Func/IR/FuncOps.h>
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/Dialect.h>
#include <mlir/IR/DialectRegistry.h>
#include <mlir/IR/FunctionInterfaces.h>
#include <mlir/Interfaces/CallInterfaces.h>
#include <mlir/Interfaces/ControlFlowInterfaces.h>
#include <mlir/Interfaces/InferTypeOpInterface.h>
#include <mlir/Interfaces/SideEffectInterfaces.h>
#define GET_OP_CLASSES
#include "serene/slir/ops.h.inc"
#endif

View File

@ -1,370 +0,0 @@
/*
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_DIALECT_OPS_TD
#define SERENE_DIALECT_OPS_TD
include "mlir/IR/OpBase.td"
include "mlir/Interfaces/CallInterfaces.td"
include "mlir/IR/RegionKindInterface.td"
include "mlir/IR/SymbolInterfaces.td"
include "mlir/IR/OpAsmInterface.td"
// Base class for Serene dialect operations. This operation inherits from the base
// `Op` class in OpBase.td, and provides:
// * The parent dialect of the operation.
// * The mnemonic for the operation, or the name without the dialect prefix.
// * A list of traits for the operation.
class Serene_Op<string mnemonic, list<Trait> traits = []> :
Op<Serene_Dialect, mnemonic, traits>;
// serene.value
def ValueOp : Serene_Op<"value", [
ConstantLike, NoSideEffect,
TypesMatchWith<
"result and attribute have the same type",
"value", "result", "$_self">]> {
let summary = "This operation represent a compile time value";
let description = [{
The `value` operation produces an SSA value equal to value
specified by an attribute. This is the way Serene marks constant
compile time values.
Example:
```
// Integer constant
%1 = serene.value 42 : i32
// Equivalent generic form
%1 = "serene.value"() {value = 42 : i32} : () -> i32
```
}];
let arguments = (ins AnyAttr:$value);
let results = (outs AnyType:$result);
let builders = [
OpBuilder<(ins "mlir::Attribute":$value),
[{ build($_builder, $_state, value.getType(), value); }]>,
OpBuilder<(ins "mlir::Attribute":$value, "mlir::Type":$type),
[{ build($_builder, $_state, type, value); }]>,
];
let extraClassDeclaration = [{
/// Whether the constant op can be constructed with a particular value and
/// type.
static bool isBuildableWith(mlir::Attribute value, mlir::Type type);
}];
// Need to define the ::fold() method to make value op foldable
// let hasFolder = 1;
let assemblyFormat = "attr-dict $value";
let hasFolder = 1;
}
// serene.intern
def InternOp : Serene_Op<"intern", [
NoSideEffect
]> {
let summary = "This operation is the runtime contructor for symbol type";
let description = [{
The `intern` operation produces an SSA value that holds a value of type
symbol at runtime.
Example:
```
%ns = serene.string "some.ns"
%name = serene.string "symbol_name"
%1 = serene.intern %ns %name
// Equivalent generic form
%1 = "serene.symbol"(%ns, %name){} : (!serene.string, !serene.string) -> !serene.ptr<serene.symbol>
```
}];
let arguments = (ins StringType:$ns, StringType:$name);
let results = (outs Ptr<SymbolType>:$result);
let assemblyFormat = "attr-dict $ns $name";
}
// serene.symbol
def SymbolOp : Serene_Op<"symbol", [
NoSideEffect, ConstantLike,
]> {
let summary = "This operation is the compile time contructor for symbol type";
let description = [{
The `symbol` operation produces an SSA value that holds a value of type
symbol at compile time.
Example:
```
%1 = serene.symbol "some.ns" "symbol_name"
// Equivalent generic form
%1 = "serene.symbol"() {ns = "some.ns", name = "symbol_name"} : () -> i32
```
}];
let arguments = (ins SymbolAttr:$value);
let results = (outs Ptr<SymbolType>:$result);
let assemblyFormat = "attr-dict $value";
let hasFolder = 1;
}
// serene.convert
def ConvertOp : Serene_Op<"convert", [
NoSideEffect
]> {
let summary = "This operation converts a symbol to the equivelant llvm type";
let description = [{
This operation converts a symbol to the equivelant llvm type
}];
let arguments = (ins AnyType:$value);
let results = (outs AnyType:$result);
let assemblyFormat = [{
$value attr-dict `:` functional-type($value, results)
}];
}
// ============================================================================
// Define Family
// ============================================================================
// serene.define
def DefineOp: Serene_Op<"define", [Symbol]> {
let summary = "This operation defines a global binding in the current namespace";
let description = [{
`define` defines a global binding in the current namespace. It always return a
symbol type.
Examples:
```mlir
%foo = "serene.define"(%0){name = "foo"}: (i64) -> !serene.symbol
// compact form
%bar = serene.define "bar", %0 : i64
```
}];
let arguments = (ins SymbolNameAttr:$sym_name, AnyType:$value,
OptionalAttr<BoolAttr>:$is_top_level,
OptionalAttr<StrAttr>:$sym_visibility);
//let results = (outs SereneSymbol);
let assemblyFormat = "attr-dict $sym_name `,` $value `:` type($value)";
}
// serene.define_constant
def DefineConstantOp: Serene_Op<"define_constant", [Symbol]> {
let summary = "This operation defines a constant global binding in the current namespace";
let description = [{
`define` defines a constant global binding in the current namespace. It always return a
symbol type.
Examples:
```mlir
%foo = "serene.define_constant"(%0){name = "foo"}: (i64) -> !serene.symbol
// compact form
%bar = serene.define_constant "bar", %0 : i64
```
}];
let arguments = (ins SymbolNameAttr:$sym_name, AnyAttr:$value,
OptionalAttr<BoolAttr>:$is_top_level,
OptionalAttr<StrAttr>:$sym_visibility);
// let results = (outs SereneSymbol);
let assemblyFormat = "attr-dict $sym_name $value";
}
// serene.set_value
def SetValueOp: Serene_Op<"set_value", []> {
let summary = "Sets the value of a SLIRS global varible.";
let description = [{
This operation sets the value of a SLIR global variable. This operation
is NOT threadsafe.
}];
let arguments = (ins SymbolRefAttr:$var,
AnyType:$value);
let assemblyFormat = "attr-dict $var `,` $value `:` type($value)";
}
// serene.fn
def FnOp: Serene_Op<"fn", [
AffineScope, AutomaticAllocationScope,
IsolatedFromAbove,
]> {
let summary = "This operation is just a place holder for a function";
let description = [{
A place holder for an anonymous function. For example consider an expression
like `(def a (fn (x) x))`, in this case we don't immediately create an anonymous
function since we need to set the name and create the function later.
}];
let arguments = (ins StrAttr:$name,
TypeAttr:$return_type,
OptionalAttr<StrAttr>:$sym_visibility);
let regions = (region VariadicRegion<AnyRegion>:$bodies);
let results = (outs FnType);
}
// serene.ret
def ReturnOp: Serene_Op<"ret", [
NoSideEffect, HasParent<"FnOp">,
ReturnLike, Terminator
// MemRefsNormalizable
]> {
let summary = "This operation marks the return value of a function";
let description = [{
`ret` marks the return value of a function body.
Example:
```mlir
serene.ret %foo : i32
```
}];
let arguments = (ins Variadic<AnyType>:$operands);
let builders = [OpBuilder<(ins), [{
build($_builder, $_state, llvm::None);
}]>];
let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?";
// TODO: Turn on the verifier for `ret`
// let hasVerifier = 1;
}
// serene.call
def CallOp : Serene_Op<"call",
[MemRefsNormalizable]> {
let summary = "call operation";
let description = [{
The `serene.call` operation represents a direct call to a function that is
within the same symbol scope as the call. The operands and result types of
the call must match the specified function type. The callee is encoded as a
symbol reference attribute named "callee".
Example:
```mlir
%2 = serene.call @my_add(%0, %1) : (f32, f32) -> f32
```
}];
let arguments = (ins FnType:$fn,
Variadic<AnyType>:$args);
let results = (outs Variadic<AnyType>);
let assemblyFormat = [{
$fn `(` $args `)` attr-dict `:` functional-type($args, results)
}];
}
// TODO: Do we need to have a NS type?
def NsOp : Serene_Op<"ns", [
AffineScope, IsolatedFromAbove, NoRegionArguments, SymbolTable, Symbol,
OpAsmOpInterface
] # GraphRegionNoTerminator.traits> {
let summary = "This operation represents a Serene namespace.";
let description = [{
The `serene.ns` operation represents a namespace that will eventually
lowers to a `mlir::ModuleOp`.
Example:
```mlir
serene.ns @some.ns {
....
}
```
}];
// TODO: Create a new Attr type that is an array to represent
// required namespaces
let arguments = (ins SymbolNameAttr:$sym_name,
OptionalAttr<StrAttr>:$sym_visibility);
let regions = (region SizedRegion<1>:$body);
let assemblyFormat = "$sym_name attr-dict-with-keyword $body";
let builders = [
OpBuilder<(ins CArg<"llvm::Optional<llvm::StringRef>", "{}">:$name)>
];
let extraClassDeclaration = [{
/// Construct a namespace from the given location with an optional name.
// static NsOp create(slir::reader::LocationRange loc, Optional<StringRef> name = llvm::None);
/// Return the name of this module if present.
llvm::StringRef getName() { return sym_name(); }
//===------------------------------------------------------------------===//
// SymbolOpInterface Methods
//===------------------------------------------------------------------===//
/// A ModuleOp may optionally define a symbol.
bool isOptionalSymbol() { return false; }
//===------------------------------------------------------------------===//
// DataLayoutOpInterface Methods
//===------------------------------------------------------------------===//
mlir::DataLayoutSpecInterface getDataLayoutSpec();
//===------------------------------------------------------------------===//
// OpAsmOpInterface Methods
//===------------------------------------------------------------------===//
static ::llvm::StringRef getDefaultDialect() {
return "builtin";
}
}];
}
#endif // SERENE_DIALECT_OPS

View File

@ -1,39 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_SLIR_SLIR_H
#define SERENE_SLIR_SLIR_H
#include "serene/exprs/expression.h"
#include <llvm/ADT/Optional.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/MLIRContext.h>
#include <memory>
namespace serene {
namespace slir {
// std::unique_ptr<llvm::Module> compileToLLVMIR(serene::SereneContext &ctx,
// mlir::ModuleOp &module);
std::optional<llvm::orc::ThreadSafeModule>
compileToLLVMIR(serene::SereneContext &ctx, mlir::ModuleOp &module);
} // namespace slir
} // namespace serene
#endif

View File

@ -1,81 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/>.
*/
// SereneSymbol is the Symbil type in SLIT that represents an expr::Sympol
#ifndef SERENE_SLIR_SYMBOL_H
#define SERENE_SLIR_SYMBOL_H
#include <llvm/Support/ErrorHandling.h>
#include <mlir/IR/TypeSupport.h>
namespace serene {
namespace slir {
namespace detail {
/// This is the storage class for the `SymbolType` (SereneSymbol in ODS)
// class SymbolTypeStorage : public mlir::TypeStorage {
// using KeyTy = std::string;
// SymbolTypeStorage(std::string &ns, std::string &name) : ns(ns), name(name)
// {} SymbolTypeStorage(const KeyTy &k) {
// auto partDelimiter = k.find('/');
// if (partDelimiter == std::string::npos) {
// llvm::llvm_unreachable_internal("SLIR symbol has to have NS");
// } else {
// name = k.substr(partDelimiter + 1, k.size());
// ns = k.substr(0, partDelimiter);
// }
// }
// /// The hash key for this storage is a pair of the integer and type params.
// /// Define the comparison function for the key type.
// bool operator==(const KeyTy &key) const {
// // TODO: Use formatv to concat strings
// return key == ns + "/" + name;
// }
// static llvm::hash_code hashKey(const KeyTy &key) {
// return llvm::hash_combine(key);
// }
// /// Define a construction function for the key type.
// /// Note: This isn't necessary because KeyTy can be directly constructed
// with
// /// the given parameters.
// static KeyTy getKey(std::string &ns, std::string &name) {
// // TODO: Use formatv to concat strings
// return KeyTy(ns + "/" + name);
// }
// /// Define a construction method for creating a new instance of this
// storage. static SymbolTypeStorage *construct(mlir::TypeStorageAllocator
// &allocator,
// const KeyTy &key) {
// return new (allocator.allocate<SymbolTypeStorage>())
// SymbolTypeStorage(key);
// }
// std::string ns;
// std::string name;
// };
}; // namespace detail
}; // namespace slir
}; // namespace serene
#endif

View File

@ -1,117 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_SLIR_TRAITS_H
#define SERENE_SLIR_TRAITS_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 {
template <typename T>
class GeneratableUnit : public TraitBase<T, GeneratableUnit> {
public:
GeneratableUnit(){};
GeneratableUnit(const GeneratableUnit &) = delete;
void generate(serene::Namespace &ns) { this->Object().generateIR(ns); };
};
template <typename T>
class Generatable : public TraitBase<T, Generatable> {
public:
Generatable(){};
Generatable(const Generatable &) = delete;
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();
// 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 std::move(llvmModule);
};
template <typename T>
void dump(Generatable<T> &t) {
t.dump();
};
} // namespace serene::slir
#endif

View File

@ -1,86 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_SLIR_TYPE_CONVERTER_H
#define SERENE_SLIR_TYPE_CONVERTER_H
#include "serene/config.h"
#include "serene/context.h"
#include "serene/export.h"
#include "serene/slir/dialect.h"
#include "serene/slir/ops.h"
#include "serene/slir/types.h"
#include "serene/utils.h"
#include <mlir/Dialect/LLVMIR/LLVMTypes.h>
#include <mlir/IR/BuiltinTypes.h>
#include <mlir/Transforms/DialectConversion.h>
namespace serene::slir {
class SymbolType;
class PtrType;
/// Returns the LLVM pointer type to the type `T` that is Serene pointer
/// `p` is pointing to.
mlir::Type getPtrTypeinLLVM(mlir::MLIRContext &ctx, PtrType p);
/// Returns the conversion result of converting Serene String type
/// to LLVM dialect
mlir::Type getStringTypeinLLVM(mlir::MLIRContext &ctx);
/// Returns the conversoin result of converting Serene Symbol type
/// to LLVM dialect
mlir::Type getSymbolTypeinLLVM(mlir::MLIRContext &ctx);
class TypeConverter : public mlir::TypeConverter {
mlir::MLIRContext &ctx;
using mlir::TypeConverter::convertType;
using MaybeType = std::optional<mlir::Type>;
using MaybeValue = std::optional<mlir::Value>;
using ConverterFn = std::function<MaybeType(mlir::Type)>;
std::function<MaybeValue(mlir::OpBuilder &, SymbolType, mlir::ValueRange,
mlir::Location)>
materializeSymbolFn() {
return [&](mlir::OpBuilder &builder, SymbolType type,
mlir::ValueRange values, mlir::Location loc) -> MaybeValue {
auto targetType = convertType(type);
auto ret = builder.create<ConvertOp>(loc, targetType, values[0]);
llvm::outs() << "RR: " << ret << "\n";
return ret.getResult();
};
};
/// This method will be called via `convertType` to convert Serene types
/// to llvm dialect types
ConverterFn convertSereneTypes();
public:
TypeConverter(mlir::MLIRContext &ctx) : ctx(ctx) {
addConversion([](mlir::Type type) { return type; });
addConversion(convertSereneTypes());
addArgumentMaterialization(materializeSymbolFn());
addTargetMaterialization(materializeSymbolFn());
}
};
} // namespace serene::slir
#endif

View File

@ -1,34 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_DIALECT_TYPES_H
#define SERENE_DIALECT_TYPES_H
#include <llvm/ADT/TypeSwitch.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/BuiltinTypes.h>
#include <mlir/IR/DialectImplementation.h>
#include <mlir/IR/TypeSupport.h>
#define GET_ATTRDEF_CLASSES
#include "serene/slir/attrs.h.inc"
#define GET_TYPEDEF_CLASSES
#include "serene/slir/types.h.inc"
#endif

View File

@ -1,141 +0,0 @@
/*
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_DIALECT_TYPES_TD
#define SERENE_DIALECT_TYPES_TD
include "mlir/IR/OpBase.td"
include "mlir/IR/AttrTypeBase.td"
// The base class for all the Serene types
class Serene_Type<string name, string typeMnemonic, list<Trait> traits = []>
: TypeDef<Serene_Dialect, name, traits> {
let mnemonic = typeMnemonic;
}
// The base class for all the Serene attributes
class Serene_Attr<string name, string attrMnemonic, list<Trait> traits = []>
: AttrDef<Serene_Dialect, name, traits> {
let mnemonic = attrMnemonic;
}
// Attributes =================================================================
def SymbolAttr : Serene_Attr<"Symbol", "symbol"> {
let summary = "An Attribute containing a symbol value.";
let description = [{
An integer attribute is a literal attribute that represents a symbol
value.
}];
let parameters = (ins StringRefParameter<"The namespace of the symbol">:$ns,
StringRefParameter<"The symbol name itself">:$name);
let assemblyFormat = "`<` $ns `,` $name `>`";
}
// Type Traits ================================================================
def AnyPtr : Type<CPred<"$_self.isa<PtrType>()">,
"Serene pointer type", "Ptr">;
class Ptr<Type type> : Type<
And<[AnyPtr.predicate,
Or<[CPred<"$_self.cast<PtrType>().isOpaque()">,
SubstLeaves<
"$_self",
"$_self.cast<PtrType>().getPointeeType()",
type.predicate>]>]>,
"A pointer to type " # type.summary,
"PtrType">,
// We need Ptr to be buildable coz we need to be able to infer
// the type out of it when we use Ptr<T> as the result type
// of an operation
SameBuildabilityAs<type, "PtrType::get(" # type # "::get($_builder.getContext()))"> {
Type pointeeType = type;
}
// Types ======================================================================
def PtrType : Serene_Type<"Ptr", "ptr"> {
let summary = "A pointer to a value of type T.";
let description = [{
A pointer to a value of type T. For example
%0 = serene.address-of %1 : (!serene.symbol) -> !ptr<serene.symbol>
}];
let parameters = (ins "mlir::Type":$pointeeType, DefaultValuedParameter<"unsigned", "0">:$addressSpace);
let genAccessors = 1;
let assemblyFormat = "`<` qualified($pointeeType) `>`";
let extraClassDeclaration = [{
/// Gets or creates an instance of pointer type pointing to an
/// object of `pointee` type in the given address space. The pointer type is
/// created in the same context as `pointee`. If the pointee is not provided,
/// creates an opaque pointer in the given context and address space.
static PtrType get(mlir::MLIRContext *context, unsigned addressSpace = 0);
static PtrType get(mlir::Type pointee, unsigned addressSpace = 0);
bool isOpaque() const;
}];
}
def StringType : Serene_Type<"String", "string"> {
let summary = "A simple string type";
let description = [{
A simple string type that contains the following structure:
i8*: buffer; pointer to the character buffer
i32: length; the number of chars in the buffer
}];
}
def SymbolType : Serene_Type<"Symbol", "symbol"> {
let summary = "A Lisp symbol type";
let description = [{
A Lisp symbol type
}];
// let parameters = (ins "std::string":$ns, "std::string":$name);
}
def FnType : Serene_Type<"Fn", "fn"> {
let summary = "Function type";
let description = [{
Function type represent any function, anonymous or named with multiple
bodies.
}];
// TODO: do we need to know about number of bodies and the signature
// of each one?
// let parameters = (ins "std::string":$ns, "std::string":$name);
}
def Serene_Type : AnyTypeOf<[
PtrType,
SymbolType,
StringType,
FnType
]>;
#endif // SERENE_DIALECT_TYPES_TD

View File

@ -1,40 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_SLIR_UTILS_H
#define SERENE_SLIR_UTILS_H
#include "serene/reader/location.h"
#include <mlir/IR/BuiltinOps.h>
namespace serene {
class Namespace;
} // namespace serene
namespace serene::slir {
/**
* Convert a Serene location to MLIR FileLineLoc Location
*/
::mlir::Location toMLIRLocation(serene::Namespace &,
serene::reader::Location &);
} // namespace serene::slir
#endif

View File

@ -1,190 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_SOURCE_MGR_H
#define SERENE_SOURCE_MGR_H
#include "serene/namespace.h"
#include "serene/reader/location.h"
#include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/StringMap.h>
#include <llvm/Support/ErrorHandling.h>
#include <llvm/Support/ErrorOr.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/SourceMgr.h>
#include <mlir/IR/Diagnostics.h>
#include <mlir/Support/Timing.h>
#include <memory>
#define SMGR_LOG(...) \
DEBUG_WITH_TYPE("sourcemgr", llvm::dbgs() \
<< "[SMGR]: " << __VA_ARGS__ << "\n");
namespace serene {
class SereneContext;
/// This class is quite similar to the `llvm::SourceMgr` in functionality. We
/// even borrowed some of the code from the original implementation but removed
/// a lot of code that were irrelevant to us.
///
/// SouceMgr is responsible for finding a namespace in the `loadPaths` and read
/// the content of the `.srn` (or any of the `DEFAULT_SUFFIX`) into a
/// `llvm::MemoryBuffer` embedded in a `SrcBuffer` object as the owner of the
/// source files and then it will call the `reader` on the buffer to parse it
/// and create the actual `Namespace` object from the parsed AST.
///
/// Later on, whenever we need to refer to the source file of a namespace for
/// diagnosis purposes or any other purpose we can use the functions in this
/// class to get hold of a pointer to a specific `reader::Location` of the
/// buffer.
///
/// Note: Unlike the original version, SourceMgr does not handle the diagnostics
/// and it uses the Serene's `DiagnosticEngine` for that matter.
class SERENE_EXPORT SourceMgr {
public:
// TODO: Make it a vector of supported suffixes
std::string DEFAULT_SUFFIX = "srn";
private:
struct SrcBuffer {
/// The memory buffer for the file.
std::unique_ptr<llvm::MemoryBuffer> buffer;
/// Vector of offsets into Buffer at which there are line-endings
/// (lazily populated). Once populated, the '\n' that marks the end of
/// line number N from [1..] is at Buffer[OffsetCache[N-1]]. Since
/// these offsets are in sorted (ascending) order, they can be
/// binary-searched for the first one after any given offset (eg. an
/// offset corresponding to a particular SMLoc).
///
/// Since we're storing offsets into relatively small files (often smaller
/// than 2^8 or 2^16 bytes), we select the offset vector element type
/// dynamically based on the size of Buffer.
mutable void *offsetCache = nullptr;
/// Look up a given \p ptr in in the buffer, determining which line it came
/// from.
unsigned getLineNumber(const char *ptr) const;
template <typename T>
unsigned getLineNumberSpecialized(const char *ptr) const;
/// Return a pointer to the first character of the specified line number or
/// null if the line number is invalid.
const char *getPointerForLineNumber(unsigned lineNo) const;
template <typename T>
const char *getPointerForLineNumberSpecialized(unsigned lineNo) const;
/// This is the location of the parent import or unknown location if it is
/// the main namespace
reader::LocationRange importLoc;
SrcBuffer() = default;
SrcBuffer(SrcBuffer &&) noexcept;
SrcBuffer(const SrcBuffer &) = delete;
SrcBuffer &operator=(const SrcBuffer &) = delete;
~SrcBuffer();
};
using MemBufPtr = std::unique_ptr<llvm::MemoryBuffer>;
/// This is all of the buffers that we are reading from.
std::vector<SrcBuffer> buffers;
/// A hashtable that works as an index from namespace names to the buffer
/// position it the `buffer`
llvm::StringMap<unsigned> nsTable;
// This is the list of directories we should search for include files in.
std::vector<std::string> loadPaths;
// Find a namespace file with the given \p name in the load path and \r retuns
// 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.
MemBufPtr findFileInLoadPath(const std::string &name,
std::string &importedFile);
bool isValidBufferID(unsigned i) const;
/// Converts the ns name to a partial path by replacing the dots with slashes
static std::string convertNamespaceToPath(std::string ns_name);
public:
SourceMgr() = default;
SourceMgr(const SourceMgr &) = delete;
SourceMgr &operator=(const SourceMgr &) = delete;
SourceMgr(SourceMgr &&) = default;
SourceMgr &operator=(SourceMgr &&) = default;
~SourceMgr() = default;
/// Set the `loadPaths` to the given \p dirs. `loadPaths` is a vector of
/// directories that Serene will look in order to find a file that constains a
/// namespace which it is looking for.
void setLoadPaths(std::vector<std::string> &dirs) { loadPaths.swap(dirs); }
/// Return a reference to a `SrcBuffer` with the given ID \p i.
const SrcBuffer &getBufferInfo(unsigned i) const {
assert(isValidBufferID(i));
return buffers[i - 1];
}
/// Return a reference to a `SrcBuffer` with the given namspace name \p ns.
const SrcBuffer &getBufferInfo(llvm::StringRef ns) const {
auto bufferId = nsTable.lookup(ns);
if (bufferId == 0) {
// No such namespace
llvm_unreachable("couldn't find the src buffer for a namespace. It "
"should never happen.");
}
return buffers[bufferId - 1];
}
/// Return a pointer to the internal `llvm::MemoryBuffer` of the `SrcBuffer`
/// with the given ID \p i.
const llvm::MemoryBuffer *getMemoryBuffer(unsigned i) const {
assert(isValidBufferID(i));
return buffers[i - 1].buffer.get();
}
unsigned getNumBuffers() const { return buffers.size(); }
/// Add a new source buffer to this source manager. This takes ownership of
/// the memory buffer.
unsigned AddNewSourceBuffer(std::unique_ptr<llvm::MemoryBuffer> f,
reader::LocationRange includeLoc);
/// Lookup for a file containing the namespace definition of with given
/// namespace name \p name. In case that the file exists, it returns an
/// `ErrorTree`. It will use the parser to read the file and create an AST
/// from it. Then create a namespace, set the its AST to the AST that we just
/// read from the file and return a shared pointer to the namespace.
///
/// \p importLoc is a location in the source code where the give namespace is
/// imported.
MaybeNS readNamespace(SereneContext &ctx, std::string name,
reader::LocationRange importLoc);
};
}; // namespace serene
#endif

View File

@ -1,123 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/>.
*/
/**
* This is a CRTP based Trait implementation that allows to use Trait like
* classes to create polymorphic functions and API statically at compile type
* without any runtime shenanigans. For more on CRTP checkout:
*
* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
*
* In order to define a trait, use the `TraitBase` class like:
* \code
* template <typename ConcreteType>
* class Blahable : public TraitBase<ConcreteType, Blahable> {}
* \endcode
*
* Every Trait has to take the `ConcreteType` as template argument and pass it
* to the `TraitBase`. Checkout the documentation of `TraitBase` for more info
* on creating a new Trait.
*
* Alongside with each Trait type, you should provide the "Official" interface
* of the Trait via some standalone functions that operates on the Trait type.
* For example Imagine we have a Trait type called `ABC` with two main
* functionality `foo` and `bar`. We need to create two functions as follows:
*
* \code
* template <typename T>
* SomeType Foo(ABC<T> &t) { return t.foo(); };
*
* template <typename T>
* SomeType bar(ABC<T> &t, int x) { return t.bar(x); };
* \endcode
*
* These two functions will be the official interface to the trait `ABC`.
* IMPORTANT NOTE: Make sure to pass a reference of type `ABC<T>` to the
* functions and DO NOT PASS BY COPY. Since copying will copy the value by the
* trait type only, we would not be able to statically case it to the
* implementor type and it will lead to some allocation problem.
*
* Traits can be used via `WithTrait` helper class which provides a clean
* interface to mix and match Trait types.
*
*/
#ifndef SERENE_TRAITS_H
#define SERENE_TRAITS_H
#include <type_traits>
#include <string>
namespace serene {
/// A placeholder structure that replaces the concrete type of the
/// Imlementations Which might have child classes.
struct FinalImpl;
/// In order to use Traits, we can use `WithTrait` class as the base
/// of any implementation class and pass the Trait classes as template argument
/// for example:
///
/// \code
/// class Expression : public WithTrait<Expression, Printable, Locatable> {}
/// \endcode
template <typename ConcreteType, template <typename T> class... Traits>
class WithTrait : public Traits<ConcreteType>... {
protected:
WithTrait(){};
friend ConcreteType;
};
/// This class provides the common functionality among the Trait Types and
/// every Trait has to inherit from this class. Here is an example:
///
/// \code
/// template <typename ConcreteType>
/// class Blahable : public TraitBase<ConcreteType, Blahable> {}
/// \endcode
///
/// In the Trait class the underlaying object which implements the Trait
/// is accessable via the `Object` method.
template <typename ConcreteType, template <typename> class TraitType>
class TraitBase {
protected:
/// Statically casts the object to the concrete type object to be
/// used in the Trait Types.
// const ConcreteType &Object() const {
// return static_cast<const ConcreteType &>(*this);
// };
ConcreteType &Object() { return static_cast<ConcreteType &>(*this); };
};
template <typename ConcreteType>
class IDebuggable : public TraitBase<ConcreteType, IDebuggable> {
public:
IDebuggable(){};
IDebuggable(const IDebuggable &) = delete;
std::string toString() const { return this->Object().toString(); }
};
template <typename T>
std::string toString(IDebuggable<T> &t) {
return t.toString();
}
}; // namespace serene
#endif

View File

@ -1,22 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_TRAITS_LOCATABLE_H
#define SERENE_TRAITS_LOCATABLE_H
#endif

View File

@ -1,35 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_TYPES_TYPE_H
#define SERENE_TYPES_TYPE_H
#include <string>
namespace serene::types {
class Type {
public:
virtual ~Type() = default;
/// The AST representation of a type
virtual std::string toString() const = 0;
};
}; // namespace serene::types
#endif

View File

@ -1,140 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_UTILS_H
#define SERENE_UTILS_H
#include "serene/export.h"
#include <llvm/Support/Error.h>
#include <variant>
// Sometimes we need this to make both analyzer happy
// and the fn signature right.
#define UNUSED(x) (void)(x)
// We use this value with llvm::SmallString<MAX_PATH_SLOTS>
#define MAX_PATH_SLOTS 256
// C++17 required. We can't go back to 14 any more :))
namespace serene {
/// A similar type to Rust's Result data structure. It either holds a value of
/// type `T` successfully or holds a value of type `E` errorfully. It is
/// designed to be used in situations which the return value of a function might
/// contains some errors. The official way to use this type is to use the
/// factory functions `Success` and `Error`. For example:
///
/// \code
/// auto successfulResult = Result<int>::success(3);
/// auto notOkResult = Result<int>::error(SomeLLVMError());
// \endcode
///
/// In order check for a value being errorful or successful checkout the `ok`
/// method or simply use the value as a conditiona.
///
/// This class is setup in a way tha you can us a value of type `T` in places
/// that the compiler expects a `Result<T>`. So for example:
///
/// \code
/// Result<int> fn() {return 2;}
/// \endcode
///
/// works perfectly.
template <typename T, typename E = llvm::Error>
class SERENE_EXPORT Result {
// The actual data container
std::variant<T, E> contents;
/// The main constructor which we made private to avoid ambiguousness in
/// input type. `Success` and `Error` call this ctor.
template <typename InPlace, typename Content>
Result(InPlace i, Content &&c) : contents(i, std::forward<Content>(c)){};
public:
constexpr Result(const T &v)
: Result(std::in_place_index_t<0>(), std::move(v)){};
/// Return a pointer to the success case value of the result. It is
/// important to check for the success case before calling this function.
constexpr const T *getPointer() const { return &getValue(); }
/// Return a pointer to the success case value of the result. It is
/// important to check for the success case before calling this function.
T *getPointer() { return &getValue(); }
/// Return a pointer to the success case value of the result. It is
/// important to check for the success case before calling this function.
T *operator->() { return getPointer(); }
/// Return a pointer to the success case value of the result. It is
/// important to check for the success case before calling this function.
constexpr const T *operator->() const { return getPointer(); }
/// Dereference the success case and returns the value. It is
/// important to check for the success case before calling this function.
constexpr const T &operator*() const & { return getValue(); }
/// Dereference the success case and returns the value. It is
/// important to check for the success case before calling this function.
T &operator*() & { return getValue(); }
/// Create a succesfull result with the given value of type `T`.
static Result success(T v) {
return Result(std::in_place_index_t<0>(), std::move(v));
}
/// Create an errorful result with the given value of type `E` (default
/// `llvm::Error`).
static Result error(E e) {
return Result(std::in_place_index_t<1>(), std::move(e));
}
/// Return the value if it's successful otherwise throw an error
T &&getValue() && { return std::move(std::get<0>(contents)); };
/// Return the error value if it's errorful otherwise throw an error
E &&getError() && { return std::move(std::get<1>(contents)); };
// using std::get, it'll throw if contents doesn't contain what you ask for
/// Return the value if it's successful otherwise throw an error
T &getValue() & { return std::get<0>(contents); };
/// Return the error value if it's errorful otherwise throw an error
E &getError() & { return std::get<1>(contents); };
const T &getValue() const & { return std::get<0>(contents); }
const E &getError() const & { return std::get<1>(contents); }
/// Return the a boolean value indicating whether the value is succesful
/// or errorful.
bool ok() const { return std::holds_alternative<T>(contents); };
operator bool() const { return ok(); }
};
inline void makeFQSymbolName(const llvm::StringRef &ns,
const llvm::StringRef &sym, std::string &result) {
result = (ns + "/" + sym).str();
};
} // namespace serene
#endif

View File

@ -1,169 +0,0 @@
# Serene Programming Language
#
# Copyright (c) 2019-2023 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/>.
#TODO: To support MacOS look into cmake's public headers
# https://cmake.org/cmake/help/latest/prop_tgt/PUBLIC_HEADER.html
# Prevent any future RPATH issue on Posix
if(NOT APPLE)
set(CMAKE_INSTALL_RPATH $ORIGIN)
endif()
add_library(serene
exprs/symbol.cpp
exprs/list.cpp
exprs/number.cpp
exprs/expression.cpp
exprs/def.cpp
exprs/fn.cpp
exprs/call.cpp
serene.cpp
context.cpp
namespace.cpp
source_mgr.cpp
diagnostics.cpp
semantics.cpp
# jit.cpp
# jit/engine.cpp
# jit/layers.cpp
jit/halley.cpp
errors.cpp
# Reader
reader/reader.cpp
# IR
slir/types.cpp
slir/slir.cpp
slir/value_op.cpp
slir/generatable.cpp
slir/utils.cpp
slir/ops.cpp
slir/type_converter.cpp
slir/dialect.cpp
passes/slir_lowering.cpp
passes/to_llvm_dialect.cpp
)
# Create an ALIAS target. This way if we mess up the name
# there will be an cmake error inseat of a linker error which is harder
# to understand. So any binary that wants to use serene has to
# use `Serene::lib` alias instead
add_library(Serene::lib ALIAS serene)
set_target_properties(serene PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
# Warn on unused libs
LINK_WHAT_YOU_USE TRUE
# LTO support
INTERPROCEDURAL_OPTIMIZATION TRUE)
if(SERENE_ENABLE_TIDY)
set_target_properties(serene PROPERTIES CXX_CLANG_TIDY ${CLANG_TIDY_PATH})
endif()
# Do we need to build serene as a shared lib? default is "yes"
if(SERENE_SHARED_LIB)
# We need to use libsan as a shared lib on debug mode. The
# target executable has to be built with `-fsanitize=address`
# as well and it has to run with:
# LD_PRELOAD=$(clang -print-file-name=libclang_rt.asan-x86_64.so)
target_compile_options(serene PRIVATE
$<$<CONFIG:DEBUG>:-shared-libsan>
)
target_link_options(
serene PRIVATE
$<$<CONFIG:DEBUG>:-shared-libsan>
)
endif()
if (CPP_20_SUPPORT)
target_compile_features(serene PUBLIC cxx_std_20)
else()
target_compile_features(serene PUBLIC cxx_std_17)
endif()
# Generate the tablegen ODS files before this target
add_dependencies(serene SereneTablegen SereneDialectGen SerenePassGen SereneErrorGen)
# We need this directory, and users of our library will need it too
target_include_directories(serene PUBLIC "$<BUILD_INTERFACE:${INCLUDE_DIR}>")
target_include_directories(serene PUBLIC "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>")
# Generate the export.h
include(GenerateExportHeader)
generate_export_header(serene EXPORT_FILE_NAME ${PROJECT_BINARY_DIR}/include/serene/export.h)
target_compile_definitions(
serene PUBLIC "$<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:SERENE_STATIC_DEFINE>")
get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS)
if(SERENE_SHOW_MLIR_DIALECTS)
message(STATUS "MLIR Dialects to choose from:")
foreach(lib ${dialect_libs})
message(STATUS "\t${lib}")
endforeach()
endif()
if(SERENE_SHOW_MLIR_TRANSFORMERS)
message(STATUS "MLIR Dialects transformers to choose from:")
foreach(lib ${conversion_libs})
message(STATUS "\t${lib}")
endforeach()
endif()
if(SERENE_SHOW_LLVM_LIBS)
execute_process(COMMAND llvm-config --libs all
OUTPUT_VARIABLE SERENE_LLVM_LIBS)
message(STATUS "LLVM libs available:\n ${SERENE_LLVM_LIBS}")
endif()
set(serene_lib_dialects_in_use
MLIRFunc)
set(serene_lib_transformers_in_use
MLIRFuncToLLVM)
target_link_libraries(serene PRIVATE
MLIRIR
MLIRPass
MLIRTransforms
${serene_lib_dialects_in_use}
${serene_lib_transformers_in_use}
#TODO: Remove this lib, we're just using one func
MLIRExecutionEngine
# LLVM's JIT lib
LLVMExecutionEngine
LLVMOrcJIT
MLIRLLVMToLLVMIRTranslation
LLVMTarget
LLVMX86AsmParser
${llvm_libs})

View File

@ -1,185 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/context.h"
#include "serene/namespace.h"
#include "serene/passes.h"
#include "serene/reader/location.h"
#include "serene/slir/generatable.h"
#include <llvm/Support/FormatVariadic.h>
#include <utility>
namespace serene {
void SereneContext::insertNS(NSPtr &ns) {
auto nsName = ns->name;
namespaces[nsName] = ns;
};
Namespace *SereneContext::getNS(llvm::StringRef nsName) {
if (namespaces.count(nsName.str()) != 0) {
return namespaces[nsName.str()].get();
}
return nullptr;
};
Namespace &SereneContext::getCurrentNS() {
if (this->currentNS.empty() || (namespaces.count(this->currentNS) == 0)) {
panic(*this, llvm::formatv("getCurrentNS: Namespace '{0}' does not exist",
this->currentNS)
.str());
}
return *namespaces[this->currentNS];
};
NSPtr SereneContext::getSharedPtrToNS(llvm::StringRef nsName) {
if (namespaces.count(nsName.str()) == 0) {
return nullptr;
}
return namespaces[nsName.str()];
};
void SereneContext::setOperationPhase(CompilationPhase phase) {
this->targetPhase = phase;
if (phase == CompilationPhase::SLIR) {
return;
}
if (phase >= CompilationPhase::MLIR) {
pm.addPass(serene::passes::createSLIRLowerToMLIRPass());
}
if (phase >= CompilationPhase::LIR) {
pm.addPass(serene::passes::createSLIRLowerToLLVMDialectPass());
}
};
int SereneContext::getOptimizatioLevel() {
if (targetPhase <= CompilationPhase::NoOptimization) {
return 0;
}
if (targetPhase == CompilationPhase::O1) {
return 1;
}
if (targetPhase == CompilationPhase::O2) {
return 2;
}
return 3;
}
NSPtr SereneContext::makeNamespace(llvm::StringRef name,
std::optional<llvm::StringRef> filename) {
auto ns = Namespace::make(*this, name, filename);
if (ns != nullptr) {
insertNS(ns);
}
return ns;
};
MaybeNS SereneContext::readNamespace(const std::string &name) {
// TODO: Replace this location with a proper location indicating
// the reason why the location data is not available.
// For example, because the ns might be the entry point ns.
auto loc = reader::LocationRange::UnknownLocation(name);
return readNamespace(name, loc);
};
MaybeNS SereneContext::readNamespace(const std::string &name,
reader::LocationRange loc) {
return withCurrentNS<MaybeNS>(name, [&]() -> MaybeNS {
return this->sourceManager.readNamespace(*this, name, loc);
});
}
MaybeNS SereneContext::importNamespace(const std::string &name) {
auto loc = reader::LocationRange::UnknownLocation(name);
return importNamespace(name, loc);
}
MaybeNS SereneContext::importNamespace(const std::string &name,
reader::LocationRange loc) {
auto maybeNS = readNamespace(name, loc);
if (maybeNS) {
auto &ns = *maybeNS;
if (auto err = jit->addNS(*ns, loc)) {
return err;
}
insertNS(ns);
}
return maybeNS;
}
llvm::orc::JITDylib *SereneContext::getLatestJITDylib(Namespace &ns) {
if (jitDylibs.count(ns.name) == 0) {
return nullptr;
}
auto vec = jitDylibs[ns.name];
// TODO: Make sure that the returning Dylib still exists in the JIT
// by calling jit->engine->getJITDylibByName(dylib_name);
return vec.empty() ? nullptr : vec.back();
};
void SereneContext::pushJITDylib(Namespace &ns, llvm::orc::JITDylib *l) {
if (jitDylibs.count(ns.name) == 0) {
llvm::SmallVector<llvm::orc::JITDylib *, 1> vec{l};
jitDylibs[ns.name] = vec;
return;
}
auto vec = jitDylibs[ns.name];
vec.push_back(l);
jitDylibs[ns.name] = vec;
}
size_t SereneContext::getNumberOfJITDylibs(Namespace &ns) {
if (jitDylibs.count(ns.name) == 0) {
return 0;
}
auto vec = jitDylibs[ns.name];
return vec.size();
};
void terminate(SereneContext &ctx, int exitCode) {
UNUSED(ctx);
// TODO: Since we are running in a single thread for now using exit is fine
// but we need to adjust and change it to a thread safe termination
// process later on.
// NOLINTNEXTLINE(concurrency-mt-unsafe)
std::exit(exitCode);
}
std::unique_ptr<SereneContext> makeSereneContext(Options opts) {
return SereneContext::make(opts);
};
}; // namespace serene

View File

@ -1,218 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/diagnostics.h"
#include "serene/context.h"
#include "serene/reader/location.h"
#include "serene/source_mgr.h"
#include "serene/utils.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/Error.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 {
void Diagnostic::writeColorByType(llvm::raw_ostream &os, llvm::StringRef str) {
llvm::ColorMode mode =
ctx.opts.withColors ? llvm::ColorMode::Auto : llvm::ColorMode::Disable;
llvm::WithColor s(os, llvm::raw_ostream::SAVEDCOLOR, true, false, mode);
switch (type) {
case Type::Error:
s.changeColor(llvm::raw_ostream::Colors::RED);
break;
// case Type::Warning:
// s.changeColor(llvm::raw_ostream::Colors::YELLOW);
// break;
case Type::Note:
s.changeColor(llvm::raw_ostream::Colors::CYAN);
break;
case Type::Remark:
s.changeColor(llvm::raw_ostream::Colors::MAGENTA);
break;
}
s << str;
s.resetColor();
};
std::string Diagnostic::getPrefix(llvm::StringRef prefix) {
if (!prefix.empty()) {
return prefix.str();
}
switch (type) {
case Type::Error:
return "Error";
break;
case Type::Note:
return "Note";
case Type::Remark:
return "Remark";
}
};
void Diagnostic::print(llvm::raw_ostream &os, llvm::StringRef prefix) const {
llvm::ColorMode mode =
ctx.opts.withColors ? llvm::ColorMode::Auto : llvm::ColorMode::Disable;
llvm::WithColor s(os, llvm::raw_ostream::SAVEDCOLOR, true, false, mode);
UNUSED(prefix);
UNUSED(err);
// s << "\n[";
// writeColorByType(os, "Error");
// s << "]>\n";
// s << "In ns '";
// s.changeColor(llvm::raw_ostream::Colors::MAGENTA);
// s << loc.start.ns;
// s.resetColor();
// if (loc.start.filename.hasValue()) {
// s << "' At: ";
// s.changeColor(llvm::raw_ostream::Colors::YELLOW);
// s << loc.start.filename.getValue();
// s.resetColor();
// s << ":";
// s.changeColor(llvm::raw_ostream::Colors::CYAN);
// s << loc.start.line;
// s.resetColor();
// s << ":";
// s.changeColor(llvm::raw_ostream::Colors::CYAN);
// s << loc.start.col;
// s.resetColor();
// }
// s << "\n\n";
// const auto &srcBuf = ctx.sourceManager.getBufferInfo(loc.start.ns);
// const char *line = srcBuf.getPointerForLineNumber(loc.start.line);
// while (*line != '\n' && line != srcBuf.buffer->getBufferEnd()) {
// s << *line;
// line++;
// }
// s << '\n';
// s.changeColor(llvm::raw_ostream::Colors::GREEN);
// s << llvm::formatv("{0}", llvm::fmt_pad("^", (size_t)loc.start.col - 1,
// 0)); s.resetColor();
// s << '\n';
// s << "[";
// if (err != nullptr) {
// writeColorByType(os, err->getErrId 8());
// }
// s << "] ";
// writeColorByType(os, getPrefix(prefix));
// s << ": ";
// if (err != nullptr) {
// s << err->description << '\n';
// }
// if (!message.empty()) {
// s.changeColor(llvm::raw_ostream::Colors::YELLOW);
// s << "With message";
// s.resetColor();
// s << ": " << message << "\n";
// }
// if (err != nullptr) {
// s << "For more information checkout";
// s.changeColor(llvm::raw_ostream::Colors::CYAN);
// s << " `serenec --explain ";
// s << err->getErrId() << "`\n";
// }
};
DiagnosticEngine::DiagnosticEngine(SereneContext &ctx)
: ctx(ctx), diagEngine(ctx.mlirContext.getDiagEngine()){};
void DiagnosticEngine::print(llvm::raw_ostream &os, Diagnostic &d) {
UNUSED(ctx);
UNUSED(os);
UNUSED(d);
};
Diagnostic DiagnosticEngine::toDiagnostic(reader::LocationRange loc,
llvm::Error &e, llvm::StringRef msg,
llvm::StringRef fn) {
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);
terminate(ctx, 1);
};
void DiagnosticEngine::emitSyntaxError(reader::LocationRange loc,
llvm::Error &e, llvm::StringRef msg) {
Diagnostic diag(ctx, loc, &e, msg);
diag.print(llvm::errs(), "SyntaxError");
terminate(ctx, 1);
};
std::unique_ptr<DiagnosticEngine> makeDiagnosticEngine(SereneContext &ctx) {
return std::make_unique<DiagnosticEngine>(ctx);
}
void DiagnosticEngine::emit(const llvm::Error &err) {
UNUSED(ctx);
// TODO: create a diag and print it
llvm::errs() << "[Error]: " << err << "\n";
};
// void DiagnosticEngine::emit(const llvm::Error &errs) {
//
// // for (const auto &e : errs) {
// // emit(e);
// // }
//
// };
void panic(SereneContext &ctx, llvm::Twine msg) {
auto err =
llvm::make_error<llvm::StringError>(msg, llvm::inconvertibleErrorCode());
ctx.diagEngine->emit(err);
terminate(ctx, 1);
};
void panic(SereneContext &ctx, const llvm::Error &err) {
ctx.diagEngine->emit(err);
terminate(ctx, 1);
};
void throwErrors(SereneContext &ctx, const llvm::Error &err) {
// llvm::handleErrors(errs,
// [](const errors::SereneError &e){});
ctx.diagEngine->emit(err);
};
} // namespace serene

View File

@ -1,42 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/errors.h"
#include <llvm/Support/Casting.h>
#include <llvm/Support/Error.h>
namespace serene::errors {
// We need this to make Error class a llvm::Error friendy implementation
char SereneError::ID;
std::string getMessage(const llvm::Error &e) {
std::string msg;
llvm::raw_string_ostream os(msg);
os << e;
return os.str();
};
const ErrorVariant *getVariant(ErrorType t) {
if ((0 <= (int)t) && (t < NUMBER_OF_ERRORS)) {
return &errorVariants[t];
}
return nullptr;
};
} // namespace serene::errors

View File

@ -1,141 +0,0 @@
/*
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/exprs/call.h"
#include "serene/errors.h"
#include "serene/exprs/def.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/list.h"
#include "serene/exprs/symbol.h"
#include "serene/namespace.h"
#include "serene/utils.h"
#include <llvm/Support/Casting.h>
#include <llvm/Support/ErrorHandling.h>
#include <llvm/Support/FormatVariadic.h>
namespace serene {
namespace exprs {
ExprType Call::getType() const { return ExprType::Call; };
std::string Call::toString() const {
return llvm::formatv("<Call {0} {1}>", this->target->toString(),
astToString(&this->params));
}
MaybeNode Call::analyze(semantics::AnalysisState &state) {
UNUSED(state);
return EmptyNode;
};
bool Call::classof(const Expression *e) {
return e->getType() == ExprType::Call;
};
MaybeNode Call::make(semantics::AnalysisState &state, List *list) {
auto &ctx = state.ns.getContext();
// TODO: replace this with a runtime check
assert((list->count() != 0) && "Empty call? Seriously ?");
// Let's find out what is the first element of the list
auto maybeFirst = list->elements[0]->analyze(state);
if (!maybeFirst) {
// There's something wrong with the first element. Return the error
return maybeFirst;
}
Node &first = *maybeFirst;
// No rewrite is needed for the first element
if (!first) {
first = list->elements[0];
}
Node targetNode;
Ast rawParams;
if (list->count() > 1) {
rawParams = list->from(1);
}
// We need to create the Call node based on the type of the first
// element after it being analyzed.
switch (first->getType()) {
// In case of a Symbol, We should look it up in the current scope and
// if it resolves to a value. Then we have to make sure that the
// return value is callable.
case ExprType::Symbol: {
auto *sym = llvm::dyn_cast<Symbol>(first.get());
if (sym == nullptr) {
llvm_unreachable("Couldn't case to Symbol while the type is symbol!");
}
auto maybeResult = state.env.lookup(sym->name);
if (!maybeResult.hasValue()) {
std::string msg =
llvm::formatv("Can't resolve the symbol '{0}'", sym->name);
return errors::makeError(ctx, errors::CantResolveSymbol, sym->location,
msg);
}
targetNode = std::move(maybeResult.getValue());
break;
}
case ExprType::Def:
// If the first element was a Call itself we need to just chain it
// with a new call. It would be something like `((blah 1) 4)`. `blah`
// should return a callable expression itself, which we need to let
// the typechecker to check
case ExprType::Call:
// If the first element was a function, then just use it as the target
// of the call. It would be like `((fn (x) x) 4)`
case ExprType::Fn: {
targetNode = first;
break;
}
// Otherwise we don't know how to call the first element.
default: {
std::string msg = llvm::formatv("Don't know how to call a '{0}'",
stringifyExprType(first->getType()));
return errors::makeError(ctx, errors::DontKnowHowToCallNode,
first->location, msg);
}
};
auto analyzedParams = semantics::analyze(state, rawParams);
if (!analyzedParams) {
return analyzedParams.takeError();
}
return makeSuccessfulNode<Call>(list->location, targetNode, *analyzedParams);
};
} // namespace exprs
} // namespace serene

View File

@ -1,138 +0,0 @@
/*
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/exprs/def.h"
#include "serene/errors.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/fn.h"
#include "serene/exprs/list.h"
#include "serene/exprs/symbol.h"
#include "serene/exprs/traits.h"
#include "serene/slir/dialect.h"
#include "serene/slir/utils.h"
#include <llvm/Support/Casting.h>
#include <llvm/Support/ErrorHandling.h>
#include <llvm/Support/FormatVariadic.h>
namespace serene {
namespace exprs {
ExprType Def::getType() const { return ExprType::Def; };
std::string Def::toString() const {
return llvm::formatv("<Def {0} -> {1}>", this->binding,
this->value->toString());
}
MaybeNode Def::analyze(semantics::AnalysisState &state) {
UNUSED(state);
return EmptyNode;
};
bool Def::classof(const Expression *e) {
return e->getType() == ExprType::Def;
};
MaybeNode Def::make(semantics::AnalysisState &state, List *list) {
auto &ctx = state.ns.getContext();
// TODO: Add support for docstring as the 3rd argument (4th element)
if (list->count() != 3) {
std::string msg = llvm::formatv("Expected 3 got {0}", list->count());
return errors::makeError(ctx, errors::DefWrongNumberOfArgs,
list->elements[0]->location, msg);
}
// Make sure that the list starts with a `def`
Symbol *defSym = llvm::dyn_cast<Symbol>(list->elements[0].get());
assert((defSym && defSym->name == "def") &&
"The first element of the list should be a 'def'.");
// Make sure that the first argument is a Symbol
Symbol *binding = llvm::dyn_cast<Symbol>(list->elements[1].get());
if (binding == nullptr) {
return errors::makeError(ctx, errors::DefExpectSymbol,
list->elements[1]->location);
}
// Analyze the value
MaybeNode value = list->elements[2]->analyze(state);
Node analyzedValue;
// TODO: To refactor this logic into a generic function
if (value) {
// Success value
auto &valueNode = *value;
if (valueNode) {
// A rewrite is necessary
analyzedValue = valueNode;
} else {
// no rewrite
analyzedValue = list->elements[2];
}
} else {
// Error value
return value;
}
if (analyzedValue->getType() == ExprType::Fn) {
Fn *tmp = llvm::dyn_cast<Fn>(analyzedValue.get());
if (tmp == nullptr) {
llvm_unreachable("inconsistent getType for function");
}
tmp->setName(binding->name);
}
auto result = state.ns.define(binding->name, analyzedValue);
if (result.succeeded()) {
return makeSuccessfulNode<Def>(list->location, binding->name,
analyzedValue);
}
llvm_unreachable("Inserting a value in the semantic env failed!");
};
void Def::generateIR(serene::Namespace &ns, mlir::ModuleOp &m) {
if (value->getType() == ExprType::Fn) {
value->generateIR(ns, m);
return;
}
// auto loc = slir::toMLIRLocation(ns, location.start);
// auto &mctx = ns.getContext().mlirContext;
// mlir::OpBuilder builder(&mctx);
// auto sym = slir::SymbolType::get(&mctx, ns.name, binding);
// TODO: we need to change the generate method of expressions
// to return mlir::Value or any wrapper of that which would
// be the ssa form of the result of the expression.
// and then use it to define the def op here.
// auto def = builder.create<slir::DefOp>(sym, binding, value);
m.emitError("Def: not implemented!");
};
} // namespace exprs
} // namespace serene

View File

@ -1,46 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/exprs/expression.h"
#include <llvm/Support/FormatVariadic.h>
namespace serene {
namespace exprs {
std::string astToString(const Ast *tree) {
if (tree->empty()) {
return "";
}
std::string result = tree->at(0)->toString();
for (unsigned int i = 1; i < tree->size(); i++) {
result = llvm::formatv("{0} {1}", result, tree->at(i)->toString());
}
return result;
}
std::string stringifyExprType(ExprType t) { return exprTypes[(int)t]; };
/// Dump the given AST tree to the standard out
void dump(Ast &tree) { llvm::outs() << astToString(&tree) << "\n"; };
} // namespace exprs
} // namespace serene

View File

@ -1,166 +0,0 @@
/*
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/exprs/fn.h"
#include "serene/errors.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/list.h"
#include "serene/exprs/symbol.h"
#include "serene/slir/dialect.h"
#include "serene/slir/ops.h"
#include "serene/slir/utils.h"
#include <llvm/Support/Casting.h>
#include <llvm/Support/FormatVariadic.h>
#include <mlir/IR/Block.h>
#include <mlir/IR/BuiltinAttributes.h>
#include <utility>
namespace serene {
namespace exprs {
Fn::Fn(SereneContext &ctx, reader::LocationRange &loc, List &args, Ast body)
: Expression(loc), args(args), body(std::move(body)) {
this->setName(
llvm::formatv("___fn___{0}", ctx.getCurrentNS().nextFnCounter()));
};
ExprType Fn::getType() const { return ExprType::Fn; };
std::string Fn::toString() const {
return llvm::formatv("<Fn {0} {1} to {2}>",
this->name.empty() ? "Anonymous" : this->name,
this->args.toString(),
this->body.empty() ? "<>" : astToString(&this->body));
}
MaybeNode Fn::analyze(semantics::AnalysisState &state) {
UNUSED(state);
return EmptyNode;
};
bool Fn::classof(const Expression *e) { return e->getType() == ExprType::Fn; };
void Fn::setName(std::string n) { this->name = std::move(n); };
MaybeNode Fn::make(semantics::AnalysisState &state, List *list) {
auto &ctx = state.ns.getContext();
// TODO: Add support for docstring as the 3rd argument (4th element)
if (list->count() < 2) {
return errors::makeError(ctx, errors::FnNoArgsList,
list->elements[0]->location,
"The argument list is mandatory.");
}
Symbol *fnSym = llvm::dyn_cast<Symbol>(list->elements[0].get());
// TODO: Replace this assert with a runtime check
assert((fnSym && fnSym->name == "fn") &&
"The first element of the list should be a 'fn'.");
List *args = llvm::dyn_cast<List>(list->elements[1].get());
if (args == nullptr) {
std::string msg =
llvm::formatv("Arguments of a function has to be a list, got '{0}'",
stringifyExprType(list->elements[1]->getType()));
return errors::makeError(ctx, errors::FnArgsMustBeList,
list->elements[1]->location, msg);
}
Ast body;
// If there is a body for this function analyze the body and set
// the retuned ast as the final body
if (list->count() > 2) {
body = std::vector<Node>(list->begin() + 2, list->end());
// TODO: call state.moveToNewEnv to create a new env and set the
// arguments into the new env.
auto maybeAst = semantics::analyze(state, body);
if (!maybeAst) {
return maybeAst.takeError();
}
body = *maybeAst;
}
return makeSuccessfulNode<Fn>(ctx, list->location, *args, body);
};
void Fn::generateIR(serene::Namespace &ns, mlir::ModuleOp &m) {
auto loc = slir::toMLIRLocation(ns, location.start);
auto &ctx = ns.getContext();
mlir::OpBuilder builder(&ctx.mlirContext);
// llvm::SmallVector<mlir::Type, 4> arg_types;
llvm::SmallVector<mlir::NamedAttribute, 4> arguments;
// at the moment we only support integers
for (auto &arg : args) {
auto *argSym = llvm::dyn_cast<Symbol>(arg.get());
if (argSym == nullptr) {
m->emitError(llvm::formatv(
"Arguments of a function have to be symbols. Fn: '{0}'", name));
return;
}
arguments.push_back(builder.getNamedAttr(
argSym->name, mlir::TypeAttr::get(builder.getI64Type())));
}
auto fn = builder.create<slir::Fn1Op>(
loc, builder.getI64Type(), name,
mlir::DictionaryAttr::get(builder.getContext(), arguments),
builder.getStringAttr("public"));
if (!fn) {
m.emitError(llvm::formatv("Can't create the function '{0}'", name));
return;
}
auto &body = fn.body();
auto *entryBlock = new mlir::Block();
body.push_back(entryBlock);
builder.setInsertionPointToStart(entryBlock);
auto retVal = builder.create<slir::Value1Op>(loc, 0).getResult();
slir::Return1Op returnOp = builder.create<slir::Return1Op>(loc, retVal);
if (!returnOp) {
m.emitError(
llvm::formatv("Can't create the return value of function '{0}'", name));
fn.erase();
return;
}
m.push_back(fn);
};
} // namespace exprs
} // namespace serene

View File

@ -1,118 +0,0 @@
/*
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/exprs/list.h"
#include "serene/errors.h"
#include "serene/exprs/call.h"
#include "serene/exprs/def.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/fn.h"
#include "serene/exprs/symbol.h"
#include <llvm/Support/Casting.h>
#include <llvm/Support/ErrorHandling.h>
#include <llvm/Support/FormatVariadic.h>
#include <iterator>
#include <utility>
namespace serene {
namespace exprs {
List::List(const List &l) : Expression(l.location) {
this->elements = l.elements;
};
List::List(const reader::LocationRange &loc, Node &e) : Expression(loc) {
elements.push_back(e);
};
List::List(const reader::LocationRange &loc, Ast elems)
: Expression(loc), elements(std::move(elems)){};
ExprType List::getType() const { return ExprType::List; };
std::string List::toString() const {
std::string s{this->elements.empty() ? "-" : ""};
for (const auto &n : this->elements) {
s = llvm::formatv("{0} {1}", s, n->toString());
}
return llvm::formatv("<List {0}>", s);
};
MaybeNode List::analyze(semantics::AnalysisState &state) {
if (!elements.empty()) {
auto *first = elements[0].get();
if (first->getType() == ExprType::Symbol) {
auto *sym = llvm::dyn_cast<Symbol>(first);
if (sym != nullptr) {
if (sym->name == "def") {
return Def::make(state, this);
}
if (sym->name == "fn") {
return Fn::make(state, this);
}
}
}
return Call::make(state, this);
}
return EmptyNode;
};
bool List::classof(const Expression *e) {
return e->getType() == ExprType::List;
};
std::vector<Node>::const_iterator List::cbegin() { return elements.begin(); }
std::vector<Node>::const_iterator List::cend() { return elements.end(); }
std::vector<Node>::iterator List::begin() { return elements.begin(); }
std::vector<Node>::iterator List::end() { return elements.end(); }
size_t List::count() const { return elements.size(); }
std::optional<Expression *> List::at(uint index) {
if (index >= elements.size()) {
return llvm::None;
}
return std::optional<Expression *>(this->elements[index].get());
}
Ast List::from(uint index) {
if (index < elements.size()) {
return Ast(elements.begin() + index, elements.end());
}
return Ast();
}
void List::append(Node n) { elements.push_back(std::move(n)); }
} // namespace exprs
} // namespace serene

View File

@ -1,59 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/exprs/number.h"
#include "serene/exprs/expression.h"
#include "serene/slir/dialect.h"
#include "serene/slir/ops.h"
#include "serene/slir/utils.h"
namespace serene {
namespace exprs {
ExprType Number::getType() const { return ExprType::Number; };
std::string Number::toString() const {
return llvm::formatv("<Number {0}>", value);
}
MaybeNode Number::analyze(semantics::AnalysisState &state) {
UNUSED(state);
return EmptyNode;
};
bool Number::classof(const Expression *e) {
return e->getType() == ExprType::Number;
};
int Number::toI64() const { return std::stoi(this->value); };
void Number::generateIR(serene::Namespace &ns, mlir::ModuleOp &m) {
mlir::OpBuilder builder(&ns.getContext().mlirContext);
auto op = builder.create<serene::slir::Value1Op>(
serene::slir::toMLIRLocation(ns, location.start), toI64());
if (op) {
m.push_back(op);
}
// TODO: in case of failure attach the error to the NS
};
} // namespace exprs
} // namespace serene

View File

@ -1,46 +0,0 @@
/*
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/exprs/symbol.h"
#include "serene/exprs/expression.h"
#include <llvm/Support/Casting.h>
#include <llvm/Support/FormatVariadic.h>
namespace serene {
namespace exprs {
ExprType Symbol::getType() const { return ExprType::Symbol; };
std::string Symbol::toString() const {
return llvm::formatv("<Symbol {0}/{1}>", nsName, name);
}
MaybeNode Symbol::analyze(semantics::AnalysisState &state) {
UNUSED(state);
return EmptyNode;
};
bool Symbol::classof(const Expression *e) {
return e->getType() == ExprType::Symbol;
};
} // namespace exprs
} // namespace serene

View File

@ -1,110 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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"
#include <memory>
namespace serene::jit {
static void handleLazyCallThroughError() {
// TODO: Report to the diag engine
llvm::errs() << "LazyCallThrough error: Could not find function body";
// TODO: terminate ?
}
SereneJIT::SereneJIT(Namespace &entryNS,
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))),
transformLayer(*this->es, compileLayer, optimizeModule),
astLayer(transformLayer, mangler),
nsLayer(entryNS.getContext(), transformLayer, mangler, dl),
mainJD(this->es->createBareJITDylib(entryNS.name)),
ctx(entryNS.getContext()), currentNS(entryNS) {
UNUSED(this->ctx);
mainJD.addGenerator(
cantFail(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
dl.getGlobalPrefix())));
// if (numCompileThreads > 0) {
// compileOnDemandLayer.setCloneToNewContextOnEmit(true);
// }
}
llvm::Error SereneJIT::addNS(llvm::StringRef nsname,
orc::ResourceTrackerSP rt) {
if (!rt) {
rt = mainJD.getDefaultResourceTracker();
}
return nsLayer.add(rt, nsname);
};
llvm::Error SereneJIT::addAst(exprs::Ast &ast, orc::ResourceTrackerSP rt) {
if (!rt) {
rt = mainJD.getDefaultResourceTracker();
}
return astLayer.add(rt, getCurrentNS(), ast);
};
llvm::Expected<std::unique_ptr<SereneJIT>> makeSereneJIT(Namespace &ns) {
auto epc = orc::SelfExecutorProcessControl::Create();
if (!epc) {
return epc.takeError();
}
auto es = std::make_unique<orc::ExecutionSession>(std::move(*epc));
auto epciu =
orc::EPCIndirectionUtils::Create(es->getExecutorProcessControl());
if (!epciu) {
return epciu.takeError();
}
(*epciu)->createLazyCallThroughManager(
*es, llvm::pointerToJITTargetAddress(&handleLazyCallThroughError));
if (auto err = setUpInProcessLCTMReentryViaEPCIU(**epciu)) {
return std::move(err);
}
orc::JITTargetMachineBuilder jtmb(
es->getExecutorProcessControl().getTargetTriple());
auto dl = jtmb.getDefaultDataLayoutForTarget();
if (!dl) {
return dl.takeError();
}
return std::make_unique<SereneJIT>(ns, std::move(es), std::move(*epciu),
std::move(jtmb), std::move(*dl));
};
} // namespace serene::jit

View File

@ -1,457 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/halley.h"
#include "serene/context.h"
#include "serene/diagnostics.h"
#include "serene/errors.h"
#include "serene/exprs/symbol.h"
#include "serene/namespace.h"
#include "serene/utils.h"
#include <llvm-c/Types.h>
#include <llvm/ADT/None.h>
#include <llvm/ADT/Optional.h>
#include <llvm/ExecutionEngine/Orc/CompileUtils.h>
#include <llvm/ExecutionEngine/Orc/Core.h>
#include <llvm/ExecutionEngine/Orc/ExecutionUtils.h>
#include <llvm/ExecutionEngine/Orc/IRCompileLayer.h>
#include <llvm/ExecutionEngine/Orc/IRTransformLayer.h>
#include <llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h>
#include <llvm/ExecutionEngine/Orc/LLJIT.h>
#include <llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/ErrorHandling.h>
#include <llvm/Support/FormatVariadic.h>
#include <llvm/Support/FormatVariadicDetails.h>
#include <llvm/Support/ToolOutputFile.h>
#include <llvm/Support/raw_ostream.h>
#include <mlir/ExecutionEngine/ExecutionEngine.h>
#include <mlir/Support/FileUtilities.h>
#include <memory>
#include <stdexcept>
#include <vector>
#define COMMON_ARGS_COUNT 8
// Just to make the linter happy
#define I64_BIT_SIZE 64
namespace serene {
namespace jit {
static std::string makePackedFunctionName(llvm::StringRef name) {
// TODO: move the "_serene_" constant to a macro or something
return "_serene_" + name.str();
}
static void packFunctionArguments(llvm::Module *module) {
auto &ctx = module->getContext();
llvm::IRBuilder<> builder(ctx);
llvm::DenseSet<llvm::Function *> interfaceFunctions;
for (auto &func : module->getFunctionList()) {
if (func.isDeclaration()) {
continue;
}
if (interfaceFunctions.count(&func) != 0) {
continue;
}
// Given a function `foo(<...>)`, define the interface function
// `serene_foo(i8**)`.
auto *newType = llvm::FunctionType::get(
builder.getVoidTy(), builder.getInt8PtrTy()->getPointerTo(),
/*isVarArg=*/false);
auto newName = makePackedFunctionName(func.getName());
auto funcCst = module->getOrInsertFunction(newName, newType);
llvm::Function *interfaceFunc =
llvm::cast<llvm::Function>(funcCst.getCallee());
interfaceFunctions.insert(interfaceFunc);
// Extract the arguments from the type-erased argument list and cast them to
// the proper types.
auto *bb = llvm::BasicBlock::Create(ctx);
bb->insertInto(interfaceFunc);
builder.SetInsertPoint(bb);
llvm::Value *argList = interfaceFunc->arg_begin();
llvm::SmallVector<llvm::Value *, COMMON_ARGS_COUNT> args;
args.reserve(llvm::size(func.args()));
for (const auto &indexedArg : llvm::enumerate(func.args())) {
llvm::Value *argIndex = llvm::Constant::getIntegerValue(
builder.getInt64Ty(), llvm::APInt(I64_BIT_SIZE, indexedArg.index()));
llvm::Value *argPtrPtr =
builder.CreateGEP(builder.getInt8PtrTy(), argList, argIndex);
llvm::Value *argPtr =
builder.CreateLoad(builder.getInt8PtrTy(), argPtrPtr);
llvm::Type *argTy = indexedArg.value().getType();
argPtr = builder.CreateBitCast(argPtr, argTy->getPointerTo());
llvm::Value *arg = builder.CreateLoad(argTy, argPtr);
args.push_back(arg);
}
// Call the implementation function with the extracted arguments.
llvm::Value *result = builder.CreateCall(&func, args);
// Assuming the result is one value, potentially of type `void`.
if (!result->getType()->isVoidTy()) {
llvm::Value *retIndex = llvm::Constant::getIntegerValue(
builder.getInt64Ty(),
llvm::APInt(I64_BIT_SIZE, llvm::size(func.args())));
llvm::Value *retPtrPtr =
builder.CreateGEP(builder.getInt8PtrTy(), argList, retIndex);
llvm::Value *retPtr =
builder.CreateLoad(builder.getInt8PtrTy(), retPtrPtr);
retPtr = builder.CreateBitCast(retPtr, result->getType()->getPointerTo());
builder.CreateStore(result, retPtr);
}
// The interface function returns void.
builder.CreateRetVoid();
}
};
void ObjectCache::notifyObjectCompiled(const llvm::Module *m,
llvm::MemoryBufferRef objBuffer) {
cachedObjects[m->getModuleIdentifier()] =
llvm::MemoryBuffer::getMemBufferCopy(objBuffer.getBuffer(),
objBuffer.getBufferIdentifier());
}
std::unique_ptr<llvm::MemoryBuffer>
ObjectCache::getObject(const llvm::Module *m) {
auto i = cachedObjects.find(m->getModuleIdentifier());
if (i == cachedObjects.end()) {
HALLEY_LOG("No object for " + m->getModuleIdentifier() +
" in cache. Compiling.");
return nullptr;
}
HALLEY_LOG("Object for " + m->getModuleIdentifier() + " loaded from cache.");
return llvm::MemoryBuffer::getMemBuffer(i->second->getMemBufferRef());
}
void ObjectCache::dumpToObjectFile(llvm::StringRef outputFilename) {
// Set up the output file.
std::string errorMessage;
auto file = mlir::openOutputFile(outputFilename, &errorMessage);
if (!file) {
llvm::errs() << errorMessage << "\n";
return;
}
// Dump the object generated for a single module to the output file.
// TODO: Replace this with a runtime check
assert(cachedObjects.size() == 1 && "Expected only one object entry.");
auto &cachedObject = cachedObjects.begin()->second;
file->os() << cachedObject->getBuffer();
file->keep();
}
Halley::Halley(serene::SereneContext &ctx,
llvm::orc::JITTargetMachineBuilder &&jtmb, llvm::DataLayout &&dl)
: cache(ctx.opts.JITenableObjectCache ? new ObjectCache() : nullptr),
gdbListener(ctx.opts.JITenableGDBNotificationListener
? llvm::JITEventListener::createGDBRegistrationListener()
: nullptr),
perfListener(ctx.opts.JITenablePerfNotificationListener
? llvm::JITEventListener::createPerfJITEventListener()
: nullptr),
jtmb(jtmb), dl(dl), ctx(ctx),
activeNS(ctx.getSharedPtrToNS(ctx.getCurrentNS().name)) {
assert(activeNS != nullptr && "Active NS is null!!!");
};
MaybeJITPtr Halley::lookup(exprs::Symbol &sym) const {
HALLEY_LOG("Looking up: " << sym.toString());
auto *ns = ctx.getNS(sym.nsName);
if (ns == nullptr) {
return errors::makeError(ctx, errors::CantResolveSymbol, sym.location,
"Can't find the namespace in the context: " +
sym.nsName);
}
auto *dylib = ctx.getLatestJITDylib(*ns);
//
if (dylib == nullptr) {
return errors::makeError(ctx, errors::CantResolveSymbol, sym.location,
"Don't know about namespace: " + sym.nsName);
}
auto expectedSymbol =
engine->lookup(*dylib, makePackedFunctionName(sym.name));
// JIT lookup may return an Error referring to strings stored internally by
// the JIT. If the Error outlives the ExecutionEngine, it would want have a
// dangling reference, which is currently caught by an assertion inside JIT
// thanks to hand-rolled reference counting. Rewrap the error message into a
// string before returning. Alternatively, ORC JIT should consider copying
// the string into the error message.
if (!expectedSymbol) {
std::string errorMessage;
llvm::raw_string_ostream os(errorMessage);
llvm::handleAllErrors(expectedSymbol.takeError(),
[&os](llvm::ErrorInfoBase &ei) { ei.log(os); });
return errors::makeError(ctx, errors::CantResolveSymbol, sym.location,
os.str());
}
auto rawFPtr = expectedSymbol->getValue();
// NOLINTNEXTLINE(performance-no-int-to-ptr)
auto fptr = reinterpret_cast<void (*)(void **)>(rawFPtr);
if (fptr == nullptr) {
return errors::makeError(ctx, errors::CantResolveSymbol, sym.location,
"Lookup function is null!");
}
return fptr;
};
// llvm::Error Halley::invokePacked(llvm::StringRef name,
// llvm::MutableArrayRef<void *> args) const {
// auto expectedFPtr = lookup(name);
// if (!expectedFPtr) {
// return expectedFPtr.takeError();
// }
// auto fptr = *expectedFPtr;
// (*fptr)(args.data());
// return llvm::Error::success();
// }
llvm::Error Halley::addNS(Namespace &ns, reader::LocationRange &loc) {
HALLEY_LOG(llvm::formatv("Creating Dylib {0}#{1}", ns.name,
ctx.getNumberOfJITDylibs(ns) + 1));
auto newDylib = engine->createJITDylib(
llvm::formatv("{0}#{1}", ns.name, ctx.getNumberOfJITDylibs(ns) + 1));
if (!newDylib) {
return errors::makeError(ctx, errors::CompilationError, loc,
"Filed to create dylib for " + ns.name);
}
ctx.pushJITDylib(ns, &(*newDylib));
// TODO: Fix compileToLLVM to return proper errors
auto maybeModule = ns.compileToLLVM();
if (!maybeModule.hasValue()) {
return errors::makeError(ctx, errors::CompilationError, loc);
}
auto tsm = std::move(maybeModule.getValue());
tsm.withModuleDo([](llvm::Module &m) { packFunctionArguments(&m); });
// TODO: Make sure that the data layout of the module is the same as the
// engine
cantFail(engine->addIRModule(*newDylib, std::move(tsm)));
return llvm::Error::success();
};
void Halley::setEngine(std::unique_ptr<llvm::orc::LLJIT> e, bool isLazy) {
// Later on we might use different classes of JIT which might need some
// work for lazyness
engine = std::move(e);
this->isLazy = isLazy;
};
void Halley::dumpToObjectFile(llvm::StringRef filename) {
cache->dumpToObjectFile(filename);
};
MaybeJIT Halley::make(SereneContext &serene_ctx,
llvm::orc::JITTargetMachineBuilder &&jtmb) {
auto dl = jtmb.getDefaultDataLayoutForTarget();
if (!dl) {
return dl.takeError();
}
auto jitEngine =
std::make_unique<Halley>(serene_ctx, std::move(jtmb), std::move(*dl));
// Why not the llvmcontext from the SereneContext??
// Sice we're going to pass the ownership of this context to a thread
// safe module later on and we will only create the jit function wrappers
// with it, then it is fine to use a new context.
//
// What might go wrong?
// in a repl env when we have to create new modules on top of each other
// having two different contex might be a problem, but i think since we
// use the first context to generate the IR and the second one to just
// run it.
std::unique_ptr<llvm::LLVMContext> ctx(new llvm::LLVMContext);
// Callback to create the object layer with symbol resolution to current
// process and dynamically linked libraries.
auto objectLinkingLayerCreator = [&](llvm::orc::ExecutionSession &session,
const llvm::Triple &tt) {
UNUSED(tt);
auto objectLayer =
std::make_unique<llvm::orc::RTDyldObjectLinkingLayer>(session, []() {
return std::make_unique<llvm::SectionMemoryManager>();
});
// Register JIT event listeners if they are enabled.
if (jitEngine->gdbListener != nullptr) {
objectLayer->registerJITEventListener(*jitEngine->gdbListener);
}
if (jitEngine->perfListener != nullptr) {
objectLayer->registerJITEventListener(*jitEngine->perfListener);
}
// COFF format binaries (Windows) need special handling to deal with
// exported symbol visibility.
// cf llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
// LLJIT::createObjectLinkingLayer
if (serene_ctx.triple.isOSBinFormatCOFF()) {
objectLayer->setOverrideObjectFlagsWithResponsibilityFlags(true);
objectLayer->setAutoClaimResponsibilityForObjectSymbols(true);
}
// Resolve symbols from shared libraries.
// for (auto libPath : sharedLibPaths) {
// auto mb = llvm::MemoryBuffer::getFile(libPath);
// if (!mb) {
// llvm::errs() << "Failed to create MemoryBuffer for: " << libPath
// << "\nError: " << mb.getError().message() << "\n";
// continue;
// }
// auto &JD = session.createBareJITDylib(std::string(libPath));
// auto loaded = llvm::orc::DynamicLibrarySearchGenerator::Load(
// libPath.data(), dataLayout.getGlobalPrefix());
// if (!loaded) {
// llvm::errs() << "Could not load " << libPath << ":\n "
// << loaded.takeError() << "\n";
// continue;
// }
// JD.addGenerator(std::move(*loaded));
// cantFail(objectLayer->add(JD, std::move(mb.get())));
// }
return objectLayer;
};
// Callback to inspect the cache and recompile on demand. This follows Lang's
// LLJITWithObjectCache example.
auto compileFunctionCreator = [&](llvm::orc::JITTargetMachineBuilder JTMB)
-> llvm::Expected<
std::unique_ptr<llvm::orc::IRCompileLayer::IRCompiler>> {
llvm::CodeGenOpt::Level jitCodeGenOptLevel =
static_cast<llvm::CodeGenOpt::Level>(serene_ctx.getOptimizatioLevel());
JTMB.setCodeGenOptLevel(jitCodeGenOptLevel);
auto targetMachine = JTMB.createTargetMachine();
if (!targetMachine) {
return targetMachine.takeError();
}
return std::make_unique<llvm::orc::TMOwningSimpleCompiler>(
std::move(*targetMachine), jitEngine->cache.get());
};
if (serene_ctx.opts.JITLazy) {
// Setup a LLLazyJIT instance to the times that latency is important
// for example in a REPL. This way
auto jit =
cantFail(llvm::orc::LLLazyJITBuilder()
.setCompileFunctionCreator(compileFunctionCreator)
.setObjectLinkingLayerCreator(objectLinkingLayerCreator)
.create());
jitEngine->setEngine(std::move(jit), true);
} else {
// Setup a LLJIT instance for the times that performance is important
// and we want to compile everything as soon as possible. For instance
// when we run the JIT in the compiler
auto jit =
cantFail(llvm::orc::LLJITBuilder()
.setCompileFunctionCreator(compileFunctionCreator)
.setObjectLinkingLayerCreator(objectLinkingLayerCreator)
.create());
jitEngine->setEngine(std::move(jit), false);
}
jitEngine->engine->getIRCompileLayer().setNotifyCompiled(
[&](llvm::orc::MaterializationResponsibility &r,
llvm::orc::ThreadSafeModule tsm) {
auto syms = r.getRequestedSymbols();
tsm.withModuleDo([&](llvm::Module &m) {
HALLEY_LOG("Compiled "
<< syms << " for the module: " << m.getModuleIdentifier());
});
});
// Resolve symbols that are statically linked in the current process.
llvm::orc::JITDylib &mainJD = jitEngine->engine->getMainJITDylib();
mainJD.addGenerator(
cantFail(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
jitEngine->dl.getGlobalPrefix())));
return MaybeJIT(std::move(jitEngine));
};
llvm::Error Halley::addAST(exprs::Ast &ast) {
auto offset = activeNS->getTree().size();
if (auto errs = activeNS->addTree(ast)) {
return errs;
}
auto maybeModule = activeNS->compileToLLVMFromOffset(offset);
auto tsm = std::move(maybeModule.getValue());
tsm.withModuleDo([](llvm::Module &m) { packFunctionArguments(&m); });
auto *dylib = ctx.getLatestJITDylib(*activeNS);
// TODO: Make sure that the data layout of the module is the same as the
// engine
cantFail(engine->addIRModule(*dylib, std::move(tsm)));
return llvm::Error::success();
};
Namespace &Halley::getActiveNS() { return *activeNS; };
llvm::Expected<std::unique_ptr<Halley>> makeHalleyJIT(SereneContext &ctx) {
llvm::orc::JITTargetMachineBuilder jtmb(ctx.triple);
auto maybeJIT = Halley::make(ctx, std::move(jtmb));
if (!maybeJIT) {
return maybeJIT.takeError();
}
return maybeJIT;
};
} // namespace jit
} // namespace serene

View File

@ -1,162 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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 "serene/exprs/fn.h"
#include "serene/exprs/traits.h"
#include <llvm/ExecutionEngine/Orc/ThreadSafeModule.h>
#include <llvm/Support/Error.h> // for report_fatal_error
#include <algorithm>
namespace serene::jit {
llvm::orc::ThreadSafeModule compileAst(Namespace &ns, exprs::Ast &ast) {
assert(ns.getTree().size() < ast.size() && "Did you add the ast to the NS?");
LAYER_LOG("Compile in context of namespace: " + ns.name);
unsigned offset = ns.getTree().size() - ast.size();
auto maybeModule = ns.compileToLLVMFromOffset(offset);
if (!maybeModule) {
// TODO: Handle failure
llvm::report_fatal_error("Couldn't compile lazily JIT'd function");
}
return std::move(maybeModule.getValue());
};
AstMaterializationUnit::AstMaterializationUnit(Namespace &ns, AstLayer &l,
exprs::Ast &ast)
: orc::MaterializationUnit(l.getInterface(ns, ast)), ns(ns), astLayer(l),
ast(ast){};
void AstMaterializationUnit::materialize(
std::unique_ptr<orc::MaterializationResponsibility> r) {
astLayer.emit(std::move(r), ns, ast);
}
orc::MaterializationUnit::Interface AstLayer::getInterface(Namespace &ns,
exprs::Ast &e) {
orc::SymbolFlagsMap Symbols;
auto symList = ns.getSymList();
unsigned index = symList.size();
// This probably will change symList
auto err = ns.addTree(e);
if (err) {
// TODO: Fix this by a call to diag engine or return the err
llvm::outs() << "Fixme: semantic err\n";
return orc::MaterializationUnit::Interface(std::move(Symbols), nullptr);
}
auto &env = ns.getRootEnv();
auto populateTableFn = [&env, this, &Symbols](auto name) {
auto flags = llvm::JITSymbolFlags::Exported;
auto maybeExpr = env.lookup(name.str());
if (!maybeExpr) {
LAYER_LOG("Skiping '" + name + "' symbol");
return;
}
auto expr = maybeExpr.getValue();
if (expr->getType() == exprs::ExprType::Fn) {
flags = flags | llvm::JITSymbolFlags::Callable;
}
auto mangledSym = this->mangler(name);
LAYER_LOG("Mangle symbol for: " + name + " = " << mangledSym);
Symbols[mangledSym] = llvm::JITSymbolFlags(flags);
};
std::for_each(symList.begin() + index, symList.end(), populateTableFn);
return orc::MaterializationUnit::Interface(std::move(Symbols), nullptr);
}
/// NS Layer ==================================================================
llvm::orc::ThreadSafeModule compileNS(Namespace &ns) {
LAYER_LOG("Compile namespace: " + ns.name);
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(NSLayer &l, Namespace &ns)
: MaterializationUnit(l.getInterface(ns)), 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) {
LAYER_LOG("Add namespace: " + nsname);
auto maybeNS = ctx.readNamespace(nsname.str(), loc);
if (!maybeNS) {
// TODO: Fix this by making Serene errors compatible with llvm::Error
auto err = maybeNS.takeError();
return err;
}
auto ns = *maybeNS;
LAYER_LOG("Add the materialize unit for: " + nsname);
return rt->getJITDylib().define(
std::make_unique<NSMaterializationUnit>(*this, *ns), rt);
}
orc::MaterializationUnit::Interface
NSLayer::getInterface(serene::Namespace &ns) {
orc::SymbolFlagsMap Symbols;
for (auto &k : ns.getRootEnv()) {
auto flags = llvm::JITSymbolFlags::Exported;
auto name = k.getFirst();
auto expr = k.getSecond();
if (expr->getType() == exprs::ExprType::Fn) {
flags = flags | llvm::JITSymbolFlags::Callable;
}
auto mangledSym = mangler(name);
LAYER_LOG("Mangle symbol for: " + name + " = " << mangledSym);
Symbols[mangledSym] = llvm::JITSymbolFlags(flags);
}
return orc::MaterializationUnit::Interface(std::move(Symbols), nullptr);
}
} // namespace serene::jit

View File

@ -1,224 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/namespace.h"
#include "serene/context.h"
#include "serene/export.h"
#include "serene/exprs/expression.h"
#include "serene/llvm/IR/Value.h"
#include "serene/semantics.h"
#include "serene/slir/slir.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/FormatVariadic.h>
#include <llvm/Support/raw_ostream.h>
#include <mlir/IR/Builders.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/Verifier.h>
#include <mlir/Support/LogicalResult.h>
#include <memory>
#include <stdexcept>
#include <string>
using namespace std;
using namespace llvm;
namespace serene {
Namespace::Namespace(SereneContext &ctx, llvm::StringRef ns_name,
std::optional<llvm::StringRef> filename)
: ctx(ctx), name(ns_name) {
if (filename.hasValue()) {
this->filename.emplace(filename.getValue().str());
}
// Create the root environment
createEnv(nullptr);
};
SemanticEnv &Namespace::createEnv(SemanticEnv *parent) {
auto env = std::make_unique<SemanticEnv>(parent);
environments.push_back(std::move(env));
return *environments.back();
};
SemanticEnv &Namespace::getRootEnv() {
assert(!environments.empty() && "Root env is not created!");
return *environments.front();
};
mlir::LogicalResult Namespace::define(std::string &name, exprs::Node &node) {
auto &rootEnv = getRootEnv();
if (failed(rootEnv.insert_symbol(name, node))) {
return mlir::failure();
}
symbolList.push_back(name);
return mlir::success();
}
exprs::Ast &Namespace::getTree() { return this->tree; }
llvm::Error Namespace::addTree(exprs::Ast &ast) {
// If the target phase is just parsing we don't want
// to run the semantic analyzer or anything beyond parser
if (ctx.getTargetPhase() == CompilationPhase::Parse) {
// we just want the raw AST
this->tree.insert(this->tree.end(), ast.begin(), ast.end());
return llvm::Error::success();
}
auto &rootEnv = getRootEnv();
auto state = semantics::makeAnalysisState(*this, rootEnv);
// Run the semantic analyer on the ast and then if everything
// is ok add the form to the tree and forms
auto maybeForm = semantics::analyze(*state, ast);
if (!maybeForm) {
return maybeForm.takeError();
}
auto semanticAst = std::move(*maybeForm);
this->tree.insert(this->tree.end(), semanticAst.begin(), semanticAst.end());
return llvm::Error::success();
}
uint Namespace::nextFnCounter() { return fn_counter++; };
SereneContext &Namespace::getContext() { return this->ctx; };
MaybeModuleOp Namespace::generate(unsigned offset) {
// The reason why we return an optional value instead of Errors
// is the way MLIR's diagnostic engine works. Passes may use
// the `emit` function of operations to report errors to the
// diagnostic engine. So we can't return any error diractly.
mlir::OpBuilder builder(&ctx.mlirContext);
// TODO: Fix the unknown location by pointing to the `ns` form
auto module = mlir::ModuleOp::create(builder.getUnknownLoc(),
std::optional<llvm::StringRef>(name));
auto treeSize = getTree().size();
// Walk the AST and call the `generateIR` function of each node.
// Since nodes will have access to the a reference of the
// namespace they can use the builder and keep adding more
// operations to the module via the builder
for (unsigned i = offset; i < treeSize; ++i) {
auto &node = getTree()[i];
node->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!");
module.erase();
return llvm::None;
}
return MaybeModuleOp(module);
}
mlir::LogicalResult Namespace::runPasses(mlir::ModuleOp &m) {
return ctx.pm.run(m);
};
void Namespace::dump() {
llvm::outs() << "\nMLIR: \n";
auto maybeModuleOp = generate();
if (!maybeModuleOp) {
llvm::errs() << "Failed to generate the IR.\n";
return;
}
mlir::OpPrintingFlags flags;
flags.enableDebugInfo();
maybeModuleOp.getValue()->print(llvm::outs(), flags);
};
MaybeModule Namespace::compileToLLVM() {
// The reason why we return an optional value instead of Errors
// is the way MLIR's diagnostic engine works. Passes may use
// the `emit` function of operations to report errors to the
// diagnostic engine. So we can't return any error diractly.
auto maybeModule = generate();
if (!maybeModule) {
NAMESPACE_LOG("IR generation failed for '" << name << "'");
return llvm::None;
}
if (ctx.getTargetPhase() >= CompilationPhase::IR) {
mlir::ModuleOp module = maybeModule.getValue().get();
return ::serene::slir::compileToLLVMIR(ctx, module);
}
return llvm::None;
};
MaybeModule Namespace::compileToLLVMFromOffset(unsigned offset) {
// The reason why we return an optional value instead of Errors
// is the way MLIR's diagnostic engine works. Passes may use
// the `emit` function of operations to report errors to the
// diagnostic engine. So we can't return any error diractly.
auto maybeModule = generate(offset);
if (!maybeModule) {
NAMESPACE_LOG("IR generation failed for '" << name << "'");
return llvm::None;
}
if (ctx.getTargetPhase() >= CompilationPhase::IR) {
mlir::ModuleOp module = maybeModule.getValue().get();
return ::serene::slir::compileToLLVMIR(ctx, module);
}
return llvm::None;
};
NSPtr Namespace::make(SereneContext &ctx, llvm::StringRef name,
std::optional<llvm::StringRef> filename) {
return std::make_shared<Namespace>(ctx, name, filename);
};
Namespace::~Namespace() {
// TODO: Clean up anything related to this namespace in the context
// TODO: Remove anything related to this namespace in the JIT
NAMESPACE_LOG("Destructing NS: " << name);
};
} // namespace serene

View File

@ -1,761 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/context.h"
#include "serene/conventions.h"
#include "serene/diagnostics.h"
#include "serene/passes.h"
#include "serene/slir/dialect.h"
#include "serene/slir/ops.h"
#include "serene/slir/type_converter.h"
#include "serene/slir/types.h"
#include "serene/utils.h"
#include <serene/config.h>
#include <llvm/Support/Casting.h>
#include <llvm/Support/Error.h>
#include <llvm/Support/ErrorHandling.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Support/thread.h>
#include <mlir/Dialect/Arithmetic/IR/Arithmetic.h>
#include <mlir/Dialect/Func/IR/FuncOps.h>
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
#include <mlir/Dialect/LLVMIR/LLVMTypes.h>
#include <mlir/Dialect/MemRef/IR/MemRef.h>
#include <mlir/IR/Attributes.h>
#include <mlir/IR/BuiltinAttributes.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/BuiltinTypes.h>
#include <mlir/IR/Matchers.h>
#include <mlir/IR/OperationSupport.h>
#include <mlir/Pass/Pass.h>
#include <mlir/Support/LLVM.h>
#include <mlir/Support/LogicalResult.h>
#include <mlir/Transforms/DialectConversion.h>
#include <cstdint>
namespace ll = mlir::LLVM;
namespace serene::passes {
// static ll::GlobalOp getOrCreateInternalString(mlir::Location loc,
// mlir::OpBuilder &builder,
// llvm::StringRef name,
// llvm::StringRef value,
// mlir::ModuleOp module) {
// // Create the global at the entry of the module.
// ll::GlobalOp global;
// if (!(global = module.lookupSymbol<ll::GlobalOp>(name))) {
// mlir::OpBuilder::InsertionGuard insertGuard(builder);
// builder.setInsertionPointToStart(module.getBody());
// auto type = ll::LLVMArrayType::get(
// mlir::IntegerType::get(builder.getContext(), I8_SIZE), value.size());
// // TODO: Do we want link once ?
// global = builder.create<ll::GlobalOp>(loc, type, /*isConstant=*/true,
// ll::Linkage::Linkonce, name,
// builder.getStringAttr(value),
// /*alignment=*/0);
// }
// return global;
// };
// static mlir::Value getPtrToInternalString(mlir::OpBuilder &builder,
// ll::GlobalOp global) {
// auto loc = global.getLoc();
// auto I8 = mlir::IntegerType::get(builder.getContext(), I8_SIZE);
// // Get the pointer to the first character in the global string.
// mlir::Value globalPtr = builder.create<ll::AddressOfOp>(loc, global);
// mlir::Value cst0 = builder.create<ll::ConstantOp>(
// loc, mlir::IntegerType::get(builder.getContext(), I64_SIZE),
// builder.getIntegerAttr(builder.getIndexType(), 0));
// return builder.create<ll::GEPOp>(loc, ll::LLVMPointerType::get(I8),
// globalPtr,
// llvm::ArrayRef<mlir::Value>({cst0}));
// };
// static ll::GlobalOp getOrCreateString(mlir::Location loc,
// mlir::OpBuilder &builder,
// llvm::StringRef name,
// llvm::StringRef value, uint32_t len,
// mlir::ModuleOp module) {
// auto *ctx = builder.getContext();
// ll::GlobalOp global;
// if (!(global = module.lookupSymbol<ll::GlobalOp>(name))) {
// mlir::OpBuilder::InsertionGuard insertGuard(builder);
// builder.setInsertionPointToStart(module.getBody());
// mlir::Attribute initValue{};
// auto type = slir::getStringTypeinLLVM(*ctx);
// global = builder.create<ll::GlobalOp>(
// loc, type, /*isConstant=*/true, ll::Linkage::Linkonce, name,
// initValue);
// auto &gr = global.getInitializerRegion();
// auto *block = builder.createBlock(&gr);
// if (block == nullptr) {
// module.emitError("Faild to create block of the globalOp!");
// // TODO: change the return type to Expected<GlobalOp> and return
// // an error here
// }
// builder.setInsertionPoint(block, block->begin());
// mlir::Value structInstant = builder.create<ll::UndefOp>(loc, type);
// auto strOp = getOrCreateInternalString(loc, builder, name, value,
// module); auto ptrToStr = getPtrToInternalString(builder, strOp);
// auto length = builder.create<ll::ConstantOp>(
// loc, mlir::IntegerType::get(ctx, I32_SIZE),
// builder.getI32IntegerAttr(len));
// // Setting the string pointer field
// structInstant = builder.create<ll::InsertValueOp>(
// loc, structInstant.getType(), structInstant, ptrToStr,
// builder.getI64ArrayAttr(0));
// // Setting the len field
// structInstant = builder.create<ll::InsertValueOp>(
// loc, structInstant.getType(), structInstant, length,
// builder.getI64ArrayAttr(1));
// builder.create<ll::ReturnOp>(loc, structInstant);
// }
// return global;
// };
// static ll::GlobalOp getOrCreateSymbol(mlir::Location loc,
// mlir::OpBuilder &builder,
// llvm::StringRef ns, llvm::StringRef
// name, mlir::ModuleOp module) {
// std::string fqName;
// ll::GlobalOp global;
// auto *ctx = builder.getContext();
// auto symName = serene::mangleInternalSymName(fqName);
// makeFQSymbolName(ns, name, fqName);
// if (!(global = module.lookupSymbol<ll::GlobalOp>(symName))) {
// mlir::OpBuilder::InsertionGuard insertGuard(builder);
// builder.setInsertionPointToStart(module.getBody());
// mlir::Attribute initValue{};
// auto type = slir::getSymbolTypeinLLVM(*ctx);
// // We want to allow merging the strings representing the ns or name part
// // of the symbol with other modules to unify them.
// ll::Linkage linkage = ll::Linkage::Linkonce;
// global = builder.create<ll::GlobalOp>(loc, type, /*isConstant=*/true,
// linkage, symName, initValue);
// auto &gr = global.getInitializerRegion();
// auto *block = builder.createBlock(&gr);
// if (block == nullptr) {
// module.emitError("Faild to create block of the globalOp!");
// // TODO: change the return type to Expected<GlobalOp> and return
// // an error here
// }
// builder.setInsertionPoint(block, block->begin());
// mlir::Value structInstant = builder.create<ll::UndefOp>(loc, type);
// // We want to use the mangled ns as the name of the constant that
// // holds the ns string
// auto mangledNSName = serene::mangleInternalStringName(ns);
// // The globalop that we want to use for the ns field
// auto nsField =
// getOrCreateString(loc, builder, mangledNSName, ns, ns.size(),
// module);
// auto ptrToNs = builder.create<ll::AddressOfOp>(loc, nsField);
// // We want to use the mangled 'name' as the name of the constant that
// // holds the 'name' string
// auto mangledName = serene::mangleInternalStringName(name);
// // The global op to use as the 'name' field
// auto nameField =
// getOrCreateString(loc, builder, mangledName, name, name.size(),
// module);
// auto ptrToName = builder.create<ll::AddressOfOp>(loc, nameField);
// // Setting the string pointer field
// structInstant = builder.create<ll::InsertValueOp>(
// loc, structInstant.getType(), structInstant, ptrToNs,
// builder.getI64ArrayAttr(0));
// // Setting the len field
// structInstant = builder.create<ll::InsertValueOp>(
// loc, structInstant.getType(), structInstant, ptrToName,
// builder.getI64ArrayAttr(0));
// builder.create<ll::ReturnOp>(loc, structInstant);
// }
// return global;
// };
// static ll::GlobalOp getOrCreateSymbol(mlir::Location loc,
// mlir::OpBuilder &builder, mlir::Value
// ns, mlir::Value name, mlir::ModuleOp
// module) {
// assert(!ns.getType().isa<slir::StringType>() &&
// !ns.getType().isa<slir::StringType>() &&
// "TypeError: ns and name has to be strings");
// std::string fqName;
// ll::GlobalOp global;
// auto *ctx = builder.getContext();
// auto symName = serene::mangleInternalSymName(fqName);
// makeFQSymbolName(ns, name, fqName);
// if (!(global = module.lookupSymbol<ll::GlobalOp>(symName))) {
// mlir::OpBuilder::InsertionGuard insertGuard(builder);
// builder.setInsertionPointToStart(module.getBody());
// mlir::Attribute initValue{};
// auto type = slir::getSymbolTypeinLLVM(*ctx);
// // We want to allow merging the strings representing the ns or name part
// // of the symbol with other modules to unify them.
// ll::Linkage linkage = ll::Linkage::Linkonce;
// global = builder.create<ll::GlobalOp>(loc, type, /*isConstant=*/true,
// linkage, symName, initValue);
// auto &gr = global.getInitializerRegion();
// auto *block = builder.createBlock(&gr);
// if (block == nullptr) {
// module.emitError("Faild to create block of the globalOp!");
// // TODO: change the return type to Expected<GlobalOp> and return
// // an error here
// }
// builder.setInsertionPoint(block, block->begin());
// mlir::Value structInstant = builder.create<ll::UndefOp>(loc, type);
// // We want to use the mangled ns as the name of the constant that
// // holds the ns string
// auto mangledNSName = serene::mangleInternalStringName(ns);
// // The globalop that we want to use for the ns field
// auto nsField =
// getOrCreateString(loc, builder, mangledNSName, ns, ns.size(),
// module);
// auto ptrToNs = builder.create<ll::AddressOfOp>(loc, nsField);
// // We want to use the mangled 'name' as the name of the constant that
// // holds the 'name' string
// auto mangledName = serene::mangleInternalStringName(name);
// // The global op to use as the 'name' field
// auto nameField =
// getOrCreateString(loc, builder, mangledName, name, name.size(),
// module);
// auto ptrToName = builder.create<ll::AddressOfOp>(loc, nameField);
// // Setting the string pointer field
// structInstant = builder.create<ll::InsertValueOp>(
// loc, structInstant.getType(), structInstant, ptrToNs,
// builder.getI64ArrayAttr(0));
// // Setting the len field
// structInstant = builder.create<ll::InsertValueOp>(
// loc, structInstant.getType(), structInstant, ptrToName,
// builder.getI64ArrayAttr(0));
// builder.create<ll::ReturnOp>(loc, structInstant);
// }
// return global;
// };
// struct LowerIntern : public mlir::OpConversionPattern<slir::InternOp> {
// using OpConversionPattern<slir::InternOp>::OpConversionPattern;
// mlir::LogicalResult
// matchAndRewrite(serene::slir::InternOp op, OpAdaptor adaptor,
// mlir::ConversionPatternRewriter &rewriter) const override;
// };
// mlir::LogicalResult
// LowerIntern::matchAndRewrite(serene::slir::InternOp op, OpAdaptor adaptor,
// mlir::ConversionPatternRewriter &rewriter) const
// {
// UNUSED(adaptor);
// auto ns = op.ns();
// auto name = op.name();
// auto loc = op.getLoc();
// auto module = op->getParentOfType<mlir::ModuleOp>();
// // If there is no use for the result of this op then simply erase it
// if (op.getResult().use_empty()) {
// rewriter.eraseOp(op);
// return mlir::success();
// }
// auto global = getOrCreateSymbol(loc, rewriter, ns, name, module);
// auto ptr = rewriter.create<ll::AddressOfOp>(loc, global);
// rewriter.replaceOp(op, ptr.getResult());
// return mlir::success();
// }
// struct LowerSymbol : public mlir::OpConversionPattern<slir::SymbolOp> {
// using OpConversionPattern<slir::SymbolOp>::OpConversionPattern;
// mlir::LogicalResult
// matchAndRewrite(serene::slir::SymbolOp op, OpAdaptor adaptor,
// mlir::ConversionPatternRewriter &rewriter) const override;
// };
// mlir::LogicalResult
// LowerSymbol::matchAndRewrite(serene::slir::SymbolOp op, OpAdaptor adaptor,
// mlir::ConversionPatternRewriter &rewriter) const
// {
// UNUSED(adaptor);
// auto ns = op.ns();
// auto name = op.name();
// auto loc = op.getLoc();
// auto module = op->getParentOfType<mlir::ModuleOp>();
// // If there is no use for the result of this op then simply erase it
// if (op.getResult().use_empty()) {
// rewriter.eraseOp(op);
// return mlir::success();
// }
// auto global = getOrCreateSymbol(loc, rewriter, ns, name, module);
// auto ptr = rewriter.create<ll::AddressOfOp>(loc, global);
// rewriter.replaceOp(op, ptr.getResult());
// return mlir::success();
// }
struct LowerDefine : public mlir::OpConversionPattern<slir::DefineOp> {
using OpConversionPattern<slir::DefineOp>::OpConversionPattern;
mlir::LogicalResult
matchAndRewrite(serene::slir::DefineOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override;
};
mlir::LogicalResult
LowerDefine::matchAndRewrite(serene::slir::DefineOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
(void)rewriter;
(void)adaptor;
auto value = op.value();
auto *valueop = value.getDefiningOp();
auto maybeTopLevel = op.is_top_level();
bool isTopLevel = false;
if (maybeTopLevel) {
isTopLevel = *maybeTopLevel;
}
// If the value than we bind a name to is a constant, rewrite to
// `define_constant`
// TODO: Replace the isConstantLike with a `hasTrait` call
if (mlir::detail::isConstantLike(valueop)) {
mlir::Attribute constantValue;
if (!mlir::matchPattern(value, mlir::m_Constant(&constantValue))) {
PASS_LOG(
"Failure: The constant like op don't have a constant attribute.");
return mlir::failure();
}
rewriter.replaceOpWithNewOp<slir::DefineConstantOp>(
op, op.sym_name(), constantValue, rewriter.getBoolAttr(isTopLevel),
op.sym_visibilityAttr());
if (valueop->use_empty()) {
PASS_LOG("Erase op due to empty use:" << valueop);
rewriter.eraseOp(valueop);
}
return mlir::success();
}
// If the value was a Function literal (like an anonymous function)
// rewrite to a Func.FuncOp
if (mlir::isa<slir::FnOp>(valueop)) {
// TODO: Lower to a function op
rewriter.eraseOp(op);
return mlir::success();
}
// TODO: [lib] If we're building an executable `linkonce` is a good choice
// but for a library we need to choose a better link type
ll::Linkage linkage = ll::Linkage::Linkonce;
auto loc = op.getLoc();
auto moduleOp = op->getParentOfType<mlir::ModuleOp>();
auto ns = moduleOp.getNameAttr();
auto name = op.getName();
mlir::Attribute initAttr{};
std::string fqsym;
makeFQSymbolName(ns.getValue(), name, fqsym);
if (!isTopLevel) {
auto llvmType = typeConverter->convertType(value.getType());
{
mlir::PatternRewriter::InsertionGuard insertGuard(rewriter);
rewriter.setInsertionPointToStart(moduleOp.getBody());
auto globalOp = rewriter.create<ll::GlobalOp>(loc, llvmType,
/*isConstant=*/false,
linkage, fqsym, initAttr);
auto &gr = globalOp.getInitializerRegion();
auto *block = rewriter.createBlock(&gr);
if (block == nullptr) {
op.emitError("Faild to create block of the globalOp!");
return mlir::failure();
}
rewriter.setInsertionPointToStart(block);
auto undef = rewriter.create<ll::UndefOp>(loc, llvmType);
rewriter.create<ll::ReturnOp>(loc, undef.getResult());
}
rewriter.setInsertionPointAfter(op);
auto symRef = mlir::SymbolRefAttr::get(rewriter.getContext(), fqsym);
// auto llvmValue = typeConverter->materializeTargetConversion(
// rewriter, loc, llvmType, value);
// llvm::outs() << ">>> " << symRef << "|" << llvmValue << "|" << op <<
// "\n";
rewriter.replaceOpWithNewOp<slir::SetValueOp>(op, symRef, value);
// auto setvalOp = rewriter.create<slir::SetValueOp>(loc, symRef,
// llvmValue); rewriter.insert(setvalOp); rewriter.eraseOp(op);
return mlir::success();
}
// auto globop = rewriter.create<ll::GlobalOp>(loc, value.getType(),
// /*isConstant=*/false,
// linkage, fqsym,
// initAttr);
// auto &gr = globop.getInitializerRegion();
// auto *block = rewriter.createBlock(&gr);
// block->addArgument(value.getType(), value.getLoc());
// rewriter.setInsertionPoint(block, block->begin());
// rewriter.create<ll::ReturnOp>(value.getLoc(),
// adaptor.getOperands());
// if (!op.getResult().use_empty()) {
// auto symValue = rewriter.create<slir::SymbolOp>(loc, ns, name);
// rewriter.replaceOp(op, symValue.getResult());
// }
rewriter.eraseOp(op);
return mlir::success();
}
struct LowerDefineConstant
: public mlir::OpConversionPattern<slir::DefineConstantOp> {
using OpConversionPattern<slir::DefineConstantOp>::OpConversionPattern;
mlir::LogicalResult
matchAndRewrite(serene::slir::DefineConstantOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override;
};
mlir::LogicalResult LowerDefineConstant::matchAndRewrite(
serene::slir::DefineConstantOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
(void)rewriter;
(void)adaptor;
auto value = op.value();
auto name = op.getName();
auto loc = op.getLoc();
auto moduleOp = op->getParentOfType<mlir::ModuleOp>();
auto ns = moduleOp.getNameAttr();
std::string fqsym;
makeFQSymbolName(ns.getValue(), name, fqsym);
// TODO: [lib] If we're building an executable `linkonce` is a good choice
// but for a library we need to choose a better link type
ll::Linkage linkage = ll::Linkage::Linkonce;
// TODO: use ll::ConstantOp instead
UNUSED(rewriter.create<ll::GlobalOp>(loc, value.getType(),
/*isConstant=*/true, linkage, fqsym,
value));
// if (!op.value().use_empty()) {
// auto symValue = rewriter.create<slir::SymbolOp>(loc, ns, name);
// rewriter.replaceOp(op, symValue.getResult());
// }
rewriter.eraseOp(op);
return mlir::success();
}
#define GEN_PASS_CLASSES
#include "serene/passes/passes.h.inc"
class LowerSLIR : public LowerSLIRBase<LowerSLIR> {
void runOnOperation() override {
mlir::ModuleOp module = getOperation();
// The first thing to define is the conversion target. This will define the
// final target for this lowering.
mlir::ConversionTarget target(getContext());
slir::TypeConverter typeConverter(getContext());
// We define the specific operations, or dialects, that are legal targets
// for this lowering. In our case, we are lowering to the `Standard`
// dialects.
target.addLegalDialect<mlir::func::FuncDialect>();
target.addLegalDialect<mlir::arith::ArithmeticDialect>();
target.addLegalDialect<ll::LLVMDialect>();
// We also define the SLIR dialect as Illegal so that the conversion will
// fail if any of these operations are *not* converted.
target.addIllegalDialect<serene::slir::SereneDialect>();
// Mark operations that are LEGAL for this pass. It means that we don't
// lower them is this pass but we will in another pass. So we don't want to
// get an error since we are not lowering them.
// target.addLegalOp<serene::slir::PrintOp>();
target.addLegalOp<slir::FnOp, slir::ValueOp, slir::SetValueOp>();
// Now that the conversion target has been defined, we just need to provide
// the set of patterns that will lower the SLIR operations.
mlir::RewritePatternSet patterns(&getContext());
// Pattern to lower ValueOp and FnOp
// LowerDefineConstant
patterns.add<LowerDefine, LowerDefineConstant>(typeConverter,
&getContext());
// With the target and rewrite patterns defined, we can now attempt the
// conversion. The conversion will signal failure if any of our `illegal`
// operations were not converted successfully.
if (failed(applyPartialConversion(module, target, std::move(patterns)))) {
signalPassFailure();
}
}
};
std::unique_ptr<mlir::Pass> createLowerSLIR() {
return std::make_unique<LowerSLIR>();
}
#define GEN_PASS_REGISTRATION
#include "serene/passes/passes.h.inc"
// ----------------------------------------------------------------------------
// ValueOp lowering to constant op
struct ValueOpLowering : public mlir::OpRewritePattern<serene::slir::Value1Op> {
using OpRewritePattern<serene::slir::Value1Op>::OpRewritePattern;
mlir::LogicalResult
matchAndRewrite(serene::slir::Value1Op op,
mlir::PatternRewriter &rewriter) const final;
};
mlir::LogicalResult
ValueOpLowering::matchAndRewrite(serene::slir::Value1Op op,
mlir::PatternRewriter &rewriter) const {
auto value = op.value();
mlir::Location loc = op.getLoc();
llvm::SmallVector<mlir::Type, 1> arg_types(0);
auto func_type = rewriter.getFunctionType(arg_types, rewriter.getI64Type());
// TODO: use a mechanism to generate unique names
auto fn = rewriter.create<mlir::func::FuncOp>(loc, "randomname", func_type);
auto *entryBlock = fn.addEntryBlock();
rewriter.setInsertionPointToStart(entryBlock);
// Since we only support i64 at the moment we use ConstantOp
auto retVal = rewriter
.create<mlir::arith::ConstantIntOp>(loc, (int64_t)value,
rewriter.getI64Type())
.getResult();
UNUSED(rewriter.create<mlir::func::ReturnOp>(loc, retVal));
fn.setPrivate();
// Erase the original ValueOP
rewriter.eraseOp(op);
return mlir::success();
}
// ----------------------------------------------------------------------------
// Fn lowering pattern
struct FnOpLowering : public mlir::OpRewritePattern<serene::slir::Fn1Op> {
using OpRewritePattern<serene::slir::Fn1Op>::OpRewritePattern;
mlir::LogicalResult
matchAndRewrite(serene::slir::Fn1Op op,
mlir::PatternRewriter &rewriter) const final;
};
mlir::LogicalResult
FnOpLowering::matchAndRewrite(serene::slir::Fn1Op op,
mlir::PatternRewriter &rewriter) const {
auto args = op.args();
auto name = op.name();
auto isPublic = op.sym_visibility().getValueOr("public") == "public";
mlir::Location loc = op.getLoc();
llvm::SmallVector<mlir::Type, 4> arg_types;
for (const auto &arg : args) {
auto attr = arg.getValue().dyn_cast<mlir::TypeAttr>();
if (!attr) {
op.emitError("It's not a type attr");
return mlir::failure();
}
arg_types.push_back(attr.getValue());
}
auto func_type = rewriter.getFunctionType(arg_types, rewriter.getI64Type());
auto fn = rewriter.create<mlir::func::FuncOp>(loc, name, func_type);
auto *entryBlock = fn.addEntryBlock();
rewriter.setInsertionPointToStart(entryBlock);
auto retVal = rewriter
.create<mlir::arith::ConstantIntOp>(loc, (int64_t)3,
rewriter.getI64Type())
.getResult();
rewriter.create<mlir::func::ReturnOp>(loc, retVal);
if (!isPublic) {
fn.setPrivate();
}
rewriter.eraseOp(op);
return mlir::success();
}
// ----------------------------------------------------------------------------
// SLIR lowering pass
// This Pass will lower SLIR to MLIR's standard dialect.
struct SLIRToMLIRPass
: public mlir::PassWrapper<SLIRToMLIRPass,
mlir::OperationPass<mlir::ModuleOp>> {
void getDependentDialects(mlir::DialectRegistry &registry) const override;
void runOnOperation() final;
void runOnModule();
mlir::ModuleOp getModule();
};
// Mark what dialects we need for this pass. It's basically translate to what
// dialects do we want to lower to
void SLIRToMLIRPass::getDependentDialects(
mlir::DialectRegistry &registry) const {
registry.insert<mlir::func::FuncDialect, mlir::arith::ArithmeticDialect>();
};
/// Return the current function being transformed.
mlir::ModuleOp SLIRToMLIRPass::getModule() { return this->getOperation(); }
void SLIRToMLIRPass::runOnOperation() { runOnModule(); }
void SLIRToMLIRPass::runOnModule() {
auto module = getModule();
// The first thing to define is the conversion target. This will define the
// final target for this lowering.
mlir::ConversionTarget target(getContext());
// We define the specific operations, or dialects, that are legal targets for
// this lowering. In our case, we are lowering to the `Standard` dialects.
target.addLegalDialect<mlir::func::FuncDialect>();
target.addLegalDialect<mlir::arith::ArithmeticDialect>();
// We also define the SLIR dialect as Illegal so that the conversion will fail
// if any of these operations are *not* converted.
target.addIllegalDialect<serene::slir::SereneDialect>();
// Mark operations that are LEGAL for this pass. It means that we don't lower
// them is this pass but we will in another pass. So we don't want to get
// an error since we are not lowering them.
// target.addLegalOp<serene::slir::PrintOp>();
target.addLegalOp<mlir::func::FuncOp>();
// Now that the conversion target has been defined, we just need to provide
// the set of patterns that will lower the SLIR operations.
mlir::RewritePatternSet patterns(&getContext());
// Pattern to lower ValueOp and FnOp
patterns.add<ValueOpLowering, FnOpLowering>(&getContext());
// With the target and rewrite patterns defined, we can now attempt the
// conversion. The conversion will signal failure if any of our `illegal`
// operations were not converted successfully.
if (failed(applyPartialConversion(module, target, std::move(patterns)))) {
signalPassFailure();
}
};
std::unique_ptr<mlir::Pass> createSLIRLowerToMLIRPass() {
return std::make_unique<SLIRToMLIRPass>();
};
void registerAllPasses() { registerPasses(); }
} // namespace serene::passes

View File

@ -1,87 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/passes.h"
#include "serene/slir/dialect.h"
#include <mlir/Conversion/AffineToStandard/AffineToStandard.h>
#include <mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h>
#include <mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h>
#include <mlir/Conversion/LLVMCommon/ConversionTarget.h>
#include <mlir/Conversion/LLVMCommon/TypeConverter.h>
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
#include <mlir/Dialect/SCF/SCF.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/Pass/Pass.h>
#include <mlir/Transforms/DialectConversion.h>
#include <memory>
namespace serene::passes {
struct SLIRToLLVMDialect
: public mlir::PassWrapper<SLIRToLLVMDialect,
mlir::OperationPass<mlir::ModuleOp>> {
void getDependentDialects(mlir::DialectRegistry &registry) const override {
registry.insert<mlir::LLVM::LLVMDialect>();
}
void runOnOperation() final;
};
void SLIRToLLVMDialect::runOnOperation() {
// The first thing to define is the conversion target. This will define the
// final target for this lowering. For this lowering, we are only targeting
// the LLVM dialect.
mlir::LLVMConversionTarget target(getContext());
target.addLegalOp<mlir::ModuleOp>();
// During this lowering, we will also be lowering the MemRef types, that are
// currently being operated on, to a representation in LLVM. To perform this
// conversion we use a TypeConverter as part of the lowering. This converter
// details how one type maps to another. This is necessary now that we will be
// doing more complicated lowerings, involving loop region arguments.
mlir::LLVMTypeConverter typeConverter(&getContext());
// Now that the conversion target has been defined, we need to provide the
// patterns used for lowering. At this point of the compilation process, we
// have a combination of `serene`, `affine`, and `std` operations. Luckily,
// there are already exists a set of patterns to transform `affine` and `std`
// dialects. These patterns lowering in multiple stages, relying on transitive
// lowerings. Transitive lowering, or A->B->C lowering, is when multiple
// patterns must be applied to fully transform an illegal operation into a
// set of legal ones.
mlir::RewritePatternSet patterns(&getContext());
mlir::populateFuncToLLVMConversionPatterns(typeConverter, patterns);
mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
patterns);
// patterns.add<PrintOpLowering>(&getContext());
// We want to completely lower to LLVM, so we use a `FullConversion`. This
// ensures that only legal operations will remain after the conversion.
auto module = getOperation();
if (failed(applyFullConversion(module, target, std::move(patterns)))) {
signalPassFailure();
}
};
std::unique_ptr<mlir::Pass> createSLIRLowerToLLVMDialectPass() {
return std::make_unique<SLIRToLLVMDialect>();
};
} // namespace serene::passes

View File

@ -1,438 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/reader/reader.h"
#include "serene/errors.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/list.h"
#include "serene/exprs/number.h"
#include "serene/exprs/symbol.h"
#include "serene/namespace.h"
#include "serene/utils.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/Error.h>
#include <llvm/Support/ErrorHandling.h>
#include <llvm/Support/ErrorOr.h>
#include <llvm/Support/FormatVariadic.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/SMLoc.h>
#include <mlir/IR/Diagnostics.h>
#include <mlir/IR/Location.h>
#include <mlir/Support/LogicalResult.h>
#include <assert.h>
#include <cctype>
#include <fstream>
#include <memory>
#include <string>
namespace serene {
namespace reader {
// LocationRange::LocationRange(const LocationRange &loc) {
// start = loc.start.clone();
// end = loc.end.clone();
// }
/// Return the string represenation of the location.
std::string Location::toString() const {
return llvm::formatv("{0}:{1}", line, col);
};
Location Location::clone() const {
return Location{ns, filename, c, line, col, knownLocation};
}
mlir::Location Location::toMLIRLocation(SereneContext &ctx) {
// TODO: Create a new Location attribute that is namespace base
if (filename.hasValue()) {
return mlir::FileLineColLoc::get(&ctx.mlirContext, filename.getValue(),
line, col);
}
return mlir::FileLineColLoc::get(&ctx.mlirContext, ns, line, col);
}
/// Increase the given location by one and set the line/col value in respect to
/// the `newline` in place.
/// \param loc The `Location` data
/// \param c A pointer to the current char that the location has to point to
void incLocation(Location &loc, const char *c) {
// TODO: Handle the end of line with respect to the OS.
// increase the current position in the buffer with respect to the end
// of line.
auto newline = *c == '\n';
if (!newline) {
loc.col++;
} else {
loc.line++;
loc.col = 0;
}
}
/// decrease the given location by one and set the line/col value in respect to
/// the `newline` in place.
/// \param loc The `Location` data
/// \param c A pointer to the current char that the location has to point to
void decLocation(Location &loc, const char *c) {
// TODO: Handle the end of line with respect to the OS.
// increase the current position in the buffer with respect to the end
// of line.
auto newline = *c == '\n';
if (newline) {
loc.line = loc.line == 0 ? 0 : loc.line - 1;
// We don't move back the `col` value because we simply don't know it
} else {
loc.col = loc.col == 0 ? 0 : loc.col - 1;
}
}
Reader::Reader(SereneContext &ctx, llvm::StringRef buffer, llvm::StringRef ns,
std::optional<llvm::StringRef> filename)
: ctx(ctx), ns(ns), filename(filename), buf(buffer),
currentLocation(Location(ns, filename)) {
UNUSED(this->ctx);
READER_LOG("Setting the first char of the buffer");
currentChar = buf.begin() - 1;
currentPos = 1;
currentLocation.line = 1;
currentLocation.col = 1;
};
Reader::Reader(SereneContext &ctx, llvm::MemoryBufferRef buffer,
llvm::StringRef ns, std::optional<llvm::StringRef> filename)
: Reader(ctx, buffer.getBuffer(), ns, filename){};
Reader::~Reader() { READER_LOG("Destroying the reader"); }
void Reader::advanceByOne() {
currentChar++;
currentPos++;
currentLocation.col++;
if (*currentChar == '\n') {
READER_LOG("Detected end of line");
if (readEOL) {
currentLocation.col = 1;
currentLocation.line++;
}
readEOL = true;
} else {
if (readEOL) {
currentLocation.line++;
currentLocation.col = 1;
}
readEOL = false;
}
READER_LOG("Moving to Char: " << *currentChar << " at location: "
<< currentLocation.toString());
};
void Reader::advance(bool skipWhitespace) {
if (skipWhitespace) {
for (;;) {
const auto *next = currentChar + 1;
if (isspace(*next) == 0) {
return;
}
advanceByOne();
}
} else {
advanceByOne();
}
};
const char *Reader::nextChar(bool skipWhitespace, unsigned count) {
if (!skipWhitespace) {
READER_LOG("Next char: " << *(currentChar + count));
return currentChar + count;
}
const auto *c = currentChar + 1;
while (isspace(*c) != 0) {
c++;
};
READER_LOG("Next char: " << *c);
return c;
};
bool Reader::isEndOfBuffer(const char *c) {
return *c == '\0' || currentPos > buf.size() || ((const int)*c == EOF);
};
Location Reader::getCurrentLocation() { return currentLocation.clone(); };
/// A predicate function indicating whether the given char `c` is a valid
/// char for the starting point of a symbol or not.
bool Reader::isValidForIdentifier(char c) {
switch (c) {
case '!':
case '$':
case '%':
case '&':
case '*':
case '+':
case '-':
case '.':
case '~':
case '/':
case ':':
case '<':
case '=':
case '>':
case '?':
case '@':
case '^':
case '_':
return true;
}
return std::isalnum(c) != 0;
}
/// Reads a number,
/// \param neg whether to read a negative number or not.
exprs::MaybeNode Reader::readNumber(bool neg) {
READER_LOG("Reading a number...");
std::string number(neg ? "-" : "");
bool floatNum = false;
bool empty = false;
const auto *c = nextChar();
advance();
LocationRange loc(getCurrentLocation());
if (isdigit(*c) == 0) {
return errors::makeError(ctx, errors::InvalidDigitForNumber, loc);
}
for (;;) {
number += *c;
c = nextChar(false);
empty = false;
if ((isdigit(*c) != 0) || *c == '.') {
if (*c == '.' && floatNum) {
loc = LocationRange(getCurrentLocation());
return errors::makeError(ctx, errors::TwoFloatPoints, loc);
}
if (*c == '.') {
floatNum = true;
}
advance();
continue;
}
break;
}
if (((std::isalpha(*c) != 0) && !empty) || empty) {
advance();
loc.start = getCurrentLocation();
return errors::makeError(ctx, errors::InvalidDigitForNumber, loc);
}
loc.end = getCurrentLocation();
return exprs::make<exprs::Number>(loc, number, neg, floatNum);
};
/// Reads a symbol. If the symbol looks like a number
/// If reads it as number
exprs::MaybeNode Reader::readSymbol() {
READER_LOG("Reading a symbol...");
LocationRange loc;
const auto *c = nextChar();
if (!this->isValidForIdentifier(*c) || isEndOfBuffer(c) ||
(isspace(*c) != 0)) {
advance();
loc = LocationRange(getCurrentLocation());
std::string msg;
if (*c == ')') {
msg = "An extra ')' is detected.";
}
return errors::makeError(ctx, errors::InvalidCharacterForSymbol, loc, msg);
}
if (*c == '-') {
const auto *next = nextChar(false, 2);
if (isdigit(*next) != 0) {
// Swallow the -
advance();
return readNumber(true);
}
}
if (isdigit(*c) != 0) {
return readNumber(false);
}
std::string sym;
advance();
for (;;) {
sym += *c;
c = nextChar();
if (!isEndOfBuffer(c) &&
((((isspace(*c)) == 0) && this->isValidForIdentifier(*c)))) {
advance();
continue;
}
break;
}
// TODO: Make sure that the symbol has 0 or 1 '/'.
// TODO: Make sure that `/` is not at the start or at the end of the symbol
loc.end = getCurrentLocation();
return exprs::makeSuccessfulNode<exprs::Symbol>(loc, sym, this->ns);
};
/// Reads a list recursively
exprs::MaybeNode Reader::readList() {
READER_LOG("Reading a list...");
const auto *c = nextChar();
advance();
auto list = exprs::makeAndCast<exprs::List>(getCurrentLocation());
// TODO: Replace the assert with an actual check.
assert(*c == '(');
bool list_terminated = false;
do {
const auto *c = nextChar(true);
if (isEndOfBuffer(c)) {
advance(true);
advance();
list->location.end = getCurrentLocation();
return errors::makeError(ctx, errors::EOFWhileScaningAList,
list->location);
}
switch (*c) {
case ')':
advance(true);
advance();
list_terminated = true;
list->location.end = getCurrentLocation();
break;
default:
advance(true);
auto expr = readExpr();
if (!expr) {
return expr;
}
list->append(*expr);
}
} while (!list_terminated);
return list;
};
/// Reads an expression by dispatching to the proper reader function.
exprs::MaybeNode Reader::readExpr() {
const auto *c = nextChar(true);
READER_LOG("Read char at `readExpr`: " << *c);
if (isEndOfBuffer(c)) {
return exprs::EmptyNode;
}
switch (*c) {
case '(': {
advance(true);
return readList();
}
default:
advance(true);
return readSymbol();
}
};
/// Reads all the expressions in the reader's buffer as an AST.
/// Each expression type (from the reader perspective) has a
/// reader function.
exprs::MaybeAst Reader::read() {
for (size_t current_pos = 0; current_pos < buf.size();) {
const auto *c = nextChar(true);
if (isEndOfBuffer(c)) {
break;
}
advance(true);
auto tmp = readExpr();
if (tmp) {
if (*tmp == nullptr) {
break;
}
this->ast.push_back(std::move(*tmp));
} else {
return tmp.takeError();
}
}
return std::move(this->ast);
};
exprs::MaybeAst read(SereneContext &ctx, const llvm::StringRef input,
llvm::StringRef ns,
std::optional<llvm::StringRef> filename) {
reader::Reader r(ctx, input, ns, filename);
auto ast = r.read();
return ast;
}
exprs::MaybeAst read(SereneContext &ctx, const llvm::MemoryBufferRef input,
llvm::StringRef ns,
std::optional<llvm::StringRef> filename) {
reader::Reader r(ctx, input, ns, filename);
auto ast = r.read();
return ast;
}
} // namespace reader
} // namespace serene

View File

@ -1,71 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/semantics.h"
#include "serene/context.h"
#include "serene/exprs/expression.h"
#include "serene/namespace.h"
#include <llvm/Support/Error.h>
namespace serene::semantics {
std::unique_ptr<AnalysisState> AnalysisState::moveToNewEnv() {
auto &newEnv = ns.createEnv(&env);
return makeAnalysisState(ns, newEnv);
};
AnalyzeResult analyze(AnalysisState &state, exprs::Ast &forms) {
llvm::Error errors = llvm::Error::success();
exprs::Ast ast;
for (auto &element : forms) {
auto maybeNode = element->analyze(state);
// Is it a `success` result
if (maybeNode) {
auto &node = *maybeNode;
if (node) {
// is there a new node to replace the current node ?
ast.push_back(node);
} else {
// Analyze returned a `nullptr`. No rewrite is needed.
// Use the current element instead.
ast.push_back(element);
}
} else {
// `analyze` returned an errorful result. This type of error
// is llvm related and has to be raised later
// (std::move());
auto err = maybeNode.takeError();
errors = llvm::joinErrors(std::move(errors), std::move(err));
}
}
// If the errors (which is an ErrorList) contains error and is
// not succssful
if (!errors) {
return std::move(ast);
}
return std::move(errors);
};
}; // namespace serene::semantics

View File

@ -1,157 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/serene.h"
#include "serene/diagnostics.h"
#include "serene/exprs/expression.h"
// TODO: Remove it
#include "serene/exprs/number.h"
#include "serene/jit/halley.h"
#include "serene/reader/reader.h"
#include "serene/utils.h"
#include <llvm/ADT/None.h>
#include <llvm/Support/Casting.h>
#include <llvm/Support/CommandLine.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();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllAsmPrinters();
};
// CLI Option ----------------
/// All the global CLI option ar defined here. If you need to add a new global
/// option
/// make sure that you are handling it in `applySereneCLOptions` too.
struct SereneOptions {
llvm::cl::OptionCategory clOptionsCategory{"Discovery options"};
llvm::cl::list<std::string> loadPaths{
"l", llvm::cl::desc("The load path to use for compilation."),
llvm::cl::ZeroOrMore, llvm::cl::MiscFlags::PositionalEatsArgs,
llvm::cl::cat(clOptionsCategory)};
llvm::cl::list<std::string> sharedLibraryPaths{
"sl", llvm::cl::desc("Where to find shared libraries"),
llvm::cl::ZeroOrMore, llvm::cl::MiscFlags::PositionalEatsArgs,
llvm::cl::cat(clOptionsCategory)};
};
static llvm::ManagedStatic<SereneOptions> options;
void registerSereneCLOptions() {
// Make sure that the options struct has been constructed.
*options;
#ifdef SERENE_WITH_MLIR_CL_OPTION
// mlir::registerAsmPrinterCLOptions();
mlir::registerMLIRContextCLOptions();
mlir::registerPassManagerCLOptions();
#endif
}
void applySereneCLOptions(SereneContext &ctx) {
if (!options.isConstructed()) {
return;
}
ctx.sourceManager.setLoadPaths(options->loadPaths);
#ifdef SERENE_WITH_MLIR_CL_OPTION
mlir::applyPassManagerCLOptions(ctx.pm);
#endif
}
SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx, std::string &input) {
auto &currentNS = ctx.getCurrentNS();
auto filename =
!currentNS.filename.hasValue()
? llvm::None
: std::optional<llvm::StringRef>(currentNS.filename.getValue());
return reader::read(ctx, input, currentNS.name, filename);
};
SERENE_EXPORT exprs::MaybeNode eval(SereneContext &ctx, exprs::Ast &input) {
auto loc = reader::LocationRange::UnknownLocation("nsname");
// auto ns = ctx.importNamespace("docs.examples.hello_world", loc);
// if (!ns) {
// auto es = ns.getError();
// auto nsloadErr = errors::makeError(loc, errors::NSLoadError);
// es.push_back(nsloadErr);
// return exprs::MaybeNode::error(es);
// }
auto errs = ctx.jit->addAST(input);
if (errs) {
return errs;
}
// auto e = input[0];
// auto *sym = llvm::dyn_cast<exprs::Symbol>(e.get());
// if (sym == nullptr) {
// return exprs::makeErrorNode(e->location, errors::UnknownError, "only
// sym");
// }
// llvm::outs() << "Read: " << sym->toString() << "\n";
// // Get the anonymous expression's JITSymbol.
// auto symptr = ctx.jit->lookup(*sym);
// if (!symptr) {
// return exprs::MaybeNode::error(symptr.getError());
// }
llvm::outs() << "eval here\n";
// sym((void **)3);
// err = ctx.jit->addAst(input);
// if (err) {
// llvm::errs() << err;
// auto e = errors::makeErrorTree(loc, errors::NSLoadError);
// return exprs::makeErrorNode(loc, errors::NSLoadError);
// }
return exprs::make<exprs::Number>(loc, "4", false, false);
};
SERENE_EXPORT void print(SereneContext &ctx, const exprs::Ast &input,
std::string &result) {
UNUSED(ctx);
result = exprs::astToString(&input);
};
} // namespace serene

View File

@ -1,48 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/slir/dialect.h"
#include "serene/slir/dialect.cpp.inc"
#include "serene/slir/ops.h"
#include "serene/slir/types.h"
#include <mlir/IR/Builders.h>
#include <mlir/IR/Dialect.h>
#include <mlir/IR/DialectImplementation.h>
#include <mlir/IR/MLIRContext.h>
namespace serene {
namespace slir {
/// Dialect initialization, the instance will be owned by the context. This is
/// the point of registration of types and operations for the dialect.
void SereneDialect::initialize() {
registerType();
addOperations<
#define GET_OP_LIST
#include "serene/slir/ops.cpp.inc"
>();
}
void registerTo(mlir::DialectRegistry &registry) {
registry.insert<serene::slir::SereneDialect>();
};
} // namespace slir
} // namespace serene

View File

@ -1,172 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/slir/generatable.h"
#include "serene/slir/dialect.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 {
// mlir::Operation *Generatable::generate(exprs::Expression *x) {
// switch (x->getType()) {
// case SereneType::Number: {
// return generate(llvm::cast<Number>(x));
// }
// case SereneType::List: {
// generate(llvm::cast<List>(x));
// return nullptr;
// }
// default: {
// return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)3);
// }
// }
//};
// mlir::Value Generator::generate(exprs::List *l) {
// auto first = l->at(0);
// if (!first) {
// // Empty list.
// // TODO: Return Nil or empty list.
// // Just for now.
// return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)0);
// }
// if (first->get()->getType() == SereneType::Symbol) {
// auto fnNameSymbol = llvm::dyn_cast<Symbol>(first->get());
// if (fnNameSymbol->getName() == "fn") {
// if (l->count() <= 3) {
// module.emitError("'fn' form needs exactly 2 arguments.");
// }
// auto args = llvm::dyn_cast<List>(l->at(1).getValue().get());
// auto body = llvm::dyn_cast<List>(l->from(2).get());
// if (!args) {
// module.emitError("The first element of 'def' has to be a symbol.");
// }
// // Create a new anonymous function and push it to the anonymous
// functions
// // map, later on we
// auto loc(fnNameSymbol->location->start);
// auto anonymousName = fmt::format("__fn_{}__", anonymousFnCounter);
// anonymousFnCounter++;
// auto fn = generateFn(loc, anonymousName, args, body);
// mlir::Identifier fnid = builder.getIdentifier(anonymousName);
// anonymousFunctions.insert({fnid, fn});
// return builder.create<FnIdOp>(builder.getUnknownLoc(), fnid.str());
// }
// }
// // auto rest = l->from(1);
// // auto loc = toMLIRLocation(&first->get()->location->start);
// // for (auto x : *rest) {
// // generate(x.get());
// // }
// return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)100);
//};
// mlir::FuncOp Generator::generateFn(serene::reader::Location loc,
// std::string name, List *args, List *body)
// {
// auto location = toMLIRLocation(&loc);
// llvm::SmallVector<mlir::Type, 4> arg_types(args->count(),
// builder.getI64Type());
// auto func_type = builder.getFunctionType(arg_types, builder.getI64Type());
// auto proto = mlir::FuncOp::create(location, name, func_type);
// mlir::FuncOp fn(proto);
// if (!fn) {
// module.emitError("Can not create the function.");
// }
// auto &entryBlock = *fn.addEntryBlock();
// llvm::ScopedHashTableScope<llvm::StringRef, mlir::Value>
// scope(symbolTable);
// // Declare all the function arguments in the symbol table.
// for (const auto arg :
// llvm::zip(args->asArrayRef(), entryBlock.getArguments())) {
// auto argSymbol = llvm::dyn_cast<Symbol>(std::get<0>(arg).get());
// if (!argSymbol) {
// module.emitError("Function parameters must be symbols");
// }
// if (symbolTable.count(argSymbol->getName())) {
// return nullptr;
// }
// symbolTable.insert(argSymbol->getName(), std::get<1>(arg));
// }
// // Set the insertion point in the builder to the beginning of the function
// // body, it will be used throughout the codegen to create operations in
// this
// // function.
// builder.setInsertionPointToStart(&entryBlock);
// // Emit the body of the function.
// if (!generate(body)) {
// fn.erase();
// return nullptr;
// }
// // // Implicitly return void if no return statement was emitted.
// // // FIXME: we may fix the parser instead to always return the last
// // expression
// // // (this would possibly help the REPL case later)
// // func::ReturnOp returnOp;
// // if (!entryBlock.empty())
// // returnOp = dyn_cast<func::ReturnOp>(entryBlock.back());
// // if (!returnOp) {
// // builder.create<func::ReturnOp>(loc(funcAST.getProto()->loc()));
// // } else if (returnOp.hasOperand()) {
// // // Otherwise, if this return operation has an operand then add a
// result
// // to
// // // the function.
// // function.setType(builder.getFunctionType(function.getType().getInputs(),
// // getType(VarType{})));
// // }
// return fn;
// }
} // namespace slir
} // namespace serene

View File

@ -1,57 +0,0 @@
/*
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/slir/ops.h"
#include "serene/slir/dialect.h"
#include "serene/slir/types.h"
#include "serene/utils.h"
#include <llvm/Support/FormatVariadic.h>
#include <mlir/IR/Attributes.h>
#include <mlir/IR/Builders.h>
#include <mlir/IR/BuiltinAttributes.h>
#include <mlir/IR/OperationSupport.h>
#define GET_OP_CLASSES
#include "serene/slir/ops.cpp.inc"
namespace serene::slir {
mlir::DataLayoutSpecInterface NsOp::getDataLayoutSpec() {
// Take the first and only (if present) attribute that implements the
// interface. This needs a linear search, but is called only once per data
// layout object construction that is used for repeated queries.
for (mlir::NamedAttribute attr : getOperation()->getAttrs()) {
if (auto spec = attr.getValue().dyn_cast<mlir::DataLayoutSpecInterface>()) {
return spec;
}
}
return {};
}
mlir::OpFoldResult SymbolOp::fold(llvm::ArrayRef<mlir::Attribute> operands) {
UNUSED(operands);
return value();
};
mlir::OpFoldResult ValueOp::fold(llvm::ArrayRef<mlir::Attribute> operands) {
UNUSED(operands);
return value();
};
} // namespace serene::slir

View File

@ -1,65 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/slir/slir.h"
namespace serene {
namespace slir {
std::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(*module.getContext());
// Convert the module to LLVM IR in a new LLVM IR context.
auto llvmModule = mlir::translateModuleToLLVMIR(module, *llvmContext);
if (!llvmModule) {
// TODO: Return a Result type instead
module.emitError("Failed to emit LLVM IR\n");
return llvm::None;
}
// 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())) {
// TODO: Return a proper error
module.emitError("Failed to optimize LLVM IR\n");
return llvm::None;
}
auto tsm = llvm::orc::ThreadSafeModule(std::move(llvmModule),
std::move(llvmContext));
return tsm;
};
} // namespace slir
} // namespace serene

View File

@ -1,86 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/slir/type_converter.h"
#include "serene/slir/dialect.h"
#include <llvm/Support/Casting.h>
#include <mlir/Dialect/LLVMIR/LLVMTypes.h>
namespace ll = mlir::LLVM;
namespace serene {
namespace slir {
mlir::Type getStringTypeinLLVM(mlir::MLIRContext &ctx) {
auto stringStruct =
ll::LLVMStructType::getIdentified(&ctx, "serene.core.string");
mlir::SmallVector<mlir::Type, 4> subtypes;
subtypes.push_back(
ll::LLVMPointerType::get(mlir::IntegerType::get(&ctx, I8_SIZE)));
// Length field
subtypes.push_back(mlir::IntegerType::get(&ctx, I32_SIZE));
(void)stringStruct.setBody(subtypes, false);
return stringStruct;
};
mlir::Type getSymbolTypeinLLVM(mlir::MLIRContext &ctx) {
auto symbolStruct =
ll::LLVMStructType::getIdentified(&ctx, "serene.core.symbol");
auto strType = getStringTypeinLLVM(ctx);
auto strPtr = ll::LLVMPointerType::get(strType);
llvm::SmallVector<mlir::Type, 2> strings{strPtr, strPtr};
// We discard the result becasue if the body was already set it means
// that we're ok and the struct already exists
(void)symbolStruct.setBody(strings, false);
return symbolStruct;
};
mlir::Type getPtrTypeinLLVM(mlir::MLIRContext &ctx, PtrType p) {
UNUSED(ctx);
auto T = p.getPointeeType();
auto llvmPtr = ll::LLVMPointerType::get(T);
return llvmPtr;
}
TypeConverter::ConverterFn TypeConverter::convertSereneTypes() {
return [&](mlir::Type type) -> MaybeType {
if (type.isa<StringType>()) {
return getStringTypeinLLVM(ctx);
}
if (type.isa<SymbolType>()) {
return getSymbolTypeinLLVM(ctx);
}
if (type.isa<PtrType>()) {
return getPtrTypeinLLVM(ctx, type.cast<PtrType>());
}
return llvm::None;
};
}
} // namespace slir
} // namespace serene

View File

@ -1,52 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/slir/types.h"
#include "serene/slir/dialect.h"
#define GET_ATTRDEF_CLASSES
#include "serene/slir/attrs.cpp.inc"
#define GET_TYPEDEF_CLASSES
#include "serene/slir/types.cpp.inc"
namespace serene::slir {
PtrType PtrType::get(mlir::MLIRContext *context, unsigned addressSpace) {
return Base::get(context, mlir::Type(), addressSpace);
}
PtrType PtrType::get(mlir::Type pointee, unsigned addressSpace) {
return Base::get(pointee.getContext(), pointee, addressSpace);
}
bool PtrType::isOpaque() const { return !getImpl()->pointeeType; }
void SereneDialect::registerType() {
addAttributes<
#define GET_ATTRDEF_LIST
#include "serene/slir/attrs.cpp.inc"
>();
addTypes<
#define GET_TYPEDEF_LIST
#include "serene/slir/types.cpp.inc"
>();
};
} // namespace serene::slir

View File

@ -1,36 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/context.h"
#include "serene/namespace.h"
#include "serene/reader/location.h"
#include <mlir/IR/BuiltinOps.h>
namespace serene::slir {
::mlir::Location toMLIRLocation(serene::Namespace &ns,
serene::reader::Location &loc) {
mlir::OpBuilder builder(&ns.getContext().mlirContext);
auto file = ns.filename;
std::string filename{file.getValueOr("REPL")};
return mlir::FileLineColLoc::get(builder.getStringAttr(filename), loc.line,
loc.col);
}
} // namespace serene::slir

View File

@ -1,28 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/slir/dialect.h"
#include "serene/slir/slir.h"
#include <llvm/Support/Casting.h>
#include <mlir/IR/Builders.h>
#include <mlir/IR/MLIRContext.h>
#include <mlir/IR/Value.h>
namespace serene {
namespace slir {} // namespace slir
} // namespace serene

View File

@ -1,225 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/source_mgr.h"
#include "serene/namespace.h"
#include "serene/reader/location.h"
#include "serene/reader/reader.h"
#include "serene/utils.h"
#include <system_error>
#include <llvm/Support/Error.h>
#include <llvm/Support/FormatVariadic.h>
#include <llvm/Support/Locale.h>
#include <llvm/Support/MemoryBufferRef.h>
#include <llvm/Support/Path.h>
#include <llvm/Support/raw_ostream.h>
#include <mlir/Support/LogicalResult.h>
namespace serene {
std::string SourceMgr::convertNamespaceToPath(std::string ns_name) {
std::replace(ns_name.begin(), ns_name.end(), '.', '/');
llvm::SmallString<MAX_PATH_SLOTS> path;
path.append(ns_name);
llvm::sys::path::native(path);
return std::string(path);
};
bool SourceMgr::isValidBufferID(unsigned i) const {
return i != 0 && i <= buffers.size();
};
SourceMgr::MemBufPtr SourceMgr::findFileInLoadPath(const std::string &name,
std::string &importedFile) {
auto path = convertNamespaceToPath(name);
// If the file didn't exist directly, see if it's in an include path.
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);
auto newBufOrErr = llvm::MemoryBuffer::getFile(importedFile);
if (auto err = newBufOrErr.getError()) {
llvm::consumeError(llvm::errorCodeToError(err));
continue;
}
return std::move(*newBufOrErr);
}
return nullptr;
};
MaybeNS SourceMgr::readNamespace(SereneContext &ctx, std::string name,
reader::LocationRange importLoc) {
std::string importedFile;
SMGR_LOG("Attempt to load namespace: " + name);
MemBufPtr newBufOrErr(findFileInLoadPath(name, importedFile));
if (newBufOrErr == nullptr) {
auto msg = llvm::formatv("Couldn't find namespace '{0}'", name).str();
return errors::makeError(ctx, errors::NSLoadError, importLoc, msg);
}
auto bufferId = AddNewSourceBuffer(std::move(newBufOrErr), importLoc);
UNUSED(nsTable.insert_or_assign(name, bufferId));
if (bufferId == 0) {
auto msg = llvm::formatv("Couldn't add namespace '{0}'", name).str();
return errors::makeError(ctx, errors::NSAddToSMError, importLoc, msg);
}
// Since we moved the buffer to be added as the source storage we
// need to get a pointer to it again
const auto *buf = getMemoryBuffer(bufferId);
// Read the content of the buffer by passing it the reader
auto maybeAst = reader::read(ctx, buf->getBuffer(), name,
std::optional(llvm::StringRef(importedFile)));
if (!maybeAst) {
SMGR_LOG("Couldn't Read namespace: " + name);
return maybeAst.takeError();
}
// Create the NS and set the AST
auto ns =
ctx.makeNamespace(name, std::optional(llvm::StringRef(importedFile)));
if (auto errs = ns->addTree(*maybeAst)) {
SMGR_LOG("Couldn't set the AST for namespace: " + name);
return errs;
}
return ns;
};
unsigned SourceMgr::AddNewSourceBuffer(std::unique_ptr<llvm::MemoryBuffer> f,
reader::LocationRange includeLoc) {
SrcBuffer nb;
nb.buffer = std::move(f);
nb.importLoc = includeLoc;
buffers.push_back(std::move(nb));
return buffers.size();
};
template <typename T>
static std::vector<T> &GetOrCreateOffsetCache(void *&offsetCache,
llvm::MemoryBuffer *buffer) {
if (offsetCache) {
return *static_cast<std::vector<T> *>(offsetCache);
}
// Lazily fill in the offset cache.
auto *offsets = new std::vector<T>();
size_t sz = buffer->getBufferSize();
// TODO: Replace this assert with a realtime check
assert(sz <= std::numeric_limits<T>::max());
llvm::StringRef s = buffer->getBuffer();
for (size_t n = 0; n < sz; ++n) {
if (s[n] == '\n') {
offsets->push_back(static_cast<T>(n));
}
}
offsetCache = offsets;
return *offsets;
}
template <typename T>
const char *SourceMgr::SrcBuffer::getPointerForLineNumberSpecialized(
unsigned lineNo) const {
std::vector<T> &offsets =
GetOrCreateOffsetCache<T>(offsetCache, buffer.get());
// We start counting line and column numbers from 1.
if (lineNo != 0) {
--lineNo;
}
const char *bufStart = buffer->getBufferStart();
// The offset cache contains the location of the \n for the specified line,
// we want the start of the line. As such, we look for the previous entry.
if (lineNo == 0) {
return bufStart;
}
if (lineNo > offsets.size()) {
return nullptr;
}
return bufStart + offsets[lineNo - 1] + 1;
}
/// Return a pointer to the first character of the specified line number or
/// null if the line number is invalid.
const char *
SourceMgr::SrcBuffer::getPointerForLineNumber(unsigned lineNo) const {
size_t sz = buffer->getBufferSize();
if (sz <= std::numeric_limits<uint8_t>::max()) {
return getPointerForLineNumberSpecialized<uint8_t>(lineNo);
}
if (sz <= std::numeric_limits<uint16_t>::max()) {
return getPointerForLineNumberSpecialized<uint16_t>(lineNo);
}
if (sz <= std::numeric_limits<uint32_t>::max()) {
return getPointerForLineNumberSpecialized<uint32_t>(lineNo);
}
return getPointerForLineNumberSpecialized<uint64_t>(lineNo);
}
SourceMgr::SrcBuffer::SrcBuffer(SourceMgr::SrcBuffer &&other) noexcept
: buffer(std::move(other.buffer)), offsetCache(other.offsetCache),
importLoc(other.importLoc) {
other.offsetCache = nullptr;
}
SourceMgr::SrcBuffer::~SrcBuffer() {
if (offsetCache != nullptr) {
size_t sz = buffer->getBufferSize();
if (sz <= std::numeric_limits<uint8_t>::max()) {
delete static_cast<std::vector<uint8_t> *>(offsetCache);
} else if (sz <= std::numeric_limits<uint16_t>::max()) {
delete static_cast<std::vector<uint16_t> *>(offsetCache);
} else if (sz <= std::numeric_limits<uint32_t>::max()) {
delete static_cast<std::vector<uint32_t> *>(offsetCache);
} else {
delete static_cast<std::vector<uint64_t> *>(offsetCache);
}
offsetCache = nullptr;
}
}
}; // namespace serene

View File

@ -1,29 +0,0 @@
# Catch2 should be installed system wide
#find_package(Catch2 3 REQUIRED)
# Tests need to be added as executables first
add_executable(libsereneTests serenetests.cpp)
add_dependencies(libsereneTests SereneDialectGen)
add_dependencies(libsereneTests serene)
target_link_libraries(libsereneTests PRIVATE
serene
${llvm_libs}
MLIRAnalysis
MLIRIR
MLIRParser
MLIRSideEffectInterfaces
MLIRTransforms
Catch2::Catch2WithMain
)
target_compile_features(libsereneTests PRIVATE cxx_std_17)
# target_include_directories(serene SYSTEM PRIVATE $ENV{INCLUDE})
# target_include_directories(serene PUBLIC ${INCLUDE_DIR})
include(CTest)
include(Catch)
catch_discover_tests(libsereneTests)

View File

@ -1,161 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_TEST_CONTEXT_H
#define SERENE_TEST_CONTEXT_H
#include "serene/context.h"
#include "serene/namespace.h"
#include "serene/reader/location.h"
#include "serene/serene.h"
#include "./test_helpers.cpp.inc"
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <llvm/ADT/None.h>
namespace serene {
TEST_CASE("Compiler options", "[context]") {
auto opts = Options();
opts.JITLazy = !opts.JITLazy;
opts.JITenableGDBNotificationListener =
!opts.JITenableGDBNotificationListener;
auto ctx = makeSereneContext(opts);
CHECK(ctx->opts.JITLazy == opts.JITLazy);
CHECK(ctx->opts.JITenableGDBNotificationListener ==
opts.JITenableGDBNotificationListener);
};
TEST_CASE("makeNamespace & getNS", "[context]") {
auto ctx = makeSereneContext();
auto *ns = ctx->getNS("blah");
REQUIRE_FALSE(ns);
auto userNs =
ctx->makeNamespace("user", llvm::Optional<llvm::StringRef>("/some/file"));
CHECK(userNs->name == "user");
REQUIRE(userNs->filename);
CHECK(userNs->filename.getValue() == "/some/file");
ns = ctx->getNS("user");
REQUIRE(ns);
CHECK(ns->name == userNs->name);
/// Creating new ns with the same name overrides the old one
auto userNs1 = ctx->makeNamespace(
"user", llvm::Optional<llvm::StringRef>("/some/other/file"));
ns = ctx->getNS("user");
REQUIRE(ns);
CHECK(ns->name == userNs1->name);
REQUIRE(ns->filename);
CHECK(ns->filename.getValue() == "/some/other/file");
};
TEST_CASE(
"withCurrentNS run a function with the given namespace as the current NS",
"[context]") {
auto ctx = makeSereneContext();
auto userNs =
ctx->makeNamespace("user", llvm::Optional<llvm::StringRef>("/some/file"));
ctx->withCurrentNS<int>("user", [&]() -> int {
CHECK(ctx->getCurrentNS().name == userNs->name);
return 0;
});
// Checking the `void` type specialization
ctx->withCurrentNS<void>("user", [&]() -> void {
CHECK(ctx->getCurrentNS().name == userNs->name);
});
CHECK(ctx->getCurrentNS().name == DEFAULT_NS_NAME);
};
TEST_CASE("getSharedPtrToNS returns a shared ptr to the NS", "[context]") {
auto ctx = makeSereneContext();
auto userNs =
ctx->makeNamespace("user", llvm::Optional<llvm::StringRef>("/some/file"));
auto userNs1 = ctx->makeNamespace(
"user1", llvm::Optional<llvm::StringRef>("/some/file1"));
CHECK(ctx->getCurrentNS().name == DEFAULT_NS_NAME);
NSPtr shouldBeUser1 = ctx->getSharedPtrToNS("user");
REQUIRE(shouldBeUser1);
CHECK(shouldBeUser1->name == userNs->name);
};
TEST_CASE("Compilation phase", "[context]") {
auto ctx = makeSereneContext();
auto cp = CompilationPhase::O3;
ctx->setOperationPhase(cp);
CHECK(ctx->getTargetPhase() == cp);
CHECK(ctx->getOptimizatioLevel() == 3);
// Anything below 0 is 0
cp = CompilationPhase::MLIR;
ctx->setOperationPhase(cp);
CHECK(ctx->getOptimizatioLevel() == 0);
};
TEST_CASE("makeNamespace", "[context]") {
auto ctx = makeSereneContext();
auto ns = ctx->makeNamespace("example.ns", llvm::None);
// Namespace has to be empty
CHECK(ns->name == "example.ns");
CHECK(ns->getTree().empty());
CHECK(ns->filename == llvm::None);
};
TEST_CASE("context and jit", "[context]") {
auto ctx = makeSereneContext();
auto ns = ctx->makeNamespace("example.ns", llvm::None);
REQUIRE(ctx->jit);
// no JITDylib should be defined for an empty NS
CHECK(ctx->getNumberOfJITDylibs(*ns) == 0);
SECTION("JITDylib management") {
auto unknown = reader::LocationRange::UnknownLocation(DEFAULT_NS_NAME);
REQUIRE(!ctx->jit->addNS(*ns, unknown));
CHECK(ctx->getNumberOfJITDylibs(*ns) == 1);
REQUIRE(ctx->getLatestJITDylib(*ns));
CHECK(ctx->getLatestJITDylib(*ns)->getName() == "example.ns#1");
};
};
} // namespace serene
#endif

View File

@ -1,95 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/environment.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/symbol.h"
#include "serene/reader/location.h"
#include "serene/serene.h"
#include "./test_helpers.cpp.inc"
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <llvm/Support/Casting.h>
namespace serene {
TEST_CASE("Environment tests", "[environment]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation());
exprs::Node sym = exprs::make<exprs::Symbol>(*range, "example", "ns");
Environment<exprs::Node> e;
llvm::Optional<exprs::Node> result = e.lookup("a");
REQUIRE_FALSE(result.hasValue());
auto status = e.insert_symbol("a", sym);
REQUIRE(status.succeeded());
result = e.lookup("a");
REQUIRE(result.hasValue());
CHECK(result.getValue() == sym);
auto *fetchedSym = llvm::dyn_cast<exprs::Symbol>(result.getValue().get());
REQUIRE(fetchedSym != nullptr);
CHECK(fetchedSym->name == "example");
CHECK(fetchedSym->nsName == "ns");
SECTION("Testing the environment copy") {
Environment<exprs::Node> e1(&e);
result = e1.lookup("b");
REQUIRE_FALSE(result.hasValue());
// It should lookup the value in the parent environment
result = e1.lookup("a");
REQUIRE(result.hasValue());
CHECK(result.getValue() == sym);
};
};
TEST_CASE("Test generic Environment usage", "[environment]") {
Environment<char> env;
auto result = env.lookup("blah");
REQUIRE_FALSE(result);
auto status = env.insert_symbol("blah", 'A');
REQUIRE(status.succeeded());
result = env.lookup("blah");
REQUIRE(result);
CHECK(*result == 'A');
result = env.lookup("blah");
REQUIRE(result);
CHECK(*result == 'A');
// Test the overwrite functionality
status = env.insert_symbol("blah", 'B');
REQUIRE(status.succeeded());
result = env.lookup("blah");
REQUIRE(result);
CHECK(*result == 'B');
}
} // namespace serene

View File

@ -1,66 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_TEST_ERRORS_H
#define SERENE_TEST_ERRORS_H
#include "serene/context.h"
#include "serene/errors.h"
#include "../test_helpers.cpp.inc"
#include <catch2/catch_test_macros.hpp>
#include <llvm/ADT/StringMap.h>
#include <llvm/Support/Error.h>
#include <llvm/Support/ErrorHandling.h>
namespace serene {
namespace errors {
TEST_CASE("Serene Error construction", "[errors]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation());
auto ctx = makeSereneContext();
llvm::Error err = makeError(*ctx, PassFailureError, *range, "test error");
auto unhandled =
llvm::handleErrors(std::move(err), [&](const SereneError &e) {
REQUIRE(e.message() == "test error");
const auto *v = getVariant(e.errorType);
REQUIRE(v != nullptr);
CHECK(v->title == "PassFailureError");
CHECK(v->desc == "Pass Failure.");
CHECK(v->help.empty());
});
CHECK(!unhandled);
}
TEST_CASE("getMessage function", "[errors]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation());
auto ctx = makeSereneContext();
llvm::Error err = makeError(*ctx, PassFailureError, *range, "test error");
CHECK(getMessage(err) == "test error");
CHECK_SERENE_ERR(PassFailureError, std::move(err));
}
}; // namespace errors
} // namespace serene
#endif

View File

@ -1,43 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/exprs/expression.h"
#include "serene/exprs/list.h"
#include "serene/exprs/symbol.h"
#include "../test_helpers.cpp.inc"
#include <llvm/ADT/ArrayRef.h>
namespace serene {
namespace exprs {
TEST_CASE("Public Expression API", "[expression]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation());
auto sym = make<Symbol>(*range, "example", "user");
REQUIRE(sym->getType() == ExprType::Symbol);
CHECK(sym->toString() == "<Symbol user/example>");
auto list = makeAndCast<List>(*range, sym);
CHECK(list->toString() == "<List <Symbol user/example>>");
};
} // namespace exprs
} // namespace serene

View File

@ -1,196 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/errors.h"
#include "serene/exprs/expression.h"
#include "serene/exprs/list.h"
#include "serene/exprs/symbol.h"
#include "serene/namespace.h"
#include "serene/reader/reader.h"
#include "serene/semantics.h"
#include "../test_helpers.cpp.inc"
#include <llvm/Support/Error.h>
namespace serene {
namespace exprs {
TEST_CASE("List Expression", "[expression]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation());
Node sym = make<Symbol>(*range, "example", "user");
Node sym1 = make<Symbol>(*range, "example1", "user");
Node list = make<List>(*range);
REQUIRE(list->getType() == ExprType::List);
CHECK(list->toString() == "<List ->");
Node list2 = make<List>(*range, list);
CHECK(list2->toString() == "<List <List ->>");
Ast elements;
elements.push_back(list);
elements.push_back(list2);
elements.push_back(sym);
auto list3 = make<List>(*range, elements);
CHECK(list3->toString() ==
"<List <List -> <List <List ->> <Symbol user/example>>");
auto l = llvm::dyn_cast<List>(list.get());
l->append(sym1);
REQUIRE(list->getType() == ExprType::List);
CHECK(list->toString() == "<List <Symbol user/example1>>");
l->append(sym);
REQUIRE(l->count() == 2);
auto expr = l->at(1);
REQUIRE(expr.hasValue());
CHECK(expr.getValue()->toString() == "<Symbol user/example>");
expr = l->at(2);
REQUIRE_FALSE(expr.hasValue());
for (auto x : *l) {
CHECK(x->getType() == ExprType::Symbol);
}
};
TEST_CASE("List semantic analysis of 'def'", "[semantic,expression,list]") {
auto ctx = makeSereneContext();
auto ns = ctx->makeNamespace("user", llvm::None);
auto ast = llvm::cantFail(READ("(def (a) b)"));
SemanticEnv env;
semantics::AnalysisState state(*ns, env);
auto afterAst = semantics::analyze(state, ast);
REQUIRE_FALSE(afterAst);
{
auto err = afterAst.takeError();
CHECK(err.isA<llvm::ErrorList>());
}
ast = llvm::cantFail(READ("(def a)"));
afterAst = semantics::analyze(state, ast);
REQUIRE_FALSE(afterAst);
CHECK(errors::getMessage(afterAst.takeError()) == "Expected 3 got 2");
ast = llvm::cantFail(READ("(def a b c)"));
afterAst = semantics::analyze(state, ast);
REQUIRE_FALSE(afterAst);
CHECK(errors::getMessage(afterAst.takeError()) == "Expected 3 got 4");
ast = llvm::cantFail(READ("(def a b)"));
afterAst = semantics::analyze(state, ast);
REQUIRE(afterAst);
CHECK(astToString(&(*afterAst)) == "<Def a -> <Symbol b>>");
ast = llvm::cantFail(READ("(def a (fn () a))"));
afterAst = semantics::analyze(state, ast);
REQUIRE(afterAst);
CHECK(astToString(&(*afterAst)) ==
"<Def a -> <Fn a <List -> to <Symbol a>>>");
}
// TEST_CASE("List semantic analysis for 'fn'", "[semantic]") {
// auto ctx = makeSereneContext();
// auto ns = ctx->makeNamespace("user", llvm::None);
// auto ast = READ("(fn)");
// auto afterAst = semantics::analyze(*ctx, *ast);
// REQUIRE_FALSE(afterAst);
// REQUIRE(afterAst.takeError().size() == 1);
// CHECK(afterAst.getError()[0]->toString() ==
// "<Error E3: The argument list is mandatory.>");
// ast = reader::read("(fn ())");
// afterAst = semantics::analyze(*ctx, ast.getValue());
// REQUIRE(afterAst);
// CHECK(astToString(&afterAst.getValue()) == "<Fn ___fn___0 <List -> to
// <>>");
// ast = reader::read("(fn (a b c) a a a)");
// afterAst = semantics::analyze(*ctx, ast.getValue());
// REQUIRE(afterAst);
// CHECK(astToString(&afterAst.getValue()) ==
// "<Fn ___fn___1 <List <Symbol a> <Symbol b> <Symbol c>> to <Symbol a>
// "
// "<Symbol a> <Symbol a>>");
// ast = reader::read("(fn () a b)");
// afterAst = semantics::analyze(*ctx, ast.getValue());
// REQUIRE(afterAst);
// CHECK(astToString(&afterAst.getValue()) ==
// "<Fn ___fn___2 <List -> to <Symbol a> <Symbol b>>");
// ast = reader::read("(fn (x) (fn (y) x) z)");
// afterAst = semantics::analyze(*ctx, ast.getValue());
// REQUIRE(afterAst);
// CHECK(astToString(&afterAst.getValue()) ==
// "<Fn ___fn___4 <List <Symbol x>> to <Fn ___fn___3 <List <Symbol y>>
// " "to <Symbol x>> <Symbol z>>");
// ast = reader::read("(fn (x) (def a b) (def b c))");
// afterAst = semantics::analyze(*ctx, ast.getValue());
// REQUIRE(afterAst);
// CHECK(astToString(&afterAst.getValue()) ==
// "<Fn ___fn___5 <List <Symbol x>> to <Def a -> <Symbol b>> <Def b ->
// "
// "<Symbol c>>>");
// }
// TEST_CASE("Complex semantic analysis", "[semantic]") {
// auto ctx = makeSereneContext();
// auto ns = makeNamespace(*ctx, "user", llvm::None);
// auto ast =
// reader::read("(def a (fn (x) x))\n((def b (fn (x) (fn (y) y))))\n\n");
// auto afterAst = semantics::analyze(*ctx, ast.getValue());
// REQUIRE(afterAst);
// CHECK(astToString(&afterAst.getValue()) ==
// "<Def a -> <Fn a <List <Symbol x>> to <Symbol x>>> <Call <Def b -> "
// "<Fn b <List <Symbol x>> to <Fn ___fn___1 <List <Symbol y>> to "
// "<Symbol y>>>> >");
// ctx = makeSereneContext();
// ns = makeNamespace(*ctx, "user", llvm::None);
// ast = reader::read("((a b))");
// afterAst = semantics::analyze(*ctx, ast.getValue());
// REQUIRE_FALSE(afterAst);
// auto errs = afterAst.getError();
// CHECK(errs[0]->toString() == "<Error E5: Can't resolve the symbol 'a'>");
// ctx = makeSereneContext();
// ns = makeNamespace(*ctx, "user", llvm::None);
// ast = reader::read("(def a (fn (x) x)) (a b)");
// afterAst = semantics::analyze(*ctx, ast.getValue());
// REQUIRE(afterAst);
// CHECK(astToString(&afterAst.getValue()) ==
// "<Def a -> <Fn a <List <Symbol x>> to <Symbol x>>> <Call <Fn a <List
// "
// "<Symbol x>> to <Symbol x>> <Symbol b>>");
// }
} // namespace exprs
} // namespace serene

View File

@ -1,42 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/exprs/number.h"
#include "../test_helpers.cpp.inc"
namespace serene {
namespace exprs {
TEST_CASE("Number Expression", "[expression]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation());
auto num1 = makeAndCast<Number>(*range, "3", false, false);
auto num2 = makeAndCast<Number>(*range, "3.4", false, true);
// Hence the `isNeg` being true. We need to provide the sign as the input
// anyway
auto num3 = makeAndCast<Number>(*range, "3", true, false);
auto num4 = makeAndCast<Number>(*range, "-3", true, false);
CHECK(num1->toString() == "<Number 3>");
CHECK(num2->toString() == "<Number 3.4>");
CHECK(num3->toString() == "<Number 3>");
CHECK(num4->toString() == "<Number -3>");
};
} // namespace exprs
} // namespace serene

View File

@ -1,35 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/exprs/expression.h"
#include "serene/exprs/symbol.h"
#include "../test_helpers.cpp.inc"
namespace serene {
namespace exprs {
TEST_CASE("Public Symbol API", "[expression]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation());
auto sym = make<Symbol>(*range, "example", "user");
REQUIRE(sym->getType() == ExprType::Symbol);
CHECK(sym->toString() == "<Symbol user/example>");
};
} // namespace exprs
} // namespace serene

View File

@ -1,50 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/context.h"
#include "serene/exprs/expression.h"
#include "serene/namespace.h"
#include "serene/reader/reader.h"
#include "./test_helpers.cpp.inc"
#include <catch2/catch_all.hpp>
namespace serene {
TEST_CASE("Namespace tests", "[namespace]") {
auto ctx = makeSereneContext();
auto userNs = makeNamespace(*ctx, "user",
llvm::Optional<llvm::StringRef>("/some/file"));
auto maybeAst = reader::read("(x 1) (def b a)");
if (!maybeAst) {
FAIL();
}
auto result = userNs->setTree(maybeAst.getValue());
REQUIRE(result.succeeded());
REQUIRE_FALSE(userNs->getTree().empty());
CHECK(exprs::astToString(&userNs->getTree()) ==
"<List <Symbol x> <Number 1>> <List <Symbol def> <Symbol b> <Symbol "
"a>>");
};
} // namespace serene

View File

@ -1,110 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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_TEST_READER_H
#define SERENE_TEST_READER_H
#include "serene/reader/reader.h"
#include "../test_helpers.cpp.inc"
#include <catch2/catch_test_macros.hpp>
namespace serene {
namespace reader {
TEST_CASE("Read numbers", "[reader]") {
auto ctx = makeSereneContext();
auto maybeAst = READ("3");
if (!maybeAst) {
FAIL();
}
auto ast = *maybeAst;
REQUIRE_FALSE(ast.empty());
CHECK(ast.front()->toString() == "<Number 3>");
maybeAst = READ("-34");
if (!maybeAst) {
FAIL();
}
ast = *maybeAst;
REQUIRE_FALSE(ast.empty());
CHECK(ast.front()->toString() == "<Number -34>");
maybeAst = READ("-3.5434");
if (!maybeAst) {
FAIL();
}
ast = *maybeAst;
REQUIRE_FALSE(ast.empty());
CHECK(ast.front()->toString() == "<Number -3.5434>");
maybeAst = READ("444323 2123 123123");
if (!maybeAst) {
FAIL();
}
ast = *maybeAst;
REQUIRE(ast.size() == 3);
CHECK(ast.front()->toString() == "<Number 444323>");
CHECK(ast[1]->toString() == "<Number 2123>");
CHECK(ast[2]->toString() == "<Number 123123>");
};
TEST_CASE("Read Lists and Symbols", "[reader]") {
auto ctx = makeSereneContext();
auto maybeAst = READ("(x 1)");
if (!maybeAst) {
FAIL();
}
auto ast = *maybeAst;
REQUIRE_FALSE(ast.empty());
CHECK(ast.front()->toString() == "<List <Symbol user/x> <Number 1>>");
maybeAst = READ("(x (y (z)))");
if (!maybeAst) {
FAIL();
}
ast = *maybeAst;
REQUIRE_FALSE(ast.empty());
CHECK(ast.front()->toString() == "<List <Symbol user/x> <List <Symbol "
"user/y> <List <Symbol user/z>>>>");
maybeAst = READ("(x \n y)");
if (!maybeAst) {
FAIL();
}
ast = *maybeAst;
REQUIRE_FALSE(ast.empty());
CHECK(ast.front()->toString() == "<List <Symbol user/x> <Symbol user/y>>");
};
} // namespace reader
} // namespace serene
#endif

View File

@ -1,33 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/>.
*/
#define CATCH_CONFIG_MAIN
#include "./context_tests.cpp.inc"
#include "./environment_tests.cpp.inc"
#include "./errors/error_tests.cpp.inc"
#include "./exprs/expression_tests.cpp.inc"
#include "./exprs/list_tests.cpp.inc"
#include "./exprs/number_tests.cpp.inc"
#include "./exprs/symbol_tests.cpp.inc"
#include "./setup.cpp.inc"
// #include "./namespace_tests.cpp.inc"
#include "./reader/reader_tests.cpp.inc"
// #include "./traits_tests.cpp.inc"
// #include "./utils_tests.cpp.inc"
// include <catch2/catch.hpp>
#include <catch2/catch_all.hpp>

View File

@ -1,37 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/serene.h"
#include "serene/utils.h"
#include <catch2/reporters/catch_reporter_event_listener.hpp>
#include <catch2/reporters/catch_reporter_registrars.hpp>
#include <llvm/Support/raw_ostream.h>
class testRunListener : public Catch::EventListenerBase {
public:
using Catch::EventListenerBase::EventListenerBase;
void testRunStarting(Catch::TestRunInfo const &info) override {
UNUSED(info);
serene::initCompiler();
}
};
CATCH_REGISTER_LISTENER(testRunListener)

View File

@ -1,66 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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 TEST_HEALPERS_H
#define TEST_HEALPERS_H
#include "serene/reader/location.h"
// *IMPORTANT NOTE:* The `READ` macro is just a quick way to eliminate
// the overhead of writing the same function signature
// over and over again. Nothing special about it.
#define READ(input) reader::read(*ctx, input, "user", llvm::None)
#define CHECK_ERR_MSG(e, s) CHECK(serene::errors::getMessage(e) == s)
// `llvm::Error`s has to be checked in the same scope. This macro makes
// the check easy while were testing the other aspects of the error.
// `t` is the concrete error type and `e` is the error instance.
#define CHECK_SERENE_ERR(t, e) \
auto unhandled = llvm::handleErrors( \
e, [&](const SereneError &x) { CHECK(x.errorType == t); }); \
CHECK(!unhandled);
#define CHECK_ERR(t, e) \
auto unhandled = llvm::handleErrors(e, [&](const t &x) { \
(void)x; \
CHECK(true); \
}); \
CHECK(!unhandled);
namespace serene {
reader::LocationRange *dummyLocation() {
reader::Location start("serene.test.ns");
reader::Location end("serene.test.ns");
constexpr const int line1 = 2;
constexpr const int line2 = 3;
constexpr const int col1 = 20;
constexpr const int col2 = 30;
start.line = line1;
start.col = col1;
end.line = line2;
end.col = col2;
return new reader::LocationRange(start, end);
};
} // namespace serene
#endif

View File

@ -1,129 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/traits.h"
#include "./test_helpers.cpp.inc"
#include <catch2/catch_all.hpp>
namespace serene {
template <typename ConcreteType>
class Printable : public TraitBase<ConcreteType, Printable> {
public:
Printable(){};
Printable(const Printable &) = delete;
std::string Print() { return this->Object().Print(); }
};
template <typename ConcreteType>
class Analyzable : public TraitBase<ConcreteType, Analyzable> {
public:
Analyzable(){};
Analyzable(const Analyzable &) = delete;
std::string Analyze() { return this->Object().Analyze(); }
};
template <typename T>
std::string Print(Printable<T> &t) {
return t.Print();
}
template <typename T>
std::string Analyze(Analyzable<T> &t) {
return t.Analyze();
};
class A : public WithTrait<A, Printable, Analyzable> {
std::string x;
public:
A(std::string a) : x(a){};
std::string Print() const { return "A: print"; }
std::string Analyze() const { return "A: " + x; }
};
template <typename T = FinalImpl>
class B : public std::conditional<std::is_same_v<T, FinalImpl>,
WithTrait<B<>, Printable, Analyzable>,
WithTrait<T, Printable, Analyzable>>::type {
std::string y;
public:
B(std::string a) : y(a){};
std::string Print() const { return "B: print"; }
std::string Analyze() const { return "B: " + y; }
};
class C : public B<C> {
std::string z;
std::string w;
public:
C(std::string a, std::string b, std::string c) : B<C>(a), z(b), w(c){};
std::string Print() const { return z; }
std::string Analyze() const { return w; }
};
class D : public WithTrait<D, Printable> {
public:
std::string Print() const { return "D: print"; }
std::string Analyze() const { return "D: analyze with no trait"; }
};
template <typename T>
class MetaTrait : public WithTrait<T, Printable, Analyzable> {};
class E : public MetaTrait<E> {
public:
std::string Print() const { return "E: print"; };
std::string Analyze() const { return "E: in E"; };
};
TEST_CASE("Trait functionality tests", "[Traits]") {
auto a = A("in A");
auto b = B("in B");
auto c = C("gray", "white", "black");
auto d = D();
auto e = E();
CHECK(Print(a) == "A: print");
CHECK(Print(b) == "B: print");
CHECK(Print(c) == "white");
CHECK(Print(d) == "D: print");
CHECK(Print(e) == "E: print");
CHECK(Analyze(a) == "A: in A");
CHECK(Analyze(b) == "B: in B");
CHECK(Analyze(c) == "black");
CHECK(Analyze(e) == "E: in E");
// Even though D has a Analyze method, It's not Analyzable to the
// Analyze function signature won't match
CHECK(d.Analyze() == "D: analyze with no trait");
};
} // namespace serene

View File

@ -1,34 +0,0 @@
/* -*- C++ -*-
* Serene Programming Language
*
* Copyright (c) 2019-2023 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/utils.h"
#include "./test_helpers.cpp.inc"
namespace serene {
TEST_CASE("Result Type", "[utils]") {
auto r = Result<int>::success(4);
REQUIRE(r == true);
CHECK(r.getValue() == 4);
auto r1 = Result<int, char>::error('c');
REQUIRE_FALSE(r1);
CHECK(r1.getError() == 'c');
};
} // namespace serene

View File

@ -1,82 +0,0 @@
# Serene Programming Language
#
# Copyright (c) 2019-2023 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/>.
set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(LIBSERENE_INCLUDE_DIR ${INCLUDE_DIR} PARENT_SCOPE)
include_directories(${INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib)
add_subdirectory(lib)
# Install rules for libserene target
install(TARGETS serene
EXPORT SereneExports
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
# Install rules for the public header files.
install(DIRECTORY ${INCLUDE_DIR}/serene
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN *.h
PATTERN *.td
PATTERN "CMake*" EXCLUDE)
# Install rule for the public generated header files
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN *.h
PATTERN *.td
PATTERN *.h.inc
PATTERN "CMake*" EXCLUDE)
include(CMakePackageConfigHelpers)
# Package config file let us use find_package with serene
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
)
write_basic_package_version_file(
"${PROJECT_NAME}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
)
# Install the package exports
install(EXPORT SereneExports
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
NAMESPACE serene::)
# Testing only available if this is the main app
# Emergency override SERENE_CMAKE_BUILD_TESTING provided as well
if(SERENE_BUILD_TESTING)
message("Build the test binary")
add_subdirectory(tests)
endif()

View File

@ -1,21 +0,0 @@
# Serene Programming Language
#
# Copyright (c) 2019-2023 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/>.
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Exports.cmake")
check_required_components("@PROJECT_NAME@")

Some files were not shown because too many files have changed in this diff Show More