Remove the old libserene implementations
This commit is contained in:
parent
1ef8a5f0ca
commit
d54835a961
|
@ -1,81 +0,0 @@
|
|||
# Serene Programming Language
|
||||
#
|
||||
# Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
include_directories(${INCLUDE_DIR})
|
||||
|
||||
add_subdirectory(include)
|
||||
add_subdirectory(lib)
|
||||
|
||||
# Install rules for libserene target
|
||||
install(TARGETS serene
|
||||
EXPORT SereneExports
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
|
||||
# Install rules for the public header files.
|
||||
install(DIRECTORY ${INCLUDE_DIR}/serene
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
FILES_MATCHING
|
||||
PATTERN *.h
|
||||
PATTERN *.td
|
||||
PATTERN "CMake*" EXCLUDE)
|
||||
|
||||
# Install rule for the public generated header files
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
FILES_MATCHING
|
||||
PATTERN *.h
|
||||
PATTERN *.td
|
||||
PATTERN *.h.inc
|
||||
PATTERN "CMake*" EXCLUDE)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# Package config file let us use find_package with serene
|
||||
configure_package_config_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
|
||||
INSTALL_DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${PROJECT_NAME}ConfigVersion.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
|
||||
)
|
||||
|
||||
# Install the package exports
|
||||
install(EXPORT SereneExports
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
|
||||
NAMESPACE serene::)
|
||||
|
||||
# Testing only available if this is the main app
|
||||
# Emergency override SERENE_CMAKE_BUILD_TESTING provided as well
|
||||
if(SERENE_BUILD_TESTING)
|
||||
message("Build the test binary")
|
||||
add_subdirectory(tests)
|
||||
endif()
|
|
@ -1,21 +0,0 @@
|
|||
# Serene Programming Language
|
||||
#
|
||||
# Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Exports.cmake")
|
||||
|
||||
check_required_components("@PROJECT_NAME@")
|
|
@ -1,3 +0,0 @@
|
|||
add_subdirectory("serene/slir/")
|
||||
add_subdirectory("serene/passes/")
|
||||
add_subdirectory("serene/errors/")
|
|
@ -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
|
|
@ -1,285 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_CONTEXT_H
|
||||
#define SERENE_CONTEXT_H
|
||||
|
||||
#include "serene/diagnostics.h"
|
||||
#include "serene/environment.h"
|
||||
#include "serene/export.h"
|
||||
#include "serene/jit/halley.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/passes.h"
|
||||
#include "serene/slir/dialect.h"
|
||||
#include "serene/source_mgr.h"
|
||||
|
||||
#include <llvm/ADT/DenseMap.h>
|
||||
#include <llvm/ADT/None.h>
|
||||
#include <llvm/ADT/Optional.h>
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/ADT/Triple.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/Support/Host.h>
|
||||
#include <mlir/Dialect/Func/IR/FuncOps.h>
|
||||
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
|
||||
#include <mlir/IR/MLIRContext.h>
|
||||
#include <mlir/Pass/PassManager.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define DEFAULT_NS_NAME "serene.user"
|
||||
#define INTERNAL_NS "serene.internal"
|
||||
|
||||
namespace serene {
|
||||
class SereneContext;
|
||||
|
||||
namespace reader {
|
||||
class LocationRange;
|
||||
} // namespace reader
|
||||
|
||||
namespace exprs {
|
||||
class Expression;
|
||||
using Node = std::shared_ptr<Expression>;
|
||||
} // namespace exprs
|
||||
|
||||
/// This enum describes the different operational phases for the compiler
|
||||
/// in order. Anything below `NoOptimization` is considered only for debugging
|
||||
enum class CompilationPhase {
|
||||
Parse,
|
||||
Analysis,
|
||||
SLIR,
|
||||
MLIR, // Lowered slir to other dialects
|
||||
LIR, // Lowered to the llvm ir dialect
|
||||
IR, // Lowered to the LLVMIR itself
|
||||
NoOptimization,
|
||||
O1,
|
||||
O2,
|
||||
O3,
|
||||
};
|
||||
|
||||
/// Terminates the serene compiler process in a thread safe manner
|
||||
SERENE_EXPORT void terminate(SereneContext &ctx, int exitCode);
|
||||
|
||||
/// Options describes the compiler options that can be passed to the
|
||||
/// compiler via command line. Anything that user should be able to
|
||||
/// tweak about the compiler has to end up here regardless of the
|
||||
/// different subsystem that might use it.
|
||||
struct SERENE_EXPORT Options {
|
||||
|
||||
/// Whether to use colors for the output or not
|
||||
bool withColors = true;
|
||||
|
||||
// JIT related flags
|
||||
bool JITenableObjectCache = true;
|
||||
bool JITenableGDBNotificationListener = true;
|
||||
bool JITenablePerfNotificationListener = true;
|
||||
bool JITLazy = false;
|
||||
|
||||
Options() = default;
|
||||
};
|
||||
|
||||
class SERENE_EXPORT SereneContext {
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
using CurrentNSFn = std::function<T()>;
|
||||
|
||||
mlir::MLIRContext mlirContext;
|
||||
mlir::PassManager pm;
|
||||
std::unique_ptr<DiagnosticEngine> diagEngine;
|
||||
std::unique_ptr<serene::jit::Halley> jit;
|
||||
|
||||
/// The source manager is responsible for loading namespaces and practically
|
||||
/// managing the source code in form of memory buffers.
|
||||
SourceMgr sourceManager;
|
||||
|
||||
/// The set of options to change the compilers behaivoirs
|
||||
Options opts;
|
||||
|
||||
const llvm::Triple triple;
|
||||
|
||||
/// Insert the given `ns` into the context. The Context object is
|
||||
/// the owner of all the namespaces. The `ns` will overwrite any
|
||||
/// namespace with the same name.
|
||||
void insertNS(NSPtr &ns);
|
||||
|
||||
/// Execute the given function \p f by setting the `currentNS`
|
||||
/// to the given \p nsName. It will restore the value of `currentNS`
|
||||
/// after \p f returned.
|
||||
template <typename T>
|
||||
T withCurrentNS(llvm::StringRef nsName, CurrentNSFn<T> f) {
|
||||
assert(!currentNS.empty() && "The currentNS is not initialized!");
|
||||
auto tmp = this->currentNS;
|
||||
this->currentNS = nsName.str();
|
||||
|
||||
T res = f();
|
||||
this->currentNS = tmp;
|
||||
return res;
|
||||
};
|
||||
|
||||
// void specialization
|
||||
template <>
|
||||
void withCurrentNS(llvm::StringRef nsName, CurrentNSFn<void> f) {
|
||||
assert(!currentNS.empty() && "The currentNS is not initialized!");
|
||||
auto tmp = this->currentNS;
|
||||
this->currentNS = nsName.str();
|
||||
|
||||
f();
|
||||
this->currentNS = tmp;
|
||||
}
|
||||
|
||||
/// Return the current namespace that is being processed at the moment
|
||||
Namespace &getCurrentNS();
|
||||
|
||||
/// Lookup the namespace with the give name in the current context and
|
||||
/// return a pointer to it or a `nullptr` in it doesn't exist.
|
||||
Namespace *getNS(llvm::StringRef nsName);
|
||||
|
||||
/// Lookup and return a shared pointer to the given \p ns_name. This
|
||||
/// method should be used only if you need to own the namespace as well
|
||||
/// and want to keep it long term (like the JIT).
|
||||
NSPtr getSharedPtrToNS(llvm::StringRef nsName);
|
||||
|
||||
SereneContext(Options &options)
|
||||
: pm(&mlirContext), diagEngine(makeDiagnosticEngine(*this)),
|
||||
opts(options), triple(llvm::sys::getDefaultTargetTriple()),
|
||||
targetPhase(CompilationPhase::NoOptimization) {
|
||||
mlirContext.getOrLoadDialect<serene::slir::SereneDialect>();
|
||||
mlirContext.getOrLoadDialect<mlir::func::FuncDialect>();
|
||||
mlirContext.getOrLoadDialect<mlir::LLVM::LLVMDialect>();
|
||||
|
||||
// We need to create one empty namespace, so that the JIT can
|
||||
// start it's operation.
|
||||
auto ns = Namespace::make(*this, DEFAULT_NS_NAME, std::nullopt);
|
||||
|
||||
insertNS(ns);
|
||||
currentNS = ns->name;
|
||||
|
||||
// TODO: Get the crash report path dynamically from the cli
|
||||
// pm.enableCrashReproducerGeneration("/home/lxsameer/mlir.mlir");
|
||||
};
|
||||
|
||||
/// Set the target compilation phase of the compiler. The compilation
|
||||
/// phase dictates the behavior and the output type of the compiler.
|
||||
void setOperationPhase(CompilationPhase phase);
|
||||
|
||||
CompilationPhase getTargetPhase() { return targetPhase; };
|
||||
int getOptimizatioLevel();
|
||||
|
||||
// Namespace stuff ---
|
||||
|
||||
/// Create an empty namespace with the given \p name and optional \p filename
|
||||
/// and then insert it into the context
|
||||
NSPtr makeNamespace(llvm::StringRef name,
|
||||
std::optional<llvm::StringRef> filename);
|
||||
|
||||
/// Read a namespace with the given \p name and returns a share pointer
|
||||
/// to the name or an Error.
|
||||
///
|
||||
/// It just `read` the namespace by parsing it and running the semantic
|
||||
/// analyzer on it.
|
||||
MaybeNS readNamespace(const std::string &name);
|
||||
MaybeNS readNamespace(const std::string &name, reader::LocationRange loc);
|
||||
|
||||
/// Reads and add the namespace with the given \p name to the context. The
|
||||
/// namespace will be added to the context and the JIT engine as well.
|
||||
///
|
||||
/// It will \r a shared pointer to the namespace or an error tree.
|
||||
MaybeNS importNamespace(const std::string &name);
|
||||
MaybeNS importNamespace(const std::string &name, reader::LocationRange loc);
|
||||
// ---
|
||||
|
||||
static std::unique_ptr<llvm::LLVMContext> genLLVMContext() {
|
||||
return std::make_unique<llvm::LLVMContext>();
|
||||
};
|
||||
|
||||
static std::unique_ptr<SereneContext> make(Options &options) {
|
||||
auto ctx = std::make_unique<SereneContext>(options);
|
||||
auto *ns = ctx->getNS(DEFAULT_NS_NAME);
|
||||
|
||||
assert(ns != nullptr && "Default ns doesn't exit!");
|
||||
|
||||
auto maybeJIT = serene::jit::makeHalleyJIT(*ctx);
|
||||
|
||||
if (!maybeJIT) {
|
||||
auto err = maybeJIT.takeError();
|
||||
panic(*ctx, err);
|
||||
}
|
||||
|
||||
ctx->jit.swap(*maybeJIT);
|
||||
|
||||
// Make serene.user which is the defult NS available on the
|
||||
// JIT
|
||||
auto loc = reader::LocationRange::UnknownLocation(INTERNAL_NS);
|
||||
if (auto err = ctx->jit->addNS(*ns, loc)) {
|
||||
panic(*ctx, err);
|
||||
}
|
||||
|
||||
return ctx;
|
||||
};
|
||||
|
||||
// JIT JITDylib related functions ---
|
||||
|
||||
// TODO: For Dylib related functions, make sure that the namespace in questoin
|
||||
// is aleady registered in the context
|
||||
|
||||
/// Return a pointer to the most registered JITDylib of the given \p ns
|
||||
////name
|
||||
llvm::orc::JITDylib *getLatestJITDylib(Namespace &ns);
|
||||
|
||||
/// Register the given pointer to a `JITDylib` \p l, with the give \p ns.
|
||||
void pushJITDylib(Namespace &ns, llvm::orc::JITDylib *l);
|
||||
|
||||
/// Returns the number of registered `JITDylib` for the given \p ns.
|
||||
size_t getNumberOfJITDylibs(Namespace &ns);
|
||||
|
||||
private:
|
||||
CompilationPhase targetPhase;
|
||||
|
||||
// TODO: We need to keep different instances of the namespace
|
||||
// because if any one of them gets cleaned up via reference
|
||||
// count (if we are still using shared ptr for namespaces if not
|
||||
// remove this todo) then we will end up with dangling references
|
||||
// it the JIT
|
||||
|
||||
/// The namespace table. Every namespace that needs to be compiled has
|
||||
/// to register itself with the context and appear on this table.
|
||||
/// This table acts as a cache as well.
|
||||
std::map<std::string, NSPtr> namespaces;
|
||||
|
||||
/// Why string vs pointer? We might rewrite the namespace and
|
||||
/// holding a pointer means that it might point to the old version
|
||||
std::string currentNS;
|
||||
|
||||
/// A vector of pointers to all the jitDylibs for namespaces. Usually
|
||||
/// There will be only one pre NS but in case of forceful reloads of a
|
||||
/// namespace there will be more.
|
||||
llvm::StringMap<llvm::SmallVector<llvm::orc::JITDylib *, 1>> jitDylibs;
|
||||
};
|
||||
|
||||
/// Creates a new context object. Contexts are used through out the compilation
|
||||
/// process to store the state.
|
||||
///
|
||||
/// \p opts is an instance of \c Options that can be used to set options of
|
||||
/// of the compiler.
|
||||
SERENE_EXPORT std::unique_ptr<SereneContext>
|
||||
makeSereneContext(Options opts = Options());
|
||||
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,39 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_CONVENTIONS_H
|
||||
#define SERENE_CONVENTIONS_H
|
||||
|
||||
#include "serene/config.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace serene {
|
||||
// static std::string mangleInternalStringName(llvm::StringRef str) {
|
||||
// return "__serene__internal__str__" + str.str();
|
||||
// }
|
||||
|
||||
// static std::string mangleInternalSymName(llvm::StringRef str) {
|
||||
// return "__serene__symbol__" + str.str();
|
||||
// }
|
||||
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,127 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Commentary:
|
||||
* `DiagEngine` is in charge of error handling of the compiler. It receives
|
||||
* the incoming errors (`llvm::Error`) and print them out to stderr.
|
||||
*
|
||||
* Errors might raise from different contextes and the incoming channel might
|
||||
* vary. For exmaple in many case we just return `llvm::Error` from functions
|
||||
* and propagate them to the highest level call site and deal with them there
|
||||
* or during the pass management we call the `emit()` function on operations
|
||||
* to report back an error.
|
||||
*
|
||||
* Serene extends `llvm::Error`. For more info have a look at
|
||||
* `serene/errors/base.h` and the `serene/errors/errors.td`.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_DIAGNOSTICS_H
|
||||
#define SERENE_DIAGNOSTICS_H
|
||||
|
||||
#include "serene/errors.h"
|
||||
#include "serene/export.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/source_mgr.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <mlir/IR/Diagnostics.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace serene {
|
||||
class SereneContext;
|
||||
class DiagnosticEngine;
|
||||
|
||||
// TODO: Finish up the Diagnostic interface and utility functions
|
||||
// to build diagnostics from Errors
|
||||
|
||||
class Diagnostic {
|
||||
// TODO: Add support for llvm::SMFixIt
|
||||
friend DiagnosticEngine;
|
||||
|
||||
enum Type {
|
||||
Error,
|
||||
|
||||
// TODO: Add support for remarks and notes
|
||||
Remark,
|
||||
Note,
|
||||
};
|
||||
|
||||
SereneContext &ctx;
|
||||
reader::LocationRange loc;
|
||||
std::string fn;
|
||||
llvm::Error *err = nullptr;
|
||||
Type type = Type::Error;
|
||||
std::string message, lineContents;
|
||||
|
||||
std::string getPrefix(llvm::StringRef prefix = "");
|
||||
|
||||
public:
|
||||
Diagnostic(SereneContext &ctx, reader::LocationRange loc, llvm::Error *e,
|
||||
llvm::StringRef msg, llvm::StringRef fn = "")
|
||||
: ctx(ctx), loc(loc), fn(fn), err(e), message(msg){};
|
||||
|
||||
protected:
|
||||
void print(llvm::raw_ostream &os, llvm::StringRef prefix = "") const;
|
||||
void writeColorByType(llvm::raw_ostream &os, llvm::StringRef str);
|
||||
};
|
||||
|
||||
/// DiagnosticEngine is the central hub for dealing with errors in Serene. It
|
||||
/// integrates with MLIR's diag engine and LLVM's error system to handle error
|
||||
/// reporting for Serene's compiler
|
||||
class DiagnosticEngine {
|
||||
SereneContext &ctx;
|
||||
|
||||
mlir::DiagnosticEngine &diagEngine;
|
||||
|
||||
Diagnostic toDiagnostic(reader::LocationRange loc, llvm::Error &e,
|
||||
llvm::StringRef msg, llvm::StringRef fn = "");
|
||||
|
||||
void print(llvm::raw_ostream &os, Diagnostic &d);
|
||||
|
||||
public:
|
||||
DiagnosticEngine(SereneContext &ctx);
|
||||
|
||||
void enqueueError(llvm::StringRef msg);
|
||||
void emitSyntaxError(reader::LocationRange loc, llvm::Error &e,
|
||||
llvm::StringRef msg = "");
|
||||
|
||||
void emit(const llvm::Error &err);
|
||||
};
|
||||
|
||||
/// Create a new instance of the `DiagnosticEngine` from the give
|
||||
/// `SereneContext`
|
||||
std::unique_ptr<DiagnosticEngine> makeDiagnosticEngine(SereneContext &ctx);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Public API
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Throw out an error with the given \p and terminate the execution.
|
||||
SERENE_EXPORT void panic(SereneContext &ctx, llvm::Twine msg);
|
||||
|
||||
/// Throw out the give error \p err and stop the execution.
|
||||
SERENE_EXPORT void panic(SereneContext &ctx, const llvm::Error &err);
|
||||
|
||||
/// Throw the give `llvm::Error` \p errs to the stderr.
|
||||
SERENE_EXPORT void throwErrors(SereneContext &ctx, const llvm::Error &err);
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,80 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_TEST_ENVIRONMENT_H
|
||||
#define SERENE_TEST_ENVIRONMENT_H
|
||||
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/ADT/StringMap.h>
|
||||
#include <mlir/Support/LogicalResult.h>
|
||||
|
||||
namespace serene {
|
||||
|
||||
/// This class represents a classic lisp environment (or scope) that holds the
|
||||
/// bindings from type `K` to type `V`. For example an environment of symbols
|
||||
/// to expressions would be `Environment<Symbol, Node>`
|
||||
template <typename V>
|
||||
class Environment {
|
||||
|
||||
Environment<V> *parent;
|
||||
|
||||
using StorageType = llvm::StringMap<V>;
|
||||
// The actual bindings storage
|
||||
StorageType pairs;
|
||||
|
||||
public:
|
||||
Environment() : parent(nullptr) {}
|
||||
Environment(Environment *parent) : parent(parent){};
|
||||
|
||||
/// Look up the given `key` in the environment and return it.
|
||||
std::optional<V> lookup(llvm::StringRef key) {
|
||||
if (auto value = pairs.lookup(key)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
return parent->lookup(key);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
/// Insert the given `key` with the given `value` into the storage. This
|
||||
/// operation will shadow an aleady exist `key` in the parent environment
|
||||
mlir::LogicalResult insert_symbol(llvm::StringRef key, V value) {
|
||||
auto result = pairs.insert_or_assign(key, value);
|
||||
UNUSED(result);
|
||||
return mlir::success();
|
||||
};
|
||||
|
||||
inline typename StorageType::iterator begin() { return pairs.begin(); }
|
||||
|
||||
inline typename StorageType::iterator end() { return pairs.end(); }
|
||||
|
||||
inline typename StorageType::const_iterator begin() const {
|
||||
return pairs.begin();
|
||||
}
|
||||
inline typename StorageType::const_iterator end() const {
|
||||
return pairs.end();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,82 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_ERRORS_H
|
||||
#define SERENE_ERRORS_H
|
||||
|
||||
#include "serene/export.h"
|
||||
#include "serene/reader/location.h"
|
||||
|
||||
#define GET_CLASS_DEFS
|
||||
#include "serene/errors/errs.h.inc"
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
|
||||
namespace serene {
|
||||
class SereneContext;
|
||||
} // namespace serene
|
||||
|
||||
namespace serene::errors {
|
||||
|
||||
class SERENE_EXPORT SereneError : public llvm::ErrorInfo<SereneError> {
|
||||
public:
|
||||
static char ID;
|
||||
ErrorType errorType;
|
||||
|
||||
SereneContext &ctx;
|
||||
reader::LocationRange location;
|
||||
std::string msg;
|
||||
|
||||
void log(llvm::raw_ostream &os) const override { os << msg; }
|
||||
|
||||
std::error_code convertToErrorCode() const override {
|
||||
// TODO: Fix this by creating a mapping from ErrorType to standard
|
||||
// errc or return the ErrorType number instead
|
||||
return std::make_error_code(std::errc::io_error);
|
||||
}
|
||||
|
||||
SereneError(SereneContext &ctx, ErrorType errtype, reader::LocationRange &loc)
|
||||
: errorType(errtype), ctx(ctx), location(loc){};
|
||||
|
||||
SereneError(SereneContext &ctx, ErrorType errtype, reader::LocationRange &loc,
|
||||
llvm::StringRef msg)
|
||||
: errorType(errtype), ctx(ctx), location(loc), msg(msg.str()){};
|
||||
|
||||
reader::LocationRange &where() { return location; };
|
||||
};
|
||||
|
||||
/// Create and return a Serene flavored `llvm::Error` by passing the parameters
|
||||
/// directly to the constructor of type `E`.
|
||||
///
|
||||
/// This is the official way of creating error objects in Serene.
|
||||
template <typename... Args>
|
||||
SERENE_EXPORT llvm::Error makeError(SereneContext &ctx, ErrorType errtype,
|
||||
Args &&...args) {
|
||||
return llvm::make_error<SereneError>(ctx, errtype,
|
||||
std::forward<Args>(args)...);
|
||||
};
|
||||
|
||||
/// Returns the messange that the given error \p e is holding. It doesn't cast
|
||||
/// the error to a concrete error type.
|
||||
SERENE_EXPORT std::string getMessage(const llvm::Error &e);
|
||||
|
||||
SERENE_EXPORT const ErrorVariant *getVariant(ErrorType t);
|
||||
} // namespace serene::errors
|
||||
|
||||
#endif
|
|
@ -1,5 +0,0 @@
|
|||
set(LLVM_TARGET_DEFINITIONS errors.td)
|
||||
|
||||
serene_tablegen(errs.h.inc -errors-backend)
|
||||
|
||||
add_public_tablegen_target(SereneErrorGen)
|
|
@ -1,65 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_ERRORS_BASE_H
|
||||
#define SERENE_ERRORS_BASE_H
|
||||
|
||||
#include "serene/export.h"
|
||||
#include "serene/reader/location.h"
|
||||
|
||||
#define GET_CLASS_DEFS
|
||||
#include "serene/errors/errs.h.inc"
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#include <llvm/Support/Error.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
|
||||
namespace serene::errors {
|
||||
|
||||
class SERENE_EXPORT Error {};
|
||||
|
||||
class SERENE_EXPORT SereneError : public llvm::ErrorInfo<SereneError> {
|
||||
public:
|
||||
static char ID;
|
||||
ErrorType errorType;
|
||||
|
||||
SereneContext &ctx;
|
||||
reader::LocationRange location;
|
||||
std::string msg;
|
||||
|
||||
void log(llvm::raw_ostream &os) const override { os << msg; }
|
||||
|
||||
std::error_code convertToErrorCode() const override {
|
||||
// TODO: Fix this by creating a mapping from ErrorType to standard
|
||||
// errc or return the ErrorType number instead
|
||||
return std::make_error_code(std::errc::io_error);
|
||||
}
|
||||
|
||||
SereneError(SereneContext &ctx, ErrorType errtype, reader::LocationRange &loc)
|
||||
: errorType(errtype), ctx(ctx), location(loc){};
|
||||
|
||||
SereneError(SereneContext &ctx, ErrorType errtype, reader::LocationRange &loc,
|
||||
llvm::StringRef msg)
|
||||
: errorType(errtype), ctx(ctx), location(loc), msg(msg.str()){};
|
||||
|
||||
reader::LocationRange &where() { return location; };
|
||||
};
|
||||
|
||||
}; // namespace serene::errors
|
||||
#endif
|
|
@ -1,76 +0,0 @@
|
|||
|
||||
|
||||
class Error<string _desc = "", string _help = ""> {
|
||||
string desc = _desc;
|
||||
string help = _help;
|
||||
}
|
||||
|
||||
// Examples of how to define a new error
|
||||
// def Err : Error<"Err1 titel"> {
|
||||
// let description = [{
|
||||
// err1
|
||||
// multiline
|
||||
// desc}];
|
||||
// }
|
||||
|
||||
// def Err2 : Error {
|
||||
// let title = "err 2 titel";
|
||||
// let description = "err2 desc";
|
||||
// }
|
||||
|
||||
// def Err3 : Error<"err3", [{
|
||||
// err3
|
||||
// multiline
|
||||
// desc
|
||||
// }]>;
|
||||
|
||||
def UnknownError: Error<"Can't find any description for this error.">;
|
||||
|
||||
def DefExpectSymbol: Error<"The first argument to 'def' has to be a Symbol.">;
|
||||
|
||||
def DefWrongNumberOfArgs: Error<"Wrong number of arguments is passed to the 'def' form.">;
|
||||
|
||||
def FnNoArgsList: Error<"'fn' form requires an argument list.">;
|
||||
|
||||
def FnArgsMustBeList: Error<"'fn' arguments should be a list.">;
|
||||
|
||||
def CantResolveSymbol: Error<"Can't resolve the given name.">;
|
||||
|
||||
def DontKnowHowToCallNode: Error<"Don't know how to call the given expression.">;
|
||||
|
||||
def PassFailureError: Error<"Pass Failure.">;
|
||||
|
||||
def NSLoadError: Error<"Faild to find a namespace.">;
|
||||
|
||||
def NSAddToSMError: Error<"Faild to add the namespace to the source manager.">;
|
||||
|
||||
def
|
||||
EOFWhileScaningAList: Error<"EOF reached before closing of list">;
|
||||
|
||||
def InvalidDigitForNumber: Error<"Invalid digit for a number.">;
|
||||
|
||||
def
|
||||
TwoFloatPoints: Error<"Two or more float point characters in a number">;
|
||||
|
||||
def
|
||||
InvalidCharacterForSymbol: Error<"Invalid character for a symbol">;
|
||||
|
||||
def CompilationError: Error<"Compilation error!">;
|
||||
|
||||
defvar errorsIndex = [
|
||||
UnknownError,
|
||||
DefExpectSymbol,
|
||||
DefWrongNumberOfArgs,
|
||||
FnNoArgsList,
|
||||
FnArgsMustBeList,
|
||||
CantResolveSymbol,
|
||||
DontKnowHowToCallNode,
|
||||
PassFailureError,
|
||||
NSLoadError,
|
||||
NSAddToSMError,
|
||||
EOFWhileScaningAList,
|
||||
InvalidDigitForNumber,
|
||||
TwoFloatPoints,
|
||||
InvalidCharacterForSymbol,
|
||||
CompilationError,
|
||||
];
|
|
@ -1,43 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_ERRORS_VARIANT_H
|
||||
#define SERENE_ERRORS_VARIANT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace serene::errors {
|
||||
|
||||
// This class is used in the generated code
|
||||
struct ErrorVariant {
|
||||
const int id;
|
||||
const std::string title;
|
||||
const std::string desc;
|
||||
const std::string help;
|
||||
|
||||
static ErrorVariant make(const int id, const char *t, const char *d,
|
||||
const char *h) {
|
||||
return ErrorVariant(id, t, d, h);
|
||||
};
|
||||
|
||||
private:
|
||||
ErrorVariant(const int id, const char *t, const char *d, const char *h)
|
||||
: id(id), title(t), desc(d), help(h){};
|
||||
};
|
||||
} // namespace serene::errors
|
||||
#endif
|
|
@ -1,76 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_EXPRS_CALL_H
|
||||
#define SERENE_EXPRS_CALL_H
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/list.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace serene {
|
||||
|
||||
namespace exprs {
|
||||
class List;
|
||||
|
||||
/// This data structure represents a function. with a collection of
|
||||
/// arguments and the ast of a body
|
||||
class Call : public Expression {
|
||||
|
||||
public:
|
||||
Node target;
|
||||
Ast params;
|
||||
|
||||
Call(reader::LocationRange &loc, Node &target, Ast ¶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
|
|
@ -1,72 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_EXPRS_DEF_H
|
||||
#define SERENE_EXPRS_DEF_H
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace serene {
|
||||
|
||||
namespace exprs {
|
||||
class List;
|
||||
|
||||
/// This data structure represents the operation to define a new binding via
|
||||
/// the `def` special form.
|
||||
class Def : public Expression {
|
||||
|
||||
public:
|
||||
std::string binding;
|
||||
Node value;
|
||||
|
||||
Def(reader::LocationRange &loc, llvm::StringRef binding, Node &v)
|
||||
: Expression(loc), binding(binding), value(v){};
|
||||
|
||||
Def(Def &d) = delete;
|
||||
|
||||
ExprType getType() const override;
|
||||
std::string toString() const override;
|
||||
|
||||
MaybeNode analyze(semantics::AnalysisState &state) override;
|
||||
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override;
|
||||
|
||||
static bool classof(const Expression *e);
|
||||
|
||||
/// Create a Def node out a list. The list should contain the
|
||||
/// correct `def` form like `(def blah value)`. This function
|
||||
/// is supposed to be used in the semantic analysis phase.
|
||||
///
|
||||
/// \param state is the semantic analysis state to use in creation time.
|
||||
/// \param list the list containing the `def` form
|
||||
static MaybeNode make(semantics::AnalysisState &state, List *list);
|
||||
|
||||
~Def() = default;
|
||||
};
|
||||
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,148 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_EXPRS_EXPRESSION_H
|
||||
#define SERENE_EXPRS_EXPRESSION_H
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/traits.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/semantics.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/Support/Error.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace serene {
|
||||
|
||||
/// Contains all the builtin AST expressions including those which do not appear
|
||||
/// in the syntax directly. Like function definitions.
|
||||
namespace exprs {
|
||||
|
||||
class Expression;
|
||||
|
||||
using Node = std::shared_ptr<Expression>;
|
||||
using MaybeNode = llvm::Expected<Node>;
|
||||
|
||||
using Ast = std::vector<Node>;
|
||||
using MaybeAst = llvm::Expected<Ast>;
|
||||
|
||||
constexpr static auto EmptyNode = nullptr;
|
||||
|
||||
/// The base class of the expressions which provides the common interface for
|
||||
/// the expressions to implement.
|
||||
class SERENE_EXPORT Expression {
|
||||
public:
|
||||
/// The location range provide information regarding to where in the input
|
||||
/// string the current expression is used.
|
||||
reader::LocationRange location;
|
||||
|
||||
Expression(const reader::LocationRange &loc) : location(loc){};
|
||||
virtual ~Expression() = default;
|
||||
|
||||
/// Returns the type of the expression. We need this funciton to perform
|
||||
/// dynamic casting of expression object to implementations such as lisp or
|
||||
/// symbol.
|
||||
virtual ExprType getType() const = 0;
|
||||
|
||||
/// The AST representation of an expression
|
||||
virtual std::string toString() const = 0;
|
||||
|
||||
/// Analyzes the semantics of current node and return a new node in case
|
||||
/// that we need to semantically rewrite the current node and replace it with
|
||||
/// another node. For example to change from a List containing `(def a b)`
|
||||
/// to a `Def` node that represents defining a new binding.
|
||||
///
|
||||
/// \param state is the analysis state object of the semantic analyzer.
|
||||
virtual MaybeNode analyze(semantics::AnalysisState &state) = 0;
|
||||
|
||||
/// Genenates the correspondig SLIR of the expressoin and attach it to the
|
||||
/// given module.
|
||||
///
|
||||
/// \param ns The namespace that current expression is in it.
|
||||
/// \param m The target MLIR moduleOp to attach the operations to
|
||||
virtual void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) = 0;
|
||||
};
|
||||
|
||||
/// Create a new `node` of type `T` and forwards any given parameter
|
||||
/// to the constructor of type `T`. This is the **official way** to create
|
||||
/// a new `Expression`. Here is an example:
|
||||
/// \code
|
||||
/// auto list = make<List>();
|
||||
/// \endcode
|
||||
///
|
||||
/// \param[args] Any argument with any type passed to this function will be
|
||||
/// passed to the constructor of type T.
|
||||
/// \return A unique pointer to an Expression
|
||||
template <typename T, typename... Args>
|
||||
Node make(Args &&...args) {
|
||||
return std::make_shared<T>(std::forward<Args>(args)...);
|
||||
};
|
||||
/// Create a new `node` of type `T` and forwards any given parameter
|
||||
/// to the constructor of type `T`. This is the **official way** to create
|
||||
/// a new `Expression`. Here is an example:
|
||||
/// \code
|
||||
/// auto list = makeAndCast<List>();
|
||||
/// \endcode
|
||||
///
|
||||
/// \param[args] Any argument with any type passed to this function will be
|
||||
/// passed to the constructor of type T.
|
||||
/// \return A unique pointer to a value of type T.
|
||||
template <typename T, typename... Args>
|
||||
std::shared_ptr<T> makeAndCast(Args &&...args) {
|
||||
return std::make_shared<T>(std::forward<Args>(args)...);
|
||||
};
|
||||
|
||||
/// The helper function to create a new `Node` and returnsit. It should be useds
|
||||
/// where every we want to return a `MaybeNode` successfully.
|
||||
template <typename T, typename... Args>
|
||||
MaybeNode makeSuccessfulNode(Args &&...args) {
|
||||
return make<T>(std::forward<Args>(args)...);
|
||||
};
|
||||
|
||||
/// The hlper function to creates an Error (`llvm::Error`) by passing all
|
||||
/// the given arguments to the constructor of the template param `E`.
|
||||
template <typename E, typename T = Node, typename... Args>
|
||||
llvm::Expected<T> makeErrorful(Args &&...args) {
|
||||
return llvm::make_error<E>(std::forward<Args>(args)...);
|
||||
};
|
||||
|
||||
/// The hlper function to creates an Error (`llvm::Error`) by passing all
|
||||
/// the given arguments to the constructor of the template param `E`.
|
||||
template <typename E, typename... Args>
|
||||
MaybeNode makeErrorNode(Args &&...args) {
|
||||
return makeErrorful<E, Node>(std::forward<Args>(args)...);
|
||||
};
|
||||
|
||||
/// Convert the given AST to string by calling the `toString` method
|
||||
/// of each node.
|
||||
SERENE_EXPORT std::string astToString(const Ast *);
|
||||
/// Converts the given ExprType to string.
|
||||
std::string stringifyExprType(ExprType);
|
||||
|
||||
/// Converts the given AST to string and prints it out
|
||||
void dump(Ast &);
|
||||
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,77 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_EXPRS_FN_H
|
||||
#define SERENE_EXPRS_FN_H
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/list.h"
|
||||
#include "serene/namespace.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace serene {
|
||||
|
||||
namespace exprs {
|
||||
class List;
|
||||
|
||||
/// This data structure represents a function. with a collection of
|
||||
/// arguments and the ast of a body
|
||||
class Fn : public Expression {
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
// TODO: Use a coll type instead of a list here
|
||||
List args;
|
||||
Ast body;
|
||||
|
||||
Fn(SereneContext &ctx, reader::LocationRange &loc, List &args, Ast body);
|
||||
|
||||
Fn(Fn &f) = delete;
|
||||
|
||||
ExprType getType() const override;
|
||||
std::string toString() const override;
|
||||
MaybeNode analyze(semantics::AnalysisState &state) override;
|
||||
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override;
|
||||
|
||||
static bool classof(const Expression *e);
|
||||
|
||||
/// Creates a function node out of a function definition
|
||||
/// in a list. the list has to contain the correct definition
|
||||
/// of a function, for exmaple: `(fn (args1 arg2) body)`.This function
|
||||
/// is supposed to be used in the semantic analysis phase.
|
||||
///
|
||||
/// \param state is the semantic analysis state to use.
|
||||
/// \param list the list containing the `fn` form
|
||||
static MaybeNode make(semantics::AnalysisState &state, List *list);
|
||||
|
||||
void setName(std::string);
|
||||
~Fn() = default;
|
||||
};
|
||||
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,88 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_EXPRS_LIST_H
|
||||
#define SERENE_EXPRS_LIST_H
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/export.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
|
||||
#include <llvm/ADT/Optional.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace serene {
|
||||
|
||||
namespace exprs {
|
||||
|
||||
/// This class represents a List in the AST level and not the List as the data
|
||||
/// type.
|
||||
class SERENE_EXPORT List : public Expression {
|
||||
public:
|
||||
// Internal elements of the lest (small vector of shared pointers to
|
||||
// expressions)
|
||||
Ast elements;
|
||||
|
||||
List(const List &l); // Copy ctor
|
||||
List(List &&e) noexcept = default; // Move ctor
|
||||
|
||||
List(const reader::LocationRange &loc) : Expression(loc){};
|
||||
List(const reader::LocationRange &loc, Node &e);
|
||||
List(const reader::LocationRange &loc, Ast elems);
|
||||
|
||||
ExprType getType() const override;
|
||||
std::string toString() const override;
|
||||
|
||||
void append(Node);
|
||||
|
||||
size_t count() const;
|
||||
|
||||
Ast from(uint index);
|
||||
|
||||
std::optional<Expression *> at(uint index);
|
||||
|
||||
/// Return an iterator to be used with the `for` loop. It's implicitly called
|
||||
/// by the for loop.
|
||||
std::vector<Node>::const_iterator cbegin();
|
||||
|
||||
/// Return an iterator to be used with the `for` loop. It's implicitly called
|
||||
/// by the for loop.
|
||||
std::vector<Node>::const_iterator cend();
|
||||
|
||||
/// Return an iterator to be used with the `for` loop. It's implicitly called
|
||||
/// by the for loop.
|
||||
std::vector<Node>::iterator begin();
|
||||
|
||||
/// Return an iterator to be used with the `for` loop. It's implicitly called
|
||||
/// by the for loop.
|
||||
std::vector<Node>::iterator end();
|
||||
|
||||
MaybeNode analyze(semantics::AnalysisState &state) override;
|
||||
// NOLINTNEXTLINE(readability-named-parameter)
|
||||
void generateIR(serene::Namespace &, mlir::ModuleOp &) override{};
|
||||
|
||||
~List() = default;
|
||||
|
||||
static bool classof(const Expression *e);
|
||||
};
|
||||
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,65 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_EXPRS_NUMBER_H
|
||||
#define SERENE_EXPRS_NUMBER_H
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/export.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/namespace.h"
|
||||
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
|
||||
/// This data structure represent a number. I handles float points, integers,
|
||||
/// positive and negative numbers. This is not a data type representative.
|
||||
/// So it won't cast to actual numeric types and it has a string container
|
||||
/// to hold the parsed value.
|
||||
struct SERENE_EXPORT Number : public Expression {
|
||||
|
||||
// TODO: Use a variant here instead to store different number types
|
||||
std::string value;
|
||||
|
||||
bool isNeg;
|
||||
bool isFloat;
|
||||
|
||||
Number(reader::LocationRange &loc, const std::string &num, bool isNeg,
|
||||
bool isFloat)
|
||||
: Expression(loc), value(num), isNeg(isNeg), isFloat(isFloat){};
|
||||
|
||||
ExprType getType() const override;
|
||||
std::string toString() const override;
|
||||
|
||||
MaybeNode analyze(semantics::AnalysisState &state) override;
|
||||
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override;
|
||||
|
||||
// TODO: This is horrible, we need to fix it after the mvp
|
||||
int toI64() const;
|
||||
|
||||
~Number() = default;
|
||||
|
||||
static bool classof(const Expression *e);
|
||||
};
|
||||
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,81 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_EXPRS_SYMBOL_H
|
||||
#define SERENE_EXPRS_SYMBOL_H
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/export.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/namespace.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace serene {
|
||||
|
||||
namespace exprs {
|
||||
|
||||
/// This data structure represent the Lisp symbol. Just a symbol
|
||||
/// in the context of the AST and nothing else.
|
||||
class SERENE_EXPORT Symbol : public Expression {
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
std::string nsName;
|
||||
|
||||
Symbol(const reader::LocationRange &loc, llvm::StringRef name,
|
||||
llvm::StringRef currentNS)
|
||||
: Expression(loc) {
|
||||
// IMPORTANT NOTE: the `name` and `currentNS` should be valid string and
|
||||
// already validated.
|
||||
auto partDelimiter = name.find('/');
|
||||
if (partDelimiter == std::string::npos) {
|
||||
nsName = currentNS.str();
|
||||
this->name = name.str();
|
||||
|
||||
} else {
|
||||
this->name = name.substr(partDelimiter + 1, name.size()).str();
|
||||
nsName = name.substr(0, partDelimiter).str();
|
||||
}
|
||||
};
|
||||
|
||||
Symbol(Symbol &s) : Expression(s.location) {
|
||||
this->name = s.name;
|
||||
this->nsName = s.nsName;
|
||||
}
|
||||
|
||||
ExprType getType() const override;
|
||||
std::string toString() const override;
|
||||
|
||||
MaybeNode analyze(semantics::AnalysisState &state) override;
|
||||
void generateIR(serene::Namespace &ns, mlir::ModuleOp &m) override {
|
||||
UNUSED(ns);
|
||||
UNUSED(m);
|
||||
};
|
||||
|
||||
~Symbol() = default;
|
||||
|
||||
static bool classof(const Expression *e);
|
||||
};
|
||||
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,47 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_EXPRS_TRAITS_H
|
||||
#define SERENE_EXPRS_TRAITS_H
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/reader/traits.h"
|
||||
#include "serene/traits.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
namespace serene::exprs {
|
||||
/// This enum represent the expression type and **not** the value type.
|
||||
enum class ExprType {
|
||||
Symbol,
|
||||
List,
|
||||
Number,
|
||||
Def,
|
||||
Error,
|
||||
Fn,
|
||||
Call,
|
||||
};
|
||||
|
||||
/// The string represantion of built in expr types (NOT DATATYPES).
|
||||
static const char *exprTypes[] = {
|
||||
"Symbol", "List", "Number", "Def", "Error", "Fn", "Call",
|
||||
};
|
||||
|
||||
}; // namespace serene::exprs
|
||||
|
||||
#endif
|
|
@ -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.
|
|
@ -1,180 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
// TODO: Look at the LLJITBuilderState::prepareForConstruction to setup the
|
||||
// object linking layer
|
||||
|
||||
#ifndef SERENE_JIT_ENGINE_H
|
||||
#define SERENE_JIT_ENGINE_H
|
||||
|
||||
#include "serene/jit/layers.h"
|
||||
#include "serene/namespace.h"
|
||||
|
||||
#include <llvm/ExecutionEngine/Orc/CompileUtils.h> // for ConcurrentIRCompiler
|
||||
#include <llvm/ExecutionEngine/Orc/Core.h>
|
||||
#include <llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h>
|
||||
#include <llvm/ExecutionEngine/Orc/ExecutionUtils.h> // for DynamicLibrarySearchGenerator
|
||||
#include <llvm/ExecutionEngine/Orc/IRCompileLayer.h>
|
||||
#include <llvm/ExecutionEngine/Orc/Mangling.h>
|
||||
#include <llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h>
|
||||
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define JIT_LOG(...) \
|
||||
DEBUG_WITH_TYPE("jit", llvm::dbgs() << "[JIT]: " << __VA_ARGS__ << "\n");
|
||||
|
||||
namespace orc = llvm::orc;
|
||||
|
||||
namespace serene {
|
||||
|
||||
class SereneContext;
|
||||
|
||||
namespace jit {
|
||||
|
||||
class SereneJIT {
|
||||
/// An ExecutionSession represents a running JIT program.
|
||||
std::unique_ptr<orc::ExecutionSession> es;
|
||||
|
||||
std::unique_ptr<orc::EPCIndirectionUtils> epciu;
|
||||
|
||||
llvm::DataLayout dl;
|
||||
|
||||
/// Mangler in responsible for changing the symbol names based on our
|
||||
/// naming convention.
|
||||
orc::MangleAndInterner mangler;
|
||||
|
||||
// Layers -------------------------------------------------------------------
|
||||
// Serene's JIT follows the same design as the ORC and uses the framework that
|
||||
// it provides.
|
||||
//
|
||||
// Layers are the building blocks of the JIT and work on top of each other
|
||||
// to add different functionalities to the JIT. Order is important here and
|
||||
// layers call each other and pa
|
||||
|
||||
/// The object linking layer allows object files to be added to the JIT
|
||||
orc::RTDyldObjectLinkingLayer objectLayer;
|
||||
|
||||
/// The compiler layer is responsible to compile the LLVMIR to target code
|
||||
/// for us
|
||||
orc::IRCompileLayer compileLayer;
|
||||
|
||||
// TODO: Enable these two layers when we add the `Platform support`
|
||||
// std::unique_ptr<orc::IRTransformLayer> transformLayer;
|
||||
// std::unique_ptr<orc::IRTransformLayer> initHelperTransformLayer;
|
||||
|
||||
// orc::CompileOnDemandLayer compileOnDemandLayer;
|
||||
|
||||
/// Transform layaer is responsible for running a pass pipeline on the AST
|
||||
/// and generate LLVM IR
|
||||
orc::IRTransformLayer transformLayer;
|
||||
|
||||
// The AST Layer reads and import the Serene Ast directly to the JIT
|
||||
AstLayer astLayer;
|
||||
|
||||
/// NS layer is responsible for adding namespace to the JIT by name.
|
||||
/// It will import the entire namespace.
|
||||
NSLayer nsLayer;
|
||||
|
||||
/// This a symbol table tha holds symbols from whatever code we execute
|
||||
orc::JITDylib &mainJD;
|
||||
|
||||
serene::SereneContext &ctx;
|
||||
|
||||
/// The name of the NS which we are in it at the moment.
|
||||
std::string inNS;
|
||||
|
||||
static llvm::Expected<orc::ThreadSafeModule>
|
||||
optimizeModule(orc::ThreadSafeModule tsm,
|
||||
const orc::MaterializationResponsibility &r) {
|
||||
// TSM.withModuleDo([](Module &M) {
|
||||
// // Create a function pass manager.
|
||||
// auto FPM = std::make_unique<legacy::FunctionPassManager>(&M);
|
||||
|
||||
// // Add some optimizations.
|
||||
// FPM->add(createInstructionCombiningPass());
|
||||
// FPM->add(createReassociatePass());
|
||||
// FPM->add(createGVNPass());
|
||||
// FPM->add(createCFGSimplificationPass());
|
||||
// FPM->doInitialization();
|
||||
|
||||
// // Run the optimizations over all functions in the module being added
|
||||
// to
|
||||
// // the JIT.
|
||||
// for (auto &F : M)
|
||||
// FPM->run(F);
|
||||
// });
|
||||
UNUSED(r);
|
||||
return std::move(tsm);
|
||||
}
|
||||
|
||||
Namespace ¤tNS;
|
||||
|
||||
public:
|
||||
SereneJIT(Namespace &entryNS, std::unique_ptr<orc::ExecutionSession> es,
|
||||
std::unique_ptr<orc::EPCIndirectionUtils> epciu,
|
||||
orc::JITTargetMachineBuilder jtmb, llvm::DataLayout &&dl);
|
||||
|
||||
~SereneJIT() {
|
||||
// if (compileThreads) {
|
||||
// compileThreads->wait();
|
||||
// }
|
||||
|
||||
if (auto err = es->endSession()) {
|
||||
es->reportError(std::move(err));
|
||||
}
|
||||
};
|
||||
|
||||
Namespace &getCurrentNS() { return currentNS; }
|
||||
|
||||
const llvm::DataLayout &getDataLayout() const { return dl; }
|
||||
|
||||
orc::JITDylib &getMainJITDylib() { return mainJD; }
|
||||
|
||||
llvm::Error addModule(orc::ThreadSafeModule tsm,
|
||||
orc::ResourceTrackerSP rt = nullptr) {
|
||||
if (!rt) {
|
||||
rt = mainJD.getDefaultResourceTracker();
|
||||
}
|
||||
|
||||
return compileLayer.add(rt, std::move(tsm));
|
||||
}
|
||||
|
||||
llvm::Error addNS(llvm::StringRef nsname,
|
||||
orc::ResourceTrackerSP rt = nullptr);
|
||||
|
||||
llvm::Error addAst(exprs::Ast &ast, orc::ResourceTrackerSP rt = nullptr);
|
||||
|
||||
llvm::Expected<llvm::JITEvaluatedSymbol> lookup(llvm::StringRef name) {
|
||||
JIT_LOG("Looking up symbol: " + name);
|
||||
return es->lookup({&mainJD}, mangler(name.str()));
|
||||
}
|
||||
};
|
||||
|
||||
llvm::Expected<std::unique_ptr<SereneJIT>> makeSereneJIT(Namespace &ns);
|
||||
|
||||
}; // namespace jit
|
||||
}; // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,196 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Commentary:
|
||||
This is the first working attempt on building a JIT engine for Serene
|
||||
and named after Edmond Halley.
|
||||
|
||||
- It supports both ASTs and Namespaces
|
||||
- Every Namespace might have one or more JITDylibs. Depends on the method
|
||||
of the compilation.
|
||||
- It operates in lazy (for REPL) and non-lazy mode and wraps LLJIT
|
||||
and LLLazyJIT
|
||||
- It uses an object cache layer to cache module (not NSs) objects.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_JIT_HALLEY_H
|
||||
#define SERENE_JIT_HALLEY_H
|
||||
|
||||
#include "serene/errors.h"
|
||||
#include "serene/export.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/ExecutionEngine/JITEventListener.h>
|
||||
#include <llvm/ExecutionEngine/ObjectCache.h>
|
||||
#include <llvm/ExecutionEngine/Orc/LLJIT.h>
|
||||
#include <llvm/Support/CodeGen.h>
|
||||
#include <llvm/Support/Debug.h>
|
||||
#include <llvm/Support/SmallVectorMemoryBuffer.h>
|
||||
#include <mlir/Support/LLVM.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define HALLEY_LOG(...) \
|
||||
DEBUG_WITH_TYPE("halley", llvm::dbgs() \
|
||||
<< "[HALLEY]: " << __VA_ARGS__ << "\n");
|
||||
|
||||
namespace serene {
|
||||
|
||||
class SereneContext;
|
||||
class Namespace;
|
||||
|
||||
namespace exprs {
|
||||
class Symbol;
|
||||
} // namespace exprs
|
||||
|
||||
namespace jit {
|
||||
class Halley;
|
||||
|
||||
using MaybeJIT = llvm::Expected<std::unique_ptr<Halley>>;
|
||||
using MaybeJITPtr = llvm::Expected<void (*)(void **)>;
|
||||
|
||||
/// A simple object cache following Lang's LLJITWithObjectCache example and
|
||||
/// MLIR's SimpelObjectCache.
|
||||
class ObjectCache : public llvm::ObjectCache {
|
||||
public:
|
||||
/// Cache the given `objBuffer` for the given module `m`. The buffer contains
|
||||
/// the combiled objects of the module
|
||||
void notifyObjectCompiled(const llvm::Module *m,
|
||||
llvm::MemoryBufferRef objBuffer) override;
|
||||
|
||||
// Lookup the cache for the given module `m` or returen a nullptr.
|
||||
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module *m) override;
|
||||
|
||||
/// Dump cached object to output file `filename`.
|
||||
void dumpToObjectFile(llvm::StringRef filename);
|
||||
|
||||
private:
|
||||
llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> cachedObjects;
|
||||
};
|
||||
|
||||
class SERENE_EXPORT Halley {
|
||||
// TODO: Replace this with a variant of LLJIT and LLLazyJIT
|
||||
std::unique_ptr<llvm::orc::LLJIT> engine;
|
||||
std::unique_ptr<ObjectCache> cache;
|
||||
|
||||
/// GDB notification listener.
|
||||
llvm::JITEventListener *gdbListener;
|
||||
|
||||
/// Perf notification listener.
|
||||
llvm::JITEventListener *perfListener;
|
||||
|
||||
llvm::orc::JITTargetMachineBuilder jtmb;
|
||||
llvm::DataLayout &dl;
|
||||
SereneContext &ctx;
|
||||
|
||||
std::shared_ptr<Namespace> activeNS;
|
||||
|
||||
bool isLazy = false;
|
||||
|
||||
public:
|
||||
Halley(serene::SereneContext &ctx, llvm::orc::JITTargetMachineBuilder &&jtmb,
|
||||
llvm::DataLayout &&dl);
|
||||
|
||||
static MaybeJIT make(serene::SereneContext &ctx,
|
||||
llvm::orc::JITTargetMachineBuilder &&jtmb);
|
||||
|
||||
void setEngine(std::unique_ptr<llvm::orc::LLJIT> e, bool isLazy);
|
||||
/// Looks up a packed-argument function with the given sym name and returns a
|
||||
/// pointer to it. Propagates errors in case of failure.
|
||||
MaybeJITPtr lookup(exprs::Symbol &sym) const;
|
||||
|
||||
/// Invokes the function with the given name passing it the list of opaque
|
||||
/// pointers to the actual arguments.
|
||||
// llvm::Error
|
||||
// invokePacked(llvm::StringRef name,
|
||||
// llvm::MutableArrayRef<void *> args = std::nullopt) const;
|
||||
|
||||
/// Trait that defines how a given type is passed to the JIT code. This
|
||||
/// defaults to passing the address but can be specialized.
|
||||
template <typename T>
|
||||
struct Argument {
|
||||
static void pack(llvm::SmallVectorImpl<void *> &args, T &val) {
|
||||
args.push_back(&val);
|
||||
}
|
||||
};
|
||||
|
||||
/// Tag to wrap an output parameter when invoking a jitted function.
|
||||
template <typename T>
|
||||
struct FnResult {
|
||||
FnResult(T &result) : value(result) {}
|
||||
T &value;
|
||||
};
|
||||
|
||||
/// Helper function to wrap an output operand when using
|
||||
/// ExecutionEngine::invoke.
|
||||
template <typename T>
|
||||
static FnResult<T> result(T &t) {
|
||||
return FnResult<T>(t);
|
||||
}
|
||||
|
||||
// Specialization for output parameter: their address is forwarded directly to
|
||||
// the native code.
|
||||
template <typename T>
|
||||
struct Argument<Result<T>> {
|
||||
static void pack(llvm::SmallVectorImpl<void *> &args, FnResult<T> &result) {
|
||||
args.push_back(&result.value);
|
||||
}
|
||||
};
|
||||
|
||||
/// Invokes the function with the given name passing it the list of arguments
|
||||
/// by value. Function result can be obtain through output parameter using the
|
||||
/// `FnResult` wrapper defined above. For example:
|
||||
///
|
||||
/// func @foo(%arg0 : i32) -> i32 attributes { llvm.emit_c_interface }
|
||||
///
|
||||
/// can be invoked:
|
||||
///
|
||||
/// int32_t result = 0;
|
||||
/// llvm::Error error = jit->invoke("foo", 42,
|
||||
/// result(result));
|
||||
// template <typename... Args>
|
||||
// llvm::Error invoke(llvm::StringRef funcName, Args... args) {
|
||||
// const std::string adapterName = std::string("") + funcName.str();
|
||||
// llvm::SmallVector<void *> argsArray;
|
||||
// // Pack every arguments in an array of pointers. Delegate the packing to
|
||||
// a
|
||||
// // trait so that it can be overridden per argument type.
|
||||
// // TODO: replace with a fold expression when migrating to C++17.
|
||||
// int dummy[] = {0, ((void)Argument<Args>::pack(argsArray, args), 0)...};
|
||||
// (void)dummy;
|
||||
// return invokePacked(adapterName, argsArray);
|
||||
// };
|
||||
|
||||
void dumpToObjectFile(llvm::StringRef filename);
|
||||
|
||||
llvm::Error addNS(Namespace &ns, reader::LocationRange &loc);
|
||||
|
||||
llvm::Error addAST(exprs::Ast &ast);
|
||||
|
||||
Namespace &getActiveNS();
|
||||
};
|
||||
|
||||
llvm::Expected<std::unique_ptr<Halley>> makeHalleyJIT(SereneContext &ctx);
|
||||
|
||||
} // namespace jit
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,164 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_JIT_LAYERS_H
|
||||
#define SERENE_JIT_LAYERS_H
|
||||
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/ADT/Twine.h>
|
||||
#include <llvm/ExecutionEngine/JITSymbol.h>
|
||||
#include <llvm/ExecutionEngine/Orc/Core.h>
|
||||
#include <llvm/ExecutionEngine/Orc/Layer.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
|
||||
#define LAYER_LOG(...) \
|
||||
DEBUG_WITH_TYPE("layer", llvm::dbgs() << "[Layer]: " << __VA_ARGS__ << "\n");
|
||||
|
||||
namespace orc = llvm::orc;
|
||||
|
||||
namespace serene {
|
||||
class SereneContext;
|
||||
class Namespace;
|
||||
|
||||
namespace exprs {
|
||||
class Expression;
|
||||
using Node = std::shared_ptr<Expression>;
|
||||
using Ast = std::vector<Node>;
|
||||
} // namespace exprs
|
||||
|
||||
namespace jit {
|
||||
|
||||
class AstLayer;
|
||||
|
||||
/// This will compile the ast to llvm ir.
|
||||
llvm::orc::ThreadSafeModule compileAst(Namespace &ns, exprs::Ast &ast);
|
||||
|
||||
class AstMaterializationUnit : public orc::MaterializationUnit {
|
||||
public:
|
||||
AstMaterializationUnit(Namespace &ns, AstLayer &l, exprs::Ast &ast);
|
||||
|
||||
llvm::StringRef getName() const override {
|
||||
return "SereneAstMaterializationUnit";
|
||||
}
|
||||
|
||||
void
|
||||
materialize(std::unique_ptr<orc::MaterializationResponsibility> r) override;
|
||||
|
||||
private:
|
||||
void discard(const orc::JITDylib &jd,
|
||||
const orc::SymbolStringPtr &sym) override {
|
||||
UNUSED(jd);
|
||||
UNUSED(sym);
|
||||
llvm_unreachable("Serene functions are not overridable");
|
||||
}
|
||||
|
||||
Namespace &ns;
|
||||
AstLayer &astLayer;
|
||||
exprs::Ast *
|
||||
};
|
||||
|
||||
class AstLayer {
|
||||
orc::IRLayer &baseLayer;
|
||||
orc::MangleAndInterner &mangler;
|
||||
|
||||
public:
|
||||
AstLayer(orc::IRLayer &baseLayer, orc::MangleAndInterner &mangler)
|
||||
: baseLayer(baseLayer), mangler(mangler){};
|
||||
|
||||
llvm::Error add(orc::ResourceTrackerSP &rt, Namespace &ns, exprs::Ast &ast) {
|
||||
return rt->getJITDylib().define(
|
||||
std::make_unique<AstMaterializationUnit>(ns, *this, ast), rt);
|
||||
}
|
||||
|
||||
void emit(std::unique_ptr<orc::MaterializationResponsibility> mr,
|
||||
Namespace &ns, exprs::Ast &e) {
|
||||
|
||||
baseLayer.emit(std::move(mr), compileAst(ns, e));
|
||||
}
|
||||
orc::MaterializationUnit::Interface getInterface(Namespace &ns,
|
||||
exprs::Ast &e);
|
||||
};
|
||||
|
||||
/// NS Layer ==================================================================
|
||||
class NSLayer;
|
||||
|
||||
/// This will compile the NS to llvm ir.
|
||||
llvm::orc::ThreadSafeModule compileNS(Namespace &ns);
|
||||
|
||||
class NSMaterializationUnit : public orc::MaterializationUnit {
|
||||
public:
|
||||
NSMaterializationUnit(NSLayer &l, Namespace &ns);
|
||||
|
||||
llvm::StringRef getName() const override { return "NSMaterializationUnit"; }
|
||||
|
||||
void
|
||||
materialize(std::unique_ptr<orc::MaterializationResponsibility> r) override;
|
||||
|
||||
private:
|
||||
void discard(const orc::JITDylib &jd,
|
||||
const orc::SymbolStringPtr &sym) override {
|
||||
UNUSED(jd);
|
||||
UNUSED(sym);
|
||||
llvm_unreachable("Serene functions are not overridable");
|
||||
// TODO: Check the ctx to see whether we need to remove the sym or not
|
||||
}
|
||||
|
||||
NSLayer &nsLayer;
|
||||
Namespace &ns;
|
||||
};
|
||||
|
||||
/// NS Layer is responsible for adding namespaces to the JIT
|
||||
class NSLayer {
|
||||
serene::SereneContext &ctx;
|
||||
orc::IRLayer &baseLayer;
|
||||
orc::MangleAndInterner &mangler;
|
||||
const llvm::DataLayout &dl;
|
||||
|
||||
public:
|
||||
NSLayer(SereneContext &ctx, orc::IRLayer &baseLayer,
|
||||
orc::MangleAndInterner &mangler, const llvm::DataLayout &dl)
|
||||
: ctx(ctx), baseLayer(baseLayer), mangler(mangler), dl(dl){};
|
||||
|
||||
llvm::Error add(orc::ResourceTrackerSP &rt, llvm::StringRef nsname) {
|
||||
auto loc = serene::reader::LocationRange::UnknownLocation(nsname);
|
||||
|
||||
return add(rt, nsname, loc);
|
||||
}
|
||||
|
||||
llvm::Error add(orc::ResourceTrackerSP &rt, llvm::StringRef nsname,
|
||||
serene::reader::LocationRange &loc);
|
||||
|
||||
void emit(std::unique_ptr<orc::MaterializationResponsibility> mr,
|
||||
serene::Namespace &ns) {
|
||||
// TODO: We need to pass dl to the compilerNS later to aviod recreating
|
||||
// the data layout all the time
|
||||
UNUSED(dl);
|
||||
LAYER_LOG("Emit namespace");
|
||||
baseLayer.emit(std::move(mr), compileNS(ns));
|
||||
}
|
||||
|
||||
orc::MaterializationUnit::Interface getInterface(serene::Namespace &ns);
|
||||
};
|
||||
} // namespace jit
|
||||
} // namespace serene
|
||||
#endif
|
|
@ -1,7 +0,0 @@
|
|||
#ifndef LLVM_IR_VALUE_H
|
||||
#define LLVM_IR_VALUE_H
|
||||
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#include <llvm/IR/Value.h>
|
||||
|
||||
#endif
|
|
@ -1,57 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LLVM_PATCHES_H
|
||||
#define LLVM_PATCHES_H
|
||||
|
||||
#include <llvm/ADT/DenseMap.h>
|
||||
#include <llvm/ADT/Hashing.h>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
// Our specialization of DensMapInfo for string type. This will allow use to use
|
||||
// string
|
||||
template <>
|
||||
struct DenseMapInfo<std::string> {
|
||||
static inline std::string getEmptyKey() { return ""; }
|
||||
|
||||
static inline std::string getTombstoneKey() {
|
||||
// Maybe we need to use something else beside strings ????
|
||||
return "0TOMBED";
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const std::string &Val) {
|
||||
assert(Val != getEmptyKey() && "Cannot hash the empty key!");
|
||||
assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
|
||||
return (unsigned)(llvm::hash_value(Val));
|
||||
}
|
||||
|
||||
static bool isEqual(const std::string &LHS, const std::string &RHS) {
|
||||
if (RHS == getEmptyKey()) {
|
||||
return LHS == getEmptyKey();
|
||||
}
|
||||
if (RHS == getTombstoneKey()) {
|
||||
return LHS == getTombstoneKey();
|
||||
}
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
|
@ -1,178 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Commentary:
|
||||
* Rules of a namespace:
|
||||
* - A namespace has have a name and it has to own it.
|
||||
* - A namespace may or may not be associated with a file
|
||||
* - The internal AST of a namespace is an evergrowing tree which may expand at
|
||||
* any given time. For example via iteration of a REPL
|
||||
* - `environments` vector is the owner of all the semantic envs
|
||||
* - The first env in the `environments` is the root env.
|
||||
*
|
||||
* How to create a namespace ?
|
||||
* The official way to create a namespace object is to use the `SereneContext`
|
||||
* object and call `readNamespace`, `importNamespace` or `makeNamespace`.
|
||||
*/
|
||||
|
||||
// TODO: Add a mechanism to figure out whether a namespace has changed or not
|
||||
// either on memory or disk
|
||||
|
||||
#ifndef SERENE_NAMESPACE_H
|
||||
#define SERENE_NAMESPACE_H
|
||||
|
||||
#include "serene/environment.h"
|
||||
#include "serene/errors.h"
|
||||
#include "serene/export.h"
|
||||
#include "serene/slir/generatable.h"
|
||||
#include "serene/traits.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/ADT/SmallString.h>
|
||||
#include <llvm/ADT/SmallVector.h>
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/ADT/Twine.h>
|
||||
#include <llvm/ExecutionEngine/Orc/Core.h>
|
||||
#include <llvm/ExecutionEngine/Orc/ThreadSafeModule.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
#include <mlir/IR/Builders.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/IR/OwningOpRef.h>
|
||||
#include <mlir/IR/Value.h>
|
||||
#include <mlir/Support/LogicalResult.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#define NAMESPACE_LOG(...) \
|
||||
DEBUG_WITH_TYPE("NAMESPACE", llvm::dbgs() << __VA_ARGS__ << "\n");
|
||||
|
||||
namespace serene {
|
||||
class SereneContext;
|
||||
class Namespace;
|
||||
|
||||
namespace exprs {
|
||||
class Expression;
|
||||
using Node = std::shared_ptr<Expression>;
|
||||
using Ast = std::vector<Node>;
|
||||
} // namespace exprs
|
||||
|
||||
using NSPtr = std::shared_ptr<Namespace>;
|
||||
using MaybeNS = llvm::Expected<NSPtr>;
|
||||
using MaybeModule = std::optional<llvm::orc::ThreadSafeModule>;
|
||||
using MaybeModuleOp = std::optional<mlir::OwningOpRef<mlir::ModuleOp>>;
|
||||
using SemanticEnv = Environment<exprs::Node>;
|
||||
using SemanticEnvPtr = std::unique_ptr<SemanticEnv>;
|
||||
using SemanticEnvironments = std::vector<SemanticEnvPtr>;
|
||||
using Form = std::pair<SemanticEnv, exprs::Ast>;
|
||||
using Forms = std::vector<Form>;
|
||||
|
||||
/// Serene's namespaces are the unit of compilation. Any code that needs to be
|
||||
/// compiled has to be in a namespace. The official way to create a new
|
||||
/// namespace is to use the `readNamespace`, `importNamespace` and
|
||||
/// `makeNamespace` member functions of `SereneContext`.
|
||||
class SERENE_EXPORT Namespace {
|
||||
friend SereneContext;
|
||||
|
||||
private:
|
||||
SereneContext &ctx;
|
||||
|
||||
// Anonymous function counter. We need to assing a unique name to each
|
||||
// anonymous function and we use this counter to generate those names
|
||||
std::atomic<uint> fn_counter = 0;
|
||||
|
||||
/// The content of the namespace. It should alway hold a semantically
|
||||
/// correct AST. It means thet the AST that we want to store here has
|
||||
/// to pass the semantic analyzer checks.
|
||||
exprs::Ast tree;
|
||||
|
||||
SemanticEnvironments environments;
|
||||
|
||||
std::vector<llvm::StringRef> symbolList;
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
std::optional<std::string> filename;
|
||||
|
||||
/// Create a naw namespace with the given `name` and optional `filename` and
|
||||
/// return a shared pointer to it in the given Serene context.
|
||||
static NSPtr make(SereneContext &ctx, llvm::StringRef name,
|
||||
std::optional<llvm::StringRef> filename);
|
||||
|
||||
Namespace(SereneContext &ctx, llvm::StringRef ns_name,
|
||||
std::optional<llvm::StringRef> filename);
|
||||
|
||||
/// Create a new environment with the give \p parent as the parent,
|
||||
/// push the environment to the internal environment storage and
|
||||
/// return a reference to it. The namespace itself is the owner of
|
||||
/// environments.
|
||||
SemanticEnv &createEnv(SemanticEnv *parent);
|
||||
|
||||
/// Return a referenece to the top level (root) environment of ns.
|
||||
SemanticEnv &getRootEnv();
|
||||
|
||||
/// Define a new binding in the root environment with the given \p name
|
||||
/// and the given \p node. Defining a new binding with a name that
|
||||
/// already exists in legal and will overwrite the previous binding and
|
||||
/// the given name will point to a new value from now on.
|
||||
mlir::LogicalResult define(std::string &name, exprs::Node &node);
|
||||
|
||||
/// Add the given \p ast to the namespace and return any possible error.
|
||||
/// The given \p ast will be added to a vector of ASTs by expanding
|
||||
/// the tree vector to contain \p ast.
|
||||
///
|
||||
/// This function runs the semantic analyzer on the \p ast as well.
|
||||
llvm::Error addTree(exprs::Ast &ast);
|
||||
|
||||
exprs::Ast &getTree();
|
||||
|
||||
const std::vector<llvm::StringRef> &getSymList() { return symbolList; };
|
||||
|
||||
/// Increase the function counter by one
|
||||
uint nextFnCounter();
|
||||
|
||||
SereneContext &getContext();
|
||||
|
||||
/// Generate and return a MLIR ModuleOp tha contains the IR of the namespace
|
||||
/// with respect to the compilation phase
|
||||
MaybeModuleOp generate(unsigned offset = 0);
|
||||
|
||||
/// Compile the namespace to a llvm module. It will call the
|
||||
/// `generate` method of the namespace to generate the IR.
|
||||
MaybeModule compileToLLVM();
|
||||
|
||||
/// Compile the given namespace from the given \p offset of AST till the end
|
||||
/// of the trees.
|
||||
MaybeModule compileToLLVMFromOffset(unsigned offset);
|
||||
|
||||
/// Run all the passes specified in the context on the given MLIR ModuleOp.
|
||||
mlir::LogicalResult runPasses(mlir::ModuleOp &m);
|
||||
|
||||
/// Dumps the namespace with respect to the compilation phase
|
||||
void dump();
|
||||
|
||||
~Namespace();
|
||||
};
|
||||
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,44 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_PASSES_H
|
||||
#define SERENE_PASSES_H
|
||||
|
||||
#include "serene/export.h"
|
||||
|
||||
#include <mlir/Pass/Pass.h>
|
||||
|
||||
#define PASS_LOG(...) \
|
||||
DEBUG_WITH_TYPE("PASSES", llvm::dbgs() << __VA_ARGS__ << "\n");
|
||||
|
||||
namespace serene::passes {
|
||||
|
||||
/// Return a pass to lower the serene.symbol op
|
||||
SERENE_EXPORT std::unique_ptr<mlir::Pass> createLowerSymbol();
|
||||
|
||||
SERENE_EXPORT void registerAllPasses();
|
||||
/// Return a pass to convert SLIR dialect to built-in dialects
|
||||
/// of MLIR.
|
||||
std::unique_ptr<mlir::Pass> createSLIRLowerToMLIRPass();
|
||||
|
||||
/// Return a pass to convert different dialects of MLIR to LLVM dialect.
|
||||
std::unique_ptr<mlir::Pass> createSLIRLowerToLLVMDialectPass();
|
||||
|
||||
} // namespace serene::passes
|
||||
|
||||
#endif
|
|
@ -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)
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_PASSES_TD
|
||||
#define SERENE_PASSES_TD
|
||||
|
||||
include "mlir/Pass/PassBase.td"
|
||||
|
||||
|
||||
// TODO: When we add and utilize namespace operations we need
|
||||
// use NsOp instead of the ModuleOp for root of lowering
|
||||
def LowerSLIR : Pass<"lower-slir", "mlir::ModuleOp"> {
|
||||
let summary = "Lowers the SLIR dialect to LLVM IR indirectly";
|
||||
|
||||
let description = [{
|
||||
Lowers the SLIR dialect to LLVM IR indirectly meaning
|
||||
that it will lower SLIR to other dialects that in turn will
|
||||
eventually lower to llvm ir.
|
||||
}];
|
||||
|
||||
// This is how we can create the default instance of this pass.
|
||||
// via `createLowerSymbol` function
|
||||
let constructor = "serene::passes::createLowerSLIR()";
|
||||
let dependentDialects = ["mlir::func::FuncDialect", "mlir::arith::ArithmeticDialect"];
|
||||
|
||||
}
|
||||
|
||||
#endif // SERENE_PASSES_TD
|
|
@ -1,92 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_READER_LOCATION_H
|
||||
#define SERENE_READER_LOCATION_H
|
||||
|
||||
#include <mlir/IR/Diagnostics.h>
|
||||
#include <mlir/IR/Location.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace serene {
|
||||
class SereneContext;
|
||||
|
||||
namespace reader {
|
||||
|
||||
/// It represents a location in the input string to the parser via `line`,
|
||||
struct Location {
|
||||
/// Since namespaces are our unit of compilation, we need to have
|
||||
/// a namespace in hand
|
||||
llvm::StringRef ns;
|
||||
|
||||
std::optional<llvm::StringRef> filename = std::nullopt;
|
||||
/// A pointer to the character that this location is pointing to
|
||||
/// it the input buffer
|
||||
const char *c = nullptr;
|
||||
|
||||
/// At this stage we only support 65535 lines of code in each file
|
||||
unsigned short int line = 0;
|
||||
/// At this stage we only support 65535 chars in each line
|
||||
unsigned short int col = 0;
|
||||
|
||||
bool knownLocation = true;
|
||||
|
||||
::std::string toString() const;
|
||||
|
||||
Location() = default;
|
||||
Location(llvm::StringRef ns,
|
||||
std::optional<llvm::StringRef> fname = std::nullopt,
|
||||
const char *c = nullptr, unsigned short int line = 0,
|
||||
unsigned short int col = 0, bool knownLocation = true)
|
||||
: ns(ns), filename(fname), c(c), line(line), col(col),
|
||||
knownLocation(knownLocation){};
|
||||
|
||||
Location clone() const;
|
||||
|
||||
mlir::Location toMLIRLocation(SereneContext &ctx);
|
||||
|
||||
/// Returns an unknown location for the given \p ns.
|
||||
static Location UnknownLocation(llvm::StringRef ns) {
|
||||
return Location(ns, std::nullopt, nullptr, 0, 0, false);
|
||||
}
|
||||
};
|
||||
|
||||
class LocationRange {
|
||||
public:
|
||||
Location start;
|
||||
Location end;
|
||||
|
||||
LocationRange() = default;
|
||||
LocationRange(Location _start) : start(_start), end(_start){};
|
||||
LocationRange(Location _start, Location _end) : start(_start), end(_end){};
|
||||
// LocationRange(const LocationRange &);
|
||||
|
||||
bool isKnownLocation() const { return start.knownLocation; };
|
||||
|
||||
static LocationRange UnknownLocation(llvm::StringRef ns) {
|
||||
return LocationRange(Location::UnknownLocation(ns));
|
||||
}
|
||||
};
|
||||
|
||||
void incLocation(Location &, const char *);
|
||||
void decLocation(Location &, const char *);
|
||||
|
||||
} // namespace reader
|
||||
} // namespace serene
|
||||
#endif
|
|
@ -1,137 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Commentary:
|
||||
* `Reader` is the base parser class and accepts a buffer like objenct (usually
|
||||
* `llvm::StringRef`) as the input and parsess it to create an AST (look at the
|
||||
* `serene::exprs::Expression` class).
|
||||
*
|
||||
* The parsing algorithm is quite simple and it is a LL(2). It means that, we
|
||||
* start parsing the input from the very first character and parse the input
|
||||
* one char at a time till we reach the end of the input. Please note that
|
||||
* when we call the `advance` function to move forward in the buffer, we
|
||||
* can't go back. In order to look ahead in the buffer without moving in the
|
||||
* buffer we use the `nextChar` method.
|
||||
*
|
||||
* We have dedicated methods to read different forms like `list`, `symbol`
|
||||
* `number` and etc. Each of them return a `MaybeNode` that in the success
|
||||
* case contains the node and an `Error` on the failure case.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_READER_READER_H
|
||||
#define SERENE_READER_READER_H
|
||||
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/list.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/serene.h"
|
||||
|
||||
#include <serene/export.h>
|
||||
#include <system_error>
|
||||
|
||||
#include <llvm/Support/Debug.h>
|
||||
#include <llvm/Support/MemoryBuffer.h>
|
||||
#include <llvm/Support/MemoryBufferRef.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define READER_LOG(...) \
|
||||
DEBUG_WITH_TYPE("READER", llvm::dbgs() \
|
||||
<< "[READER]: " << __VA_ARGS__ << "\n");
|
||||
|
||||
namespace serene::reader {
|
||||
|
||||
/// Base reader class which reads from a string directly.
|
||||
class Reader {
|
||||
private:
|
||||
SereneContext &ctx;
|
||||
|
||||
llvm::StringRef ns;
|
||||
std::optional<llvm::StringRef> filename;
|
||||
|
||||
const char *currentChar = NULL;
|
||||
|
||||
llvm::StringRef buf;
|
||||
|
||||
/// The position tracker that we will use to determine the end of the
|
||||
/// buffer since the buffer might not be null terminated
|
||||
size_t currentPos = -1;
|
||||
|
||||
Location currentLocation;
|
||||
|
||||
bool readEOL = false;
|
||||
|
||||
/// Returns a clone of the current location
|
||||
Location getCurrentLocation();
|
||||
/// Returns the next character from the stream.
|
||||
/// @param skip_whitespace An indicator to whether skip white space like chars
|
||||
/// or not
|
||||
void advance(bool skipWhitespace = false);
|
||||
void advanceByOne();
|
||||
|
||||
const char *nextChar(bool skipWhitespace = false, unsigned count = 1);
|
||||
|
||||
/// Returns a boolean indicating whether the given input character is valid
|
||||
/// for an identifier or not.
|
||||
static bool isValidForIdentifier(char c);
|
||||
|
||||
// The property to store the ast tree
|
||||
exprs::Ast ast;
|
||||
|
||||
exprs::MaybeNode readSymbol();
|
||||
exprs::MaybeNode readNumber(bool);
|
||||
exprs::MaybeNode readList();
|
||||
exprs::MaybeNode readExpr();
|
||||
|
||||
bool isEndOfBuffer(const char *);
|
||||
|
||||
public:
|
||||
Reader(SereneContext &ctx, llvm::StringRef buf, llvm::StringRef ns,
|
||||
std::optional<llvm::StringRef> filename);
|
||||
Reader(SereneContext &ctx, llvm::MemoryBufferRef buf, llvm::StringRef ns,
|
||||
std::optional<llvm::StringRef> filename);
|
||||
|
||||
// void setInput(const llvm::StringRef string);
|
||||
|
||||
/// Parses the the input and creates a possible AST out of it or errors
|
||||
/// otherwise.
|
||||
exprs::MaybeAst read();
|
||||
|
||||
~Reader();
|
||||
};
|
||||
|
||||
/// Parses the given `input` string and returns a `Result<ast>`
|
||||
/// which may contains an AST or an `llvm::Error`
|
||||
SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx, llvm::StringRef input,
|
||||
llvm::StringRef ns,
|
||||
std::optional<llvm::StringRef> filename);
|
||||
SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx,
|
||||
llvm::MemoryBufferRef input,
|
||||
llvm::StringRef ns,
|
||||
std::optional<llvm::StringRef> filename);
|
||||
} // namespace serene::reader
|
||||
|
||||
#endif
|
|
@ -1,40 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_READER_TRAITS_H
|
||||
#define SERENE_READER_TRAITS_H
|
||||
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/traits.h"
|
||||
|
||||
namespace serene::reader {
|
||||
|
||||
template <typename ConcreteType>
|
||||
class ILocatable : public TraitBase<ConcreteType, ILocatable> {
|
||||
public:
|
||||
ILocatable(){};
|
||||
ILocatable(const ILocatable &) = delete;
|
||||
serene::reader::LocationRange &where() const {
|
||||
return this->Object().where();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
serene::reader::LocationRange &where(ILocatable<T> &);
|
||||
} // namespace serene::reader
|
||||
#endif
|
|
@ -1,75 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_SEMANTICS_H
|
||||
#define SERENE_SEMANTICS_H
|
||||
|
||||
#include "serene/environment.h"
|
||||
#include "serene/errors.h"
|
||||
#include "serene/export.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace serene {
|
||||
|
||||
namespace exprs {
|
||||
class Expression;
|
||||
using Node = std::shared_ptr<Expression>;
|
||||
using Ast = std::vector<Node>;
|
||||
}; // namespace exprs
|
||||
|
||||
class Namespace;
|
||||
using SemanticEnv = Environment<exprs::Node>;
|
||||
namespace semantics {
|
||||
|
||||
using AnalyzeResult = llvm::Expected<exprs::Ast>;
|
||||
|
||||
/// This struct represent the state necessary for each analysis job.
|
||||
struct AnalysisState {
|
||||
Namespace &ns;
|
||||
SemanticEnv &env;
|
||||
|
||||
AnalysisState(Namespace &ns, SemanticEnv &env) : ns(ns), env(env){};
|
||||
|
||||
std::unique_ptr<AnalysisState> moveToNewEnv();
|
||||
};
|
||||
|
||||
/// Create an new `AnalysisState` by forwarding all parameters off this
|
||||
/// function directly to the ctor of `AnalysisState` and returns a
|
||||
/// unique pointer to the state.
|
||||
template <typename... Args>
|
||||
std::unique_ptr<AnalysisState> makeAnalysisState(Args &&...args) {
|
||||
return std::make_unique<AnalysisState>(std::forward<Args>(args)...);
|
||||
};
|
||||
|
||||
/// The entry point to the Semantic analysis phase. It calls the `analyze`
|
||||
/// method of each node in the given \p form and creates a new AST that
|
||||
/// contains a more comprehensive set of nodes in a semantically correct
|
||||
/// AST. If the `analyze` method of a node return a `nullptr` value then the
|
||||
/// original node will be used instead. Any possible error will be returned.
|
||||
///
|
||||
/// \param state The semantic analysis state that keep track of the envs.
|
||||
/// \param form The actual AST in question.
|
||||
SERENE_EXPORT AnalyzeResult analyze(AnalysisState &state, exprs::Ast &forms);
|
||||
|
||||
} // namespace semantics
|
||||
} // namespace serene
|
||||
#endif
|
|
@ -1,79 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_SERENE_H
|
||||
#define SERENE_SERENE_H
|
||||
|
||||
#include "serene/config.h"
|
||||
#include "serene/context.h"
|
||||
#include "serene/export.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/source_mgr.h"
|
||||
|
||||
namespace serene {
|
||||
|
||||
/// Clinet applications have to call this function before any interaction with
|
||||
/// the Serene's compiler API.
|
||||
SERENE_EXPORT void initCompiler();
|
||||
|
||||
/// Register the global CLI options of the serene compiler. If the client
|
||||
/// application needs to setup the compilers options automatically use this
|
||||
/// function in conjunction with `applySereneCLOptions`.
|
||||
SERENE_EXPORT void registerSereneCLOptions();
|
||||
|
||||
/// Applies the global compiler options on the give \p SereneContext. This
|
||||
/// function has to be called after `llvm::cl::ParseCommandLineOptions`.
|
||||
SERENE_EXPORT void applySereneCLOptions(SereneContext &ctx);
|
||||
|
||||
/// \brief Reads the the given \p input as Serene source code in the given
|
||||
/// \c SereneContext \p ctx and returns the possible AST tree of the input or an
|
||||
/// error otherwise.
|
||||
///
|
||||
/// If any thing goes wrong it will return an \return \c llvm::Error describing
|
||||
/// the issue.
|
||||
///
|
||||
/// Be aware than this function reads the input in the context of the current
|
||||
/// namespace. So for example if the input is somthing like:
|
||||
///
|
||||
/// (ns example.code) ....
|
||||
///
|
||||
/// and the current ns is `user` then if there is a syntax error in the input
|
||||
/// the error will be reported under the `user` ns. This is logical because
|
||||
/// this function reads the code and not evaluate it. the `ns` form has to be
|
||||
/// evaluated in order to change the ns.
|
||||
SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx, std::string &input);
|
||||
|
||||
/// Evaluates the given AST form \p input in the given \c SereneContext \p ctx
|
||||
/// and retuns a new expression as the result or a possible error.
|
||||
///
|
||||
/// In case of an error Serene will throw the error messages vis the diagnostic
|
||||
/// engine as well and the error that this function returns will be the
|
||||
/// generic error message.
|
||||
SERENE_EXPORT exprs::MaybeNode eval(SereneContext &ctx, exprs::Ast &input);
|
||||
|
||||
// TODO: Return a Serene String type instead of the std::string
|
||||
// TODO: Create an overload to get a stream instead of the result string
|
||||
/// Prints the given AST form \p input in the given \c SereneContext \p ctx
|
||||
/// into the given \p result.
|
||||
/// Note: print is a lisp action. Don't confuse it with a print function such
|
||||
/// as `println`.
|
||||
SERENE_EXPORT void print(SereneContext &ctx, const exprs::Ast &input,
|
||||
std::string &result);
|
||||
|
||||
} // namespace serene
|
||||
#endif
|
|
@ -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)
|
|
@ -1,45 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef SERENE_SLIR_DIALECT_H
|
||||
#define SERENE_SLIR_DIALECT_H
|
||||
|
||||
#include <serene/export.h>
|
||||
|
||||
#include <llvm/ADT/TypeSwitch.h>
|
||||
#include <mlir/Dialect/Arithmetic/IR/Arithmetic.h>
|
||||
#include <mlir/Dialect/Func/IR/FuncOps.h>
|
||||
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/IR/Dialect.h>
|
||||
#include <mlir/IR/DialectRegistry.h>
|
||||
#include <mlir/IR/FunctionInterfaces.h>
|
||||
#include <mlir/Interfaces/CallInterfaces.h>
|
||||
#include <mlir/Interfaces/ControlFlowInterfaces.h>
|
||||
#include <mlir/Interfaces/InferTypeOpInterface.h>
|
||||
#include <mlir/Interfaces/SideEffectInterfaces.h>
|
||||
|
||||
// Include the auto-generated header file containing the declaration of the
|
||||
// serene's dialect.
|
||||
#include "serene/slir/dialect.h.inc"
|
||||
#include "serene/slir/types.h"
|
||||
|
||||
namespace serene::slir {
|
||||
SERENE_EXPORT void registerTo(mlir::DialectRegistry ®istry);
|
||||
} // namespace serene::slir
|
||||
//
|
||||
#endif // SERENE_SLIR_DIALECT_H
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_DIALECT_TD
|
||||
#define SERENE_DIALECT_TD
|
||||
|
||||
|
||||
include "mlir/IR/OpBase.td"
|
||||
include "mlir/IR/OpAsmInterface.td"
|
||||
include "mlir/IR/AttrTypeBase.td"
|
||||
include "mlir/Interfaces/SideEffectInterfaces.td"
|
||||
include "mlir/Interfaces/ControlFlowInterfaces.td"
|
||||
include "mlir/Interfaces/InferTypeOpInterface.td"
|
||||
include "mlir/Interfaces/DataLayoutInterfaces.td"
|
||||
include "mlir/Interfaces/VectorInterfaces.td"
|
||||
|
||||
// Dialect definition. It will directly generate the SereneDialect class
|
||||
def Serene_Dialect : Dialect {
|
||||
let name = "serene";
|
||||
let cppNamespace = "::serene::slir";
|
||||
let summary = "Primary IR of serene language.";
|
||||
let description = [{
|
||||
This dialect tries to map the special forms of a lisp into
|
||||
IR level operations.
|
||||
}];
|
||||
|
||||
// All the types have to define a mnemonic
|
||||
let useDefaultTypePrinterParser = 1;
|
||||
|
||||
// TODO: Uncomment this when ever we have custom attrs
|
||||
// All the attributes have to define a mnemonic
|
||||
// let useDefaultAttributePrinterParser = 1;
|
||||
|
||||
let dependentDialects = [
|
||||
"mlir::arith::ArithmeticDialect",
|
||||
"mlir::func::FuncDialect",
|
||||
"mlir::LLVM::LLVMDialect"
|
||||
];
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
/// Register all the Serene types
|
||||
void registerType();
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
include "types.td"
|
||||
include "ops.td"
|
||||
|
||||
// ============================================================================
|
||||
// Old operations that are we need to replace
|
||||
// ============================================================================
|
||||
def Value1Op: Serene_Op<"value1"> {
|
||||
let summary = "This operation represent a compile time value";
|
||||
let description = [{
|
||||
ValueOp represent a value in compile time. For example:
|
||||
|
||||
```mlir
|
||||
%0 = "serene.value"(){value = 3} : () -> i64
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins I64Attr:$value);
|
||||
let results = (outs I64);
|
||||
|
||||
//let verifier = [{ return serene::sir::verify(*this); }];
|
||||
|
||||
let builders = [
|
||||
OpBuilder<(ins "int":$value), [{
|
||||
// Build from fix 64 bit int
|
||||
build(odsBuilder, odsState, odsBuilder.getI64Type(), (uint64_t) value);
|
||||
}]>,
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
def Fn1Op: Serene_Op<"fn1", [
|
||||
AffineScope, AutomaticAllocationScope,
|
||||
IsolatedFromAbove,
|
||||
]> {
|
||||
|
||||
let summary = "This operation is just a place holder for a function";
|
||||
let description = [{
|
||||
A place holder for an anonymous function. For example consider an expression
|
||||
like `(def a (fn (x) x))`, in this case we don't immediately create an anonymous
|
||||
function since we need to set the name and create the function later.
|
||||
}];
|
||||
|
||||
let arguments = (ins StrAttr:$name,
|
||||
DictionaryAttr:$args,
|
||||
OptionalAttr<StrAttr>:$sym_visibility);
|
||||
|
||||
let regions = (region AnyRegion:$body);
|
||||
let results = (outs FnType);
|
||||
|
||||
}
|
||||
|
||||
def Return1Op: Serene_Op<"return", [NoSideEffect, HasParent<"Fn1Op">,
|
||||
ReturnLike, Terminator]> {
|
||||
|
||||
let summary = "This operation marks the return value of a function";
|
||||
let description = [{
|
||||
ReturnOp
|
||||
}];
|
||||
|
||||
let arguments = (ins AnyType:$operand);
|
||||
let assemblyFormat =
|
||||
[{ attr-dict $operand `:` type($operand) }];
|
||||
}
|
||||
|
||||
#endif // SERENE_DIALECT
|
|
@ -1,121 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_SLIR_GENERATABLE_H
|
||||
#define SERENE_SLIR_GENERATABLE_H
|
||||
|
||||
#include "serene/slir/dialect.h"
|
||||
#include "serene/traits.h"
|
||||
|
||||
#include <llvm/ADT/STLExtras.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
#include <mlir/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <mlir/ExecutionEngine/OptUtils.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/IR/MLIRContext.h>
|
||||
#include <mlir/Support/LogicalResult.h>
|
||||
#include <mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h>
|
||||
#include <mlir/Target/LLVMIR/ModuleTranslation.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace serene {
|
||||
class Namespace;
|
||||
class SereneContext;
|
||||
} // namespace serene
|
||||
|
||||
namespace serene::slir {
|
||||
|
||||
template <typename T>
|
||||
class GeneratableUnit : public TraitBase<T, GeneratableUnit> {
|
||||
public:
|
||||
GeneratableUnit(){};
|
||||
GeneratableUnit(const GeneratableUnit &) = delete;
|
||||
|
||||
void generate(serene::Namespace &ns) {
|
||||
// TODO: should we return any status or possible error here or
|
||||
// should we just populate them in a ns wide state?
|
||||
this->Object().generateIR(ns);
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Generatable : public TraitBase<T, Generatable> {
|
||||
public:
|
||||
Generatable(){};
|
||||
Generatable(const Generatable &) = delete;
|
||||
|
||||
mlir::LogicalResult generate() { return this->Object().generate(); };
|
||||
mlir::LogicalResult runPasses() { return this->Object().runPasses(); };
|
||||
|
||||
mlir::ModuleOp &getModule() { return this->Object().getModule(); };
|
||||
serene::SereneContext &getContext() { return this->Object().getContext(); };
|
||||
|
||||
void dump() { this->Object().dump(); };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
mlir::LogicalResult generate(Generatable<T> &t) {
|
||||
return t.generate();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::unique_ptr<llvm::Module> toLLVMIR(Generatable<T> &t) {
|
||||
auto &module = t.getModule();
|
||||
auto &ctx = t.getContext();
|
||||
// Register the translation to LLVM IR with the MLIR context.
|
||||
mlir::registerLLVMDialectTranslation(ctx.mlirContext);
|
||||
|
||||
// Convert the module to LLVM IR in a new LLVM IR context.
|
||||
auto llvmModule = mlir::translateModuleToLLVMIR(module, ctx.llvmContext);
|
||||
if (!llvmModule) {
|
||||
// TODO: Return a Result type instead
|
||||
llvm::errs() << "Failed to emit LLVM IR\n";
|
||||
throw std::runtime_error("Failed to emit LLVM IR\n");
|
||||
}
|
||||
|
||||
// Initialize LLVM targets.
|
||||
llvm::InitializeNativeTarget();
|
||||
// llvm::InitializeNativeTargetAsmPrinter();
|
||||
|
||||
// TODO: replace this call with our own version of setupTargetTriple
|
||||
mlir::ExecutionEngine::setupTargetTriple(llvmModule.get());
|
||||
|
||||
/// Optionally run an optimization pipeline over the llvm module.
|
||||
auto optPipeline = mlir::makeOptimizingTransformer(
|
||||
/*optLevel=*/ctx.getOptimizatioLevel(), /*sizeLevel=*/0,
|
||||
/*targetMachine=*/nullptr);
|
||||
if (auto err = optPipeline(llvmModule.get())) {
|
||||
llvm::errs() << "Failed to optimize LLVM IR " << err << "\n";
|
||||
throw std::runtime_error("Failed to optimize LLVM IR");
|
||||
}
|
||||
|
||||
return std::move(llvmModule);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void dump(Generatable<T> &t) {
|
||||
t.dump();
|
||||
};
|
||||
|
||||
} // namespace serene::slir
|
||||
|
||||
#endif
|
|
@ -1,40 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_DIALECT_OPS_H
|
||||
#define SERENE_DIALECT_OPS_H
|
||||
|
||||
#include "serene/slir/types.h"
|
||||
|
||||
#include <llvm/ADT/TypeSwitch.h>
|
||||
#include <mlir/Dialect/Arithmetic/IR/Arithmetic.h>
|
||||
#include <mlir/Dialect/Func/IR/FuncOps.h>
|
||||
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/IR/Dialect.h>
|
||||
#include <mlir/IR/DialectRegistry.h>
|
||||
#include <mlir/IR/FunctionInterfaces.h>
|
||||
#include <mlir/Interfaces/CallInterfaces.h>
|
||||
#include <mlir/Interfaces/ControlFlowInterfaces.h>
|
||||
#include <mlir/Interfaces/InferTypeOpInterface.h>
|
||||
#include <mlir/Interfaces/SideEffectInterfaces.h>
|
||||
|
||||
#define GET_OP_CLASSES
|
||||
#include "serene/slir/ops.h.inc"
|
||||
|
||||
#endif
|
|
@ -1,370 +0,0 @@
|
|||
/*
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_DIALECT_OPS_TD
|
||||
#define SERENE_DIALECT_OPS_TD
|
||||
|
||||
include "mlir/IR/OpBase.td"
|
||||
include "mlir/Interfaces/CallInterfaces.td"
|
||||
include "mlir/IR/RegionKindInterface.td"
|
||||
include "mlir/IR/SymbolInterfaces.td"
|
||||
include "mlir/IR/OpAsmInterface.td"
|
||||
|
||||
// Base class for Serene dialect operations. This operation inherits from the base
|
||||
// `Op` class in OpBase.td, and provides:
|
||||
// * The parent dialect of the operation.
|
||||
// * The mnemonic for the operation, or the name without the dialect prefix.
|
||||
// * A list of traits for the operation.
|
||||
class Serene_Op<string mnemonic, list<Trait> traits = []> :
|
||||
Op<Serene_Dialect, mnemonic, traits>;
|
||||
|
||||
|
||||
// serene.value
|
||||
def ValueOp : Serene_Op<"value", [
|
||||
ConstantLike, NoSideEffect,
|
||||
TypesMatchWith<
|
||||
"result and attribute have the same type",
|
||||
"value", "result", "$_self">]> {
|
||||
|
||||
let summary = "This operation represent a compile time value";
|
||||
let description = [{
|
||||
The `value` operation produces an SSA value equal to value
|
||||
specified by an attribute. This is the way Serene marks constant
|
||||
compile time values.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
// Integer constant
|
||||
%1 = serene.value 42 : i32
|
||||
|
||||
// Equivalent generic form
|
||||
%1 = "serene.value"() {value = 42 : i32} : () -> i32
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins AnyAttr:$value);
|
||||
let results = (outs AnyType:$result);
|
||||
|
||||
let builders = [
|
||||
OpBuilder<(ins "mlir::Attribute":$value),
|
||||
[{ build($_builder, $_state, value.getType(), value); }]>,
|
||||
OpBuilder<(ins "mlir::Attribute":$value, "mlir::Type":$type),
|
||||
[{ build($_builder, $_state, type, value); }]>,
|
||||
];
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
/// Whether the constant op can be constructed with a particular value and
|
||||
/// type.
|
||||
static bool isBuildableWith(mlir::Attribute value, mlir::Type type);
|
||||
}];
|
||||
|
||||
// Need to define the ::fold() method to make value op foldable
|
||||
// let hasFolder = 1;
|
||||
let assemblyFormat = "attr-dict $value";
|
||||
let hasFolder = 1;
|
||||
}
|
||||
|
||||
// serene.intern
|
||||
def InternOp : Serene_Op<"intern", [
|
||||
NoSideEffect
|
||||
]> {
|
||||
|
||||
let summary = "This operation is the runtime contructor for symbol type";
|
||||
let description = [{
|
||||
The `intern` operation produces an SSA value that holds a value of type
|
||||
symbol at runtime.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
|
||||
%ns = serene.string "some.ns"
|
||||
%name = serene.string "symbol_name"
|
||||
%1 = serene.intern %ns %name
|
||||
|
||||
// Equivalent generic form
|
||||
%1 = "serene.symbol"(%ns, %name){} : (!serene.string, !serene.string) -> !serene.ptr<serene.symbol>
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins StringType:$ns, StringType:$name);
|
||||
let results = (outs Ptr<SymbolType>:$result);
|
||||
|
||||
let assemblyFormat = "attr-dict $ns $name";
|
||||
}
|
||||
|
||||
// serene.symbol
|
||||
def SymbolOp : Serene_Op<"symbol", [
|
||||
NoSideEffect, ConstantLike,
|
||||
]> {
|
||||
|
||||
let summary = "This operation is the compile time contructor for symbol type";
|
||||
let description = [{
|
||||
The `symbol` operation produces an SSA value that holds a value of type
|
||||
symbol at compile time.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
|
||||
%1 = serene.symbol "some.ns" "symbol_name"
|
||||
|
||||
// Equivalent generic form
|
||||
%1 = "serene.symbol"() {ns = "some.ns", name = "symbol_name"} : () -> i32
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins SymbolAttr:$value);
|
||||
let results = (outs Ptr<SymbolType>:$result);
|
||||
|
||||
let assemblyFormat = "attr-dict $value";
|
||||
let hasFolder = 1;
|
||||
}
|
||||
|
||||
// serene.convert
|
||||
def ConvertOp : Serene_Op<"convert", [
|
||||
NoSideEffect
|
||||
]> {
|
||||
|
||||
let summary = "This operation converts a symbol to the equivelant llvm type";
|
||||
let description = [{
|
||||
This operation converts a symbol to the equivelant llvm type
|
||||
}];
|
||||
|
||||
let arguments = (ins AnyType:$value);
|
||||
|
||||
let results = (outs AnyType:$result);
|
||||
|
||||
let assemblyFormat = [{
|
||||
$value attr-dict `:` functional-type($value, results)
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Define Family
|
||||
// ============================================================================
|
||||
|
||||
// serene.define
|
||||
def DefineOp: Serene_Op<"define", [Symbol]> {
|
||||
let summary = "This operation defines a global binding in the current namespace";
|
||||
let description = [{
|
||||
`define` defines a global binding in the current namespace. It always return a
|
||||
symbol type.
|
||||
|
||||
Examples:
|
||||
|
||||
```mlir
|
||||
%foo = "serene.define"(%0){name = "foo"}: (i64) -> !serene.symbol
|
||||
|
||||
// compact form
|
||||
%bar = serene.define "bar", %0 : i64
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins SymbolNameAttr:$sym_name, AnyType:$value,
|
||||
OptionalAttr<BoolAttr>:$is_top_level,
|
||||
OptionalAttr<StrAttr>:$sym_visibility);
|
||||
|
||||
//let results = (outs SereneSymbol);
|
||||
let assemblyFormat = "attr-dict $sym_name `,` $value `:` type($value)";
|
||||
}
|
||||
|
||||
// serene.define_constant
|
||||
def DefineConstantOp: Serene_Op<"define_constant", [Symbol]> {
|
||||
let summary = "This operation defines a constant global binding in the current namespace";
|
||||
let description = [{
|
||||
`define` defines a constant global binding in the current namespace. It always return a
|
||||
symbol type.
|
||||
|
||||
Examples:
|
||||
|
||||
```mlir
|
||||
%foo = "serene.define_constant"(%0){name = "foo"}: (i64) -> !serene.symbol
|
||||
|
||||
// compact form
|
||||
%bar = serene.define_constant "bar", %0 : i64
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins SymbolNameAttr:$sym_name, AnyAttr:$value,
|
||||
OptionalAttr<BoolAttr>:$is_top_level,
|
||||
OptionalAttr<StrAttr>:$sym_visibility);
|
||||
|
||||
// let results = (outs SereneSymbol);
|
||||
let assemblyFormat = "attr-dict $sym_name $value";
|
||||
}
|
||||
|
||||
// serene.set_value
|
||||
def SetValueOp: Serene_Op<"set_value", []> {
|
||||
|
||||
let summary = "Sets the value of a SLIRS global varible.";
|
||||
let description = [{
|
||||
This operation sets the value of a SLIR global variable. This operation
|
||||
is NOT threadsafe.
|
||||
|
||||
}];
|
||||
|
||||
let arguments = (ins SymbolRefAttr:$var,
|
||||
AnyType:$value);
|
||||
|
||||
let assemblyFormat = "attr-dict $var `,` $value `:` type($value)";
|
||||
}
|
||||
|
||||
// serene.fn
|
||||
def FnOp: Serene_Op<"fn", [
|
||||
AffineScope, AutomaticAllocationScope,
|
||||
IsolatedFromAbove,
|
||||
]> {
|
||||
|
||||
let summary = "This operation is just a place holder for a function";
|
||||
let description = [{
|
||||
A place holder for an anonymous function. For example consider an expression
|
||||
like `(def a (fn (x) x))`, in this case we don't immediately create an anonymous
|
||||
function since we need to set the name and create the function later.
|
||||
|
||||
|
||||
}];
|
||||
|
||||
let arguments = (ins StrAttr:$name,
|
||||
TypeAttr:$return_type,
|
||||
OptionalAttr<StrAttr>:$sym_visibility);
|
||||
|
||||
let regions = (region VariadicRegion<AnyRegion>:$bodies);
|
||||
let results = (outs FnType);
|
||||
}
|
||||
|
||||
|
||||
// serene.ret
|
||||
def ReturnOp: Serene_Op<"ret", [
|
||||
NoSideEffect, HasParent<"FnOp">,
|
||||
ReturnLike, Terminator
|
||||
// MemRefsNormalizable
|
||||
]> {
|
||||
|
||||
let summary = "This operation marks the return value of a function";
|
||||
let description = [{
|
||||
`ret` marks the return value of a function body.
|
||||
Example:
|
||||
|
||||
```mlir
|
||||
serene.ret %foo : i32
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins Variadic<AnyType>:$operands);
|
||||
|
||||
let builders = [OpBuilder<(ins), [{
|
||||
build($_builder, $_state, llvm::None);
|
||||
}]>];
|
||||
|
||||
let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?";
|
||||
// TODO: Turn on the verifier for `ret`
|
||||
// let hasVerifier = 1;
|
||||
}
|
||||
|
||||
|
||||
// serene.call
|
||||
def CallOp : Serene_Op<"call",
|
||||
[MemRefsNormalizable]> {
|
||||
let summary = "call operation";
|
||||
let description = [{
|
||||
The `serene.call` operation represents a direct call to a function that is
|
||||
within the same symbol scope as the call. The operands and result types of
|
||||
the call must match the specified function type. The callee is encoded as a
|
||||
symbol reference attribute named "callee".
|
||||
|
||||
Example:
|
||||
|
||||
```mlir
|
||||
%2 = serene.call @my_add(%0, %1) : (f32, f32) -> f32
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins FnType:$fn,
|
||||
Variadic<AnyType>:$args);
|
||||
let results = (outs Variadic<AnyType>);
|
||||
|
||||
let assemblyFormat = [{
|
||||
$fn `(` $args `)` attr-dict `:` functional-type($args, results)
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
// TODO: Do we need to have a NS type?
|
||||
def NsOp : Serene_Op<"ns", [
|
||||
AffineScope, IsolatedFromAbove, NoRegionArguments, SymbolTable, Symbol,
|
||||
OpAsmOpInterface
|
||||
] # GraphRegionNoTerminator.traits> {
|
||||
let summary = "This operation represents a Serene namespace.";
|
||||
let description = [{
|
||||
The `serene.ns` operation represents a namespace that will eventually
|
||||
lowers to a `mlir::ModuleOp`.
|
||||
|
||||
Example:
|
||||
|
||||
```mlir
|
||||
serene.ns @some.ns {
|
||||
....
|
||||
}
|
||||
```
|
||||
}];
|
||||
|
||||
// TODO: Create a new Attr type that is an array to represent
|
||||
// required namespaces
|
||||
let arguments = (ins SymbolNameAttr:$sym_name,
|
||||
OptionalAttr<StrAttr>:$sym_visibility);
|
||||
|
||||
let regions = (region SizedRegion<1>:$body);
|
||||
|
||||
let assemblyFormat = "$sym_name attr-dict-with-keyword $body";
|
||||
|
||||
let builders = [
|
||||
OpBuilder<(ins CArg<"llvm::Optional<llvm::StringRef>", "{}">:$name)>
|
||||
];
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
/// Construct a namespace from the given location with an optional name.
|
||||
// static NsOp create(slir::reader::LocationRange loc, Optional<StringRef> name = llvm::None);
|
||||
|
||||
/// Return the name of this module if present.
|
||||
llvm::StringRef getName() { return sym_name(); }
|
||||
|
||||
//===------------------------------------------------------------------===//
|
||||
// SymbolOpInterface Methods
|
||||
//===------------------------------------------------------------------===//
|
||||
|
||||
/// A ModuleOp may optionally define a symbol.
|
||||
bool isOptionalSymbol() { return false; }
|
||||
|
||||
//===------------------------------------------------------------------===//
|
||||
// DataLayoutOpInterface Methods
|
||||
//===------------------------------------------------------------------===//
|
||||
mlir::DataLayoutSpecInterface getDataLayoutSpec();
|
||||
|
||||
//===------------------------------------------------------------------===//
|
||||
// OpAsmOpInterface Methods
|
||||
//===------------------------------------------------------------------===//
|
||||
static ::llvm::StringRef getDefaultDialect() {
|
||||
return "builtin";
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#endif // SERENE_DIALECT_OPS
|
|
@ -1,39 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef SERENE_SLIR_SLIR_H
|
||||
#define SERENE_SLIR_SLIR_H
|
||||
|
||||
#include "serene/exprs/expression.h"
|
||||
|
||||
#include <llvm/ADT/Optional.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/IR/MLIRContext.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace serene {
|
||||
namespace slir {
|
||||
// std::unique_ptr<llvm::Module> compileToLLVMIR(serene::SereneContext &ctx,
|
||||
// mlir::ModuleOp &module);
|
||||
|
||||
std::optional<llvm::orc::ThreadSafeModule>
|
||||
compileToLLVMIR(serene::SereneContext &ctx, mlir::ModuleOp &module);
|
||||
} // namespace slir
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,81 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// SereneSymbol is the Symbil type in SLIT that represents an expr::Sympol
|
||||
|
||||
#ifndef SERENE_SLIR_SYMBOL_H
|
||||
#define SERENE_SLIR_SYMBOL_H
|
||||
|
||||
#include <llvm/Support/ErrorHandling.h>
|
||||
#include <mlir/IR/TypeSupport.h>
|
||||
|
||||
namespace serene {
|
||||
namespace slir {
|
||||
namespace detail {
|
||||
/// This is the storage class for the `SymbolType` (SereneSymbol in ODS)
|
||||
// class SymbolTypeStorage : public mlir::TypeStorage {
|
||||
// using KeyTy = std::string;
|
||||
|
||||
// SymbolTypeStorage(std::string &ns, std::string &name) : ns(ns), name(name)
|
||||
// {} SymbolTypeStorage(const KeyTy &k) {
|
||||
|
||||
// auto partDelimiter = k.find('/');
|
||||
// if (partDelimiter == std::string::npos) {
|
||||
// llvm::llvm_unreachable_internal("SLIR symbol has to have NS");
|
||||
// } else {
|
||||
// name = k.substr(partDelimiter + 1, k.size());
|
||||
// ns = k.substr(0, partDelimiter);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// The hash key for this storage is a pair of the integer and type params.
|
||||
|
||||
// /// Define the comparison function for the key type.
|
||||
// bool operator==(const KeyTy &key) const {
|
||||
// // TODO: Use formatv to concat strings
|
||||
// return key == ns + "/" + name;
|
||||
// }
|
||||
|
||||
// static llvm::hash_code hashKey(const KeyTy &key) {
|
||||
// return llvm::hash_combine(key);
|
||||
// }
|
||||
|
||||
// /// Define a construction function for the key type.
|
||||
// /// Note: This isn't necessary because KeyTy can be directly constructed
|
||||
// with
|
||||
// /// the given parameters.
|
||||
// static KeyTy getKey(std::string &ns, std::string &name) {
|
||||
// // TODO: Use formatv to concat strings
|
||||
// return KeyTy(ns + "/" + name);
|
||||
// }
|
||||
|
||||
// /// Define a construction method for creating a new instance of this
|
||||
// storage. static SymbolTypeStorage *construct(mlir::TypeStorageAllocator
|
||||
// &allocator,
|
||||
// const KeyTy &key) {
|
||||
// return new (allocator.allocate<SymbolTypeStorage>())
|
||||
// SymbolTypeStorage(key);
|
||||
// }
|
||||
|
||||
// std::string ns;
|
||||
// std::string name;
|
||||
// };
|
||||
}; // namespace detail
|
||||
}; // namespace slir
|
||||
}; // namespace serene
|
||||
#endif
|
|
@ -1,117 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_SLIR_TRAITS_H
|
||||
#define SERENE_SLIR_TRAITS_H
|
||||
|
||||
#include "serene/slir/dialect.h"
|
||||
#include "serene/traits.h"
|
||||
|
||||
#include <llvm/ADT/STLExtras.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
#include <mlir/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <mlir/ExecutionEngine/OptUtils.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/IR/MLIRContext.h>
|
||||
#include <mlir/Support/LogicalResult.h>
|
||||
#include <mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h>
|
||||
#include <mlir/Target/LLVMIR/ModuleTranslation.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace serene {
|
||||
class Namespace;
|
||||
class SereneContext;
|
||||
} // namespace serene
|
||||
|
||||
namespace serene::slir {
|
||||
|
||||
template <typename T>
|
||||
class GeneratableUnit : public TraitBase<T, GeneratableUnit> {
|
||||
public:
|
||||
GeneratableUnit(){};
|
||||
GeneratableUnit(const GeneratableUnit &) = delete;
|
||||
|
||||
void generate(serene::Namespace &ns) { this->Object().generateIR(ns); };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Generatable : public TraitBase<T, Generatable> {
|
||||
public:
|
||||
Generatable(){};
|
||||
Generatable(const Generatable &) = delete;
|
||||
|
||||
mlir::LogicalResult generate() { return this->Object().generate(); };
|
||||
mlir::LogicalResult runPasses() { return this->Object().runPasses(); };
|
||||
|
||||
mlir::ModuleOp &getModule() { return this->Object().getModule(); };
|
||||
serene::SereneContext &getContext() { return this->Object().getContext(); };
|
||||
|
||||
void dump() { this->Object().dump(); };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
mlir::LogicalResult generate(Generatable<T> &t) {
|
||||
return t.generate();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::unique_ptr<llvm::Module> toLLVMIR(Generatable<T> &t) {
|
||||
auto &module = t.getModule();
|
||||
auto &ctx = t.getContext();
|
||||
// Register the translation to LLVM IR with the MLIR context.
|
||||
mlir::registerLLVMDialectTranslation(ctx.mlirContext);
|
||||
|
||||
// Convert the module to LLVM IR in a new LLVM IR context.
|
||||
auto llvmModule = mlir::translateModuleToLLVMIR(module, ctx.llvmContext);
|
||||
if (!llvmModule) {
|
||||
// TODO: Return a Result type instead
|
||||
llvm::errs() << "Failed to emit LLVM IR\n";
|
||||
throw std::runtime_error("Failed to emit LLVM IR\n");
|
||||
}
|
||||
|
||||
// Initialize LLVM targets.
|
||||
llvm::InitializeNativeTarget();
|
||||
// llvm::InitializeNativeTargetAsmPrinter();
|
||||
|
||||
// TODO: replace this call with our own version of setupTargetTriple
|
||||
mlir::ExecutionEngine::setupTargetTriple(llvmModule.get());
|
||||
|
||||
/// Optionally run an optimization pipeline over the llvm module.
|
||||
auto optPipeline = mlir::makeOptimizingTransformer(
|
||||
/*optLevel=*/ctx.getOptimizatioLevel(), /*sizeLevel=*/0,
|
||||
/*targetMachine=*/nullptr);
|
||||
if (auto err = optPipeline(llvmModule.get())) {
|
||||
llvm::errs() << "Failed to optimize LLVM IR " << err << "\n";
|
||||
throw std::runtime_error("Failed to optimize LLVM IR");
|
||||
}
|
||||
|
||||
return std::move(llvmModule);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void dump(Generatable<T> &t) {
|
||||
t.dump();
|
||||
};
|
||||
|
||||
} // namespace serene::slir
|
||||
|
||||
#endif
|
|
@ -1,86 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_SLIR_TYPE_CONVERTER_H
|
||||
#define SERENE_SLIR_TYPE_CONVERTER_H
|
||||
|
||||
#include "serene/config.h"
|
||||
#include "serene/context.h"
|
||||
#include "serene/export.h"
|
||||
#include "serene/slir/dialect.h"
|
||||
#include "serene/slir/ops.h"
|
||||
#include "serene/slir/types.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <mlir/Dialect/LLVMIR/LLVMTypes.h>
|
||||
#include <mlir/IR/BuiltinTypes.h>
|
||||
#include <mlir/Transforms/DialectConversion.h>
|
||||
|
||||
namespace serene::slir {
|
||||
class SymbolType;
|
||||
class PtrType;
|
||||
|
||||
/// Returns the LLVM pointer type to the type `T` that is Serene pointer
|
||||
/// `p` is pointing to.
|
||||
mlir::Type getPtrTypeinLLVM(mlir::MLIRContext &ctx, PtrType p);
|
||||
|
||||
/// Returns the conversion result of converting Serene String type
|
||||
/// to LLVM dialect
|
||||
mlir::Type getStringTypeinLLVM(mlir::MLIRContext &ctx);
|
||||
|
||||
/// Returns the conversoin result of converting Serene Symbol type
|
||||
/// to LLVM dialect
|
||||
mlir::Type getSymbolTypeinLLVM(mlir::MLIRContext &ctx);
|
||||
|
||||
class TypeConverter : public mlir::TypeConverter {
|
||||
mlir::MLIRContext &ctx;
|
||||
|
||||
using mlir::TypeConverter::convertType;
|
||||
|
||||
using MaybeType = std::optional<mlir::Type>;
|
||||
using MaybeValue = std::optional<mlir::Value>;
|
||||
using ConverterFn = std::function<MaybeType(mlir::Type)>;
|
||||
|
||||
std::function<MaybeValue(mlir::OpBuilder &, SymbolType, mlir::ValueRange,
|
||||
mlir::Location)>
|
||||
|
||||
materializeSymbolFn() {
|
||||
return [&](mlir::OpBuilder &builder, SymbolType type,
|
||||
mlir::ValueRange values, mlir::Location loc) -> MaybeValue {
|
||||
auto targetType = convertType(type);
|
||||
auto ret = builder.create<ConvertOp>(loc, targetType, values[0]);
|
||||
llvm::outs() << "RR: " << ret << "\n";
|
||||
return ret.getResult();
|
||||
};
|
||||
};
|
||||
|
||||
/// This method will be called via `convertType` to convert Serene types
|
||||
/// to llvm dialect types
|
||||
ConverterFn convertSereneTypes();
|
||||
|
||||
public:
|
||||
TypeConverter(mlir::MLIRContext &ctx) : ctx(ctx) {
|
||||
addConversion([](mlir::Type type) { return type; });
|
||||
addConversion(convertSereneTypes());
|
||||
addArgumentMaterialization(materializeSymbolFn());
|
||||
addTargetMaterialization(materializeSymbolFn());
|
||||
}
|
||||
};
|
||||
} // namespace serene::slir
|
||||
|
||||
#endif
|
|
@ -1,34 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_DIALECT_TYPES_H
|
||||
#define SERENE_DIALECT_TYPES_H
|
||||
|
||||
#include <llvm/ADT/TypeSwitch.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/IR/BuiltinTypes.h>
|
||||
#include <mlir/IR/DialectImplementation.h>
|
||||
#include <mlir/IR/TypeSupport.h>
|
||||
|
||||
#define GET_ATTRDEF_CLASSES
|
||||
#include "serene/slir/attrs.h.inc"
|
||||
|
||||
#define GET_TYPEDEF_CLASSES
|
||||
#include "serene/slir/types.h.inc"
|
||||
|
||||
#endif
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_DIALECT_TYPES_TD
|
||||
#define SERENE_DIALECT_TYPES_TD
|
||||
|
||||
include "mlir/IR/OpBase.td"
|
||||
include "mlir/IR/AttrTypeBase.td"
|
||||
|
||||
// The base class for all the Serene types
|
||||
class Serene_Type<string name, string typeMnemonic, list<Trait> traits = []>
|
||||
: TypeDef<Serene_Dialect, name, traits> {
|
||||
let mnemonic = typeMnemonic;
|
||||
}
|
||||
|
||||
// The base class for all the Serene attributes
|
||||
class Serene_Attr<string name, string attrMnemonic, list<Trait> traits = []>
|
||||
: AttrDef<Serene_Dialect, name, traits> {
|
||||
let mnemonic = attrMnemonic;
|
||||
}
|
||||
|
||||
// Attributes =================================================================
|
||||
def SymbolAttr : Serene_Attr<"Symbol", "symbol"> {
|
||||
let summary = "An Attribute containing a symbol value.";
|
||||
let description = [{
|
||||
An integer attribute is a literal attribute that represents a symbol
|
||||
value.
|
||||
}];
|
||||
|
||||
let parameters = (ins StringRefParameter<"The namespace of the symbol">:$ns,
|
||||
StringRefParameter<"The symbol name itself">:$name);
|
||||
|
||||
let assemblyFormat = "`<` $ns `,` $name `>`";
|
||||
}
|
||||
|
||||
// Type Traits ================================================================
|
||||
def AnyPtr : Type<CPred<"$_self.isa<PtrType>()">,
|
||||
"Serene pointer type", "Ptr">;
|
||||
|
||||
class Ptr<Type type> : Type<
|
||||
And<[AnyPtr.predicate,
|
||||
Or<[CPred<"$_self.cast<PtrType>().isOpaque()">,
|
||||
SubstLeaves<
|
||||
"$_self",
|
||||
"$_self.cast<PtrType>().getPointeeType()",
|
||||
type.predicate>]>]>,
|
||||
"A pointer to type " # type.summary,
|
||||
"PtrType">,
|
||||
// We need Ptr to be buildable coz we need to be able to infer
|
||||
// the type out of it when we use Ptr<T> as the result type
|
||||
// of an operation
|
||||
SameBuildabilityAs<type, "PtrType::get(" # type # "::get($_builder.getContext()))"> {
|
||||
Type pointeeType = type;
|
||||
}
|
||||
|
||||
|
||||
// Types ======================================================================
|
||||
def PtrType : Serene_Type<"Ptr", "ptr"> {
|
||||
let summary = "A pointer to a value of type T.";
|
||||
|
||||
let description = [{
|
||||
A pointer to a value of type T. For example
|
||||
|
||||
%0 = serene.address-of %1 : (!serene.symbol) -> !ptr<serene.symbol>
|
||||
}];
|
||||
|
||||
let parameters = (ins "mlir::Type":$pointeeType, DefaultValuedParameter<"unsigned", "0">:$addressSpace);
|
||||
let genAccessors = 1;
|
||||
let assemblyFormat = "`<` qualified($pointeeType) `>`";
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
/// Gets or creates an instance of pointer type pointing to an
|
||||
/// object of `pointee` type in the given address space. The pointer type is
|
||||
/// created in the same context as `pointee`. If the pointee is not provided,
|
||||
/// creates an opaque pointer in the given context and address space.
|
||||
static PtrType get(mlir::MLIRContext *context, unsigned addressSpace = 0);
|
||||
static PtrType get(mlir::Type pointee, unsigned addressSpace = 0);
|
||||
|
||||
bool isOpaque() const;
|
||||
}];
|
||||
}
|
||||
|
||||
def StringType : Serene_Type<"String", "string"> {
|
||||
let summary = "A simple string type";
|
||||
|
||||
let description = [{
|
||||
A simple string type that contains the following structure:
|
||||
|
||||
i8*: buffer; pointer to the character buffer
|
||||
i32: length; the number of chars in the buffer
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
def SymbolType : Serene_Type<"Symbol", "symbol"> {
|
||||
let summary = "A Lisp symbol type";
|
||||
|
||||
let description = [{
|
||||
A Lisp symbol type
|
||||
}];
|
||||
// let parameters = (ins "std::string":$ns, "std::string":$name);
|
||||
}
|
||||
|
||||
def FnType : Serene_Type<"Fn", "fn"> {
|
||||
|
||||
let summary = "Function type";
|
||||
|
||||
let description = [{
|
||||
Function type represent any function, anonymous or named with multiple
|
||||
bodies.
|
||||
}];
|
||||
|
||||
// TODO: do we need to know about number of bodies and the signature
|
||||
// of each one?
|
||||
// let parameters = (ins "std::string":$ns, "std::string":$name);
|
||||
}
|
||||
|
||||
def Serene_Type : AnyTypeOf<[
|
||||
PtrType,
|
||||
SymbolType,
|
||||
StringType,
|
||||
FnType
|
||||
]>;
|
||||
|
||||
|
||||
#endif // SERENE_DIALECT_TYPES_TD
|
|
@ -1,40 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_SLIR_UTILS_H
|
||||
#define SERENE_SLIR_UTILS_H
|
||||
|
||||
#include "serene/reader/location.h"
|
||||
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
|
||||
namespace serene {
|
||||
class Namespace;
|
||||
} // namespace serene
|
||||
|
||||
namespace serene::slir {
|
||||
|
||||
/**
|
||||
* Convert a Serene location to MLIR FileLineLoc Location
|
||||
*/
|
||||
::mlir::Location toMLIRLocation(serene::Namespace &,
|
||||
serene::reader::Location &);
|
||||
|
||||
} // namespace serene::slir
|
||||
|
||||
#endif
|
|
@ -1,190 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_SOURCE_MGR_H
|
||||
#define SERENE_SOURCE_MGR_H
|
||||
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/reader/location.h"
|
||||
|
||||
#include <llvm/ADT/SmallVector.h>
|
||||
#include <llvm/ADT/StringMap.h>
|
||||
#include <llvm/Support/ErrorHandling.h>
|
||||
#include <llvm/Support/ErrorOr.h>
|
||||
#include <llvm/Support/MemoryBuffer.h>
|
||||
#include <llvm/Support/SourceMgr.h>
|
||||
#include <mlir/IR/Diagnostics.h>
|
||||
#include <mlir/Support/Timing.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define SMGR_LOG(...) \
|
||||
DEBUG_WITH_TYPE("sourcemgr", llvm::dbgs() \
|
||||
<< "[SMGR]: " << __VA_ARGS__ << "\n");
|
||||
|
||||
namespace serene {
|
||||
class SereneContext;
|
||||
|
||||
/// This class is quite similar to the `llvm::SourceMgr` in functionality. We
|
||||
/// even borrowed some of the code from the original implementation but removed
|
||||
/// a lot of code that were irrelevant to us.
|
||||
///
|
||||
/// SouceMgr is responsible for finding a namespace in the `loadPaths` and read
|
||||
/// the content of the `.srn` (or any of the `DEFAULT_SUFFIX`) into a
|
||||
/// `llvm::MemoryBuffer` embedded in a `SrcBuffer` object as the owner of the
|
||||
/// source files and then it will call the `reader` on the buffer to parse it
|
||||
/// and create the actual `Namespace` object from the parsed AST.
|
||||
///
|
||||
/// Later on, whenever we need to refer to the source file of a namespace for
|
||||
/// diagnosis purposes or any other purpose we can use the functions in this
|
||||
/// class to get hold of a pointer to a specific `reader::Location` of the
|
||||
/// buffer.
|
||||
///
|
||||
/// Note: Unlike the original version, SourceMgr does not handle the diagnostics
|
||||
/// and it uses the Serene's `DiagnosticEngine` for that matter.
|
||||
class SERENE_EXPORT SourceMgr {
|
||||
|
||||
public:
|
||||
// TODO: Make it a vector of supported suffixes
|
||||
std::string DEFAULT_SUFFIX = "srn";
|
||||
|
||||
private:
|
||||
struct SrcBuffer {
|
||||
/// The memory buffer for the file.
|
||||
std::unique_ptr<llvm::MemoryBuffer> buffer;
|
||||
|
||||
/// Vector of offsets into Buffer at which there are line-endings
|
||||
/// (lazily populated). Once populated, the '\n' that marks the end of
|
||||
/// line number N from [1..] is at Buffer[OffsetCache[N-1]]. Since
|
||||
/// these offsets are in sorted (ascending) order, they can be
|
||||
/// binary-searched for the first one after any given offset (eg. an
|
||||
/// offset corresponding to a particular SMLoc).
|
||||
///
|
||||
/// Since we're storing offsets into relatively small files (often smaller
|
||||
/// than 2^8 or 2^16 bytes), we select the offset vector element type
|
||||
/// dynamically based on the size of Buffer.
|
||||
mutable void *offsetCache = nullptr;
|
||||
|
||||
/// Look up a given \p ptr in in the buffer, determining which line it came
|
||||
/// from.
|
||||
unsigned getLineNumber(const char *ptr) const;
|
||||
template <typename T>
|
||||
unsigned getLineNumberSpecialized(const char *ptr) const;
|
||||
|
||||
/// Return a pointer to the first character of the specified line number or
|
||||
/// null if the line number is invalid.
|
||||
const char *getPointerForLineNumber(unsigned lineNo) const;
|
||||
|
||||
template <typename T>
|
||||
const char *getPointerForLineNumberSpecialized(unsigned lineNo) const;
|
||||
|
||||
/// This is the location of the parent import or unknown location if it is
|
||||
/// the main namespace
|
||||
reader::LocationRange importLoc;
|
||||
|
||||
SrcBuffer() = default;
|
||||
SrcBuffer(SrcBuffer &&) noexcept;
|
||||
SrcBuffer(const SrcBuffer &) = delete;
|
||||
SrcBuffer &operator=(const SrcBuffer &) = delete;
|
||||
~SrcBuffer();
|
||||
};
|
||||
using MemBufPtr = std::unique_ptr<llvm::MemoryBuffer>;
|
||||
|
||||
/// This is all of the buffers that we are reading from.
|
||||
std::vector<SrcBuffer> buffers;
|
||||
|
||||
/// A hashtable that works as an index from namespace names to the buffer
|
||||
/// position it the `buffer`
|
||||
llvm::StringMap<unsigned> nsTable;
|
||||
|
||||
// This is the list of directories we should search for include files in.
|
||||
std::vector<std::string> loadPaths;
|
||||
|
||||
// Find a namespace file with the given \p name in the load path and \r retuns
|
||||
// a unique pointer to the memory buffer containing the content or an error.
|
||||
// In the success case it will put the path of the file into the \p
|
||||
// importedFile.
|
||||
MemBufPtr findFileInLoadPath(const std::string &name,
|
||||
std::string &importedFile);
|
||||
|
||||
bool isValidBufferID(unsigned i) const;
|
||||
|
||||
/// Converts the ns name to a partial path by replacing the dots with slashes
|
||||
static std::string convertNamespaceToPath(std::string ns_name);
|
||||
|
||||
public:
|
||||
SourceMgr() = default;
|
||||
SourceMgr(const SourceMgr &) = delete;
|
||||
SourceMgr &operator=(const SourceMgr &) = delete;
|
||||
SourceMgr(SourceMgr &&) = default;
|
||||
SourceMgr &operator=(SourceMgr &&) = default;
|
||||
~SourceMgr() = default;
|
||||
|
||||
/// Set the `loadPaths` to the given \p dirs. `loadPaths` is a vector of
|
||||
/// directories that Serene will look in order to find a file that constains a
|
||||
/// namespace which it is looking for.
|
||||
void setLoadPaths(std::vector<std::string> &dirs) { loadPaths.swap(dirs); }
|
||||
|
||||
/// Return a reference to a `SrcBuffer` with the given ID \p i.
|
||||
const SrcBuffer &getBufferInfo(unsigned i) const {
|
||||
assert(isValidBufferID(i));
|
||||
return buffers[i - 1];
|
||||
}
|
||||
|
||||
/// Return a reference to a `SrcBuffer` with the given namspace name \p ns.
|
||||
const SrcBuffer &getBufferInfo(llvm::StringRef ns) const {
|
||||
auto bufferId = nsTable.lookup(ns);
|
||||
|
||||
if (bufferId == 0) {
|
||||
// No such namespace
|
||||
llvm_unreachable("couldn't find the src buffer for a namespace. It "
|
||||
"should never happen.");
|
||||
}
|
||||
|
||||
return buffers[bufferId - 1];
|
||||
}
|
||||
|
||||
/// Return a pointer to the internal `llvm::MemoryBuffer` of the `SrcBuffer`
|
||||
/// with the given ID \p i.
|
||||
const llvm::MemoryBuffer *getMemoryBuffer(unsigned i) const {
|
||||
assert(isValidBufferID(i));
|
||||
return buffers[i - 1].buffer.get();
|
||||
}
|
||||
|
||||
unsigned getNumBuffers() const { return buffers.size(); }
|
||||
|
||||
/// Add a new source buffer to this source manager. This takes ownership of
|
||||
/// the memory buffer.
|
||||
unsigned AddNewSourceBuffer(std::unique_ptr<llvm::MemoryBuffer> f,
|
||||
reader::LocationRange includeLoc);
|
||||
|
||||
/// Lookup for a file containing the namespace definition of with given
|
||||
/// namespace name \p name. In case that the file exists, it returns an
|
||||
/// `ErrorTree`. It will use the parser to read the file and create an AST
|
||||
/// from it. Then create a namespace, set the its AST to the AST that we just
|
||||
/// read from the file and return a shared pointer to the namespace.
|
||||
///
|
||||
/// \p importLoc is a location in the source code where the give namespace is
|
||||
/// imported.
|
||||
MaybeNS readNamespace(SereneContext &ctx, std::string name,
|
||||
reader::LocationRange importLoc);
|
||||
};
|
||||
|
||||
}; // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,123 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is a CRTP based Trait implementation that allows to use Trait like
|
||||
* classes to create polymorphic functions and API statically at compile type
|
||||
* without any runtime shenanigans. For more on CRTP checkout:
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
|
||||
*
|
||||
* In order to define a trait, use the `TraitBase` class like:
|
||||
|
||||
* \code
|
||||
* template <typename ConcreteType>
|
||||
* class Blahable : public TraitBase<ConcreteType, Blahable> {}
|
||||
* \endcode
|
||||
*
|
||||
* Every Trait has to take the `ConcreteType` as template argument and pass it
|
||||
* to the `TraitBase`. Checkout the documentation of `TraitBase` for more info
|
||||
* on creating a new Trait.
|
||||
*
|
||||
* Alongside with each Trait type, you should provide the "Official" interface
|
||||
* of the Trait via some standalone functions that operates on the Trait type.
|
||||
* For example Imagine we have a Trait type called `ABC` with two main
|
||||
* functionality `foo` and `bar`. We need to create two functions as follows:
|
||||
*
|
||||
* \code
|
||||
* template <typename T>
|
||||
* SomeType Foo(ABC<T> &t) { return t.foo(); };
|
||||
*
|
||||
* template <typename T>
|
||||
* SomeType bar(ABC<T> &t, int x) { return t.bar(x); };
|
||||
* \endcode
|
||||
*
|
||||
* These two functions will be the official interface to the trait `ABC`.
|
||||
* IMPORTANT NOTE: Make sure to pass a reference of type `ABC<T>` to the
|
||||
* functions and DO NOT PASS BY COPY. Since copying will copy the value by the
|
||||
* trait type only, we would not be able to statically case it to the
|
||||
* implementor type and it will lead to some allocation problem.
|
||||
*
|
||||
* Traits can be used via `WithTrait` helper class which provides a clean
|
||||
* interface to mix and match Trait types.
|
||||
*
|
||||
*/
|
||||
#ifndef SERENE_TRAITS_H
|
||||
#define SERENE_TRAITS_H
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace serene {
|
||||
|
||||
/// A placeholder structure that replaces the concrete type of the
|
||||
/// Imlementations Which might have child classes.
|
||||
struct FinalImpl;
|
||||
|
||||
/// In order to use Traits, we can use `WithTrait` class as the base
|
||||
/// of any implementation class and pass the Trait classes as template argument
|
||||
/// for example:
|
||||
///
|
||||
/// \code
|
||||
/// class Expression : public WithTrait<Expression, Printable, Locatable> {}
|
||||
/// \endcode
|
||||
template <typename ConcreteType, template <typename T> class... Traits>
|
||||
class WithTrait : public Traits<ConcreteType>... {
|
||||
protected:
|
||||
WithTrait(){};
|
||||
friend ConcreteType;
|
||||
};
|
||||
|
||||
/// This class provides the common functionality among the Trait Types and
|
||||
/// every Trait has to inherit from this class. Here is an example:
|
||||
///
|
||||
/// \code
|
||||
/// template <typename ConcreteType>
|
||||
/// class Blahable : public TraitBase<ConcreteType, Blahable> {}
|
||||
/// \endcode
|
||||
///
|
||||
/// In the Trait class the underlaying object which implements the Trait
|
||||
/// is accessable via the `Object` method.
|
||||
template <typename ConcreteType, template <typename> class TraitType>
|
||||
class TraitBase {
|
||||
protected:
|
||||
/// Statically casts the object to the concrete type object to be
|
||||
/// used in the Trait Types.
|
||||
// const ConcreteType &Object() const {
|
||||
// return static_cast<const ConcreteType &>(*this);
|
||||
// };
|
||||
|
||||
ConcreteType &Object() { return static_cast<ConcreteType &>(*this); };
|
||||
};
|
||||
|
||||
template <typename ConcreteType>
|
||||
class IDebuggable : public TraitBase<ConcreteType, IDebuggable> {
|
||||
public:
|
||||
IDebuggable(){};
|
||||
IDebuggable(const IDebuggable &) = delete;
|
||||
std::string toString() const { return this->Object().toString(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::string toString(IDebuggable<T> &t) {
|
||||
return t.toString();
|
||||
}
|
||||
|
||||
}; // namespace serene
|
||||
#endif
|
|
@ -1,22 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_TRAITS_LOCATABLE_H
|
||||
#define SERENE_TRAITS_LOCATABLE_H
|
||||
|
||||
#endif
|
|
@ -1,35 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_TYPES_TYPE_H
|
||||
#define SERENE_TYPES_TYPE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace serene::types {
|
||||
|
||||
class Type {
|
||||
public:
|
||||
virtual ~Type() = default;
|
||||
|
||||
/// The AST representation of a type
|
||||
virtual std::string toString() const = 0;
|
||||
};
|
||||
}; // namespace serene::types
|
||||
|
||||
#endif
|
|
@ -1,140 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_UTILS_H
|
||||
#define SERENE_UTILS_H
|
||||
|
||||
#include "serene/export.h"
|
||||
|
||||
#include <llvm/Support/Error.h>
|
||||
|
||||
#include <variant>
|
||||
|
||||
// Sometimes we need this to make both analyzer happy
|
||||
// and the fn signature right.
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
// We use this value with llvm::SmallString<MAX_PATH_SLOTS>
|
||||
#define MAX_PATH_SLOTS 256
|
||||
// C++17 required. We can't go back to 14 any more :))
|
||||
|
||||
namespace serene {
|
||||
|
||||
/// A similar type to Rust's Result data structure. It either holds a value of
|
||||
/// type `T` successfully or holds a value of type `E` errorfully. It is
|
||||
/// designed to be used in situations which the return value of a function might
|
||||
/// contains some errors. The official way to use this type is to use the
|
||||
/// factory functions `Success` and `Error`. For example:
|
||||
///
|
||||
/// \code
|
||||
/// auto successfulResult = Result<int>::success(3);
|
||||
/// auto notOkResult = Result<int>::error(SomeLLVMError());
|
||||
// \endcode
|
||||
///
|
||||
/// In order check for a value being errorful or successful checkout the `ok`
|
||||
/// method or simply use the value as a conditiona.
|
||||
///
|
||||
/// This class is setup in a way tha you can us a value of type `T` in places
|
||||
/// that the compiler expects a `Result<T>`. So for example:
|
||||
///
|
||||
/// \code
|
||||
/// Result<int> fn() {return 2;}
|
||||
/// \endcode
|
||||
///
|
||||
/// works perfectly.
|
||||
template <typename T, typename E = llvm::Error>
|
||||
class SERENE_EXPORT Result {
|
||||
|
||||
// The actual data container
|
||||
std::variant<T, E> contents;
|
||||
|
||||
/// The main constructor which we made private to avoid ambiguousness in
|
||||
/// input type. `Success` and `Error` call this ctor.
|
||||
template <typename InPlace, typename Content>
|
||||
Result(InPlace i, Content &&c) : contents(i, std::forward<Content>(c)){};
|
||||
|
||||
public:
|
||||
constexpr Result(const T &v)
|
||||
: Result(std::in_place_index_t<0>(), std::move(v)){};
|
||||
|
||||
/// Return a pointer to the success case value of the result. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
constexpr const T *getPointer() const { return &getValue(); }
|
||||
|
||||
/// Return a pointer to the success case value of the result. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
T *getPointer() { return &getValue(); }
|
||||
|
||||
/// Return a pointer to the success case value of the result. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
T *operator->() { return getPointer(); }
|
||||
|
||||
/// Return a pointer to the success case value of the result. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
constexpr const T *operator->() const { return getPointer(); }
|
||||
|
||||
/// Dereference the success case and returns the value. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
constexpr const T &operator*() const & { return getValue(); }
|
||||
|
||||
/// Dereference the success case and returns the value. It is
|
||||
/// important to check for the success case before calling this function.
|
||||
T &operator*() & { return getValue(); }
|
||||
|
||||
/// Create a succesfull result with the given value of type `T`.
|
||||
static Result success(T v) {
|
||||
return Result(std::in_place_index_t<0>(), std::move(v));
|
||||
}
|
||||
|
||||
/// Create an errorful result with the given value of type `E` (default
|
||||
/// `llvm::Error`).
|
||||
static Result error(E e) {
|
||||
return Result(std::in_place_index_t<1>(), std::move(e));
|
||||
}
|
||||
|
||||
/// Return the value if it's successful otherwise throw an error
|
||||
T &&getValue() && { return std::move(std::get<0>(contents)); };
|
||||
|
||||
/// Return the error value if it's errorful otherwise throw an error
|
||||
E &&getError() && { return std::move(std::get<1>(contents)); };
|
||||
|
||||
// using std::get, it'll throw if contents doesn't contain what you ask for
|
||||
|
||||
/// Return the value if it's successful otherwise throw an error
|
||||
T &getValue() & { return std::get<0>(contents); };
|
||||
|
||||
/// Return the error value if it's errorful otherwise throw an error
|
||||
E &getError() & { return std::get<1>(contents); };
|
||||
|
||||
const T &getValue() const & { return std::get<0>(contents); }
|
||||
const E &getError() const & { return std::get<1>(contents); }
|
||||
|
||||
/// Return the a boolean value indicating whether the value is succesful
|
||||
/// or errorful.
|
||||
bool ok() const { return std::holds_alternative<T>(contents); };
|
||||
|
||||
operator bool() const { return ok(); }
|
||||
};
|
||||
|
||||
inline void makeFQSymbolName(const llvm::StringRef &ns,
|
||||
const llvm::StringRef &sym, std::string &result) {
|
||||
result = (ns + "/" + sym).str();
|
||||
};
|
||||
|
||||
} // namespace serene
|
||||
#endif
|
|
@ -1,169 +0,0 @@
|
|||
# Serene Programming Language
|
||||
#
|
||||
# Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#TODO: To support MacOS look into cmake's public headers
|
||||
# https://cmake.org/cmake/help/latest/prop_tgt/PUBLIC_HEADER.html
|
||||
|
||||
# Prevent any future RPATH issue on Posix
|
||||
if(NOT APPLE)
|
||||
set(CMAKE_INSTALL_RPATH $ORIGIN)
|
||||
endif()
|
||||
|
||||
add_library(serene
|
||||
exprs/symbol.cpp
|
||||
exprs/list.cpp
|
||||
exprs/number.cpp
|
||||
exprs/expression.cpp
|
||||
exprs/def.cpp
|
||||
exprs/fn.cpp
|
||||
exprs/call.cpp
|
||||
|
||||
serene.cpp
|
||||
context.cpp
|
||||
namespace.cpp
|
||||
source_mgr.cpp
|
||||
diagnostics.cpp
|
||||
semantics.cpp
|
||||
|
||||
# jit.cpp
|
||||
# jit/engine.cpp
|
||||
# jit/layers.cpp
|
||||
jit/halley.cpp
|
||||
|
||||
errors.cpp
|
||||
|
||||
# Reader
|
||||
reader/reader.cpp
|
||||
|
||||
# IR
|
||||
slir/types.cpp
|
||||
slir/slir.cpp
|
||||
slir/value_op.cpp
|
||||
slir/generatable.cpp
|
||||
slir/utils.cpp
|
||||
slir/ops.cpp
|
||||
slir/type_converter.cpp
|
||||
slir/dialect.cpp
|
||||
passes/slir_lowering.cpp
|
||||
passes/to_llvm_dialect.cpp
|
||||
)
|
||||
|
||||
# Create an ALIAS target. This way if we mess up the name
|
||||
# there will be an cmake error inseat of a linker error which is harder
|
||||
# to understand. So any binary that wants to use serene has to
|
||||
# use `Serene::lib` alias instead
|
||||
add_library(Serene::lib ALIAS serene)
|
||||
|
||||
set_target_properties(serene PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
# Warn on unused libs
|
||||
LINK_WHAT_YOU_USE TRUE
|
||||
# LTO support
|
||||
INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
|
||||
if(SERENE_ENABLE_TIDY)
|
||||
set_target_properties(serene PROPERTIES CXX_CLANG_TIDY ${CLANG_TIDY_PATH})
|
||||
endif()
|
||||
|
||||
# Do we need to build serene as a shared lib? default is "yes"
|
||||
if(SERENE_SHARED_LIB)
|
||||
|
||||
# We need to use libsan as a shared lib on debug mode. The
|
||||
# target executable has to be built with `-fsanitize=address`
|
||||
# as well and it has to run with:
|
||||
# LD_PRELOAD=$(clang -print-file-name=libclang_rt.asan-x86_64.so)
|
||||
target_compile_options(serene PRIVATE
|
||||
$<$<CONFIG:DEBUG>:-shared-libsan>
|
||||
)
|
||||
target_link_options(
|
||||
serene PRIVATE
|
||||
$<$<CONFIG:DEBUG>:-shared-libsan>
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
if (CPP_20_SUPPORT)
|
||||
target_compile_features(serene PUBLIC cxx_std_20)
|
||||
else()
|
||||
target_compile_features(serene PUBLIC cxx_std_17)
|
||||
endif()
|
||||
|
||||
# Generate the tablegen ODS files before this target
|
||||
add_dependencies(serene SereneTablegen SereneDialectGen SerenePassGen SereneErrorGen)
|
||||
|
||||
# We need this directory, and users of our library will need it too
|
||||
target_include_directories(serene PUBLIC "$<BUILD_INTERFACE:${INCLUDE_DIR}>")
|
||||
target_include_directories(serene PUBLIC "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>")
|
||||
|
||||
# Generate the export.h
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(serene EXPORT_FILE_NAME ${PROJECT_BINARY_DIR}/include/serene/export.h)
|
||||
target_compile_definitions(
|
||||
serene PUBLIC "$<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:SERENE_STATIC_DEFINE>")
|
||||
|
||||
get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
|
||||
get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS)
|
||||
|
||||
|
||||
if(SERENE_SHOW_MLIR_DIALECTS)
|
||||
message(STATUS "MLIR Dialects to choose from:")
|
||||
foreach(lib ${dialect_libs})
|
||||
message(STATUS "\t${lib}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
if(SERENE_SHOW_MLIR_TRANSFORMERS)
|
||||
message(STATUS "MLIR Dialects transformers to choose from:")
|
||||
foreach(lib ${conversion_libs})
|
||||
message(STATUS "\t${lib}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(SERENE_SHOW_LLVM_LIBS)
|
||||
execute_process(COMMAND llvm-config --libs all
|
||||
OUTPUT_VARIABLE SERENE_LLVM_LIBS)
|
||||
message(STATUS "LLVM libs available:\n ${SERENE_LLVM_LIBS}")
|
||||
endif()
|
||||
|
||||
|
||||
set(serene_lib_dialects_in_use
|
||||
MLIRFunc)
|
||||
|
||||
set(serene_lib_transformers_in_use
|
||||
MLIRFuncToLLVM)
|
||||
|
||||
target_link_libraries(serene PRIVATE
|
||||
MLIRIR
|
||||
MLIRPass
|
||||
MLIRTransforms
|
||||
|
||||
${serene_lib_dialects_in_use}
|
||||
${serene_lib_transformers_in_use}
|
||||
|
||||
#TODO: Remove this lib, we're just using one func
|
||||
MLIRExecutionEngine
|
||||
|
||||
# LLVM's JIT lib
|
||||
LLVMExecutionEngine
|
||||
LLVMOrcJIT
|
||||
MLIRLLVMToLLVMIRTranslation
|
||||
|
||||
LLVMTarget
|
||||
LLVMX86AsmParser
|
||||
|
||||
${llvm_libs})
|
|
@ -1,185 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/context.h"
|
||||
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/passes.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/slir/generatable.h"
|
||||
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace serene {
|
||||
|
||||
void SereneContext::insertNS(NSPtr &ns) {
|
||||
auto nsName = ns->name;
|
||||
namespaces[nsName] = ns;
|
||||
};
|
||||
|
||||
Namespace *SereneContext::getNS(llvm::StringRef nsName) {
|
||||
if (namespaces.count(nsName.str()) != 0) {
|
||||
return namespaces[nsName.str()].get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
Namespace &SereneContext::getCurrentNS() {
|
||||
if (this->currentNS.empty() || (namespaces.count(this->currentNS) == 0)) {
|
||||
panic(*this, llvm::formatv("getCurrentNS: Namespace '{0}' does not exist",
|
||||
this->currentNS)
|
||||
.str());
|
||||
}
|
||||
|
||||
return *namespaces[this->currentNS];
|
||||
};
|
||||
|
||||
NSPtr SereneContext::getSharedPtrToNS(llvm::StringRef nsName) {
|
||||
if (namespaces.count(nsName.str()) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return namespaces[nsName.str()];
|
||||
};
|
||||
|
||||
void SereneContext::setOperationPhase(CompilationPhase phase) {
|
||||
this->targetPhase = phase;
|
||||
|
||||
if (phase == CompilationPhase::SLIR) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (phase >= CompilationPhase::MLIR) {
|
||||
pm.addPass(serene::passes::createSLIRLowerToMLIRPass());
|
||||
}
|
||||
|
||||
if (phase >= CompilationPhase::LIR) {
|
||||
pm.addPass(serene::passes::createSLIRLowerToLLVMDialectPass());
|
||||
}
|
||||
};
|
||||
|
||||
int SereneContext::getOptimizatioLevel() {
|
||||
if (targetPhase <= CompilationPhase::NoOptimization) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (targetPhase == CompilationPhase::O1) {
|
||||
return 1;
|
||||
}
|
||||
if (targetPhase == CompilationPhase::O2) {
|
||||
return 2;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
NSPtr SereneContext::makeNamespace(llvm::StringRef name,
|
||||
std::optional<llvm::StringRef> filename) {
|
||||
auto ns = Namespace::make(*this, name, filename);
|
||||
|
||||
if (ns != nullptr) {
|
||||
insertNS(ns);
|
||||
}
|
||||
|
||||
return ns;
|
||||
};
|
||||
|
||||
MaybeNS SereneContext::readNamespace(const std::string &name) {
|
||||
// TODO: Replace this location with a proper location indicating
|
||||
// the reason why the location data is not available.
|
||||
// For example, because the ns might be the entry point ns.
|
||||
auto loc = reader::LocationRange::UnknownLocation(name);
|
||||
|
||||
return readNamespace(name, loc);
|
||||
};
|
||||
|
||||
MaybeNS SereneContext::readNamespace(const std::string &name,
|
||||
reader::LocationRange loc) {
|
||||
return withCurrentNS<MaybeNS>(name, [&]() -> MaybeNS {
|
||||
return this->sourceManager.readNamespace(*this, name, loc);
|
||||
});
|
||||
}
|
||||
|
||||
MaybeNS SereneContext::importNamespace(const std::string &name) {
|
||||
auto loc = reader::LocationRange::UnknownLocation(name);
|
||||
return importNamespace(name, loc);
|
||||
}
|
||||
|
||||
MaybeNS SereneContext::importNamespace(const std::string &name,
|
||||
reader::LocationRange loc) {
|
||||
auto maybeNS = readNamespace(name, loc);
|
||||
|
||||
if (maybeNS) {
|
||||
auto &ns = *maybeNS;
|
||||
if (auto err = jit->addNS(*ns, loc)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
insertNS(ns);
|
||||
}
|
||||
|
||||
return maybeNS;
|
||||
}
|
||||
|
||||
llvm::orc::JITDylib *SereneContext::getLatestJITDylib(Namespace &ns) {
|
||||
|
||||
if (jitDylibs.count(ns.name) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto vec = jitDylibs[ns.name];
|
||||
// TODO: Make sure that the returning Dylib still exists in the JIT
|
||||
// by calling jit->engine->getJITDylibByName(dylib_name);
|
||||
return vec.empty() ? nullptr : vec.back();
|
||||
};
|
||||
|
||||
void SereneContext::pushJITDylib(Namespace &ns, llvm::orc::JITDylib *l) {
|
||||
if (jitDylibs.count(ns.name) == 0) {
|
||||
llvm::SmallVector<llvm::orc::JITDylib *, 1> vec{l};
|
||||
jitDylibs[ns.name] = vec;
|
||||
return;
|
||||
}
|
||||
auto vec = jitDylibs[ns.name];
|
||||
vec.push_back(l);
|
||||
jitDylibs[ns.name] = vec;
|
||||
}
|
||||
|
||||
size_t SereneContext::getNumberOfJITDylibs(Namespace &ns) {
|
||||
if (jitDylibs.count(ns.name) == 0) {
|
||||
return 0;
|
||||
}
|
||||
auto vec = jitDylibs[ns.name];
|
||||
return vec.size();
|
||||
};
|
||||
|
||||
void terminate(SereneContext &ctx, int exitCode) {
|
||||
UNUSED(ctx);
|
||||
// TODO: Since we are running in a single thread for now using exit is fine
|
||||
// but we need to adjust and change it to a thread safe termination
|
||||
// process later on.
|
||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||
std::exit(exitCode);
|
||||
}
|
||||
|
||||
std::unique_ptr<SereneContext> makeSereneContext(Options opts) {
|
||||
return SereneContext::make(opts);
|
||||
};
|
||||
|
||||
}; // namespace serene
|
|
@ -1,218 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/diagnostics.h"
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/source_mgr.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
#include <llvm/Support/FormatAdapters.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
#include <llvm/Support/WithColor.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace serene {
|
||||
void Diagnostic::writeColorByType(llvm::raw_ostream &os, llvm::StringRef str) {
|
||||
llvm::ColorMode mode =
|
||||
ctx.opts.withColors ? llvm::ColorMode::Auto : llvm::ColorMode::Disable;
|
||||
|
||||
llvm::WithColor s(os, llvm::raw_ostream::SAVEDCOLOR, true, false, mode);
|
||||
|
||||
switch (type) {
|
||||
case Type::Error:
|
||||
s.changeColor(llvm::raw_ostream::Colors::RED);
|
||||
break;
|
||||
// case Type::Warning:
|
||||
// s.changeColor(llvm::raw_ostream::Colors::YELLOW);
|
||||
// break;
|
||||
case Type::Note:
|
||||
s.changeColor(llvm::raw_ostream::Colors::CYAN);
|
||||
break;
|
||||
case Type::Remark:
|
||||
s.changeColor(llvm::raw_ostream::Colors::MAGENTA);
|
||||
break;
|
||||
}
|
||||
|
||||
s << str;
|
||||
s.resetColor();
|
||||
};
|
||||
|
||||
std::string Diagnostic::getPrefix(llvm::StringRef prefix) {
|
||||
if (!prefix.empty()) {
|
||||
return prefix.str();
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case Type::Error:
|
||||
return "Error";
|
||||
break;
|
||||
case Type::Note:
|
||||
return "Note";
|
||||
case Type::Remark:
|
||||
return "Remark";
|
||||
}
|
||||
};
|
||||
|
||||
void Diagnostic::print(llvm::raw_ostream &os, llvm::StringRef prefix) const {
|
||||
llvm::ColorMode mode =
|
||||
ctx.opts.withColors ? llvm::ColorMode::Auto : llvm::ColorMode::Disable;
|
||||
|
||||
llvm::WithColor s(os, llvm::raw_ostream::SAVEDCOLOR, true, false, mode);
|
||||
UNUSED(prefix);
|
||||
UNUSED(err);
|
||||
// s << "\n[";
|
||||
// writeColorByType(os, "Error");
|
||||
// s << "]>\n";
|
||||
|
||||
// s << "In ns '";
|
||||
// s.changeColor(llvm::raw_ostream::Colors::MAGENTA);
|
||||
// s << loc.start.ns;
|
||||
// s.resetColor();
|
||||
|
||||
// if (loc.start.filename.hasValue()) {
|
||||
// s << "' At: ";
|
||||
// s.changeColor(llvm::raw_ostream::Colors::YELLOW);
|
||||
// s << loc.start.filename.getValue();
|
||||
// s.resetColor();
|
||||
// s << ":";
|
||||
// s.changeColor(llvm::raw_ostream::Colors::CYAN);
|
||||
// s << loc.start.line;
|
||||
// s.resetColor();
|
||||
// s << ":";
|
||||
// s.changeColor(llvm::raw_ostream::Colors::CYAN);
|
||||
// s << loc.start.col;
|
||||
// s.resetColor();
|
||||
// }
|
||||
|
||||
// s << "\n\n";
|
||||
|
||||
// const auto &srcBuf = ctx.sourceManager.getBufferInfo(loc.start.ns);
|
||||
// const char *line = srcBuf.getPointerForLineNumber(loc.start.line);
|
||||
|
||||
// while (*line != '\n' && line != srcBuf.buffer->getBufferEnd()) {
|
||||
// s << *line;
|
||||
// line++;
|
||||
// }
|
||||
|
||||
// s << '\n';
|
||||
|
||||
// s.changeColor(llvm::raw_ostream::Colors::GREEN);
|
||||
// s << llvm::formatv("{0}", llvm::fmt_pad("^", (size_t)loc.start.col - 1,
|
||||
// 0)); s.resetColor();
|
||||
|
||||
// s << '\n';
|
||||
|
||||
// s << "[";
|
||||
// if (err != nullptr) {
|
||||
// writeColorByType(os, err->getErrId 8());
|
||||
// }
|
||||
// s << "] ";
|
||||
// writeColorByType(os, getPrefix(prefix));
|
||||
// s << ": ";
|
||||
|
||||
// if (err != nullptr) {
|
||||
// s << err->description << '\n';
|
||||
// }
|
||||
|
||||
// if (!message.empty()) {
|
||||
// s.changeColor(llvm::raw_ostream::Colors::YELLOW);
|
||||
// s << "With message";
|
||||
// s.resetColor();
|
||||
// s << ": " << message << "\n";
|
||||
// }
|
||||
|
||||
// if (err != nullptr) {
|
||||
// s << "For more information checkout";
|
||||
// s.changeColor(llvm::raw_ostream::Colors::CYAN);
|
||||
// s << " `serenec --explain ";
|
||||
// s << err->getErrId() << "`\n";
|
||||
// }
|
||||
};
|
||||
|
||||
DiagnosticEngine::DiagnosticEngine(SereneContext &ctx)
|
||||
: ctx(ctx), diagEngine(ctx.mlirContext.getDiagEngine()){};
|
||||
|
||||
void DiagnosticEngine::print(llvm::raw_ostream &os, Diagnostic &d) {
|
||||
UNUSED(ctx);
|
||||
UNUSED(os);
|
||||
UNUSED(d);
|
||||
};
|
||||
|
||||
Diagnostic DiagnosticEngine::toDiagnostic(reader::LocationRange loc,
|
||||
llvm::Error &e, llvm::StringRef msg,
|
||||
llvm::StringRef fn) {
|
||||
return Diagnostic(ctx, loc, &e, msg, fn);
|
||||
};
|
||||
|
||||
void DiagnosticEngine::enqueueError(llvm::StringRef msg) {
|
||||
llvm::errs() << llvm::formatv("FIX ME (better emit error): {0}\n", msg);
|
||||
terminate(ctx, 1);
|
||||
};
|
||||
|
||||
void DiagnosticEngine::emitSyntaxError(reader::LocationRange loc,
|
||||
llvm::Error &e, llvm::StringRef msg) {
|
||||
Diagnostic diag(ctx, loc, &e, msg);
|
||||
|
||||
diag.print(llvm::errs(), "SyntaxError");
|
||||
terminate(ctx, 1);
|
||||
};
|
||||
|
||||
std::unique_ptr<DiagnosticEngine> makeDiagnosticEngine(SereneContext &ctx) {
|
||||
return std::make_unique<DiagnosticEngine>(ctx);
|
||||
}
|
||||
|
||||
void DiagnosticEngine::emit(const llvm::Error &err) {
|
||||
UNUSED(ctx);
|
||||
// TODO: create a diag and print it
|
||||
llvm::errs() << "[Error]: " << err << "\n";
|
||||
};
|
||||
|
||||
// void DiagnosticEngine::emit(const llvm::Error &errs) {
|
||||
//
|
||||
// // for (const auto &e : errs) {
|
||||
// // emit(e);
|
||||
// // }
|
||||
//
|
||||
// };
|
||||
|
||||
void panic(SereneContext &ctx, llvm::Twine msg) {
|
||||
|
||||
auto err =
|
||||
llvm::make_error<llvm::StringError>(msg, llvm::inconvertibleErrorCode());
|
||||
ctx.diagEngine->emit(err);
|
||||
terminate(ctx, 1);
|
||||
};
|
||||
|
||||
void panic(SereneContext &ctx, const llvm::Error &err) {
|
||||
ctx.diagEngine->emit(err);
|
||||
terminate(ctx, 1);
|
||||
};
|
||||
|
||||
void throwErrors(SereneContext &ctx, const llvm::Error &err) {
|
||||
// llvm::handleErrors(errs,
|
||||
// [](const errors::SereneError &e){});
|
||||
ctx.diagEngine->emit(err);
|
||||
};
|
||||
|
||||
} // namespace serene
|
|
@ -1,42 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/errors.h"
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
|
||||
namespace serene::errors {
|
||||
|
||||
// We need this to make Error class a llvm::Error friendy implementation
|
||||
char SereneError::ID;
|
||||
|
||||
std::string getMessage(const llvm::Error &e) {
|
||||
std::string msg;
|
||||
llvm::raw_string_ostream os(msg);
|
||||
os << e;
|
||||
return os.str();
|
||||
};
|
||||
|
||||
const ErrorVariant *getVariant(ErrorType t) {
|
||||
if ((0 <= (int)t) && (t < NUMBER_OF_ERRORS)) {
|
||||
return &errorVariants[t];
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
} // namespace serene::errors
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/exprs/call.h"
|
||||
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/def.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/list.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/ErrorHandling.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
|
||||
ExprType Call::getType() const { return ExprType::Call; };
|
||||
|
||||
std::string Call::toString() const {
|
||||
return llvm::formatv("<Call {0} {1}>", this->target->toString(),
|
||||
astToString(&this->params));
|
||||
}
|
||||
|
||||
MaybeNode Call::analyze(semantics::AnalysisState &state) {
|
||||
UNUSED(state);
|
||||
|
||||
return EmptyNode;
|
||||
};
|
||||
|
||||
bool Call::classof(const Expression *e) {
|
||||
return e->getType() == ExprType::Call;
|
||||
};
|
||||
|
||||
MaybeNode Call::make(semantics::AnalysisState &state, List *list) {
|
||||
|
||||
auto &ctx = state.ns.getContext();
|
||||
// TODO: replace this with a runtime check
|
||||
assert((list->count() != 0) && "Empty call? Seriously ?");
|
||||
|
||||
// Let's find out what is the first element of the list
|
||||
auto maybeFirst = list->elements[0]->analyze(state);
|
||||
|
||||
if (!maybeFirst) {
|
||||
// There's something wrong with the first element. Return the error
|
||||
return maybeFirst;
|
||||
}
|
||||
|
||||
Node &first = *maybeFirst;
|
||||
|
||||
// No rewrite is needed for the first element
|
||||
if (!first) {
|
||||
first = list->elements[0];
|
||||
}
|
||||
|
||||
Node targetNode;
|
||||
Ast rawParams;
|
||||
|
||||
if (list->count() > 1) {
|
||||
rawParams = list->from(1);
|
||||
}
|
||||
|
||||
// We need to create the Call node based on the type of the first
|
||||
// element after it being analyzed.
|
||||
switch (first->getType()) {
|
||||
|
||||
// In case of a Symbol, We should look it up in the current scope and
|
||||
// if it resolves to a value. Then we have to make sure that the
|
||||
// return value is callable.
|
||||
case ExprType::Symbol: {
|
||||
|
||||
auto *sym = llvm::dyn_cast<Symbol>(first.get());
|
||||
|
||||
if (sym == nullptr) {
|
||||
llvm_unreachable("Couldn't case to Symbol while the type is symbol!");
|
||||
}
|
||||
|
||||
auto maybeResult = state.env.lookup(sym->name);
|
||||
|
||||
if (!maybeResult.hasValue()) {
|
||||
std::string msg =
|
||||
llvm::formatv("Can't resolve the symbol '{0}'", sym->name);
|
||||
return errors::makeError(ctx, errors::CantResolveSymbol, sym->location,
|
||||
msg);
|
||||
}
|
||||
|
||||
targetNode = std::move(maybeResult.getValue());
|
||||
break;
|
||||
}
|
||||
|
||||
case ExprType::Def:
|
||||
|
||||
// If the first element was a Call itself we need to just chain it
|
||||
// with a new call. It would be something like `((blah 1) 4)`. `blah`
|
||||
// should return a callable expression itself, which we need to let
|
||||
// the typechecker to check
|
||||
case ExprType::Call:
|
||||
// If the first element was a function, then just use it as the target
|
||||
// of the call. It would be like `((fn (x) x) 4)`
|
||||
case ExprType::Fn: {
|
||||
targetNode = first;
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise we don't know how to call the first element.
|
||||
default: {
|
||||
std::string msg = llvm::formatv("Don't know how to call a '{0}'",
|
||||
stringifyExprType(first->getType()));
|
||||
return errors::makeError(ctx, errors::DontKnowHowToCallNode,
|
||||
first->location, msg);
|
||||
}
|
||||
};
|
||||
|
||||
auto analyzedParams = semantics::analyze(state, rawParams);
|
||||
|
||||
if (!analyzedParams) {
|
||||
return analyzedParams.takeError();
|
||||
}
|
||||
|
||||
return makeSuccessfulNode<Call>(list->location, targetNode, *analyzedParams);
|
||||
};
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/exprs/def.h"
|
||||
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/fn.h"
|
||||
#include "serene/exprs/list.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
#include "serene/exprs/traits.h"
|
||||
#include "serene/slir/dialect.h"
|
||||
#include "serene/slir/utils.h"
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/ErrorHandling.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
|
||||
ExprType Def::getType() const { return ExprType::Def; };
|
||||
|
||||
std::string Def::toString() const {
|
||||
return llvm::formatv("<Def {0} -> {1}>", this->binding,
|
||||
this->value->toString());
|
||||
}
|
||||
|
||||
MaybeNode Def::analyze(semantics::AnalysisState &state) {
|
||||
UNUSED(state);
|
||||
|
||||
return EmptyNode;
|
||||
};
|
||||
|
||||
bool Def::classof(const Expression *e) {
|
||||
return e->getType() == ExprType::Def;
|
||||
};
|
||||
|
||||
MaybeNode Def::make(semantics::AnalysisState &state, List *list) {
|
||||
auto &ctx = state.ns.getContext();
|
||||
|
||||
// TODO: Add support for docstring as the 3rd argument (4th element)
|
||||
if (list->count() != 3) {
|
||||
std::string msg = llvm::formatv("Expected 3 got {0}", list->count());
|
||||
return errors::makeError(ctx, errors::DefWrongNumberOfArgs,
|
||||
list->elements[0]->location, msg);
|
||||
}
|
||||
|
||||
// Make sure that the list starts with a `def`
|
||||
Symbol *defSym = llvm::dyn_cast<Symbol>(list->elements[0].get());
|
||||
|
||||
assert((defSym && defSym->name == "def") &&
|
||||
"The first element of the list should be a 'def'.");
|
||||
|
||||
// Make sure that the first argument is a Symbol
|
||||
Symbol *binding = llvm::dyn_cast<Symbol>(list->elements[1].get());
|
||||
if (binding == nullptr) {
|
||||
return errors::makeError(ctx, errors::DefExpectSymbol,
|
||||
list->elements[1]->location);
|
||||
}
|
||||
|
||||
// Analyze the value
|
||||
MaybeNode value = list->elements[2]->analyze(state);
|
||||
Node analyzedValue;
|
||||
|
||||
// TODO: To refactor this logic into a generic function
|
||||
if (value) {
|
||||
// Success value
|
||||
auto &valueNode = *value;
|
||||
|
||||
if (valueNode) {
|
||||
// A rewrite is necessary
|
||||
analyzedValue = valueNode;
|
||||
} else {
|
||||
// no rewrite
|
||||
analyzedValue = list->elements[2];
|
||||
}
|
||||
} else {
|
||||
// Error value
|
||||
return value;
|
||||
}
|
||||
|
||||
if (analyzedValue->getType() == ExprType::Fn) {
|
||||
Fn *tmp = llvm::dyn_cast<Fn>(analyzedValue.get());
|
||||
if (tmp == nullptr) {
|
||||
llvm_unreachable("inconsistent getType for function");
|
||||
}
|
||||
|
||||
tmp->setName(binding->name);
|
||||
}
|
||||
|
||||
auto result = state.ns.define(binding->name, analyzedValue);
|
||||
|
||||
if (result.succeeded()) {
|
||||
return makeSuccessfulNode<Def>(list->location, binding->name,
|
||||
analyzedValue);
|
||||
}
|
||||
llvm_unreachable("Inserting a value in the semantic env failed!");
|
||||
};
|
||||
|
||||
void Def::generateIR(serene::Namespace &ns, mlir::ModuleOp &m) {
|
||||
|
||||
if (value->getType() == ExprType::Fn) {
|
||||
value->generateIR(ns, m);
|
||||
return;
|
||||
}
|
||||
|
||||
// auto loc = slir::toMLIRLocation(ns, location.start);
|
||||
// auto &mctx = ns.getContext().mlirContext;
|
||||
|
||||
// mlir::OpBuilder builder(&mctx);
|
||||
|
||||
// auto sym = slir::SymbolType::get(&mctx, ns.name, binding);
|
||||
|
||||
// TODO: we need to change the generate method of expressions
|
||||
// to return mlir::Value or any wrapper of that which would
|
||||
// be the ssa form of the result of the expression.
|
||||
// and then use it to define the def op here.
|
||||
// auto def = builder.create<slir::DefOp>(sym, binding, value);
|
||||
m.emitError("Def: not implemented!");
|
||||
};
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
|
@ -1,46 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/exprs/expression.h"
|
||||
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
|
||||
std::string astToString(const Ast *tree) {
|
||||
if (tree->empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string result = tree->at(0)->toString();
|
||||
|
||||
for (unsigned int i = 1; i < tree->size(); i++) {
|
||||
result = llvm::formatv("{0} {1}", result, tree->at(i)->toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string stringifyExprType(ExprType t) { return exprTypes[(int)t]; };
|
||||
|
||||
/// Dump the given AST tree to the standard out
|
||||
void dump(Ast &tree) { llvm::outs() << astToString(&tree) << "\n"; };
|
||||
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
|
@ -1,166 +0,0 @@
|
|||
/*
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/exprs/fn.h"
|
||||
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/list.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
#include "serene/slir/dialect.h"
|
||||
#include "serene/slir/ops.h"
|
||||
#include "serene/slir/utils.h"
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
#include <mlir/IR/Block.h>
|
||||
#include <mlir/IR/BuiltinAttributes.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
|
||||
Fn::Fn(SereneContext &ctx, reader::LocationRange &loc, List &args, Ast body)
|
||||
: Expression(loc), args(args), body(std::move(body)) {
|
||||
this->setName(
|
||||
llvm::formatv("___fn___{0}", ctx.getCurrentNS().nextFnCounter()));
|
||||
};
|
||||
|
||||
ExprType Fn::getType() const { return ExprType::Fn; };
|
||||
|
||||
std::string Fn::toString() const {
|
||||
return llvm::formatv("<Fn {0} {1} to {2}>",
|
||||
this->name.empty() ? "Anonymous" : this->name,
|
||||
this->args.toString(),
|
||||
this->body.empty() ? "<>" : astToString(&this->body));
|
||||
}
|
||||
|
||||
MaybeNode Fn::analyze(semantics::AnalysisState &state) {
|
||||
UNUSED(state);
|
||||
|
||||
return EmptyNode;
|
||||
};
|
||||
|
||||
bool Fn::classof(const Expression *e) { return e->getType() == ExprType::Fn; };
|
||||
|
||||
void Fn::setName(std::string n) { this->name = std::move(n); };
|
||||
|
||||
MaybeNode Fn::make(semantics::AnalysisState &state, List *list) {
|
||||
|
||||
auto &ctx = state.ns.getContext();
|
||||
|
||||
// TODO: Add support for docstring as the 3rd argument (4th element)
|
||||
if (list->count() < 2) {
|
||||
return errors::makeError(ctx, errors::FnNoArgsList,
|
||||
list->elements[0]->location,
|
||||
"The argument list is mandatory.");
|
||||
}
|
||||
|
||||
Symbol *fnSym = llvm::dyn_cast<Symbol>(list->elements[0].get());
|
||||
|
||||
// TODO: Replace this assert with a runtime check
|
||||
assert((fnSym && fnSym->name == "fn") &&
|
||||
"The first element of the list should be a 'fn'.");
|
||||
|
||||
List *args = llvm::dyn_cast<List>(list->elements[1].get());
|
||||
|
||||
if (args == nullptr) {
|
||||
std::string msg =
|
||||
llvm::formatv("Arguments of a function has to be a list, got '{0}'",
|
||||
stringifyExprType(list->elements[1]->getType()));
|
||||
|
||||
return errors::makeError(ctx, errors::FnArgsMustBeList,
|
||||
list->elements[1]->location, msg);
|
||||
}
|
||||
|
||||
Ast body;
|
||||
|
||||
// If there is a body for this function analyze the body and set
|
||||
// the retuned ast as the final body
|
||||
if (list->count() > 2) {
|
||||
body = std::vector<Node>(list->begin() + 2, list->end());
|
||||
|
||||
// TODO: call state.moveToNewEnv to create a new env and set the
|
||||
// arguments into the new env.
|
||||
|
||||
auto maybeAst = semantics::analyze(state, body);
|
||||
|
||||
if (!maybeAst) {
|
||||
return maybeAst.takeError();
|
||||
}
|
||||
|
||||
body = *maybeAst;
|
||||
}
|
||||
|
||||
return makeSuccessfulNode<Fn>(ctx, list->location, *args, body);
|
||||
};
|
||||
|
||||
void Fn::generateIR(serene::Namespace &ns, mlir::ModuleOp &m) {
|
||||
auto loc = slir::toMLIRLocation(ns, location.start);
|
||||
auto &ctx = ns.getContext();
|
||||
|
||||
mlir::OpBuilder builder(&ctx.mlirContext);
|
||||
|
||||
// llvm::SmallVector<mlir::Type, 4> arg_types;
|
||||
llvm::SmallVector<mlir::NamedAttribute, 4> arguments;
|
||||
// at the moment we only support integers
|
||||
for (auto &arg : args) {
|
||||
auto *argSym = llvm::dyn_cast<Symbol>(arg.get());
|
||||
|
||||
if (argSym == nullptr) {
|
||||
m->emitError(llvm::formatv(
|
||||
"Arguments of a function have to be symbols. Fn: '{0}'", name));
|
||||
return;
|
||||
}
|
||||
|
||||
arguments.push_back(builder.getNamedAttr(
|
||||
argSym->name, mlir::TypeAttr::get(builder.getI64Type())));
|
||||
}
|
||||
|
||||
auto fn = builder.create<slir::Fn1Op>(
|
||||
loc, builder.getI64Type(), name,
|
||||
mlir::DictionaryAttr::get(builder.getContext(), arguments),
|
||||
builder.getStringAttr("public"));
|
||||
|
||||
if (!fn) {
|
||||
m.emitError(llvm::formatv("Can't create the function '{0}'", name));
|
||||
return;
|
||||
}
|
||||
|
||||
auto &body = fn.body();
|
||||
auto *entryBlock = new mlir::Block();
|
||||
|
||||
body.push_back(entryBlock);
|
||||
|
||||
builder.setInsertionPointToStart(entryBlock);
|
||||
auto retVal = builder.create<slir::Value1Op>(loc, 0).getResult();
|
||||
|
||||
slir::Return1Op returnOp = builder.create<slir::Return1Op>(loc, retVal);
|
||||
|
||||
if (!returnOp) {
|
||||
m.emitError(
|
||||
llvm::formatv("Can't create the return value of function '{0}'", name));
|
||||
fn.erase();
|
||||
return;
|
||||
}
|
||||
|
||||
m.push_back(fn);
|
||||
};
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/exprs/list.h"
|
||||
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/call.h"
|
||||
#include "serene/exprs/def.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/fn.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/ErrorHandling.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
|
||||
List::List(const List &l) : Expression(l.location) {
|
||||
this->elements = l.elements;
|
||||
};
|
||||
|
||||
List::List(const reader::LocationRange &loc, Node &e) : Expression(loc) {
|
||||
elements.push_back(e);
|
||||
};
|
||||
|
||||
List::List(const reader::LocationRange &loc, Ast elems)
|
||||
: Expression(loc), elements(std::move(elems)){};
|
||||
|
||||
ExprType List::getType() const { return ExprType::List; };
|
||||
|
||||
std::string List::toString() const {
|
||||
std::string s{this->elements.empty() ? "-" : ""};
|
||||
|
||||
for (const auto &n : this->elements) {
|
||||
s = llvm::formatv("{0} {1}", s, n->toString());
|
||||
}
|
||||
|
||||
return llvm::formatv("<List {0}>", s);
|
||||
};
|
||||
|
||||
MaybeNode List::analyze(semantics::AnalysisState &state) {
|
||||
|
||||
if (!elements.empty()) {
|
||||
auto *first = elements[0].get();
|
||||
|
||||
if (first->getType() == ExprType::Symbol) {
|
||||
auto *sym = llvm::dyn_cast<Symbol>(first);
|
||||
|
||||
if (sym != nullptr) {
|
||||
if (sym->name == "def") {
|
||||
return Def::make(state, this);
|
||||
}
|
||||
|
||||
if (sym->name == "fn") {
|
||||
return Fn::make(state, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Call::make(state, this);
|
||||
}
|
||||
|
||||
return EmptyNode;
|
||||
};
|
||||
|
||||
bool List::classof(const Expression *e) {
|
||||
return e->getType() == ExprType::List;
|
||||
};
|
||||
|
||||
std::vector<Node>::const_iterator List::cbegin() { return elements.begin(); }
|
||||
|
||||
std::vector<Node>::const_iterator List::cend() { return elements.end(); }
|
||||
|
||||
std::vector<Node>::iterator List::begin() { return elements.begin(); }
|
||||
|
||||
std::vector<Node>::iterator List::end() { return elements.end(); }
|
||||
|
||||
size_t List::count() const { return elements.size(); }
|
||||
|
||||
std::optional<Expression *> List::at(uint index) {
|
||||
if (index >= elements.size()) {
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
return std::optional<Expression *>(this->elements[index].get());
|
||||
}
|
||||
|
||||
Ast List::from(uint index) {
|
||||
if (index < elements.size()) {
|
||||
return Ast(elements.begin() + index, elements.end());
|
||||
}
|
||||
|
||||
return Ast();
|
||||
}
|
||||
|
||||
void List::append(Node n) { elements.push_back(std::move(n)); }
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
|
@ -1,59 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/exprs/number.h"
|
||||
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/slir/dialect.h"
|
||||
#include "serene/slir/ops.h"
|
||||
#include "serene/slir/utils.h"
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
|
||||
ExprType Number::getType() const { return ExprType::Number; };
|
||||
|
||||
std::string Number::toString() const {
|
||||
return llvm::formatv("<Number {0}>", value);
|
||||
}
|
||||
|
||||
MaybeNode Number::analyze(semantics::AnalysisState &state) {
|
||||
UNUSED(state);
|
||||
|
||||
return EmptyNode;
|
||||
};
|
||||
|
||||
bool Number::classof(const Expression *e) {
|
||||
return e->getType() == ExprType::Number;
|
||||
};
|
||||
|
||||
int Number::toI64() const { return std::stoi(this->value); };
|
||||
|
||||
void Number::generateIR(serene::Namespace &ns, mlir::ModuleOp &m) {
|
||||
mlir::OpBuilder builder(&ns.getContext().mlirContext);
|
||||
|
||||
auto op = builder.create<serene::slir::Value1Op>(
|
||||
serene::slir::toMLIRLocation(ns, location.start), toI64());
|
||||
|
||||
if (op) {
|
||||
m.push_back(op);
|
||||
}
|
||||
// TODO: in case of failure attach the error to the NS
|
||||
};
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/exprs/symbol.h"
|
||||
|
||||
#include "serene/exprs/expression.h"
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
|
||||
ExprType Symbol::getType() const { return ExprType::Symbol; };
|
||||
|
||||
std::string Symbol::toString() const {
|
||||
return llvm::formatv("<Symbol {0}/{1}>", nsName, name);
|
||||
}
|
||||
|
||||
MaybeNode Symbol::analyze(semantics::AnalysisState &state) {
|
||||
UNUSED(state);
|
||||
|
||||
return EmptyNode;
|
||||
};
|
||||
|
||||
bool Symbol::classof(const Expression *e) {
|
||||
return e->getType() == ExprType::Symbol;
|
||||
};
|
||||
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
|
@ -1,110 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/jit/engine.h"
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/jit/layers.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace serene::jit {
|
||||
|
||||
static void handleLazyCallThroughError() {
|
||||
// TODO: Report to the diag engine
|
||||
llvm::errs() << "LazyCallThrough error: Could not find function body";
|
||||
// TODO: terminate ?
|
||||
}
|
||||
|
||||
SereneJIT::SereneJIT(Namespace &entryNS,
|
||||
std::unique_ptr<orc::ExecutionSession> es,
|
||||
std::unique_ptr<orc::EPCIndirectionUtils> epciu,
|
||||
orc::JITTargetMachineBuilder jtmb, llvm::DataLayout &&dl)
|
||||
|
||||
: es(std::move(es)), epciu(std::move(epciu)), dl(dl),
|
||||
mangler(*this->es, this->dl),
|
||||
objectLayer(
|
||||
*this->es,
|
||||
[]() { return std::make_unique<llvm::SectionMemoryManager>(); }),
|
||||
compileLayer(
|
||||
*this->es, objectLayer,
|
||||
std::make_unique<orc::ConcurrentIRCompiler>(std::move(jtmb))),
|
||||
transformLayer(*this->es, compileLayer, optimizeModule),
|
||||
astLayer(transformLayer, mangler),
|
||||
nsLayer(entryNS.getContext(), transformLayer, mangler, dl),
|
||||
mainJD(this->es->createBareJITDylib(entryNS.name)),
|
||||
ctx(entryNS.getContext()), currentNS(entryNS) {
|
||||
UNUSED(this->ctx);
|
||||
mainJD.addGenerator(
|
||||
cantFail(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
|
||||
dl.getGlobalPrefix())));
|
||||
|
||||
// if (numCompileThreads > 0) {
|
||||
// compileOnDemandLayer.setCloneToNewContextOnEmit(true);
|
||||
// }
|
||||
}
|
||||
|
||||
llvm::Error SereneJIT::addNS(llvm::StringRef nsname,
|
||||
orc::ResourceTrackerSP rt) {
|
||||
if (!rt) {
|
||||
rt = mainJD.getDefaultResourceTracker();
|
||||
}
|
||||
|
||||
return nsLayer.add(rt, nsname);
|
||||
};
|
||||
|
||||
llvm::Error SereneJIT::addAst(exprs::Ast &ast, orc::ResourceTrackerSP rt) {
|
||||
if (!rt) {
|
||||
rt = mainJD.getDefaultResourceTracker();
|
||||
}
|
||||
|
||||
return astLayer.add(rt, getCurrentNS(), ast);
|
||||
};
|
||||
|
||||
llvm::Expected<std::unique_ptr<SereneJIT>> makeSereneJIT(Namespace &ns) {
|
||||
auto epc = orc::SelfExecutorProcessControl::Create();
|
||||
if (!epc) {
|
||||
return epc.takeError();
|
||||
}
|
||||
auto es = std::make_unique<orc::ExecutionSession>(std::move(*epc));
|
||||
auto epciu =
|
||||
orc::EPCIndirectionUtils::Create(es->getExecutorProcessControl());
|
||||
if (!epciu) {
|
||||
return epciu.takeError();
|
||||
}
|
||||
|
||||
(*epciu)->createLazyCallThroughManager(
|
||||
*es, llvm::pointerToJITTargetAddress(&handleLazyCallThroughError));
|
||||
|
||||
if (auto err = setUpInProcessLCTMReentryViaEPCIU(**epciu)) {
|
||||
return std::move(err);
|
||||
}
|
||||
|
||||
orc::JITTargetMachineBuilder jtmb(
|
||||
es->getExecutorProcessControl().getTargetTriple());
|
||||
|
||||
auto dl = jtmb.getDefaultDataLayoutForTarget();
|
||||
if (!dl) {
|
||||
return dl.takeError();
|
||||
}
|
||||
|
||||
return std::make_unique<SereneJIT>(ns, std::move(es), std::move(*epciu),
|
||||
std::move(jtmb), std::move(*dl));
|
||||
};
|
||||
} // namespace serene::jit
|
|
@ -1,457 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/jit/halley.h"
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/diagnostics.h"
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm/ADT/None.h>
|
||||
#include <llvm/ADT/Optional.h>
|
||||
#include <llvm/ExecutionEngine/Orc/CompileUtils.h>
|
||||
#include <llvm/ExecutionEngine/Orc/Core.h>
|
||||
#include <llvm/ExecutionEngine/Orc/ExecutionUtils.h>
|
||||
#include <llvm/ExecutionEngine/Orc/IRCompileLayer.h>
|
||||
#include <llvm/ExecutionEngine/Orc/IRTransformLayer.h>
|
||||
#include <llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h>
|
||||
#include <llvm/ExecutionEngine/Orc/LLJIT.h>
|
||||
#include <llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h>
|
||||
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Support/ErrorHandling.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
#include <llvm/Support/FormatVariadicDetails.h>
|
||||
#include <llvm/Support/ToolOutputFile.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <mlir/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <mlir/Support/FileUtilities.h>
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#define COMMON_ARGS_COUNT 8
|
||||
// Just to make the linter happy
|
||||
#define I64_BIT_SIZE 64
|
||||
|
||||
namespace serene {
|
||||
|
||||
namespace jit {
|
||||
|
||||
static std::string makePackedFunctionName(llvm::StringRef name) {
|
||||
// TODO: move the "_serene_" constant to a macro or something
|
||||
return "_serene_" + name.str();
|
||||
}
|
||||
|
||||
static void packFunctionArguments(llvm::Module *module) {
|
||||
auto &ctx = module->getContext();
|
||||
llvm::IRBuilder<> builder(ctx);
|
||||
llvm::DenseSet<llvm::Function *> interfaceFunctions;
|
||||
for (auto &func : module->getFunctionList()) {
|
||||
if (func.isDeclaration()) {
|
||||
continue;
|
||||
}
|
||||
if (interfaceFunctions.count(&func) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Given a function `foo(<...>)`, define the interface function
|
||||
// `serene_foo(i8**)`.
|
||||
auto *newType = llvm::FunctionType::get(
|
||||
builder.getVoidTy(), builder.getInt8PtrTy()->getPointerTo(),
|
||||
/*isVarArg=*/false);
|
||||
auto newName = makePackedFunctionName(func.getName());
|
||||
auto funcCst = module->getOrInsertFunction(newName, newType);
|
||||
llvm::Function *interfaceFunc =
|
||||
llvm::cast<llvm::Function>(funcCst.getCallee());
|
||||
interfaceFunctions.insert(interfaceFunc);
|
||||
|
||||
// Extract the arguments from the type-erased argument list and cast them to
|
||||
// the proper types.
|
||||
auto *bb = llvm::BasicBlock::Create(ctx);
|
||||
bb->insertInto(interfaceFunc);
|
||||
builder.SetInsertPoint(bb);
|
||||
llvm::Value *argList = interfaceFunc->arg_begin();
|
||||
llvm::SmallVector<llvm::Value *, COMMON_ARGS_COUNT> args;
|
||||
args.reserve(llvm::size(func.args()));
|
||||
for (const auto &indexedArg : llvm::enumerate(func.args())) {
|
||||
llvm::Value *argIndex = llvm::Constant::getIntegerValue(
|
||||
builder.getInt64Ty(), llvm::APInt(I64_BIT_SIZE, indexedArg.index()));
|
||||
llvm::Value *argPtrPtr =
|
||||
builder.CreateGEP(builder.getInt8PtrTy(), argList, argIndex);
|
||||
llvm::Value *argPtr =
|
||||
builder.CreateLoad(builder.getInt8PtrTy(), argPtrPtr);
|
||||
llvm::Type *argTy = indexedArg.value().getType();
|
||||
argPtr = builder.CreateBitCast(argPtr, argTy->getPointerTo());
|
||||
llvm::Value *arg = builder.CreateLoad(argTy, argPtr);
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
||||
// Call the implementation function with the extracted arguments.
|
||||
llvm::Value *result = builder.CreateCall(&func, args);
|
||||
|
||||
// Assuming the result is one value, potentially of type `void`.
|
||||
if (!result->getType()->isVoidTy()) {
|
||||
llvm::Value *retIndex = llvm::Constant::getIntegerValue(
|
||||
builder.getInt64Ty(),
|
||||
llvm::APInt(I64_BIT_SIZE, llvm::size(func.args())));
|
||||
llvm::Value *retPtrPtr =
|
||||
builder.CreateGEP(builder.getInt8PtrTy(), argList, retIndex);
|
||||
llvm::Value *retPtr =
|
||||
builder.CreateLoad(builder.getInt8PtrTy(), retPtrPtr);
|
||||
retPtr = builder.CreateBitCast(retPtr, result->getType()->getPointerTo());
|
||||
builder.CreateStore(result, retPtr);
|
||||
}
|
||||
|
||||
// The interface function returns void.
|
||||
builder.CreateRetVoid();
|
||||
}
|
||||
};
|
||||
|
||||
void ObjectCache::notifyObjectCompiled(const llvm::Module *m,
|
||||
llvm::MemoryBufferRef objBuffer) {
|
||||
cachedObjects[m->getModuleIdentifier()] =
|
||||
llvm::MemoryBuffer::getMemBufferCopy(objBuffer.getBuffer(),
|
||||
objBuffer.getBufferIdentifier());
|
||||
}
|
||||
|
||||
std::unique_ptr<llvm::MemoryBuffer>
|
||||
ObjectCache::getObject(const llvm::Module *m) {
|
||||
auto i = cachedObjects.find(m->getModuleIdentifier());
|
||||
|
||||
if (i == cachedObjects.end()) {
|
||||
HALLEY_LOG("No object for " + m->getModuleIdentifier() +
|
||||
" in cache. Compiling.");
|
||||
return nullptr;
|
||||
}
|
||||
HALLEY_LOG("Object for " + m->getModuleIdentifier() + " loaded from cache.");
|
||||
return llvm::MemoryBuffer::getMemBuffer(i->second->getMemBufferRef());
|
||||
}
|
||||
|
||||
void ObjectCache::dumpToObjectFile(llvm::StringRef outputFilename) {
|
||||
// Set up the output file.
|
||||
std::string errorMessage;
|
||||
auto file = mlir::openOutputFile(outputFilename, &errorMessage);
|
||||
if (!file) {
|
||||
llvm::errs() << errorMessage << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Dump the object generated for a single module to the output file.
|
||||
// TODO: Replace this with a runtime check
|
||||
assert(cachedObjects.size() == 1 && "Expected only one object entry.");
|
||||
|
||||
auto &cachedObject = cachedObjects.begin()->second;
|
||||
file->os() << cachedObject->getBuffer();
|
||||
file->keep();
|
||||
}
|
||||
|
||||
Halley::Halley(serene::SereneContext &ctx,
|
||||
llvm::orc::JITTargetMachineBuilder &&jtmb, llvm::DataLayout &&dl)
|
||||
: cache(ctx.opts.JITenableObjectCache ? new ObjectCache() : nullptr),
|
||||
gdbListener(ctx.opts.JITenableGDBNotificationListener
|
||||
|
||||
? llvm::JITEventListener::createGDBRegistrationListener()
|
||||
: nullptr),
|
||||
perfListener(ctx.opts.JITenablePerfNotificationListener
|
||||
? llvm::JITEventListener::createPerfJITEventListener()
|
||||
: nullptr),
|
||||
jtmb(jtmb), dl(dl), ctx(ctx),
|
||||
activeNS(ctx.getSharedPtrToNS(ctx.getCurrentNS().name)) {
|
||||
assert(activeNS != nullptr && "Active NS is null!!!");
|
||||
};
|
||||
|
||||
MaybeJITPtr Halley::lookup(exprs::Symbol &sym) const {
|
||||
HALLEY_LOG("Looking up: " << sym.toString());
|
||||
auto *ns = ctx.getNS(sym.nsName);
|
||||
|
||||
if (ns == nullptr) {
|
||||
return errors::makeError(ctx, errors::CantResolveSymbol, sym.location,
|
||||
"Can't find the namespace in the context: " +
|
||||
sym.nsName);
|
||||
}
|
||||
|
||||
auto *dylib = ctx.getLatestJITDylib(*ns);
|
||||
//
|
||||
|
||||
if (dylib == nullptr) {
|
||||
return errors::makeError(ctx, errors::CantResolveSymbol, sym.location,
|
||||
"Don't know about namespace: " + sym.nsName);
|
||||
}
|
||||
|
||||
auto expectedSymbol =
|
||||
engine->lookup(*dylib, makePackedFunctionName(sym.name));
|
||||
|
||||
// JIT lookup may return an Error referring to strings stored internally by
|
||||
// the JIT. If the Error outlives the ExecutionEngine, it would want have a
|
||||
// dangling reference, which is currently caught by an assertion inside JIT
|
||||
// thanks to hand-rolled reference counting. Rewrap the error message into a
|
||||
// string before returning. Alternatively, ORC JIT should consider copying
|
||||
// the string into the error message.
|
||||
if (!expectedSymbol) {
|
||||
std::string errorMessage;
|
||||
llvm::raw_string_ostream os(errorMessage);
|
||||
llvm::handleAllErrors(expectedSymbol.takeError(),
|
||||
[&os](llvm::ErrorInfoBase &ei) { ei.log(os); });
|
||||
return errors::makeError(ctx, errors::CantResolveSymbol, sym.location,
|
||||
os.str());
|
||||
}
|
||||
|
||||
auto rawFPtr = expectedSymbol->getValue();
|
||||
// NOLINTNEXTLINE(performance-no-int-to-ptr)
|
||||
auto fptr = reinterpret_cast<void (*)(void **)>(rawFPtr);
|
||||
|
||||
if (fptr == nullptr) {
|
||||
return errors::makeError(ctx, errors::CantResolveSymbol, sym.location,
|
||||
"Lookup function is null!");
|
||||
}
|
||||
|
||||
return fptr;
|
||||
};
|
||||
|
||||
// llvm::Error Halley::invokePacked(llvm::StringRef name,
|
||||
// llvm::MutableArrayRef<void *> args) const {
|
||||
// auto expectedFPtr = lookup(name);
|
||||
// if (!expectedFPtr) {
|
||||
// return expectedFPtr.takeError();
|
||||
// }
|
||||
// auto fptr = *expectedFPtr;
|
||||
|
||||
// (*fptr)(args.data());
|
||||
|
||||
// return llvm::Error::success();
|
||||
// }
|
||||
|
||||
llvm::Error Halley::addNS(Namespace &ns, reader::LocationRange &loc) {
|
||||
|
||||
HALLEY_LOG(llvm::formatv("Creating Dylib {0}#{1}", ns.name,
|
||||
ctx.getNumberOfJITDylibs(ns) + 1));
|
||||
|
||||
auto newDylib = engine->createJITDylib(
|
||||
llvm::formatv("{0}#{1}", ns.name, ctx.getNumberOfJITDylibs(ns) + 1));
|
||||
|
||||
if (!newDylib) {
|
||||
return errors::makeError(ctx, errors::CompilationError, loc,
|
||||
"Filed to create dylib for " + ns.name);
|
||||
}
|
||||
|
||||
ctx.pushJITDylib(ns, &(*newDylib));
|
||||
|
||||
// TODO: Fix compileToLLVM to return proper errors
|
||||
auto maybeModule = ns.compileToLLVM();
|
||||
|
||||
if (!maybeModule.hasValue()) {
|
||||
return errors::makeError(ctx, errors::CompilationError, loc);
|
||||
}
|
||||
|
||||
auto tsm = std::move(maybeModule.getValue());
|
||||
tsm.withModuleDo([](llvm::Module &m) { packFunctionArguments(&m); });
|
||||
|
||||
// TODO: Make sure that the data layout of the module is the same as the
|
||||
// engine
|
||||
cantFail(engine->addIRModule(*newDylib, std::move(tsm)));
|
||||
return llvm::Error::success();
|
||||
};
|
||||
|
||||
void Halley::setEngine(std::unique_ptr<llvm::orc::LLJIT> e, bool isLazy) {
|
||||
// Later on we might use different classes of JIT which might need some
|
||||
// work for lazyness
|
||||
engine = std::move(e);
|
||||
this->isLazy = isLazy;
|
||||
};
|
||||
|
||||
void Halley::dumpToObjectFile(llvm::StringRef filename) {
|
||||
cache->dumpToObjectFile(filename);
|
||||
};
|
||||
|
||||
MaybeJIT Halley::make(SereneContext &serene_ctx,
|
||||
llvm::orc::JITTargetMachineBuilder &&jtmb) {
|
||||
|
||||
auto dl = jtmb.getDefaultDataLayoutForTarget();
|
||||
if (!dl) {
|
||||
return dl.takeError();
|
||||
}
|
||||
|
||||
auto jitEngine =
|
||||
std::make_unique<Halley>(serene_ctx, std::move(jtmb), std::move(*dl));
|
||||
|
||||
// Why not the llvmcontext from the SereneContext??
|
||||
// Sice we're going to pass the ownership of this context to a thread
|
||||
// safe module later on and we will only create the jit function wrappers
|
||||
// with it, then it is fine to use a new context.
|
||||
//
|
||||
// What might go wrong?
|
||||
// in a repl env when we have to create new modules on top of each other
|
||||
// having two different contex might be a problem, but i think since we
|
||||
// use the first context to generate the IR and the second one to just
|
||||
// run it.
|
||||
std::unique_ptr<llvm::LLVMContext> ctx(new llvm::LLVMContext);
|
||||
|
||||
// Callback to create the object layer with symbol resolution to current
|
||||
// process and dynamically linked libraries.
|
||||
auto objectLinkingLayerCreator = [&](llvm::orc::ExecutionSession &session,
|
||||
const llvm::Triple &tt) {
|
||||
UNUSED(tt);
|
||||
|
||||
auto objectLayer =
|
||||
std::make_unique<llvm::orc::RTDyldObjectLinkingLayer>(session, []() {
|
||||
return std::make_unique<llvm::SectionMemoryManager>();
|
||||
});
|
||||
|
||||
// Register JIT event listeners if they are enabled.
|
||||
if (jitEngine->gdbListener != nullptr) {
|
||||
objectLayer->registerJITEventListener(*jitEngine->gdbListener);
|
||||
}
|
||||
if (jitEngine->perfListener != nullptr) {
|
||||
objectLayer->registerJITEventListener(*jitEngine->perfListener);
|
||||
}
|
||||
|
||||
// COFF format binaries (Windows) need special handling to deal with
|
||||
// exported symbol visibility.
|
||||
// cf llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
|
||||
// LLJIT::createObjectLinkingLayer
|
||||
|
||||
if (serene_ctx.triple.isOSBinFormatCOFF()) {
|
||||
objectLayer->setOverrideObjectFlagsWithResponsibilityFlags(true);
|
||||
objectLayer->setAutoClaimResponsibilityForObjectSymbols(true);
|
||||
}
|
||||
|
||||
// Resolve symbols from shared libraries.
|
||||
// for (auto libPath : sharedLibPaths) {
|
||||
// auto mb = llvm::MemoryBuffer::getFile(libPath);
|
||||
// if (!mb) {
|
||||
// llvm::errs() << "Failed to create MemoryBuffer for: " << libPath
|
||||
// << "\nError: " << mb.getError().message() << "\n";
|
||||
// continue;
|
||||
// }
|
||||
// auto &JD = session.createBareJITDylib(std::string(libPath));
|
||||
// auto loaded = llvm::orc::DynamicLibrarySearchGenerator::Load(
|
||||
// libPath.data(), dataLayout.getGlobalPrefix());
|
||||
// if (!loaded) {
|
||||
// llvm::errs() << "Could not load " << libPath << ":\n "
|
||||
// << loaded.takeError() << "\n";
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// JD.addGenerator(std::move(*loaded));
|
||||
// cantFail(objectLayer->add(JD, std::move(mb.get())));
|
||||
// }
|
||||
|
||||
return objectLayer;
|
||||
};
|
||||
|
||||
// Callback to inspect the cache and recompile on demand. This follows Lang's
|
||||
// LLJITWithObjectCache example.
|
||||
auto compileFunctionCreator = [&](llvm::orc::JITTargetMachineBuilder JTMB)
|
||||
-> llvm::Expected<
|
||||
std::unique_ptr<llvm::orc::IRCompileLayer::IRCompiler>> {
|
||||
llvm::CodeGenOpt::Level jitCodeGenOptLevel =
|
||||
static_cast<llvm::CodeGenOpt::Level>(serene_ctx.getOptimizatioLevel());
|
||||
|
||||
JTMB.setCodeGenOptLevel(jitCodeGenOptLevel);
|
||||
|
||||
auto targetMachine = JTMB.createTargetMachine();
|
||||
if (!targetMachine) {
|
||||
return targetMachine.takeError();
|
||||
}
|
||||
|
||||
return std::make_unique<llvm::orc::TMOwningSimpleCompiler>(
|
||||
std::move(*targetMachine), jitEngine->cache.get());
|
||||
};
|
||||
|
||||
if (serene_ctx.opts.JITLazy) {
|
||||
// Setup a LLLazyJIT instance to the times that latency is important
|
||||
// for example in a REPL. This way
|
||||
|
||||
auto jit =
|
||||
cantFail(llvm::orc::LLLazyJITBuilder()
|
||||
.setCompileFunctionCreator(compileFunctionCreator)
|
||||
.setObjectLinkingLayerCreator(objectLinkingLayerCreator)
|
||||
.create());
|
||||
jitEngine->setEngine(std::move(jit), true);
|
||||
|
||||
} else {
|
||||
// Setup a LLJIT instance for the times that performance is important
|
||||
// and we want to compile everything as soon as possible. For instance
|
||||
// when we run the JIT in the compiler
|
||||
auto jit =
|
||||
cantFail(llvm::orc::LLJITBuilder()
|
||||
.setCompileFunctionCreator(compileFunctionCreator)
|
||||
.setObjectLinkingLayerCreator(objectLinkingLayerCreator)
|
||||
.create());
|
||||
|
||||
jitEngine->setEngine(std::move(jit), false);
|
||||
}
|
||||
|
||||
jitEngine->engine->getIRCompileLayer().setNotifyCompiled(
|
||||
[&](llvm::orc::MaterializationResponsibility &r,
|
||||
llvm::orc::ThreadSafeModule tsm) {
|
||||
auto syms = r.getRequestedSymbols();
|
||||
tsm.withModuleDo([&](llvm::Module &m) {
|
||||
HALLEY_LOG("Compiled "
|
||||
<< syms << " for the module: " << m.getModuleIdentifier());
|
||||
});
|
||||
});
|
||||
|
||||
// Resolve symbols that are statically linked in the current process.
|
||||
llvm::orc::JITDylib &mainJD = jitEngine->engine->getMainJITDylib();
|
||||
mainJD.addGenerator(
|
||||
cantFail(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
|
||||
jitEngine->dl.getGlobalPrefix())));
|
||||
|
||||
return MaybeJIT(std::move(jitEngine));
|
||||
};
|
||||
|
||||
llvm::Error Halley::addAST(exprs::Ast &ast) {
|
||||
auto offset = activeNS->getTree().size();
|
||||
|
||||
if (auto errs = activeNS->addTree(ast)) {
|
||||
return errs;
|
||||
}
|
||||
|
||||
auto maybeModule = activeNS->compileToLLVMFromOffset(offset);
|
||||
|
||||
auto tsm = std::move(maybeModule.getValue());
|
||||
tsm.withModuleDo([](llvm::Module &m) { packFunctionArguments(&m); });
|
||||
|
||||
auto *dylib = ctx.getLatestJITDylib(*activeNS);
|
||||
// TODO: Make sure that the data layout of the module is the same as the
|
||||
// engine
|
||||
cantFail(engine->addIRModule(*dylib, std::move(tsm)));
|
||||
return llvm::Error::success();
|
||||
};
|
||||
|
||||
Namespace &Halley::getActiveNS() { return *activeNS; };
|
||||
|
||||
llvm::Expected<std::unique_ptr<Halley>> makeHalleyJIT(SereneContext &ctx) {
|
||||
|
||||
llvm::orc::JITTargetMachineBuilder jtmb(ctx.triple);
|
||||
auto maybeJIT = Halley::make(ctx, std::move(jtmb));
|
||||
if (!maybeJIT) {
|
||||
return maybeJIT.takeError();
|
||||
}
|
||||
|
||||
return maybeJIT;
|
||||
};
|
||||
} // namespace jit
|
||||
} // namespace serene
|
|
@ -1,162 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/jit/layers.h"
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/exprs/fn.h"
|
||||
#include "serene/exprs/traits.h"
|
||||
|
||||
#include <llvm/ExecutionEngine/Orc/ThreadSafeModule.h>
|
||||
#include <llvm/Support/Error.h> // for report_fatal_error
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace serene::jit {
|
||||
|
||||
llvm::orc::ThreadSafeModule compileAst(Namespace &ns, exprs::Ast &ast) {
|
||||
|
||||
assert(ns.getTree().size() < ast.size() && "Did you add the ast to the NS?");
|
||||
|
||||
LAYER_LOG("Compile in context of namespace: " + ns.name);
|
||||
unsigned offset = ns.getTree().size() - ast.size();
|
||||
|
||||
auto maybeModule = ns.compileToLLVMFromOffset(offset);
|
||||
|
||||
if (!maybeModule) {
|
||||
// TODO: Handle failure
|
||||
llvm::report_fatal_error("Couldn't compile lazily JIT'd function");
|
||||
}
|
||||
|
||||
return std::move(maybeModule.getValue());
|
||||
};
|
||||
|
||||
AstMaterializationUnit::AstMaterializationUnit(Namespace &ns, AstLayer &l,
|
||||
exprs::Ast &ast)
|
||||
: orc::MaterializationUnit(l.getInterface(ns, ast)), ns(ns), astLayer(l),
|
||||
ast(ast){};
|
||||
|
||||
void AstMaterializationUnit::materialize(
|
||||
std::unique_ptr<orc::MaterializationResponsibility> r) {
|
||||
astLayer.emit(std::move(r), ns, ast);
|
||||
}
|
||||
|
||||
orc::MaterializationUnit::Interface AstLayer::getInterface(Namespace &ns,
|
||||
exprs::Ast &e) {
|
||||
orc::SymbolFlagsMap Symbols;
|
||||
auto symList = ns.getSymList();
|
||||
unsigned index = symList.size();
|
||||
|
||||
// This probably will change symList
|
||||
auto err = ns.addTree(e);
|
||||
|
||||
if (err) {
|
||||
// TODO: Fix this by a call to diag engine or return the err
|
||||
llvm::outs() << "Fixme: semantic err\n";
|
||||
return orc::MaterializationUnit::Interface(std::move(Symbols), nullptr);
|
||||
}
|
||||
|
||||
auto &env = ns.getRootEnv();
|
||||
auto populateTableFn = [&env, this, &Symbols](auto name) {
|
||||
auto flags = llvm::JITSymbolFlags::Exported;
|
||||
auto maybeExpr = env.lookup(name.str());
|
||||
|
||||
if (!maybeExpr) {
|
||||
LAYER_LOG("Skiping '" + name + "' symbol");
|
||||
return;
|
||||
}
|
||||
|
||||
auto expr = maybeExpr.getValue();
|
||||
|
||||
if (expr->getType() == exprs::ExprType::Fn) {
|
||||
flags = flags | llvm::JITSymbolFlags::Callable;
|
||||
}
|
||||
|
||||
auto mangledSym = this->mangler(name);
|
||||
LAYER_LOG("Mangle symbol for: " + name + " = " << mangledSym);
|
||||
Symbols[mangledSym] = llvm::JITSymbolFlags(flags);
|
||||
};
|
||||
|
||||
std::for_each(symList.begin() + index, symList.end(), populateTableFn);
|
||||
return orc::MaterializationUnit::Interface(std::move(Symbols), nullptr);
|
||||
}
|
||||
|
||||
/// NS Layer ==================================================================
|
||||
|
||||
llvm::orc::ThreadSafeModule compileNS(Namespace &ns) {
|
||||
LAYER_LOG("Compile namespace: " + ns.name);
|
||||
|
||||
auto maybeModule = ns.compileToLLVM();
|
||||
|
||||
if (!maybeModule) {
|
||||
// TODO: Handle failure
|
||||
llvm::report_fatal_error("Couldn't compile lazily JIT'd function");
|
||||
}
|
||||
|
||||
return std::move(maybeModule.getValue());
|
||||
};
|
||||
|
||||
NSMaterializationUnit::NSMaterializationUnit(NSLayer &l, Namespace &ns)
|
||||
: MaterializationUnit(l.getInterface(ns)), nsLayer(l), ns(ns){};
|
||||
|
||||
void NSMaterializationUnit::materialize(
|
||||
std::unique_ptr<orc::MaterializationResponsibility> r) {
|
||||
nsLayer.emit(std::move(r), ns);
|
||||
}
|
||||
|
||||
llvm::Error NSLayer::add(orc::ResourceTrackerSP &rt, llvm::StringRef nsname,
|
||||
reader::LocationRange &loc) {
|
||||
|
||||
LAYER_LOG("Add namespace: " + nsname);
|
||||
auto maybeNS = ctx.readNamespace(nsname.str(), loc);
|
||||
|
||||
if (!maybeNS) {
|
||||
// TODO: Fix this by making Serene errors compatible with llvm::Error
|
||||
auto err = maybeNS.takeError();
|
||||
return err;
|
||||
}
|
||||
|
||||
auto ns = *maybeNS;
|
||||
|
||||
LAYER_LOG("Add the materialize unit for: " + nsname);
|
||||
return rt->getJITDylib().define(
|
||||
std::make_unique<NSMaterializationUnit>(*this, *ns), rt);
|
||||
}
|
||||
|
||||
orc::MaterializationUnit::Interface
|
||||
NSLayer::getInterface(serene::Namespace &ns) {
|
||||
orc::SymbolFlagsMap Symbols;
|
||||
|
||||
for (auto &k : ns.getRootEnv()) {
|
||||
auto flags = llvm::JITSymbolFlags::Exported;
|
||||
auto name = k.getFirst();
|
||||
auto expr = k.getSecond();
|
||||
|
||||
if (expr->getType() == exprs::ExprType::Fn) {
|
||||
flags = flags | llvm::JITSymbolFlags::Callable;
|
||||
}
|
||||
|
||||
auto mangledSym = mangler(name);
|
||||
LAYER_LOG("Mangle symbol for: " + name + " = " << mangledSym);
|
||||
Symbols[mangledSym] = llvm::JITSymbolFlags(flags);
|
||||
}
|
||||
|
||||
return orc::MaterializationUnit::Interface(std::move(Symbols), nullptr);
|
||||
}
|
||||
|
||||
} // namespace serene::jit
|
|
@ -1,224 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "serene/namespace.h"
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/export.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/llvm/IR/Value.h"
|
||||
#include "serene/semantics.h"
|
||||
#include "serene/slir/slir.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <mlir/IR/Builders.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/IR/Verifier.h>
|
||||
#include <mlir/Support/LogicalResult.h>
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace llvm;
|
||||
|
||||
namespace serene {
|
||||
|
||||
Namespace::Namespace(SereneContext &ctx, llvm::StringRef ns_name,
|
||||
std::optional<llvm::StringRef> filename)
|
||||
: ctx(ctx), name(ns_name) {
|
||||
if (filename.hasValue()) {
|
||||
this->filename.emplace(filename.getValue().str());
|
||||
}
|
||||
|
||||
// Create the root environment
|
||||
createEnv(nullptr);
|
||||
};
|
||||
|
||||
SemanticEnv &Namespace::createEnv(SemanticEnv *parent) {
|
||||
auto env = std::make_unique<SemanticEnv>(parent);
|
||||
environments.push_back(std::move(env));
|
||||
|
||||
return *environments.back();
|
||||
};
|
||||
|
||||
SemanticEnv &Namespace::getRootEnv() {
|
||||
assert(!environments.empty() && "Root env is not created!");
|
||||
|
||||
return *environments.front();
|
||||
};
|
||||
|
||||
mlir::LogicalResult Namespace::define(std::string &name, exprs::Node &node) {
|
||||
auto &rootEnv = getRootEnv();
|
||||
|
||||
if (failed(rootEnv.insert_symbol(name, node))) {
|
||||
return mlir::failure();
|
||||
}
|
||||
|
||||
symbolList.push_back(name);
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
exprs::Ast &Namespace::getTree() { return this->tree; }
|
||||
|
||||
llvm::Error Namespace::addTree(exprs::Ast &ast) {
|
||||
|
||||
// If the target phase is just parsing we don't want
|
||||
// to run the semantic analyzer or anything beyond parser
|
||||
if (ctx.getTargetPhase() == CompilationPhase::Parse) {
|
||||
// we just want the raw AST
|
||||
this->tree.insert(this->tree.end(), ast.begin(), ast.end());
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
auto &rootEnv = getRootEnv();
|
||||
|
||||
auto state = semantics::makeAnalysisState(*this, rootEnv);
|
||||
// Run the semantic analyer on the ast and then if everything
|
||||
// is ok add the form to the tree and forms
|
||||
auto maybeForm = semantics::analyze(*state, ast);
|
||||
|
||||
if (!maybeForm) {
|
||||
return maybeForm.takeError();
|
||||
}
|
||||
|
||||
auto semanticAst = std::move(*maybeForm);
|
||||
this->tree.insert(this->tree.end(), semanticAst.begin(), semanticAst.end());
|
||||
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
uint Namespace::nextFnCounter() { return fn_counter++; };
|
||||
|
||||
SereneContext &Namespace::getContext() { return this->ctx; };
|
||||
|
||||
MaybeModuleOp Namespace::generate(unsigned offset) {
|
||||
// The reason why we return an optional value instead of Errors
|
||||
// is the way MLIR's diagnostic engine works. Passes may use
|
||||
// the `emit` function of operations to report errors to the
|
||||
// diagnostic engine. So we can't return any error diractly.
|
||||
|
||||
mlir::OpBuilder builder(&ctx.mlirContext);
|
||||
|
||||
// TODO: Fix the unknown location by pointing to the `ns` form
|
||||
auto module = mlir::ModuleOp::create(builder.getUnknownLoc(),
|
||||
std::optional<llvm::StringRef>(name));
|
||||
|
||||
auto treeSize = getTree().size();
|
||||
|
||||
// Walk the AST and call the `generateIR` function of each node.
|
||||
// Since nodes will have access to the a reference of the
|
||||
// namespace they can use the builder and keep adding more
|
||||
// operations to the module via the builder
|
||||
for (unsigned i = offset; i < treeSize; ++i) {
|
||||
auto &node = getTree()[i];
|
||||
node->generateIR(*this, module);
|
||||
}
|
||||
|
||||
if (mlir::failed(mlir::verify(module))) {
|
||||
module.emitError("Can't verify the module");
|
||||
module.erase();
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
if (mlir::failed(runPasses(module))) {
|
||||
// TODO: Report a proper error
|
||||
module.emitError("Failure in passes!");
|
||||
module.erase();
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
return MaybeModuleOp(module);
|
||||
}
|
||||
|
||||
mlir::LogicalResult Namespace::runPasses(mlir::ModuleOp &m) {
|
||||
return ctx.pm.run(m);
|
||||
};
|
||||
|
||||
void Namespace::dump() {
|
||||
llvm::outs() << "\nMLIR: \n";
|
||||
auto maybeModuleOp = generate();
|
||||
|
||||
if (!maybeModuleOp) {
|
||||
|
||||
llvm::errs() << "Failed to generate the IR.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
mlir::OpPrintingFlags flags;
|
||||
flags.enableDebugInfo();
|
||||
|
||||
maybeModuleOp.getValue()->print(llvm::outs(), flags);
|
||||
};
|
||||
|
||||
MaybeModule Namespace::compileToLLVM() {
|
||||
// The reason why we return an optional value instead of Errors
|
||||
// is the way MLIR's diagnostic engine works. Passes may use
|
||||
// the `emit` function of operations to report errors to the
|
||||
// diagnostic engine. So we can't return any error diractly.
|
||||
|
||||
auto maybeModule = generate();
|
||||
|
||||
if (!maybeModule) {
|
||||
NAMESPACE_LOG("IR generation failed for '" << name << "'");
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
if (ctx.getTargetPhase() >= CompilationPhase::IR) {
|
||||
mlir::ModuleOp module = maybeModule.getValue().get();
|
||||
return ::serene::slir::compileToLLVMIR(ctx, module);
|
||||
}
|
||||
|
||||
return llvm::None;
|
||||
};
|
||||
|
||||
MaybeModule Namespace::compileToLLVMFromOffset(unsigned offset) {
|
||||
// The reason why we return an optional value instead of Errors
|
||||
// is the way MLIR's diagnostic engine works. Passes may use
|
||||
// the `emit` function of operations to report errors to the
|
||||
// diagnostic engine. So we can't return any error diractly.
|
||||
|
||||
auto maybeModule = generate(offset);
|
||||
|
||||
if (!maybeModule) {
|
||||
NAMESPACE_LOG("IR generation failed for '" << name << "'");
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
if (ctx.getTargetPhase() >= CompilationPhase::IR) {
|
||||
mlir::ModuleOp module = maybeModule.getValue().get();
|
||||
return ::serene::slir::compileToLLVMIR(ctx, module);
|
||||
}
|
||||
|
||||
return llvm::None;
|
||||
};
|
||||
|
||||
NSPtr Namespace::make(SereneContext &ctx, llvm::StringRef name,
|
||||
std::optional<llvm::StringRef> filename) {
|
||||
return std::make_shared<Namespace>(ctx, name, filename);
|
||||
};
|
||||
|
||||
Namespace::~Namespace() {
|
||||
// TODO: Clean up anything related to this namespace in the context
|
||||
// TODO: Remove anything related to this namespace in the JIT
|
||||
NAMESPACE_LOG("Destructing NS: " << name);
|
||||
};
|
||||
|
||||
} // namespace serene
|
|
@ -1,761 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/conventions.h"
|
||||
#include "serene/diagnostics.h"
|
||||
#include "serene/passes.h"
|
||||
#include "serene/slir/dialect.h"
|
||||
#include "serene/slir/ops.h"
|
||||
#include "serene/slir/type_converter.h"
|
||||
#include "serene/slir/types.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <serene/config.h>
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
#include <llvm/Support/ErrorHandling.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <llvm/Support/thread.h>
|
||||
#include <mlir/Dialect/Arithmetic/IR/Arithmetic.h>
|
||||
#include <mlir/Dialect/Func/IR/FuncOps.h>
|
||||
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
|
||||
#include <mlir/Dialect/LLVMIR/LLVMTypes.h>
|
||||
#include <mlir/Dialect/MemRef/IR/MemRef.h>
|
||||
#include <mlir/IR/Attributes.h>
|
||||
#include <mlir/IR/BuiltinAttributes.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/IR/BuiltinTypes.h>
|
||||
#include <mlir/IR/Matchers.h>
|
||||
#include <mlir/IR/OperationSupport.h>
|
||||
#include <mlir/Pass/Pass.h>
|
||||
#include <mlir/Support/LLVM.h>
|
||||
#include <mlir/Support/LogicalResult.h>
|
||||
#include <mlir/Transforms/DialectConversion.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ll = mlir::LLVM;
|
||||
|
||||
namespace serene::passes {
|
||||
|
||||
// static ll::GlobalOp getOrCreateInternalString(mlir::Location loc,
|
||||
// mlir::OpBuilder &builder,
|
||||
// llvm::StringRef name,
|
||||
// llvm::StringRef value,
|
||||
// mlir::ModuleOp module) {
|
||||
|
||||
// // Create the global at the entry of the module.
|
||||
// ll::GlobalOp global;
|
||||
|
||||
// if (!(global = module.lookupSymbol<ll::GlobalOp>(name))) {
|
||||
// mlir::OpBuilder::InsertionGuard insertGuard(builder);
|
||||
// builder.setInsertionPointToStart(module.getBody());
|
||||
|
||||
// auto type = ll::LLVMArrayType::get(
|
||||
// mlir::IntegerType::get(builder.getContext(), I8_SIZE), value.size());
|
||||
// // TODO: Do we want link once ?
|
||||
// global = builder.create<ll::GlobalOp>(loc, type, /*isConstant=*/true,
|
||||
// ll::Linkage::Linkonce, name,
|
||||
// builder.getStringAttr(value),
|
||||
// /*alignment=*/0);
|
||||
// }
|
||||
|
||||
// return global;
|
||||
// };
|
||||
|
||||
// static mlir::Value getPtrToInternalString(mlir::OpBuilder &builder,
|
||||
// ll::GlobalOp global) {
|
||||
// auto loc = global.getLoc();
|
||||
// auto I8 = mlir::IntegerType::get(builder.getContext(), I8_SIZE);
|
||||
// // Get the pointer to the first character in the global string.
|
||||
// mlir::Value globalPtr = builder.create<ll::AddressOfOp>(loc, global);
|
||||
// mlir::Value cst0 = builder.create<ll::ConstantOp>(
|
||||
// loc, mlir::IntegerType::get(builder.getContext(), I64_SIZE),
|
||||
// builder.getIntegerAttr(builder.getIndexType(), 0));
|
||||
|
||||
// return builder.create<ll::GEPOp>(loc, ll::LLVMPointerType::get(I8),
|
||||
// globalPtr,
|
||||
// llvm::ArrayRef<mlir::Value>({cst0}));
|
||||
// };
|
||||
|
||||
// static ll::GlobalOp getOrCreateString(mlir::Location loc,
|
||||
// mlir::OpBuilder &builder,
|
||||
// llvm::StringRef name,
|
||||
// llvm::StringRef value, uint32_t len,
|
||||
// mlir::ModuleOp module) {
|
||||
// auto *ctx = builder.getContext();
|
||||
// ll::GlobalOp global;
|
||||
|
||||
// if (!(global = module.lookupSymbol<ll::GlobalOp>(name))) {
|
||||
|
||||
// mlir::OpBuilder::InsertionGuard insertGuard(builder);
|
||||
// builder.setInsertionPointToStart(module.getBody());
|
||||
|
||||
// mlir::Attribute initValue{};
|
||||
// auto type = slir::getStringTypeinLLVM(*ctx);
|
||||
|
||||
// global = builder.create<ll::GlobalOp>(
|
||||
// loc, type, /*isConstant=*/true, ll::Linkage::Linkonce, name,
|
||||
// initValue);
|
||||
|
||||
// auto &gr = global.getInitializerRegion();
|
||||
// auto *block = builder.createBlock(&gr);
|
||||
|
||||
// if (block == nullptr) {
|
||||
// module.emitError("Faild to create block of the globalOp!");
|
||||
// // TODO: change the return type to Expected<GlobalOp> and return
|
||||
// // an error here
|
||||
// }
|
||||
|
||||
// builder.setInsertionPoint(block, block->begin());
|
||||
|
||||
// mlir::Value structInstant = builder.create<ll::UndefOp>(loc, type);
|
||||
|
||||
// auto strOp = getOrCreateInternalString(loc, builder, name, value,
|
||||
// module); auto ptrToStr = getPtrToInternalString(builder, strOp);
|
||||
|
||||
// auto length = builder.create<ll::ConstantOp>(
|
||||
// loc, mlir::IntegerType::get(ctx, I32_SIZE),
|
||||
// builder.getI32IntegerAttr(len));
|
||||
|
||||
// // Setting the string pointer field
|
||||
// structInstant = builder.create<ll::InsertValueOp>(
|
||||
// loc, structInstant.getType(), structInstant, ptrToStr,
|
||||
// builder.getI64ArrayAttr(0));
|
||||
|
||||
// // Setting the len field
|
||||
// structInstant = builder.create<ll::InsertValueOp>(
|
||||
// loc, structInstant.getType(), structInstant, length,
|
||||
// builder.getI64ArrayAttr(1));
|
||||
|
||||
// builder.create<ll::ReturnOp>(loc, structInstant);
|
||||
// }
|
||||
|
||||
// return global;
|
||||
// };
|
||||
|
||||
// static ll::GlobalOp getOrCreateSymbol(mlir::Location loc,
|
||||
// mlir::OpBuilder &builder,
|
||||
// llvm::StringRef ns, llvm::StringRef
|
||||
// name, mlir::ModuleOp module) {
|
||||
// std::string fqName;
|
||||
// ll::GlobalOp global;
|
||||
|
||||
// auto *ctx = builder.getContext();
|
||||
// auto symName = serene::mangleInternalSymName(fqName);
|
||||
|
||||
// makeFQSymbolName(ns, name, fqName);
|
||||
|
||||
// if (!(global = module.lookupSymbol<ll::GlobalOp>(symName))) {
|
||||
// mlir::OpBuilder::InsertionGuard insertGuard(builder);
|
||||
// builder.setInsertionPointToStart(module.getBody());
|
||||
|
||||
// mlir::Attribute initValue{};
|
||||
// auto type = slir::getSymbolTypeinLLVM(*ctx);
|
||||
|
||||
// // We want to allow merging the strings representing the ns or name part
|
||||
// // of the symbol with other modules to unify them.
|
||||
// ll::Linkage linkage = ll::Linkage::Linkonce;
|
||||
|
||||
// global = builder.create<ll::GlobalOp>(loc, type, /*isConstant=*/true,
|
||||
// linkage, symName, initValue);
|
||||
|
||||
// auto &gr = global.getInitializerRegion();
|
||||
// auto *block = builder.createBlock(&gr);
|
||||
|
||||
// if (block == nullptr) {
|
||||
// module.emitError("Faild to create block of the globalOp!");
|
||||
// // TODO: change the return type to Expected<GlobalOp> and return
|
||||
// // an error here
|
||||
// }
|
||||
|
||||
// builder.setInsertionPoint(block, block->begin());
|
||||
|
||||
// mlir::Value structInstant = builder.create<ll::UndefOp>(loc, type);
|
||||
|
||||
// // We want to use the mangled ns as the name of the constant that
|
||||
// // holds the ns string
|
||||
// auto mangledNSName = serene::mangleInternalStringName(ns);
|
||||
// // The globalop that we want to use for the ns field
|
||||
// auto nsField =
|
||||
// getOrCreateString(loc, builder, mangledNSName, ns, ns.size(),
|
||||
// module);
|
||||
// auto ptrToNs = builder.create<ll::AddressOfOp>(loc, nsField);
|
||||
|
||||
// // We want to use the mangled 'name' as the name of the constant that
|
||||
// // holds the 'name' string
|
||||
// auto mangledName = serene::mangleInternalStringName(name);
|
||||
// // The global op to use as the 'name' field
|
||||
// auto nameField =
|
||||
// getOrCreateString(loc, builder, mangledName, name, name.size(),
|
||||
// module);
|
||||
// auto ptrToName = builder.create<ll::AddressOfOp>(loc, nameField);
|
||||
|
||||
// // Setting the string pointer field
|
||||
// structInstant = builder.create<ll::InsertValueOp>(
|
||||
// loc, structInstant.getType(), structInstant, ptrToNs,
|
||||
// builder.getI64ArrayAttr(0));
|
||||
|
||||
// // Setting the len field
|
||||
// structInstant = builder.create<ll::InsertValueOp>(
|
||||
// loc, structInstant.getType(), structInstant, ptrToName,
|
||||
// builder.getI64ArrayAttr(0));
|
||||
|
||||
// builder.create<ll::ReturnOp>(loc, structInstant);
|
||||
// }
|
||||
|
||||
// return global;
|
||||
// };
|
||||
|
||||
// static ll::GlobalOp getOrCreateSymbol(mlir::Location loc,
|
||||
// mlir::OpBuilder &builder, mlir::Value
|
||||
// ns, mlir::Value name, mlir::ModuleOp
|
||||
// module) {
|
||||
|
||||
// assert(!ns.getType().isa<slir::StringType>() &&
|
||||
// !ns.getType().isa<slir::StringType>() &&
|
||||
// "TypeError: ns and name has to be strings");
|
||||
|
||||
// std::string fqName;
|
||||
// ll::GlobalOp global;
|
||||
|
||||
// auto *ctx = builder.getContext();
|
||||
// auto symName = serene::mangleInternalSymName(fqName);
|
||||
|
||||
// makeFQSymbolName(ns, name, fqName);
|
||||
|
||||
// if (!(global = module.lookupSymbol<ll::GlobalOp>(symName))) {
|
||||
// mlir::OpBuilder::InsertionGuard insertGuard(builder);
|
||||
// builder.setInsertionPointToStart(module.getBody());
|
||||
|
||||
// mlir::Attribute initValue{};
|
||||
// auto type = slir::getSymbolTypeinLLVM(*ctx);
|
||||
|
||||
// // We want to allow merging the strings representing the ns or name part
|
||||
// // of the symbol with other modules to unify them.
|
||||
// ll::Linkage linkage = ll::Linkage::Linkonce;
|
||||
|
||||
// global = builder.create<ll::GlobalOp>(loc, type, /*isConstant=*/true,
|
||||
// linkage, symName, initValue);
|
||||
|
||||
// auto &gr = global.getInitializerRegion();
|
||||
// auto *block = builder.createBlock(&gr);
|
||||
|
||||
// if (block == nullptr) {
|
||||
// module.emitError("Faild to create block of the globalOp!");
|
||||
// // TODO: change the return type to Expected<GlobalOp> and return
|
||||
// // an error here
|
||||
// }
|
||||
|
||||
// builder.setInsertionPoint(block, block->begin());
|
||||
|
||||
// mlir::Value structInstant = builder.create<ll::UndefOp>(loc, type);
|
||||
|
||||
// // We want to use the mangled ns as the name of the constant that
|
||||
// // holds the ns string
|
||||
// auto mangledNSName = serene::mangleInternalStringName(ns);
|
||||
// // The globalop that we want to use for the ns field
|
||||
// auto nsField =
|
||||
// getOrCreateString(loc, builder, mangledNSName, ns, ns.size(),
|
||||
// module);
|
||||
// auto ptrToNs = builder.create<ll::AddressOfOp>(loc, nsField);
|
||||
|
||||
// // We want to use the mangled 'name' as the name of the constant that
|
||||
// // holds the 'name' string
|
||||
// auto mangledName = serene::mangleInternalStringName(name);
|
||||
// // The global op to use as the 'name' field
|
||||
// auto nameField =
|
||||
// getOrCreateString(loc, builder, mangledName, name, name.size(),
|
||||
// module);
|
||||
// auto ptrToName = builder.create<ll::AddressOfOp>(loc, nameField);
|
||||
|
||||
// // Setting the string pointer field
|
||||
// structInstant = builder.create<ll::InsertValueOp>(
|
||||
// loc, structInstant.getType(), structInstant, ptrToNs,
|
||||
// builder.getI64ArrayAttr(0));
|
||||
|
||||
// // Setting the len field
|
||||
// structInstant = builder.create<ll::InsertValueOp>(
|
||||
// loc, structInstant.getType(), structInstant, ptrToName,
|
||||
// builder.getI64ArrayAttr(0));
|
||||
|
||||
// builder.create<ll::ReturnOp>(loc, structInstant);
|
||||
// }
|
||||
|
||||
// return global;
|
||||
// };
|
||||
|
||||
// struct LowerIntern : public mlir::OpConversionPattern<slir::InternOp> {
|
||||
// using OpConversionPattern<slir::InternOp>::OpConversionPattern;
|
||||
|
||||
// mlir::LogicalResult
|
||||
// matchAndRewrite(serene::slir::InternOp op, OpAdaptor adaptor,
|
||||
// mlir::ConversionPatternRewriter &rewriter) const override;
|
||||
// };
|
||||
|
||||
// mlir::LogicalResult
|
||||
// LowerIntern::matchAndRewrite(serene::slir::InternOp op, OpAdaptor adaptor,
|
||||
// mlir::ConversionPatternRewriter &rewriter) const
|
||||
// {
|
||||
|
||||
// UNUSED(adaptor);
|
||||
// auto ns = op.ns();
|
||||
// auto name = op.name();
|
||||
// auto loc = op.getLoc();
|
||||
// auto module = op->getParentOfType<mlir::ModuleOp>();
|
||||
|
||||
// // If there is no use for the result of this op then simply erase it
|
||||
// if (op.getResult().use_empty()) {
|
||||
// rewriter.eraseOp(op);
|
||||
// return mlir::success();
|
||||
// }
|
||||
|
||||
// auto global = getOrCreateSymbol(loc, rewriter, ns, name, module);
|
||||
// auto ptr = rewriter.create<ll::AddressOfOp>(loc, global);
|
||||
|
||||
// rewriter.replaceOp(op, ptr.getResult());
|
||||
|
||||
// return mlir::success();
|
||||
// }
|
||||
|
||||
// struct LowerSymbol : public mlir::OpConversionPattern<slir::SymbolOp> {
|
||||
// using OpConversionPattern<slir::SymbolOp>::OpConversionPattern;
|
||||
|
||||
// mlir::LogicalResult
|
||||
// matchAndRewrite(serene::slir::SymbolOp op, OpAdaptor adaptor,
|
||||
// mlir::ConversionPatternRewriter &rewriter) const override;
|
||||
// };
|
||||
|
||||
// mlir::LogicalResult
|
||||
// LowerSymbol::matchAndRewrite(serene::slir::SymbolOp op, OpAdaptor adaptor,
|
||||
// mlir::ConversionPatternRewriter &rewriter) const
|
||||
// {
|
||||
|
||||
// UNUSED(adaptor);
|
||||
// auto ns = op.ns();
|
||||
// auto name = op.name();
|
||||
// auto loc = op.getLoc();
|
||||
// auto module = op->getParentOfType<mlir::ModuleOp>();
|
||||
|
||||
// // If there is no use for the result of this op then simply erase it
|
||||
// if (op.getResult().use_empty()) {
|
||||
// rewriter.eraseOp(op);
|
||||
// return mlir::success();
|
||||
// }
|
||||
|
||||
// auto global = getOrCreateSymbol(loc, rewriter, ns, name, module);
|
||||
// auto ptr = rewriter.create<ll::AddressOfOp>(loc, global);
|
||||
|
||||
// rewriter.replaceOp(op, ptr.getResult());
|
||||
|
||||
// return mlir::success();
|
||||
// }
|
||||
|
||||
struct LowerDefine : public mlir::OpConversionPattern<slir::DefineOp> {
|
||||
using OpConversionPattern<slir::DefineOp>::OpConversionPattern;
|
||||
|
||||
mlir::LogicalResult
|
||||
matchAndRewrite(serene::slir::DefineOp op, OpAdaptor adaptor,
|
||||
mlir::ConversionPatternRewriter &rewriter) const override;
|
||||
};
|
||||
|
||||
mlir::LogicalResult
|
||||
LowerDefine::matchAndRewrite(serene::slir::DefineOp op, OpAdaptor adaptor,
|
||||
mlir::ConversionPatternRewriter &rewriter) const {
|
||||
|
||||
(void)rewriter;
|
||||
(void)adaptor;
|
||||
|
||||
auto value = op.value();
|
||||
auto *valueop = value.getDefiningOp();
|
||||
auto maybeTopLevel = op.is_top_level();
|
||||
bool isTopLevel = false;
|
||||
|
||||
if (maybeTopLevel) {
|
||||
isTopLevel = *maybeTopLevel;
|
||||
}
|
||||
|
||||
// If the value than we bind a name to is a constant, rewrite to
|
||||
// `define_constant`
|
||||
|
||||
// TODO: Replace the isConstantLike with a `hasTrait` call
|
||||
if (mlir::detail::isConstantLike(valueop)) {
|
||||
|
||||
mlir::Attribute constantValue;
|
||||
if (!mlir::matchPattern(value, mlir::m_Constant(&constantValue))) {
|
||||
PASS_LOG(
|
||||
"Failure: The constant like op don't have a constant attribute.");
|
||||
return mlir::failure();
|
||||
}
|
||||
|
||||
rewriter.replaceOpWithNewOp<slir::DefineConstantOp>(
|
||||
op, op.sym_name(), constantValue, rewriter.getBoolAttr(isTopLevel),
|
||||
op.sym_visibilityAttr());
|
||||
|
||||
if (valueop->use_empty()) {
|
||||
PASS_LOG("Erase op due to empty use:" << valueop);
|
||||
rewriter.eraseOp(valueop);
|
||||
}
|
||||
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
// If the value was a Function literal (like an anonymous function)
|
||||
// rewrite to a Func.FuncOp
|
||||
if (mlir::isa<slir::FnOp>(valueop)) {
|
||||
// TODO: Lower to a function op
|
||||
rewriter.eraseOp(op);
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
// TODO: [lib] If we're building an executable `linkonce` is a good choice
|
||||
// but for a library we need to choose a better link type
|
||||
ll::Linkage linkage = ll::Linkage::Linkonce;
|
||||
auto loc = op.getLoc();
|
||||
auto moduleOp = op->getParentOfType<mlir::ModuleOp>();
|
||||
auto ns = moduleOp.getNameAttr();
|
||||
auto name = op.getName();
|
||||
|
||||
mlir::Attribute initAttr{};
|
||||
std::string fqsym;
|
||||
|
||||
makeFQSymbolName(ns.getValue(), name, fqsym);
|
||||
|
||||
if (!isTopLevel) {
|
||||
auto llvmType = typeConverter->convertType(value.getType());
|
||||
|
||||
{
|
||||
mlir::PatternRewriter::InsertionGuard insertGuard(rewriter);
|
||||
rewriter.setInsertionPointToStart(moduleOp.getBody());
|
||||
|
||||
auto globalOp = rewriter.create<ll::GlobalOp>(loc, llvmType,
|
||||
/*isConstant=*/false,
|
||||
linkage, fqsym, initAttr);
|
||||
auto &gr = globalOp.getInitializerRegion();
|
||||
auto *block = rewriter.createBlock(&gr);
|
||||
|
||||
if (block == nullptr) {
|
||||
op.emitError("Faild to create block of the globalOp!");
|
||||
return mlir::failure();
|
||||
}
|
||||
|
||||
rewriter.setInsertionPointToStart(block);
|
||||
|
||||
auto undef = rewriter.create<ll::UndefOp>(loc, llvmType);
|
||||
rewriter.create<ll::ReturnOp>(loc, undef.getResult());
|
||||
}
|
||||
|
||||
rewriter.setInsertionPointAfter(op);
|
||||
|
||||
auto symRef = mlir::SymbolRefAttr::get(rewriter.getContext(), fqsym);
|
||||
// auto llvmValue = typeConverter->materializeTargetConversion(
|
||||
// rewriter, loc, llvmType, value);
|
||||
|
||||
// llvm::outs() << ">>> " << symRef << "|" << llvmValue << "|" << op <<
|
||||
// "\n";
|
||||
rewriter.replaceOpWithNewOp<slir::SetValueOp>(op, symRef, value);
|
||||
// auto setvalOp = rewriter.create<slir::SetValueOp>(loc, symRef,
|
||||
// llvmValue); rewriter.insert(setvalOp); rewriter.eraseOp(op);
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
// auto globop = rewriter.create<ll::GlobalOp>(loc, value.getType(),
|
||||
// /*isConstant=*/false,
|
||||
// linkage, fqsym,
|
||||
// initAttr);
|
||||
// auto &gr = globop.getInitializerRegion();
|
||||
// auto *block = rewriter.createBlock(&gr);
|
||||
|
||||
// block->addArgument(value.getType(), value.getLoc());
|
||||
|
||||
// rewriter.setInsertionPoint(block, block->begin());
|
||||
|
||||
// rewriter.create<ll::ReturnOp>(value.getLoc(),
|
||||
// adaptor.getOperands());
|
||||
|
||||
// if (!op.getResult().use_empty()) {
|
||||
// auto symValue = rewriter.create<slir::SymbolOp>(loc, ns, name);
|
||||
|
||||
// rewriter.replaceOp(op, symValue.getResult());
|
||||
// }
|
||||
|
||||
rewriter.eraseOp(op);
|
||||
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
struct LowerDefineConstant
|
||||
: public mlir::OpConversionPattern<slir::DefineConstantOp> {
|
||||
using OpConversionPattern<slir::DefineConstantOp>::OpConversionPattern;
|
||||
|
||||
mlir::LogicalResult
|
||||
matchAndRewrite(serene::slir::DefineConstantOp op, OpAdaptor adaptor,
|
||||
mlir::ConversionPatternRewriter &rewriter) const override;
|
||||
};
|
||||
|
||||
mlir::LogicalResult LowerDefineConstant::matchAndRewrite(
|
||||
serene::slir::DefineConstantOp op, OpAdaptor adaptor,
|
||||
mlir::ConversionPatternRewriter &rewriter) const {
|
||||
|
||||
(void)rewriter;
|
||||
(void)adaptor;
|
||||
|
||||
auto value = op.value();
|
||||
auto name = op.getName();
|
||||
auto loc = op.getLoc();
|
||||
auto moduleOp = op->getParentOfType<mlir::ModuleOp>();
|
||||
auto ns = moduleOp.getNameAttr();
|
||||
std::string fqsym;
|
||||
|
||||
makeFQSymbolName(ns.getValue(), name, fqsym);
|
||||
|
||||
// TODO: [lib] If we're building an executable `linkonce` is a good choice
|
||||
// but for a library we need to choose a better link type
|
||||
ll::Linkage linkage = ll::Linkage::Linkonce;
|
||||
|
||||
// TODO: use ll::ConstantOp instead
|
||||
UNUSED(rewriter.create<ll::GlobalOp>(loc, value.getType(),
|
||||
/*isConstant=*/true, linkage, fqsym,
|
||||
value));
|
||||
|
||||
// if (!op.value().use_empty()) {
|
||||
// auto symValue = rewriter.create<slir::SymbolOp>(loc, ns, name);
|
||||
|
||||
// rewriter.replaceOp(op, symValue.getResult());
|
||||
// }
|
||||
|
||||
rewriter.eraseOp(op);
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
#define GEN_PASS_CLASSES
|
||||
#include "serene/passes/passes.h.inc"
|
||||
|
||||
class LowerSLIR : public LowerSLIRBase<LowerSLIR> {
|
||||
void runOnOperation() override {
|
||||
mlir::ModuleOp module = getOperation();
|
||||
|
||||
// The first thing to define is the conversion target. This will define the
|
||||
// final target for this lowering.
|
||||
mlir::ConversionTarget target(getContext());
|
||||
|
||||
slir::TypeConverter typeConverter(getContext());
|
||||
|
||||
// We define the specific operations, or dialects, that are legal targets
|
||||
// for this lowering. In our case, we are lowering to the `Standard`
|
||||
// dialects.
|
||||
target.addLegalDialect<mlir::func::FuncDialect>();
|
||||
target.addLegalDialect<mlir::arith::ArithmeticDialect>();
|
||||
target.addLegalDialect<ll::LLVMDialect>();
|
||||
|
||||
// We also define the SLIR dialect as Illegal so that the conversion will
|
||||
// fail if any of these operations are *not* converted.
|
||||
target.addIllegalDialect<serene::slir::SereneDialect>();
|
||||
|
||||
// Mark operations that are LEGAL for this pass. It means that we don't
|
||||
// lower them is this pass but we will in another pass. So we don't want to
|
||||
// get an error since we are not lowering them.
|
||||
// target.addLegalOp<serene::slir::PrintOp>();
|
||||
target.addLegalOp<slir::FnOp, slir::ValueOp, slir::SetValueOp>();
|
||||
|
||||
// Now that the conversion target has been defined, we just need to provide
|
||||
// the set of patterns that will lower the SLIR operations.
|
||||
mlir::RewritePatternSet patterns(&getContext());
|
||||
|
||||
// Pattern to lower ValueOp and FnOp
|
||||
// LowerDefineConstant
|
||||
patterns.add<LowerDefine, LowerDefineConstant>(typeConverter,
|
||||
&getContext());
|
||||
|
||||
// With the target and rewrite patterns defined, we can now attempt the
|
||||
// conversion. The conversion will signal failure if any of our `illegal`
|
||||
// operations were not converted successfully.
|
||||
if (failed(applyPartialConversion(module, target, std::move(patterns)))) {
|
||||
signalPassFailure();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<mlir::Pass> createLowerSLIR() {
|
||||
return std::make_unique<LowerSLIR>();
|
||||
}
|
||||
|
||||
#define GEN_PASS_REGISTRATION
|
||||
#include "serene/passes/passes.h.inc"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ValueOp lowering to constant op
|
||||
struct ValueOpLowering : public mlir::OpRewritePattern<serene::slir::Value1Op> {
|
||||
using OpRewritePattern<serene::slir::Value1Op>::OpRewritePattern;
|
||||
|
||||
mlir::LogicalResult
|
||||
matchAndRewrite(serene::slir::Value1Op op,
|
||||
mlir::PatternRewriter &rewriter) const final;
|
||||
};
|
||||
|
||||
mlir::LogicalResult
|
||||
ValueOpLowering::matchAndRewrite(serene::slir::Value1Op op,
|
||||
mlir::PatternRewriter &rewriter) const {
|
||||
auto value = op.value();
|
||||
mlir::Location loc = op.getLoc();
|
||||
|
||||
llvm::SmallVector<mlir::Type, 1> arg_types(0);
|
||||
auto func_type = rewriter.getFunctionType(arg_types, rewriter.getI64Type());
|
||||
// TODO: use a mechanism to generate unique names
|
||||
auto fn = rewriter.create<mlir::func::FuncOp>(loc, "randomname", func_type);
|
||||
|
||||
auto *entryBlock = fn.addEntryBlock();
|
||||
rewriter.setInsertionPointToStart(entryBlock);
|
||||
|
||||
// Since we only support i64 at the moment we use ConstantOp
|
||||
auto retVal = rewriter
|
||||
.create<mlir::arith::ConstantIntOp>(loc, (int64_t)value,
|
||||
rewriter.getI64Type())
|
||||
.getResult();
|
||||
|
||||
UNUSED(rewriter.create<mlir::func::ReturnOp>(loc, retVal));
|
||||
|
||||
fn.setPrivate();
|
||||
|
||||
// Erase the original ValueOP
|
||||
rewriter.eraseOp(op);
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Fn lowering pattern
|
||||
struct FnOpLowering : public mlir::OpRewritePattern<serene::slir::Fn1Op> {
|
||||
using OpRewritePattern<serene::slir::Fn1Op>::OpRewritePattern;
|
||||
|
||||
mlir::LogicalResult
|
||||
matchAndRewrite(serene::slir::Fn1Op op,
|
||||
mlir::PatternRewriter &rewriter) const final;
|
||||
};
|
||||
|
||||
mlir::LogicalResult
|
||||
FnOpLowering::matchAndRewrite(serene::slir::Fn1Op op,
|
||||
mlir::PatternRewriter &rewriter) const {
|
||||
auto args = op.args();
|
||||
auto name = op.name();
|
||||
auto isPublic = op.sym_visibility().getValueOr("public") == "public";
|
||||
mlir::Location loc = op.getLoc();
|
||||
|
||||
llvm::SmallVector<mlir::Type, 4> arg_types;
|
||||
|
||||
for (const auto &arg : args) {
|
||||
auto attr = arg.getValue().dyn_cast<mlir::TypeAttr>();
|
||||
|
||||
if (!attr) {
|
||||
op.emitError("It's not a type attr");
|
||||
return mlir::failure();
|
||||
}
|
||||
arg_types.push_back(attr.getValue());
|
||||
}
|
||||
|
||||
auto func_type = rewriter.getFunctionType(arg_types, rewriter.getI64Type());
|
||||
auto fn = rewriter.create<mlir::func::FuncOp>(loc, name, func_type);
|
||||
|
||||
auto *entryBlock = fn.addEntryBlock();
|
||||
|
||||
rewriter.setInsertionPointToStart(entryBlock);
|
||||
|
||||
auto retVal = rewriter
|
||||
.create<mlir::arith::ConstantIntOp>(loc, (int64_t)3,
|
||||
rewriter.getI64Type())
|
||||
.getResult();
|
||||
|
||||
rewriter.create<mlir::func::ReturnOp>(loc, retVal);
|
||||
|
||||
if (!isPublic) {
|
||||
fn.setPrivate();
|
||||
}
|
||||
|
||||
rewriter.eraseOp(op);
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// SLIR lowering pass
|
||||
// This Pass will lower SLIR to MLIR's standard dialect.
|
||||
struct SLIRToMLIRPass
|
||||
: public mlir::PassWrapper<SLIRToMLIRPass,
|
||||
mlir::OperationPass<mlir::ModuleOp>> {
|
||||
void getDependentDialects(mlir::DialectRegistry ®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<mlir::func::FuncDialect, mlir::arith::ArithmeticDialect>();
|
||||
};
|
||||
|
||||
/// Return the current function being transformed.
|
||||
mlir::ModuleOp SLIRToMLIRPass::getModule() { return this->getOperation(); }
|
||||
|
||||
void SLIRToMLIRPass::runOnOperation() { runOnModule(); }
|
||||
|
||||
void SLIRToMLIRPass::runOnModule() {
|
||||
|
||||
auto module = getModule();
|
||||
|
||||
// The first thing to define is the conversion target. This will define the
|
||||
// final target for this lowering.
|
||||
mlir::ConversionTarget target(getContext());
|
||||
|
||||
// We define the specific operations, or dialects, that are legal targets for
|
||||
// this lowering. In our case, we are lowering to the `Standard` dialects.
|
||||
target.addLegalDialect<mlir::func::FuncDialect>();
|
||||
target.addLegalDialect<mlir::arith::ArithmeticDialect>();
|
||||
|
||||
// We also define the SLIR dialect as Illegal so that the conversion will fail
|
||||
// if any of these operations are *not* converted.
|
||||
target.addIllegalDialect<serene::slir::SereneDialect>();
|
||||
|
||||
// Mark operations that are LEGAL for this pass. It means that we don't lower
|
||||
// them is this pass but we will in another pass. So we don't want to get
|
||||
// an error since we are not lowering them.
|
||||
// target.addLegalOp<serene::slir::PrintOp>();
|
||||
target.addLegalOp<mlir::func::FuncOp>();
|
||||
|
||||
// Now that the conversion target has been defined, we just need to provide
|
||||
// the set of patterns that will lower the SLIR operations.
|
||||
mlir::RewritePatternSet patterns(&getContext());
|
||||
|
||||
// Pattern to lower ValueOp and FnOp
|
||||
patterns.add<ValueOpLowering, FnOpLowering>(&getContext());
|
||||
|
||||
// With the target and rewrite patterns defined, we can now attempt the
|
||||
// conversion. The conversion will signal failure if any of our `illegal`
|
||||
// operations were not converted successfully.
|
||||
if (failed(applyPartialConversion(module, target, std::move(patterns)))) {
|
||||
signalPassFailure();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<mlir::Pass> createSLIRLowerToMLIRPass() {
|
||||
return std::make_unique<SLIRToMLIRPass>();
|
||||
};
|
||||
|
||||
void registerAllPasses() { registerPasses(); }
|
||||
} // namespace serene::passes
|
|
@ -1,87 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/passes.h"
|
||||
#include "serene/slir/dialect.h"
|
||||
|
||||
#include <mlir/Conversion/AffineToStandard/AffineToStandard.h>
|
||||
#include <mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h>
|
||||
#include <mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h>
|
||||
#include <mlir/Conversion/LLVMCommon/ConversionTarget.h>
|
||||
#include <mlir/Conversion/LLVMCommon/TypeConverter.h>
|
||||
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
|
||||
#include <mlir/Dialect/SCF/SCF.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/Pass/Pass.h>
|
||||
#include <mlir/Transforms/DialectConversion.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace serene::passes {
|
||||
struct SLIRToLLVMDialect
|
||||
: public mlir::PassWrapper<SLIRToLLVMDialect,
|
||||
mlir::OperationPass<mlir::ModuleOp>> {
|
||||
void getDependentDialects(mlir::DialectRegistry ®istry) const override {
|
||||
registry.insert<mlir::LLVM::LLVMDialect>();
|
||||
}
|
||||
|
||||
void runOnOperation() final;
|
||||
};
|
||||
|
||||
void SLIRToLLVMDialect::runOnOperation() {
|
||||
// The first thing to define is the conversion target. This will define the
|
||||
// final target for this lowering. For this lowering, we are only targeting
|
||||
// the LLVM dialect.
|
||||
mlir::LLVMConversionTarget target(getContext());
|
||||
target.addLegalOp<mlir::ModuleOp>();
|
||||
|
||||
// During this lowering, we will also be lowering the MemRef types, that are
|
||||
// currently being operated on, to a representation in LLVM. To perform this
|
||||
// conversion we use a TypeConverter as part of the lowering. This converter
|
||||
// details how one type maps to another. This is necessary now that we will be
|
||||
// doing more complicated lowerings, involving loop region arguments.
|
||||
mlir::LLVMTypeConverter typeConverter(&getContext());
|
||||
|
||||
// Now that the conversion target has been defined, we need to provide the
|
||||
// patterns used for lowering. At this point of the compilation process, we
|
||||
// have a combination of `serene`, `affine`, and `std` operations. Luckily,
|
||||
// there are already exists a set of patterns to transform `affine` and `std`
|
||||
// dialects. These patterns lowering in multiple stages, relying on transitive
|
||||
// lowerings. Transitive lowering, or A->B->C lowering, is when multiple
|
||||
// patterns must be applied to fully transform an illegal operation into a
|
||||
// set of legal ones.
|
||||
mlir::RewritePatternSet patterns(&getContext());
|
||||
|
||||
mlir::populateFuncToLLVMConversionPatterns(typeConverter, patterns);
|
||||
mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
|
||||
patterns);
|
||||
// patterns.add<PrintOpLowering>(&getContext());
|
||||
|
||||
// We want to completely lower to LLVM, so we use a `FullConversion`. This
|
||||
// ensures that only legal operations will remain after the conversion.
|
||||
auto module = getOperation();
|
||||
if (failed(applyFullConversion(module, target, std::move(patterns)))) {
|
||||
signalPassFailure();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<mlir::Pass> createSLIRLowerToLLVMDialectPass() {
|
||||
return std::make_unique<SLIRToLLVMDialect>();
|
||||
};
|
||||
|
||||
} // namespace serene::passes
|
|
@ -1,438 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/reader/reader.h"
|
||||
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/list.h"
|
||||
#include "serene/exprs/number.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
#include <llvm/Support/ErrorHandling.h>
|
||||
#include <llvm/Support/ErrorOr.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
#include <llvm/Support/MemoryBuffer.h>
|
||||
#include <llvm/Support/SMLoc.h>
|
||||
#include <mlir/IR/Diagnostics.h>
|
||||
#include <mlir/IR/Location.h>
|
||||
#include <mlir/Support/LogicalResult.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace serene {
|
||||
|
||||
namespace reader {
|
||||
// LocationRange::LocationRange(const LocationRange &loc) {
|
||||
// start = loc.start.clone();
|
||||
// end = loc.end.clone();
|
||||
// }
|
||||
|
||||
/// Return the string represenation of the location.
|
||||
std::string Location::toString() const {
|
||||
return llvm::formatv("{0}:{1}", line, col);
|
||||
};
|
||||
|
||||
Location Location::clone() const {
|
||||
return Location{ns, filename, c, line, col, knownLocation};
|
||||
}
|
||||
|
||||
mlir::Location Location::toMLIRLocation(SereneContext &ctx) {
|
||||
// TODO: Create a new Location attribute that is namespace base
|
||||
if (filename.hasValue()) {
|
||||
return mlir::FileLineColLoc::get(&ctx.mlirContext, filename.getValue(),
|
||||
line, col);
|
||||
}
|
||||
return mlir::FileLineColLoc::get(&ctx.mlirContext, ns, line, col);
|
||||
}
|
||||
/// Increase the given location by one and set the line/col value in respect to
|
||||
/// the `newline` in place.
|
||||
/// \param loc The `Location` data
|
||||
/// \param c A pointer to the current char that the location has to point to
|
||||
void incLocation(Location &loc, const char *c) {
|
||||
// TODO: Handle the end of line with respect to the OS.
|
||||
// increase the current position in the buffer with respect to the end
|
||||
// of line.
|
||||
auto newline = *c == '\n';
|
||||
|
||||
if (!newline) {
|
||||
loc.col++;
|
||||
} else {
|
||||
loc.line++;
|
||||
loc.col = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// decrease the given location by one and set the line/col value in respect to
|
||||
/// the `newline` in place.
|
||||
/// \param loc The `Location` data
|
||||
/// \param c A pointer to the current char that the location has to point to
|
||||
void decLocation(Location &loc, const char *c) {
|
||||
// TODO: Handle the end of line with respect to the OS.
|
||||
// increase the current position in the buffer with respect to the end
|
||||
// of line.
|
||||
auto newline = *c == '\n';
|
||||
|
||||
if (newline) {
|
||||
loc.line = loc.line == 0 ? 0 : loc.line - 1;
|
||||
|
||||
// We don't move back the `col` value because we simply don't know it
|
||||
} else {
|
||||
loc.col = loc.col == 0 ? 0 : loc.col - 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reader::Reader(SereneContext &ctx, llvm::StringRef buffer, llvm::StringRef ns,
|
||||
std::optional<llvm::StringRef> filename)
|
||||
: ctx(ctx), ns(ns), filename(filename), buf(buffer),
|
||||
currentLocation(Location(ns, filename)) {
|
||||
UNUSED(this->ctx);
|
||||
READER_LOG("Setting the first char of the buffer");
|
||||
currentChar = buf.begin() - 1;
|
||||
currentPos = 1;
|
||||
currentLocation.line = 1;
|
||||
currentLocation.col = 1;
|
||||
};
|
||||
|
||||
Reader::Reader(SereneContext &ctx, llvm::MemoryBufferRef buffer,
|
||||
llvm::StringRef ns, std::optional<llvm::StringRef> filename)
|
||||
: Reader(ctx, buffer.getBuffer(), ns, filename){};
|
||||
|
||||
Reader::~Reader() { READER_LOG("Destroying the reader"); }
|
||||
|
||||
void Reader::advanceByOne() {
|
||||
currentChar++;
|
||||
currentPos++;
|
||||
currentLocation.col++;
|
||||
|
||||
if (*currentChar == '\n') {
|
||||
READER_LOG("Detected end of line");
|
||||
|
||||
if (readEOL) {
|
||||
currentLocation.col = 1;
|
||||
currentLocation.line++;
|
||||
}
|
||||
|
||||
readEOL = true;
|
||||
} else {
|
||||
if (readEOL) {
|
||||
currentLocation.line++;
|
||||
currentLocation.col = 1;
|
||||
}
|
||||
readEOL = false;
|
||||
}
|
||||
|
||||
READER_LOG("Moving to Char: " << *currentChar << " at location: "
|
||||
<< currentLocation.toString());
|
||||
};
|
||||
void Reader::advance(bool skipWhitespace) {
|
||||
if (skipWhitespace) {
|
||||
for (;;) {
|
||||
const auto *next = currentChar + 1;
|
||||
|
||||
if (isspace(*next) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
advanceByOne();
|
||||
}
|
||||
} else {
|
||||
advanceByOne();
|
||||
}
|
||||
};
|
||||
|
||||
const char *Reader::nextChar(bool skipWhitespace, unsigned count) {
|
||||
if (!skipWhitespace) {
|
||||
READER_LOG("Next char: " << *(currentChar + count));
|
||||
return currentChar + count;
|
||||
}
|
||||
|
||||
const auto *c = currentChar + 1;
|
||||
while (isspace(*c) != 0) {
|
||||
c++;
|
||||
};
|
||||
|
||||
READER_LOG("Next char: " << *c);
|
||||
return c;
|
||||
};
|
||||
|
||||
bool Reader::isEndOfBuffer(const char *c) {
|
||||
return *c == '\0' || currentPos > buf.size() || ((const int)*c == EOF);
|
||||
};
|
||||
|
||||
Location Reader::getCurrentLocation() { return currentLocation.clone(); };
|
||||
|
||||
/// A predicate function indicating whether the given char `c` is a valid
|
||||
/// char for the starting point of a symbol or not.
|
||||
bool Reader::isValidForIdentifier(char c) {
|
||||
switch (c) {
|
||||
case '!':
|
||||
case '$':
|
||||
case '%':
|
||||
case '&':
|
||||
case '*':
|
||||
case '+':
|
||||
case '-':
|
||||
case '.':
|
||||
case '~':
|
||||
case '/':
|
||||
case ':':
|
||||
case '<':
|
||||
case '=':
|
||||
case '>':
|
||||
case '?':
|
||||
case '@':
|
||||
case '^':
|
||||
case '_':
|
||||
return true;
|
||||
}
|
||||
|
||||
return std::isalnum(c) != 0;
|
||||
}
|
||||
|
||||
/// Reads a number,
|
||||
/// \param neg whether to read a negative number or not.
|
||||
exprs::MaybeNode Reader::readNumber(bool neg) {
|
||||
READER_LOG("Reading a number...");
|
||||
std::string number(neg ? "-" : "");
|
||||
bool floatNum = false;
|
||||
bool empty = false;
|
||||
|
||||
const auto *c = nextChar();
|
||||
advance();
|
||||
|
||||
LocationRange loc(getCurrentLocation());
|
||||
|
||||
if (isdigit(*c) == 0) {
|
||||
return errors::makeError(ctx, errors::InvalidDigitForNumber, loc);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
number += *c;
|
||||
c = nextChar(false);
|
||||
empty = false;
|
||||
|
||||
if ((isdigit(*c) != 0) || *c == '.') {
|
||||
if (*c == '.' && floatNum) {
|
||||
loc = LocationRange(getCurrentLocation());
|
||||
return errors::makeError(ctx, errors::TwoFloatPoints, loc);
|
||||
}
|
||||
|
||||
if (*c == '.') {
|
||||
floatNum = true;
|
||||
}
|
||||
|
||||
advance();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (((std::isalpha(*c) != 0) && !empty) || empty) {
|
||||
advance();
|
||||
loc.start = getCurrentLocation();
|
||||
return errors::makeError(ctx, errors::InvalidDigitForNumber, loc);
|
||||
}
|
||||
|
||||
loc.end = getCurrentLocation();
|
||||
return exprs::make<exprs::Number>(loc, number, neg, floatNum);
|
||||
};
|
||||
|
||||
/// Reads a symbol. If the symbol looks like a number
|
||||
/// If reads it as number
|
||||
exprs::MaybeNode Reader::readSymbol() {
|
||||
READER_LOG("Reading a symbol...");
|
||||
LocationRange loc;
|
||||
const auto *c = nextChar();
|
||||
|
||||
if (!this->isValidForIdentifier(*c) || isEndOfBuffer(c) ||
|
||||
(isspace(*c) != 0)) {
|
||||
advance();
|
||||
loc = LocationRange(getCurrentLocation());
|
||||
std::string msg;
|
||||
|
||||
if (*c == ')') {
|
||||
msg = "An extra ')' is detected.";
|
||||
}
|
||||
|
||||
return errors::makeError(ctx, errors::InvalidCharacterForSymbol, loc, msg);
|
||||
}
|
||||
|
||||
if (*c == '-') {
|
||||
const auto *next = nextChar(false, 2);
|
||||
if (isdigit(*next) != 0) {
|
||||
// Swallow the -
|
||||
advance();
|
||||
return readNumber(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (isdigit(*c) != 0) {
|
||||
return readNumber(false);
|
||||
}
|
||||
|
||||
std::string sym;
|
||||
advance();
|
||||
|
||||
for (;;) {
|
||||
sym += *c;
|
||||
c = nextChar();
|
||||
|
||||
if (!isEndOfBuffer(c) &&
|
||||
((((isspace(*c)) == 0) && this->isValidForIdentifier(*c)))) {
|
||||
advance();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Make sure that the symbol has 0 or 1 '/'.
|
||||
|
||||
// TODO: Make sure that `/` is not at the start or at the end of the symbol
|
||||
|
||||
loc.end = getCurrentLocation();
|
||||
return exprs::makeSuccessfulNode<exprs::Symbol>(loc, sym, this->ns);
|
||||
};
|
||||
|
||||
/// Reads a list recursively
|
||||
exprs::MaybeNode Reader::readList() {
|
||||
READER_LOG("Reading a list...");
|
||||
|
||||
const auto *c = nextChar();
|
||||
advance();
|
||||
|
||||
auto list = exprs::makeAndCast<exprs::List>(getCurrentLocation());
|
||||
|
||||
// TODO: Replace the assert with an actual check.
|
||||
assert(*c == '(');
|
||||
|
||||
bool list_terminated = false;
|
||||
|
||||
do {
|
||||
const auto *c = nextChar(true);
|
||||
|
||||
if (isEndOfBuffer(c)) {
|
||||
advance(true);
|
||||
advance();
|
||||
list->location.end = getCurrentLocation();
|
||||
return errors::makeError(ctx, errors::EOFWhileScaningAList,
|
||||
list->location);
|
||||
}
|
||||
|
||||
switch (*c) {
|
||||
case ')':
|
||||
advance(true);
|
||||
advance();
|
||||
list_terminated = true;
|
||||
list->location.end = getCurrentLocation();
|
||||
break;
|
||||
|
||||
default:
|
||||
advance(true);
|
||||
auto expr = readExpr();
|
||||
if (!expr) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
list->append(*expr);
|
||||
}
|
||||
|
||||
} while (!list_terminated);
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
/// Reads an expression by dispatching to the proper reader function.
|
||||
exprs::MaybeNode Reader::readExpr() {
|
||||
const auto *c = nextChar(true);
|
||||
|
||||
READER_LOG("Read char at `readExpr`: " << *c);
|
||||
|
||||
if (isEndOfBuffer(c)) {
|
||||
return exprs::EmptyNode;
|
||||
}
|
||||
|
||||
switch (*c) {
|
||||
case '(': {
|
||||
advance(true);
|
||||
return readList();
|
||||
}
|
||||
|
||||
default:
|
||||
advance(true);
|
||||
return readSymbol();
|
||||
}
|
||||
};
|
||||
|
||||
/// Reads all the expressions in the reader's buffer as an AST.
|
||||
/// Each expression type (from the reader perspective) has a
|
||||
/// reader function.
|
||||
exprs::MaybeAst Reader::read() {
|
||||
|
||||
for (size_t current_pos = 0; current_pos < buf.size();) {
|
||||
const auto *c = nextChar(true);
|
||||
|
||||
if (isEndOfBuffer(c)) {
|
||||
break;
|
||||
}
|
||||
|
||||
advance(true);
|
||||
|
||||
auto tmp = readExpr();
|
||||
|
||||
if (tmp) {
|
||||
if (*tmp == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
this->ast.push_back(std::move(*tmp));
|
||||
|
||||
} else {
|
||||
return tmp.takeError();
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(this->ast);
|
||||
};
|
||||
|
||||
exprs::MaybeAst read(SereneContext &ctx, const llvm::StringRef input,
|
||||
llvm::StringRef ns,
|
||||
std::optional<llvm::StringRef> filename) {
|
||||
reader::Reader r(ctx, input, ns, filename);
|
||||
auto ast = r.read();
|
||||
return ast;
|
||||
}
|
||||
|
||||
exprs::MaybeAst read(SereneContext &ctx, const llvm::MemoryBufferRef input,
|
||||
llvm::StringRef ns,
|
||||
std::optional<llvm::StringRef> filename) {
|
||||
reader::Reader r(ctx, input, ns, filename);
|
||||
|
||||
auto ast = r.read();
|
||||
return ast;
|
||||
}
|
||||
} // namespace reader
|
||||
} // namespace serene
|
|
@ -1,71 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/semantics.h"
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/namespace.h"
|
||||
|
||||
#include <llvm/Support/Error.h>
|
||||
|
||||
namespace serene::semantics {
|
||||
|
||||
std::unique_ptr<AnalysisState> AnalysisState::moveToNewEnv() {
|
||||
auto &newEnv = ns.createEnv(&env);
|
||||
return makeAnalysisState(ns, newEnv);
|
||||
};
|
||||
|
||||
AnalyzeResult analyze(AnalysisState &state, exprs::Ast &forms) {
|
||||
llvm::Error errors = llvm::Error::success();
|
||||
exprs::Ast ast;
|
||||
|
||||
for (auto &element : forms) {
|
||||
auto maybeNode = element->analyze(state);
|
||||
|
||||
// Is it a `success` result
|
||||
if (maybeNode) {
|
||||
auto &node = *maybeNode;
|
||||
|
||||
if (node) {
|
||||
// is there a new node to replace the current node ?
|
||||
ast.push_back(node);
|
||||
} else {
|
||||
// Analyze returned a `nullptr`. No rewrite is needed.
|
||||
// Use the current element instead.
|
||||
ast.push_back(element);
|
||||
}
|
||||
} else {
|
||||
|
||||
// `analyze` returned an errorful result. This type of error
|
||||
// is llvm related and has to be raised later
|
||||
// (std::move());
|
||||
auto err = maybeNode.takeError();
|
||||
errors = llvm::joinErrors(std::move(errors), std::move(err));
|
||||
}
|
||||
}
|
||||
|
||||
// If the errors (which is an ErrorList) contains error and is
|
||||
// not succssful
|
||||
if (!errors) {
|
||||
return std::move(ast);
|
||||
}
|
||||
|
||||
return std::move(errors);
|
||||
};
|
||||
}; // namespace serene::semantics
|
|
@ -1,157 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/serene.h"
|
||||
|
||||
#include "serene/diagnostics.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
|
||||
// TODO: Remove it
|
||||
#include "serene/exprs/number.h"
|
||||
#include "serene/jit/halley.h"
|
||||
#include "serene/reader/reader.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/ADT/None.h>
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/CommandLine.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
namespace serene {
|
||||
using exprs::Number;
|
||||
|
||||
void initCompiler() {
|
||||
llvm::InitializeAllTargetInfos();
|
||||
llvm::InitializeAllTargets();
|
||||
llvm::InitializeAllTargetMCs();
|
||||
llvm::InitializeAllAsmParsers();
|
||||
llvm::InitializeAllAsmPrinters();
|
||||
};
|
||||
|
||||
// CLI Option ----------------
|
||||
|
||||
/// All the global CLI option ar defined here. If you need to add a new global
|
||||
/// option
|
||||
/// make sure that you are handling it in `applySereneCLOptions` too.
|
||||
struct SereneOptions {
|
||||
|
||||
llvm::cl::OptionCategory clOptionsCategory{"Discovery options"};
|
||||
|
||||
llvm::cl::list<std::string> loadPaths{
|
||||
"l", llvm::cl::desc("The load path to use for compilation."),
|
||||
llvm::cl::ZeroOrMore, llvm::cl::MiscFlags::PositionalEatsArgs,
|
||||
llvm::cl::cat(clOptionsCategory)};
|
||||
|
||||
llvm::cl::list<std::string> sharedLibraryPaths{
|
||||
"sl", llvm::cl::desc("Where to find shared libraries"),
|
||||
llvm::cl::ZeroOrMore, llvm::cl::MiscFlags::PositionalEatsArgs,
|
||||
llvm::cl::cat(clOptionsCategory)};
|
||||
};
|
||||
|
||||
static llvm::ManagedStatic<SereneOptions> options;
|
||||
|
||||
void registerSereneCLOptions() {
|
||||
// Make sure that the options struct has been constructed.
|
||||
*options;
|
||||
|
||||
#ifdef SERENE_WITH_MLIR_CL_OPTION
|
||||
// mlir::registerAsmPrinterCLOptions();
|
||||
mlir::registerMLIRContextCLOptions();
|
||||
mlir::registerPassManagerCLOptions();
|
||||
#endif
|
||||
}
|
||||
|
||||
void applySereneCLOptions(SereneContext &ctx) {
|
||||
if (!options.isConstructed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.sourceManager.setLoadPaths(options->loadPaths);
|
||||
|
||||
#ifdef SERENE_WITH_MLIR_CL_OPTION
|
||||
mlir::applyPassManagerCLOptions(ctx.pm);
|
||||
#endif
|
||||
}
|
||||
|
||||
SERENE_EXPORT exprs::MaybeAst read(SereneContext &ctx, std::string &input) {
|
||||
auto ¤tNS = ctx.getCurrentNS();
|
||||
auto filename =
|
||||
!currentNS.filename.hasValue()
|
||||
? llvm::None
|
||||
: std::optional<llvm::StringRef>(currentNS.filename.getValue());
|
||||
|
||||
return reader::read(ctx, input, currentNS.name, filename);
|
||||
};
|
||||
|
||||
SERENE_EXPORT exprs::MaybeNode eval(SereneContext &ctx, exprs::Ast &input) {
|
||||
|
||||
auto loc = reader::LocationRange::UnknownLocation("nsname");
|
||||
|
||||
// auto ns = ctx.importNamespace("docs.examples.hello_world", loc);
|
||||
|
||||
// if (!ns) {
|
||||
// auto es = ns.getError();
|
||||
// auto nsloadErr = errors::makeError(loc, errors::NSLoadError);
|
||||
// es.push_back(nsloadErr);
|
||||
// return exprs::MaybeNode::error(es);
|
||||
// }
|
||||
|
||||
auto errs = ctx.jit->addAST(input);
|
||||
if (errs) {
|
||||
return errs;
|
||||
}
|
||||
|
||||
// auto e = input[0];
|
||||
// auto *sym = llvm::dyn_cast<exprs::Symbol>(e.get());
|
||||
|
||||
// if (sym == nullptr) {
|
||||
// return exprs::makeErrorNode(e->location, errors::UnknownError, "only
|
||||
// sym");
|
||||
// }
|
||||
|
||||
// llvm::outs() << "Read: " << sym->toString() << "\n";
|
||||
|
||||
// // Get the anonymous expression's JITSymbol.
|
||||
// auto symptr = ctx.jit->lookup(*sym);
|
||||
// if (!symptr) {
|
||||
// return exprs::MaybeNode::error(symptr.getError());
|
||||
// }
|
||||
|
||||
llvm::outs() << "eval here\n";
|
||||
|
||||
// sym((void **)3);
|
||||
|
||||
// err = ctx.jit->addAst(input);
|
||||
// if (err) {
|
||||
// llvm::errs() << err;
|
||||
// auto e = errors::makeErrorTree(loc, errors::NSLoadError);
|
||||
|
||||
// return exprs::makeErrorNode(loc, errors::NSLoadError);
|
||||
// }
|
||||
return exprs::make<exprs::Number>(loc, "4", false, false);
|
||||
};
|
||||
|
||||
SERENE_EXPORT void print(SereneContext &ctx, const exprs::Ast &input,
|
||||
std::string &result) {
|
||||
UNUSED(ctx);
|
||||
result = exprs::astToString(&input);
|
||||
};
|
||||
|
||||
} // namespace serene
|
|
@ -1,48 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/slir/dialect.h"
|
||||
|
||||
#include "serene/slir/dialect.cpp.inc"
|
||||
#include "serene/slir/ops.h"
|
||||
#include "serene/slir/types.h"
|
||||
|
||||
#include <mlir/IR/Builders.h>
|
||||
#include <mlir/IR/Dialect.h>
|
||||
#include <mlir/IR/DialectImplementation.h>
|
||||
#include <mlir/IR/MLIRContext.h>
|
||||
|
||||
namespace serene {
|
||||
namespace slir {
|
||||
|
||||
/// Dialect initialization, the instance will be owned by the context. This is
|
||||
/// the point of registration of types and operations for the dialect.
|
||||
void SereneDialect::initialize() {
|
||||
registerType();
|
||||
addOperations<
|
||||
#define GET_OP_LIST
|
||||
#include "serene/slir/ops.cpp.inc"
|
||||
>();
|
||||
}
|
||||
|
||||
void registerTo(mlir::DialectRegistry ®istry) {
|
||||
registry.insert<serene::slir::SereneDialect>();
|
||||
};
|
||||
|
||||
} // namespace slir
|
||||
} // namespace serene
|
|
@ -1,172 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/slir/generatable.h"
|
||||
|
||||
#include "serene/slir/dialect.h"
|
||||
|
||||
#include <llvm/ADT/STLExtras.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
#include <mlir/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <mlir/ExecutionEngine/OptUtils.h>
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
#include <mlir/Support/LogicalResult.h>
|
||||
#include <mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h>
|
||||
#include <mlir/Target/LLVMIR/ModuleTranslation.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace serene {
|
||||
namespace slir {
|
||||
|
||||
// mlir::Operation *Generatable::generate(exprs::Expression *x) {
|
||||
// switch (x->getType()) {
|
||||
// case SereneType::Number: {
|
||||
// return generate(llvm::cast<Number>(x));
|
||||
// }
|
||||
|
||||
// case SereneType::List: {
|
||||
// generate(llvm::cast<List>(x));
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
// default: {
|
||||
// return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)3);
|
||||
// }
|
||||
// }
|
||||
//};
|
||||
|
||||
// mlir::Value Generator::generate(exprs::List *l) {
|
||||
// auto first = l->at(0);
|
||||
|
||||
// if (!first) {
|
||||
// // Empty list.
|
||||
// // TODO: Return Nil or empty list.
|
||||
|
||||
// // Just for now.
|
||||
// return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)0);
|
||||
// }
|
||||
|
||||
// if (first->get()->getType() == SereneType::Symbol) {
|
||||
// auto fnNameSymbol = llvm::dyn_cast<Symbol>(first->get());
|
||||
|
||||
// if (fnNameSymbol->getName() == "fn") {
|
||||
// if (l->count() <= 3) {
|
||||
// module.emitError("'fn' form needs exactly 2 arguments.");
|
||||
// }
|
||||
|
||||
// auto args = llvm::dyn_cast<List>(l->at(1).getValue().get());
|
||||
// auto body = llvm::dyn_cast<List>(l->from(2).get());
|
||||
|
||||
// if (!args) {
|
||||
// module.emitError("The first element of 'def' has to be a symbol.");
|
||||
// }
|
||||
|
||||
// // Create a new anonymous function and push it to the anonymous
|
||||
// functions
|
||||
// // map, later on we
|
||||
// auto loc(fnNameSymbol->location->start);
|
||||
// auto anonymousName = fmt::format("__fn_{}__", anonymousFnCounter);
|
||||
// anonymousFnCounter++;
|
||||
|
||||
// auto fn = generateFn(loc, anonymousName, args, body);
|
||||
// mlir::Identifier fnid = builder.getIdentifier(anonymousName);
|
||||
// anonymousFunctions.insert({fnid, fn});
|
||||
// return builder.create<FnIdOp>(builder.getUnknownLoc(), fnid.str());
|
||||
// }
|
||||
// }
|
||||
// // auto rest = l->from(1);
|
||||
// // auto loc = toMLIRLocation(&first->get()->location->start);
|
||||
// // for (auto x : *rest) {
|
||||
// // generate(x.get());
|
||||
// // }
|
||||
// return builder.create<ValueOp>(builder.getUnknownLoc(), (uint64_t)100);
|
||||
//};
|
||||
|
||||
// mlir::FuncOp Generator::generateFn(serene::reader::Location loc,
|
||||
// std::string name, List *args, List *body)
|
||||
// {
|
||||
|
||||
// auto location = toMLIRLocation(&loc);
|
||||
// llvm::SmallVector<mlir::Type, 4> arg_types(args->count(),
|
||||
// builder.getI64Type());
|
||||
// auto func_type = builder.getFunctionType(arg_types, builder.getI64Type());
|
||||
// auto proto = mlir::FuncOp::create(location, name, func_type);
|
||||
// mlir::FuncOp fn(proto);
|
||||
|
||||
// if (!fn) {
|
||||
// module.emitError("Can not create the function.");
|
||||
// }
|
||||
|
||||
// auto &entryBlock = *fn.addEntryBlock();
|
||||
// llvm::ScopedHashTableScope<llvm::StringRef, mlir::Value>
|
||||
// scope(symbolTable);
|
||||
|
||||
// // Declare all the function arguments in the symbol table.
|
||||
// for (const auto arg :
|
||||
// llvm::zip(args->asArrayRef(), entryBlock.getArguments())) {
|
||||
|
||||
// auto argSymbol = llvm::dyn_cast<Symbol>(std::get<0>(arg).get());
|
||||
// if (!argSymbol) {
|
||||
// module.emitError("Function parameters must be symbols");
|
||||
// }
|
||||
// if (symbolTable.count(argSymbol->getName())) {
|
||||
// return nullptr;
|
||||
// }
|
||||
// symbolTable.insert(argSymbol->getName(), std::get<1>(arg));
|
||||
// }
|
||||
|
||||
// // Set the insertion point in the builder to the beginning of the function
|
||||
// // body, it will be used throughout the codegen to create operations in
|
||||
// this
|
||||
// // function.
|
||||
// builder.setInsertionPointToStart(&entryBlock);
|
||||
|
||||
// // Emit the body of the function.
|
||||
// if (!generate(body)) {
|
||||
// fn.erase();
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
// // // Implicitly return void if no return statement was emitted.
|
||||
// // // FIXME: we may fix the parser instead to always return the last
|
||||
// // expression
|
||||
// // // (this would possibly help the REPL case later)
|
||||
// // func::ReturnOp returnOp;
|
||||
|
||||
// // if (!entryBlock.empty())
|
||||
// // returnOp = dyn_cast<func::ReturnOp>(entryBlock.back());
|
||||
// // if (!returnOp) {
|
||||
// // builder.create<func::ReturnOp>(loc(funcAST.getProto()->loc()));
|
||||
// // } else if (returnOp.hasOperand()) {
|
||||
// // // Otherwise, if this return operation has an operand then add a
|
||||
// result
|
||||
// // to
|
||||
// // // the function.
|
||||
// // function.setType(builder.getFunctionType(function.getType().getInputs(),
|
||||
// // getType(VarType{})));
|
||||
// // }
|
||||
|
||||
// return fn;
|
||||
// }
|
||||
} // namespace slir
|
||||
|
||||
} // namespace serene
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/slir/ops.h"
|
||||
|
||||
#include "serene/slir/dialect.h"
|
||||
#include "serene/slir/types.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
#include <mlir/IR/Attributes.h>
|
||||
#include <mlir/IR/Builders.h>
|
||||
#include <mlir/IR/BuiltinAttributes.h>
|
||||
#include <mlir/IR/OperationSupport.h>
|
||||
|
||||
#define GET_OP_CLASSES
|
||||
#include "serene/slir/ops.cpp.inc"
|
||||
|
||||
namespace serene::slir {
|
||||
|
||||
mlir::DataLayoutSpecInterface NsOp::getDataLayoutSpec() {
|
||||
// Take the first and only (if present) attribute that implements the
|
||||
// interface. This needs a linear search, but is called only once per data
|
||||
// layout object construction that is used for repeated queries.
|
||||
for (mlir::NamedAttribute attr : getOperation()->getAttrs()) {
|
||||
if (auto spec = attr.getValue().dyn_cast<mlir::DataLayoutSpecInterface>()) {
|
||||
return spec;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
mlir::OpFoldResult SymbolOp::fold(llvm::ArrayRef<mlir::Attribute> operands) {
|
||||
UNUSED(operands);
|
||||
return value();
|
||||
};
|
||||
|
||||
mlir::OpFoldResult ValueOp::fold(llvm::ArrayRef<mlir::Attribute> operands) {
|
||||
UNUSED(operands);
|
||||
return value();
|
||||
};
|
||||
} // namespace serene::slir
|
|
@ -1,65 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/slir/slir.h"
|
||||
|
||||
namespace serene {
|
||||
namespace slir {
|
||||
|
||||
std::optional<llvm::orc::ThreadSafeModule>
|
||||
compileToLLVMIR(serene::SereneContext &ctx, mlir::ModuleOp &module) {
|
||||
|
||||
auto llvmContext = serene::SereneContext::genLLVMContext();
|
||||
// Register the translation to LLVM IR with the MLIR context.
|
||||
mlir::registerLLVMDialectTranslation(*module.getContext());
|
||||
|
||||
// Convert the module to LLVM IR in a new LLVM IR context.
|
||||
auto llvmModule = mlir::translateModuleToLLVMIR(module, *llvmContext);
|
||||
|
||||
if (!llvmModule) {
|
||||
// TODO: Return a Result type instead
|
||||
module.emitError("Failed to emit LLVM IR\n");
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
// Initialize LLVM targets.
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
|
||||
// TODO: replace this call with our own version of setupTargetTriple
|
||||
mlir::ExecutionEngine::setupTargetTriple(llvmModule.get());
|
||||
|
||||
/// Optionally run an optimization pipeline over the llvm module.
|
||||
auto optPipeline = mlir::makeOptimizingTransformer(
|
||||
/*optLevel=*/ctx.getOptimizatioLevel(), /*sizeLevel=*/0,
|
||||
/*targetMachine=*/nullptr);
|
||||
|
||||
if (auto err = optPipeline(llvmModule.get())) {
|
||||
// TODO: Return a proper error
|
||||
module.emitError("Failed to optimize LLVM IR\n");
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
auto tsm = llvm::orc::ThreadSafeModule(std::move(llvmModule),
|
||||
std::move(llvmContext));
|
||||
return tsm;
|
||||
};
|
||||
|
||||
} // namespace slir
|
||||
|
||||
} // namespace serene
|
|
@ -1,86 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/slir/type_converter.h"
|
||||
|
||||
#include "serene/slir/dialect.h"
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <mlir/Dialect/LLVMIR/LLVMTypes.h>
|
||||
|
||||
namespace ll = mlir::LLVM;
|
||||
|
||||
namespace serene {
|
||||
namespace slir {
|
||||
|
||||
mlir::Type getStringTypeinLLVM(mlir::MLIRContext &ctx) {
|
||||
auto stringStruct =
|
||||
ll::LLVMStructType::getIdentified(&ctx, "serene.core.string");
|
||||
|
||||
mlir::SmallVector<mlir::Type, 4> subtypes;
|
||||
|
||||
subtypes.push_back(
|
||||
ll::LLVMPointerType::get(mlir::IntegerType::get(&ctx, I8_SIZE)));
|
||||
// Length field
|
||||
subtypes.push_back(mlir::IntegerType::get(&ctx, I32_SIZE));
|
||||
(void)stringStruct.setBody(subtypes, false);
|
||||
|
||||
return stringStruct;
|
||||
};
|
||||
|
||||
mlir::Type getSymbolTypeinLLVM(mlir::MLIRContext &ctx) {
|
||||
auto symbolStruct =
|
||||
ll::LLVMStructType::getIdentified(&ctx, "serene.core.symbol");
|
||||
|
||||
auto strType = getStringTypeinLLVM(ctx);
|
||||
auto strPtr = ll::LLVMPointerType::get(strType);
|
||||
|
||||
llvm::SmallVector<mlir::Type, 2> strings{strPtr, strPtr};
|
||||
|
||||
// We discard the result becasue if the body was already set it means
|
||||
// that we're ok and the struct already exists
|
||||
(void)symbolStruct.setBody(strings, false);
|
||||
return symbolStruct;
|
||||
};
|
||||
|
||||
mlir::Type getPtrTypeinLLVM(mlir::MLIRContext &ctx, PtrType p) {
|
||||
UNUSED(ctx);
|
||||
auto T = p.getPointeeType();
|
||||
auto llvmPtr = ll::LLVMPointerType::get(T);
|
||||
return llvmPtr;
|
||||
}
|
||||
|
||||
TypeConverter::ConverterFn TypeConverter::convertSereneTypes() {
|
||||
return [&](mlir::Type type) -> MaybeType {
|
||||
if (type.isa<StringType>()) {
|
||||
return getStringTypeinLLVM(ctx);
|
||||
}
|
||||
|
||||
if (type.isa<SymbolType>()) {
|
||||
return getSymbolTypeinLLVM(ctx);
|
||||
}
|
||||
|
||||
if (type.isa<PtrType>()) {
|
||||
return getPtrTypeinLLVM(ctx, type.cast<PtrType>());
|
||||
}
|
||||
|
||||
return llvm::None;
|
||||
};
|
||||
}
|
||||
} // namespace slir
|
||||
} // namespace serene
|
|
@ -1,52 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/slir/types.h"
|
||||
|
||||
#include "serene/slir/dialect.h"
|
||||
|
||||
#define GET_ATTRDEF_CLASSES
|
||||
#include "serene/slir/attrs.cpp.inc"
|
||||
|
||||
#define GET_TYPEDEF_CLASSES
|
||||
#include "serene/slir/types.cpp.inc"
|
||||
|
||||
namespace serene::slir {
|
||||
|
||||
PtrType PtrType::get(mlir::MLIRContext *context, unsigned addressSpace) {
|
||||
return Base::get(context, mlir::Type(), addressSpace);
|
||||
}
|
||||
|
||||
PtrType PtrType::get(mlir::Type pointee, unsigned addressSpace) {
|
||||
return Base::get(pointee.getContext(), pointee, addressSpace);
|
||||
}
|
||||
|
||||
bool PtrType::isOpaque() const { return !getImpl()->pointeeType; }
|
||||
|
||||
void SereneDialect::registerType() {
|
||||
addAttributes<
|
||||
#define GET_ATTRDEF_LIST
|
||||
#include "serene/slir/attrs.cpp.inc"
|
||||
>();
|
||||
addTypes<
|
||||
#define GET_TYPEDEF_LIST
|
||||
#include "serene/slir/types.cpp.inc"
|
||||
>();
|
||||
};
|
||||
|
||||
} // namespace serene::slir
|
|
@ -1,36 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/reader/location.h"
|
||||
|
||||
#include <mlir/IR/BuiltinOps.h>
|
||||
|
||||
namespace serene::slir {
|
||||
::mlir::Location toMLIRLocation(serene::Namespace &ns,
|
||||
serene::reader::Location &loc) {
|
||||
mlir::OpBuilder builder(&ns.getContext().mlirContext);
|
||||
auto file = ns.filename;
|
||||
std::string filename{file.getValueOr("REPL")};
|
||||
|
||||
return mlir::FileLineColLoc::get(builder.getStringAttr(filename), loc.line,
|
||||
loc.col);
|
||||
}
|
||||
|
||||
} // namespace serene::slir
|
|
@ -1,28 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "serene/slir/dialect.h"
|
||||
#include "serene/slir/slir.h"
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
#include <mlir/IR/Builders.h>
|
||||
#include <mlir/IR/MLIRContext.h>
|
||||
#include <mlir/IR/Value.h>
|
||||
|
||||
namespace serene {
|
||||
namespace slir {} // namespace slir
|
||||
} // namespace serene
|
|
@ -1,225 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/source_mgr.h"
|
||||
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/reader/reader.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#include <llvm/Support/Error.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
#include <llvm/Support/Locale.h>
|
||||
#include <llvm/Support/MemoryBufferRef.h>
|
||||
#include <llvm/Support/Path.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <mlir/Support/LogicalResult.h>
|
||||
|
||||
namespace serene {
|
||||
|
||||
std::string SourceMgr::convertNamespaceToPath(std::string ns_name) {
|
||||
std::replace(ns_name.begin(), ns_name.end(), '.', '/');
|
||||
|
||||
llvm::SmallString<MAX_PATH_SLOTS> path;
|
||||
path.append(ns_name);
|
||||
llvm::sys::path::native(path);
|
||||
|
||||
return std::string(path);
|
||||
};
|
||||
|
||||
bool SourceMgr::isValidBufferID(unsigned i) const {
|
||||
return i != 0 && i <= buffers.size();
|
||||
};
|
||||
|
||||
SourceMgr::MemBufPtr SourceMgr::findFileInLoadPath(const std::string &name,
|
||||
std::string &importedFile) {
|
||||
|
||||
auto path = convertNamespaceToPath(name);
|
||||
|
||||
// If the file didn't exist directly, see if it's in an include path.
|
||||
for (unsigned i = 0, e = loadPaths.size(); i != e; ++i) {
|
||||
|
||||
// TODO: Ugh, Udgly, fix this using llvm::sys::path functions
|
||||
importedFile = loadPaths[i] + llvm::sys::path::get_separator().data() +
|
||||
path + "." + DEFAULT_SUFFIX;
|
||||
|
||||
SMGR_LOG("Try to load the ns from: " + importedFile);
|
||||
auto newBufOrErr = llvm::MemoryBuffer::getFile(importedFile);
|
||||
|
||||
if (auto err = newBufOrErr.getError()) {
|
||||
llvm::consumeError(llvm::errorCodeToError(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
return std::move(*newBufOrErr);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
MaybeNS SourceMgr::readNamespace(SereneContext &ctx, std::string name,
|
||||
reader::LocationRange importLoc) {
|
||||
std::string importedFile;
|
||||
|
||||
SMGR_LOG("Attempt to load namespace: " + name);
|
||||
MemBufPtr newBufOrErr(findFileInLoadPath(name, importedFile));
|
||||
|
||||
if (newBufOrErr == nullptr) {
|
||||
auto msg = llvm::formatv("Couldn't find namespace '{0}'", name).str();
|
||||
return errors::makeError(ctx, errors::NSLoadError, importLoc, msg);
|
||||
}
|
||||
|
||||
auto bufferId = AddNewSourceBuffer(std::move(newBufOrErr), importLoc);
|
||||
|
||||
UNUSED(nsTable.insert_or_assign(name, bufferId));
|
||||
|
||||
if (bufferId == 0) {
|
||||
auto msg = llvm::formatv("Couldn't add namespace '{0}'", name).str();
|
||||
return errors::makeError(ctx, errors::NSAddToSMError, importLoc, msg);
|
||||
}
|
||||
|
||||
// Since we moved the buffer to be added as the source storage we
|
||||
// need to get a pointer to it again
|
||||
const auto *buf = getMemoryBuffer(bufferId);
|
||||
|
||||
// Read the content of the buffer by passing it the reader
|
||||
auto maybeAst = reader::read(ctx, buf->getBuffer(), name,
|
||||
std::optional(llvm::StringRef(importedFile)));
|
||||
|
||||
if (!maybeAst) {
|
||||
SMGR_LOG("Couldn't Read namespace: " + name);
|
||||
return maybeAst.takeError();
|
||||
}
|
||||
|
||||
// Create the NS and set the AST
|
||||
auto ns =
|
||||
ctx.makeNamespace(name, std::optional(llvm::StringRef(importedFile)));
|
||||
|
||||
if (auto errs = ns->addTree(*maybeAst)) {
|
||||
SMGR_LOG("Couldn't set the AST for namespace: " + name);
|
||||
return errs;
|
||||
}
|
||||
|
||||
return ns;
|
||||
};
|
||||
|
||||
unsigned SourceMgr::AddNewSourceBuffer(std::unique_ptr<llvm::MemoryBuffer> f,
|
||||
reader::LocationRange includeLoc) {
|
||||
SrcBuffer nb;
|
||||
nb.buffer = std::move(f);
|
||||
nb.importLoc = includeLoc;
|
||||
buffers.push_back(std::move(nb));
|
||||
return buffers.size();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static std::vector<T> &GetOrCreateOffsetCache(void *&offsetCache,
|
||||
llvm::MemoryBuffer *buffer) {
|
||||
if (offsetCache) {
|
||||
return *static_cast<std::vector<T> *>(offsetCache);
|
||||
}
|
||||
|
||||
// Lazily fill in the offset cache.
|
||||
auto *offsets = new std::vector<T>();
|
||||
size_t sz = buffer->getBufferSize();
|
||||
|
||||
// TODO: Replace this assert with a realtime check
|
||||
assert(sz <= std::numeric_limits<T>::max());
|
||||
|
||||
llvm::StringRef s = buffer->getBuffer();
|
||||
for (size_t n = 0; n < sz; ++n) {
|
||||
if (s[n] == '\n') {
|
||||
offsets->push_back(static_cast<T>(n));
|
||||
}
|
||||
}
|
||||
|
||||
offsetCache = offsets;
|
||||
return *offsets;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const char *SourceMgr::SrcBuffer::getPointerForLineNumberSpecialized(
|
||||
unsigned lineNo) const {
|
||||
std::vector<T> &offsets =
|
||||
GetOrCreateOffsetCache<T>(offsetCache, buffer.get());
|
||||
|
||||
// We start counting line and column numbers from 1.
|
||||
if (lineNo != 0) {
|
||||
--lineNo;
|
||||
}
|
||||
|
||||
const char *bufStart = buffer->getBufferStart();
|
||||
|
||||
// The offset cache contains the location of the \n for the specified line,
|
||||
// we want the start of the line. As such, we look for the previous entry.
|
||||
if (lineNo == 0) {
|
||||
return bufStart;
|
||||
}
|
||||
|
||||
if (lineNo > offsets.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return bufStart + offsets[lineNo - 1] + 1;
|
||||
}
|
||||
|
||||
/// Return a pointer to the first character of the specified line number or
|
||||
/// null if the line number is invalid.
|
||||
const char *
|
||||
SourceMgr::SrcBuffer::getPointerForLineNumber(unsigned lineNo) const {
|
||||
size_t sz = buffer->getBufferSize();
|
||||
if (sz <= std::numeric_limits<uint8_t>::max()) {
|
||||
return getPointerForLineNumberSpecialized<uint8_t>(lineNo);
|
||||
}
|
||||
|
||||
if (sz <= std::numeric_limits<uint16_t>::max()) {
|
||||
return getPointerForLineNumberSpecialized<uint16_t>(lineNo);
|
||||
}
|
||||
|
||||
if (sz <= std::numeric_limits<uint32_t>::max()) {
|
||||
return getPointerForLineNumberSpecialized<uint32_t>(lineNo);
|
||||
}
|
||||
|
||||
return getPointerForLineNumberSpecialized<uint64_t>(lineNo);
|
||||
}
|
||||
|
||||
SourceMgr::SrcBuffer::SrcBuffer(SourceMgr::SrcBuffer &&other) noexcept
|
||||
: buffer(std::move(other.buffer)), offsetCache(other.offsetCache),
|
||||
importLoc(other.importLoc) {
|
||||
other.offsetCache = nullptr;
|
||||
}
|
||||
|
||||
SourceMgr::SrcBuffer::~SrcBuffer() {
|
||||
if (offsetCache != nullptr) {
|
||||
size_t sz = buffer->getBufferSize();
|
||||
if (sz <= std::numeric_limits<uint8_t>::max()) {
|
||||
delete static_cast<std::vector<uint8_t> *>(offsetCache);
|
||||
} else if (sz <= std::numeric_limits<uint16_t>::max()) {
|
||||
delete static_cast<std::vector<uint16_t> *>(offsetCache);
|
||||
} else if (sz <= std::numeric_limits<uint32_t>::max()) {
|
||||
delete static_cast<std::vector<uint32_t> *>(offsetCache);
|
||||
} else {
|
||||
delete static_cast<std::vector<uint64_t> *>(offsetCache);
|
||||
}
|
||||
offsetCache = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace serene
|
|
@ -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)
|
|
@ -1,161 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef SERENE_TEST_CONTEXT_H
|
||||
#define SERENE_TEST_CONTEXT_H
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/serene.h"
|
||||
|
||||
#include "./test_helpers.cpp.inc"
|
||||
#include <catch2/catch_all.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <llvm/ADT/None.h>
|
||||
|
||||
namespace serene {
|
||||
|
||||
TEST_CASE("Compiler options", "[context]") {
|
||||
auto opts = Options();
|
||||
opts.JITLazy = !opts.JITLazy;
|
||||
opts.JITenableGDBNotificationListener =
|
||||
!opts.JITenableGDBNotificationListener;
|
||||
|
||||
auto ctx = makeSereneContext(opts);
|
||||
|
||||
CHECK(ctx->opts.JITLazy == opts.JITLazy);
|
||||
CHECK(ctx->opts.JITenableGDBNotificationListener ==
|
||||
opts.JITenableGDBNotificationListener);
|
||||
};
|
||||
|
||||
TEST_CASE("makeNamespace & getNS", "[context]") {
|
||||
auto ctx = makeSereneContext();
|
||||
auto *ns = ctx->getNS("blah");
|
||||
|
||||
REQUIRE_FALSE(ns);
|
||||
|
||||
auto userNs =
|
||||
ctx->makeNamespace("user", llvm::Optional<llvm::StringRef>("/some/file"));
|
||||
|
||||
CHECK(userNs->name == "user");
|
||||
REQUIRE(userNs->filename);
|
||||
CHECK(userNs->filename.getValue() == "/some/file");
|
||||
|
||||
ns = ctx->getNS("user");
|
||||
|
||||
REQUIRE(ns);
|
||||
CHECK(ns->name == userNs->name);
|
||||
|
||||
/// Creating new ns with the same name overrides the old one
|
||||
auto userNs1 = ctx->makeNamespace(
|
||||
"user", llvm::Optional<llvm::StringRef>("/some/other/file"));
|
||||
|
||||
ns = ctx->getNS("user");
|
||||
|
||||
REQUIRE(ns);
|
||||
CHECK(ns->name == userNs1->name);
|
||||
REQUIRE(ns->filename);
|
||||
CHECK(ns->filename.getValue() == "/some/other/file");
|
||||
};
|
||||
|
||||
TEST_CASE(
|
||||
"withCurrentNS run a function with the given namespace as the current NS",
|
||||
"[context]") {
|
||||
auto ctx = makeSereneContext();
|
||||
auto userNs =
|
||||
ctx->makeNamespace("user", llvm::Optional<llvm::StringRef>("/some/file"));
|
||||
|
||||
ctx->withCurrentNS<int>("user", [&]() -> int {
|
||||
CHECK(ctx->getCurrentNS().name == userNs->name);
|
||||
return 0;
|
||||
});
|
||||
|
||||
// Checking the `void` type specialization
|
||||
ctx->withCurrentNS<void>("user", [&]() -> void {
|
||||
CHECK(ctx->getCurrentNS().name == userNs->name);
|
||||
});
|
||||
|
||||
CHECK(ctx->getCurrentNS().name == DEFAULT_NS_NAME);
|
||||
};
|
||||
|
||||
TEST_CASE("getSharedPtrToNS returns a shared ptr to the NS", "[context]") {
|
||||
auto ctx = makeSereneContext();
|
||||
auto userNs =
|
||||
ctx->makeNamespace("user", llvm::Optional<llvm::StringRef>("/some/file"));
|
||||
|
||||
auto userNs1 = ctx->makeNamespace(
|
||||
"user1", llvm::Optional<llvm::StringRef>("/some/file1"));
|
||||
|
||||
CHECK(ctx->getCurrentNS().name == DEFAULT_NS_NAME);
|
||||
|
||||
NSPtr shouldBeUser1 = ctx->getSharedPtrToNS("user");
|
||||
|
||||
REQUIRE(shouldBeUser1);
|
||||
CHECK(shouldBeUser1->name == userNs->name);
|
||||
};
|
||||
|
||||
TEST_CASE("Compilation phase", "[context]") {
|
||||
auto ctx = makeSereneContext();
|
||||
auto cp = CompilationPhase::O3;
|
||||
|
||||
ctx->setOperationPhase(cp);
|
||||
|
||||
CHECK(ctx->getTargetPhase() == cp);
|
||||
CHECK(ctx->getOptimizatioLevel() == 3);
|
||||
|
||||
// Anything below 0 is 0
|
||||
cp = CompilationPhase::MLIR;
|
||||
ctx->setOperationPhase(cp);
|
||||
CHECK(ctx->getOptimizatioLevel() == 0);
|
||||
};
|
||||
|
||||
TEST_CASE("makeNamespace", "[context]") {
|
||||
auto ctx = makeSereneContext();
|
||||
|
||||
auto ns = ctx->makeNamespace("example.ns", llvm::None);
|
||||
|
||||
// Namespace has to be empty
|
||||
CHECK(ns->name == "example.ns");
|
||||
CHECK(ns->getTree().empty());
|
||||
CHECK(ns->filename == llvm::None);
|
||||
};
|
||||
|
||||
TEST_CASE("context and jit", "[context]") {
|
||||
auto ctx = makeSereneContext();
|
||||
|
||||
auto ns = ctx->makeNamespace("example.ns", llvm::None);
|
||||
|
||||
REQUIRE(ctx->jit);
|
||||
|
||||
// no JITDylib should be defined for an empty NS
|
||||
CHECK(ctx->getNumberOfJITDylibs(*ns) == 0);
|
||||
|
||||
SECTION("JITDylib management") {
|
||||
auto unknown = reader::LocationRange::UnknownLocation(DEFAULT_NS_NAME);
|
||||
REQUIRE(!ctx->jit->addNS(*ns, unknown));
|
||||
|
||||
CHECK(ctx->getNumberOfJITDylibs(*ns) == 1);
|
||||
|
||||
REQUIRE(ctx->getLatestJITDylib(*ns));
|
||||
CHECK(ctx->getLatestJITDylib(*ns)->getName() == "example.ns#1");
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace serene
|
||||
#endif
|
|
@ -1,95 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/environment.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
#include "serene/reader/location.h"
|
||||
#include "serene/serene.h"
|
||||
|
||||
#include "./test_helpers.cpp.inc"
|
||||
#include <catch2/catch_all.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <llvm/Support/Casting.h>
|
||||
|
||||
namespace serene {
|
||||
|
||||
TEST_CASE("Environment tests", "[environment]") {
|
||||
std::unique_ptr<reader::LocationRange> range(dummyLocation());
|
||||
|
||||
exprs::Node sym = exprs::make<exprs::Symbol>(*range, "example", "ns");
|
||||
|
||||
Environment<exprs::Node> e;
|
||||
llvm::Optional<exprs::Node> result = e.lookup("a");
|
||||
|
||||
REQUIRE_FALSE(result.hasValue());
|
||||
|
||||
auto status = e.insert_symbol("a", sym);
|
||||
|
||||
REQUIRE(status.succeeded());
|
||||
|
||||
result = e.lookup("a");
|
||||
|
||||
REQUIRE(result.hasValue());
|
||||
CHECK(result.getValue() == sym);
|
||||
|
||||
auto *fetchedSym = llvm::dyn_cast<exprs::Symbol>(result.getValue().get());
|
||||
REQUIRE(fetchedSym != nullptr);
|
||||
CHECK(fetchedSym->name == "example");
|
||||
CHECK(fetchedSym->nsName == "ns");
|
||||
|
||||
SECTION("Testing the environment copy") {
|
||||
Environment<exprs::Node> e1(&e);
|
||||
|
||||
result = e1.lookup("b");
|
||||
REQUIRE_FALSE(result.hasValue());
|
||||
|
||||
// It should lookup the value in the parent environment
|
||||
result = e1.lookup("a");
|
||||
REQUIRE(result.hasValue());
|
||||
CHECK(result.getValue() == sym);
|
||||
};
|
||||
};
|
||||
|
||||
TEST_CASE("Test generic Environment usage", "[environment]") {
|
||||
Environment<char> env;
|
||||
|
||||
auto result = env.lookup("blah");
|
||||
REQUIRE_FALSE(result);
|
||||
|
||||
auto status = env.insert_symbol("blah", 'A');
|
||||
REQUIRE(status.succeeded());
|
||||
|
||||
result = env.lookup("blah");
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 'A');
|
||||
|
||||
result = env.lookup("blah");
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 'A');
|
||||
|
||||
// Test the overwrite functionality
|
||||
status = env.insert_symbol("blah", 'B');
|
||||
REQUIRE(status.succeeded());
|
||||
|
||||
result = env.lookup("blah");
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 'B');
|
||||
}
|
||||
} // namespace serene
|
|
@ -1,66 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_TEST_ERRORS_H
|
||||
#define SERENE_TEST_ERRORS_H
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/errors.h"
|
||||
|
||||
#include "../test_helpers.cpp.inc"
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <llvm/ADT/StringMap.h>
|
||||
#include <llvm/Support/Error.h>
|
||||
#include <llvm/Support/ErrorHandling.h>
|
||||
|
||||
namespace serene {
|
||||
namespace errors {
|
||||
|
||||
TEST_CASE("Serene Error construction", "[errors]") {
|
||||
|
||||
std::unique_ptr<reader::LocationRange> range(dummyLocation());
|
||||
auto ctx = makeSereneContext();
|
||||
llvm::Error err = makeError(*ctx, PassFailureError, *range, "test error");
|
||||
|
||||
auto unhandled =
|
||||
llvm::handleErrors(std::move(err), [&](const SereneError &e) {
|
||||
REQUIRE(e.message() == "test error");
|
||||
const auto *v = getVariant(e.errorType);
|
||||
REQUIRE(v != nullptr);
|
||||
CHECK(v->title == "PassFailureError");
|
||||
CHECK(v->desc == "Pass Failure.");
|
||||
CHECK(v->help.empty());
|
||||
});
|
||||
|
||||
CHECK(!unhandled);
|
||||
}
|
||||
|
||||
TEST_CASE("getMessage function", "[errors]") {
|
||||
std::unique_ptr<reader::LocationRange> range(dummyLocation());
|
||||
auto ctx = makeSereneContext();
|
||||
llvm::Error err = makeError(*ctx, PassFailureError, *range, "test error");
|
||||
|
||||
CHECK(getMessage(err) == "test error");
|
||||
CHECK_SERENE_ERR(PassFailureError, std::move(err));
|
||||
}
|
||||
|
||||
}; // namespace errors
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,43 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/list.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
|
||||
#include "../test_helpers.cpp.inc"
|
||||
|
||||
#include <llvm/ADT/ArrayRef.h>
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
|
||||
TEST_CASE("Public Expression API", "[expression]") {
|
||||
std::unique_ptr<reader::LocationRange> range(dummyLocation());
|
||||
auto sym = make<Symbol>(*range, "example", "user");
|
||||
|
||||
REQUIRE(sym->getType() == ExprType::Symbol);
|
||||
CHECK(sym->toString() == "<Symbol user/example>");
|
||||
|
||||
auto list = makeAndCast<List>(*range, sym);
|
||||
|
||||
CHECK(list->toString() == "<List <Symbol user/example>>");
|
||||
};
|
||||
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
|
@ -1,196 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/errors.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/list.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/reader/reader.h"
|
||||
#include "serene/semantics.h"
|
||||
|
||||
#include "../test_helpers.cpp.inc"
|
||||
|
||||
#include <llvm/Support/Error.h>
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
|
||||
TEST_CASE("List Expression", "[expression]") {
|
||||
std::unique_ptr<reader::LocationRange> range(dummyLocation());
|
||||
|
||||
Node sym = make<Symbol>(*range, "example", "user");
|
||||
Node sym1 = make<Symbol>(*range, "example1", "user");
|
||||
|
||||
Node list = make<List>(*range);
|
||||
|
||||
REQUIRE(list->getType() == ExprType::List);
|
||||
CHECK(list->toString() == "<List ->");
|
||||
|
||||
Node list2 = make<List>(*range, list);
|
||||
CHECK(list2->toString() == "<List <List ->>");
|
||||
|
||||
Ast elements;
|
||||
elements.push_back(list);
|
||||
elements.push_back(list2);
|
||||
elements.push_back(sym);
|
||||
|
||||
auto list3 = make<List>(*range, elements);
|
||||
|
||||
CHECK(list3->toString() ==
|
||||
"<List <List -> <List <List ->> <Symbol user/example>>");
|
||||
|
||||
auto l = llvm::dyn_cast<List>(list.get());
|
||||
|
||||
l->append(sym1);
|
||||
|
||||
REQUIRE(list->getType() == ExprType::List);
|
||||
CHECK(list->toString() == "<List <Symbol user/example1>>");
|
||||
|
||||
l->append(sym);
|
||||
REQUIRE(l->count() == 2);
|
||||
|
||||
auto expr = l->at(1);
|
||||
REQUIRE(expr.hasValue());
|
||||
CHECK(expr.getValue()->toString() == "<Symbol user/example>");
|
||||
|
||||
expr = l->at(2);
|
||||
REQUIRE_FALSE(expr.hasValue());
|
||||
|
||||
for (auto x : *l) {
|
||||
CHECK(x->getType() == ExprType::Symbol);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("List semantic analysis of 'def'", "[semantic,expression,list]") {
|
||||
|
||||
auto ctx = makeSereneContext();
|
||||
auto ns = ctx->makeNamespace("user", llvm::None);
|
||||
auto ast = llvm::cantFail(READ("(def (a) b)"));
|
||||
|
||||
SemanticEnv env;
|
||||
semantics::AnalysisState state(*ns, env);
|
||||
auto afterAst = semantics::analyze(state, ast);
|
||||
|
||||
REQUIRE_FALSE(afterAst);
|
||||
{
|
||||
auto err = afterAst.takeError();
|
||||
CHECK(err.isA<llvm::ErrorList>());
|
||||
}
|
||||
ast = llvm::cantFail(READ("(def a)"));
|
||||
afterAst = semantics::analyze(state, ast);
|
||||
REQUIRE_FALSE(afterAst);
|
||||
CHECK(errors::getMessage(afterAst.takeError()) == "Expected 3 got 2");
|
||||
|
||||
ast = llvm::cantFail(READ("(def a b c)"));
|
||||
afterAst = semantics::analyze(state, ast);
|
||||
REQUIRE_FALSE(afterAst);
|
||||
CHECK(errors::getMessage(afterAst.takeError()) == "Expected 3 got 4");
|
||||
|
||||
ast = llvm::cantFail(READ("(def a b)"));
|
||||
afterAst = semantics::analyze(state, ast);
|
||||
REQUIRE(afterAst);
|
||||
CHECK(astToString(&(*afterAst)) == "<Def a -> <Symbol b>>");
|
||||
|
||||
ast = llvm::cantFail(READ("(def a (fn () a))"));
|
||||
afterAst = semantics::analyze(state, ast);
|
||||
REQUIRE(afterAst);
|
||||
CHECK(astToString(&(*afterAst)) ==
|
||||
"<Def a -> <Fn a <List -> to <Symbol a>>>");
|
||||
}
|
||||
|
||||
// TEST_CASE("List semantic analysis for 'fn'", "[semantic]") {
|
||||
// auto ctx = makeSereneContext();
|
||||
// auto ns = ctx->makeNamespace("user", llvm::None);
|
||||
// auto ast = READ("(fn)");
|
||||
// auto afterAst = semantics::analyze(*ctx, *ast);
|
||||
|
||||
// REQUIRE_FALSE(afterAst);
|
||||
// REQUIRE(afterAst.takeError().size() == 1);
|
||||
// CHECK(afterAst.getError()[0]->toString() ==
|
||||
// "<Error E3: The argument list is mandatory.>");
|
||||
|
||||
// ast = reader::read("(fn ())");
|
||||
// afterAst = semantics::analyze(*ctx, ast.getValue());
|
||||
// REQUIRE(afterAst);
|
||||
// CHECK(astToString(&afterAst.getValue()) == "<Fn ___fn___0 <List -> to
|
||||
// <>>");
|
||||
|
||||
// ast = reader::read("(fn (a b c) a a a)");
|
||||
// afterAst = semantics::analyze(*ctx, ast.getValue());
|
||||
// REQUIRE(afterAst);
|
||||
// CHECK(astToString(&afterAst.getValue()) ==
|
||||
// "<Fn ___fn___1 <List <Symbol a> <Symbol b> <Symbol c>> to <Symbol a>
|
||||
// "
|
||||
// "<Symbol a> <Symbol a>>");
|
||||
|
||||
// ast = reader::read("(fn () a b)");
|
||||
// afterAst = semantics::analyze(*ctx, ast.getValue());
|
||||
// REQUIRE(afterAst);
|
||||
// CHECK(astToString(&afterAst.getValue()) ==
|
||||
// "<Fn ___fn___2 <List -> to <Symbol a> <Symbol b>>");
|
||||
|
||||
// ast = reader::read("(fn (x) (fn (y) x) z)");
|
||||
// afterAst = semantics::analyze(*ctx, ast.getValue());
|
||||
// REQUIRE(afterAst);
|
||||
// CHECK(astToString(&afterAst.getValue()) ==
|
||||
// "<Fn ___fn___4 <List <Symbol x>> to <Fn ___fn___3 <List <Symbol y>>
|
||||
// " "to <Symbol x>> <Symbol z>>");
|
||||
|
||||
// ast = reader::read("(fn (x) (def a b) (def b c))");
|
||||
// afterAst = semantics::analyze(*ctx, ast.getValue());
|
||||
// REQUIRE(afterAst);
|
||||
// CHECK(astToString(&afterAst.getValue()) ==
|
||||
// "<Fn ___fn___5 <List <Symbol x>> to <Def a -> <Symbol b>> <Def b ->
|
||||
// "
|
||||
// "<Symbol c>>>");
|
||||
// }
|
||||
|
||||
// TEST_CASE("Complex semantic analysis", "[semantic]") {
|
||||
// auto ctx = makeSereneContext();
|
||||
// auto ns = makeNamespace(*ctx, "user", llvm::None);
|
||||
// auto ast =
|
||||
// reader::read("(def a (fn (x) x))\n((def b (fn (x) (fn (y) y))))\n\n");
|
||||
// auto afterAst = semantics::analyze(*ctx, ast.getValue());
|
||||
// REQUIRE(afterAst);
|
||||
// CHECK(astToString(&afterAst.getValue()) ==
|
||||
// "<Def a -> <Fn a <List <Symbol x>> to <Symbol x>>> <Call <Def b -> "
|
||||
// "<Fn b <List <Symbol x>> to <Fn ___fn___1 <List <Symbol y>> to "
|
||||
// "<Symbol y>>>> >");
|
||||
|
||||
// ctx = makeSereneContext();
|
||||
// ns = makeNamespace(*ctx, "user", llvm::None);
|
||||
// ast = reader::read("((a b))");
|
||||
// afterAst = semantics::analyze(*ctx, ast.getValue());
|
||||
// REQUIRE_FALSE(afterAst);
|
||||
// auto errs = afterAst.getError();
|
||||
// CHECK(errs[0]->toString() == "<Error E5: Can't resolve the symbol 'a'>");
|
||||
|
||||
// ctx = makeSereneContext();
|
||||
// ns = makeNamespace(*ctx, "user", llvm::None);
|
||||
// ast = reader::read("(def a (fn (x) x)) (a b)");
|
||||
// afterAst = semantics::analyze(*ctx, ast.getValue());
|
||||
// REQUIRE(afterAst);
|
||||
|
||||
// CHECK(astToString(&afterAst.getValue()) ==
|
||||
// "<Def a -> <Fn a <List <Symbol x>> to <Symbol x>>> <Call <Fn a <List
|
||||
// "
|
||||
// "<Symbol x>> to <Symbol x>> <Symbol b>>");
|
||||
// }
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
|
@ -1,42 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/exprs/number.h"
|
||||
|
||||
#include "../test_helpers.cpp.inc"
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
TEST_CASE("Number Expression", "[expression]") {
|
||||
std::unique_ptr<reader::LocationRange> range(dummyLocation());
|
||||
|
||||
auto num1 = makeAndCast<Number>(*range, "3", false, false);
|
||||
auto num2 = makeAndCast<Number>(*range, "3.4", false, true);
|
||||
// Hence the `isNeg` being true. We need to provide the sign as the input
|
||||
// anyway
|
||||
auto num3 = makeAndCast<Number>(*range, "3", true, false);
|
||||
auto num4 = makeAndCast<Number>(*range, "-3", true, false);
|
||||
|
||||
CHECK(num1->toString() == "<Number 3>");
|
||||
CHECK(num2->toString() == "<Number 3.4>");
|
||||
CHECK(num3->toString() == "<Number 3>");
|
||||
CHECK(num4->toString() == "<Number -3>");
|
||||
};
|
||||
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
|
@ -1,35 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/exprs/symbol.h"
|
||||
|
||||
#include "../test_helpers.cpp.inc"
|
||||
|
||||
namespace serene {
|
||||
namespace exprs {
|
||||
|
||||
TEST_CASE("Public Symbol API", "[expression]") {
|
||||
std::unique_ptr<reader::LocationRange> range(dummyLocation());
|
||||
auto sym = make<Symbol>(*range, "example", "user");
|
||||
|
||||
REQUIRE(sym->getType() == ExprType::Symbol);
|
||||
CHECK(sym->toString() == "<Symbol user/example>");
|
||||
};
|
||||
} // namespace exprs
|
||||
} // namespace serene
|
|
@ -1,50 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/context.h"
|
||||
#include "serene/exprs/expression.h"
|
||||
#include "serene/namespace.h"
|
||||
#include "serene/reader/reader.h"
|
||||
|
||||
#include "./test_helpers.cpp.inc"
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
namespace serene {
|
||||
|
||||
TEST_CASE("Namespace tests", "[namespace]") {
|
||||
auto ctx = makeSereneContext();
|
||||
auto userNs = makeNamespace(*ctx, "user",
|
||||
llvm::Optional<llvm::StringRef>("/some/file"));
|
||||
|
||||
auto maybeAst = reader::read("(x 1) (def b a)");
|
||||
|
||||
if (!maybeAst) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
auto result = userNs->setTree(maybeAst.getValue());
|
||||
|
||||
REQUIRE(result.succeeded());
|
||||
|
||||
REQUIRE_FALSE(userNs->getTree().empty());
|
||||
CHECK(exprs::astToString(&userNs->getTree()) ==
|
||||
"<List <Symbol x> <Number 1>> <List <Symbol def> <Symbol b> <Symbol "
|
||||
"a>>");
|
||||
};
|
||||
|
||||
} // namespace serene
|
|
@ -1,110 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERENE_TEST_READER_H
|
||||
#define SERENE_TEST_READER_H
|
||||
|
||||
#include "serene/reader/reader.h"
|
||||
|
||||
#include "../test_helpers.cpp.inc"
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
namespace serene {
|
||||
namespace reader {
|
||||
|
||||
TEST_CASE("Read numbers", "[reader]") {
|
||||
auto ctx = makeSereneContext();
|
||||
auto maybeAst = READ("3");
|
||||
|
||||
if (!maybeAst) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
auto ast = *maybeAst;
|
||||
REQUIRE_FALSE(ast.empty());
|
||||
CHECK(ast.front()->toString() == "<Number 3>");
|
||||
|
||||
maybeAst = READ("-34");
|
||||
|
||||
if (!maybeAst) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
ast = *maybeAst;
|
||||
REQUIRE_FALSE(ast.empty());
|
||||
CHECK(ast.front()->toString() == "<Number -34>");
|
||||
|
||||
maybeAst = READ("-3.5434");
|
||||
|
||||
if (!maybeAst) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
ast = *maybeAst;
|
||||
REQUIRE_FALSE(ast.empty());
|
||||
CHECK(ast.front()->toString() == "<Number -3.5434>");
|
||||
|
||||
maybeAst = READ("444323 2123 123123");
|
||||
|
||||
if (!maybeAst) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
ast = *maybeAst;
|
||||
REQUIRE(ast.size() == 3);
|
||||
CHECK(ast.front()->toString() == "<Number 444323>");
|
||||
CHECK(ast[1]->toString() == "<Number 2123>");
|
||||
CHECK(ast[2]->toString() == "<Number 123123>");
|
||||
};
|
||||
|
||||
TEST_CASE("Read Lists and Symbols", "[reader]") {
|
||||
auto ctx = makeSereneContext();
|
||||
auto maybeAst = READ("(x 1)");
|
||||
|
||||
if (!maybeAst) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
auto ast = *maybeAst;
|
||||
REQUIRE_FALSE(ast.empty());
|
||||
CHECK(ast.front()->toString() == "<List <Symbol user/x> <Number 1>>");
|
||||
|
||||
maybeAst = READ("(x (y (z)))");
|
||||
|
||||
if (!maybeAst) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
ast = *maybeAst;
|
||||
REQUIRE_FALSE(ast.empty());
|
||||
CHECK(ast.front()->toString() == "<List <Symbol user/x> <List <Symbol "
|
||||
"user/y> <List <Symbol user/z>>>>");
|
||||
|
||||
maybeAst = READ("(x \n y)");
|
||||
|
||||
if (!maybeAst) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
ast = *maybeAst;
|
||||
REQUIRE_FALSE(ast.empty());
|
||||
CHECK(ast.front()->toString() == "<List <Symbol user/x> <Symbol user/y>>");
|
||||
};
|
||||
} // namespace reader
|
||||
} // namespace serene
|
||||
#endif
|
|
@ -1,33 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "./context_tests.cpp.inc"
|
||||
#include "./environment_tests.cpp.inc"
|
||||
#include "./errors/error_tests.cpp.inc"
|
||||
#include "./exprs/expression_tests.cpp.inc"
|
||||
#include "./exprs/list_tests.cpp.inc"
|
||||
#include "./exprs/number_tests.cpp.inc"
|
||||
#include "./exprs/symbol_tests.cpp.inc"
|
||||
#include "./setup.cpp.inc"
|
||||
// #include "./namespace_tests.cpp.inc"
|
||||
#include "./reader/reader_tests.cpp.inc"
|
||||
// #include "./traits_tests.cpp.inc"
|
||||
// #include "./utils_tests.cpp.inc"
|
||||
// include <catch2/catch.hpp>
|
||||
#include <catch2/catch_all.hpp>
|
|
@ -1,37 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/serene.h"
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include <catch2/reporters/catch_reporter_event_listener.hpp>
|
||||
#include <catch2/reporters/catch_reporter_registrars.hpp>
|
||||
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
class testRunListener : public Catch::EventListenerBase {
|
||||
public:
|
||||
using Catch::EventListenerBase::EventListenerBase;
|
||||
|
||||
void testRunStarting(Catch::TestRunInfo const &info) override {
|
||||
UNUSED(info);
|
||||
serene::initCompiler();
|
||||
}
|
||||
};
|
||||
|
||||
CATCH_REGISTER_LISTENER(testRunListener)
|
|
@ -1,66 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TEST_HEALPERS_H
|
||||
#define TEST_HEALPERS_H
|
||||
|
||||
#include "serene/reader/location.h"
|
||||
|
||||
// *IMPORTANT NOTE:* The `READ` macro is just a quick way to eliminate
|
||||
// the overhead of writing the same function signature
|
||||
// over and over again. Nothing special about it.
|
||||
#define READ(input) reader::read(*ctx, input, "user", llvm::None)
|
||||
|
||||
#define CHECK_ERR_MSG(e, s) CHECK(serene::errors::getMessage(e) == s)
|
||||
// `llvm::Error`s has to be checked in the same scope. This macro makes
|
||||
// the check easy while were testing the other aspects of the error.
|
||||
// `t` is the concrete error type and `e` is the error instance.
|
||||
#define CHECK_SERENE_ERR(t, e) \
|
||||
auto unhandled = llvm::handleErrors( \
|
||||
e, [&](const SereneError &x) { CHECK(x.errorType == t); }); \
|
||||
CHECK(!unhandled);
|
||||
|
||||
#define CHECK_ERR(t, e) \
|
||||
auto unhandled = llvm::handleErrors(e, [&](const t &x) { \
|
||||
(void)x; \
|
||||
CHECK(true); \
|
||||
}); \
|
||||
CHECK(!unhandled);
|
||||
|
||||
namespace serene {
|
||||
|
||||
reader::LocationRange *dummyLocation() {
|
||||
reader::Location start("serene.test.ns");
|
||||
reader::Location end("serene.test.ns");
|
||||
|
||||
constexpr const int line1 = 2;
|
||||
constexpr const int line2 = 3;
|
||||
constexpr const int col1 = 20;
|
||||
constexpr const int col2 = 30;
|
||||
|
||||
start.line = line1;
|
||||
start.col = col1;
|
||||
end.line = line2;
|
||||
end.col = col2;
|
||||
|
||||
return new reader::LocationRange(start, end);
|
||||
};
|
||||
|
||||
} // namespace serene
|
||||
|
||||
#endif
|
|
@ -1,129 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "serene/traits.h"
|
||||
|
||||
#include "./test_helpers.cpp.inc"
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
namespace serene {
|
||||
|
||||
template <typename ConcreteType>
|
||||
class Printable : public TraitBase<ConcreteType, Printable> {
|
||||
public:
|
||||
Printable(){};
|
||||
Printable(const Printable &) = delete;
|
||||
std::string Print() { return this->Object().Print(); }
|
||||
};
|
||||
|
||||
template <typename ConcreteType>
|
||||
class Analyzable : public TraitBase<ConcreteType, Analyzable> {
|
||||
public:
|
||||
Analyzable(){};
|
||||
Analyzable(const Analyzable &) = delete;
|
||||
std::string Analyze() { return this->Object().Analyze(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::string Print(Printable<T> &t) {
|
||||
return t.Print();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string Analyze(Analyzable<T> &t) {
|
||||
return t.Analyze();
|
||||
};
|
||||
|
||||
class A : public WithTrait<A, Printable, Analyzable> {
|
||||
|
||||
std::string x;
|
||||
|
||||
public:
|
||||
A(std::string a) : x(a){};
|
||||
std::string Print() const { return "A: print"; }
|
||||
|
||||
std::string Analyze() const { return "A: " + x; }
|
||||
};
|
||||
|
||||
template <typename T = FinalImpl>
|
||||
class B : public std::conditional<std::is_same_v<T, FinalImpl>,
|
||||
WithTrait<B<>, Printable, Analyzable>,
|
||||
WithTrait<T, Printable, Analyzable>>::type {
|
||||
|
||||
std::string y;
|
||||
|
||||
public:
|
||||
B(std::string a) : y(a){};
|
||||
std::string Print() const { return "B: print"; }
|
||||
|
||||
std::string Analyze() const { return "B: " + y; }
|
||||
};
|
||||
|
||||
class C : public B<C> {
|
||||
|
||||
std::string z;
|
||||
std::string w;
|
||||
|
||||
public:
|
||||
C(std::string a, std::string b, std::string c) : B<C>(a), z(b), w(c){};
|
||||
std::string Print() const { return z; }
|
||||
|
||||
std::string Analyze() const { return w; }
|
||||
};
|
||||
|
||||
class D : public WithTrait<D, Printable> {
|
||||
public:
|
||||
std::string Print() const { return "D: print"; }
|
||||
|
||||
std::string Analyze() const { return "D: analyze with no trait"; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class MetaTrait : public WithTrait<T, Printable, Analyzable> {};
|
||||
|
||||
class E : public MetaTrait<E> {
|
||||
public:
|
||||
std::string Print() const { return "E: print"; };
|
||||
|
||||
std::string Analyze() const { return "E: in E"; };
|
||||
};
|
||||
|
||||
TEST_CASE("Trait functionality tests", "[Traits]") {
|
||||
auto a = A("in A");
|
||||
auto b = B("in B");
|
||||
auto c = C("gray", "white", "black");
|
||||
auto d = D();
|
||||
auto e = E();
|
||||
|
||||
CHECK(Print(a) == "A: print");
|
||||
CHECK(Print(b) == "B: print");
|
||||
CHECK(Print(c) == "white");
|
||||
CHECK(Print(d) == "D: print");
|
||||
CHECK(Print(e) == "E: print");
|
||||
|
||||
CHECK(Analyze(a) == "A: in A");
|
||||
CHECK(Analyze(b) == "B: in B");
|
||||
CHECK(Analyze(c) == "black");
|
||||
CHECK(Analyze(e) == "E: in E");
|
||||
|
||||
// Even though D has a Analyze method, It's not Analyzable to the
|
||||
// Analyze function signature won't match
|
||||
CHECK(d.Analyze() == "D: analyze with no trait");
|
||||
};
|
||||
|
||||
} // namespace serene
|
|
@ -1,34 +0,0 @@
|
|||
/* -*- C++ -*-
|
||||
* Serene Programming Language
|
||||
*
|
||||
* Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "serene/utils.h"
|
||||
|
||||
#include "./test_helpers.cpp.inc"
|
||||
|
||||
namespace serene {
|
||||
TEST_CASE("Result Type", "[utils]") {
|
||||
auto r = Result<int>::success(4);
|
||||
|
||||
REQUIRE(r == true);
|
||||
CHECK(r.getValue() == 4);
|
||||
|
||||
auto r1 = Result<int, char>::error('c');
|
||||
|
||||
REQUIRE_FALSE(r1);
|
||||
CHECK(r1.getError() == 'c');
|
||||
};
|
||||
} // namespace serene
|
|
@ -1,82 +0,0 @@
|
|||
# Serene Programming Language
|
||||
#
|
||||
# Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
set(LIBSERENE_INCLUDE_DIR ${INCLUDE_DIR} PARENT_SCOPE)
|
||||
include_directories(${INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib)
|
||||
|
||||
add_subdirectory(lib)
|
||||
|
||||
# Install rules for libserene target
|
||||
install(TARGETS serene
|
||||
EXPORT SereneExports
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
|
||||
# Install rules for the public header files.
|
||||
install(DIRECTORY ${INCLUDE_DIR}/serene
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
FILES_MATCHING
|
||||
PATTERN *.h
|
||||
PATTERN *.td
|
||||
PATTERN "CMake*" EXCLUDE)
|
||||
|
||||
# Install rule for the public generated header files
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
FILES_MATCHING
|
||||
PATTERN *.h
|
||||
PATTERN *.td
|
||||
PATTERN *.h.inc
|
||||
PATTERN "CMake*" EXCLUDE)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# Package config file let us use find_package with serene
|
||||
configure_package_config_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
|
||||
INSTALL_DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${PROJECT_NAME}ConfigVersion.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
|
||||
)
|
||||
|
||||
# Install the package exports
|
||||
install(EXPORT SereneExports
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/serene-${PROJECT_VERSION}
|
||||
NAMESPACE serene::)
|
||||
|
||||
# Testing only available if this is the main app
|
||||
# Emergency override SERENE_CMAKE_BUILD_TESTING provided as well
|
||||
if(SERENE_BUILD_TESTING)
|
||||
message("Build the test binary")
|
||||
add_subdirectory(tests)
|
||||
endif()
|
|
@ -1,21 +0,0 @@
|
|||
# Serene Programming Language
|
||||
#
|
||||
# Copyright (c) 2019-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Exports.cmake")
|
||||
|
||||
check_required_components("@PROJECT_NAME@")
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue