Add support for a shared libserene and make it the default behaviour

This commit is contained in:
Sameer Rahmani 2021-10-10 21:34:14 +01:00
parent 54d85eac94
commit 7a456e2d54
14 changed files with 219 additions and 83 deletions

View File

@ -41,6 +41,15 @@ option(SERENE_ENABLE_THINLTO "Enable ThisLTO." ON)
option(SERENE_ENABLE_DOCS "Enable document generation" OFF)
option(SERENE_DISABLE_CCACHE "Disable automatic ccache integration" OFF)
# libserene
option(SERENE_SHARED_LIB "Build libserene as a shared library" ON)
option(SERENE_SHOW_MLIR_DIALECTS "Print out a list of MLIR dialect libs" OFF)
option(SERENE_SHOW_MLIR_TRANSFORMERS "Print out a list of MLIR dialect transformer libs" OFF)
option(SERENE_SHOW_LLVM_LIBS "Print all the llvm libs available" OFF)
option(SERENE_WITH_MLIR_CL_OPTION "Add support for controlling MLIR via command line options" OFF)
# Only do these if this is the main project, and not if it is included through add_subdirectory
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
## Settings =======================
@ -56,7 +65,6 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
# Setup the source locations
set(INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include)
set(SRC_DIR ${CMAKE_SOURCE_DIR}/src)
set(BIN_DIR ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_CXX_CLANG_TIDY clang-tidy)
# Let's ensure -std=c++xx instead of -std=g++xx
@ -65,7 +73,7 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/scripts/cmake")
set(MemoryCheckCommand "valgrind")
configure_file(${INCLUDE_DIR}/serene/config.h.in serene/config.h)
configure_file(${INCLUDE_DIR}/serene/config.h.in include/serene/config.h)
# Let's nicely support folders in IDEs
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
@ -97,12 +105,14 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
$<$<CONFIG:RELEASE>:-O3>
$<$<CONFIG:RELEASE>:-fmerge-all-constants>
$<$<CONFIG:RELEASE>:-flto=thin>
$<$<CONFIG:DEBUG>:-shared-libsan>
)
add_link_options(
# We enforce the lld linker
-fuse-ld=lld
-Wl,-gc-sections
$<$<CONFIG:DEBUG>:-shared-libsan>
$<$<CONFIG:RELEASE>:-fsanitize-address-globals-dead-stripping>
$<$<CONFIG:DEBUG>:-fsanitize=address>
$<$<CONFIG:RELEASE>:-flto=thin>
@ -111,6 +121,10 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
#--static
)
# Do we need to build serene as a shared lib? default is "yes"
if(SERENE_SHARED_LIB)
set(BUILD_SHARED_LIBS "${SERENE_SHARED_LIB}")
endif()
find_program(LLD_PROGRAM REQUIRED NAMES lld)
find_program(MLIRTBLGEN_PROGRAM REQUIRED NAMES mlir-tblgen)
@ -119,7 +133,7 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
add_link_options(-Wl,--build-id)
endif()
#TODO: Setup the THINLTO on release
if(SERENE_ENABLE_THINLTO)
endif()
@ -182,7 +196,7 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
link_directories(${LLVM_BUILD_LIBRARY_DIR})
add_definitions(${LLVM_DEFINITIONS})
llvm_map_components_to_libnames(llvm_libs support core irreader)
llvm_map_components_to_libnames(llvm_libs support)

18
builder
View File

