diff --git a/serene/CMakeLists.txt b/serene/CMakeLists.txt
index 6e4e24d..aa0dc86 100644
--- a/serene/CMakeLists.txt
+++ b/serene/CMakeLists.txt
@@ -38,6 +38,7 @@ endif()
target_include_directories(serene
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_include_directories(serene SYSTEM PUBLIC
diff --git a/serene/src/CMakeLists.txt b/serene/src/CMakeLists.txt
index 532bb28..8595cb8 100644
--- a/serene/src/CMakeLists.txt
+++ b/serene/src/CMakeLists.txt
@@ -18,4 +18,6 @@ target_sources(serene PRIVATE
serene.cpp
commands/commands.cpp
+ jit/jit.cpp
+ context.cpp
)
diff --git a/serene/src/commands/commands.cpp b/serene/src/commands/commands.cpp
index 714eb98..3556431 100644
--- a/serene/src/commands/commands.cpp
+++ b/serene/src/commands/commands.cpp
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-#include "serene/commands.h"
+#include
#include
diff --git a/serene/include/serene/commands.h b/serene/src/commands/commands.h
similarity index 100%
rename from serene/include/serene/commands.h
rename to serene/src/commands/commands.h
diff --git a/serene/src/context.cpp b/serene/src/context.cpp
new file mode 100644
index 0000000..841ed92
--- /dev/null
+++ b/serene/src/context.cpp
@@ -0,0 +1,51 @@
+/* -*- C++ -*-
+ * Serene Programming Language
+ *
+ * Copyright (c) 2019-2023 Sameer Rahmani
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include // for exit
+
+namespace serene {
+
+int SereneContext::getOptimizatioLevel() {
+ if (targetPhase <= CompilationPhase::NoOptimization) {
+ return 0;
+ }
+
+ if (targetPhase == CompilationPhase::O1) {
+ return 1;
+ }
+ if (targetPhase == CompilationPhase::O2) {
+ return 2;
+ }
+ return 3;
+}
+
+void terminate(SereneContext &ctx, int exitCode) {
+ (void)ctx;
+ // TODO: Since we are running in a single thread for now using exit is fine
+ // but we need to adjust and change it to a thread safe termination
+ // process later on.
+ // NOLINTNEXTLINE(concurrency-mt-unsafe)
+ std::exit(exitCode);
+}
+
+std::unique_ptr makeSereneContext(Options opts) {
+ return std::make_unique(opts);
+};
+
+}; // namespace serene
diff --git a/serene/src/context.h b/serene/src/context.h
new file mode 100644
index 0000000..3955b1c
--- /dev/null
+++ b/serene/src/context.h
@@ -0,0 +1,104 @@
+/* -*- C++ -*-
+ * Serene Programming Language
+ *
+ * Copyright (c) 2019-2023 Sameer Rahmani
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CONTEXT_H
+#define CONTEXT_H
+
+#include "options.h" // for Options
+#include <__fwd/string.h> // for string
+#include <__memory/unique_ptr.h> // for make_unique, unique_ptr
+
+#include // for Twine
+#include // for LLVMContext
+#include // for getDefaultTargetTriple
+#include // for Triple
+
+#include // for basic_string
+#include // for vector
+
+namespace serene {
+class SereneContext;
+
+/// This enum describes the different operational phases for the compiler
+/// in order. Anything below `NoOptimization` is considered only for debugging
+enum class CompilationPhase {
+ Parse,
+ Analysis,
+ SLIR,
+ MLIR, // Lowered slir to other dialects
+ LIR, // Lowered to the llvm ir dialect
+ IR, // Lowered to the LLVMIR itself
+ NoOptimization,
+ O1,
+ O2,
+ O3,
+};
+
+/// Terminates the serene compiler process in a thread safe manner
+/// This function is only meant to be used in the compiler context
+/// if you want to terminate the process in context of a serene program
+/// via the JIT use an appropriate function in the `serene.core` ns.
+void terminate(SereneContext &ctx, int exitCode);
+
+// Why SereneContext and not Context? We will be using LLVMContext
+// and MLIRContext through out Serene, so it's better to follow
+// the same convention
+class SereneContext {
+
+public:
+ /// The set of options to change the compilers behaviors
+ Options options;
+
+ const llvm::Triple triple;
+
+ explicit SereneContext(Options &options)
+ : options(options), triple(llvm::sys::getDefaultTargetTriple()),
+ targetPhase(CompilationPhase::NoOptimization){};
+
+ /// Set the target compilation phase of the compiler. The compilation
+ /// phase dictates the behavior and the output type of the compiler.
+ void setOperationPhase(CompilationPhase phase);
+
+ CompilationPhase getTargetPhase() { return targetPhase; };
+ int getOptimizatioLevel();
+
+ static std::unique_ptr genLLVMContext() {
+ return std::make_unique();
+ };
+
+ /// Setup the load path for namespace lookups
+ void setLoadPaths(std::vector &dirs) { loadPaths.swap(dirs); };
+
+ /// Return the load paths for namespaces
+ std::vector &getLoadPaths() { return loadPaths; };
+
+private:
+ CompilationPhase targetPhase;
+ std::vector loadPaths;
+};
+
+/// 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.
+std::unique_ptr makeSereneContext(Options opts = Options());
+
+} // namespace serene
+
+#endif
diff --git a/serene/src/jit/jit.cpp b/serene/src/jit/jit.cpp
new file mode 100644
index 0000000..caed305
--- /dev/null
+++ b/serene/src/jit/jit.cpp
@@ -0,0 +1,127 @@
+/* -*- C++ -*-
+ * Serene Programming Language
+ *
+ * Copyright (c) 2019-2023 Sameer Rahmani
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "jit/jit.h"
+
+#include "options.h" // for Options
+#include // for error_code
+
+#include // for StringMapEntry
+#include // for iterator_facade_base
+#include // for JITEventListener
+#include // IWYU pragma: keep
+#include // for Module
+#include // for OpenFlags
+#include // for ToolOutputFile
+
+#include // for assert
+#include // for operator+, char_t...
+
+namespace serene::jit {
+
+// ----------------------------------------------------------------------------
+// ObjectCache Implementation
+// ----------------------------------------------------------------------------
+void ObjectCache::notifyObjectCompiled(const llvm::Module *m,
+ llvm::MemoryBufferRef objBuffer) {
+ cachedObjects[m->getModuleIdentifier()] =
+ llvm::MemoryBuffer::getMemBufferCopy(objBuffer.getBuffer(),
+ objBuffer.getBufferIdentifier());
+}
+
+std::unique_ptr
+ObjectCache::getObject(const llvm::Module *m) {
+ auto i = cachedObjects.find(m->getModuleIdentifier());
+
+ if (i == cachedObjects.end()) {
+ HALLEY_LOG("No object for " + m->getModuleIdentifier() +
+ " in cache. Compiling.");
+ return nullptr;
+ }
+
+ HALLEY_LOG("Object for " + m->getModuleIdentifier() + " loaded from cache.");
+ return llvm::MemoryBuffer::getMemBuffer(i->second->getMemBufferRef());
+}
+
+void ObjectCache::dumpToObjectFile(llvm::StringRef outputFilename) {
+ // Set up the output file.
+ std::error_code error;
+
+ auto file = std::make_unique(outputFilename, error,
+ llvm::sys::fs::OF_None);
+ if (error) {
+
+ llvm::errs() << "cannot open output file '" + outputFilename.str() +
+ "': " + error.message()
+ << "\n";
+ return;
+ }
+ // Dump the object generated for a single module to the output file.
+ // TODO: Replace this with a runtime check
+ assert(cachedObjects.size() == 1 && "Expected only one object entry.");
+
+ auto &cachedObject = cachedObjects.begin()->second;
+ file->os() << cachedObject->getBuffer();
+ file->keep();
+}
+
+// ----------------------------------------------------------------------------
+// JIT Implementation
+// ----------------------------------------------------------------------------
+orc::JITDylib *JIT::getLatestJITDylib(const llvm::StringRef &nsName) {
+ if (jitDylibs.count(nsName) == 0) {
+ return nullptr;
+ }
+
+ auto vec = jitDylibs[nsName];
+ // TODO: Make sure that the returning Dylib still exists in the JIT
+ // by calling jit->engine->getJITDylibByName(dylib_name);
+ return vec.empty() ? nullptr : vec.back();
+};
+
+void JIT::pushJITDylib(const llvm::StringRef &nsName, llvm::orc::JITDylib *l) {
+ if (jitDylibs.count(nsName) == 0) {
+ llvm::SmallVector vec{l};
+ jitDylibs[nsName] = vec;
+ return;
+ }
+ auto vec = jitDylibs[nsName];
+ vec.push_back(l);
+ jitDylibs[nsName] = vec;
+}
+
+size_t JIT::getNumberOfJITDylibs(const llvm::StringRef &nsName) {
+ if (jitDylibs.count(nsName) == 0) {
+ return 0;
+ }
+
+ return jitDylibs[nsName].size();
+};
+
+JIT::JIT(llvm::orc::JITTargetMachineBuilder &&jtmb, Options &opts)
+ : isLazy(opts.JITLazy),
+ cache(opts.JITenableObjectCache ? new ObjectCache() : nullptr),
+ gdbListener(opts.JITenableGDBNotificationListener
+ ? llvm::JITEventListener::createGDBRegistrationListener()
+ : nullptr),
+ perfListener(opts.JITenablePerfNotificationListener
+ ? llvm::JITEventListener::createPerfJITEventListener()
+ : nullptr),
+ jtmb(jtmb){};
+
+} // namespace serene::jit
diff --git a/serene/src/jit/jit.h b/serene/src/jit/jit.h
new file mode 100644
index 0000000..663609c
--- /dev/null
+++ b/serene/src/jit/jit.h
@@ -0,0 +1,144 @@
+/* -*- C++ -*-
+ * Serene Programming Language
+ *
+ * Copyright (c) 2019-2023 Sameer Rahmani
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * Commentary:
+ - 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 JIT_JIT_H
+#define JIT_JIT_H
+
+#include <__memory/unique_ptr.h>
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace llvm {
+class JITEventListener;
+} // namespace llvm
+namespace llvm {
+class Module;
+} // namespace llvm
+namespace llvm::orc {
+class JITDylib;
+class LLJIT;
+class LLLazyJIT;
+} // namespace llvm::orc
+namespace serene {
+struct Options;
+} // namespace serene
+
+#define HALLEY_LOG(...) \
+ DEBUG_WITH_TYPE("JIT", llvm::dbgs() << "[JIT]: " << __VA_ARGS__ << "\n");
+
+namespace orc = llvm::orc;
+
+namespace serene::jit {
+class JIT;
+using JITPtr = std::unique_ptr;
+using MaybeJIT = llvm::Expected;
+using JitWrappedAddress = void (*)(void **);
+using MaybeJitAddress = llvm::Expected;
+
+/// A simple object cache following Lang's LLJITWithObjectCache example and
+/// MLIR's SimpelObjectCache.
+class ObjectCache : public llvm::ObjectCache {
+public:
+ /// Cache the given `objBuffer` for the given module `m`. The buffer contains
+ /// the combiled objects of the module
+ void notifyObjectCompiled(const llvm::Module *m,
+ llvm::MemoryBufferRef objBuffer) override;
+
+ // Lookup the cache for the given module `m` or returen a nullptr.
+ std::unique_ptr getObject(const llvm::Module *m) override;
+
+ /// Dump cached object to output file `filename`.
+ void dumpToObjectFile(llvm::StringRef filename);
+
+private:
+ llvm::StringMap> cachedObjects;
+};
+
+class JIT {
+ const bool isLazy;
+
+ std::variant, std::unique_ptr>
+ engine;
+ std::unique_ptr cache;
+
+ llvm::JITEventListener *gdbListener;
+ /// Perf notification listener.
+ llvm::JITEventListener *perfListener;
+
+ llvm::orc::JITTargetMachineBuilder jtmb;
+
+ // We keep the jibDylibs for each name space in a mapping from the ns
+ // name to a vector of jitdylibs, the last element is always the newest
+ // jitDylib
+ //
+ // Questions:
+ // Is using string as the key good enough, what about an ID for NSs
+ // or even a pointer to the ns?
+ llvm::StringMap> jitDylibs;
+
+ void pushJITDylib(const llvm::StringRef &nsName, llvm::orc::JITDylib *l);
+ size_t getNumberOfJITDylibs(const llvm::StringRef &nsName);
+
+public:
+ JIT(llvm::orc::JITTargetMachineBuilder &&jtmb, Options &opts);
+ static MaybeJIT make(llvm::orc::JITTargetMachineBuilder &&jtmb);
+
+ /// Return a pointer to the most registered JITDylib of the given \p ns
+ ////name
+ llvm::orc::JITDylib *getLatestJITDylib(const llvm::StringRef &nsName);
+
+ /// Looks up a packed-argument function with the given sym name and returns a
+ /// pointer to it. Propagates errors in case of failure.
+ MaybeJitAddress lookup(const llvm::StringRef &nsName,
+ const llvm::StringRef &sym) const;
+
+ /// Invokes the function with the given name passing it the list of opaque
+ /// pointers containing the actual arguments.
+ llvm::Error
+ invokePacked(const llvm::StringRef &symbolName,
+ llvm::MutableArrayRef args = std::nullopt) const;
+
+ llvm::Error loadModule(const llvm::StringRef &nsName,
+ const llvm::StringRef &file);
+ void dumpToObjectFile(const llvm::StringRef &filename);
+};
+
+MaybeJIT makeJIT();
+} // namespace serene::jit
+#endif
diff --git a/serene/src/options.h b/serene/src/options.h
new file mode 100644
index 0000000..7094d51
--- /dev/null
+++ b/serene/src/options.h
@@ -0,0 +1,40 @@
+/* -*- C++ -*-
+ * Serene Programming Language
+ *
+ * Copyright (c) 2019-2023 Sameer Rahmani
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+namespace serene {
+/// Options describes the compiler options that can be passed to the
+/// compiler via command line. Anything that user should be able to
+/// tweak about the compiler has to end up here regardless of the
+/// different subsystem that might use it.
+struct Options {
+
+ /// Whether to use colors for the output or not
+ bool withColors = true;
+
+ // JIT related flags
+ bool JITenableObjectCache = true;
+ bool JITenableGDBNotificationListener = true;
+ bool JITenablePerfNotificationListener = true;
+ bool JITLazy = false;
+};
+
+} // namespace serene
+#endif
diff --git a/serene/src/serene.cpp b/serene/src/serene.cpp
index 1fa50fb..c1b575b 100644
--- a/serene/src/serene.cpp
+++ b/serene/src/serene.cpp
@@ -16,14 +16,19 @@
* along with this program. If not, see .
*/
-#include "serene/commands.h"
-#include "serene/config.h"
+#include "serene/config.h" // for SERENE_VERSION
-#include
-#include
+#include "commands/commands.h" // for cc, run
+#include <__fwd/string.h> // for string
-#include
-#include
+#include // for StringRef
+#include // for SubCommand, ParseCom...
+#include // for formatv, formatv_object
+#include // for provider_format_adapter
+
+#include // for strcmp
+#include // for basic_string
+#include // for tuple
namespace cl = llvm::cl;