diff --git a/libserene.v0/CMakeLists.txt b/libserene.v0/CMakeLists.txt deleted file mode 100644 index 3b31159..0000000 --- a/libserene.v0/CMakeLists.txt +++ /dev/null @@ -1,81 +0,0 @@ -# Serene Programming Language -# -# Copyright (c) 2019-2023 Sameer Rahmani -# -# 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 . - -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() diff --git a/libserene.v0/cmake/SereneConfig.cmake.in b/libserene.v0/cmake/SereneConfig.cmake.in deleted file mode 100644 index c638645..0000000 --- a/libserene.v0/cmake/SereneConfig.cmake.in +++ /dev/null @@ -1,21 +0,0 @@ - # Serene Programming Language -# -# Copyright (c) 2019-2023 Sameer Rahmani -# -# 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 . - -@PACKAGE_INIT@ - -include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Exports.cmake") - -check_required_components("@PROJECT_NAME@") diff --git a/libserene.v0/include/CMakeLists.txt b/libserene.v0/include/CMakeLists.txt deleted file mode 100644 index 1c51b2a..0000000 --- a/libserene.v0/include/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_subdirectory("serene/slir/") -add_subdirectory("serene/passes/") -add_subdirectory("serene/errors/") diff --git a/libserene.v0/include/serene/config.h.in b/libserene.v0/include/serene/config.h.in deleted file mode 100644 index bc74d63..0000000 --- a/libserene.v0/include/serene/config.h.in +++ /dev/null @@ -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 diff --git a/libserene.v0/include/serene/context.h b/libserene.v0/include/serene/context.h deleted file mode 100644 index 3a2a4aa..0000000 --- a/libserene.v0/include/serene/context.h +++ /dev/null @@ -1,285 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#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; -} // 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 - using CurrentNSFn = std::function; - - mlir::MLIRContext mlirContext; - mlir::PassManager pm; - std::unique_ptr diagEngine; - std::unique_ptr 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 - T withCurrentNS(llvm::StringRef nsName, CurrentNSFn 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 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(); - mlirContext.getOrLoadDialect(); - mlirContext.getOrLoadDialect(); - - // 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 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 genLLVMContext() { - return std::make_unique(); - }; - - static std::unique_ptr make(Options &options) { - auto ctx = std::make_unique(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 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> 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 -makeSereneContext(Options opts = Options()); - -} // namespace serene - -#endif diff --git a/libserene.v0/include/serene/conventions.h b/libserene.v0/include/serene/conventions.h deleted file mode 100644 index eebe34f..0000000 --- a/libserene.v0/include/serene/conventions.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_CONVENTIONS_H -#define SERENE_CONVENTIONS_H - -#include "serene/config.h" - -#include - -#include - -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 diff --git a/libserene.v0/include/serene/diagnostics.h b/libserene.v0/include/serene/diagnostics.h deleted file mode 100644 index 48b3a37..0000000 --- a/libserene.v0/include/serene/diagnostics.h +++ /dev/null @@ -1,127 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -/** - * 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 -#include -#include - -#include - -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 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 diff --git a/libserene.v0/include/serene/environment.h b/libserene.v0/include/serene/environment.h deleted file mode 100644 index 4a7b4e5..0000000 --- a/libserene.v0/include/serene/environment.h +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_TEST_ENVIRONMENT_H -#define SERENE_TEST_ENVIRONMENT_H - -#include "serene/utils.h" - -#include -#include - -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` -template -class Environment { - - Environment *parent; - - using StorageType = llvm::StringMap; - // 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 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 diff --git a/libserene.v0/include/serene/errors.h b/libserene.v0/include/serene/errors.h deleted file mode 100644 index 0626857..0000000 --- a/libserene.v0/include/serene/errors.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include - -namespace serene { -class SereneContext; -} // namespace serene - -namespace serene::errors { - -class SERENE_EXPORT SereneError : public llvm::ErrorInfo { -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 -SERENE_EXPORT llvm::Error makeError(SereneContext &ctx, ErrorType errtype, - Args &&...args) { - return llvm::make_error(ctx, errtype, - std::forward(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 diff --git a/libserene.v0/include/serene/errors/CMakeLists.txt b/libserene.v0/include/serene/errors/CMakeLists.txt deleted file mode 100644 index 5c8aaac..0000000 --- a/libserene.v0/include/serene/errors/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(LLVM_TARGET_DEFINITIONS errors.td) - -serene_tablegen(errs.h.inc -errors-backend) - -add_public_tablegen_target(SereneErrorGen) diff --git a/libserene.v0/include/serene/errors/base.h b/libserene.v0/include/serene/errors/base.h deleted file mode 100644 index 788095b..0000000 --- a/libserene.v0/include/serene/errors/base.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 - -#include -#include - -namespace serene::errors { - -class SERENE_EXPORT Error {}; - -class SERENE_EXPORT SereneError : public llvm::ErrorInfo { -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 diff --git a/libserene.v0/include/serene/errors/errors.td b/libserene.v0/include/serene/errors/errors.td deleted file mode 100644 index 71b64d7..0000000 --- a/libserene.v0/include/serene/errors/errors.td +++ /dev/null @@ -1,76 +0,0 @@ - - -class Error { - 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, -]; diff --git a/libserene.v0/include/serene/errors/variant.h b/libserene.v0/include/serene/errors/variant.h deleted file mode 100644 index 11e5a17..0000000 --- a/libserene.v0/include/serene/errors/variant.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_ERRORS_VARIANT_H -#define SERENE_ERRORS_VARIANT_H - -#include - -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 diff --git a/libserene.v0/include/serene/exprs/call.h b/libserene.v0/include/serene/exprs/call.h deleted file mode 100644 index ce0cdf5..0000000 --- a/libserene.v0/include/serene/exprs/call.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include - -#include -#include - -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 ¶ms) - : 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 diff --git a/libserene.v0/include/serene/exprs/def.h b/libserene.v0/include/serene/exprs/def.h deleted file mode 100644 index 82165c3..0000000 --- a/libserene.v0/include/serene/exprs/def.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_EXPRS_DEF_H -#define SERENE_EXPRS_DEF_H - -#include "serene/context.h" -#include "serene/errors.h" -#include "serene/exprs/expression.h" - -#include -#include - -#include -#include - -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 diff --git a/libserene.v0/include/serene/exprs/expression.h b/libserene.v0/include/serene/exprs/expression.h deleted file mode 100644 index 127f491..0000000 --- a/libserene.v0/include/serene/exprs/expression.h +++ /dev/null @@ -1,148 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include - -#include - -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; -using MaybeNode = llvm::Expected; - -using Ast = std::vector; -using MaybeAst = llvm::Expected; - -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(); -/// \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 -Node make(Args &&...args) { - return std::make_shared(std::forward(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(); -/// \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 -std::shared_ptr makeAndCast(Args &&...args) { - return std::make_shared(std::forward(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 -MaybeNode makeSuccessfulNode(Args &&...args) { - return make(std::forward(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 -llvm::Expected makeErrorful(Args &&...args) { - return llvm::make_error(std::forward(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 -MaybeNode makeErrorNode(Args &&...args) { - return makeErrorful(std::forward(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 diff --git a/libserene.v0/include/serene/exprs/fn.h b/libserene.v0/include/serene/exprs/fn.h deleted file mode 100644 index cb2192a..0000000 --- a/libserene.v0/include/serene/exprs/fn.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include - -#include -#include - -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 diff --git a/libserene.v0/include/serene/exprs/list.h b/libserene.v0/include/serene/exprs/list.h deleted file mode 100644 index 28225f2..0000000 --- a/libserene.v0/include/serene/exprs/list.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_EXPRS_LIST_H -#define SERENE_EXPRS_LIST_H - -#include "serene/context.h" -#include "serene/export.h" -#include "serene/exprs/expression.h" - -#include - -#include - -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 at(uint index); - - /// Return an iterator to be used with the `for` loop. It's implicitly called - /// by the for loop. - std::vector::const_iterator cbegin(); - - /// Return an iterator to be used with the `for` loop. It's implicitly called - /// by the for loop. - std::vector::const_iterator cend(); - - /// Return an iterator to be used with the `for` loop. It's implicitly called - /// by the for loop. - std::vector::iterator begin(); - - /// Return an iterator to be used with the `for` loop. It's implicitly called - /// by the for loop. - std::vector::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 diff --git a/libserene.v0/include/serene/exprs/number.h b/libserene.v0/include/serene/exprs/number.h deleted file mode 100644 index 828a14a..0000000 --- a/libserene.v0/include/serene/exprs/number.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 - -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 diff --git a/libserene.v0/include/serene/exprs/symbol.h b/libserene.v0/include/serene/exprs/symbol.h deleted file mode 100644 index a1f7b0a..0000000 --- a/libserene.v0/include/serene/exprs/symbol.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 - -#include - -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 diff --git a/libserene.v0/include/serene/exprs/traits.h b/libserene.v0/include/serene/exprs/traits.h deleted file mode 100644 index 9fcb7f8..0000000 --- a/libserene.v0/include/serene/exprs/traits.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 diff --git a/libserene.v0/include/serene/jit/README.org b/libserene.v0/include/serene/jit/README.org deleted file mode 100644 index a2b2525..0000000 --- a/libserene.v0/include/serene/jit/README.org +++ /dev/null @@ -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. diff --git a/libserene.v0/include/serene/jit/engine.h b/libserene.v0/include/serene/jit/engine.h deleted file mode 100644 index f96fb37..0000000 --- a/libserene.v0/include/serene/jit/engine.h +++ /dev/null @@ -1,180 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -/** - * 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 // for ConcurrentIRCompiler -#include -#include -#include // for DynamicLibrarySearchGenerator -#include -#include -#include -#include -#include -#include - -#include - -#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 es; - - std::unique_ptr 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 transformLayer; - // std::unique_ptr 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 - optimizeModule(orc::ThreadSafeModule tsm, - const orc::MaterializationResponsibility &r) { - // TSM.withModuleDo([](Module &M) { - // // Create a function pass manager. - // auto FPM = std::make_unique(&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 ¤tNS; - -public: - SereneJIT(Namespace &entryNS, std::unique_ptr es, - std::unique_ptr 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 lookup(llvm::StringRef name) { - JIT_LOG("Looking up symbol: " + name); - return es->lookup({&mainJD}, mangler(name.str())); - } -}; - -llvm::Expected> makeSereneJIT(Namespace &ns); - -}; // namespace jit -}; // namespace serene - -#endif diff --git a/libserene.v0/include/serene/jit/halley.h b/libserene.v0/include/serene/jit/halley.h deleted file mode 100644 index 8b70dcb..0000000 --- a/libserene.v0/include/serene/jit/halley.h +++ /dev/null @@ -1,196 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -/** - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include - -#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>; -using MaybeJITPtr = llvm::Expected; - -/// 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 getObject(const llvm::Module *m) override; - - /// Dump cached object to output file `filename`. - void dumpToObjectFile(llvm::StringRef filename); - -private: - llvm::StringMap> cachedObjects; -}; - -class SERENE_EXPORT Halley { - // TODO: Replace this with a variant of LLJIT and LLLazyJIT - std::unique_ptr engine; - std::unique_ptr 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 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 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 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 - struct Argument { - static void pack(llvm::SmallVectorImpl &args, T &val) { - args.push_back(&val); - } - }; - - /// Tag to wrap an output parameter when invoking a jitted function. - template - struct FnResult { - FnResult(T &result) : value(result) {} - T &value; - }; - - /// Helper function to wrap an output operand when using - /// ExecutionEngine::invoke. - template - static FnResult result(T &t) { - return FnResult(t); - } - - // Specialization for output parameter: their address is forwarded directly to - // the native code. - template - struct Argument> { - static void pack(llvm::SmallVectorImpl &args, FnResult &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 - // llvm::Error invoke(llvm::StringRef funcName, Args... args) { - // const std::string adapterName = std::string("") + funcName.str(); - // llvm::SmallVector 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::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> makeHalleyJIT(SereneContext &ctx); - -} // namespace jit -} // namespace serene - -#endif diff --git a/libserene.v0/include/serene/jit/layers.h b/libserene.v0/include/serene/jit/layers.h deleted file mode 100644 index e28842e..0000000 --- a/libserene.v0/include/serene/jit/layers.h +++ /dev/null @@ -1,164 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_JIT_LAYERS_H -#define SERENE_JIT_LAYERS_H - -#include "serene/namespace.h" -#include "serene/reader/location.h" -#include "serene/utils.h" - -#include -#include -#include -#include -#include -#include -#include - -#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; -using Ast = std::vector; -} // 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 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 * -}; - -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(ns, *this, ast), rt); - } - - void emit(std::unique_ptr 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 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 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 diff --git a/libserene.v0/include/serene/llvm/IR/Value.h b/libserene.v0/include/serene/llvm/IR/Value.h deleted file mode 100644 index fac0004..0000000 --- a/libserene.v0/include/serene/llvm/IR/Value.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef LLVM_IR_VALUE_H -#define LLVM_IR_VALUE_H - -#pragma clang diagnostic ignored "-Wunused-parameter" -#include - -#endif diff --git a/libserene.v0/include/serene/llvm/patches.h b/libserene.v0/include/serene/llvm/patches.h deleted file mode 100644 index c348f6b..0000000 --- a/libserene.v0/include/serene/llvm/patches.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef LLVM_PATCHES_H -#define LLVM_PATCHES_H - -#include -#include - -namespace llvm { - -// Our specialization of DensMapInfo for string type. This will allow use to use -// string -template <> -struct DenseMapInfo { - 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 diff --git a/libserene.v0/include/serene/namespace.h b/libserene.v0/include/serene/namespace.h deleted file mode 100644 index 245661e..0000000 --- a/libserene.v0/include/serene/namespace.h +++ /dev/null @@ -1,178 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -/** - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#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; -using Ast = std::vector; -} // namespace exprs - -using NSPtr = std::shared_ptr; -using MaybeNS = llvm::Expected; -using MaybeModule = std::optional; -using MaybeModuleOp = std::optional>; -using SemanticEnv = Environment; -using SemanticEnvPtr = std::unique_ptr; -using SemanticEnvironments = std::vector; -using Form = std::pair; -using Forms = std::vector
; - -/// 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 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 symbolList; - -public: - std::string name; - std::optional 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 filename); - - Namespace(SereneContext &ctx, llvm::StringRef ns_name, - std::optional 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 &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 diff --git a/libserene.v0/include/serene/passes.h b/libserene.v0/include/serene/passes.h deleted file mode 100644 index 9ec33e3..0000000 --- a/libserene.v0/include/serene/passes.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_PASSES_H -#define SERENE_PASSES_H - -#include "serene/export.h" - -#include - -#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 createLowerSymbol(); - -SERENE_EXPORT void registerAllPasses(); -/// Return a pass to convert SLIR dialect to built-in dialects -/// of MLIR. -std::unique_ptr createSLIRLowerToMLIRPass(); - -/// Return a pass to convert different dialects of MLIR to LLVM dialect. -std::unique_ptr createSLIRLowerToLLVMDialectPass(); - -} // namespace serene::passes - -#endif diff --git a/libserene.v0/include/serene/passes/CMakeLists.txt b/libserene.v0/include/serene/passes/CMakeLists.txt deleted file mode 100644 index 0b30d08..0000000 --- a/libserene.v0/include/serene/passes/CMakeLists.txt +++ /dev/null @@ -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) diff --git a/libserene.v0/include/serene/passes/passes.td b/libserene.v0/include/serene/passes/passes.td deleted file mode 100644 index 6627a8f..0000000 --- a/libserene.v0/include/serene/passes/passes.td +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 diff --git a/libserene.v0/include/serene/reader/location.h b/libserene.v0/include/serene/reader/location.h deleted file mode 100644 index 9b0526e..0000000 --- a/libserene.v0/include/serene/reader/location.h +++ /dev/null @@ -1,92 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_READER_LOCATION_H -#define SERENE_READER_LOCATION_H - -#include -#include - -#include - -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 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 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 diff --git a/libserene.v0/include/serene/reader/reader.h b/libserene.v0/include/serene/reader/reader.h deleted file mode 100644 index f5ba731..0000000 --- a/libserene.v0/include/serene/reader/reader.h +++ /dev/null @@ -1,137 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -/** - * 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 -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#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 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 filename); - Reader(SereneContext &ctx, llvm::MemoryBufferRef buf, llvm::StringRef ns, - std::optional 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` -/// which may contains an AST or an `llvm::Error` -SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx, llvm::StringRef input, - llvm::StringRef ns, - std::optional filename); -SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx, - llvm::MemoryBufferRef input, - llvm::StringRef ns, - std::optional filename); -} // namespace serene::reader - -#endif diff --git a/libserene.v0/include/serene/reader/traits.h b/libserene.v0/include/serene/reader/traits.h deleted file mode 100644 index cd7d264..0000000 --- a/libserene.v0/include/serene/reader/traits.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_READER_TRAITS_H -#define SERENE_READER_TRAITS_H - -#include "serene/reader/location.h" -#include "serene/traits.h" - -namespace serene::reader { - -template -class ILocatable : public TraitBase { -public: - ILocatable(){}; - ILocatable(const ILocatable &) = delete; - serene::reader::LocationRange &where() const { - return this->Object().where(); - } -}; - -template -serene::reader::LocationRange &where(ILocatable &); -} // namespace serene::reader -#endif diff --git a/libserene.v0/include/serene/semantics.h b/libserene.v0/include/serene/semantics.h deleted file mode 100644 index 966baef..0000000 --- a/libserene.v0/include/serene/semantics.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 - -#include - -namespace serene { - -namespace exprs { -class Expression; -using Node = std::shared_ptr; -using Ast = std::vector; -}; // namespace exprs - -class Namespace; -using SemanticEnv = Environment; -namespace semantics { - -using AnalyzeResult = llvm::Expected; - -/// 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 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 -std::unique_ptr makeAnalysisState(Args &&...args) { - return std::make_unique(std::forward(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 diff --git a/libserene.v0/include/serene/serene.h b/libserene.v0/include/serene/serene.h deleted file mode 100644 index 037e807..0000000 --- a/libserene.v0/include/serene/serene.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 diff --git a/libserene.v0/include/serene/slir/CMakeLists.txt b/libserene.v0/include/serene/slir/CMakeLists.txt deleted file mode 100644 index efa02d9..0000000 --- a/libserene.v0/include/serene/slir/CMakeLists.txt +++ /dev/null @@ -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) diff --git a/libserene.v0/include/serene/slir/dialect.h b/libserene.v0/include/serene/slir/dialect.h deleted file mode 100644 index 84057c1..0000000 --- a/libserene.v0/include/serene/slir/dialect.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ -#ifndef SERENE_SLIR_DIALECT_H -#define SERENE_SLIR_DIALECT_H - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// 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 ®istry); -} // namespace serene::slir - // -#endif // SERENE_SLIR_DIALECT_H diff --git a/libserene.v0/include/serene/slir/dialect.td b/libserene.v0/include/serene/slir/dialect.td deleted file mode 100644 index aae1382..0000000 --- a/libserene.v0/include/serene/slir/dialect.td +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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:$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 diff --git a/libserene.v0/include/serene/slir/generatable.h b/libserene.v0/include/serene/slir/generatable.h deleted file mode 100644 index 30f9f5f..0000000 --- a/libserene.v0/include/serene/slir/generatable.h +++ /dev/null @@ -1,121 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_SLIR_GENERATABLE_H -#define SERENE_SLIR_GENERATABLE_H - -#include "serene/slir/dialect.h" -#include "serene/traits.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace serene { -class Namespace; -class SereneContext; -} // namespace serene - -namespace serene::slir { - -template -class GeneratableUnit : public TraitBase { -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 -class Generatable : public TraitBase { -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 -mlir::LogicalResult generate(Generatable &t) { - return t.generate(); -}; - -template -std::unique_ptr toLLVMIR(Generatable &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 -void dump(Generatable &t) { - t.dump(); -}; - -} // namespace serene::slir - -#endif diff --git a/libserene.v0/include/serene/slir/ops.h b/libserene.v0/include/serene/slir/ops.h deleted file mode 100644 index 4e0be10..0000000 --- a/libserene.v0/include/serene/slir/ops.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_DIALECT_OPS_H -#define SERENE_DIALECT_OPS_H - -#include "serene/slir/types.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define GET_OP_CLASSES -#include "serene/slir/ops.h.inc" - -#endif diff --git a/libserene.v0/include/serene/slir/ops.td b/libserene.v0/include/serene/slir/ops.td deleted file mode 100644 index 5c12137..0000000 --- a/libserene.v0/include/serene/slir/ops.td +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 traits = []> : - Op; - - -// 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 - ``` - }]; - - let arguments = (ins StringType:$ns, StringType:$name); - let results = (outs Ptr:$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:$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:$is_top_level, - OptionalAttr:$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:$is_top_level, - OptionalAttr:$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:$sym_visibility); - - let regions = (region VariadicRegion:$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:$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:$args); - let results = (outs Variadic); - - 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:$sym_visibility); - - let regions = (region SizedRegion<1>:$body); - - let assemblyFormat = "$sym_name attr-dict-with-keyword $body"; - - let builders = [ - OpBuilder<(ins CArg<"llvm::Optional", "{}">:$name)> - ]; - - let extraClassDeclaration = [{ - /// Construct a namespace from the given location with an optional name. - // static NsOp create(slir::reader::LocationRange loc, Optional 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 diff --git a/libserene.v0/include/serene/slir/slir.h b/libserene.v0/include/serene/slir/slir.h deleted file mode 100644 index 63c805c..0000000 --- a/libserene.v0/include/serene/slir/slir.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ -#ifndef SERENE_SLIR_SLIR_H -#define SERENE_SLIR_SLIR_H - -#include "serene/exprs/expression.h" - -#include -#include -#include - -#include - -namespace serene { -namespace slir { -// std::unique_ptr compileToLLVMIR(serene::SereneContext &ctx, -// mlir::ModuleOp &module); - -std::optional -compileToLLVMIR(serene::SereneContext &ctx, mlir::ModuleOp &module); -} // namespace slir -} // namespace serene - -#endif diff --git a/libserene.v0/include/serene/slir/symbol.h b/libserene.v0/include/serene/slir/symbol.h deleted file mode 100644 index b99ceab..0000000 --- a/libserene.v0/include/serene/slir/symbol.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -// SereneSymbol is the Symbil type in SLIT that represents an expr::Sympol - -#ifndef SERENE_SLIR_SYMBOL_H -#define SERENE_SLIR_SYMBOL_H - -#include -#include - -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(key); -// } - -// std::string ns; -// std::string name; -// }; -}; // namespace detail -}; // namespace slir -}; // namespace serene -#endif diff --git a/libserene.v0/include/serene/slir/traits.h b/libserene.v0/include/serene/slir/traits.h deleted file mode 100644 index d34b5ab..0000000 --- a/libserene.v0/include/serene/slir/traits.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_SLIR_TRAITS_H -#define SERENE_SLIR_TRAITS_H - -#include "serene/slir/dialect.h" -#include "serene/traits.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace serene { -class Namespace; -class SereneContext; -} // namespace serene - -namespace serene::slir { - -template -class GeneratableUnit : public TraitBase { -public: - GeneratableUnit(){}; - GeneratableUnit(const GeneratableUnit &) = delete; - - void generate(serene::Namespace &ns) { this->Object().generateIR(ns); }; -}; - -template -class Generatable : public TraitBase { -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 -mlir::LogicalResult generate(Generatable &t) { - return t.generate(); -}; - -template -std::unique_ptr toLLVMIR(Generatable &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 -void dump(Generatable &t) { - t.dump(); -}; - -} // namespace serene::slir - -#endif diff --git a/libserene.v0/include/serene/slir/type_converter.h b/libserene.v0/include/serene/slir/type_converter.h deleted file mode 100644 index 86f99dd..0000000 --- a/libserene.v0/include/serene/slir/type_converter.h +++ /dev/null @@ -1,86 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include -#include - -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; - using MaybeValue = std::optional; - using ConverterFn = std::function; - - std::function - - materializeSymbolFn() { - return [&](mlir::OpBuilder &builder, SymbolType type, - mlir::ValueRange values, mlir::Location loc) -> MaybeValue { - auto targetType = convertType(type); - auto ret = builder.create(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 diff --git a/libserene.v0/include/serene/slir/types.h b/libserene.v0/include/serene/slir/types.h deleted file mode 100644 index b470492..0000000 --- a/libserene.v0/include/serene/slir/types.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_DIALECT_TYPES_H -#define SERENE_DIALECT_TYPES_H - -#include -#include -#include -#include -#include - -#define GET_ATTRDEF_CLASSES -#include "serene/slir/attrs.h.inc" - -#define GET_TYPEDEF_CLASSES -#include "serene/slir/types.h.inc" - -#endif diff --git a/libserene.v0/include/serene/slir/types.td b/libserene.v0/include/serene/slir/types.td deleted file mode 100644 index d9dff8d..0000000 --- a/libserene.v0/include/serene/slir/types.td +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 traits = []> - : TypeDef { - let mnemonic = typeMnemonic; -} - -// The base class for all the Serene attributes -class Serene_Attr traits = []> - : AttrDef { - 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()">, - "Serene pointer type", "Ptr">; - -class Ptr : Type< - And<[AnyPtr.predicate, - Or<[CPred<"$_self.cast().isOpaque()">, - SubstLeaves< - "$_self", - "$_self.cast().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 as the result type - // of an operation - SameBuildabilityAs { - 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 - }]; - - 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 diff --git a/libserene.v0/include/serene/slir/utils.h b/libserene.v0/include/serene/slir/utils.h deleted file mode 100644 index 3610480..0000000 --- a/libserene.v0/include/serene/slir/utils.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_SLIR_UTILS_H -#define SERENE_SLIR_UTILS_H - -#include "serene/reader/location.h" - -#include - -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 diff --git a/libserene.v0/include/serene/source_mgr.h b/libserene.v0/include/serene/source_mgr.h deleted file mode 100644 index ac627c9..0000000 --- a/libserene.v0/include/serene/source_mgr.h +++ /dev/null @@ -1,190 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_SOURCE_MGR_H -#define SERENE_SOURCE_MGR_H - -#include "serene/namespace.h" -#include "serene/reader/location.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#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 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 - 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 - 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; - - /// This is all of the buffers that we are reading from. - std::vector buffers; - - /// A hashtable that works as an index from namespace names to the buffer - /// position it the `buffer` - llvm::StringMap nsTable; - - // This is the list of directories we should search for include files in. - std::vector 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 &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 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 diff --git a/libserene.v0/include/serene/traits.h b/libserene.v0/include/serene/traits.h deleted file mode 100644 index a80471b..0000000 --- a/libserene.v0/include/serene/traits.h +++ /dev/null @@ -1,123 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -/** - * 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 - * class Blahable : public TraitBase {} - * \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 - * SomeType Foo(ABC &t) { return t.foo(); }; - * - * template - * SomeType bar(ABC &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` 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 - -#include - -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 {} -/// \endcode -template class... Traits> -class WithTrait : public Traits... { -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 -/// class Blahable : public TraitBase {} -/// \endcode -/// -/// In the Trait class the underlaying object which implements the Trait -/// is accessable via the `Object` method. -template 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(*this); - // }; - - ConcreteType &Object() { return static_cast(*this); }; -}; - -template -class IDebuggable : public TraitBase { -public: - IDebuggable(){}; - IDebuggable(const IDebuggable &) = delete; - std::string toString() const { return this->Object().toString(); } -}; - -template -std::string toString(IDebuggable &t) { - return t.toString(); -} - -}; // namespace serene -#endif diff --git a/libserene.v0/include/serene/traits/locatable.h b/libserene.v0/include/serene/traits/locatable.h deleted file mode 100644 index 2806bda..0000000 --- a/libserene.v0/include/serene/traits/locatable.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_TRAITS_LOCATABLE_H -#define SERENE_TRAITS_LOCATABLE_H - -#endif diff --git a/libserene.v0/include/serene/types/type.h b/libserene.v0/include/serene/types/type.h deleted file mode 100644 index c9ba5bf..0000000 --- a/libserene.v0/include/serene/types/type.h +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_TYPES_TYPE_H -#define SERENE_TYPES_TYPE_H - -#include - -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 diff --git a/libserene.v0/include/serene/utils.h b/libserene.v0/include/serene/utils.h deleted file mode 100644 index d55d814..0000000 --- a/libserene.v0/include/serene/utils.h +++ /dev/null @@ -1,140 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_UTILS_H -#define SERENE_UTILS_H - -#include "serene/export.h" - -#include - -#include - -// 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 -#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::success(3); -/// auto notOkResult = Result::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`. So for example: -/// -/// \code -/// Result fn() {return 2;} -/// \endcode -/// -/// works perfectly. -template -class SERENE_EXPORT Result { - - // The actual data container - std::variant contents; - - /// The main constructor which we made private to avoid ambiguousness in - /// input type. `Success` and `Error` call this ctor. - template - Result(InPlace i, Content &&c) : contents(i, std::forward(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(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 diff --git a/libserene.v0/lib/CMakeLists.txt b/libserene.v0/lib/CMakeLists.txt deleted file mode 100644 index d718ef6..0000000 --- a/libserene.v0/lib/CMakeLists.txt +++ /dev/null @@ -1,169 +0,0 @@ -# Serene Programming Language -# -# Copyright (c) 2019-2023 Sameer Rahmani -# -# 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 . - -#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 - $<$:-shared-libsan> - ) - target_link_options( - serene PRIVATE - $<$:-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 "$") -target_include_directories(serene PUBLIC "$") - -# 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 "$<$>: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}) diff --git a/libserene.v0/lib/context.cpp b/libserene.v0/lib/context.cpp deleted file mode 100644 index 364f524..0000000 --- a/libserene.v0/lib/context.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/context.h" - -#include "serene/namespace.h" -#include "serene/passes.h" -#include "serene/reader/location.h" -#include "serene/slir/generatable.h" - -#include - -#include - -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 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(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 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 makeSereneContext(Options opts) { - return SereneContext::make(opts); -}; - -}; // namespace serene diff --git a/libserene.v0/lib/diagnostics.cpp b/libserene.v0/lib/diagnostics.cpp deleted file mode 100644 index bc52bab..0000000 --- a/libserene.v0/lib/diagnostics.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/diagnostics.h" - -#include "serene/context.h" -#include "serene/reader/location.h" -#include "serene/source_mgr.h" -#include "serene/utils.h" - -#include -#include -#include -#include -#include -#include - -#include - -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 makeDiagnosticEngine(SereneContext &ctx) { - return std::make_unique(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(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 diff --git a/libserene.v0/lib/errors.cpp b/libserene.v0/lib/errors.cpp deleted file mode 100644 index ea3f2d2..0000000 --- a/libserene.v0/lib/errors.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/errors.h" - -#include -#include - -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 diff --git a/libserene.v0/lib/exprs/call.cpp b/libserene.v0/lib/exprs/call.cpp deleted file mode 100644 index cf4864b..0000000 --- a/libserene.v0/lib/exprs/call.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include -#include - -namespace serene { -namespace exprs { - -ExprType Call::getType() const { return ExprType::Call; }; - -std::string Call::toString() const { - return llvm::formatv("", 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(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(list->location, targetNode, *analyzedParams); -}; -} // namespace exprs -} // namespace serene diff --git a/libserene.v0/lib/exprs/def.cpp b/libserene.v0/lib/exprs/def.cpp deleted file mode 100644 index 0f076be..0000000 --- a/libserene.v0/lib/exprs/def.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include -#include - -namespace serene { -namespace exprs { - -ExprType Def::getType() const { return ExprType::Def; }; - -std::string Def::toString() const { - return llvm::formatv(" {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(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(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(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(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(sym, binding, value); - m.emitError("Def: not implemented!"); -}; -} // namespace exprs -} // namespace serene diff --git a/libserene.v0/lib/exprs/expression.cpp b/libserene.v0/lib/exprs/expression.cpp deleted file mode 100644 index c47df08..0000000 --- a/libserene.v0/lib/exprs/expression.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/exprs/expression.h" - -#include - -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 diff --git a/libserene.v0/lib/exprs/fn.cpp b/libserene.v0/lib/exprs/fn.cpp deleted file mode 100644 index 24a9b77..0000000 --- a/libserene.v0/lib/exprs/fn.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include -#include -#include - -#include - -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("", - 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(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->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(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(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 arg_types; - llvm::SmallVector arguments; - // at the moment we only support integers - for (auto &arg : args) { - auto *argSym = llvm::dyn_cast(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( - 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(loc, 0).getResult(); - - slir::Return1Op returnOp = builder.create(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 diff --git a/libserene.v0/lib/exprs/list.cpp b/libserene.v0/lib/exprs/list.cpp deleted file mode 100644 index 0ef7923..0000000 --- a/libserene.v0/lib/exprs/list.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include -#include - -#include -#include - -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("", 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(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::const_iterator List::cbegin() { return elements.begin(); } - -std::vector::const_iterator List::cend() { return elements.end(); } - -std::vector::iterator List::begin() { return elements.begin(); } - -std::vector::iterator List::end() { return elements.end(); } - -size_t List::count() const { return elements.size(); } - -std::optional List::at(uint index) { - if (index >= elements.size()) { - return llvm::None; - } - - return std::optional(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 diff --git a/libserene.v0/lib/exprs/number.cpp b/libserene.v0/lib/exprs/number.cpp deleted file mode 100644 index e08f997..0000000 --- a/libserene.v0/lib/exprs/number.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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("", 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::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 diff --git a/libserene.v0/lib/exprs/symbol.cpp b/libserene.v0/lib/exprs/symbol.cpp deleted file mode 100644 index b4c0d2a..0000000 --- a/libserene.v0/lib/exprs/symbol.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/exprs/symbol.h" - -#include "serene/exprs/expression.h" - -#include -#include - -namespace serene { -namespace exprs { - -ExprType Symbol::getType() const { return ExprType::Symbol; }; - -std::string Symbol::toString() const { - return llvm::formatv("", 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 diff --git a/libserene.v0/lib/jit/engine.cpp b/libserene.v0/lib/jit/engine.cpp deleted file mode 100644 index 68eb87e..0000000 --- a/libserene.v0/lib/jit/engine.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/jit/engine.h" - -#include "serene/context.h" -#include "serene/jit/layers.h" -#include "serene/utils.h" - -#include - -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 es, - std::unique_ptr 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(); }), - compileLayer( - *this->es, objectLayer, - std::make_unique(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> makeSereneJIT(Namespace &ns) { - auto epc = orc::SelfExecutorProcessControl::Create(); - if (!epc) { - return epc.takeError(); - } - auto es = std::make_unique(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(ns, std::move(es), std::move(*epciu), - std::move(jtmb), std::move(*dl)); -}; -} // namespace serene::jit diff --git a/libserene.v0/lib/jit/halley.cpp b/libserene.v0/lib/jit/halley.cpp deleted file mode 100644 index cd4c0ad..0000000 --- a/libserene.v0/lib/jit/halley.cpp +++ /dev/null @@ -1,457 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#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 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(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 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 -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(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 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 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(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 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(session, []() { - return std::make_unique(); - }); - - // 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::CodeGenOpt::Level jitCodeGenOptLevel = - static_cast(serene_ctx.getOptimizatioLevel()); - - JTMB.setCodeGenOptLevel(jitCodeGenOptLevel); - - auto targetMachine = JTMB.createTargetMachine(); - if (!targetMachine) { - return targetMachine.takeError(); - } - - return std::make_unique( - 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> 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 diff --git a/libserene.v0/lib/jit/layers.cpp b/libserene.v0/lib/jit/layers.cpp deleted file mode 100644 index 09044fd..0000000 --- a/libserene.v0/lib/jit/layers.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/jit/layers.h" - -#include "serene/context.h" -#include "serene/exprs/fn.h" -#include "serene/exprs/traits.h" - -#include -#include // for report_fatal_error - -#include - -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 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 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(*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 diff --git a/libserene.v0/lib/namespace.cpp b/libserene.v0/lib/namespace.cpp deleted file mode 100644 index 11b3880..0000000 --- a/libserene.v0/lib/namespace.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ -#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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -using namespace std; -using namespace llvm; - -namespace serene { - -Namespace::Namespace(SereneContext &ctx, llvm::StringRef ns_name, - std::optional 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(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(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 filename) { - return std::make_shared(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 diff --git a/libserene.v0/lib/passes/slir_lowering.cpp b/libserene.v0/lib/passes/slir_lowering.cpp deleted file mode 100644 index 289d43f..0000000 --- a/libserene.v0/lib/passes/slir_lowering.cpp +++ /dev/null @@ -1,761 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -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(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(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(loc, global); -// mlir::Value cst0 = builder.create( -// loc, mlir::IntegerType::get(builder.getContext(), I64_SIZE), -// builder.getIntegerAttr(builder.getIndexType(), 0)); - -// return builder.create(loc, ll::LLVMPointerType::get(I8), -// globalPtr, -// llvm::ArrayRef({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(name))) { - -// mlir::OpBuilder::InsertionGuard insertGuard(builder); -// builder.setInsertionPointToStart(module.getBody()); - -// mlir::Attribute initValue{}; -// auto type = slir::getStringTypeinLLVM(*ctx); - -// global = builder.create( -// 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 and return -// // an error here -// } - -// builder.setInsertionPoint(block, block->begin()); - -// mlir::Value structInstant = builder.create(loc, type); - -// auto strOp = getOrCreateInternalString(loc, builder, name, value, -// module); auto ptrToStr = getPtrToInternalString(builder, strOp); - -// auto length = builder.create( -// loc, mlir::IntegerType::get(ctx, I32_SIZE), -// builder.getI32IntegerAttr(len)); - -// // Setting the string pointer field -// structInstant = builder.create( -// loc, structInstant.getType(), structInstant, ptrToStr, -// builder.getI64ArrayAttr(0)); - -// // Setting the len field -// structInstant = builder.create( -// loc, structInstant.getType(), structInstant, length, -// builder.getI64ArrayAttr(1)); - -// builder.create(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(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(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 and return -// // an error here -// } - -// builder.setInsertionPoint(block, block->begin()); - -// mlir::Value structInstant = builder.create(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(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(loc, nameField); - -// // Setting the string pointer field -// structInstant = builder.create( -// loc, structInstant.getType(), structInstant, ptrToNs, -// builder.getI64ArrayAttr(0)); - -// // Setting the len field -// structInstant = builder.create( -// loc, structInstant.getType(), structInstant, ptrToName, -// builder.getI64ArrayAttr(0)); - -// builder.create(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() && -// !ns.getType().isa() && -// "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(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(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 and return -// // an error here -// } - -// builder.setInsertionPoint(block, block->begin()); - -// mlir::Value structInstant = builder.create(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(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(loc, nameField); - -// // Setting the string pointer field -// structInstant = builder.create( -// loc, structInstant.getType(), structInstant, ptrToNs, -// builder.getI64ArrayAttr(0)); - -// // Setting the len field -// structInstant = builder.create( -// loc, structInstant.getType(), structInstant, ptrToName, -// builder.getI64ArrayAttr(0)); - -// builder.create(loc, structInstant); -// } - -// return global; -// }; - -// struct LowerIntern : public mlir::OpConversionPattern { -// using OpConversionPattern::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(); - -// // 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(loc, global); - -// rewriter.replaceOp(op, ptr.getResult()); - -// return mlir::success(); -// } - -// struct LowerSymbol : public mlir::OpConversionPattern { -// using OpConversionPattern::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(); - -// // 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(loc, global); - -// rewriter.replaceOp(op, ptr.getResult()); - -// return mlir::success(); -// } - -struct LowerDefine : public mlir::OpConversionPattern { - using OpConversionPattern::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( - 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(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(); - 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(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(loc, llvmType); - rewriter.create(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(op, symRef, value); - // auto setvalOp = rewriter.create(loc, symRef, - // llvmValue); rewriter.insert(setvalOp); rewriter.eraseOp(op); - return mlir::success(); - } - - // auto globop = rewriter.create(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(value.getLoc(), - // adaptor.getOperands()); - - // if (!op.getResult().use_empty()) { - // auto symValue = rewriter.create(loc, ns, name); - - // rewriter.replaceOp(op, symValue.getResult()); - // } - - rewriter.eraseOp(op); - - return mlir::success(); -} - -struct LowerDefineConstant - : public mlir::OpConversionPattern { - using OpConversionPattern::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(); - 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(loc, value.getType(), - /*isConstant=*/true, linkage, fqsym, - value)); - - // if (!op.value().use_empty()) { - // auto symValue = rewriter.create(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 { - 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(); - target.addLegalDialect(); - target.addLegalDialect(); - - // We also define the SLIR dialect as Illegal so that the conversion will - // fail if any of these operations are *not* converted. - target.addIllegalDialect(); - - // 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(); - target.addLegalOp(); - - // 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(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 createLowerSLIR() { - return std::make_unique(); -} - -#define GEN_PASS_REGISTRATION -#include "serene/passes/passes.h.inc" - -// ---------------------------------------------------------------------------- -// ValueOp lowering to constant op -struct ValueOpLowering : public mlir::OpRewritePattern { - using OpRewritePattern::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 arg_types(0); - auto func_type = rewriter.getFunctionType(arg_types, rewriter.getI64Type()); - // TODO: use a mechanism to generate unique names - auto fn = rewriter.create(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(loc, (int64_t)value, - rewriter.getI64Type()) - .getResult(); - - UNUSED(rewriter.create(loc, retVal)); - - fn.setPrivate(); - - // Erase the original ValueOP - rewriter.eraseOp(op); - return mlir::success(); -} - -// ---------------------------------------------------------------------------- -// Fn lowering pattern -struct FnOpLowering : public mlir::OpRewritePattern { - using OpRewritePattern::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 arg_types; - - for (const auto &arg : args) { - auto attr = arg.getValue().dyn_cast(); - - 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(loc, name, func_type); - - auto *entryBlock = fn.addEntryBlock(); - - rewriter.setInsertionPointToStart(entryBlock); - - auto retVal = rewriter - .create(loc, (int64_t)3, - rewriter.getI64Type()) - .getResult(); - - rewriter.create(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> { - void getDependentDialects(mlir::DialectRegistry ®istry) 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 ®istry) const { - registry.insert(); -}; - -/// 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(); - target.addLegalDialect(); - - // We also define the SLIR dialect as Illegal so that the conversion will fail - // if any of these operations are *not* converted. - target.addIllegalDialect(); - - // 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(); - target.addLegalOp(); - - // 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(&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 createSLIRLowerToMLIRPass() { - return std::make_unique(); -}; - -void registerAllPasses() { registerPasses(); } -} // namespace serene::passes diff --git a/libserene.v0/lib/passes/to_llvm_dialect.cpp b/libserene.v0/lib/passes/to_llvm_dialect.cpp deleted file mode 100644 index 963ace2..0000000 --- a/libserene.v0/lib/passes/to_llvm_dialect.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/passes.h" -#include "serene/slir/dialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace serene::passes { -struct SLIRToLLVMDialect - : public mlir::PassWrapper> { - void getDependentDialects(mlir::DialectRegistry ®istry) const override { - registry.insert(); - } - - 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(); - - // 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(&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 createSLIRLowerToLLVMDialectPass() { - return std::make_unique(); -}; - -} // namespace serene::passes diff --git a/libserene.v0/lib/reader/reader.cpp b/libserene.v0/lib/reader/reader.cpp deleted file mode 100644 index ce0f59c..0000000 --- a/libserene.v0/lib/reader/reader.cpp +++ /dev/null @@ -1,438 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -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 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 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(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(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(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 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 filename) { - reader::Reader r(ctx, input, ns, filename); - - auto ast = r.read(); - return ast; -} -} // namespace reader -} // namespace serene diff --git a/libserene.v0/lib/semantics.cpp b/libserene.v0/lib/semantics.cpp deleted file mode 100644 index b44b690..0000000 --- a/libserene.v0/lib/semantics.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/semantics.h" - -#include "serene/context.h" -#include "serene/exprs/expression.h" -#include "serene/namespace.h" - -#include - -namespace serene::semantics { - -std::unique_ptr 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 diff --git a/libserene.v0/lib/serene.cpp b/libserene.v0/lib/serene.cpp deleted file mode 100644 index 2261301..0000000 --- a/libserene.v0/lib/serene.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include -#include -#include -#include -#include - -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 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 sharedLibraryPaths{ - "sl", llvm::cl::desc("Where to find shared libraries"), - llvm::cl::ZeroOrMore, llvm::cl::MiscFlags::PositionalEatsArgs, - llvm::cl::cat(clOptionsCategory)}; -}; - -static llvm::ManagedStatic 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 ¤tNS = ctx.getCurrentNS(); - auto filename = - !currentNS.filename.hasValue() - ? llvm::None - : std::optional(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(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(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 diff --git a/libserene.v0/lib/slir/dialect.cpp b/libserene.v0/lib/slir/dialect.cpp deleted file mode 100644 index 5b1def0..0000000 --- a/libserene.v0/lib/slir/dialect.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/slir/dialect.h" - -#include "serene/slir/dialect.cpp.inc" -#include "serene/slir/ops.h" -#include "serene/slir/types.h" - -#include -#include -#include -#include - -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 ®istry) { - registry.insert(); -}; - -} // namespace slir -} // namespace serene diff --git a/libserene.v0/lib/slir/generatable.cpp b/libserene.v0/lib/slir/generatable.cpp deleted file mode 100644 index 906eef9..0000000 --- a/libserene.v0/lib/slir/generatable.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/slir/generatable.h" - -#include "serene/slir/dialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace serene { -namespace slir { - -// mlir::Operation *Generatable::generate(exprs::Expression *x) { -// switch (x->getType()) { -// case SereneType::Number: { -// return generate(llvm::cast(x)); -// } - -// case SereneType::List: { -// generate(llvm::cast(x)); -// return nullptr; -// } - -// default: { -// return builder.create(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(builder.getUnknownLoc(), (uint64_t)0); -// } - -// if (first->get()->getType() == SereneType::Symbol) { -// auto fnNameSymbol = llvm::dyn_cast(first->get()); - -// if (fnNameSymbol->getName() == "fn") { -// if (l->count() <= 3) { -// module.emitError("'fn' form needs exactly 2 arguments."); -// } - -// auto args = llvm::dyn_cast(l->at(1).getValue().get()); -// auto body = llvm::dyn_cast(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(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(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 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 -// 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(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(entryBlock.back()); -// // if (!returnOp) { -// // builder.create(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 diff --git a/libserene.v0/lib/slir/ops.cpp b/libserene.v0/lib/slir/ops.cpp deleted file mode 100644 index 92fca28..0000000 --- a/libserene.v0/lib/slir/ops.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/slir/ops.h" - -#include "serene/slir/dialect.h" -#include "serene/slir/types.h" -#include "serene/utils.h" - -#include -#include -#include -#include -#include - -#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()) { - return spec; - } - } - return {}; -} - -mlir::OpFoldResult SymbolOp::fold(llvm::ArrayRef operands) { - UNUSED(operands); - return value(); -}; - -mlir::OpFoldResult ValueOp::fold(llvm::ArrayRef operands) { - UNUSED(operands); - return value(); -}; -} // namespace serene::slir diff --git a/libserene.v0/lib/slir/slir.cpp b/libserene.v0/lib/slir/slir.cpp deleted file mode 100644 index 276b28d..0000000 --- a/libserene.v0/lib/slir/slir.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/slir/slir.h" - -namespace serene { -namespace slir { - -std::optional -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 diff --git a/libserene.v0/lib/slir/type_converter.cpp b/libserene.v0/lib/slir/type_converter.cpp deleted file mode 100644 index dbb442b..0000000 --- a/libserene.v0/lib/slir/type_converter.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/slir/type_converter.h" - -#include "serene/slir/dialect.h" - -#include -#include - -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 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 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()) { - return getStringTypeinLLVM(ctx); - } - - if (type.isa()) { - return getSymbolTypeinLLVM(ctx); - } - - if (type.isa()) { - return getPtrTypeinLLVM(ctx, type.cast()); - } - - return llvm::None; - }; -} -} // namespace slir -} // namespace serene diff --git a/libserene.v0/lib/slir/types.cpp b/libserene.v0/lib/slir/types.cpp deleted file mode 100644 index 96b924d..0000000 --- a/libserene.v0/lib/slir/types.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 diff --git a/libserene.v0/lib/slir/utils.cpp b/libserene.v0/lib/slir/utils.cpp deleted file mode 100644 index 598a885..0000000 --- a/libserene.v0/lib/slir/utils.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/context.h" -#include "serene/namespace.h" -#include "serene/reader/location.h" - -#include - -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 diff --git a/libserene.v0/lib/slir/value_op.cpp b/libserene.v0/lib/slir/value_op.cpp deleted file mode 100644 index fd1a5d6..0000000 --- a/libserene.v0/lib/slir/value_op.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ -#include "serene/slir/dialect.h" -#include "serene/slir/slir.h" - -#include -#include -#include -#include - -namespace serene { -namespace slir {} // namespace slir -} // namespace serene diff --git a/libserene.v0/lib/source_mgr.cpp b/libserene.v0/lib/source_mgr.cpp deleted file mode 100644 index 5f5ac93..0000000 --- a/libserene.v0/lib/source_mgr.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/source_mgr.h" - -#include "serene/namespace.h" -#include "serene/reader/location.h" -#include "serene/reader/reader.h" -#include "serene/utils.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace serene { - -std::string SourceMgr::convertNamespaceToPath(std::string ns_name) { - std::replace(ns_name.begin(), ns_name.end(), '.', '/'); - - llvm::SmallString 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 f, - reader::LocationRange includeLoc) { - SrcBuffer nb; - nb.buffer = std::move(f); - nb.importLoc = includeLoc; - buffers.push_back(std::move(nb)); - return buffers.size(); -}; - -template -static std::vector &GetOrCreateOffsetCache(void *&offsetCache, - llvm::MemoryBuffer *buffer) { - if (offsetCache) { - return *static_cast *>(offsetCache); - } - - // Lazily fill in the offset cache. - auto *offsets = new std::vector(); - size_t sz = buffer->getBufferSize(); - - // TODO: Replace this assert with a realtime check - assert(sz <= std::numeric_limits::max()); - - llvm::StringRef s = buffer->getBuffer(); - for (size_t n = 0; n < sz; ++n) { - if (s[n] == '\n') { - offsets->push_back(static_cast(n)); - } - } - - offsetCache = offsets; - return *offsets; -} - -template -const char *SourceMgr::SrcBuffer::getPointerForLineNumberSpecialized( - unsigned lineNo) const { - std::vector &offsets = - GetOrCreateOffsetCache(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::max()) { - return getPointerForLineNumberSpecialized(lineNo); - } - - if (sz <= std::numeric_limits::max()) { - return getPointerForLineNumberSpecialized(lineNo); - } - - if (sz <= std::numeric_limits::max()) { - return getPointerForLineNumberSpecialized(lineNo); - } - - return getPointerForLineNumberSpecialized(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::max()) { - delete static_cast *>(offsetCache); - } else if (sz <= std::numeric_limits::max()) { - delete static_cast *>(offsetCache); - } else if (sz <= std::numeric_limits::max()) { - delete static_cast *>(offsetCache); - } else { - delete static_cast *>(offsetCache); - } - offsetCache = nullptr; - } -} - -}; // namespace serene diff --git a/libserene.v0/tests/CMakeLists.txt b/libserene.v0/tests/CMakeLists.txt deleted file mode 100644 index d483cdf..0000000 --- a/libserene.v0/tests/CMakeLists.txt +++ /dev/null @@ -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) diff --git a/libserene.v0/tests/context_tests.cpp.inc b/libserene.v0/tests/context_tests.cpp.inc deleted file mode 100644 index 054612d..0000000 --- a/libserene.v0/tests/context_tests.cpp.inc +++ /dev/null @@ -1,161 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ -#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 -#include - -#include - -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("/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("/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("/some/file")); - - ctx->withCurrentNS("user", [&]() -> int { - CHECK(ctx->getCurrentNS().name == userNs->name); - return 0; - }); - - // Checking the `void` type specialization - ctx->withCurrentNS("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("/some/file")); - - auto userNs1 = ctx->makeNamespace( - "user1", llvm::Optional("/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 diff --git a/libserene.v0/tests/environment_tests.cpp.inc b/libserene.v0/tests/environment_tests.cpp.inc deleted file mode 100644 index 52a3e9c..0000000 --- a/libserene.v0/tests/environment_tests.cpp.inc +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include - -#include - -namespace serene { - -TEST_CASE("Environment tests", "[environment]") { - std::unique_ptr range(dummyLocation()); - - exprs::Node sym = exprs::make(*range, "example", "ns"); - - Environment e; - llvm::Optional 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(result.getValue().get()); - REQUIRE(fetchedSym != nullptr); - CHECK(fetchedSym->name == "example"); - CHECK(fetchedSym->nsName == "ns"); - - SECTION("Testing the environment copy") { - Environment 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 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 diff --git a/libserene.v0/tests/errors/error_tests.cpp.inc b/libserene.v0/tests/errors/error_tests.cpp.inc deleted file mode 100644 index bb49f29..0000000 --- a/libserene.v0/tests/errors/error_tests.cpp.inc +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_TEST_ERRORS_H -#define SERENE_TEST_ERRORS_H - -#include "serene/context.h" -#include "serene/errors.h" - -#include "../test_helpers.cpp.inc" -#include - -#include -#include -#include - -namespace serene { -namespace errors { - -TEST_CASE("Serene Error construction", "[errors]") { - - std::unique_ptr 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 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 diff --git a/libserene.v0/tests/exprs/expression_tests.cpp.inc b/libserene.v0/tests/exprs/expression_tests.cpp.inc deleted file mode 100644 index 4bfe883..0000000 --- a/libserene.v0/tests/exprs/expression_tests.cpp.inc +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/exprs/expression.h" -#include "serene/exprs/list.h" -#include "serene/exprs/symbol.h" - -#include "../test_helpers.cpp.inc" - -#include - -namespace serene { -namespace exprs { - -TEST_CASE("Public Expression API", "[expression]") { - std::unique_ptr range(dummyLocation()); - auto sym = make(*range, "example", "user"); - - REQUIRE(sym->getType() == ExprType::Symbol); - CHECK(sym->toString() == ""); - - auto list = makeAndCast(*range, sym); - - CHECK(list->toString() == ">"); -}; - -} // namespace exprs -} // namespace serene diff --git a/libserene.v0/tests/exprs/list_tests.cpp.inc b/libserene.v0/tests/exprs/list_tests.cpp.inc deleted file mode 100644 index 04d69eb..0000000 --- a/libserene.v0/tests/exprs/list_tests.cpp.inc +++ /dev/null @@ -1,196 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 - -namespace serene { -namespace exprs { - -TEST_CASE("List Expression", "[expression]") { - std::unique_ptr range(dummyLocation()); - - Node sym = make(*range, "example", "user"); - Node sym1 = make(*range, "example1", "user"); - - Node list = make(*range); - - REQUIRE(list->getType() == ExprType::List); - CHECK(list->toString() == ""); - - Node list2 = make(*range, list); - CHECK(list2->toString() == ">"); - - Ast elements; - elements.push_back(list); - elements.push_back(list2); - elements.push_back(sym); - - auto list3 = make(*range, elements); - - CHECK(list3->toString() == - " > >"); - - auto l = llvm::dyn_cast(list.get()); - - l->append(sym1); - - REQUIRE(list->getType() == ExprType::List); - CHECK(list->toString() == ">"); - - l->append(sym); - REQUIRE(l->count() == 2); - - auto expr = l->at(1); - REQUIRE(expr.hasValue()); - CHECK(expr.getValue()->toString() == ""); - - 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()); - } - 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)) == " >"); - - ast = llvm::cantFail(READ("(def a (fn () a))")); - afterAst = semantics::analyze(state, ast); - REQUIRE(afterAst); - CHECK(astToString(&(*afterAst)) == - " to >>"); -} - -// 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() == -// ""); - -// ast = reader::read("(fn ())"); -// afterAst = semantics::analyze(*ctx, ast.getValue()); -// REQUIRE(afterAst); -// CHECK(astToString(&afterAst.getValue()) == " to -// <>>"); - -// ast = reader::read("(fn (a b c) a a a)"); -// afterAst = semantics::analyze(*ctx, ast.getValue()); -// REQUIRE(afterAst); -// CHECK(astToString(&afterAst.getValue()) == -// " > to -// " -// " >"); - -// ast = reader::read("(fn () a b)"); -// afterAst = semantics::analyze(*ctx, ast.getValue()); -// REQUIRE(afterAst); -// CHECK(astToString(&afterAst.getValue()) == -// " to >"); - -// ast = reader::read("(fn (x) (fn (y) x) z)"); -// afterAst = semantics::analyze(*ctx, ast.getValue()); -// REQUIRE(afterAst); -// CHECK(astToString(&afterAst.getValue()) == -// "> to > -// " "to > >"); - -// ast = reader::read("(fn (x) (def a b) (def b c))"); -// afterAst = semantics::analyze(*ctx, ast.getValue()); -// REQUIRE(afterAst); -// CHECK(astToString(&afterAst.getValue()) == -// "> to > -// " -// ">>"); -// } - -// 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()) == -// " > to >> " -// "> to > to " -// ">>> >"); - -// 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() == ""); - -// 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()) == -// " > to >> > to > >"); -// } -} // namespace exprs -} // namespace serene diff --git a/libserene.v0/tests/exprs/number_tests.cpp.inc b/libserene.v0/tests/exprs/number_tests.cpp.inc deleted file mode 100644 index 71a3932..0000000 --- a/libserene.v0/tests/exprs/number_tests.cpp.inc +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/exprs/number.h" - -#include "../test_helpers.cpp.inc" - -namespace serene { -namespace exprs { -TEST_CASE("Number Expression", "[expression]") { - std::unique_ptr range(dummyLocation()); - - auto num1 = makeAndCast(*range, "3", false, false); - auto num2 = makeAndCast(*range, "3.4", false, true); - // Hence the `isNeg` being true. We need to provide the sign as the input - // anyway - auto num3 = makeAndCast(*range, "3", true, false); - auto num4 = makeAndCast(*range, "-3", true, false); - - CHECK(num1->toString() == ""); - CHECK(num2->toString() == ""); - CHECK(num3->toString() == ""); - CHECK(num4->toString() == ""); -}; - -} // namespace exprs -} // namespace serene diff --git a/libserene.v0/tests/exprs/symbol_tests.cpp.inc b/libserene.v0/tests/exprs/symbol_tests.cpp.inc deleted file mode 100644 index 65b13a4..0000000 --- a/libserene.v0/tests/exprs/symbol_tests.cpp.inc +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 range(dummyLocation()); - auto sym = make(*range, "example", "user"); - - REQUIRE(sym->getType() == ExprType::Symbol); - CHECK(sym->toString() == ""); -}; -} // namespace exprs -} // namespace serene diff --git a/libserene.v0/tests/namespace_tests.cpp.inc b/libserene.v0/tests/namespace_tests.cpp.inc deleted file mode 100644 index b88c5a9..0000000 --- a/libserene.v0/tests/namespace_tests.cpp.inc +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/context.h" -#include "serene/exprs/expression.h" -#include "serene/namespace.h" -#include "serene/reader/reader.h" - -#include "./test_helpers.cpp.inc" -#include - -namespace serene { - -TEST_CASE("Namespace tests", "[namespace]") { - auto ctx = makeSereneContext(); - auto userNs = makeNamespace(*ctx, "user", - llvm::Optional("/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()) == - " > >"); -}; - -} // namespace serene diff --git a/libserene.v0/tests/reader/reader_tests.cpp.inc b/libserene.v0/tests/reader/reader_tests.cpp.inc deleted file mode 100644 index 227cb67..0000000 --- a/libserene.v0/tests/reader/reader_tests.cpp.inc +++ /dev/null @@ -1,110 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_TEST_READER_H -#define SERENE_TEST_READER_H - -#include "serene/reader/reader.h" - -#include "../test_helpers.cpp.inc" -#include - -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() == ""); - - maybeAst = READ("-34"); - - if (!maybeAst) { - FAIL(); - } - - ast = *maybeAst; - REQUIRE_FALSE(ast.empty()); - CHECK(ast.front()->toString() == ""); - - maybeAst = READ("-3.5434"); - - if (!maybeAst) { - FAIL(); - } - - ast = *maybeAst; - REQUIRE_FALSE(ast.empty()); - CHECK(ast.front()->toString() == ""); - - maybeAst = READ("444323 2123 123123"); - - if (!maybeAst) { - FAIL(); - } - - ast = *maybeAst; - REQUIRE(ast.size() == 3); - CHECK(ast.front()->toString() == ""); - CHECK(ast[1]->toString() == ""); - CHECK(ast[2]->toString() == ""); -}; - -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() == " >"); - - maybeAst = READ("(x (y (z)))"); - - if (!maybeAst) { - FAIL(); - } - - ast = *maybeAst; - REQUIRE_FALSE(ast.empty()); - CHECK(ast.front()->toString() == " >>>"); - - maybeAst = READ("(x \n y)"); - - if (!maybeAst) { - FAIL(); - } - - ast = *maybeAst; - REQUIRE_FALSE(ast.empty()); - CHECK(ast.front()->toString() == " >"); -}; -} // namespace reader -} // namespace serene -#endif diff --git a/libserene.v0/tests/serenetests.cpp b/libserene.v0/tests/serenetests.cpp deleted file mode 100644 index b502518..0000000 --- a/libserene.v0/tests/serenetests.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 -#include diff --git a/libserene.v0/tests/setup.cpp.inc b/libserene.v0/tests/setup.cpp.inc deleted file mode 100644 index 45e82b1..0000000 --- a/libserene.v0/tests/setup.cpp.inc +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/serene.h" -#include "serene/utils.h" - -#include -#include - -#include - -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) diff --git a/libserene.v0/tests/test_helpers.cpp.inc b/libserene.v0/tests/test_helpers.cpp.inc deleted file mode 100644 index 9ffc0c5..0000000 --- a/libserene.v0/tests/test_helpers.cpp.inc +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#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 diff --git a/libserene.v0/tests/traits_tests.cpp.inc b/libserene.v0/tests/traits_tests.cpp.inc deleted file mode 100644 index ad59fc2..0000000 --- a/libserene.v0/tests/traits_tests.cpp.inc +++ /dev/null @@ -1,129 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/traits.h" - -#include "./test_helpers.cpp.inc" -#include - -namespace serene { - -template -class Printable : public TraitBase { -public: - Printable(){}; - Printable(const Printable &) = delete; - std::string Print() { return this->Object().Print(); } -}; - -template -class Analyzable : public TraitBase { -public: - Analyzable(){}; - Analyzable(const Analyzable &) = delete; - std::string Analyze() { return this->Object().Analyze(); } -}; - -template -std::string Print(Printable &t) { - return t.Print(); -} - -template -std::string Analyze(Analyzable &t) { - return t.Analyze(); -}; - -class A : public WithTrait { - - 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 -class B : public std::conditional, - WithTrait, Printable, Analyzable>, - WithTrait>::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 { - - std::string z; - std::string w; - -public: - C(std::string a, std::string b, std::string c) : B(a), z(b), w(c){}; - std::string Print() const { return z; } - - std::string Analyze() const { return w; } -}; - -class D : public WithTrait { -public: - std::string Print() const { return "D: print"; } - - std::string Analyze() const { return "D: analyze with no trait"; } -}; - -template -class MetaTrait : public WithTrait {}; - -class E : public MetaTrait { -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 diff --git a/libserene.v0/tests/utils_tests.cpp.inc b/libserene.v0/tests/utils_tests.cpp.inc deleted file mode 100644 index 7ddedc6..0000000 --- a/libserene.v0/tests/utils_tests.cpp.inc +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ -#include "serene/utils.h" - -#include "./test_helpers.cpp.inc" - -namespace serene { -TEST_CASE("Result Type", "[utils]") { - auto r = Result::success(4); - - REQUIRE(r == true); - CHECK(r.getValue() == 4); - - auto r1 = Result::error('c'); - - REQUIRE_FALSE(r1); - CHECK(r1.getError() == 'c'); -}; -} // namespace serene diff --git a/libserene/CMakeLists.txt b/libserene/CMakeLists.txt deleted file mode 100644 index de7d9fd..0000000 --- a/libserene/CMakeLists.txt +++ /dev/null @@ -1,82 +0,0 @@ -# Serene Programming Language -# -# Copyright (c) 2019-2023 Sameer Rahmani -# -# 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 . - -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() diff --git a/libserene/cmake/SereneConfig.cmake.in b/libserene/cmake/SereneConfig.cmake.in deleted file mode 100644 index c638645..0000000 --- a/libserene/cmake/SereneConfig.cmake.in +++ /dev/null @@ -1,21 +0,0 @@ - # Serene Programming Language -# -# Copyright (c) 2019-2023 Sameer Rahmani -# -# 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 . - -@PACKAGE_INIT@ - -include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Exports.cmake") - -check_required_components("@PROJECT_NAME@") diff --git a/libserene/include/serene/config.h.in b/libserene/include/serene/config.h.in deleted file mode 100644 index c151f63..0000000 --- a/libserene/include/serene/config.h.in +++ /dev/null @@ -1,22 +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 - -#define MAX_PATH_SLOTS 256 - -#define COMMON_ARGS_COUNT 6 - -#define PACKED_FUNCTION_NAME_PREFIX "__serene_" - -// Should we build the support for MLIR CL OPTIONS? -#cmakedefine SERENE_WITH_MLIR_CL_OPTION - -#endif diff --git a/libserene/include/serene/context.h b/libserene/include/serene/context.h deleted file mode 100644 index e97ed91..0000000 --- a/libserene/include/serene/context.h +++ /dev/null @@ -1,124 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_CONTEXT_H -#define SERENE_CONTEXT_H - -#include "serene/export.h" // for SERENE_EXPORT -#include "serene/options.h" // for Options - -#include // for Twine -#include // for LLVMContext -#include // for getDefaultTargetTriple -#include // for Triple - -#include // for function -#include // for make_unique, unique_ptr -#include // for basic_string, string -#include // for vector - -namespace serene { -class SereneContext; - -/// 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 -/// This function is only meant to be used in the compiler context -/// if you want to terminate the process in context of a serene program -/// via the JIT use an appropriate function in the `serene.core` ns. -SERENE_EXPORT void terminate(SereneContext &ctx, int exitCode); - -class SERENE_EXPORT SereneContext { - -public: - template - using CurrentNSFn = std::function; - - /// The set of options to change the compilers behaivoirs - Options opts; - - const llvm::Triple triple; - - explicit SereneContext(Options &options) - : opts(options), triple(llvm::sys::getDefaultTargetTriple()), - targetPhase(CompilationPhase::NoOptimization){}; - - /// 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(); - - static std::unique_ptr genLLVMContext() { - return std::make_unique(); - }; - - /// Setup the load path for namespace lookups - void setLoadPaths(std::vector &dirs) { loadPaths.swap(dirs); }; - - /// Return the load paths for namespaces - std::vector &getLoadPaths() { return loadPaths; }; - // 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; - std::vector loadPaths; - /// 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> 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 -makeSereneContext(Options opts = Options()); - -} // namespace serene - -#endif diff --git a/libserene/include/serene/fs.h b/libserene/include/serene/fs.h deleted file mode 100644 index f4fb6f9..0000000 --- a/libserene/include/serene/fs.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_FS_H -#define SERENE_FS_H - -#include // for StringRef - -#include // for string - -#define MAX_PATH_SLOTS 256 - -namespace serene { -class SereneContext; -}; // namespace serene - -namespace serene::fs { - -enum class NSFileType { - Source = 0, - TextIR, - BinaryIR, - ObjectFile, - StaticLib, - SharedLib -}; - -std::string extensionFor(SereneContext &ctx, NSFileType t); -/// Converts the given namespace name `nsName` to the file name -/// for that name space. E.g, `some.random.ns` will be translated -/// to `some_random_ns`. -std::string namespaceToPath(llvm::StringRef nsName); - -bool isStaticLib(llvm::StringRef path); -bool isSharedLib(llvm::StringRef path); - -/// Return a boolean indicating whether or not the given path exists. -bool exists(llvm::StringRef path); -/// Join the given `path1` and `path2` with respect to the platform -/// conventions. -std::string join(llvm::StringRef path1, llvm::StringRef path2); - -}; // namespace serene::fs -#endif diff --git a/libserene/include/serene/jit/README.org b/libserene/include/serene/jit/README.org deleted file mode 100644 index a2b2525..0000000 --- a/libserene/include/serene/jit/README.org +++ /dev/null @@ -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. diff --git a/libserene/include/serene/jit/halley.h b/libserene/include/serene/jit/halley.h deleted file mode 100644 index 0c66560..0000000 --- a/libserene/include/serene/jit/halley.h +++ /dev/null @@ -1,249 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -/** - * Commentary: - This is the first working attempt on building a JIT engine for Serene - and named after Edmond Halley. - - - 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. - */ - -// TODO: [jit] When we want to load any static or dynamic lib for -// namespace as a dependency first look up the `ExecutionSession` -// to make sure that we did not load it already. If we did just -// use the existing `JITDylib` for it. - -// TODO: [jit] Use Bare JITDylibs for the static and dynamic libs. -// Hint: Look at `createBareJITDylib` on the `ExecutionSession` - -// TODO: [jit] Create a generator class that generates symbols -// from Serene's code and add them to the JitDylib of a namespace -// instead of having multiple jitDylibs per NS - -// TODO: [jit] Create a another overload of the `lookup` function -// than returns a pointer to the symbol. This new functions will -// be used to call those core functions that we already know the -// signature and we don't want to wrap them. For examples functions -// in the `serene.core` namespace - -#ifndef SERENE_JIT_HALLEY_H -#define SERENE_JIT_HALLEY_H - -#include "serene/context.h" // for Serene... -#include "serene/export.h" // for SERENE... - -#include // for Mutabl... -#include // for SmallV... -#include // for StringMap -#include // for StringRef -#include // for Object... -#include // for JITTar... -#include // for LLJIT -#include // for dbgs -#include // for Error -#include // for Memory... -#include // for Memory... -#include // for raw_os... - -#include // for unique... -#include // for nullopt -#include // for size_t -#include // for string -#include // for vector - -namespace llvm { -class DataLayout; -class JITEventListener; -class Module; -namespace orc { -class JITDylib; -} // namespace orc -} // namespace llvm - -namespace serene { -namespace fs { -enum class NSFileType; -} // namespace fs -namespace types { -struct Namespace; -struct Symbol; -struct InternalString; -} // namespace types -} // namespace serene - -#define HALLEY_LOG(...) \ - DEBUG_WITH_TYPE("halley", llvm::dbgs() \ - << "[HALLEY]: " << __VA_ARGS__ << "\n"); - -#define MAIN_PROCESS_JD_NAME "" - -namespace serene { -namespace jit { -class Halley; -// Why? This is the lazy man's way to make it easier to replace -// the class under the hood later on to test different implementaion -// with the same interface -using Engine = Halley; -using EnginePtr = std::unique_ptr; -using MaybeEngine = llvm::Expected; -using JitWrappedAddress = void (*)(void **); -using MaybeJitAddress = llvm::Expected; -using Dylib = llvm::orc::JITDylib; -using DylibPtr = Dylib *; -using MaybeDylibPtr = llvm::Expected; -using MaybeNSFileTypeArr = std::optional>; - -/// 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 getObject(const llvm::Module *m) override; - - /// Dump cached object to output file `filename`. - void dumpToObjectFile(llvm::StringRef filename); - -private: - llvm::StringMap> cachedObjects; -}; - -class SERENE_EXPORT Halley { - // TODO: Replace this with a variant of LLJIT and LLLazyJIT - std::unique_ptr engine; - std::unique_ptr cache; - /// GDB notification listener. - llvm::JITEventListener *gdbListener; - /// Perf notification listener. - llvm::JITEventListener *perfListener; - llvm::orc::JITTargetMachineBuilder jtmb; - // TODO: [cleanup][jit] Since we can access to the data layout via - // `engine.getDataLayout`, remove this attribute and it's usecases - llvm::DataLayout &dl; - // /TODO - - std::unique_ptr ctx; - bool isLazy = false; - - // TODO: [jit] Replace this vector with a thread safe time-optimized - // datastructure that is capable of indexing strings and own all - // the strings. A lockless algorithm would be even better - - /// Owns all the internal strings used in the compilation process - std::vector stringStorage; - - std::vector nsStorage; - // /TODO - - // JIT JITDylib related functions --- - llvm::StringMap> jitDylibs; - - /// Register the given pointer to a `JITDylib` \p l, with the give \p ns. - void pushJITDylib(types::Namespace &ns, llvm::orc::JITDylib *l); - - // /// Returns the number of registered `JITDylib` for the given \p ns. - size_t getNumberOfJITDylibs(types::Namespace &ns); - - types::Namespace &makeNamespace(const char *name); - - // ========================================================================== - // Loading namespaces from different sources like source files, objectfiles - // etc - // ========================================================================== - struct NSLoadRequest { - llvm::StringRef nsName; - llvm::StringRef path; - std::string &nsToFileName; - }; - - /// This function loads the namespace by the given `nsName` from the file - /// in the given `path`. It assumes that the `path` exists. - MaybeDylibPtr loadNamespaceFrom(fs::NSFileType type_, NSLoadRequest &req); - - template - MaybeDylibPtr loadNamespaceFrom(NSLoadRequest &req); - // ========================================================================== - - std::vector getContainedNamespaces(llvm::StringRef name, - DylibPtr jd); - - llvm::Error createCurrentProcessJD(); - -public: - Halley(std::unique_ptr ctx, - llvm::orc::JITTargetMachineBuilder &&jtmb, llvm::DataLayout &&dl); - - // TODO: [jit] Create a function to "require" a namespace as a dependency. - // If the namespace already exists return it otherwise call `loadNamespace`. - - /// Load a namespace by exploring the load paths and different file - /// formats to find the namespace. We assume that we want to load - /// the namespace from file even if it exists already. - MaybeDylibPtr loadNamespace(std::string &nsName); - - // TODO: [jit] Move all the loader related functions to a Loader class - /// Load the shared library in the given `path` to the given JITDylib - /// `jd` via the give ExecutionSession `es`. - /// This function assumes that the shared lib exists. - MaybeDylibPtr loadSharedLibFile(llvm::StringRef name, llvm::StringRef path); - MaybeDylibPtr loadStaticLibrary(const std::string &name); - MaybeDylibPtr loadSharedLibrary(const std::string &name); - // /TODO - - static MaybeEngine make(std::unique_ptr sereneCtxPtr, - llvm::orc::JITTargetMachineBuilder &&jtmb); - - SereneContext &getContext() { return *ctx; }; - - llvm::Error createEmptyNS(const char *name); - const types::InternalString &getInternalString(const char *s); - - /// Return a pointer to the most registered JITDylib of the given \p ns - ////name - llvm::orc::JITDylib *getLatestJITDylib(const types::Namespace &ns); - llvm::orc::JITDylib *getLatestJITDylib(const char *nsName); - - void setEngine(std::unique_ptr 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. - MaybeJitAddress lookup(const char *nsName, const char *sym) const; - MaybeJitAddress lookup(const types::Symbol &sym) const; - - /// Invokes the function with the given name passing it the list of opaque - /// pointers to the actual arguments. - llvm::Error - invokePacked(const types::Symbol &name, - llvm::MutableArrayRef args = std::nullopt) const; - - llvm::Error loadModule(const char *nsName, const char *file); - void dumpToObjectFile(llvm::StringRef filename); -}; - -MaybeEngine makeHalleyJIT(std::unique_ptr ctx); - -} // namespace jit -} // namespace serene - -#endif diff --git a/libserene/include/serene/jit/packer.h b/libserene/include/serene/jit/packer.h deleted file mode 100644 index 1a83328..0000000 --- a/libserene/include/serene/jit/packer.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_JIT_PACKER_H -#define SERENE_JIT_PACKER_H - -#include // for SmallVectorImpl -#include // for StringRef - -#include // for string - -namespace llvm { -class Module; -} - -namespace serene::jit { - -struct Packer { - /// Trait that defines how a given type is passed to the JIT code. This - /// defaults to passing the address but can be specialized. - template - struct Argument { - static void pack(llvm::SmallVectorImpl &args, T &val) { - args.push_back(&val); - } - }; - - /// Tag to wrap an output parameter when invoking a jitted function. - template - struct FnResult { - explicit FnResult(T &result) : value(result) {} - T &value; - }; - - /// Helper function to wrap an output operand when using - /// ExecutionEngine::invoke. - template - static FnResult result(T &t) { - return FnResult(t); - } - - // Specialization for output parameter: their address is forwarded directly to - // the native code. - template - struct Argument> { - static void pack(llvm::SmallVectorImpl &args, FnResult &result) { - args.push_back(&result.value); - } - }; -}; - -std::string makePackedFunctionName(llvm::StringRef name); -void packFunctionArguments(llvm::Module *module); - -} // namespace serene::jit -#endif diff --git a/libserene/include/serene/options.h b/libserene/include/serene/options.h deleted file mode 100644 index a75c30e..0000000 --- a/libserene/include/serene/options.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_OPTIONS_H -#define SERENE_OPTIONS_H - -#include "serene/export.h" - -namespace serene { -/// 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; - - // namespace serene Options() = default; -}; -} // namespace serene - -#endif diff --git a/libserene/include/serene/serene.h b/libserene/include/serene/serene.h deleted file mode 100644 index a6bd7be..0000000 --- a/libserene/include/serene/serene.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_SERENE_H -#define SERENE_SERENE_H - -#include "serene/export.h" // for SERENE_EXPORT -#include "serene/jit/halley.h" // for Engine, MaybeEngine -#include "serene/options.h" // for Options - -#include // for GC_INIT - -#define SERENE_INIT() \ - GC_INIT(); \ - initSerene(); - -namespace serene { - -/// Clinet applications have to call this function before any interaction -/// with the Serene's compiler API. -void SERENE_EXPORT initSerene(); - -/// 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`. -void SERENE_EXPORT registerSereneCLOptions(); - -/// Applies the global compiler options on the give \p SereneContext. This -/// function has to be called after `llvm::cl::ParseCommandLineOptions`. -void SERENE_EXPORT applySereneCLOptions(serene::jit::Engine &engine); - -serene::jit::MaybeEngine SERENE_EXPORT makeEngine(Options opts = Options()); - -} // namespace serene -#endif diff --git a/libserene/include/serene/types/types.h b/libserene/include/serene/types/types.h deleted file mode 100644 index 9a2ab3b..0000000 --- a/libserene/include/serene/types/types.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#ifndef SERENE_TYPES_TYPE_H -#define SERENE_TYPES_TYPE_H - -#include -namespace serene::types { - -// ============================================================================ -// Expression -// ============================================================================ -struct Expression { - const unsigned char *data; - explicit Expression(const unsigned char *data) : data(data){}; -}; - -// ============================================================================ -// Internal String -// ============================================================================ - -/// Internal string represts a smaller type of string with limited set of -/// functionalities that we use only for internal usage -struct InternalString { - // We store the actual string in a "string" data section - const char *data; - size_t len; - - InternalString(const char *data, const unsigned int len) - : data(data), len(len){}; -}; - -// ============================================================================ -// Symbol -// ============================================================================ -struct Symbol { - const InternalString *ns; - const InternalString *name; - - Symbol(const InternalString *ns, const InternalString *name) - : ns(ns), name(name){}; -}; - -// ============================================================================ -// Namespace -// ============================================================================ -struct Namespace { - const InternalString *name; - - explicit Namespace(const InternalString *name) : name(name){}; -}; - -}; // namespace serene::types - -#endif diff --git a/libserene/lib/CMakeLists.txt b/libserene/lib/CMakeLists.txt deleted file mode 100644 index 6ab49f7..0000000 --- a/libserene/lib/CMakeLists.txt +++ /dev/null @@ -1,82 +0,0 @@ -# Serene Programming Language -# -# Copyright (c) 2019-2023 Sameer Rahmani -# -# 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 . - -#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 - serene.cpp - context.cpp - fs.cpp - - jit/halley.cpp - jit/packer.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 - CXX_INCLUDE_WHAT_YOU_USE "${iwyu_path}" - C_INCLUDE_WHAT_YOU_USE "${iwyu_path}" - - # LTO support - INTERPROCEDURAL_OPTIMIZATION TRUE) - -target_compile_options(serene - PRIVATE) - -target_link_options(serene - PRIVATE) - -if(SERENE_ENABLE_TIDY) - set_target_properties(serene PROPERTIES CXX_CLANG_TIDY ${CLANG_TIDY_PATH}) -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) - -# We need this directory, and users of our library will need it too -target_include_directories(serene PUBLIC "$") -target_include_directories(serene PUBLIC "$") - -# 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 "$<$>:SERENE_STATIC_DEFINE>") - - -target_link_libraries(serene PRIVATE ${llvm_libs} BDWgc::gc) -llvm_update_compile_flags(serene) diff --git a/libserene/lib/context.cpp b/libserene/lib/context.cpp deleted file mode 100644 index 61be7eb..0000000 --- a/libserene/lib/context.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/context.h" - -#include // for exit - -namespace serene { - -int SereneContext::getOptimizatioLevel() { - if (targetPhase <= CompilationPhase::NoOptimization) { - return 0; - } - - if (targetPhase == CompilationPhase::O1) { - return 1; - } - if (targetPhase == CompilationPhase::O2) { - return 2; - } - return 3; -} - -void terminate(SereneContext &ctx, int exitCode) { - (void)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 makeSereneContext(Options opts) { - return std::make_unique(opts); -}; - -}; // namespace serene diff --git a/libserene/lib/fs.cpp b/libserene/lib/fs.cpp deleted file mode 100644 index de80a3c..0000000 --- a/libserene/lib/fs.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/fs.h" - -#include // for error_code - -#include // for SmallString -#include // for Twine -#include // for file_magic, identify_magic -#include // for exists, status, file_status -#include // for native, get_separator - -#include // for replace - -namespace serene::fs { - -std::string extensionFor(SereneContext &ctx, NSFileType t) { - // TODO: [fs] Create a mapping from OS type and NSFileTypes and - // append the appropriate extension to the path. For now - // only few that we need are enough. - (void)ctx; - switch (t) { - case NSFileType::Source: - return ".srn"; - case NSFileType::TextIR: - return ".ll"; - break; - case NSFileType::StaticLib: - return ".a"; - break; - case NSFileType::SharedLib: - return ".so"; - break; - - default: - // TODO: This is temporary, remove this when created - // the mapping - return ".unknown"; - }; -}; - -/// Converts the given namespace name `nsName` to the file name -/// for that name space. E.g, `some.random.ns` will be translated -/// to `some_random_ns`. -std::string namespaceToPath(llvm::StringRef nsName) { - // TODO: [fs][perf] This function is not efficient. Fix it - std::string nsNameCopy = nsName.str(); - std::replace(nsNameCopy.begin(), nsNameCopy.end(), '.', '/'); - - llvm::SmallString path; - path.append(nsNameCopy); - llvm::sys::path::native(path); - - return std::string(path); -}; - -bool isStaticLib(llvm::StringRef path) { - llvm::file_magic magic; - // llvm::identify_magic returns an error code on failure - if (llvm::identify_magic(path, magic)) { - // If there was an error loading the file then skip it. - return false; - } - - return (magic == llvm::file_magic::archive || - magic == llvm::file_magic::macho_universal_binary); -}; - -bool isSharedLib(llvm::StringRef path) { - llvm::file_magic magic; - // llvm::identify_magic returns an error code on failure - if (llvm::identify_magic(path, magic)) { - // If there was an error loading the file then skip it. - return false; - } - - return (magic == llvm::file_magic::macho_dynamically_linked_shared_lib || - magic == llvm::file_magic::elf_shared_object); -}; - -/// Return a boolean indicating whether or not the given path exists. -bool exists(llvm::StringRef path) { - llvm::sys::fs::file_status status; - auto err = llvm::sys::fs::status(path, status); - - if (err) { - return false; - }; - - return llvm::sys::fs::exists(status); -} - -/// Join the given `path1` and `path2` with respect to the platform -/// conventions. -std::string join(llvm::StringRef path1, llvm::StringRef path2) { - llvm::SmallString path; - path.append(path1); - path.append(llvm::sys::path::get_separator().data()); - path.append(path2); - llvm::sys::path::native(path); - return std::string(path); -}; -}; // namespace serene::fs diff --git a/libserene/lib/jit/halley.cpp b/libserene/lib/jit/halley.cpp deleted file mode 100644 index 2957438..0000000 --- a/libserene/lib/jit/halley.cpp +++ /dev/null @@ -1,867 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/jit/halley.h" - -#include "serene/context.h" -#include "serene/fs.h" -#include "serene/options.h" -#include "serene/types/types.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define COMMON_ARGS_COUNT 8 - -namespace serene { - -namespace jit { - -// TODO: [error] Replace this function when we implemented -// the error subsystem with the official implementation -llvm::Error tempError(SereneContext &ctx, llvm::Twine s) { - (void)ctx; - return llvm::make_error( - std::make_error_code(std::errc::executable_format_error), - "[Error]: " + s); -}; -// /TODO - -void ObjectCache::notifyObjectCompiled(const llvm::Module *m, - llvm::MemoryBufferRef objBuffer) { - cachedObjects[m->getModuleIdentifier()] = - llvm::MemoryBuffer::getMemBufferCopy(objBuffer.getBuffer(), - objBuffer.getBufferIdentifier()); -} - -std::unique_ptr -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::error_code error; - - auto file = std::make_unique(outputFilename, error, - llvm::sys::fs::OF_None); - if (error) { - - llvm::errs() << "cannot open output file '" + outputFilename.str() + - "': " + error.message() - << "\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(); -} - -llvm::orc::JITDylib *Halley::getLatestJITDylib(const types::Namespace &ns) { - return getLatestJITDylib(ns.name->data); -}; - -llvm::orc::JITDylib *Halley::getLatestJITDylib(const char *nsName) { - if (jitDylibs.count(nsName) == 0) { - return nullptr; - } - - auto vec = jitDylibs[nsName]; - // 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 Halley::pushJITDylib(types::Namespace &ns, llvm::orc::JITDylib *l) { - if (jitDylibs.count(ns.name->data) == 0) { - llvm::SmallVector vec{l}; - jitDylibs[ns.name->data] = vec; - return; - } - auto vec = jitDylibs[ns.name->data]; - vec.push_back(l); - jitDylibs[ns.name->data] = vec; -} - -size_t Halley::getNumberOfJITDylibs(types::Namespace &ns) { - if (jitDylibs.count(ns.name->data) == 0) { - return 0; - } - - return jitDylibs[ns.name->data].size(); -}; - -Halley::Halley(std::unique_ptr 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(std::move(ctx)){}; - -// 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(rawFPtr); - -// if (fptr == nullptr) { -// return errors::makeError(ctx, errors::CantResolveSymbol, sym.location, -// "Lookup function is null!"); -// } - -// return fptr; -// }; - -void Halley::setEngine(std::unique_ptr e, bool isLazy) { - // Later on we might use different classes of JIT which might need some - // work for lazyness - (void)ctx; - engine = std::move(e); - this->isLazy = isLazy; -}; - -void Halley::dumpToObjectFile(llvm::StringRef filename) { - cache->dumpToObjectFile(filename); -}; - -MaybeEngine Halley::make(std::unique_ptr sereneCtxPtr, - llvm::orc::JITTargetMachineBuilder &&jtmb) { - auto dl = jtmb.getDefaultDataLayoutForTarget(); - if (!dl) { - return dl.takeError(); - } - - auto jitEngine = std::make_unique(std::move(sereneCtxPtr), - 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 ctx(new llvm::LLVMContext); - - // Since we moved the original sereneCtxPtr into the engine. - auto &sereneCtx = jitEngine->getContext(); - - // 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) { - (void)tt; - - auto objectLayer = - std::make_unique(session, []() { - return std::make_unique(); - }); - - // 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 (sereneCtx.triple.isOSBinFormatCOFF()) { - objectLayer->setOverrideObjectFlagsWithResponsibilityFlags(true); - objectLayer->setAutoClaimResponsibilityForObjectSymbols(true); - } - - 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::CodeGenOpt::Level jitCodeGenOptLevel = - static_cast(sereneCtx.getOptimizatioLevel()); - - JTMB.setCodeGenOptLevel(jitCodeGenOptLevel); - - auto targetMachine = JTMB.createTargetMachine(); - if (!targetMachine) { - return targetMachine.takeError(); - } - - return std::make_unique( - std::move(*targetMachine), jitEngine->cache.get()); - }; - - // TODO: [jit] This is not a proper way to handle both engines. - // Create two different classes for different execution modes - // (`lazy` vs `eager`) with the same interface and use them - // where appropriate. - if (sereneCtx.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), sereneCtx.opts.JITLazy); - } 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), sereneCtx.opts.JITLazy); - } - // /TODO - - 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()); - }); - }); - - if (auto err = jitEngine->createCurrentProcessJD()) { - return err; - } - - return MaybeEngine(std::move(jitEngine)); -}; - -const types::InternalString &Halley::getInternalString(const char *s) { - // TODO: [serene.core] We need to provide some functions on llvm level to - // build instances from these type in a functional way. We need to avoid - // randomly build instances here and there that causes unsafe memory - assert(s && "s is nullptr: getInternalString"); - auto len = std::strlen(s); - - auto *str = static_cast( - GC_MALLOC(sizeof(types::InternalString))); - - // str->data = (char *)GC_MALLOC_ATOMIC(len); - // memcpy((void *)str->data, (void *)s, len); - memcpy(static_cast(str), static_cast(s), len); - str->len = len; - - stringStorage.push_back(str); - return *str; - // /TODO -}; - -types::Namespace &Halley::makeNamespace(const char *name) { - // TODO: [serene.core] We need to provide some functions on llvm level to - // build instances from these type in a functional way. We need to avoid - // randomly build instances here and there that causes unsafe memory - assert(name && "name is nullptr: createNamespace"); - const auto &nsName = getInternalString(name); - auto *ns = - static_cast(GC_MALLOC(sizeof(types::Namespace))); - ns->name = &nsName; - - nsStorage.push_back(ns); - return *ns; - // /TODO -}; - -llvm::Error Halley::createEmptyNS(const char *name) { - assert(name && "name is nullptr: createEmptyNS"); - // TODO: Look up the Namespace first. - auto &ns = makeNamespace(name); - auto numOfDylibs = getNumberOfJITDylibs(ns) + 1; - - HALLEY_LOG( - llvm::formatv("Creating Dylib {0}#{1}", ns.name->data, numOfDylibs)); - - auto newDylib = engine->createJITDylib( - llvm::formatv("{0}#{1}", ns.name->data, numOfDylibs)); - - if (!newDylib) { - llvm::errs() << "Couldn't create the jitDylib\n"; - serene::terminate(*ctx, 1); - } - - pushJITDylib(ns, &(*newDylib)); - return llvm::Error::success(); -}; - -MaybeJitAddress Halley::lookup(const types::Symbol &sym) const { - return lookup(sym.ns->data, sym.name->data); -} - -MaybeJitAddress Halley::lookup(const char *nsName, const char *sym) const { - assert(sym != nullptr && "'sym' is null: lookup"); - assert(nsName != nullptr && "'nsName' is null: lookup"); - - llvm::StringRef s{sym}; - llvm::StringRef ns{nsName}; - - std::string fqsym = (ns + "/" + s).str(); - - HALLEY_LOG("Looking up symbol: " << fqsym); - auto *dylib = const_cast(this)->jitDylibs[nsName].back(); - - if (dylib == nullptr) { - return tempError(*ctx, "No dylib " + s); - } - - HALLEY_LOG("Looking in dylib: " << static_cast(dylib)); - auto expectedSymbol = engine->lookup(*dylib, fqsym); - - // 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) { - return expectedSymbol.takeError(); - } - - auto fptr = expectedSymbol->toPtr(); - - if (fptr == nullptr) { - return tempError(*ctx, "Lookup function is null!"); - } - - HALLEY_LOG("Found symbol '" << fqsym << "' at " - << static_cast(&fptr)); - return fptr; -}; - -// TODO: Remove this function before prod release -llvm::Error Halley::loadModule(const char *nsName, const char *file) { - assert(file && "'file' is nullptr: loadModule"); - assert(nsName && "'nsName' is nullptr: loadModule"); - - auto llvmContext = ctx->genLLVMContext(); - llvm::SMDiagnostic error; - - auto *dylib = jitDylibs[nsName].back(); - - auto module = llvm::parseIRFile(file, error, *llvmContext); - - if (module == nullptr) { - return llvm::make_error( - std::make_error_code(std::errc::executable_format_error), - error.getMessage().str() + " File: " + file); - } - - auto tsm = - llvm::orc::ThreadSafeModule(std::move(module), std::move(llvmContext)); - - return engine->addIRModule(*dylib, std::move(tsm)); -}; -// /TODO - -// TODO: [error] Remove this function when we implemented -// the error subsystem -llvm::Error NotImplemented(llvm::StringRef s) { - return llvm::make_error( - std::make_error_code(std::errc::executable_format_error), - "Not Implemented: " + s); -}; - -template <> -MaybeDylibPtr -Halley::loadNamespaceFrom(NSLoadRequest &req) { - (void)req; - return nullptr; -}; - -template <> -MaybeDylibPtr -Halley::loadNamespaceFrom(NSLoadRequest &req) { - (void)req; - return nullptr; -}; - -template <> -MaybeDylibPtr -Halley::loadNamespaceFrom(NSLoadRequest &req) { - (void)req; - return nullptr; -}; - -template <> -MaybeDylibPtr -Halley::loadNamespaceFrom(NSLoadRequest &req) { - - auto file = fs::join(req.path, req.nsToFileName + ".o"); - - if (!fs::exists(file)) { - // Can't locate any object file, skit to the next loader - HALLEY_LOG("File does not exist: " << file << "\n"); - return nullptr; - } - - auto err = createEmptyNS(req.nsName.str().c_str()); - if (err) { - return err; - } - - auto *jd = getLatestJITDylib(req.nsName.str().c_str()); - assert(jd != nullptr && "'jd' must not be null since we just created it."); - - auto buf = llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(file)); - if (!buf) { - return buf.takeError(); - } - - err = engine->getObjLinkingLayer().add(*jd, std::move(*buf)); - - if (err) { - return err; - } - - return jd; -}; - -template <> -MaybeDylibPtr -Halley::loadNamespaceFrom(NSLoadRequest &req) { - - // Skip missing or non-regular paths. - if (llvm::sys::fs::get_file_type(req.path) != - llvm::sys::fs::file_type::regular_file) { - return tempError(*ctx, "Not a regular file: " + req.path); - } - - llvm::file_magic magic; - if (auto ec = llvm::identify_magic(req.path, magic)) { - // If there was an error loading the file then skip it. - return tempError(*ctx, - ec.message() + "\nFile Identification Erorr: " + req.path); - } - - if (magic != llvm::file_magic::archive || - magic != llvm::file_magic::macho_universal_binary) { - return tempError(*ctx, "Not a static lib: " + req.path); - } - - auto err = createEmptyNS(req.nsName.str().c_str()); - if (err) { - return err; - } - - auto &session = engine->getExecutionSession(); - auto *jd = getLatestJITDylib(req.nsName.str().c_str()); - assert(jd == nullptr && "'jd' must not be null since we just created it."); - - // TODO: Handle hidden static libs as well look at the addLibrary/AddArchive - // in llvm-jitlink - - auto generator = llvm::orc::StaticLibraryDefinitionGenerator::Load( - engine->getObjLinkingLayer(), req.path.str().c_str(), - session.getExecutorProcessControl().getTargetTriple(), - std::move(llvm::orc::getObjectFileInterface)); - - if (!generator) { - return generator.takeError(); - } - - jd->addGenerator(std::move(*generator)); - return jd; -}; - -template <> -MaybeDylibPtr -Halley::loadNamespaceFrom(NSLoadRequest &req) { - (void)req; - // switch (magic) { - // case llvm::file_magic::elf_shared_object: - // case llvm::file_magic::macho_dynamically_linked_shared_lib: { - // // TODO: On first reference to LibPath this should create a JITDylib - // // with a generator and add it to JD's links-against list. Subsquent - // // references should use the JITDylib created on the first - // // reference. - // auto g = llvm::EPCDynamicLibrarySearchGenerator::Load(session, path); - // if (!g) - // return g.takeError(); - // jd.addGenerator(std::move(*g)); - // break; - // } - // case llvm::file_magic::archive: - // case llvm::file_magic::macho_universal_binary: { - // } - // default: - // // This file isn't a recognized library kind. - // LibFound = false; - // break; - // } - - return NotImplemented("loadNamespaceFrom"); -}; - -MaybeDylibPtr Halley::loadNamespaceFrom(fs::NSFileType type_, - NSLoadRequest &req) { - switch (type_) { - case fs::NSFileType::Source: - return loadNamespaceFrom(req); - case fs::NSFileType::TextIR: - return loadNamespaceFrom(req); - case fs::NSFileType::BinaryIR: - return loadNamespaceFrom(req); - case fs::NSFileType::ObjectFile: - return loadNamespaceFrom(req); - case fs::NSFileType::StaticLib: - case fs::NSFileType::SharedLib: - return loadNamespaceFrom(req); - }; - llvm_unreachable("Halley::loadNamespaceFrom unhandled NSFileType"); -}; - -MaybeDylibPtr Halley::loadNamespace(std::string &nsName) { - if (ctx->getLoadPaths().empty()) { - return tempError(*ctx, "Load paths should not be empty"); - } - - for (auto &path : ctx->getLoadPaths()) { - auto nsFileName = fs::namespaceToPath(nsName); - NSLoadRequest req{nsName, path, nsFileName}; - - for (auto type_ : {fs::NSFileType::Source, fs::NSFileType::ObjectFile}) { - auto maybeJDptr = loadNamespaceFrom(type_, req); - - if (!maybeJDptr) { - return maybeJDptr.takeError(); - } - - if (*maybeJDptr != nullptr) { - auto *processJD = engine->getExecutionSession().getJITDylibByName( - MAIN_PROCESS_JD_NAME); - - if (processJD == nullptr) { - // TODO: [jit] Panic here - return tempError(*ctx, "Can't find the main process JD"); - // /TODO - } - - (*maybeJDptr)->addToLinkOrder(*processJD); - return *maybeJDptr; - } - } - } - - return tempError(*ctx, "Can't find namespace: " + nsName); -}; - -MaybeDylibPtr Halley::loadStaticLibrary(const std::string &name) { - if (ctx->getLoadPaths().empty()) { - return tempError(*ctx, "Load paths should not be empty"); - } - - for (auto &path : ctx->getLoadPaths()) { - auto file = fs::join(path, name + ".a"); - - if (!fs::exists(file)) { - continue; - } - - if (!fs::isStaticLib(file)) { - return tempError(*ctx, "Not a static lib: " + file); - } - - auto *objectLayer = &engine->getObjLinkingLayer(); - - auto generator = llvm::orc::StaticLibraryDefinitionGenerator::Load( - *objectLayer, file.c_str(), - engine->getExecutionSession() - .getExecutorProcessControl() - .getTargetTriple(), - std::move(llvm::orc::getObjectFileInterface)); - - if (!generator) { - return generator.takeError(); - } - - auto jd = engine->createJITDylib(llvm::formatv("{0}#{1}", name, 0)); - - if (!jd) { - return jd.takeError(); - } - - jd->addGenerator(std::move(*generator)); - - std::vector nsNames = {name}; - - auto definition = engine->lookup(*jd, "__serene_namespaces"); - - if (!definition) { - HALLEY_LOG("Library '" << name << "' is not a Serene lib."); - // We just want to ignore the error - llvm::consumeError(definition.takeError()); - } else { - HALLEY_LOG("Library '" << name << "' is a Serene lib."); - // TODO: call the __serene_namespaces and set nsNames to - // the list of namespaces that it returns - (void)*definition; - } - - for (auto &nsName : nsNames) { - auto ns = makeNamespace(nsName.str().c_str()); - pushJITDylib(ns, &(*jd)); - } - - return &jd.get(); - } - - return tempError(*ctx, "Can't find static lib: " + name); -}; - -MaybeDylibPtr Halley::loadSharedLibFile(llvm::StringRef name, - llvm::StringRef path) { - if (!fs::isSharedLib(path)) { - return tempError(*ctx, "Not a shared lib: " + path); - } - - auto generator = llvm::orc::EPCDynamicLibrarySearchGenerator::Load( - engine->getExecutionSession(), path.str().c_str()); - - if (!generator) { - return generator.takeError(); - } - - auto jd = engine->createJITDylib(llvm::formatv("{0}#{1}", name, 0)); - - if (!jd) { - return jd.takeError(); - } - - jd->addGenerator(std::move(*generator)); - - return &jd.get(); -}; - -MaybeDylibPtr Halley::loadSharedLibrary(const std::string &name) { - if (ctx->getLoadPaths().empty()) { - return tempError(*ctx, "Load paths should not be empty"); - } - - for (auto &path : ctx->getLoadPaths()) { - auto file = fs::join(path, name + ".so"); - - if (!fs::exists(file)) { - continue; - } - - auto maybeJD = loadSharedLibFile(name, file); - if (!maybeJD) { - return maybeJD.takeError(); - } - - auto *jd = *maybeJD; - auto nsNames = getContainedNamespaces(name, jd); - - for (const auto *nsName : nsNames) { - auto ns = makeNamespace(nsName); - pushJITDylib(ns, jd); - } - - return jd; - } - - return tempError(*ctx, "Can't find the dynamic lib: " + name); -}; - -std::vector Halley::getContainedNamespaces(llvm::StringRef name, - DylibPtr jd) { - - std::vector nsNames = {name.str().c_str()}; - auto definition = engine->lookup(*jd, "__serene_namespaces"); - - if (!definition) { - HALLEY_LOG("Library is not a Serene lib."); - // We just want to ignore the error - llvm::consumeError(definition.takeError()); - } - HALLEY_LOG("Library is a Serene lib."); - // TODO: call the __serene_namespaces and set nsNames to - // the list of namespaces that it returns - - return nsNames; -}; - -llvm::Error Halley::createCurrentProcessJD() { - - auto &es = engine->getExecutionSession(); - auto *processJDPtr = es.getJITDylibByName(MAIN_PROCESS_JD_NAME); - - if (processJDPtr != nullptr) { - // We already created the JITDylib for the current process - return llvm::Error::success(); - } - - auto processJD = es.createJITDylib(MAIN_PROCESS_JD_NAME); - - if (!processJD) { - return processJD.takeError(); - } - - auto generator = - llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( - engine->getDataLayout().getGlobalPrefix()); - - if (!generator) { - return generator.takeError(); - } - - processJD->addGenerator(std::move(*generator)); - return llvm::Error::success(); -}; - -llvm::Error Halley::invokePacked(const types::Symbol &name, - llvm::MutableArrayRef args) const { - auto expectedFPtr = lookup(name); - if (!expectedFPtr) { - return expectedFPtr.takeError(); - } - - auto *fptr = *expectedFPtr; - (*fptr)(args.data()); - - return llvm::Error::success(); -} - -MaybeEngine makeHalleyJIT(std::unique_ptr ctx) { - llvm::orc::JITTargetMachineBuilder jtmb(ctx->triple); - auto maybeJIT = Halley::make(std::move(ctx), std::move(jtmb)); - if (!maybeJIT) { - return maybeJIT.takeError(); - } - - return maybeJIT; -}; - -} // namespace jit -} // namespace serene diff --git a/libserene/lib/jit/packer.cpp b/libserene/lib/jit/packer.cpp deleted file mode 100644 index bfd7a59..0000000 --- a/libserene/lib/jit/packer.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ - -#include "serene/jit/packer.h" - -#include "serene/config.h" // for I64_SIZE, COMMON_ARGS_COUNT - -#include // for APInt -#include // for DenseSet -#include // for size, enumerate, enumerator -#include // for operator!=, ilist_iterator -#include // for iterator_facade_base -#include // for iterator_range -#include // for Argument -#include // for BasicBlock -#include // for Constant -#include // for PointerType, IntegerType, Funct... -#include // for Function -#include // for IRBuilder -#include // for LoadInst, CallInst -#include // for Module -#include // for Type -#include // for Value -#include // for cast - -#include // for uint64_t -namespace serene::jit { - -std::string makePackedFunctionName(llvm::StringRef name) { - // TODO: move the "_serene_" constant to a macro or something - return PACKED_FUNCTION_NAME_PREFIX + name.str(); -} - -void packFunctionArguments(llvm::Module *module) { - auto &ctx = module->getContext(); - llvm::IRBuilder<> builder(ctx); - llvm::DenseSet 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(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 args; - - args.reserve(static_cast(llvm::size(func.args()))); - for (const auto &indexedArg : llvm::enumerate(func.args())) { - llvm::Value *argIndex = llvm::Constant::getIntegerValue( - builder.getInt64Ty(), llvm::APInt(I64_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_SIZE, - static_cast(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(); - } -}; - -} // namespace serene::jit diff --git a/libserene/lib/serene.cpp b/libserene/lib/serene.cpp deleted file mode 100644 index c2fbd10..0000000 --- a/libserene/lib/serene.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* -*- C++ -*- - * Serene Programming Language - * - * Copyright (c) 2019-2023 Sameer Rahmani - * - * 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 . - */ -#include "serene/serene.h" - -#include "serene/context.h" // for SereneContext, makeSereneCon... -#include "serene/jit/halley.h" // for makeHalleyJIT, Engine, Maybe... - -#include // for StringRef -#include // for list, MiscFlags, cat, desc -#include // for ManagedStatic -#include // for InitializeAllAsmParsers, Ini... - -#include // for string -#include // for move -#include // for vector - -namespace serene { -// 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 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 sharedLibraryPaths{ - "sl", llvm::cl::desc("Where to find shared libraries"), - llvm::cl::ZeroOrMore, llvm::cl::MiscFlags::PositionalEatsArgs, - llvm::cl::cat(clOptionsCategory)}; -}; - -static llvm::ManagedStatic 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(serene::jit::Engine &engine) { - if (!options.isConstructed()) { - return; - } - - // TODO: [engine] Should we move this to the engine itself? - auto &ctx = engine.getContext(); - ctx.setLoadPaths(options->loadPaths); - - // #ifdef SERENE_WITH_MLIR_CL_OPTION - // mlir::applyPassManagerCLOptions(ctx.pm); - // #endif -} - -void initSerene() { - llvm::InitializeAllTargetInfos(); - llvm::InitializeAllTargets(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllAsmParsers(); - llvm::InitializeAllAsmPrinters(); -}; - -serene::jit::MaybeEngine makeEngine(Options opts) { - auto ctx = makeSereneContext(opts); - return serene::jit::makeHalleyJIT(std::move(ctx)); -}; - -} // namespace serene