@ -5,8 +5,9 @@ command=$1
export CC=$(which clang)
export CXX=$(which clang++)
#export CCACHE_SLOPPINESS="pch_defines,time_macros"
# Meke sure to use `lld` linker it faster and has a better UX
# TODO: Add sloppiness to the cmake list file as well
export CCACHE_SLOPPINESS="pch_defines,time_macros"
export ASAN_OPTIONS=check_initialization_order=1
LSAN_OPTIONS=suppressions=$(pwd)/.ignore_sanitize
export LSAN_OPTIONS
@ -16,8 +17,9 @@ export LSAN_OPTIONS
ROOT_DIR=$(pwd)
BUILD_DIR=$ROOT_DIR/build
ME=$(cd "$(dirname "$0")/." >/dev/null 2>&1 ; pwd -P)
# -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON
CMAKEARGS=" -DSERENE_CCACHE_DIR=~/.ccache"
CMAKEARGS_DEBUG=" -DCMAKE_BUILD_TYPE=Debug -DSERENE_WITH_MLIR_CL_OPTION=ON"
CMAKEARGS="-DSERENE_CCACHE_DIR=~/.ccache"
scanbuild=scan-build
@ -48,8 +50,8 @@ function build() {
pushed_build
echo "Running: "
echo "cmake -G Ninja $CMAKE_CCACHE $CMAKEARGS -DCMAKE_BUILD_TYPE=Debug \"$@\" \"$ROOT_DIR\""
cmake -G Ninja $CMAKEARGS -DCMAKE_BUILD_TYPE=Debug "$@" "$ROOT_DIR"
cmake --build .
cmake -G Ninja $CMAKEARGS $CMAKEARGS_DEBUG "$@" "$ROOT_DIR"
cmake --build . --verbose
popd_build
}
@ -80,7 +82,7 @@ function clean() {
function run() {
pushed_build
"$BUILD_DIR"/src/serenec/serenec "$@"
LD_PRELOAD=$(clang -print-file-name=libclang_rt.asan-x86_64.so) "$BUILD_DIR"/src/serenec/serenec "$@"
popd_build
}
@ -93,7 +95,7 @@ function memcheck() {
}
function run-tests() {
"$BUILD_DIR"/src/tests/tests
LD_PRELOAD=$(clang -print-file-name=libclang_rt.asan-x86_64.so) "$BUILD_DIR"/src/tests/tests
}
function tests() {

View File

@ -1,10 +1,11 @@
#ifndef CONFIG_H
#define CONFIG_H
// the configured options and settings for Tutorial
// the configured options and settings
#define SERENE_VERSION "@PROJECT_VERSION@"
#cmakedefine ENABLE_READER_LOG
#cmakedefine ENABLE_EXPR_LOG
#cmakedefine ENABLE_LOG
// Should we build the support for MLIR CL OPTIONS?
#cmakedefine SERENE_WITH_MLIR_CL_OPTION
#endif

View File

@ -27,6 +27,7 @@
#include "serene/diagnostics.h"
#include "serene/environment.h"
#include "serene/export.h"
#include "serene/namespace.h"
#include "serene/passes.h"
#include "serene/slir/dialect.h"
@ -66,7 +67,7 @@ enum class CompilationPhase {
O3,
};
class SereneContext {
class SERENE_EXPORT SereneContext {
struct Options {
/// Whether to use colors for the output or not
bool withColors = true;
@ -156,7 +157,7 @@ private:
/// Creates a new context object. Contexts are used through out the compilation
/// process to store the state
std::unique_ptr<SereneContext> makeSereneContext();
SERENE_EXPORT std::unique_ptr<SereneContext> makeSereneContext();
}; // namespace serene

View File

@ -25,7 +25,6 @@
#ifndef EXPRS_EXPRESSION_H
#define EXPRS_EXPRESSION_H
#include "mlir/IR/BuiltinOps.h"
#include "serene/context.h"
#include "serene/errors/error.h"
#include "serene/exprs/traits.h"
@ -33,6 +32,7 @@
#include "serene/utils.h"
#include <memory>
#include <mlir/IR/BuiltinOps.h>
namespace serene {
@ -140,7 +140,7 @@ Result<T, ErrorTree> makeErrorful(Args &&...args) {
/// Convert the given AST to string by calling the `toString` method
/// of each node.
std::string astToString(const Ast *);
SERENE_EXPORT std::string astToString(const Ast *);
/// Converts the given ExprType to string.
std::string stringifyExprType(ExprType);

View File

@ -30,6 +30,7 @@
#define SERENE_JIT_H
#include "serene/errors.h"
#include "serene/export.h"
#include "serene/slir/generatable.h"
#include "serene/utils.h"
@ -46,7 +47,7 @@
DEBUG_WITH_TYPE("JIT", llvm::dbgs() << "[JIT]: " << __VA_ARGS__ << "\n");
namespace serene {
class JIT;
class SERENE_EXPORT JIT;
using MaybeJIT = llvm::Optional<std::unique_ptr<JIT>>;

View File

@ -35,6 +35,7 @@
#define SERENE_NAMESPACE_H
#include "serene/environment.h"
#include "serene/export.h"
#include "serene/slir/generatable.h"
#include "serene/traits.h"
#include "serene/utils.h"
@ -70,7 +71,7 @@ using MaybeModuleOp = llvm::Optional<mlir::OwningOpRef<mlir::ModuleOp>>;
/// 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 `makeNamespace` function.
class Namespace {
class SERENE_EXPORT Namespace {
private:
SereneContext &ctx;
@ -135,9 +136,9 @@ using NSPtr = std::shared_ptr<Namespace>;
/// return a shared pointer to it in the given Serene context. If the
/// `setCurrent` argument is set to true, the created NS will become the curret
/// namespace in the context
NSPtr makeNamespace(SereneContext &ctx, llvm::StringRef name,
llvm::Optional<llvm::StringRef> filename,
bool setCurrent = true);
SERENE_EXPORT NSPtr makeNamespace(SereneContext &ctx, llvm::StringRef name,
llvm::Optional<llvm::StringRef> filename,
bool setCurrent = true);
} // namespace serene

View File

@ -22,12 +22,27 @@
* SOFTWARE.
*/
#ifndef SERENE_H
#define SERENE_H
#ifndef SERENE_SERENE_H
#define SERENE_SERENE_H
#include "serene/config.h"
#include "serene/context.h"
#include "serene/export.h"
#include "serene/source_mgr.h"
namespace serene {
namespace serene {} // 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);
} // namespace serene
#endif

View File

@ -28,10 +28,9 @@
#include "serene/namespace.h"
#include "serene/reader/location.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/ErrorHandling.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>
@ -63,7 +62,7 @@ class SereneContext;
///
/// Note: Unlike the original version, SourceMgr does not handle the diagnostics
/// and it uses the Serene's `DiagnosticEngine` for that matter.
class SourceMgr {
class SERENE_EXPORT SourceMgr {
public:
// TODO: Make it a vector of supported suffixes

View File

@ -25,8 +25,9 @@
#ifndef SERENE_UTILS_H
#define SERENE_UTILS_H
#include "llvm/Support/Error.h"
#include "serene/export.h"
#include <llvm/Support/Error.h>
#include <variant>
// Sometimes we need this to make both analyzer happy
@ -51,7 +52,7 @@ namespace serene {
/// In order check for a value being errorful or successful checkout the `ok`
/// method or simply use the value as a conditiona.
template <typename T, typename E = llvm::Error>
class Result {
class SERENE_EXPORT Result {
// The actual data container
std::variant<T, E> contents;

View File

@ -20,12 +20,18 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
option(SERENE_SHOW_MLIR_DIALECTS "Print out a list of MLIR dialect libs" OFF)
option(SERENE_SHOW_MLIR_TRANSFORMERS "Print out a list of MLIR dialect transformer libs" OFF)
option(SERENE_SHOW_LLVM_LIBS "Print all the llvm libs available" OFF)
#TODO: To support MacOS look into cmake's public headers
# https://cmake.org/cmake/help/latest/prop_tgt/PUBLIC_HEADER.html
# Hide all the symbols by default
if (NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET AND
NOT DEFINED CMAKE_VISIBILITY_INLINES_HIDDEN)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)
endif ()
add_library(libserene OBJECT
add_library(serene
exprs/symbol.cpp
exprs/list.cpp
exprs/number.cpp
@ -34,6 +40,7 @@ add_library(libserene OBJECT
exprs/fn.cpp
exprs/call.cpp
serene.cpp
context.cpp
namespace.cpp
jit.cpp
@ -59,18 +66,51 @@ add_library(libserene OBJECT
passes/to_llvm_dialect.cpp
)
add_dependencies(libserene SereneDialectGen)
# 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)
if (CPP_20_SUPPORT)
target_compile_features(libserene PUBLIC cxx_std_20)
else()
target_compile_features(libserene PUBLIC cxx_std_17)
set_target_properties(serene PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR})
# 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 SereneDialectGen)
# We need this directory, and users of our library will need it too
target_include_directories(libserene PUBLIC ${INCLUDE_DIR})
target_include_directories(libserene PUBLIC ${PROJECT_BINARY_DIR})
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)
@ -104,7 +144,7 @@ set(serene_lib_dialects_in_use
set(serene_lib_transformers_in_use
MLIRStandardToLLVM)
set(static_deps
target_link_libraries(serene PRIVATE
MLIRIR
MLIRPass
MLIRTransforms
@ -112,20 +152,14 @@ set(static_deps
${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
#TODO: Remove this lib, we're just using one func
MLIRExecutionEngine
LLVMX86AsmParser
${llvm_libs})
set_target_properties(libserene PROPERTIES StaticDeps "${static_deps}")
# 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 libserene has to
# use `Serene::lib` alias instead
add_library(Serene::lib ALIAS libserene)

84
src/libserene/serene.cpp Normal file
View File

@ -0,0 +1,84 @@
/*
* Serene programming language.
*
* Copyright (c) 2020 Sameer Rahmani <lxsameer@gnu.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "serene/serene.h"
#include <llvm/Support/TargetSelect.h>
namespace serene {
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
}
} // namespace serene

View File

@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
add_executable(serenec serenec.cpp $<TARGET_OBJECTS:Serene::lib>)
add_executable(serenec serenec.cpp)
if (CPP_20_SUPPORT)
target_compile_features(serenec PRIVATE cxx_std_20)
@ -28,12 +28,12 @@ else()
target_compile_features(serenec PRIVATE cxx_std_17)
endif()
get_property(serene_lib_deps TARGET Serene::lib PROPERTY StaticDeps)
target_link_libraries(serenec
PRIVATE
${serene_lib_deps}
LLVMX86AsmParser
Serene::lib
MLIRPass
LLVMTarget
LLVMOption

View File

@ -22,8 +22,6 @@
* SOFTWARE.
*/
#include "serene/config.h"
#include "serene/context.h"
#include "serene/jit.h"
#include "serene/namespace.h"
#include "serene/reader/location.h"
@ -47,7 +45,6 @@
#include <llvm/Support/Host.h>
#include <llvm/Support/Path.h>
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/TargetOptions.h>
@ -114,12 +111,6 @@ static cl::opt<enum Action> emitAction(
);
llvm::cl::OptionCategory clOptionsCategory{"Discovery options"};
static cl::list<std::string>
loadPaths("l", cl::desc("The load path to use for compilation."),
llvm::cl::ZeroOrMore, llvm::cl::MiscFlags::PositionalEatsArgs,
llvm::cl::cat(clOptionsCategory));
int dumpAsObject(Namespace &ns) {
// TODO: Move the compilation process to the Namespace class
auto maybeModule = ns.compileToLLVM();
@ -224,23 +215,15 @@ int dumpAsObject(Namespace &ns) {
};
int main(int argc, char *argv[]) {
// mlir::registerAsmPrinterCLOptions();
mlir::registerMLIRContextCLOptions();
mlir::registerPassManagerCLOptions();
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllAsmPrinters();
initCompiler();
registerSereneCLOptions();
cl::ParseCommandLineOptions(argc, argv, banner);
auto ctx = makeSereneContext();
auto userNS = makeNamespace(*ctx, "user", llvm::None);
// TODO: We might want to find a better place for this
applyPassManagerCLOptions(ctx->pm);
ctx->sourceManager.setLoadPaths(loadPaths);
applySereneCLOptions(*ctx);
// TODO: handle the outputDir by not forcing it. it should be
// default to the current working dir