From 166e1ad455d3a9a3f912af484ed4d12e8c0a6959 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Tue, 30 Nov 2021 11:50:28 -0500 Subject: [PATCH] JIT (#6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add JIT engine * More engine updates * Fix takeModule() * Parser JIT support [wip] * Fix class definition * Parser JIT support [wip] * Parser JIT support [wip] * Fix LLVM context conflict * Parser JIT support [wip] * Fix JIT engine * Parser JIT support [wip] * Doc fix * JIT fix * Fix JIT exceptions * Refactor * Add JIT status codes * Add compiler class * Better logging * Better logging * Update .gitignore * Add Jupyter Xeus support * Update JIT * Remove print * Update errors * Fix assert * Fix asserts * Update docgen signature * Update file system interface * Update plugin errors * Use numeric_limits * JIT refactor [wip] * Set JIT flag on LLVMVisitor * Update module opt * JIT imports * First Jupyter integration * Update JIT API to return outputs as string * Capture runtime exception output * Fix libbacktrace build * Initial Jupyter support * Format * Fix print * Support run-mode backtraces * Fix multithreaded backtrace * Update backtraces * Upgrade OpenMP * Add libunwind * Fix build * Fix build * Fix build * Fix build * Fix OpenMP & tests * Use libbacktrace instead of libunwind * Add debug listener * Remove unused include * Remove unused class * Fix backtraces * Update backtrace config * Fix debug info generation * Refactor backtraces * Fix ASAN flag * Fix JIT * Fix JIT backtraces * Fix JIT backtrace * Fix Jupyter, fix xeus build flags * Fix JIT output capture * Fix Jupyter * Fix Jupyter Python support * Add __repr_pretty__ support * Update JIT output capturing * Better backtrace method names * Support plugins in JIT mode Co-authored-by: Ibrahim Numanagić --- .gitignore | 4 + CMakeLists.txt | 55 +- cmake/backtrace-config.h.in | 3 + cmake/deps.cmake | 85 +- codon/app/main.cpp | 244 ++- codon/compiler/compiler.cpp | 116 ++ codon/compiler/compiler.h | 55 + codon/compiler/debug_listener.cpp | 73 + codon/compiler/debug_listener.h | 49 + codon/compiler/engine.cpp | 93 ++ codon/compiler/engine.h | 70 + codon/compiler/error.cpp | 15 + codon/compiler/error.h | 150 ++ codon/compiler/jit.cpp | 166 ++ codon/compiler/jit.h | 37 + .../{sir/llvm => compiler}/memory_manager.cpp | 2 - codon/{sir/llvm => compiler}/memory_manager.h | 2 - codon/dsl/plugins.cpp | 38 +- codon/dsl/plugins.h | 6 +- codon/parser/ast/stmt.cpp | 12 +- codon/parser/ast/stmt.h | 1 + codon/parser/ast/types.cpp | 4 +- codon/parser/cache.cpp | 3 +- codon/parser/cache.h | 14 +- codon/parser/common.cpp | 130 +- codon/parser/common.h | 2 + codon/parser/parser.cpp | 166 -- codon/parser/parser.h | 22 - codon/parser/peg/peg.cpp | 59 +- codon/parser/peg/peg.h | 12 +- codon/parser/peg/rules.h | 9 +- codon/parser/visitors/doc/doc.cpp | 12 +- codon/parser/visitors/doc/doc.h | 2 +- codon/parser/visitors/format/format.cpp | 2 +- codon/parser/visitors/format/format.h | 8 +- codon/parser/visitors/simplify/simplify.cpp | 13 +- codon/parser/visitors/simplify/simplify.h | 12 +- .../parser/visitors/simplify/simplify_ctx.cpp | 2 +- codon/parser/visitors/simplify/simplify_ctx.h | 4 +- .../visitors/simplify/simplify_stmt.cpp | 17 +- codon/parser/visitors/translate/translate.cpp | 35 +- codon/parser/visitors/translate/translate.h | 2 +- .../visitors/translate/translate_ctx.cpp | 6 +- .../parser/visitors/translate/translate_ctx.h | 5 +- codon/parser/visitors/typecheck/typecheck.cpp | 10 +- codon/parser/visitors/typecheck/typecheck.h | 2 +- .../visitors/typecheck/typecheck_ctx.cpp | 8 +- .../parser/visitors/typecheck/typecheck_ctx.h | 4 +- .../visitors/typecheck/typecheck_expr.cpp | 5 +- .../visitors/typecheck/typecheck_infer.cpp | 26 +- codon/runtime/exc.cpp | 121 +- codon/runtime/lib.cpp | 26 +- codon/runtime/lib.h | 51 +- codon/sir/func.h | 14 +- codon/sir/llvm/llvisitor.cpp | 1375 +++++++++-------- codon/sir/llvm/llvisitor.h | 156 +- codon/sir/llvm/llvm.h | 2 + codon/sir/llvm/optimize.cpp | 40 +- codon/sir/llvm/optimize.h | 3 +- codon/sir/module.cpp | 3 +- codon/sir/module.h | 12 +- codon/sir/transform/cleanup/global_demote.cpp | 2 +- codon/sir/transform/folding/const_prop.h | 1 + codon/sir/transform/manager.cpp | 5 +- codon/sir/util/cloning.cpp | 2 +- codon/sir/util/matching.cpp | 2 +- codon/util/common.cpp | 61 +- codon/util/common.h | 109 +- .../jupyter/kernels/codon/kernel.json.in | 9 + extra/jupyter/src/codon.cpp | 102 ++ extra/jupyter/src/codon.h | 40 + stdlib/internal/__init__.codon | 1 + stdlib/internal/builtin.codon | 11 + stdlib/internal/dlopen.codon | 9 +- stdlib/internal/python.codon | 71 +- test/main.cpp | 41 +- test/sir/func.cpp | 6 +- test/sir/util/matching.cpp | 6 +- test/types.cpp | 1 - 79 files changed, 2766 insertions(+), 1388 deletions(-) create mode 100644 codon/compiler/compiler.cpp create mode 100644 codon/compiler/compiler.h create mode 100644 codon/compiler/debug_listener.cpp create mode 100644 codon/compiler/debug_listener.h create mode 100644 codon/compiler/engine.cpp create mode 100644 codon/compiler/engine.h create mode 100644 codon/compiler/error.cpp create mode 100644 codon/compiler/error.h create mode 100644 codon/compiler/jit.cpp create mode 100644 codon/compiler/jit.h rename codon/{sir/llvm => compiler}/memory_manager.cpp (96%) rename codon/{sir/llvm => compiler}/memory_manager.h (95%) delete mode 100644 codon/parser/parser.cpp delete mode 100644 codon/parser/parser.h create mode 100644 extra/jupyter/share/jupyter/kernels/codon/kernel.json.in create mode 100644 extra/jupyter/src/codon.cpp create mode 100644 extra/jupyter/src/codon.h diff --git a/.gitignore b/.gitignore index ffdbdd1a..ff18d965 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ *.pyc build/ build_*/ +extra/jupyter/build/ # Packages # ############ @@ -50,3 +51,6 @@ Thumbs.db .idea .mypy_cache .vscode + +extra/jupyter/share/jupyter/kernels/codon/kernel.json +scratch.seq diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d8db2a6..fa87c24c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,17 +7,20 @@ configure_file("${PROJECT_SOURCE_DIR}/cmake/config.h.in" configure_file("${PROJECT_SOURCE_DIR}/cmake/config.py.in" "${PROJECT_SOURCE_DIR}/docs/sphinx/config/config.py") +option(CODON_JUPYTER "build Codon Jupyter server" OFF) +if(CODON_JUPYTER) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/extra/jupyter/share/jupyter/kernels/codon/kernel.json.in" + "${CMAKE_CURRENT_SOURCE_DIR}/extra/jupyter/share/jupyter/kernels/codon/kernel.json" + ) +endif() + set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden -pedantic -Wall -Wno-return-type-c-linkage -Wno-gnu-zero-variadic-macro-arguments") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden -pedantic -Wno-return-type-c-linkage -Wno-gnu-zero-variadic-macro-arguments") set(CMAKE_CXX_FLAGS_DEBUG "-g -fno-limit-debug-info") set(CMAKE_CXX_FLAGS_RELEASE "-O3") include_directories(.) -if(ASAN) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") - set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") -endif() - set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) find_package(LLVM REQUIRED) @@ -70,14 +73,23 @@ else() $ -Wl,--no-whole-archive) endif() +if(ASAN) + target_compile_options(codonrt PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address") + target_link_libraries(codonrt PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address") +endif() add_custom_command(TARGET codonrt POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${CMAKE_BINARY_DIR}) # Codon compiler library include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) - set(CODON_HPPFILES + codon/compiler/compiler.h + codon/compiler/debug_listener.h + codon/compiler/engine.h + codon/compiler/error.h + codon/compiler/jit.h + codon/compiler/memory_manager.h codon/dsl/dsl.h codon/dsl/plugins.h codon/parser/ast.h @@ -89,7 +101,6 @@ set(CODON_HPPFILES codon/parser/ctx.h codon/parser/peg/peg.h codon/parser/peg/rules.h - codon/parser/parser.h codon/parser/visitors/doc/doc.h codon/parser/visitors/format/format.h codon/parser/visitors/simplify/simplify.h @@ -122,7 +133,6 @@ set(CODON_HPPFILES codon/sir/llvm/coro/Coroutines.h codon/sir/llvm/llvisitor.h codon/sir/llvm/llvm.h - codon/sir/llvm/memory_manager.h codon/sir/llvm/optimize.h codon/sir/module.h codon/sir/sir.h @@ -198,8 +208,15 @@ set(CODON_HPPFILES codon/util/toml++/toml_default_formatter.h codon/util/toml++/toml_node.h codon/util/toml++/toml_parser.hpp - codon/util/toml++/toml_utf8_streams.h) + codon/util/toml++/toml_utf8_streams.h + extra/jupyter/src/codon.h) set(CODON_CPPFILES + codon/compiler/compiler.cpp + codon/compiler/debug_listener.cpp + codon/compiler/engine.cpp + codon/compiler/error.cpp + codon/compiler/jit.cpp + codon/compiler/memory_manager.cpp codon/dsl/plugins.cpp codon/parser/ast/expr.cpp codon/parser/ast/stmt.cpp @@ -207,7 +224,6 @@ set(CODON_CPPFILES codon/parser/cache.cpp codon/parser/common.cpp codon/parser/peg/peg.cpp - codon/parser/parser.cpp codon/parser/visitors/doc/doc.cpp codon/parser/visitors/format/format.cpp codon/parser/visitors/simplify/simplify.cpp @@ -242,7 +258,6 @@ set(CODON_CPPFILES codon/sir/llvm/coro/CoroSplit.cpp codon/sir/llvm/coro/Coroutines.cpp codon/sir/llvm/llvisitor.cpp - codon/sir/llvm/memory_manager.cpp codon/sir/llvm/optimize.cpp codon/sir/module.cpp codon/sir/transform/cleanup/canonical.cpp @@ -272,9 +287,19 @@ set(CODON_CPPFILES codon/sir/value.cpp codon/sir/var.cpp codon/util/common.cpp - codon/util/fmt/format.cpp) + codon/util/fmt/format.cpp + extra/jupyter/src/codon.cpp) add_library(codonc SHARED ${CODON_HPPFILES}) target_sources(codonc PRIVATE ${CODON_CPPFILES} codon_rules.cpp omp_rules.cpp) +if(CODON_JUPYTER) + add_compile_definitions(CODON_JUPYTER) + add_dependencies(codonc xeus-static nlohmann_json) + target_link_libraries(codonc PRIVATE xeus-static) +endif() +if(ASAN) + target_compile_options(codonc PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address") + target_link_libraries(codonc PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address") +endif() if(CMAKE_BUILD_TYPE MATCHES Debug) set_source_files_properties(codon_rules.cpp codon/parser/peg/peg.cpp PROPERTIES COMPILE_FLAGS "-O2") endif() @@ -301,6 +326,7 @@ llvm_map_components_to_libnames(LLVM_LIBS Remarks ScalarOpts Support + Symbolize Target TransformUtils Vectorize @@ -328,7 +354,7 @@ add_dependencies(headers codonrt codonc) # Codon command-line tool add_executable(codon codon/app/main.cpp) -target_link_libraries(codon ${STATIC_LIBCPP} codonc Threads::Threads) +target_link_libraries(codon PUBLIC ${STATIC_LIBCPP} codonc Threads::Threads) # Codon test # Download and unpack googletest at configure time @@ -359,3 +385,4 @@ add_executable(codon_test ${CODON_TEST_CPPFILES}) target_include_directories(codon_test PRIVATE test/sir "${gc_SOURCE_DIR}/include") target_link_libraries(codon_test codonc codonrt gtest_main) target_compile_definitions(codon_test PRIVATE TEST_DIR="${CMAKE_CURRENT_SOURCE_DIR}/test") + diff --git a/cmake/backtrace-config.h.in b/cmake/backtrace-config.h.in index 361796b3..27b277ab 100644 --- a/cmake/backtrace-config.h.in +++ b/cmake/backtrace-config.h.in @@ -46,6 +46,9 @@ /* Define to 1 if you have the `lstat' function. */ #cmakedefine HAVE_LSTAT 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_MACH_O_DYLD_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MEMORY_H 1 diff --git a/cmake/deps.cmake b/cmake/deps.cmake index 77541cf2..dfa45958 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -66,7 +66,6 @@ if(bdwgc_ADDED) endif() CPMAddPackage( - NAME openmp GITHUB_REPOSITORY "llvm-mirror/openmp" VERSION 9.0 GIT_TAG release_90 @@ -79,29 +78,10 @@ CPMAddPackage( GIT_TAG d0f5e95a87a4d3e0a1ed6c069b5dae7cbab3ed2a DOWNLOAD_ONLY YES) if(backtrace_ADDED) - # https://go.googlesource.com/gollvm/+/refs/heads/master/cmake/modules/LibbacktraceUtils.cmake - set(BACKTRACE_ELF_SIZE 64) - set(HAVE_GETIPINFO 1) - set(BACKTRACE_USES_MALLOC 0) - set(BACKTRACE_SUPPORTS_THREADS 1) - set(BACKTRACE_SUPPORTS_DATA 1) - if(HAVE_SYNC_BOOL_COMPARE_AND_SWAP_4) - if(HAVE_SYNC_BOOL_COMPARE_AND_SWAP_8) - set(HAVE_SYNC_FUNCTIONS 1) - endif() - endif() - # Generate backtrace-supported.h based on the above. - configure_file( - ${CMAKE_SOURCE_DIR}/cmake/backtrace-supported.h.in - ${backtrace_SOURCE_DIR}/backtrace-supported.h) - configure_file( - ${CMAKE_SOURCE_DIR}/cmake/backtrace-config.h.in - ${backtrace_SOURCE_DIR}/config.h) - add_library(backtrace STATIC + set(backtrace_SOURCES "${backtrace_SOURCE_DIR}/atomic.c" "${backtrace_SOURCE_DIR}/backtrace.c" "${backtrace_SOURCE_DIR}/dwarf.c" - "${backtrace_SOURCE_DIR}/elf.c" "${backtrace_SOURCE_DIR}/fileline.c" "${backtrace_SOURCE_DIR}/mmapio.c" "${backtrace_SOURCE_DIR}/mmap.c" @@ -110,8 +90,71 @@ if(backtrace_ADDED) "${backtrace_SOURCE_DIR}/simple.c" "${backtrace_SOURCE_DIR}/sort.c" "${backtrace_SOURCE_DIR}/state.c") + + # https://go.googlesource.com/gollvm/+/refs/heads/master/cmake/modules/LibbacktraceUtils.cmake + set(BACKTRACE_SUPPORTED 1) + set(BACKTRACE_ELF_SIZE 64) + set(HAVE_GETIPINFO 1) + set(BACKTRACE_USES_MALLOC 1) + set(BACKTRACE_SUPPORTS_THREADS 1) + set(BACKTRACE_SUPPORTS_DATA 1) + set(HAVE_SYNC_FUNCTIONS 1) + if(APPLE) + set(HAVE_MACH_O_DYLD_H 1) + list(APPEND backtrace_SOURCES "${backtrace_SOURCE_DIR}/macho.c") + else() + set(HAVE_MACH_O_DYLD_H 0) + list(APPEND backtrace_SOURCES "${backtrace_SOURCE_DIR}/elf.c") + endif() + # Generate backtrace-supported.h based on the above. + configure_file( + ${CMAKE_SOURCE_DIR}/cmake/backtrace-supported.h.in + ${backtrace_SOURCE_DIR}/backtrace-supported.h) + configure_file( + ${CMAKE_SOURCE_DIR}/cmake/backtrace-config.h.in + ${backtrace_SOURCE_DIR}/config.h) + add_library(backtrace STATIC ${backtrace_SOURCES}) target_include_directories(backtrace BEFORE PRIVATE "${backtrace_SOURCE_DIR}") set_target_properties(backtrace PROPERTIES COMPILE_FLAGS "-funwind-tables -D_GNU_SOURCE" POSITION_INDEPENDENT_CODE ON) endif() + +if(CODON_JUPYTER) + CPMAddPackage( + NAME libzmq + VERSION 4.3.4 + URL https://github.com/zeromq/libzmq/releases/download/v4.3.4/zeromq-4.3.4.tar.gz + OPTIONS "WITH_PERF_TOOL OFF" + "ZMQ_BUILD_TESTS OFF" + "ENABLE_CPACK OFF" + "BUILD_SHARED ON" + "WITH_LIBSODIUM OFF") + CPMAddPackage( + NAME cppzmq + URL https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.tar.gz + VERSION 4.8.1 + OPTIONS "CPPZMQ_BUILD_TESTS OFF") + CPMAddPackage( + NAME xtl + GITHUB_REPOSITORY "xtensor-stack/xtl" + VERSION 0.7.3 + GIT_TAG 0.7.3 + OPTIONS "BUILD_TESTS OFF") + CPMAddPackage( + NAME json + GITHUB_REPOSITORY "nlohmann/json" + VERSION 3.10.4) + CPMAddPackage( + NAME xeus + GITHUB_REPOSITORY "jupyter-xeus/xeus" + VERSION 2.2.0 + GIT_TAG 2.2.0 + PATCH_COMMAND sed -i bak "s/-Wunused-parameter -Wextra -Wreorder//g" CMakeLists.txt + OPTIONS "BUILD_EXAMPLES OFF" + "XEUS_BUILD_SHARED_LIBS OFF" + "XEUS_STATIC_DEPENDENCIES ON") + if (xeus_ADDED) + install(TARGETS nlohmann_json EXPORT xeus-targets) + endif() +endif() \ No newline at end of file diff --git a/codon/app/main.cpp b/codon/app/main.cpp index 5b3b730c..0d8d652f 100644 --- a/codon/app/main.cpp +++ b/codon/app/main.cpp @@ -1,17 +1,18 @@ -#include "codon/dsl/plugins.h" -#include "codon/parser/parser.h" -#include "codon/sir/llvm/llvisitor.h" -#include "codon/sir/transform/manager.h" -#include "codon/sir/transform/pass.h" -#include "codon/util/common.h" -#include "llvm/Support/CommandLine.h" #include -#include +#include #include +#include +#include #include #include #include +#include "codon/compiler/compiler.h" +#include "codon/compiler/error.h" +#include "codon/compiler/jit.h" +#include "codon/util/common.h" +#include "llvm/Support/CommandLine.h" + namespace { void versMsg(llvm::raw_ostream &out) { out << CODON_VERSION_MAJOR << "." << CODON_VERSION_MINOR << "." << CODON_VERSION_PATCH @@ -46,23 +47,41 @@ std::string makeOutputFilename(const std::string &filename, return filename + extension; } +void display(const codon::error::ParserErrorInfo &e) { + for (auto &msg : e) { + codon::compilationError(msg.getMessage(), msg.getFile(), msg.getLine(), + msg.getColumn(), + /*terminate=*/false); + } +} + enum BuildKind { LLVM, Bitcode, Object, Executable, Detect }; enum OptMode { Debug, Release }; -struct ProcessResult { - std::unique_ptr pm; - std::unique_ptr plm; - std::unique_ptr visitor; - std::string input; -}; } // namespace int docMode(const std::vector &args, const std::string &argv0) { llvm::cl::ParseCommandLineOptions(args.size(), args.data()); - codon::generateDocstr(argv0); + std::vector files; + for (std::string line; std::getline(std::cin, line);) + files.push_back(line); + + auto compiler = std::make_unique(args[0]); + bool failed = false; + auto result = compiler->docgen(files); + llvm::handleAllErrors(result.takeError(), + [&failed](const codon::error::ParserErrorInfo &e) { + display(e); + failed = true; + }); + if (failed) + return EXIT_FAILURE; + + fmt::print("{}\n", *result); return EXIT_SUCCESS; } -ProcessResult processSource(const std::vector &args) { +std::unique_ptr processSource(const std::vector &args, + bool standalone) { llvm::cl::opt input(llvm::cl::Positional, llvm::cl::desc(""), llvm::cl::init("-")); llvm::cl::opt optMode( @@ -80,8 +99,12 @@ ProcessResult processSource(const std::vector &args) { "disable-opt", llvm::cl::desc("Disable the specified IR optimization")); llvm::cl::list plugins("plugin", llvm::cl::desc("Load specified plugin")); + llvm::cl::opt log("log", llvm::cl::desc("Enable given log streams")); llvm::cl::ParseCommandLineOptions(args.size(), args.data()); + codon::getLogger().parse(log); + if (auto *d = getenv("CODON_DEBUG")) + codon::getLogger().parse(std::string(d)); auto &exts = supportedExtensions(); if (input != "-" && std::find_if(exts.begin(), exts.end(), [&](auto &ext) { @@ -110,54 +133,37 @@ ProcessResult processSource(const std::vector &args) { } const bool isDebug = (optMode == OptMode::Debug); - auto t = std::chrono::high_resolution_clock::now(); - std::vector disabledOptsVec(disabledOpts); - auto pm = - std::make_unique(isDebug, disabledOptsVec); - auto plm = std::make_unique(); + auto compiler = std::make_unique(args[0], isDebug, disabledOptsVec); + compiler->getLLVMVisitor()->setStandalone(standalone); - LOG_TIME("[T] ir-setup = {:.1f}", - std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - t) - .count() / - 1000.0); - - // load other plugins + // load plugins for (const auto &plugin : plugins) { - std::string errMsg; - if (!plm->load(plugin, &errMsg)) - codon::compilationError(errMsg); + bool failed = false; + llvm::handleAllErrors( + compiler->load(plugin), [&failed](const codon::error::PluginErrorInfo &e) { + codon::compilationError(e.getMessage(), /*file=*/"", /*line=*/0, /*col=*/0, + /*terminate=*/false); + failed = true; + }); + if (failed) + return {}; } - // add all IR passes - for (const auto *plugin : *plm) { - plugin->dsl->addIRPasses(pm.get(), isDebug); + + bool failed = false; + llvm::handleAllErrors(compiler->parseFile(input, /*isTest=*/0, defmap), + [&failed](const codon::error::ParserErrorInfo &e) { + display(e); + failed = true; + }); + if (failed) + return {}; + + { + TIME("compile"); + llvm::cantFail(compiler->compile()); } - t = std::chrono::high_resolution_clock::now(); - - auto *module = codon::parse(args[0], input.c_str(), /*code=*/"", /*isCode=*/false, - /*isTest=*/false, /*startLine=*/0, defmap, plm.get()); - if (!module) - return {{}, {}}; - - pm->run(module); - LOG_TIME("[T] ir-opt = {:.1f}", std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - t) - .count() / - 1000.0); - - t = std::chrono::high_resolution_clock::now(); - auto visitor = std::make_unique(isDebug); - visitor->setPluginManager(plm.get()); - visitor->visit(module); - LOG_TIME("[T] ir-visitor = {:.1f}", - std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - t) - .count() / - 1000.0); - if (_dbg_level) - visitor->dump(); - return {std::move(pm), std::move(plm), std::move(visitor), input}; + return compiler; } int runMode(const std::vector &args) { @@ -165,19 +171,72 @@ int runMode(const std::vector &args) { "l", llvm::cl::desc("Load and link the specified library")); llvm::cl::list seqArgs(llvm::cl::ConsumeAfter, llvm::cl::desc("...")); - auto start_t = std::chrono::high_resolution_clock::now(); - auto result = processSource(args); - if (!result.visitor) + auto compiler = processSource(args, /*standalone=*/false); + if (!compiler) return EXIT_FAILURE; std::vector libsVec(libs); std::vector argsVec(seqArgs); - argsVec.insert(argsVec.begin(), result.input); - LOG_USER("compiler took: {:.2f} seconds", - std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_t) - .count() / - 1000.0); - result.visitor->run(argsVec, libsVec); + argsVec.insert(argsVec.begin(), compiler->getInput()); + compiler->getLLVMVisitor()->run(argsVec, libsVec); + return EXIT_SUCCESS; +} + +namespace { +std::string jitExec(codon::jit::JIT *jit, const std::string &code) { + auto result = jit->exec(code); + if (auto err = result.takeError()) { + std::string output; + llvm::handleAllErrors( + std::move(err), [](const codon::error::ParserErrorInfo &e) { display(e); }, + [&output](const codon::error::RuntimeErrorInfo &e) { + std::stringstream buf; + buf << e.getOutput(); + buf << "\n\033[1mBacktrace:\033[0m\n"; + for (const auto &line : e.getBacktrace()) { + buf << " " << line << "\n"; + } + output = buf.str(); + }); + return output; + } + return *result; +} +} // namespace + +int jitMode(const std::vector &args) { + llvm::cl::list plugins("plugin", + llvm::cl::desc("Load specified plugin")); + llvm::cl::ParseCommandLineOptions(args.size(), args.data()); + codon::jit::JIT jit(args[0]); + + // load plugins + for (const auto &plugin : plugins) { + bool failed = false; + llvm::handleAllErrors(jit.getCompiler()->load(plugin), + [&failed](const codon::error::PluginErrorInfo &e) { + codon::compilationError(e.getMessage(), /*file=*/"", + /*line=*/0, /*col=*/0, + /*terminate=*/false); + failed = true; + }); + if (failed) + return EXIT_FAILURE; + } + + llvm::cantFail(jit.init()); + fmt::print(">>> Codon JIT v{} <<<\n", CODON_VERSION); + std::string code; + for (std::string line; std::getline(std::cin, line);) { + if (line != "#%%") { + code += line + "\n"; + } else { + fmt::print("{}\n\n[done]\n\n", jitExec(&jit, code)); + code = ""; + fflush(stdout); + } + } + if (!code.empty()) + fmt::print("{}\n\n[done]\n\n", jitExec(&jit, code)); return EXIT_SUCCESS; } @@ -199,12 +258,12 @@ int buildMode(const std::vector &args) { "Write compiled output to specified file. Supported extensions: " "none (executable), .o (object file), .ll (LLVM IR), .bc (LLVM bitcode)")); - auto result = processSource(args); - if (!result.visitor) + auto compiler = processSource(args, /*standalone=*/true); + if (!compiler) return EXIT_FAILURE; std::vector libsVec(libs); - if (output.empty() && result.input == "-") + if (output.empty() && compiler->getInput() == "-") codon::compilationError("output file must be specified when reading from stdin"); std::string extension; switch (buildKind) { @@ -222,33 +281,50 @@ int buildMode(const std::vector &args) { extension = ""; break; default: - assert(0); + seqassert(0, "unknown build kind"); } const std::string filename = - output.empty() ? makeOutputFilename(result.input, extension) : output; + output.empty() ? makeOutputFilename(compiler->getInput(), extension) : output; switch (buildKind) { case BuildKind::LLVM: - result.visitor->writeToLLFile(filename); + compiler->getLLVMVisitor()->writeToLLFile(filename); break; case BuildKind::Bitcode: - result.visitor->writeToBitcodeFile(filename); + compiler->getLLVMVisitor()->writeToBitcodeFile(filename); break; case BuildKind::Object: - result.visitor->writeToObjectFile(filename); + compiler->getLLVMVisitor()->writeToObjectFile(filename); break; case BuildKind::Executable: - result.visitor->writeToExecutable(filename, libsVec); + compiler->getLLVMVisitor()->writeToExecutable(filename, libsVec); break; case BuildKind::Detect: - result.visitor->compile(filename, libsVec); + compiler->getLLVMVisitor()->compile(filename, libsVec); break; default: - assert(0); + seqassert(0, "unknown build kind"); } return EXIT_SUCCESS; } +#ifdef CODON_JUPYTER +namespace codon { +int startJupyterKernel(const std::string &argv0, const std::string &configPath); +} +#endif +int jupyterMode(const std::vector &args) { +#ifdef CODON_JUPYTER + int code = codon::startJupyterKernel(args[0], args.size() > 1 ? std::string(args[1]) + : "connection.json"); + return code; +#else + fmt::print("Jupyter support not included. Please recompile with " + "-DCODON_JUPYTER."); + return EXIT_FAILURE; +#endif +} + void showCommandsAndExit() { codon::compilationError("Available commands: seqc "); } @@ -290,5 +366,13 @@ int main(int argc, const char **argv) { args[0] = argv0.data(); return docMode(args, oldArgv0); } + if (mode == "jit") { + args[0] = argv0.data(); + return jitMode(args); + } + if (mode == "jupyter") { + args[0] = argv0.data(); + return jupyterMode(args); + } return otherMode({argv, argv + argc}); } diff --git a/codon/compiler/compiler.cpp b/codon/compiler/compiler.cpp new file mode 100644 index 00000000..55e8153f --- /dev/null +++ b/codon/compiler/compiler.cpp @@ -0,0 +1,116 @@ +#include "compiler.h" + +#include + +#include "codon/parser/cache.h" +#include "codon/parser/peg/peg.h" +#include "codon/parser/visitors/doc/doc.h" +#include "codon/parser/visitors/format/format.h" +#include "codon/parser/visitors/simplify/simplify.h" +#include "codon/parser/visitors/translate/translate.h" +#include "codon/parser/visitors/typecheck/typecheck.h" + +namespace codon { + +Compiler::Compiler(const std::string &argv0, bool debug, + const std::vector &disabledPasses, bool isTest) + : argv0(argv0), debug(debug), input(), plm(std::make_unique()), + cache(std::make_unique(argv0)), + module(std::make_unique()), + pm(std::make_unique(debug && !isTest, + disabledPasses)), + llvisitor(std::make_unique()) { + cache->module = module.get(); + module->setCache(cache.get()); + llvisitor->setDebug(debug); + llvisitor->setPluginManager(plm.get()); +} + +llvm::Error Compiler::load(const std::string &plugin) { + auto result = plm->load(plugin); + if (auto err = result.takeError()) + return err; + + auto *p = *result; + if (!p->info.stdlibPath.empty()) { + cache->pluginImportPaths.push_back(p->info.stdlibPath); + } + for (auto &kw : p->dsl->getExprKeywords()) { + cache->customExprStmts[kw.keyword] = kw.callback; + } + for (auto &kw : p->dsl->getBlockKeywords()) { + cache->customBlockStmts[kw.keyword] = {kw.hasExpr, kw.callback}; + } + p->dsl->addIRPasses(pm.get(), debug); + return llvm::Error::success(); +} + +llvm::Error +Compiler::parse(bool isCode, const std::string &file, const std::string &code, + int startLine, int testFlags, + const std::unordered_map &defines) { + input = file; + std::string abspath = + (file != "-") ? std::filesystem::absolute(std::filesystem::path(file)).string() + : file; + try { + Timer t1("parse"); + ast::StmtPtr codeStmt = isCode + ? ast::parseCode(cache.get(), abspath, code, startLine) + : ast::parseFile(cache.get(), abspath); + t1.log(); + + cache->module0 = file; + if (testFlags) + cache->testFlags = testFlags; + + Timer t2("simplify"); + auto transformed = ast::SimplifyVisitor::apply(cache.get(), std::move(codeStmt), + abspath, defines, (testFlags > 1)); + t2.log(); + + Timer t3("typecheck"); + auto typechecked = + ast::TypecheckVisitor::apply(cache.get(), std::move(transformed)); + t3.log(); + + Timer t4("translate"); + ast::TranslateVisitor::apply(cache.get(), std::move(typechecked)); + t4.log(); + } catch (const exc::ParserException &e) { + return llvm::make_error(e); + } + module->setSrcInfo({abspath, 0, 0, 0}); + return llvm::Error::success(); +} + +llvm::Error +Compiler::parseFile(const std::string &file, int testFlags, + const std::unordered_map &defines) { + return parse(/*isCode=*/false, file, /*code=*/"", /*startLine=*/0, testFlags, + defines); +} + +llvm::Error +Compiler::parseCode(const std::string &file, const std::string &code, int startLine, + int testFlags, + const std::unordered_map &defines) { + return parse(/*isCode=*/true, file, code, startLine, testFlags, defines); +} + +llvm::Error Compiler::compile() { + pm->run(module.get()); + llvisitor->visit(module.get()); + return llvm::Error::success(); +} + +llvm::Expected Compiler::docgen(const std::vector &files) { + try { + auto j = ast::DocVisitor::apply(argv0, files); + return j->toString(); + } catch (exc::ParserException &e) { + return llvm::make_error(e); + } +} + +} // namespace codon diff --git a/codon/compiler/compiler.h b/codon/compiler/compiler.h new file mode 100644 index 00000000..35a30d69 --- /dev/null +++ b/codon/compiler/compiler.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include + +#include "codon/compiler/error.h" +#include "codon/dsl/plugins.h" +#include "codon/parser/cache.h" +#include "codon/sir/llvm/llvisitor.h" +#include "codon/sir/module.h" +#include "codon/sir/transform/manager.h" + +namespace codon { + +class Compiler { +private: + std::string argv0; + bool debug; + std::string input; + std::unique_ptr plm; + std::unique_ptr cache; + std::unique_ptr module; + std::unique_ptr pm; + std::unique_ptr llvisitor; + + llvm::Error parse(bool isCode, const std::string &file, const std::string &code, + int startLine, int testFlags, + const std::unordered_map &defines); + +public: + Compiler(const std::string &argv0, bool debug = false, + const std::vector &disabledPasses = {}, bool isTest = false); + + std::string getInput() const { return input; } + PluginManager *getPluginManager() const { return plm.get(); } + ast::Cache *getCache() const { return cache.get(); } + ir::Module *getModule() const { return module.get(); } + ir::transform::PassManager *getPassManager() const { return pm.get(); } + ir::LLVMVisitor *getLLVMVisitor() const { return llvisitor.get(); } + + llvm::Error load(const std::string &plugin); + llvm::Error + parseFile(const std::string &file, int testFlags = 0, + const std::unordered_map &defines = {}); + llvm::Error + parseCode(const std::string &file, const std::string &code, int startLine = 0, + int testFlags = 0, + const std::unordered_map &defines = {}); + llvm::Error compile(); + llvm::Expected docgen(const std::vector &files); +}; + +} // namespace codon diff --git a/codon/compiler/debug_listener.cpp b/codon/compiler/debug_listener.cpp new file mode 100644 index 00000000..f40d23b7 --- /dev/null +++ b/codon/compiler/debug_listener.cpp @@ -0,0 +1,73 @@ +#include "debug_listener.h" + +#include +#include + +#include "codon/runtime/lib.h" + +namespace codon { + +void DebugListener::notifyObjectLoaded(ObjectKey key, + const llvm::object::ObjectFile &obj, + const llvm::RuntimeDyld::LoadedObjectInfo &L) { + uintptr_t start = 0, stop = 0; + for (const auto &sec : obj.sections()) { + if (sec.isText()) { + start = L.getSectionLoadAddress(sec); + stop = start + sec.getSize(); + break; + } + } + auto buf = llvm::MemoryBuffer::getMemBufferCopy(obj.getData(), obj.getFileName()); + auto newObj = llvm::cantFail( + llvm::object::ObjectFile::createObjectFile(buf->getMemBufferRef())); + objects.emplace_back(key, std::move(newObj), std::move(buf), start, stop); +} + +void DebugListener::notifyFreeingObject(ObjectKey key) { + objects.erase( + std::remove_if(objects.begin(), objects.end(), + [key](const ObjectInfo &o) { return key == o.getKey(); }), + objects.end()); +} + +llvm::Expected DebugListener::symbolize(uintptr_t pc) { + for (const auto &o : objects) { + if (o.contains(pc)) { + llvm::symbolize::LLVMSymbolizer::Options opt; + opt.PrintFunctions = llvm::DILineInfoSpecifier::FunctionNameKind::ShortName; + opt.PathStyle = llvm::DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly; + llvm::symbolize::LLVMSymbolizer sym(opt); + return sym.symbolizeCode( + o.getObject(), + {pc - o.getStart(), llvm::object::SectionedAddress::UndefSection}); + } + } + return llvm::DILineInfo(); +} + +llvm::Expected DebugListener::getPrettyBacktrace(uintptr_t pc) { + auto invalid = [](const std::string &name) { return name == ""; }; + auto src = symbolize(pc); + if (auto err = src.takeError()) + return std::move(err); + if (invalid(src->FunctionName) || invalid(src->FileName)) + return ""; + return makeBacktraceFrameString(pc, src->FunctionName, src->FileName, src->Line, + src->Column); +} + +std::string DebugListener::getPrettyBacktrace(const std::vector &backtrace) { + std::ostringstream buf; + buf << "\033[1mBacktrace:\033[0m\n"; + for (auto pc : backtrace) { + auto line = getPrettyBacktrace(pc); + if (!line) + break; + if (!line->empty()) + buf << " " << *line << "\n"; + } + return buf.str(); +} + +} // namespace codon diff --git a/codon/compiler/debug_listener.h b/codon/compiler/debug_listener.h new file mode 100644 index 00000000..d70f7330 --- /dev/null +++ b/codon/compiler/debug_listener.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include "codon/sir/llvm/llvm.h" + +namespace codon { + +class DebugListener : public llvm::JITEventListener { +public: + class ObjectInfo { + private: + ObjectKey key; + std::unique_ptr object; + std::unique_ptr buffer; + uintptr_t start; + uintptr_t stop; + + public: + ObjectInfo(ObjectKey key, std::unique_ptr object, + std::unique_ptr buffer, uintptr_t start, + uintptr_t stop) + : key(key), object(std::move(object)), buffer(std::move(buffer)), start(start), + stop(stop) {} + + ObjectKey getKey() const { return key; } + const llvm::object::ObjectFile &getObject() const { return *object; } + uintptr_t getStart() const { return start; } + uintptr_t getStop() const { return stop; } + bool contains(uintptr_t pc) const { return start <= pc && pc < stop; } + }; + +private: + std::vector objects; + + void notifyObjectLoaded(ObjectKey key, const llvm::object::ObjectFile &obj, + const llvm::RuntimeDyld::LoadedObjectInfo &L) override; + void notifyFreeingObject(ObjectKey key) override; + +public: + DebugListener() : llvm::JITEventListener(), objects() {} + + llvm::Expected symbolize(uintptr_t pc); + llvm::Expected getPrettyBacktrace(uintptr_t pc); + std::string getPrettyBacktrace(const std::vector &backtrace); +}; + +} // namespace codon diff --git a/codon/compiler/engine.cpp b/codon/compiler/engine.cpp new file mode 100644 index 00000000..b5f7bcd5 --- /dev/null +++ b/codon/compiler/engine.cpp @@ -0,0 +1,93 @@ +#include "engine.h" + +#include "codon/compiler/memory_manager.h" +#include "codon/sir/llvm/optimize.h" + +namespace codon { +namespace jit { + +void Engine::handleLazyCallThroughError() { + llvm::errs() << "LazyCallThrough error: Could not find function body"; + exit(1); +} + +llvm::Expected +Engine::optimizeModule(llvm::orc::ThreadSafeModule module, + const llvm::orc::MaterializationResponsibility &R) { + module.withModuleDo([](llvm::Module &module) { + ir::optimize(&module, /*debug=*/true, /*jit=*/true); + }); + return std::move(module); +} + +Engine::Engine(std::unique_ptr tpc, + std::unique_ptr sess, + std::unique_ptr tpciu, + llvm::orc::JITTargetMachineBuilder jtmb, llvm::DataLayout layout) + : tpc(std::move(tpc)), sess(std::move(sess)), tpciu(std::move(tpciu)), + layout(std::move(layout)), mangle(*this->sess, this->layout), + objectLayer(*this->sess, + []() { return std::make_unique(); }), + compileLayer(*this->sess, objectLayer, + std::make_unique(std::move(jtmb))), + optimizeLayer(*this->sess, compileLayer, optimizeModule), + codLayer(*this->sess, optimizeLayer, this->tpciu->getLazyCallThroughManager(), + [this] { return this->tpciu->createIndirectStubsManager(); }), + mainJD(this->sess->createBareJITDylib("
")), + dbListener(std::make_unique()) { + mainJD.addGenerator( + llvm::cantFail(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( + layout.getGlobalPrefix()))); + objectLayer.setAutoClaimResponsibilityForObjectSymbols(true); + objectLayer.registerJITEventListener(*dbListener); +} + +Engine::~Engine() { + if (auto err = sess->endSession()) + sess->reportError(std::move(err)); + if (auto err = tpciu->cleanup()) + sess->reportError(std::move(err)); +} + +llvm::Expected> Engine::create() { + auto ssp = std::make_shared(); + auto tpc = llvm::orc::SelfTargetProcessControl::Create(ssp); + if (!tpc) + return tpc.takeError(); + + auto sess = std::make_unique(std::move(ssp)); + + auto tpciu = llvm::orc::TPCIndirectionUtils::Create(**tpc); + if (!tpciu) + return tpciu.takeError(); + + (*tpciu)->createLazyCallThroughManager( + *sess, llvm::pointerToJITTargetAddress(&handleLazyCallThroughError)); + + if (auto err = llvm::orc::setUpInProcessLCTMReentryViaTPCIU(**tpciu)) + return std::move(err); + + llvm::orc::JITTargetMachineBuilder jtmb((*tpc)->getTargetTriple()); + + auto layout = jtmb.getDefaultDataLayoutForTarget(); + if (!layout) + return layout.takeError(); + + return std::make_unique(std::move(*tpc), std::move(sess), std::move(*tpciu), + std::move(jtmb), std::move(*layout)); +} + +llvm::Error Engine::addModule(llvm::orc::ThreadSafeModule module, + llvm::orc::ResourceTrackerSP rt) { + if (!rt) + rt = mainJD.getDefaultResourceTracker(); + + return optimizeLayer.add(rt, std::move(module)); +} + +llvm::Expected Engine::lookup(llvm::StringRef name) { + return sess->lookup({&mainJD}, mangle(name.str())); +} + +} // namespace jit +} // namespace codon diff --git a/codon/compiler/engine.h b/codon/compiler/engine.h new file mode 100644 index 00000000..de47eb28 --- /dev/null +++ b/codon/compiler/engine.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +#include "codon/compiler/debug_listener.h" +#include "codon/sir/llvm/llvm.h" + +#include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.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/RTDyldObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/TPCIndirectionUtils.h" +#include "llvm/ExecutionEngine/Orc/TargetProcessControl.h" + +namespace codon { +namespace jit { + +class Engine { +private: + std::unique_ptr tpc; + std::unique_ptr sess; + std::unique_ptr tpciu; + + llvm::DataLayout layout; + llvm::orc::MangleAndInterner mangle; + + llvm::orc::RTDyldObjectLinkingLayer objectLayer; + llvm::orc::IRCompileLayer compileLayer; + llvm::orc::IRTransformLayer optimizeLayer; + llvm::orc::CompileOnDemandLayer codLayer; + + llvm::orc::JITDylib &mainJD; + + std::unique_ptr dbListener; + + static void handleLazyCallThroughError(); + + static llvm::Expected + optimizeModule(llvm::orc::ThreadSafeModule module, + const llvm::orc::MaterializationResponsibility &R); + +public: + Engine(std::unique_ptr tpc, + std::unique_ptr sess, + std::unique_ptr tpciu, + llvm::orc::JITTargetMachineBuilder jtmb, llvm::DataLayout layout); + + ~Engine(); + + static llvm::Expected> create(); + + const llvm::DataLayout &getDataLayout() const { return layout; } + + llvm::orc::JITDylib &getMainJITDylib() { return mainJD; } + + DebugListener *getDebugListener() const { return dbListener.get(); } + + llvm::Error addModule(llvm::orc::ThreadSafeModule module, + llvm::orc::ResourceTrackerSP rt = nullptr); + + llvm::Expected lookup(llvm::StringRef name); +}; + +} // namespace jit +} // namespace codon diff --git a/codon/compiler/error.cpp b/codon/compiler/error.cpp new file mode 100644 index 00000000..b8b1d590 --- /dev/null +++ b/codon/compiler/error.cpp @@ -0,0 +1,15 @@ +#include "error.h" + +namespace codon { +namespace error { + +char ParserErrorInfo::ID = 0; + +char RuntimeErrorInfo::ID = 0; + +char PluginErrorInfo::ID = 0; + +char IOErrorInfo::ID = 0; + +} // namespace error +} // namespace codon diff --git a/codon/compiler/error.h b/codon/compiler/error.h new file mode 100644 index 00000000..58a7b2d4 --- /dev/null +++ b/codon/compiler/error.h @@ -0,0 +1,150 @@ +#pragma once + +#include +#include + +#include "codon/parser/common.h" + +#include "llvm/Support/Error.h" + +namespace codon { +namespace error { + +class Message { +private: + std::string msg; + std::string file; + int line = 0; + int col = 0; + +public: + explicit Message(const std::string &msg, const std::string &file = "", int line = 0, + int col = 0) + : msg(msg), file(file), line(line), col(col) {} + + std::string getMessage() const { return msg; } + std::string getFile() const { return file; } + int getLine() const { return line; } + int getColumn() const { return col; } + + void log(llvm::raw_ostream &out) const { + if (!getFile().empty()) { + out << getFile(); + if (getLine() != 0) { + out << ":" << getLine(); + if (getColumn() != 0) { + out << ":" << getColumn(); + } + } + out << ": "; + } + out << getMessage(); + } +}; + +class ParserErrorInfo : public llvm::ErrorInfo { +private: + std::vector messages; + +public: + explicit ParserErrorInfo(std::vector messages) + : messages(std::move(messages)) {} + explicit ParserErrorInfo(const exc::ParserException &e) : messages() { + for (unsigned i = 0; i < e.messages.size(); i++) { + if (!e.messages[i].empty()) + messages.emplace_back(e.messages[i], e.locations[i].file, e.locations[i].line, + e.locations[i].col); + } + } + + auto begin() { return messages.begin(); } + auto end() { return messages.end(); } + auto begin() const { return messages.begin(); } + auto end() const { return messages.end(); } + + void log(llvm::raw_ostream &out) const override { + for (auto &msg : *this) { + msg.log(out); + out << "\n"; + } + } + + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } + + static char ID; +}; + +class RuntimeErrorInfo : public llvm::ErrorInfo { +private: + std::string output; + std::string type; + Message message; + std::vector backtrace; + +public: + RuntimeErrorInfo(const std::string &output, const std::string &type, + const std::string &msg, const std::string &file = "", int line = 0, + int col = 0, std::vector backtrace = {}) + : output(output), type(type), message(msg, file, line, col), + backtrace(std::move(backtrace)) {} + + std::string getOutput() const { return output; } + std::string getType() const { return type; } + std::string getMessage() const { return message.getMessage(); } + std::string getFile() const { return message.getFile(); } + int getLine() const { return message.getLine(); } + int getColumn() const { return message.getColumn(); } + std::vector getBacktrace() const { return backtrace; } + + void log(llvm::raw_ostream &out) const override { + out << type << ": "; + message.log(out); + } + + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } + + static char ID; +}; + +class PluginErrorInfo : public llvm::ErrorInfo { +private: + std::string message; + +public: + explicit PluginErrorInfo(const std::string &message) : message(message) {} + + std::string getMessage() const { return message; } + + void log(llvm::raw_ostream &out) const override { out << message; } + + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } + + static char ID; +}; + +class IOErrorInfo : public llvm::ErrorInfo { +private: + std::string message; + +public: + explicit IOErrorInfo(const std::string &message) : message(message) {} + + std::string getMessage() const { return message; } + + void log(llvm::raw_ostream &out) const override { out << message; } + + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } + + static char ID; +}; + +} // namespace error +} // namespace codon diff --git a/codon/compiler/jit.cpp b/codon/compiler/jit.cpp new file mode 100644 index 00000000..496e46d0 --- /dev/null +++ b/codon/compiler/jit.cpp @@ -0,0 +1,166 @@ +#include "jit.h" + +#include "codon/parser/peg/peg.h" +#include "codon/parser/visitors/doc/doc.h" +#include "codon/parser/visitors/format/format.h" +#include "codon/parser/visitors/simplify/simplify.h" +#include "codon/parser/visitors/translate/translate.h" +#include "codon/parser/visitors/typecheck/typecheck.h" +#include "codon/runtime/lib.h" + +namespace codon { +namespace jit { +namespace { +typedef int MainFunc(int, char **); +typedef void InputFunc(); + +const std::string JIT_FILENAME = ""; +} // namespace + +JIT::JIT(const std::string &argv0, const std::string &mode) + : compiler(std::make_unique(argv0, /*debug=*/true)), mode(mode) { + if (auto e = Engine::create()) { + engine = std::move(e.get()); + } else { + engine = {}; + seqassert(false, "JIT engine creation error"); + } + compiler->getLLVMVisitor()->setJIT(true); +} + +llvm::Error JIT::init() { + auto *cache = compiler->getCache(); + auto *module = compiler->getModule(); + auto *pm = compiler->getPassManager(); + auto *llvisitor = compiler->getLLVMVisitor(); + + auto transformed = ast::SimplifyVisitor::apply( + cache, std::make_shared(), JIT_FILENAME, {}); + + auto typechecked = ast::TypecheckVisitor::apply(cache, std::move(transformed)); + ast::TranslateVisitor::apply(cache, std::move(typechecked)); + cache->isJit = true; // we still need main(), so set isJit after it has been set + module->setSrcInfo({JIT_FILENAME, 0, 0, 0}); + + pm->run(module); + module->accept(*llvisitor); + auto pair = llvisitor->takeModule(); + + if (auto err = engine->addModule({std::move(pair.first), std::move(pair.second)})) + return err; + + auto func = engine->lookup("main"); + if (auto err = func.takeError()) + return err; + + auto *main = (MainFunc *)func->getAddress(); + (*main)(0, nullptr); + return llvm::Error::success(); +} + +llvm::Expected JIT::run(const ir::Func *input, + const std::vector &newGlobals) { + auto *module = compiler->getModule(); + auto *pm = compiler->getPassManager(); + auto *llvisitor = compiler->getLLVMVisitor(); + pm->run(module); + + const std::string name = ir::LLVMVisitor::getNameForFunction(input); + llvisitor->registerGlobal(input); + for (auto *var : newGlobals) { + llvisitor->registerGlobal(var); + } + for (auto *var : newGlobals) { + if (auto *func = ir::cast(var)) + func->accept(*llvisitor); + } + input->accept(*llvisitor); + auto pair = llvisitor->takeModule(); + + if (auto err = engine->addModule({std::move(pair.first), std::move(pair.second)})) + return std::move(err); + + auto func = engine->lookup(name); + if (auto err = func.takeError()) + return std::move(err); + + auto *repl = (InputFunc *)func->getAddress(); + try { + (*repl)(); + } catch (const JITError &e) { + std::vector backtrace; + for (auto pc : e.getBacktrace()) { + auto line = engine->getDebugListener()->getPrettyBacktrace(pc); + if (line && !line->empty()) + backtrace.push_back(*line); + } + return llvm::make_error(e.getOutput(), e.getType(), + e.what(), e.getFile(), e.getLine(), + e.getCol(), backtrace); + } + return getCapturedOutput(); +} + +llvm::Expected JIT::exec(const std::string &code) { + auto *cache = compiler->getCache(); + ast::StmtPtr node = ast::parseCode(cache, JIT_FILENAME, code, /*startLine=*/0); + + auto sctx = cache->imports[MAIN_IMPORT].ctx; + auto preamble = std::make_shared(); + try { + auto *e = node->getSuite() + ? const_cast(node->getSuite())->lastInBlock() + : &node; + if (e) + if (auto ex = (*e)->getExpr()) { + *e = std::make_shared( + std::vector{std::make_shared( + std::make_shared("_jit_display"), ex->expr, + std::make_shared(mode))}, + false); + } + auto s = ast::SimplifyVisitor(sctx, preamble).transform(node); + auto simplified = std::make_shared(); + for (auto &s : preamble->globals) + simplified->stmts.push_back(s); + for (auto &s : preamble->functions) + simplified->stmts.push_back(s); + simplified->stmts.push_back(s); + // TODO: unroll on errors... + + auto *cache = compiler->getCache(); + auto typechecked = ast::TypecheckVisitor::apply(cache, simplified); + std::vector globalNames; + for (auto &g : cache->globals) { + if (!g.second) + globalNames.push_back(g.first); + } + // add newly realized functions + std::vector v; + std::vector frs; + v.push_back(typechecked); + for (auto &p : cache->pendingRealizations) { + v.push_back(cache->functions[p.first].ast); + frs.push_back(&cache->functions[p.first].realizations[p.second]->ir); + } + auto func = + ast::TranslateVisitor::apply(cache, std::make_shared(v, false)); + cache->jitCell++; + + std::vector globalVars; + for (auto &g : globalNames) { + seqassert(cache->globals[g], "JIT global {} not set", g); + globalVars.push_back(cache->globals[g]); + } + for (auto &i : frs) { + seqassert(*i, "JIT fn not set"); + globalVars.push_back(*i); + } + return run(func, globalVars); + } catch (const exc::ParserException &e) { + return llvm::make_error(e); + } +} + +} // namespace jit +} // namespace codon diff --git a/codon/compiler/jit.h b/codon/compiler/jit.h new file mode 100644 index 00000000..b781299d --- /dev/null +++ b/codon/compiler/jit.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +#include "codon/compiler/compiler.h" +#include "codon/compiler/engine.h" +#include "codon/compiler/error.h" +#include "codon/parser/cache.h" +#include "codon/sir/llvm/llvisitor.h" +#include "codon/sir/transform/manager.h" +#include "codon/sir/var.h" + +namespace codon { +namespace jit { + +class JIT { +private: + std::unique_ptr compiler; + std::unique_ptr engine; + std::string mode; + +public: + explicit JIT(const std::string &argv0, const std::string &mode = ""); + + Compiler *getCompiler() const { return compiler.get(); } + Engine *getEngine() const { return engine.get(); } + + llvm::Error init(); + llvm::Expected run(const ir::Func *input, + const std::vector &newGlobals = {}); + llvm::Expected exec(const std::string &code); +}; + +} // namespace jit +} // namespace codon diff --git a/codon/sir/llvm/memory_manager.cpp b/codon/compiler/memory_manager.cpp similarity index 96% rename from codon/sir/llvm/memory_manager.cpp rename to codon/compiler/memory_manager.cpp index 85507535..abaeb097 100644 --- a/codon/sir/llvm/memory_manager.cpp +++ b/codon/compiler/memory_manager.cpp @@ -3,7 +3,6 @@ #include "codon/runtime/lib.h" namespace codon { -namespace ir { BoehmGCMemoryManager::BoehmGCMemoryManager() : SectionMemoryManager(), roots() {} @@ -26,5 +25,4 @@ BoehmGCMemoryManager::~BoehmGCMemoryManager() { } } -} // namespace ir } // namespace codon diff --git a/codon/sir/llvm/memory_manager.h b/codon/compiler/memory_manager.h similarity index 95% rename from codon/sir/llvm/memory_manager.h rename to codon/compiler/memory_manager.h index 52eb63ae..0718956d 100644 --- a/codon/sir/llvm/memory_manager.h +++ b/codon/compiler/memory_manager.h @@ -6,7 +6,6 @@ #include "codon/sir/llvm/llvm.h" namespace codon { -namespace ir { /// Simple extension of LLVM's SectionMemoryManager which catches data section /// allocations and registers them with the GC. This allows the GC to know not @@ -23,5 +22,4 @@ public: ~BoehmGCMemoryManager() override; }; -} // namespace ir } // namespace codon diff --git a/codon/dsl/plugins.cpp b/codon/dsl/plugins.cpp index d1cc876a..07c1fb97 100644 --- a/codon/dsl/plugins.cpp +++ b/codon/dsl/plugins.cpp @@ -10,10 +10,8 @@ namespace codon { namespace { -bool error(const std::string &msg, std::string *errMsg) { - if (!msg.empty() && errMsg) - *errMsg = msg; - return false; +llvm::Expected pluginError(const std::string &msg) { + return llvm::make_error(msg); } typedef std::unique_ptr LoadFunc(); @@ -21,7 +19,7 @@ typedef std::unique_ptr LoadFunc(); namespace fs = std::filesystem; -bool PluginManager::load(const std::string &path, std::string *errMsg) { +llvm::Expected PluginManager::load(const std::string &path) { #if __APPLE__ const std::string libExt = "dylib"; #else @@ -40,9 +38,8 @@ bool PluginManager::load(const std::string &path, std::string *errMsg) { try { tml = toml::parse_file(tomlPath.string()); } catch (const toml::parse_error &e) { - return error( - fmt::format("[toml::parse_file(\"{}\")] {}", tomlPath.string(), e.what()), - errMsg); + return pluginError( + fmt::format("[toml::parse_file(\"{}\")] {}", tomlPath.string(), e.what())); } auto about = tml["about"]; auto library = tml["library"]; @@ -70,31 +67,26 @@ bool PluginManager::load(const std::string &path, std::string *errMsg) { semver::version(CODON_VERSION_MAJOR, CODON_VERSION_MINOR, CODON_VERSION_PATCH), info.supported); } catch (const std::invalid_argument &e) { - return error(fmt::format("[semver::range::satisfies(..., \"{}\")] {}", - info.supported, e.what()), - errMsg); + return pluginError(fmt::format("[semver::range::satisfies(..., \"{}\")] {}", + info.supported, e.what())); } if (!versionOk) - return error(fmt::format("unsupported version {} (supported: {})", CODON_VERSION, - info.supported), - errMsg); + return pluginError(fmt::format("unsupported version {} (supported: {})", + CODON_VERSION, info.supported)); if (!dylibPath.empty()) { std::string libLoadErrorMsg; auto handle = llvm::sys::DynamicLibrary::getPermanentLibrary(dylibPath.c_str(), &libLoadErrorMsg); if (!handle.isValid()) - return error( - fmt::format( - "[llvm::sys::DynamicLibrary::getPermanentLibrary(\"{}\", ...)] {}", - dylibPath, libLoadErrorMsg), - errMsg); + return pluginError(fmt::format( + "[llvm::sys::DynamicLibrary::getPermanentLibrary(\"{}\", ...)] {}", dylibPath, + libLoadErrorMsg)); auto *entry = (LoadFunc *)handle.getAddressOfSymbol("load"); if (!entry) - return error( - fmt::format("could not find 'load' in plugin shared library: {}", dylibPath), - errMsg); + return pluginError( + fmt::format("could not find 'load' in plugin shared library: {}", dylibPath)); auto dsl = (*entry)(); plugins.push_back(std::make_unique(std::move(dsl), info, handle)); @@ -102,7 +94,7 @@ bool PluginManager::load(const std::string &path, std::string *errMsg) { plugins.push_back(std::make_unique(std::make_unique(), info, llvm::sys::DynamicLibrary())); } - return true; + return plugins.back().get(); } } // namespace codon diff --git a/codon/dsl/plugins.h b/codon/dsl/plugins.h index 63c9eb9e..755dc8dc 100644 --- a/codon/dsl/plugins.h +++ b/codon/dsl/plugins.h @@ -5,6 +5,7 @@ #include #include +#include "codon/compiler/error.h" #include "codon/dsl/dsl.h" #include "codon/sir/util/iterators.h" #include "llvm/Support/DynamicLibrary.h" @@ -45,9 +46,8 @@ public: /// Loads the plugin at the given load path. /// @param path path to plugin directory containing "plugin.toml" file - /// @param errMsg where to store potential error messages, if non-null - /// @return true if the plugin was loaded successfully, false otherwise - bool load(const std::string &path, std::string *errMsg = nullptr); + /// @return plugin pointer if successful, plugin error otherwise + llvm::Expected load(const std::string &path); }; } // namespace codon diff --git a/codon/parser/ast/stmt.cpp b/codon/parser/ast/stmt.cpp index 0e73e902..119825aa 100644 --- a/codon/parser/ast/stmt.cpp +++ b/codon/parser/ast/stmt.cpp @@ -52,6 +52,16 @@ void SuiteStmt::flatten(StmtPtr s, std::vector &stmts) { stmts.push_back(ss); } } +StmtPtr *SuiteStmt::lastInBlock() { + if (stmts.empty()) + return nullptr; + if (auto s = const_cast(stmts.back()->getSuite())) { + auto l = s->lastInBlock(); + if (l) + return l; + } + return &(stmts.back()); +} std::string BreakStmt::toString(int) const { return "(break)"; } ACCEPT_IMPL(BreakStmt, ASTVisitor); @@ -362,7 +372,7 @@ ACCEPT_IMPL(YieldFromStmt, ASTVisitor); WithStmt::WithStmt(std::vector items, std::vector vars, StmtPtr suite) : Stmt(), items(std::move(items)), vars(std::move(vars)), suite(std::move(suite)) { - assert(items.size() == vars.size()); + seqassert(items.size() == vars.size(), "vector size mismatch"); } WithStmt::WithStmt(std::vector> itemVarPairs, StmtPtr suite) : Stmt(), suite(std::move(suite)) { diff --git a/codon/parser/ast/stmt.h b/codon/parser/ast/stmt.h index b0d7d7ac..23fde86b 100644 --- a/codon/parser/ast/stmt.h +++ b/codon/parser/ast/stmt.h @@ -92,6 +92,7 @@ struct SuiteStmt : public Stmt { const Stmt *firstInBlock() const override { return stmts.empty() ? nullptr : stmts[0]->firstInBlock(); } + StmtPtr *lastInBlock(); /// Flatten all nested SuiteStmt objects that do not own a block in the statement /// vector. This is shallow flattening. diff --git a/codon/parser/ast/types.cpp b/codon/parser/ast/types.cpp index 1d68c229..e932851b 100644 --- a/codon/parser/ast/types.cpp +++ b/codon/parser/ast/types.cpp @@ -18,7 +18,7 @@ void Type::Unification::undo() { linked[i]->type = nullptr; } for (int i = int(leveled.size()) - 1; i >= 0; i--) { - assert(leveled[i].first->kind == LinkType::Unbound); + seqassert(leveled[i].first->kind == LinkType::Unbound, "not unbound"); leveled[i].first->level = leveled[i].second; } for (auto &t : traits) @@ -687,7 +687,7 @@ std::string StaticType::realizedName() const { deps.push_back(e.type->realizedName()); if (!expr->staticValue.evaluated) // If not already evaluated, evaluate! const_cast(this)->expr->staticValue = evaluate(); - assert(expr->staticValue.evaluated); + seqassert(expr->staticValue.evaluated, "static value not evaluated"); return expr->staticValue.toString(); } StaticValue StaticType::evaluate() const { diff --git a/codon/parser/cache.cpp b/codon/parser/cache.cpp index 6c2f1b14..dedaaf9c 100644 --- a/codon/parser/cache.cpp +++ b/codon/parser/cache.cpp @@ -14,7 +14,8 @@ namespace ast { Cache::Cache(std::string argv0) : generatedSrcInfoCount(0), unboundCount(0), varCount(0), age(0), testFlags(0), - argv0(move(argv0)), module(nullptr), typeCtx(nullptr), codegenCtx(nullptr) {} + argv0(move(argv0)), module(nullptr), typeCtx(nullptr), codegenCtx(nullptr), + isJit(false), jitCell(0) {} std::string Cache::getTemporaryVar(const std::string &prefix, char sigil) { return fmt::format("{}{}_{}", sigil ? fmt::format("{}_", sigil) : "", prefix, diff --git a/codon/parser/cache.h b/codon/parser/cache.h index 490a95ea..2c32c8bb 100644 --- a/codon/parser/cache.h +++ b/codon/parser/cache.h @@ -88,16 +88,16 @@ struct Cache : public std::enable_shared_from_this { std::string argv0; /// Absolute path of the entry-point module (if available). std::string module0; - /// LLVM module. - codon::ir::Module *module = nullptr; + /// IR module. + ir::Module *module = nullptr; /// Table of imported files that maps an absolute filename to a Import structure. - /// By convention, the key of Seq standard library is "". + /// By convention, the key of the Codon's standard library is "". std::unordered_map imports; /// Set of unique (canonical) global identifiers for marking such variables as global - /// in code-generation step. - std::set globals; + /// in code-generation step and in JIT. + std::map globals; /// Stores class data for each class (type) in the source code. struct Class { @@ -195,6 +195,10 @@ struct Cache : public std::enable_shared_from_this { /// Plugin-added import paths std::vector pluginImportPaths; + /// Set if the Codon is running in JIT mode. + bool isJit; + int jitCell; + public: explicit Cache(std::string argv0 = ""); diff --git a/codon/parser/common.cpp b/codon/parser/common.cpp index d45f4262..bb64649c 100644 --- a/codon/parser/common.cpp +++ b/codon/parser/common.cpp @@ -1,8 +1,8 @@ #include "common.h" -#include +#include #include -#include +#include #include "codon/parser/common.h" #include "codon/util/fmt/format.h" @@ -195,79 +195,77 @@ std::string executable_path(const char *argv0) { std::string executable_path(const char *argv0) { return std::string(argv0); } #endif +namespace fs = std::filesystem; + +namespace { +void addPath(std::vector &paths, const fs::path &path) { + if (fs::exists(path)) + paths.push_back(fs::canonical(path)); +} + +std::vector getStdLibPaths(const std::string &argv0, + const std::vector &plugins) { + std::vector paths; + if (auto c = getenv("CODON_PATH")) { + addPath(paths, fs::path(std::string(c))); + } + if (!argv0.empty()) { + auto base = fs::path(executable_path(argv0.c_str())); + for (auto loci : {"../lib/codon/stdlib", "../stdlib", "stdlib"}) { + addPath(paths, base.parent_path() / loci); + } + } + for (auto &path : plugins) { + addPath(paths, fs::path(path)); + } + return paths; +} + +ImportFile getRoot(const std::string argv0, const std::vector &plugins, + const std::string &module0Root, const std::string &s) { + bool isStdLib = false; + std::string root; + for (auto &p : getStdLibPaths(argv0, plugins)) + if (startswith(s, p)) { + root = p; + isStdLib = true; + break; + } + if (!isStdLib && startswith(s, module0Root)) + root = module0Root; + const std::string ext = ".codon"; + seqassert(startswith(s, root) && endswith(s, ext), "bad path substitution: {}, {}", s, + root); + auto module = s.substr(root.size() + 1, s.size() - root.size() - ext.size() - 1); + std::replace(module.begin(), module.end(), '/', '.'); + return ImportFile{(!isStdLib && root == module0Root) ? ImportFile::PACKAGE + : ImportFile::STDLIB, + s, module}; +} +} // namespace + std::shared_ptr getImportFile(const std::string &argv0, const std::string &what, const std::string &relativeTo, bool forceStdlib, const std::string &module0, const std::vector &plugins) { - using fmt::format; - - auto getStdLibPaths = [](const std::string &argv0, - const std::vector &plugins) { - std::vector paths; - char abs[PATH_MAX + 1]; - if (auto c = getenv("CODON_PATH")) { - if (realpath(c, abs)) - paths.push_back(abs); + std::vector paths; + if (what != "") { + auto parentRelativeTo = fs::path(relativeTo).parent_path(); + if (!forceStdlib) { + addPath(paths, (parentRelativeTo / what).replace_extension("codon")); + addPath(paths, parentRelativeTo / what / "__init__.codon"); } - if (!argv0.empty()) { - for (auto loci : {"../lib/codon/stdlib", "../stdlib", "stdlib"}) { - strncpy(abs, executable_path(argv0.c_str()).c_str(), PATH_MAX); - if (realpath(format("{}/{}", dirname(abs), loci).c_str(), abs)) - paths.push_back(abs); - } - } - for (auto &path : plugins) { - if (realpath(path.c_str(), abs)) - paths.push_back(abs); - } - return paths; - }; - - char abs[PATH_MAX + 1]; - strncpy(abs, module0.c_str(), PATH_MAX); - auto module0Root = std::string(dirname(abs)); - auto getRoot = [&](const std::string &s) { - bool isStdLib = false; - std::string root; - for (auto &p : getStdLibPaths(argv0, plugins)) - if (startswith(s, p)) { - root = p; - isStdLib = true; - break; - } - if (!isStdLib && startswith(s, module0Root)) - root = module0Root; - const std::string ext = ".codon"; - seqassert(startswith(s, root) && endswith(s, ext), "bad path substitution: {}, {}", - s, root); - auto module = s.substr(root.size() + 1, s.size() - root.size() - ext.size() - 1); - std::replace(module.begin(), module.end(), '/', '.'); - return ImportFile{(!isStdLib && root == module0Root) ? ImportFile::PACKAGE - : ImportFile::STDLIB, - s, module}; - }; - - std::vector paths; - if (!forceStdlib) { - realpath(relativeTo.c_str(), abs); - auto parent = dirname(abs); - paths.push_back(format("{}/{}.codon", parent, what)); - paths.push_back(format("{}/{}/__init__.codon", parent, what)); } for (auto &p : getStdLibPaths(argv0, plugins)) { - paths.push_back(format("{}/{}.codon", p, what)); - paths.push_back(format("{}/{}/__init__.codon", p, what)); + addPath(paths, (p / what).replace_extension("codon")); + addPath(paths, p / what / "__init__.codon"); } - for (auto &p : paths) { - if (!realpath(p.c_str(), abs)) - continue; - auto path = std::string(abs); - struct stat buffer; - if (!stat(path.c_str(), &buffer)) - return std::make_shared(getRoot(path)); - } - return nullptr; + + auto module0Root = fs::path(module0).parent_path().string(); + return paths.empty() ? nullptr + : std::make_shared( + getRoot(argv0, plugins, module0Root, paths[0].string())); } } // namespace ast diff --git a/codon/parser/common.h b/codon/parser/common.h index eb3abb47..8c1be3a5 100644 --- a/codon/parser/common.h +++ b/codon/parser/common.h @@ -16,6 +16,8 @@ #include "codon/util/fmt/format.h" #include "codon/util/fmt/ostream.h" +#define CAST(s, T) dynamic_cast(s.get()) + namespace codon { namespace exc { diff --git a/codon/parser/parser.cpp b/codon/parser/parser.cpp deleted file mode 100644 index 6b7efeb8..00000000 --- a/codon/parser/parser.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include "parser.h" - -#include -#include -#include -#include -#include - -#include "codon/parser/cache.h" -#include "codon/parser/peg/peg.h" -#include "codon/parser/visitors/doc/doc.h" -#include "codon/parser/visitors/format/format.h" -#include "codon/parser/visitors/simplify/simplify.h" -#include "codon/parser/visitors/translate/translate.h" -#include "codon/parser/visitors/typecheck/typecheck.h" -#include "codon/sir/sir.h" -#include "codon/sir/util/format.h" -#include "codon/util/fmt/format.h" - -int _ocaml_time = 0; -int _ll_time = 0; -int _level = 0; -int _dbg_level = 0; -bool _isTest = false; - -namespace codon { - -ir::Module *parse(const std::string &argv0, const std::string &file, - const std::string &code, bool isCode, int isTest, int startLine, - const std::unordered_map &defines, - PluginManager *plm) { - try { - auto d = getenv("CODON_DEBUG"); - if (d) { - auto s = std::string(d); - _dbg_level |= s.find('t') != std::string::npos ? (1 << 0) : 0; // time - _dbg_level |= s.find('r') != std::string::npos ? (1 << 2) : 0; // realize - _dbg_level |= s.find('T') != std::string::npos ? (1 << 4) : 0; // type-check - _dbg_level |= s.find('L') != std::string::npos ? (1 << 5) : 0; // lexer - _dbg_level |= s.find('i') != std::string::npos ? (1 << 6) : 0; // IR - _dbg_level |= - s.find('l') != std::string::npos ? (1 << 7) : 0; // User-level debugging - } - - char abs[PATH_MAX + 1] = {'-', 0}; - if (file != "-") - realpath(file.c_str(), abs); - - auto cache = std::make_shared(argv0); - if (plm) { - for (auto *plugin : *plm) { - if (!plugin->info.stdlibPath.empty()) - cache->pluginImportPaths.push_back(plugin->info.stdlibPath); - - for (auto &kw : plugin->dsl->getExprKeywords()) { - cache->customExprStmts[kw.keyword] = kw.callback; - } - for (auto &kw : plugin->dsl->getBlockKeywords()) { - cache->customBlockStmts[kw.keyword] = {kw.hasExpr, kw.callback}; - } - } - } - - ast::StmtPtr codeStmt = isCode ? ast::parseCode(cache, abs, code, startLine) - : ast::parseFile(cache, abs); - if (_dbg_level) { - auto fo = fopen("_dump.sexp", "w"); - fmt::print(fo, "{}\n", codeStmt->toString(0)); - fclose(fo); - } - - using namespace std::chrono; - cache->module0 = file; - if (isTest) - cache->testFlags = isTest; - - auto t = high_resolution_clock::now(); - - auto transformed = - ast::SimplifyVisitor::apply(cache, move(codeStmt), abs, defines, (isTest > 1)); - if (!isTest) { - LOG_TIME("[T] ocaml = {:.1f}", _ocaml_time / 1000.0); - LOG_TIME("[T] simplify = {:.1f}", - (duration_cast(high_resolution_clock::now() - t).count() - - _ocaml_time) / - 1000.0); - if (_dbg_level) { - auto fo = fopen("_dump_simplify.sexp", "w"); - fmt::print(fo, "{}\n", transformed->toString(0)); - fclose(fo); - fo = fopen("_dump_simplify.codon", "w"); - fmt::print(fo, "{}", ast::FormatVisitor::apply(transformed, cache)); - fclose(fo); - } - } - - t = high_resolution_clock::now(); - auto typechecked = ast::TypecheckVisitor::apply(cache, move(transformed)); - if (!isTest) { - LOG_TIME("[T] typecheck = {:.1f}", - duration_cast(high_resolution_clock::now() - t).count() / - 1000.0); - if (_dbg_level) { - auto fo = fopen("_dump_typecheck.codon", "w"); - fmt::print(fo, "{}", ast::FormatVisitor::apply(typechecked, cache)); - fclose(fo); - fo = fopen("_dump_typecheck.sexp", "w"); - fmt::print(fo, "{}\n", typechecked->toString(0)); - for (auto &f : cache->functions) - for (auto &r : f.second.realizations) - fmt::print(fo, "{}\n", r.second->ast->toString(0)); - fclose(fo); - } - } - - t = high_resolution_clock::now(); - auto *module = ast::TranslateVisitor::apply(cache, move(typechecked)); - module->setSrcInfo({abs, 0, 0, 0}); - - if (!isTest) - LOG_TIME("[T] translate = {:.1f}", - duration_cast(high_resolution_clock::now() - t).count() / - 1000.0); - if (_dbg_level) { - auto out = codon::ir::util::format(module); - std::ofstream os("_dump_sir.lisp"); - os << out; - os.close(); - os.close(); - } - - _isTest = isTest; - return module; - } catch (exc::ParserException &e) { - for (int i = 0; i < e.messages.size(); i++) - if (!e.messages[i].empty()) { - if (isTest) { - _level = 0; - LOG("{}", e.messages[i]); - } else { - compilationError(e.messages[i], e.locations[i].file, e.locations[i].line, - e.locations[i].col, /*terminate=*/false); - } - } - return nullptr; - } -} - -void generateDocstr(const std::string &argv0) { - std::vector files; - std::string s; - while (std::getline(std::cin, s)) - files.push_back(s); - try { - auto j = ast::DocVisitor::apply(argv0, files); - fmt::print("{}\n", j->toString()); - } catch (exc::ParserException &e) { - for (int i = 0; i < e.messages.size(); i++) - if (!e.messages[i].empty()) { - compilationError(e.messages[i], e.locations[i].file, e.locations[i].line, - e.locations[i].col, /*terminate=*/false); - } - } -} - -} // namespace codon diff --git a/codon/parser/parser.h b/codon/parser/parser.h deleted file mode 100644 index 92f905b5..00000000 --- a/codon/parser/parser.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "codon/dsl/plugins.h" -#include "codon/sir/sir.h" -#include "codon/util/common.h" - -namespace codon { - -codon::ir::Module *parse(const std::string &argv0, const std::string &file, - const std::string &code = "", bool isCode = false, - int isTest = 0, int startLine = 0, - const std::unordered_map &defines = - std::unordered_map{}, - PluginManager *plm = nullptr); - -void generateDocstr(const std::string &argv0); - -} // namespace codon diff --git a/codon/parser/peg/peg.cpp b/codon/parser/peg/peg.cpp index 4cad32d8..f8b8dcc9 100644 --- a/codon/parser/peg/peg.cpp +++ b/codon/parser/peg/peg.cpp @@ -14,9 +14,6 @@ #include "codon/parser/visitors/format/format.h" #include "codon/util/cpp-peglib/peglib.h" -extern int _ocaml_time; - -using namespace std; namespace codon { namespace ast { @@ -27,9 +24,12 @@ std::shared_ptr initParser() { auto g = std::make_shared(); init_codon_rules(*g); init_codon_actions(*g); - ~(*g)["NLP"] <= peg::usr([](const char *s, size_t n, peg::SemanticValues &, any &dt) { - return any_cast(dt).parens ? 0 : (n >= 1 && s[0] == '\\' ? 1 : -1); - }); + ~(*g)["NLP"] <= + peg::usr([](const char *s, size_t n, peg::SemanticValues &, std::any &dt) { + return std::any_cast(dt).parens + ? 0 + : (n >= 1 && s[0] == '\\' ? 1 : -1); + }); for (auto &x : *g) { auto v = peg::LinkReferences(*g, x.second.params); x.second.accept(v); @@ -38,33 +38,32 @@ std::shared_ptr initParser() { for (auto &rule : std::vector{ "arguments", "slices", "genexp", "parentheses", "star_parens", "generics", "with_parens_item", "params", "from_as_parens", "from_params"}) { - (*g)[rule].enter = [](const char *, size_t, any &dt) { - any_cast(dt).parens++; + (*g)[rule].enter = [](const char *, size_t, std::any &dt) { + std::any_cast(dt).parens++; }; - (*g)[rule.c_str()].leave = [](const char *, size_t, size_t, any &, any &dt) { - any_cast(dt).parens--; + (*g)[rule.c_str()].leave = [](const char *, size_t, size_t, std::any &, + std::any &dt) { + std::any_cast(dt).parens--; }; } return g; } template -T parseCode(const std::shared_ptr &cache, const std::string &file, - std::string code, int line_offset, int col_offset, - const std::string &rule) { - using namespace std::chrono; - auto t = high_resolution_clock::now(); +T parseCode(Cache *cache, const std::string &file, std::string code, int line_offset, + int col_offset, const std::string &rule) { + TIME("peg"); // Initialize if (!grammar) grammar = initParser(); - std::vector> errors; + std::vector> errors; auto log = [&](size_t line, size_t col, const std::string &msg) { errors.push_back({line, col, msg}); }; T result = nullptr; - auto ctx = make_any(cache, 0, line_offset, col_offset); + auto ctx = std::make_any(cache, 0, line_offset, col_offset); auto r = (*grammar)[rule].parse_and_get_value(code.c_str(), code.size(), ctx, result, file.c_str(), log); auto ret = r.ret && r.len == code.size(); @@ -73,35 +72,34 @@ T parseCode(const std::shared_ptr &cache, const std::string &file, exc::ParserException ex; if (!errors.empty()) { for (auto &e : errors) - ex.track(fmt::format("{}", get<2>(e)), SrcInfo(file, get<0>(e), get<1>(e), 0)); + ex.track(fmt::format("{}", std::get<2>(e)), + SrcInfo(file, std::get<0>(e), std::get<1>(e), 0)); throw ex; return nullptr; } - _ocaml_time += duration_cast(high_resolution_clock::now() - t).count(); return result; } -StmtPtr parseCode(const std::shared_ptr &cache, const std::string &file, - const std::string &code, int line_offset) { +StmtPtr parseCode(Cache *cache, const std::string &file, const std::string &code, + int line_offset) { return parseCode(cache, file, code + "\n", line_offset, 0, "program"); } -ExprPtr parseExpr(const std::shared_ptr &cache, const std::string &code, - const codon::SrcInfo &offset) { +ExprPtr parseExpr(Cache *cache, const std::string &code, const codon::SrcInfo &offset) { return parseCode(cache, offset.file, code, offset.line, offset.col, "fstring"); } -StmtPtr parseFile(const std::shared_ptr &cache, const std::string &file) { +StmtPtr parseFile(Cache *cache, const std::string &file) { std::vector lines; std::string code; if (file == "-") { - for (std::string line; getline(cin, line);) { + for (std::string line; getline(std::cin, line);) { lines.push_back(line); code += line + "\n"; } } else { - ifstream fin(file); + std::ifstream fin(file); if (!fin) error(fmt::format("cannot open {}", file).c_str()); for (std::string line; getline(fin, line);) { @@ -131,18 +129,17 @@ std::shared_ptr initOpenMPParser() { return g; } -std::vector parseOpenMP(const std::shared_ptr &cache, - const std::string &code, +std::vector parseOpenMP(Cache *cache, const std::string &code, const codon::SrcInfo &loc) { if (!ompGrammar) ompGrammar = initOpenMPParser(); - std::vector> errors; + std::vector> errors; auto log = [&](size_t line, size_t col, const std::string &msg) { errors.push_back({line, col, msg}); }; std::vector result; - auto ctx = make_any(cache, 0, 0, 0); + auto ctx = std::make_any(cache, 0, 0, 0); auto r = (*ompGrammar)["pragma"].parse_and_get_value(code.c_str(), code.size(), ctx, result, "", log); auto ret = r.ret && r.len == code.size(); @@ -150,7 +147,7 @@ std::vector parseOpenMP(const std::shared_ptr &cache, r.error_info.output_log(log, code.c_str(), code.size()); exc::ParserException ex; if (!errors.empty()) { - ex.track(fmt::format("openmp {}", get<2>(errors[0])), loc); + ex.track(fmt::format("openmp {}", std::get<2>(errors[0])), loc); throw ex; } return result; diff --git a/codon/parser/peg/peg.h b/codon/parser/peg/peg.h index 56517f03..aefa24a7 100644 --- a/codon/parser/peg/peg.h +++ b/codon/parser/peg/peg.h @@ -12,17 +12,15 @@ namespace codon { namespace ast { /// Parse a Seq code block with the appropriate file and position offsets. -StmtPtr parseCode(const std::shared_ptr &cache, const std::string &file, - const std::string &code, int line_offset = 0); +StmtPtr parseCode(Cache *cache, const std::string &file, const std::string &code, + int line_offset = 0); /// Parse a Seq code expression. -ExprPtr parseExpr(const std::shared_ptr &cache, const std::string &code, - const codon::SrcInfo &offset); +ExprPtr parseExpr(Cache *cache, const std::string &code, const codon::SrcInfo &offset); /// Parse a Seq file. -StmtPtr parseFile(const std::shared_ptr &cache, const std::string &file); +StmtPtr parseFile(Cache *cache, const std::string &file); /// Parse a OpenMP clause. -std::vector parseOpenMP(const std::shared_ptr &cache, - const std::string &code, +std::vector parseOpenMP(Cache *cache, const std::string &code, const codon::SrcInfo &loc); } // namespace ast diff --git a/codon/parser/peg/rules.h b/codon/parser/peg/rules.h index 52c3dbb2..b0c46f96 100644 --- a/codon/parser/peg/rules.h +++ b/codon/parser/peg/rules.h @@ -17,14 +17,13 @@ namespace codon { namespace ast { struct ParseContext { - std::shared_ptr cache; + Cache *cache; std::stack indent; int parens; int line_offset, col_offset; - ParseContext(std::shared_ptr cache, int parens = 0, int line_offset = 0, - int col_offset = 0) - : cache(move(cache)), parens(parens), line_offset(line_offset), - col_offset(col_offset) {} + ParseContext(Cache *cache, int parens = 0, int line_offset = 0, int col_offset = 0) + : cache(cache), parens(parens), line_offset(line_offset), col_offset(col_offset) { + } bool hasCustomStmtKeyword(const std::string &kwd, bool hasExpr) const { auto i = cache->customBlockStmts.find(kwd); diff --git a/codon/parser/visitors/doc/doc.cpp b/codon/parser/visitors/doc/doc.cpp index fcc39fb0..e5dc470b 100644 --- a/codon/parser/visitors/doc/doc.cpp +++ b/codon/parser/visitors/doc/doc.cpp @@ -1,5 +1,6 @@ #include "doc.h" +#include #include #include #include @@ -89,7 +90,8 @@ std::shared_ptr DocVisitor::apply(const std::string &argv0, const std::vector &files) { auto shared = std::make_shared(); shared->argv0 = argv0; - shared->cache = std::make_shared(argv0); + auto cache = std::make_unique(argv0); + shared->cache = cache.get(); auto stdlib = getImportFile(argv0, "internal", "", true, ""); auto ast = ast::parseFile(shared->cache, stdlib->path); @@ -113,15 +115,15 @@ std::shared_ptr DocVisitor::apply(const std::string &argv0, DocVisitor(shared->modules[""]).transformModule(std::move(ast)); auto ctx = std::make_shared(shared); - char abs[PATH_MAX]; for (auto &f : files) { - realpath(f.c_str(), abs); - ctx->setFilename(abs); - ast = ast::parseFile(shared->cache, abs); + auto path = std::filesystem::canonical(std::filesystem::path(f)).string(); + ctx->setFilename(path); + ast = ast::parseFile(shared->cache, path); // LOG("parsing {}", f); DocVisitor(ctx).transformModule(std::move(ast)); } + shared->cache = nullptr; return shared->j; } diff --git a/codon/parser/visitors/doc/doc.h b/codon/parser/visitors/doc/doc.h index c1fcc6b1..b3fa6719 100644 --- a/codon/parser/visitors/doc/doc.h +++ b/codon/parser/visitors/doc/doc.h @@ -41,7 +41,7 @@ struct DocShared { std::shared_ptr j; std::unordered_map> modules; std::string argv0; - std::shared_ptr cache; + Cache *cache; std::unordered_map> generics; DocShared() : itemID(1) {} }; diff --git a/codon/parser/visitors/format/format.cpp b/codon/parser/visitors/format/format.cpp index 07b03e1d..434f0d00 100644 --- a/codon/parser/visitors/format/format.cpp +++ b/codon/parser/visitors/format/format.cpp @@ -9,7 +9,7 @@ using fmt::format; namespace codon { namespace ast { -FormatVisitor::FormatVisitor(bool html, std::shared_ptr cache) +FormatVisitor::FormatVisitor(bool html, Cache *cache) : renderType(false), renderHTML(html), indent(0), cache(cache) { if (renderHTML) { header = "\n"; diff --git a/codon/parser/visitors/format/format.h b/codon/parser/visitors/format/format.h index ca2e05cf..2f0ffd02 100644 --- a/codon/parser/visitors/format/format.h +++ b/codon/parser/visitors/format/format.h @@ -25,7 +25,7 @@ class FormatVisitor : public CallbackASTVisitor { std::string commentStart, commentEnd; std::string keywordStart, keywordEnd; - std::shared_ptr cache; + Cache *cache; private: template std::string renderExpr(T &&t, Ts &&...args) { @@ -44,15 +44,15 @@ private: std::string keyword(const std::string &s) const; public: - FormatVisitor(bool html, std::shared_ptr cache = nullptr); + FormatVisitor(bool html, Cache *cache = nullptr); std::string transform(const ExprPtr &e) override; std::string transform(const Expr *expr); std::string transform(const StmtPtr &stmt) override; std::string transform(Stmt *stmt, int indent); template - static std::string apply(const T &stmt, std::shared_ptr cache = nullptr, - bool html = false, bool init = false) { + static std::string apply(const T &stmt, Cache *cache = nullptr, bool html = false, + bool init = false) { auto t = FormatVisitor(html, cache); return fmt::format("{}{}{}", t.header, t.transform(stmt), t.footer); } diff --git a/codon/parser/visitors/simplify/simplify.cpp b/codon/parser/visitors/simplify/simplify.cpp index 3c3fcf3a..ef171ba4 100644 --- a/codon/parser/visitors/simplify/simplify.cpp +++ b/codon/parser/visitors/simplify/simplify.cpp @@ -17,14 +17,13 @@ namespace ast { using namespace types; -StmtPtr SimplifyVisitor::apply( - std::shared_ptr cache, const StmtPtr &node, const std::string &file, - const std::unordered_map &defines, bool barebones) { +StmtPtr +SimplifyVisitor::apply(Cache *cache, const StmtPtr &node, const std::string &file, + const std::unordered_map &defines, + bool barebones) { std::vector stmts; auto preamble = std::make_shared(); - - if (!cache->module) - cache->module = new codon::ir::Module("", cache); + seqassert(cache->module, "cache's module is not set"); // Load standard library if it has not been loaded. if (!in(cache->imports, STDLIB_IMPORT)) { @@ -89,7 +88,7 @@ StmtPtr SimplifyVisitor::apply( } // Reserve the following static identifiers. for (auto name : {"staticlen", "compile_error", "isinstance", "hasattr", "type", - "TypeVar", "Callable"}) + "TypeVar", "Callable", "argv"}) stdlib->generateCanonicalName(name); // This code must be placed in a preamble (these are not POD types but are diff --git a/codon/parser/visitors/simplify/simplify.h b/codon/parser/visitors/simplify/simplify.h index 39777130..7060ad46 100644 --- a/codon/parser/visitors/simplify/simplify.h +++ b/codon/parser/visitors/simplify/simplify.h @@ -27,9 +27,7 @@ namespace ast { * ➡️ Note: This visitor *copies* the incoming AST and does not modify it. */ class SimplifyVisitor : public CallbackASTVisitor { - /// Shared simplification context. - std::shared_ptr ctx; - +public: /// Simplification step will divide the input AST into four sub-ASTs that are stored /// here: /// - Type (class) signatures @@ -47,6 +45,11 @@ class SimplifyVisitor : public CallbackASTVisitor { std::vector globals; std::vector functions; }; + +private: + /// Shared simplification context. + std::shared_ptr ctx; + /// Preamble contains shared definition statements and is shared across all visitors /// (in all modules). See Preamble (type) for more details. std::shared_ptr preamble; @@ -69,8 +72,7 @@ public: /// Each value is passed as a string (integer part is ignored). /// The method will replace this map with a map that links canonical names /// to their string and integer values. - static StmtPtr apply(std::shared_ptr cache, const StmtPtr &node, - const std::string &file, + static StmtPtr apply(Cache *cache, const StmtPtr &node, const std::string &file, const std::unordered_map &defines, bool barebones = false); diff --git a/codon/parser/visitors/simplify/simplify_ctx.cpp b/codon/parser/visitors/simplify/simplify_ctx.cpp index d5d0f15f..711eeb9c 100644 --- a/codon/parser/visitors/simplify/simplify_ctx.cpp +++ b/codon/parser/visitors/simplify/simplify_ctx.cpp @@ -17,7 +17,7 @@ SimplifyItem::SimplifyItem(Kind k, std::string base, std::string canonicalName, bool global) : kind(k), base(move(base)), canonicalName(move(canonicalName)), global(global) {} -SimplifyContext::SimplifyContext(std::string filename, std::shared_ptr cache) +SimplifyContext::SimplifyContext(std::string filename, Cache *cache) : Context(move(filename)), cache(move(cache)), isStdlibLoading(false), moduleName{ImportFile::PACKAGE, "", ""}, canAssign(true), allowTypeOf(true), substitutions(nullptr) {} diff --git a/codon/parser/visitors/simplify/simplify_ctx.h b/codon/parser/visitors/simplify/simplify_ctx.h index 41d94ff1..ed5ad79a 100644 --- a/codon/parser/visitors/simplify/simplify_ctx.h +++ b/codon/parser/visitors/simplify/simplify_ctx.h @@ -51,7 +51,7 @@ public: */ struct SimplifyContext : public Context { /// A pointer to the shared cache. - std::shared_ptr cache; + Cache *cache; /// A base scope definition. Each function or a class defines a new base scope. struct Base { @@ -95,7 +95,7 @@ struct SimplifyContext : public Context { std::unordered_map *substitutions; public: - SimplifyContext(std::string filename, std::shared_ptr cache); + SimplifyContext(std::string filename, Cache *cache); using Context::add; /// Convenience method for adding an object to the context. diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 6feb2c32..53edd73c 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -156,7 +156,7 @@ void SimplifyVisitor::visit(AssertStmt *stmt) { ExprPtr msg = N(""); if (stmt->message) msg = N(N("str"), clone(stmt->message)); - if (ctx->getLevel() && ctx->bases.back().attributes & FLAG_TEST) + if (ctx->getLevel() && (ctx->bases.back().attributes & FLAG_TEST)) resultStmt = transform( N(N("!", clone(stmt->expr)), N(N(N("__internal__", "seq_assert_test"), @@ -301,7 +301,7 @@ void SimplifyVisitor::visit(ThrowStmt *stmt) { } void SimplifyVisitor::visit(WithStmt *stmt) { - assert(stmt->items.size()); + seqassert(stmt->items.size(), "stmt->items is empty"); std::vector content; for (int i = int(stmt->items.size()) - 1; i >= 0; i--) { std::string var = @@ -328,7 +328,8 @@ void SimplifyVisitor::visit(GlobalStmt *stmt) { error("not a top-level variable"); seqassert(!val->canonicalName.empty(), "'{}' does not have a canonical name", stmt->var); - ctx->cache->globals.insert(val->canonicalName); + if (!in(ctx->cache->globals, val->canonicalName)) + ctx->cache->globals[val->canonicalName] = nullptr; val->global = true; ctx->add(SimplifyItem::Var, stmt->var, val->canonicalName, true); } @@ -1042,8 +1043,9 @@ StmtPtr SimplifyVisitor::transformAssignment(const ExprPtr &lhs, const ExprPtr & // ctx->moduleName != MODULE_MAIN; // ⚠️ TODO: should we make __main__ top-level variables NOT global by default? // Problem: a = [1]; def foo(): a.append(2) won't work anymore as in Python. - if (global && !isStatic) - ctx->cache->globals.insert(canonical); + if (global && !isStatic && !(r && r->isType()) && + !in(ctx->cache->globals, canonical)) + ctx->cache->globals[canonical] = nullptr; // Handle type aliases as well! ctx->add(r && r->isType() ? SimplifyItem::Type : SimplifyItem::Var, e->value, canonical, global); @@ -1336,7 +1338,8 @@ void SimplifyVisitor::transformNewImport(const ImportFile &file) { // loaded) preamble->globals.push_back(N( N(importDoneVar = importVar + "_done"), N(false))); - ctx->cache->globals.insert(importDoneVar); + if (!in(ctx->cache->globals, importDoneVar)) + ctx->cache->globals[importDoneVar] = nullptr; std::vector stmts; stmts.push_back(nullptr); // placeholder to be filled later! // We need to wrap all imported top-level statements (not signatures! they have @@ -1436,7 +1439,7 @@ StmtPtr SimplifyVisitor::transformLLVMDefinition(const Stmt *codeStmt) { StmtPtr SimplifyVisitor::codegenMagic(const std::string &op, const Expr *typExpr, const std::vector &args, bool isRecord) { #define I(s) N(s) - assert(typExpr); + seqassert(typExpr, "typExpr is null"); ExprPtr ret; std::vector fargs; std::vector stmts; diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index 587e9ab2..13a89dfa 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -1,5 +1,6 @@ #include "translate.h" +#include #include #include #include @@ -21,19 +22,34 @@ namespace ast { TranslateVisitor::TranslateVisitor(std::shared_ptr ctx) : ctx(std::move(ctx)), result(nullptr) {} -ir::Module *TranslateVisitor::apply(std::shared_ptr cache, StmtPtr stmts) { - auto main = cast(cache->module->getMainFunc()); - - char buf[PATH_MAX + 1]; - realpath(cache->module0.c_str(), buf); - main->setSrcInfo({std::string(buf), 0, 0, 0}); +ir::Func *TranslateVisitor::apply(Cache *cache, StmtPtr stmts) { + ir::BodiedFunc *main; + if (cache->isJit) { + auto fnName = format("_jit_{}", cache->jitCell); + main = cache->module->Nr(fnName); + main->setSrcInfo({"", 0, 0, 0}); + main->setGlobal(); + auto irType = cache->module->unsafeGetFuncType( + fnName, cache->classes["void"].realizations["void"]->ir, {}, false); + main->realize(irType, {}); + main->setJIT(); + } else { + main = cast(cache->module->getMainFunc()); + auto path = + std::filesystem::canonical(std::filesystem::path(cache->module0)).string(); + main->setSrcInfo({path, 0, 0, 0}); + } auto block = cache->module->Nr("body"); main->setBody(block); - cache->codegenCtx = std::make_shared(cache, block, main); + if (!cache->codegenCtx) + cache->codegenCtx = std::make_shared(cache); + cache->codegenCtx->bases = {main}; + cache->codegenCtx->series = {block}; + TranslateVisitor(cache->codegenCtx).transform(stmts); - return cache->module; + return main; } /************************************************************************************/ @@ -228,12 +244,15 @@ void TranslateVisitor::visit(AssignStmt *stmt) { auto var = stmt->lhs->getId()->value; if (!stmt->rhs && var == VAR_ARGV) { ctx->add(TranslateItem::Var, var, ctx->getModule()->getArgVar()); + ctx->cache->globals[var] = ctx->getModule()->getArgVar(); } else if (!stmt->rhs || !stmt->rhs->isType()) { auto *newVar = make(stmt, getType((stmt->rhs ? stmt->rhs : stmt->lhs)->getType()), in(ctx->cache->globals, var), var); if (!in(ctx->cache->globals, var)) ctx->getBase()->push_back(newVar); + else + ctx->cache->globals[var] = newVar; ctx->add(TranslateItem::Var, var, newVar); if (stmt->rhs) result = make(stmt, newVar, transform(stmt->rhs)); diff --git a/codon/parser/visitors/translate/translate.h b/codon/parser/visitors/translate/translate.h index fb2578c9..8880e6f9 100644 --- a/codon/parser/visitors/translate/translate.h +++ b/codon/parser/visitors/translate/translate.h @@ -22,7 +22,7 @@ class TranslateVisitor : public CallbackASTVisitor { public: explicit TranslateVisitor(std::shared_ptr ctx); - static codon::ir::Module *apply(std::shared_ptr cache, StmtPtr stmts); + static codon::ir::Func *apply(Cache *cache, StmtPtr stmts); ir::Value *transform(const ExprPtr &expr) override; ir::Value *transform(const StmtPtr &stmt) override; diff --git a/codon/parser/visitors/translate/translate_ctx.cpp b/codon/parser/visitors/translate/translate_ctx.cpp index 9506aef0..690c0b84 100644 --- a/codon/parser/visitors/translate/translate_ctx.cpp +++ b/codon/parser/visitors/translate/translate_ctx.cpp @@ -11,13 +11,9 @@ namespace codon { namespace ast { -TranslateContext::TranslateContext(std::shared_ptr cache, - codon::ir::SeriesFlow *series, - codon::ir::BodiedFunc *base) +TranslateContext::TranslateContext(Cache *cache) : Context(""), cache(std::move(cache)) { stack.push_front(std::vector()); - bases.push_back(base); - addSeries(series); } std::shared_ptr TranslateContext::find(const std::string &name) const { diff --git a/codon/parser/visitors/translate/translate_ctx.h b/codon/parser/visitors/translate/translate_ctx.h index 6f641d50..6d182492 100644 --- a/codon/parser/visitors/translate/translate_ctx.h +++ b/codon/parser/visitors/translate/translate_ctx.h @@ -45,15 +45,14 @@ struct TranslateItem { */ struct TranslateContext : public Context { /// A pointer to the shared cache. - std::shared_ptr cache; + Cache *cache; /// Stack of function bases. std::vector bases; /// Stack of IR series (blocks). std::vector series; public: - TranslateContext(std::shared_ptr cache, codon::ir::SeriesFlow *series, - codon::ir::BodiedFunc *base); + TranslateContext(Cache *cache); using Context::add; /// Convenience method for adding an object to the context. diff --git a/codon/parser/visitors/typecheck/typecheck.cpp b/codon/parser/visitors/typecheck/typecheck.cpp index 675a0bbe..867d7da7 100644 --- a/codon/parser/visitors/typecheck/typecheck.cpp +++ b/codon/parser/visitors/typecheck/typecheck.cpp @@ -23,10 +23,12 @@ TypecheckVisitor::TypecheckVisitor(std::shared_ptr ctx, prependStmts = stmts ? stmts : std::make_shared>(); } -StmtPtr TypecheckVisitor::apply(std::shared_ptr cache, StmtPtr stmts) { - auto ctx = std::make_shared(cache); - cache->typeCtx = ctx; - TypecheckVisitor v(ctx); +StmtPtr TypecheckVisitor::apply(Cache *cache, StmtPtr stmts) { + if (!cache->typeCtx) { + auto ctx = std::make_shared(cache); + cache->typeCtx = ctx; + } + TypecheckVisitor v(cache->typeCtx); auto infer = v.inferTypes(stmts->clone(), true, ""); return std::move(infer.second); } diff --git a/codon/parser/visitors/typecheck/typecheck.h b/codon/parser/visitors/typecheck/typecheck.h index f7ce8aad..e8344461 100644 --- a/codon/parser/visitors/typecheck/typecheck.h +++ b/codon/parser/visitors/typecheck/typecheck.h @@ -26,7 +26,7 @@ class TypecheckVisitor : public CallbackASTVisitor { StmtPtr resultStmt; public: - static StmtPtr apply(std::shared_ptr cache, StmtPtr stmts); + static StmtPtr apply(Cache *cache, StmtPtr stmts); public: explicit TypecheckVisitor( diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.cpp b/codon/parser/visitors/typecheck/typecheck_ctx.cpp index a00fc299..ef2c0971 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.cpp +++ b/codon/parser/visitors/typecheck/typecheck_ctx.cpp @@ -14,7 +14,7 @@ using fmt::format; namespace codon { namespace ast { -TypeContext::TypeContext(std::shared_ptr cache) +TypeContext::TypeContext(Cache *cache) : Context(""), cache(move(cache)), typecheckLevel(0), allowActivation(true), age(0), realizationDepth(0) { stack.push_front(std::vector()); @@ -89,7 +89,7 @@ TypeContext::addUnbound(const Expr *expr, int level, bool setActive, char static types::TypePtr TypeContext::instantiate(const Expr *expr, types::TypePtr type, types::ClassType *generics, bool activate) { - assert(type); + seqassert(type, "type is null"); std::unordered_map genericCache; if (generics) for (auto &g : generics->generics) @@ -122,12 +122,12 @@ types::TypePtr TypeContext::instantiateGeneric(const Expr *expr, types::TypePtr root, const std::vector &generics) { auto c = root->getClass(); - assert(c); + seqassert(c, "root class is null"); auto g = std::make_shared("", ""); // dummy generic type if (generics.size() != c->generics.size()) error(expr->getSrcInfo(), "generics do not match"); for (int i = 0; i < c->generics.size(); i++) { - assert(c->generics[i].type); + seqassert(c->generics[i].type, "generic is null"); g->generics.push_back( types::ClassType::Generic("", "", generics[i], c->generics[i].id)); } diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.h b/codon/parser/visitors/typecheck/typecheck_ctx.h index 0fae844b..b0f6a597 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.h +++ b/codon/parser/visitors/typecheck/typecheck_ctx.h @@ -33,7 +33,7 @@ struct TypecheckItem { */ struct TypeContext : public Context { /// A pointer to the shared cache. - std::shared_ptr cache; + Cache *cache; /// A realization base definition. Each function realization defines a new base scope. /// Used to properly realize enclosed functions and to prevent mess with mutually @@ -68,7 +68,7 @@ struct TypeContext : public Context { std::set defaultCallDepth; public: - explicit TypeContext(std::shared_ptr cache); + explicit TypeContext(Cache *cache); using Context::add; /// Convenience method for adding an object to the context. diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index bf0ee4d4..a0c46863 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1143,8 +1143,9 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in // Typecheck given arguments with the expected (signature) types. bool unificationsDone = true; - assert((expr->ordered && typeArgs.empty()) || - (!expr->ordered && typeArgs.size() == calleeFn->funcGenerics.size())); + seqassert((expr->ordered && typeArgs.empty()) || + (!expr->ordered && typeArgs.size() == calleeFn->funcGenerics.size()), + "bad vector sizes"); for (int si = 0; !expr->ordered && si < calleeFn->funcGenerics.size(); si++) if (typeArgs[si]) { auto t = typeArgs[si]->type; diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index ecbde243..7347c4a0 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -149,7 +150,7 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { LOG_REALIZE("[realize] fn {} -> {} : base {} ; depth = {}", type->ast->name, type->realizedName(), ctx->getBase(), depth); { - _level++; + getLogger().level++; ctx->realizationDepth++; ctx->addBlock(); ctx->typecheckLevel++; @@ -205,7 +206,7 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { // Realize the return type. if (auto t = realize(type->args[0])) unify(type->args[0], t); - LOG_TYPECHECK("done with {} / {}", type->realizedName(), oldKey); + LOG_REALIZE("done with {} / {}", type->realizedName(), oldKey); // Create and store IR node and a realized AST to be used // during the code generation. @@ -220,8 +221,6 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { r->ir = ctx->cache->module->Nr(type->realizedName()); } else { r->ir = ctx->cache->module->Nr(type->realizedName()); - if (ast->attributes.has(Attr::ForceRealize)) - ir::cast(r->ir)->setBuiltin(); } auto parent = type->funcParent; @@ -257,7 +256,7 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { ctx->popBlock(); ctx->typecheckLevel--; ctx->realizationDepth--; - _level--; + getLogger().level--; } // Restore old bases back. ctx->bases.insert(ctx->bases.end(), oldBases.begin(), oldBases.end()); @@ -280,7 +279,7 @@ std::pair TypecheckVisitor::inferTypes(StmtPtr result, bool keepLa int minUnbound = ctx->cache->unboundCount; ctx->addBlock(); int iteration = 0; - for (int prevSize = INT_MAX;;) { + for (int prevSize = std::numeric_limits::max();;) { LOG_TYPECHECK("== iter {} ==========================================", iteration); ctx->typecheckLevel++; result = TypecheckVisitor(ctx).transform(result); @@ -306,7 +305,7 @@ std::pair TypecheckVisitor::inferTypes(StmtPtr result, bool keepLa std::map newActiveUnbounds; for (auto i = ctx->activeUnbounds.begin(); i != ctx->activeUnbounds.end();) { auto l = i->first->getLink(); - assert(l); + seqassert(l, "link is null"); if (l->kind == LinkType::Unbound) { newActiveUnbounds[i->first] = i->second; if (l->id >= minUnbound) @@ -405,20 +404,21 @@ ir::types::Type *TypecheckVisitor::getLLVMType(const types::ClassType *t) { } else if (name == "str") { handle = module->getStringType(); } else if (name == "Int" || name == "UInt") { - assert(statics.size() == 1 && statics[0]->type == StaticValue::INT && - types.empty()); + seqassert(statics.size() == 1 && statics[0]->type == StaticValue::INT && + types.empty(), + "bad generics/statics"); handle = module->Nr(statics[0]->getInt(), name == "Int"); } else if (name == "Ptr") { - assert(types.size() == 1 && statics.empty()); + seqassert(types.size() == 1 && statics.empty(), "bad generics/statics"); handle = module->unsafeGetPointerType(types[0]); } else if (name == "Generator") { - assert(types.size() == 1 && statics.empty()); + seqassert(types.size() == 1 && statics.empty(), "bad generics/statics"); handle = module->unsafeGetGeneratorType(types[0]); } else if (name == TYPE_OPTIONAL) { - assert(types.size() == 1 && statics.empty()); + seqassert(types.size() == 1 && statics.empty(), "bad generics/statics"); handle = module->unsafeGetOptionalType(types[0]); } else if (name == "NoneType") { - assert(types.empty() && statics.empty()); + seqassert(types.empty() && statics.empty(), "bad generics/statics"); auto record = ir::cast(module->unsafeGetMemberedType(realizedName)); record->realize({}, {}); diff --git a/codon/runtime/exc.cpp b/codon/runtime/exc.cpp index 0e583e90..23d546eb 100644 --- a/codon/runtime/exc.cpp +++ b/codon/runtime/exc.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -41,6 +43,8 @@ struct Backtrace { frames[count++] = {functionDup, filenameDup, pc, lineno}; } + void push_back(uintptr_t pc) { push_back("", "", pc, 0); } + void free() { for (auto i = 0; i < count; i++) { auto *frame = &frames[i]; @@ -54,7 +58,7 @@ struct Backtrace { }; void seq_backtrace_error_callback(void *data, const char *msg, int errnum) { - // nothing to do + // printf("seq_backtrace_error_callback: %s (errnum = %d)\n", msg, errnum); } int seq_backtrace_full_callback(void *data, uintptr_t pc, const char *filename, @@ -64,6 +68,12 @@ int seq_backtrace_full_callback(void *data, uintptr_t pc, const char *filename, return (bt->count < Backtrace::LIMIT) ? 0 : 1; } +int seq_backtrace_simple_callback(void *data, uintptr_t pc) { + auto *bt = ((Backtrace *)data); + bt->push_back(pc); + return (bt->count < Backtrace::LIMIT) ? 0 : 1; +} + /* * This is largely based on * llvm/examples/ExceptionDemo/ExceptionDemo.cpp @@ -127,7 +137,7 @@ static void seq_delete_exc(_Unwind_Exception *expToDelete) { if (!expToDelete || expToDelete->exception_class != ourBaseExceptionClass) return; auto *exc = (OurException *)((char *)expToDelete + ourBaseFromUnwindOffset); - if (seq_debug) { + if (seq_flags & SEQ_FLAG_DEBUG) { exc->bt.free(); } seq_free(exc); @@ -139,6 +149,7 @@ static void seq_delete_unwind_exc(_Unwind_Reason_Code reason, } static struct backtrace_state *state = nullptr; +static std::mutex stateLock; SEQ_FUNC void *seq_alloc_exc(int type, void *obj) { const size_t size = sizeof(OurException); @@ -148,19 +159,30 @@ SEQ_FUNC void *seq_alloc_exc(int type, void *obj) { e->obj = obj; e->unwindException.exception_class = ourBaseExceptionClass; e->unwindException.exception_cleanup = seq_delete_unwind_exc; - if (seq_debug) { + if (seq_flags & SEQ_FLAG_DEBUG) { e->bt.frames = nullptr; e->bt.count = 0; - if (!state) - state = backtrace_create_state(/*filename=*/nullptr, /*threaded=*/0, + + if (seq_flags & SEQ_FLAG_STANDALONE) { + if (!state) { + stateLock.lock(); + if (!state) + state = + backtrace_create_state(/*filename=*/nullptr, /*threaded=*/1, seq_backtrace_error_callback, /*data=*/nullptr); - backtrace_full(state, /*skip=*/1, seq_backtrace_full_callback, - seq_backtrace_error_callback, &e->bt); + stateLock.unlock(); + } + backtrace_full(state, /*skip=*/1, seq_backtrace_full_callback, + seq_backtrace_error_callback, &e->bt); + } else { + backtrace_simple(/*state=*/nullptr, /*skip=*/1, seq_backtrace_simple_callback, + seq_backtrace_error_callback, &e->bt); + } } return &(e->unwindException); } -static void print_from_last_dot(seq_str_t s) { +static void print_from_last_dot(seq_str_t s, std::ostringstream &buf) { char *p = s.str; int64_t n = s.len; @@ -172,7 +194,7 @@ static void print_from_last_dot(seq_str_t s) { } } - fwrite(p, 1, (size_t)n, stderr); + buf.write(p, (size_t)n); } SEQ_FUNC void seq_terminate(void *exc) { @@ -186,41 +208,64 @@ SEQ_FUNC void seq_terminate(void *exc) { exit((int)status); } - fprintf(stderr, "\033[1m"); - print_from_last_dot(hdr->type); + std::ostringstream buf; + if (seq_flags & SEQ_FLAG_JIT) + buf << codon::getCapturedOutput(); + + buf << "\033[1m"; + print_from_last_dot(hdr->type, buf); if (hdr->msg.len > 0) { - fprintf(stderr, ": "); - fprintf(stderr, "\033[0m"); - fwrite(hdr->msg.str, 1, (size_t)hdr->msg.len, stderr); + buf << ": \033[0m"; + buf.write(hdr->msg.str, hdr->msg.len); } else { - fprintf(stderr, "\033[0m"); + buf << "\033[0m"; } - fprintf(stderr, "\n\n"); - fprintf(stderr, "\033[1mRaised from:\033[0m \033[32m"); - fwrite(hdr->func.str, 1, (size_t)hdr->func.len, stderr); - fprintf(stderr, "\033[0m\n"); - fwrite(hdr->file.str, 1, (size_t)hdr->file.len, stderr); + buf << "\n\n\033[1mRaised from:\033[0m \033[32m"; + buf.write(hdr->func.str, hdr->func.len); + buf << "\033[0m\n"; + buf.write(hdr->file.str, hdr->file.len); if (hdr->line > 0) { - fprintf(stderr, ":%lld", (long long)hdr->line); + buf << ":" << hdr->line; if (hdr->col > 0) - fprintf(stderr, ":%lld", (long long)hdr->col); + buf << ":" << hdr->col; } - fprintf(stderr, "\n"); + buf << "\n"; - if (seq_debug) { + if ((seq_flags & SEQ_FLAG_DEBUG) && (seq_flags & SEQ_FLAG_STANDALONE)) { auto *bt = &base->bt; if (bt->count > 0) { - fprintf(stderr, "\n\033[1mBacktrace:\033[0m\n"); + buf << "\n\033[1mBacktrace:\033[0m\n"; for (unsigned i = 0; i < bt->count; i++) { auto *frame = &bt->frames[i]; - fprintf(stderr, " [\033[33m0x%lx\033[0m] \033[32m%s\033[0m %s:%d\n", frame->pc, - frame->function, frame->filename, frame->lineno); + buf << " " + << codon::makeBacktraceFrameString(frame->pc, std::string(frame->function), + std::string(frame->filename), + frame->lineno) + << "\n"; } } } - abort(); + auto output = buf.str(); + if (seq_flags & SEQ_FLAG_STANDALONE) { + fwrite(output.data(), 1, output.size(), stderr); + abort(); + } else { + auto *bt = &base->bt; + std::string msg(hdr->msg.str, hdr->msg.len); + std::string file(hdr->file.str, hdr->file.len); + std::string type(hdr->type.str, hdr->type.len); + + std::vector backtrace; + if (seq_flags & SEQ_FLAG_DEBUG) { + for (unsigned i = 0; i < bt->count; i++) { + backtrace.push_back(bt->frames[i].pc); + } + } + throw codon::JITError(output, msg, type, file, (int)hdr->line, (int)hdr->col, + backtrace); + } } SEQ_FUNC void seq_throw(void *exc) { @@ -546,3 +591,23 @@ SEQ_FUNC int64_t seq_exc_offset() { SEQ_FUNC uint64_t seq_exc_class() { return genClass(ourBaseExcpClassChars, sizeof(ourBaseExcpClassChars)); } + +std::string codon::makeBacktraceFrameString(uintptr_t pc, const std::string &func, + const std::string &file, int line, + int col) { + std::ostringstream buf; + buf << "[\033[33m0x" << std::hex << pc << std::dec << "\033[0m]"; + if (!func.empty()) { + buf << " \033[32m" << func << "\033[0m"; + if (!file.empty()) { + buf << " at \033[36m" << file << "\033[0m"; + if (line != 0) { + buf << ":\033[33m" << line << "\033[0m"; + if (col != 0) { + buf << ":\033[33m" << col << "\033[0m"; + } + } + } + } + return buf.str(); +} diff --git a/codon/runtime/lib.cpp b/codon/runtime/lib.cpp index 8eb69221..d444dc9d 100644 --- a/codon/runtime/lib.cpp +++ b/codon/runtime/lib.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -45,16 +46,16 @@ static void register_thread(kmp_int32 *global_tid, kmp_int32 *bound_tid) { void seq_exc_init(); -int seq_debug; +int seq_flags; -SEQ_FUNC void seq_init(int d) { +SEQ_FUNC void seq_init(int flags) { GC_INIT(); GC_set_warn_proc(GC_ignore_warn_proc); GC_allow_register_threads(); // equivalent to: #pragma omp parallel { register_thread } __kmpc_fork_call(&dummy_loc, 0, (kmpc_micro)register_thread); seq_exc_init(); - seq_debug = d; + seq_flags = flags; } SEQ_FUNC bool seq_is_macos() { @@ -214,10 +215,25 @@ SEQ_FUNC seq_str_t seq_check_errno() { return {0, nullptr}; } -SEQ_FUNC void seq_print(seq_str_t str) { fwrite(str.str, 1, (size_t)str.len, stdout); } +SEQ_FUNC void seq_print(seq_str_t str) { seq_print_full(str, stdout); } + +static std::ostringstream capture; +static std::mutex captureLock; SEQ_FUNC void seq_print_full(seq_str_t str, FILE *fo) { - fwrite(str.str, 1, (size_t)str.len, fo); + if ((seq_flags & SEQ_FLAG_JIT) && (fo == stdout || fo == stderr)) { + captureLock.lock(); + capture.write(str.str, str.len); + captureLock.unlock(); + } else { + fwrite(str.str, 1, (size_t)str.len, fo); + } +} + +std::string codon::getCapturedOutput() { + std::string result = capture.str(); + capture.str(""); + return result; } SEQ_FUNC void *seq_stdin() { return stdin; } diff --git a/codon/runtime/lib.h b/codon/runtime/lib.h index 30ca5856..8894577d 100644 --- a/codon/runtime/lib.h +++ b/codon/runtime/lib.h @@ -1,29 +1,30 @@ -#ifndef CODON_RUNTIME_LIB_H -#define CODON_RUNTIME_LIB_H +#pragma once #include #include #include #include +#include +#include #include +#include + +#define SEQ_FLAG_DEBUG (1 << 0) // compiled/running in debug mode +#define SEQ_FLAG_JIT (1 << 1) // compiled/running in JIT mode +#define SEQ_FLAG_STANDALONE (1 << 2) // compiled as a standalone object/binary #define SEQ_FUNC extern "C" typedef int64_t seq_int_t; -struct seq_t { - seq_int_t len; - char *seq; -}; - struct seq_str_t { seq_int_t len; char *str; }; -extern int seq_debug; +extern int seq_flags; -SEQ_FUNC void seq_init(int debug); +SEQ_FUNC void seq_init(int flags); SEQ_FUNC bool seq_is_macos(); SEQ_FUNC seq_int_t seq_pid(); @@ -76,4 +77,34 @@ SEQ_FUNC void *seq_rlock_new(); SEQ_FUNC bool seq_rlock_acquire(void *lock, bool block, double timeout); SEQ_FUNC void seq_rlock_release(void *lock); -#endif /* CODON_RUNTIME_LIB_H */ +namespace codon { +class JITError : public std::runtime_error { +private: + std::string output; + std::string type; + std::string file; + int line; + int col; + std::vector backtrace; + +public: + JITError(const std::string &output, const std::string &what, const std::string &type, + const std::string &file, int line, int col, + std::vector backtrace = {}) + : std::runtime_error(what), output(output), type(type), file(file), line(line), + col(col), backtrace(std::move(backtrace)) {} + + std::string getOutput() const { return output; } + std::string getType() const { return type; } + std::string getFile() const { return file; } + int getLine() const { return line; } + int getCol() const { return col; } + std::vector getBacktrace() const { return backtrace; } +}; + +std::string makeBacktraceFrameString(uintptr_t pc, const std::string &func = "", + const std::string &file = "", int line = 0, + int col = 0); + +std::string getCapturedOutput(); +} // namespace codon diff --git a/codon/sir/func.h b/codon/sir/func.h index c8cecfc9..967ae362 100644 --- a/codon/sir/func.h +++ b/codon/sir/func.h @@ -88,8 +88,8 @@ private: std::list symbols; /// the function body Value *body = nullptr; - /// whether the function is builtin - bool builtin = false; + /// whether the function is a JIT input + bool jit = false; public: static const char NodeId; @@ -136,11 +136,11 @@ public: /// @param b the new body void setBody(Flow *b) { body = b; } - /// @return true if the function is builtin - bool isBuiltin() const { return builtin; } - /// Changes the function's builtin status. - /// @param v true if builtin, false otherwise - void setBuiltin(bool v = true) { builtin = v; } + /// @return true if the function is a JIT input + bool isJIT() const { return jit; } + /// Changes the function's JIT input status. + /// @param v true if JIT input, false otherwise + void setJIT(bool v = true) { jit = v; } protected: std::vector doGetUsedValues() const override { diff --git a/codon/sir/llvm/llvisitor.cpp b/codon/sir/llvm/llvisitor.cpp index daec005e..cd0cf2ee 100644 --- a/codon/sir/llvm/llvisitor.cpp +++ b/codon/sir/llvm/llvisitor.cpp @@ -1,51 +1,21 @@ #include "llvisitor.h" #include +#include +#include #include #include #include +#include "codon/compiler/debug_listener.h" +#include "codon/compiler/memory_manager.h" #include "codon/runtime/lib.h" #include "codon/sir/dsl/codegen.h" -#include "codon/sir/llvm/memory_manager.h" #include "codon/sir/llvm/optimize.h" #include "codon/util/common.h" namespace codon { namespace ir { -namespace { -std::string getNameForFunction(const Func *x) { - if (auto *externalFunc = cast(x)) { - return x->getUnmangledName(); - } else { - return x->referenceString(); - } -} - -std::string getDebugNameForVariable(const Var *x) { - std::string name = x->getName(); - auto pos = name.find("."); - if (pos != 0 && pos != std::string::npos) { - return name.substr(0, pos); - } else { - return name; - } -} - -const SrcInfo *getSrcInfo(const Node *x) { - if (auto *srcInfo = x->getAttribute()) { - return &srcInfo->info; - } else { - static SrcInfo defaultSrcInfo("", 0, 0, 0); - return &defaultSrcInfo; - } -} - -llvm::Value *getDummyVoidValue(llvm::LLVMContext &context) { - return llvm::ConstantTokenNone::get(context); -} -} // namespace - llvm::DIFile *LLVMVisitor::DebugInfo::getFile(const std::string &path) { std::string filename; std::string directory; @@ -60,10 +30,11 @@ llvm::DIFile *LLVMVisitor::DebugInfo::getFile(const std::string &path) { return builder->createFile(filename, directory); } -LLVMVisitor::LLVMVisitor(bool debug, const std::string &flags) - : util::ConstVisitor(), context(), builder(context), module(), func(nullptr), - block(nullptr), value(nullptr), vars(), funcs(), coro(), loops(), trycatch(), - db(debug, flags), plugins(nullptr) { +LLVMVisitor::LLVMVisitor() + : util::ConstVisitor(), context(std::make_unique()), M(), + B(std::make_unique>(*context)), func(nullptr), block(nullptr), + value(nullptr), vars(), funcs(), coro(), loops(), trycatch(), db(), + plugins(nullptr) { llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmPrinters(); @@ -107,13 +78,170 @@ LLVMVisitor::LLVMVisitor(bool debug, const std::string &flags) llvm::initializeTypePromotionPass(registry); } +llvm::GlobalValue::LinkageTypes LLVMVisitor::getDefaultLinkage() { + return db.jit ? llvm::GlobalValue::ExternalLinkage + : llvm::GlobalValue::PrivateLinkage; +} + +void LLVMVisitor::registerGlobal(const Var *var) { + if (!var->isGlobal()) + return; + + if (auto *f = cast(var)) { + makeLLVMFunction(f); + insertFunc(f, func); + } else { + llvm::Type *llvmType = getLLVMType(var->getType()); + if (llvmType->isVoidTy()) { + insertVar(var, getDummyVoidValue()); + } else { + auto *storage = new llvm::GlobalVariable( + *M, llvmType, /*isConstant=*/false, getDefaultLinkage(), + llvm::Constant::getNullValue(llvmType), var->getName()); + insertVar(var, storage); + + // debug info + auto *srcInfo = getSrcInfo(var); + llvm::DIFile *file = db.getFile(srcInfo->file); + llvm::DIScope *scope = db.unit; + llvm::DIGlobalVariableExpression *debugVar = + db.builder->createGlobalVariableExpression( + scope, getDebugNameForVariable(var), var->getName(), file, srcInfo->line, + getDIType(var->getType()), + /*IsLocalToUnit=*/true); + storage->addDebugInfo(debugVar); + } + } +} + +llvm::Value *LLVMVisitor::getVar(const Var *var) { + auto it = vars.find(var->getId()); + if (db.jit && var->isGlobal()) { + if (it != vars.end()) { + if (!it->second) { // if value is null, it's from another module + // see if it's in the module already + auto name = var->getName(); + if (auto *global = M->getNamedValue(name)) + return global; + + llvm::Type *llvmType = getLLVMType(var->getType()); + auto *storage = new llvm::GlobalVariable(*M, llvmType, /*isConstant=*/false, + llvm::GlobalValue::ExternalLinkage, + /*Initializer=*/nullptr, name); + storage->setExternallyInitialized(true); + + // debug info + auto *srcInfo = getSrcInfo(var); + llvm::DIFile *file = db.getFile(srcInfo->file); + llvm::DIScope *scope = db.unit; + llvm::DIGlobalVariableExpression *debugVar = + db.builder->createGlobalVariableExpression( + scope, getDebugNameForVariable(var), name, file, srcInfo->line, + getDIType(var->getType()), + /*IsLocalToUnit=*/true); + storage->addDebugInfo(debugVar); + insertVar(var, storage); + return storage; + } + } else { + registerGlobal(var); + return it->second; + } + } + return (it != vars.end()) ? it->second : nullptr; +} + +llvm::Function *LLVMVisitor::getFunc(const Func *func) { + auto it = funcs.find(func->getId()); + if (db.jit) { + if (it != funcs.end()) { + if (!it->second) { // if value is null, it's from another module + // see if it's in the module already + const std::string name = getNameForFunction(func); + if (auto *g = M->getFunction(name)) + return g; + + auto *funcType = cast(func->getType()); + llvm::Type *returnType = getLLVMType(funcType->getReturnType()); + std::vector argTypes; + for (const auto &argType : *funcType) { + argTypes.push_back(getLLVMType(argType)); + } + + auto *llvmFuncType = + llvm::FunctionType::get(returnType, argTypes, funcType->isVariadic()); + auto *g = llvm::Function::Create(llvmFuncType, llvm::Function::ExternalLinkage, + name, M.get()); + insertFunc(func, g); + return g; + } + } else { + registerGlobal(func); + return it->second; + } + } + return (it != funcs.end()) ? it->second : nullptr; +} + +std::unique_ptr LLVMVisitor::makeModule(llvm::LLVMContext &context, + const SrcInfo *src) { + auto M = std::make_unique("codon", context); + M->setTargetTriple(llvm::EngineBuilder().selectTarget()->getTargetTriple().str()); + M->setDataLayout(llvm::EngineBuilder().selectTarget()->createDataLayout()); + B = std::make_unique>(context); + + auto *srcInfo = src ? src : getDefaultSrcInfo(); + M->setSourceFileName(srcInfo->file); + // debug info setup + db.builder = std::make_unique(*M); + llvm::DIFile *file = db.getFile(srcInfo->file); + db.unit = db.builder->createCompileUnit(llvm::dwarf::DW_LANG_C, file, + ("codon version " CODON_VERSION), !db.debug, + db.flags, + /*RV=*/0); + M->addModuleFlag(llvm::Module::Warning, "Debug Info Version", + llvm::DEBUG_METADATA_VERSION); + // darwin only supports dwarf2 + if (llvm::Triple(M->getTargetTriple()).isOSDarwin()) { + M->addModuleFlag(llvm::Module::Warning, "Dwarf Version", 2); + } + + return M; +} + +std::pair, std::unique_ptr> +LLVMVisitor::takeModule(const SrcInfo *src) { + db.builder->finalize(); + auto currentContext = std::move(context); + auto currentModule = std::move(M); + + // reset all LLVM fields/data -- they are owned by the context + B = {}; + func = nullptr; + block = nullptr; + value = nullptr; + for (auto &it : vars) + it.second = nullptr; + for (auto &it : funcs) + it.second = nullptr; + coro.reset(); + loops.clear(); + trycatch.clear(); + db.reset(); + + context = std::make_unique(); + M = makeModule(*context, src); + + return {std::move(currentModule), std::move(currentContext)}; +} + void LLVMVisitor::setDebugInfoForNode(const Node *x) { if (x && func) { auto *srcInfo = getSrcInfo(x); - builder.SetCurrentDebugLocation(llvm::DILocation::get( - context, srcInfo->line, srcInfo->col, func->getSubprogram())); + B->SetCurrentDebugLocation(llvm::DILocation::get( + *context, srcInfo->line, srcInfo->col, func->getSubprogram())); } else { - builder.SetCurrentDebugLocation(llvm::DebugLoc()); + B->SetCurrentDebugLocation(llvm::DebugLoc()); } } @@ -124,7 +252,10 @@ void LLVMVisitor::process(const Node *x) { void LLVMVisitor::dump(const std::string &filename) { writeToLLFile(filename, false); } -void LLVMVisitor::runLLVMPipeline() { optimize(module.get(), db.debug, plugins); } +void LLVMVisitor::runLLVMPipeline() { + db.builder->finalize(); + optimize(M.get(), db.debug, db.jit, plugins); +} void LLVMVisitor::writeToObjectFile(const std::string &filename) { runLLVMPipeline(); @@ -136,19 +267,19 @@ void LLVMVisitor::writeToObjectFile(const std::string &filename) { compilationError(err.message()); llvm::raw_pwrite_stream *os = &out->os(); - auto machine = getTargetMachine(module.get()); + auto machine = getTargetMachine(M.get()); auto &llvmtm = static_cast(*machine); auto *mmiwp = new llvm::MachineModuleInfoWrapperPass(&llvmtm); llvm::legacy::PassManager pm; - llvm::TargetLibraryInfoImpl tlii(llvm::Triple(module->getTargetTriple())); + llvm::TargetLibraryInfoImpl tlii(llvm::Triple(M->getTargetTriple())); pm.add(new llvm::TargetLibraryInfoWrapperPass(tlii)); seqassert(!machine->addPassesToEmitFile(pm, *os, nullptr, llvm::CGFT_ObjectFile, /*DisableVerify=*/true, mmiwp), "could not add passes"); const_cast(llvmtm.getObjFileLowering()) ->Initialize(mmiwp->getMMI().getContext(), *machine); - pm.run(*module); + pm.run(*M); out->keep(); } @@ -156,7 +287,7 @@ void LLVMVisitor::writeToBitcodeFile(const std::string &filename) { runLLVMPipeline(); std::error_code err; llvm::raw_fd_ostream stream(filename, err, llvm::sys::fs::F_None); - llvm::WriteBitcodeToFile(*module, stream); + llvm::WriteBitcodeToFile(*M, stream); if (err) { compilationError(err.message()); } @@ -167,7 +298,7 @@ void LLVMVisitor::writeToLLFile(const std::string &filename, bool optimize) { runLLVMPipeline(); auto fo = fopen(filename.c_str(), "w"); llvm::raw_fd_ostream fout(fileno(fo), true); - fout << *module; + fout << *M; fout.close(); } @@ -255,25 +386,38 @@ void LLVMVisitor::compile(const std::string &filename, void LLVMVisitor::run(const std::vector &args, const std::vector &libs, const char *const *envp) { runLLVMPipeline(); - llvm::Function *main = module->getFunction("main"); - llvm::EngineBuilder EB(std::move(module)); + llvm::Function *main = M->getFunction("main"); + llvm::EngineBuilder EB(std::move(M)); EB.setMCJITMemoryManager(std::make_unique()); llvm::ExecutionEngine *eng = EB.create(); - std::string err; + auto dbListener = std::unique_ptr(); + if (db.debug) { + dbListener = std::make_unique(); + eng->RegisterJITEventListener(dbListener.get()); + } + for (auto &lib : libs) { + std::string err; if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(lib.c_str(), &err)) { compilationError(err); } } - eng->runFunctionAsMain(main, args, envp); + try { + eng->runFunctionAsMain(main, args, envp); + } catch (const JITError &e) { + fmt::print(stderr, "{}", e.getOutput()); + if (db.debug) + fmt::print(stderr, "\n{}", dbListener->getPrettyBacktrace(e.getBacktrace())); + std::abort(); + } delete eng; } llvm::FunctionCallee LLVMVisitor::makeAllocFunc(bool atomic) { - auto f = module->getOrInsertFunction(atomic ? "seq_alloc_atomic" : "seq_alloc", - builder.getInt8PtrTy(), builder.getInt64Ty()); + auto f = M->getOrInsertFunction(atomic ? "seq_alloc_atomic" : "seq_alloc", + B->getInt8PtrTy(), B->getInt64Ty()); auto *g = cast(f.getCallee()); g->setDoesNotThrow(); g->setReturnDoesNotAlias(); @@ -282,46 +426,43 @@ llvm::FunctionCallee LLVMVisitor::makeAllocFunc(bool atomic) { } llvm::FunctionCallee LLVMVisitor::makePersonalityFunc() { - return module->getOrInsertFunction("seq_personality", builder.getInt32Ty(), - builder.getInt32Ty(), builder.getInt32Ty(), - builder.getInt64Ty(), builder.getInt8PtrTy(), - builder.getInt8PtrTy()); + return M->getOrInsertFunction("seq_personality", B->getInt32Ty(), B->getInt32Ty(), + B->getInt32Ty(), B->getInt64Ty(), B->getInt8PtrTy(), + B->getInt8PtrTy()); } llvm::FunctionCallee LLVMVisitor::makeExcAllocFunc() { - auto f = module->getOrInsertFunction("seq_alloc_exc", builder.getInt8PtrTy(), - builder.getInt32Ty(), builder.getInt8PtrTy()); + auto f = M->getOrInsertFunction("seq_alloc_exc", B->getInt8PtrTy(), B->getInt32Ty(), + B->getInt8PtrTy()); auto *g = cast(f.getCallee()); g->setDoesNotThrow(); return f; } llvm::FunctionCallee LLVMVisitor::makeThrowFunc() { - auto f = module->getOrInsertFunction("seq_throw", builder.getVoidTy(), - builder.getInt8PtrTy()); + auto f = M->getOrInsertFunction("seq_throw", B->getVoidTy(), B->getInt8PtrTy()); auto *g = cast(f.getCallee()); g->setDoesNotReturn(); return f; } llvm::FunctionCallee LLVMVisitor::makeTerminateFunc() { - auto f = module->getOrInsertFunction("seq_terminate", builder.getVoidTy(), - builder.getInt8PtrTy()); + auto f = M->getOrInsertFunction("seq_terminate", B->getVoidTy(), B->getInt8PtrTy()); auto *g = cast(f.getCallee()); g->setDoesNotReturn(); return f; } llvm::StructType *LLVMVisitor::getTypeInfoType() { - return llvm::StructType::get(builder.getInt32Ty()); + return llvm::StructType::get(B->getInt32Ty()); } llvm::StructType *LLVMVisitor::getPadType() { - return llvm::StructType::get(builder.getInt8PtrTy(), builder.getInt32Ty()); + return llvm::StructType::get(B->getInt8PtrTy(), B->getInt32Ty()); } llvm::StructType *LLVMVisitor::getExceptionType() { - return llvm::StructType::get(getTypeInfoType(), builder.getInt8PtrTy()); + return llvm::StructType::get(getTypeInfoType(), B->getInt8PtrTy()); } namespace { @@ -344,12 +485,12 @@ int typeIdxLookup(const std::string &name) { llvm::GlobalVariable *LLVMVisitor::getTypeIdxVar(const std::string &name) { auto *typeInfoType = getTypeInfoType(); const std::string typeVarName = "codon.typeidx." + (name.empty() ? "" : name); - llvm::GlobalVariable *tidx = module->getGlobalVariable(typeVarName); + llvm::GlobalVariable *tidx = M->getGlobalVariable(typeVarName); int idx = typeIdxLookup(name); if (!tidx) { tidx = new llvm::GlobalVariable( - *module, typeInfoType, /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, - llvm::ConstantStruct::get(typeInfoType, builder.getInt32(idx)), typeVarName); + *M, typeInfoType, /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, + llvm::ConstantStruct::get(typeInfoType, B->getInt32(idx)), typeVarName); } return tidx; } @@ -364,13 +505,13 @@ int LLVMVisitor::getTypeIdx(types::Type *catchType) { llvm::Value *LLVMVisitor::call(llvm::FunctionCallee callee, llvm::ArrayRef args) { - builder.SetInsertPoint(block); + B->SetInsertPoint(block); if (trycatch.empty()) { - return builder.CreateCall(callee, args); + return B->CreateCall(callee, args); } else { - auto *normalBlock = llvm::BasicBlock::Create(context, "invoke.normal", func); + auto *normalBlock = llvm::BasicBlock::Create(*context, "invoke.normal", func); auto *unwindBlock = trycatch.back().exceptionBlock; - auto *result = builder.CreateInvoke(callee, normalBlock, unwindBlock, args); + auto *result = B->CreateInvoke(callee, normalBlock, unwindBlock, args); block = normalBlock; return result; } @@ -410,70 +551,20 @@ LLVMVisitor::TryCatchData *LLVMVisitor::getInnermostTryCatchBeforeLoop() { } /* - * General values, module, functions, vars + * General values, M, functions, vars */ void LLVMVisitor::visit(const Module *x) { - module = std::make_unique("codon", context); - module->setTargetTriple( - llvm::EngineBuilder().selectTarget()->getTargetTriple().str()); - module->setDataLayout(llvm::EngineBuilder().selectTarget()->createDataLayout()); - auto *srcInfo = getSrcInfo(x->getMainFunc()); - module->setSourceFileName(srcInfo->file); - - // debug info setup - db.builder = std::make_unique(*module); - llvm::DIFile *file = db.getFile(srcInfo->file); - db.unit = db.builder->createCompileUnit(llvm::dwarf::DW_LANG_C, file, - ("codon version " CODON_VERSION), !db.debug, - db.flags, - /*RV=*/0); - module->addModuleFlag(llvm::Module::Warning, "Debug Info Version", - llvm::DEBUG_METADATA_VERSION); - // darwin only supports dwarf2 - if (llvm::Triple(module->getTargetTriple()).isOSDarwin()) { - module->addModuleFlag(llvm::Module::Warning, "Dwarf Version", 2); - } + // initialize M + M = makeModule(*context, getSrcInfo(x)); // args variable - const Var *argVar = x->getArgVar(); - llvm::Type *argVarType = getLLVMType(argVar->getType()); - auto *argStorage = new llvm::GlobalVariable( - *module, argVarType, /*isConstant=*/false, llvm::GlobalValue::PrivateLinkage, - llvm::Constant::getNullValue(argVarType), argVar->getName()); - vars.insert(argVar, argStorage); + seqassert(x->getArgVar()->isGlobal(), "arg var is not global"); + registerGlobal(x->getArgVar()); // set up global variables and initialize functions for (auto *var : *x) { - if (!var->isGlobal()) - continue; - - if (auto *f = cast(var)) { - makeLLVMFunction(f); - funcs.insert(f, func); - } else { - llvm::Type *llvmType = getLLVMType(var->getType()); - if (llvmType->isVoidTy()) { - vars.insert(var, getDummyVoidValue(context)); - } else { - auto *storage = new llvm::GlobalVariable( - *module, llvmType, /*isConstant=*/false, - llvm::GlobalVariable::PrivateLinkage, - llvm::Constant::getNullValue(llvmType), var->getName()); - vars.insert(var, storage); - - // debug info - auto *srcInfo = getSrcInfo(var); - llvm::DIFile *file = db.getFile(srcInfo->file); - llvm::DIScope *scope = db.unit; - llvm::DIGlobalVariableExpression *debugVar = - db.builder->createGlobalVariableExpression( - scope, getDebugNameForVariable(var), var->getName(), file, - srcInfo->line, getDIType(var->getType()), - /*IsLocalToUnit=*/true); - storage->addDebugInfo(debugVar); - } - } + registerGlobal(var); } // process functions @@ -490,23 +581,18 @@ void LLVMVisitor::visit(const Module *x) { setDebugInfoForNode(nullptr); // build canonical main function - auto *strType = - llvm::StructType::get(context, {builder.getInt64Ty(), builder.getInt8PtrTy()}); + auto *strType = llvm::StructType::get(*context, {B->getInt64Ty(), B->getInt8PtrTy()}); auto *arrType = - llvm::StructType::get(context, {builder.getInt64Ty(), strType->getPointerTo()}); + llvm::StructType::get(*context, {B->getInt64Ty(), strType->getPointerTo()}); auto *initFunc = llvm::cast( - module->getOrInsertFunction("seq_init", builder.getVoidTy(), builder.getInt32Ty()) - .getCallee()); + M->getOrInsertFunction("seq_init", B->getVoidTy(), B->getInt32Ty()).getCallee()); auto *strlenFunc = llvm::cast( - module - ->getOrInsertFunction("strlen", builder.getInt64Ty(), builder.getInt8PtrTy()) - .getCallee()); + M->getOrInsertFunction("strlen", B->getInt64Ty(), B->getInt8PtrTy()).getCallee()); auto *canonicalMainFunc = llvm::cast( - module - ->getOrInsertFunction("main", builder.getInt32Ty(), builder.getInt32Ty(), - builder.getInt8PtrTy()->getPointerTo()) + M->getOrInsertFunction("main", B->getInt32Ty(), B->getInt32Ty(), + B->getInt8PtrTy()->getPointerTo()) .getCallee()); canonicalMainFunc->setPersonalityFn( @@ -520,109 +606,116 @@ void LLVMVisitor::visit(const Module *x) { // The following generates code to put program arguments in an array, i.e.: // for (int i = 0; i < argc; i++) // array[i] = {strlen(argv[i]), argv[i]} - auto *entryBlock = llvm::BasicBlock::Create(context, "entry", canonicalMainFunc); - auto *loopBlock = llvm::BasicBlock::Create(context, "loop", canonicalMainFunc); - auto *bodyBlock = llvm::BasicBlock::Create(context, "body", canonicalMainFunc); - auto *exitBlock = llvm::BasicBlock::Create(context, "exit", canonicalMainFunc); + auto *entryBlock = llvm::BasicBlock::Create(*context, "entry", canonicalMainFunc); + auto *loopBlock = llvm::BasicBlock::Create(*context, "loop", canonicalMainFunc); + auto *bodyBlock = llvm::BasicBlock::Create(*context, "body", canonicalMainFunc); + auto *exitBlock = llvm::BasicBlock::Create(*context, "exit", canonicalMainFunc); - builder.SetInsertPoint(entryBlock); + B->SetInsertPoint(entryBlock); auto allocFunc = makeAllocFunc(/*atomic=*/false); - llvm::Value *len = builder.CreateZExt(argc, builder.getInt64Ty()); - llvm::Value *elemSize = - builder.getInt64(module->getDataLayout().getTypeAllocSize(strType)); - llvm::Value *allocSize = builder.CreateMul(len, elemSize); - llvm::Value *ptr = builder.CreateCall(allocFunc, allocSize); - ptr = builder.CreateBitCast(ptr, strType->getPointerTo()); + llvm::Value *len = B->CreateZExt(argc, B->getInt64Ty()); + llvm::Value *elemSize = B->getInt64(M->getDataLayout().getTypeAllocSize(strType)); + llvm::Value *allocSize = B->CreateMul(len, elemSize); + llvm::Value *ptr = B->CreateCall(allocFunc, allocSize); + ptr = B->CreateBitCast(ptr, strType->getPointerTo()); llvm::Value *arr = llvm::UndefValue::get(arrType); - arr = builder.CreateInsertValue(arr, len, 0); - arr = builder.CreateInsertValue(arr, ptr, 1); - builder.CreateBr(loopBlock); + arr = B->CreateInsertValue(arr, len, 0); + arr = B->CreateInsertValue(arr, ptr, 1); + B->CreateBr(loopBlock); - builder.SetInsertPoint(loopBlock); - llvm::PHINode *control = builder.CreatePHI(builder.getInt32Ty(), 2, "i"); - llvm::Value *next = builder.CreateAdd(control, builder.getInt32(1), "next"); - llvm::Value *cond = builder.CreateICmpSLT(control, argc); - control->addIncoming(builder.getInt32(0), entryBlock); + B->SetInsertPoint(loopBlock); + llvm::PHINode *control = B->CreatePHI(B->getInt32Ty(), 2, "i"); + llvm::Value *next = B->CreateAdd(control, B->getInt32(1), "next"); + llvm::Value *cond = B->CreateICmpSLT(control, argc); + control->addIncoming(B->getInt32(0), entryBlock); control->addIncoming(next, bodyBlock); - builder.CreateCondBr(cond, bodyBlock, exitBlock); + B->CreateCondBr(cond, bodyBlock, exitBlock); - builder.SetInsertPoint(bodyBlock); - llvm::Value *arg = builder.CreateLoad(builder.CreateGEP(argv, control)); - llvm::Value *argLen = builder.CreateZExtOrTrunc(builder.CreateCall(strlenFunc, arg), - builder.getInt64Ty()); + B->SetInsertPoint(bodyBlock); + llvm::Value *arg = B->CreateLoad(B->CreateGEP(argv, control)); + llvm::Value *argLen = + B->CreateZExtOrTrunc(B->CreateCall(strlenFunc, arg), B->getInt64Ty()); llvm::Value *str = llvm::UndefValue::get(strType); - str = builder.CreateInsertValue(str, argLen, 0); - str = builder.CreateInsertValue(str, arg, 1); - builder.CreateStore(str, builder.CreateGEP(ptr, control)); - builder.CreateBr(loopBlock); + str = B->CreateInsertValue(str, argLen, 0); + str = B->CreateInsertValue(str, arg, 1); + B->CreateStore(str, B->CreateGEP(ptr, control)); + B->CreateBr(loopBlock); - builder.SetInsertPoint(exitBlock); - builder.CreateStore(arr, argStorage); - builder.CreateCall(initFunc, builder.getInt32(db.debug ? 1 : 0)); + B->SetInsertPoint(exitBlock); + llvm::Value *argStorage = getVar(x->getArgVar()); + seqassert(argStorage, "argument storage missing"); + B->CreateStore(arr, argStorage); + const int flags = (db.debug ? SEQ_FLAG_DEBUG : 0) | (db.jit ? SEQ_FLAG_JIT : 0) | + (db.standalone ? SEQ_FLAG_STANDALONE : 0); + B->CreateCall(initFunc, B->getInt32(flags)); // Put the entire program in a new function { - auto *proxyMainTy = llvm::FunctionType::get(builder.getVoidTy(), {}, false); + auto *proxyMainTy = llvm::FunctionType::get(B->getVoidTy(), {}, false); auto *proxyMain = llvm::cast( - module->getOrInsertFunction("codon.proxy_main", proxyMainTy).getCallee()); + M->getOrInsertFunction("codon.proxy_main", proxyMainTy).getCallee()); proxyMain->setLinkage(llvm::GlobalValue::PrivateLinkage); proxyMain->setPersonalityFn( llvm::cast(makePersonalityFunc().getCallee())); - auto *proxyBlockEntry = llvm::BasicBlock::Create(context, "entry", proxyMain); - auto *proxyBlockMain = llvm::BasicBlock::Create(context, "main", proxyMain); - auto *proxyBlockExit = llvm::BasicBlock::Create(context, "exit", proxyMain); - builder.SetInsertPoint(proxyBlockEntry); + auto *proxyBlockEntry = llvm::BasicBlock::Create(*context, "entry", proxyMain); + auto *proxyBlockMain = llvm::BasicBlock::Create(*context, "main", proxyMain); + auto *proxyBlockExit = llvm::BasicBlock::Create(*context, "exit", proxyMain); + B->SetInsertPoint(proxyBlockEntry); - llvm::Value *shouldExit = builder.getFalse(); - builder.CreateCondBr(shouldExit, proxyBlockExit, proxyBlockMain); + llvm::Value *shouldExit = B->getFalse(); + B->CreateCondBr(shouldExit, proxyBlockExit, proxyBlockMain); - builder.SetInsertPoint(proxyBlockExit); - builder.CreateRetVoid(); + B->SetInsertPoint(proxyBlockExit); + B->CreateRetVoid(); // invoke real main - auto *normal = llvm::BasicBlock::Create(context, "normal", proxyMain); - auto *unwind = llvm::BasicBlock::Create(context, "unwind", proxyMain); - builder.SetInsertPoint(proxyBlockMain); - builder.CreateInvoke(realMain, normal, unwind); + auto *normal = llvm::BasicBlock::Create(*context, "normal", proxyMain); + auto *unwind = llvm::BasicBlock::Create(*context, "unwind", proxyMain); + B->SetInsertPoint(proxyBlockMain); + B->CreateInvoke(realMain, normal, unwind); - builder.SetInsertPoint(unwind); - llvm::LandingPadInst *caughtResult = builder.CreateLandingPad(getPadType(), 1); + B->SetInsertPoint(unwind); + llvm::LandingPadInst *caughtResult = B->CreateLandingPad(getPadType(), 1); caughtResult->setCleanup(true); caughtResult->addClause(getTypeIdxVar(nullptr)); - llvm::Value *unwindException = builder.CreateExtractValue(caughtResult, 0); - builder.CreateCall(makeTerminateFunc(), unwindException); - builder.CreateUnreachable(); + llvm::Value *unwindException = B->CreateExtractValue(caughtResult, 0); + B->CreateCall(makeTerminateFunc(), unwindException); + B->CreateUnreachable(); - builder.SetInsertPoint(normal); - builder.CreateRetVoid(); + B->SetInsertPoint(normal); + B->CreateRetVoid(); // actually make the call - builder.SetInsertPoint(exitBlock); - builder.CreateCall(proxyMain); + B->SetInsertPoint(exitBlock); + B->CreateCall(proxyMain); } - builder.SetInsertPoint(exitBlock); - builder.CreateRet(builder.getInt32(0)); - db.builder->finalize(); + B->SetInsertPoint(exitBlock); + B->CreateRet(B->getInt32(0)); } -void LLVMVisitor::makeLLVMFunction(const Func *x) { +llvm::DISubprogram *LLVMVisitor::getDISubprogramForFunc(const Func *x) { auto *srcInfo = getSrcInfo(x); llvm::DIFile *file = db.getFile(srcInfo->file); auto *derivedType = llvm::cast(getDIType(x->getType())); auto *subroutineType = llvm::cast(derivedType->getRawBaseType()); + + std::string baseName = x->getUnmangledName(); + if (auto *parent = x->getParentType()) + baseName = parent->getName() + "." + baseName; llvm::DISubprogram *subprogram = db.builder->createFunction( - file, x->getUnmangledName(), getNameForFunction(x), file, srcInfo->line, - subroutineType, /*ScopeLine=*/0, llvm::DINode::FlagZero, + file, baseName, getNameForFunction(x), file, srcInfo->line, subroutineType, + /*ScopeLine=*/0, llvm::DINode::FlagZero, llvm::DISubprogram::toSPFlags(/*IsLocalToUnit=*/true, /*IsDefinition=*/true, /*IsOptimized=*/!db.debug)); + return subprogram; +} +void LLVMVisitor::makeLLVMFunction(const Func *x) { // process LLVM functions in full immediately if (auto *llvmFunc = cast(x)) { process(llvmFunc); - func = module->getFunction(getNameForFunction(x)); - func->setSubprogram(subprogram); setDebugInfoForNode(nullptr); return; } @@ -638,33 +731,32 @@ void LLVMVisitor::makeLLVMFunction(const Func *x) { llvm::FunctionType::get(returnType, argTypes, funcType->isVariadic()); const std::string functionName = getNameForFunction(x); func = llvm::cast( - module->getOrInsertFunction(functionName, llvmFuncType).getCallee()); + M->getOrInsertFunction(functionName, llvmFuncType).getCallee()); if (!cast(x)) { - func->setSubprogram(subprogram); + func->setSubprogram(getDISubprogramForFunc(x)); } } void LLVMVisitor::makeYield(llvm::Value *value, bool finalYield) { - builder.SetInsertPoint(block); + B->SetInsertPoint(block); if (value) { seqassert(coro.promise, "promise is null"); - builder.CreateStore(value, coro.promise); + B->CreateStore(value, coro.promise); } llvm::FunctionCallee coroSuspend = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_suspend); - llvm::Value *suspendResult = - builder.CreateCall(coroSuspend, {llvm::ConstantTokenNone::get(context), - builder.getInt1(finalYield)}); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_suspend); + llvm::Value *suspendResult = B->CreateCall( + coroSuspend, {llvm::ConstantTokenNone::get(*context), B->getInt1(finalYield)}); - block = llvm::BasicBlock::Create(context, "yield.new", func); + block = llvm::BasicBlock::Create(*context, "yield.new", func); - llvm::SwitchInst *inst = builder.CreateSwitch(suspendResult, coro.suspend, 2); - inst->addCase(builder.getInt8(0), block); - inst->addCase(builder.getInt8(1), coro.cleanup); + llvm::SwitchInst *inst = B->CreateSwitch(suspendResult, coro.suspend, 2); + inst->addCase(B->getInt8(0), block); + inst->addCase(B->getInt8(1), coro.cleanup); } void LLVMVisitor::visit(const ExternalFunc *x) { - func = module->getFunction(getNameForFunction(x)); // inserted during module visit + func = M->getFunction(getNameForFunction(x)); coro = {}; seqassert(func, "{} not inserted", *x); func->setDoesNotThrow(); @@ -700,7 +792,7 @@ bool internalFuncMatches(const std::string &name, const InternalFunc *x) { void LLVMVisitor::visit(const InternalFunc *x) { using namespace types; - func = module->getFunction(getNameForFunction(x)); // inserted during module visit + func = M->getFunction(getNameForFunction(x)); coro = {}; seqassert(func, "{} not inserted", *x); setDebugInfoForNode(x); @@ -709,14 +801,14 @@ void LLVMVisitor::visit(const InternalFunc *x) { auto *funcType = cast(x->getType()); std::vector argTypes(funcType->begin(), funcType->end()); - func->setLinkage(llvm::GlobalValue::PrivateLinkage); + func->setLinkage(getDefaultLinkage()); func->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline); std::vector args; for (auto it = func->arg_begin(); it != func->arg_end(); ++it) { args.push_back(it); } - block = llvm::BasicBlock::Create(context, "entry", func); - builder.SetInsertPoint(block); + block = llvm::BasicBlock::Create(*context, "entry", func); + B->SetInsertPoint(block); llvm::Value *result = nullptr; if (internalFuncMatches("__new__", x)) { @@ -725,51 +817,50 @@ void LLVMVisitor::visit(const InternalFunc *x) { llvm::Type *llvmBaseType = getLLVMType(baseType); auto allocFunc = makeAllocFunc(baseType->isAtomic()); llvm::Value *elemSize = - builder.getInt64(module->getDataLayout().getTypeAllocSize(llvmBaseType)); - llvm::Value *allocSize = builder.CreateMul(elemSize, args[0]); - result = builder.CreateCall(allocFunc, allocSize); - result = builder.CreateBitCast(result, llvmBaseType->getPointerTo()); + B->getInt64(M->getDataLayout().getTypeAllocSize(llvmBaseType)); + llvm::Value *allocSize = B->CreateMul(elemSize, args[0]); + result = B->CreateCall(allocFunc, allocSize); + result = B->CreateBitCast(result, llvmBaseType->getPointerTo()); } else if (internalFuncMatches("__new__", x)) { auto *intNType = cast(argTypes[0]); if (intNType->isSigned()) { - result = builder.CreateSExtOrTrunc(args[0], builder.getInt64Ty()); + result = B->CreateSExtOrTrunc(args[0], B->getInt64Ty()); } else { - result = builder.CreateZExtOrTrunc(args[0], builder.getInt64Ty()); + result = B->CreateZExtOrTrunc(args[0], B->getInt64Ty()); } } else if (internalFuncMatches("__new__", x)) { auto *intNType = cast(parentType); if (intNType->isSigned()) { - result = builder.CreateSExtOrTrunc(args[0], getLLVMType(intNType)); + result = B->CreateSExtOrTrunc(args[0], getLLVMType(intNType)); } else { - result = builder.CreateZExtOrTrunc(args[0], getLLVMType(intNType)); + result = B->CreateZExtOrTrunc(args[0], getLLVMType(intNType)); } } else if (internalFuncMatches("__new__", x)) { auto *refType = cast(parentType); auto allocFunc = makeAllocFunc(refType->getContents()->isAtomic()); - llvm::Value *size = builder.getInt64( - module->getDataLayout().getTypeAllocSize(getLLVMType(refType->getContents()))); - result = builder.CreateCall(allocFunc, size); + llvm::Value *size = B->getInt64( + M->getDataLayout().getTypeAllocSize(getLLVMType(refType->getContents()))); + result = B->CreateCall(allocFunc, size); } else if (internalFuncMatches("__promise__", x)) { auto *generatorType = cast(parentType); llvm::Type *baseType = getLLVMType(generatorType->getBase()); if (baseType->isVoidTy()) { - result = llvm::ConstantPointerNull::get(builder.getVoidTy()->getPointerTo()); + result = llvm::ConstantPointerNull::get(B->getVoidTy()->getPointerTo()); } else { llvm::FunctionCallee coroPromise = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_promise); - llvm::Value *aln = - builder.getInt32(module->getDataLayout().getPrefTypeAlignment(baseType)); - llvm::Value *from = builder.getFalse(); - llvm::Value *ptr = builder.CreateCall(coroPromise, {args[0], aln, from}); - result = builder.CreateBitCast(ptr, baseType->getPointerTo()); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_promise); + llvm::Value *aln = B->getInt32(M->getDataLayout().getPrefTypeAlignment(baseType)); + llvm::Value *from = B->getFalse(); + llvm::Value *ptr = B->CreateCall(coroPromise, {args[0], aln, from}); + result = B->CreateBitCast(ptr, baseType->getPointerTo()); } } @@ -779,12 +870,12 @@ void LLVMVisitor::visit(const InternalFunc *x) { "args size does not match"); result = llvm::UndefValue::get(getLLVMType(recordType)); for (auto i = 0; i < args.size(); i++) { - result = builder.CreateInsertValue(result, args[i], i); + result = B->CreateInsertValue(result, args[i], i); } } seqassert(result, "internal function {} not found", *x); - builder.CreateRet(result); + B->CreateRet(result); } std::string LLVMVisitor::buildLLVMCodeString(const LLVMFunc *x) { @@ -830,7 +921,7 @@ std::string LLVMVisitor::buildLLVMCodeString(const LLVMFunc *x) { } void LLVMVisitor::visit(const LLVMFunc *x) { - func = module->getFunction(getNameForFunction(x)); + func = M->getFunction(getNameForFunction(x)); coro = {}; if (func) return; @@ -859,26 +950,39 @@ void LLVMVisitor::visit(const LLVMFunc *x) { std::unique_ptr buf = llvm::MemoryBuffer::getMemBuffer(code); seqassert(buf, "could not create buffer"); std::unique_ptr sub = - llvm::parseIR(buf->getMemBufferRef(), err, context); + llvm::parseIR(buf->getMemBufferRef(), err, *context); if (!sub) { std::string bufStr; llvm::raw_string_ostream buf(bufStr); err.print("LLVM", buf); compilationError(buf.str()); } - sub->setDataLayout(module->getDataLayout()); + sub->setDataLayout(M->getDataLayout()); - llvm::Linker L(*module); + llvm::Linker L(*M); const bool fail = L.linkInModule(std::move(sub)); seqassert(!fail, "linking failed"); - func = module->getFunction(getNameForFunction(x)); + func = M->getFunction(getNameForFunction(x)); seqassert(func, "function not linked in"); - func->setLinkage(llvm::GlobalValue::PrivateLinkage); + func->setLinkage(getDefaultLinkage()); func->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline); + func->setSubprogram(getDISubprogramForFunc(x)); + + // set up debug info + // for now we just set all to func's source location + auto *srcInfo = getSrcInfo(x); + for (auto &block : func->getBasicBlockList()) { + for (auto &inst : block) { + if (!inst.getDebugLoc()) { + inst.setDebugLoc(llvm::DebugLoc(llvm::DILocation::get( + *context, srcInfo->line, srcInfo->col, func->getSubprogram()))); + } + } + } } void LLVMVisitor::visit(const BodiedFunc *x) { - func = module->getFunction(getNameForFunction(x)); // inserted during module visit + func = M->getFunction(getNameForFunction(x)); coro = {}; seqassert(func, "{} not inserted", *x); setDebugInfoForNode(x); @@ -887,7 +991,7 @@ void LLVMVisitor::visit(const BodiedFunc *x) { if (fnAttributes && fnAttributes->has("std.internal.attributes.export")) { func->setLinkage(llvm::GlobalValue::ExternalLinkage); } else { - func->setLinkage(llvm::GlobalValue::PrivateLinkage); + func->setLinkage(getDefaultLinkage()); } if (fnAttributes && fnAttributes->has("std.internal.attributes.inline")) { func->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline); @@ -900,9 +1004,8 @@ void LLVMVisitor::visit(const BodiedFunc *x) { auto *funcType = cast(x->getType()); seqassert(funcType, "{} is not a function type", *x->getType()); auto *returnType = funcType->getReturnType(); - auto *entryBlock = llvm::BasicBlock::Create(context, "entry", func); - builder.SetInsertPoint(entryBlock); - builder.SetCurrentDebugLocation(llvm::DebugLoc()); + auto *entryBlock = llvm::BasicBlock::Create(*context, "entry", func); + B->SetInsertPoint(entryBlock); // set up arguments and other symbols seqassert(std::distance(func->arg_begin(), func->arg_end()) == @@ -912,9 +1015,9 @@ void LLVMVisitor::visit(const BodiedFunc *x) { auto argIter = func->arg_begin(); for (auto varIter = x->arg_begin(); varIter != x->arg_end(); ++varIter) { const Var *var = *varIter; - llvm::Value *storage = builder.CreateAlloca(getLLVMType(var->getType())); - builder.CreateStore(argIter, storage); - vars.insert(var, storage); + llvm::Value *storage = B->CreateAlloca(getLLVMType(var->getType())); + B->CreateStore(argIter, storage); + insertVar(var, storage); // debug info auto *srcInfo = getSrcInfo(var); @@ -925,7 +1028,8 @@ void LLVMVisitor::visit(const BodiedFunc *x) { getDIType(var->getType()), db.debug); db.builder->insertDeclare( storage, debugVar, db.builder->createExpression(), - llvm::DILocation::get(context, srcInfo->line, srcInfo->col, scope), entryBlock); + llvm::DILocation::get(*context, srcInfo->line, srcInfo->col, scope), + entryBlock); ++argIter; ++argIdx; @@ -934,10 +1038,10 @@ void LLVMVisitor::visit(const BodiedFunc *x) { for (auto *var : *x) { llvm::Type *llvmType = getLLVMType(var->getType()); if (llvmType->isVoidTy()) { - vars.insert(var, getDummyVoidValue(context)); + insertVar(var, getDummyVoidValue()); } else { - llvm::Value *storage = builder.CreateAlloca(llvmType); - vars.insert(var, storage); + llvm::Value *storage = B->CreateAlloca(llvmType); + insertVar(var, storage); // debug info auto *srcInfo = getSrcInfo(var); @@ -948,107 +1052,105 @@ void LLVMVisitor::visit(const BodiedFunc *x) { getDIType(var->getType()), db.debug); db.builder->insertDeclare( storage, debugVar, db.builder->createExpression(), - llvm::DILocation::get(context, srcInfo->line, srcInfo->col, scope), + llvm::DILocation::get(*context, srcInfo->line, srcInfo->col, scope), entryBlock); } } - auto *startBlock = llvm::BasicBlock::Create(context, "start", func); + auto *startBlock = llvm::BasicBlock::Create(*context, "start", func); if (x->isGenerator()) { auto *generatorType = cast(returnType); seqassert(generatorType, "{} is not a generator type", *returnType); llvm::FunctionCallee coroId = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_id); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_id); llvm::FunctionCallee coroBegin = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_begin); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_begin); llvm::FunctionCallee coroSize = llvm::Intrinsic::getDeclaration( - module.get(), llvm::Intrinsic::coro_size, {builder.getInt64Ty()}); + M.get(), llvm::Intrinsic::coro_size, {B->getInt64Ty()}); llvm::FunctionCallee coroEnd = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_end); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_end); llvm::FunctionCallee coroAlloc = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_alloc); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_alloc); llvm::FunctionCallee coroFree = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_free); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_free); - coro.cleanup = llvm::BasicBlock::Create(context, "coro.cleanup", func); - coro.suspend = llvm::BasicBlock::Create(context, "coro.suspend", func); - coro.exit = llvm::BasicBlock::Create(context, "coro.exit", func); - auto *allocBlock = llvm::BasicBlock::Create(context, "coro.alloc", func); - auto *freeBlock = llvm::BasicBlock::Create(context, "coro.free", func); + coro.cleanup = llvm::BasicBlock::Create(*context, "coro.cleanup", func); + coro.suspend = llvm::BasicBlock::Create(*context, "coro.suspend", func); + coro.exit = llvm::BasicBlock::Create(*context, "coro.exit", func); + auto *allocBlock = llvm::BasicBlock::Create(*context, "coro.alloc", func); + auto *freeBlock = llvm::BasicBlock::Create(*context, "coro.free", func); // coro ID and promise llvm::Value *id = nullptr; - llvm::Value *nullPtr = llvm::ConstantPointerNull::get(builder.getInt8PtrTy()); + llvm::Value *nullPtr = llvm::ConstantPointerNull::get(B->getInt8PtrTy()); if (!cast(generatorType->getBase())) { - coro.promise = builder.CreateAlloca(getLLVMType(generatorType->getBase())); + coro.promise = B->CreateAlloca(getLLVMType(generatorType->getBase())); coro.promise->setName("coro.promise"); - llvm::Value *promiseRaw = - builder.CreateBitCast(coro.promise, builder.getInt8PtrTy()); - id = builder.CreateCall(coroId, - {builder.getInt32(0), promiseRaw, nullPtr, nullPtr}); + llvm::Value *promiseRaw = B->CreateBitCast(coro.promise, B->getInt8PtrTy()); + id = B->CreateCall(coroId, {B->getInt32(0), promiseRaw, nullPtr, nullPtr}); } else { - id = builder.CreateCall(coroId, {builder.getInt32(0), nullPtr, nullPtr, nullPtr}); + id = B->CreateCall(coroId, {B->getInt32(0), nullPtr, nullPtr, nullPtr}); } id->setName("coro.id"); - llvm::Value *needAlloc = builder.CreateCall(coroAlloc, id); - builder.CreateCondBr(needAlloc, allocBlock, startBlock); + llvm::Value *needAlloc = B->CreateCall(coroAlloc, id); + B->CreateCondBr(needAlloc, allocBlock, startBlock); // coro alloc - builder.SetInsertPoint(allocBlock); - llvm::Value *size = builder.CreateCall(coroSize); + B->SetInsertPoint(allocBlock); + llvm::Value *size = B->CreateCall(coroSize); auto allocFunc = makeAllocFunc(/*atomic=*/false); - llvm::Value *alloc = builder.CreateCall(allocFunc, size); - builder.CreateBr(startBlock); + llvm::Value *alloc = B->CreateCall(allocFunc, size); + B->CreateBr(startBlock); // coro start - builder.SetInsertPoint(startBlock); - llvm::PHINode *phi = builder.CreatePHI(builder.getInt8PtrTy(), 2); + B->SetInsertPoint(startBlock); + llvm::PHINode *phi = B->CreatePHI(B->getInt8PtrTy(), 2); phi->addIncoming(nullPtr, entryBlock); phi->addIncoming(alloc, allocBlock); - coro.handle = builder.CreateCall(coroBegin, {id, phi}); + coro.handle = B->CreateCall(coroBegin, {id, phi}); coro.handle->setName("coro.handle"); // coro cleanup - builder.SetInsertPoint(coro.cleanup); - llvm::Value *mem = builder.CreateCall(coroFree, {id, coro.handle}); - llvm::Value *needFree = builder.CreateIsNotNull(mem); - builder.CreateCondBr(needFree, freeBlock, coro.suspend); + B->SetInsertPoint(coro.cleanup); + llvm::Value *mem = B->CreateCall(coroFree, {id, coro.handle}); + llvm::Value *needFree = B->CreateIsNotNull(mem); + B->CreateCondBr(needFree, freeBlock, coro.suspend); // coro free - builder.SetInsertPoint(freeBlock); // no-op: GC will free automatically - builder.CreateBr(coro.suspend); + B->SetInsertPoint(freeBlock); // no-op: GC will free automatically + B->CreateBr(coro.suspend); // coro suspend - builder.SetInsertPoint(coro.suspend); - builder.CreateCall(coroEnd, {coro.handle, builder.getFalse()}); - builder.CreateRet(coro.handle); + B->SetInsertPoint(coro.suspend); + B->CreateCall(coroEnd, {coro.handle, B->getFalse()}); + B->CreateRet(coro.handle); // coro exit block = coro.exit; makeYield(nullptr, /*finalYield=*/true); - builder.SetInsertPoint(block); - builder.CreateUnreachable(); + B->SetInsertPoint(block); + B->CreateUnreachable(); // initial yield block = startBlock; makeYield(); // coroutine will be initially suspended } else { - builder.CreateBr(startBlock); + B->CreateBr(startBlock); block = startBlock; } process(x->getBody()); - builder.SetInsertPoint(block); + B->SetInsertPoint(block); if (x->isGenerator()) { - builder.CreateBr(coro.exit); + B->CreateBr(coro.exit); } else { if (cast(returnType)) { - builder.CreateRetVoid(); + B->CreateRetVoid(); } else { - builder.CreateRet(llvm::Constant::getNullValue(getLLVMType(returnType))); + B->CreateRet(llvm::Constant::getNullValue(getLLVMType(returnType))); } } } @@ -1057,18 +1159,18 @@ void LLVMVisitor::visit(const Var *x) { seqassert(0, "cannot visit var"); } void LLVMVisitor::visit(const VarValue *x) { if (auto *f = cast(x->getVar())) { - value = funcs[f]; + value = getFunc(f); seqassert(value, "{} value not found", *x); } else { - llvm::Value *varPtr = vars[x->getVar()]; + llvm::Value *varPtr = getVar(x->getVar()); seqassert(varPtr, "{} value not found", *x); - builder.SetInsertPoint(block); - value = builder.CreateLoad(varPtr); + B->SetInsertPoint(block); + value = B->CreateLoad(varPtr); } } void LLVMVisitor::visit(const PointerValue *x) { - llvm::Value *var = vars[x->getVar()]; + llvm::Value *var = getVar(x->getVar()); seqassert(var, "{} variable not found", *x); value = var; // note: we don't load the pointer } @@ -1079,23 +1181,23 @@ void LLVMVisitor::visit(const PointerValue *x) { llvm::Type *LLVMVisitor::getLLVMType(types::Type *t) { if (auto *x = cast(t)) { - return builder.getInt64Ty(); + return B->getInt64Ty(); } if (auto *x = cast(t)) { - return builder.getDoubleTy(); + return B->getDoubleTy(); } if (auto *x = cast(t)) { - return builder.getInt8Ty(); + return B->getInt8Ty(); } if (auto *x = cast(t)) { - return builder.getInt8Ty(); + return B->getInt8Ty(); } if (auto *x = cast(t)) { - return builder.getVoidTy(); + return B->getVoidTy(); } if (auto *x = cast(t)) { @@ -1103,11 +1205,11 @@ llvm::Type *LLVMVisitor::getLLVMType(types::Type *t) { for (const auto &field : *x) { body.push_back(getLLVMType(field.getType())); } - return llvm::StructType::get(context, body); + return llvm::StructType::get(*context, body); } if (auto *x = cast(t)) { - return builder.getInt8PtrTy(); + return B->getInt8PtrTy(); } if (auto *x = cast(t)) { @@ -1124,7 +1226,7 @@ llvm::Type *LLVMVisitor::getLLVMType(types::Type *t) { if (cast(x->getBase())) { return getLLVMType(x->getBase()); } else { - return llvm::StructType::get(builder.getInt1Ty(), getLLVMType(x->getBase())); + return llvm::StructType::get(B->getInt1Ty(), getLLVMType(x->getBase())); } } @@ -1133,25 +1235,25 @@ llvm::Type *LLVMVisitor::getLLVMType(types::Type *t) { } if (auto *x = cast(t)) { - return builder.getInt8PtrTy(); + return B->getInt8PtrTy(); } if (auto *x = cast(t)) { - return builder.getIntNTy(x->getLen()); + return B->getIntNTy(x->getLen()); } if (auto *x = cast(t)) { return x->getBuilder()->buildType(this); } - seqassert(0, "unknown type"); + seqassert(0, "unknown type: {}", *t); return nullptr; } llvm::DIType *LLVMVisitor::getDITypeHelper( types::Type *t, std::unordered_map &cache) { llvm::Type *type = getLLVMType(t); - auto &layout = module->getDataLayout(); + auto &layout = M->getDataLayout(); if (auto *x = cast(t)) { return db.builder->createBasicType( @@ -1234,7 +1336,7 @@ llvm::DIType *LLVMVisitor::getDITypeHelper( argTypes.push_back(getDITypeHelper(argType, cache)); } return db.builder->createPointerType( - db.builder->createSubroutineType(llvm::MDTuple::get(context, argTypes)), + db.builder->createSubroutineType(llvm::MDTuple::get(*context, argTypes)), layout.getTypeAllocSizeInBits(type)); } @@ -1243,10 +1345,10 @@ llvm::DIType *LLVMVisitor::getDITypeHelper( return getDITypeHelper(x->getBase(), cache); } else { auto *baseType = getLLVMType(x->getBase()); - auto *structType = llvm::StructType::get(builder.getInt1Ty(), baseType); + auto *structType = llvm::StructType::get(B->getInt1Ty(), baseType); auto *structLayout = layout.getStructLayout(structType); auto *srcInfo = getSrcInfo(x); - auto i1SizeInBits = layout.getTypeAllocSizeInBits(builder.getInt1Ty()); + auto i1SizeInBits = layout.getTypeAllocSizeInBits(B->getInt1Ty()); auto *i1DebugType = db.builder->createBasicType("i1", i1SizeInBits, llvm::dwarf::DW_ATE_boolean); llvm::DIFile *file = db.getFile(srcInfo->file); @@ -1314,33 +1416,33 @@ LLVMVisitor::LoopData *LLVMVisitor::getLoopData(id_t loopId) { */ void LLVMVisitor::visit(const IntConst *x) { - builder.SetInsertPoint(block); - value = builder.getInt64(x->getVal()); + B->SetInsertPoint(block); + value = B->getInt64(x->getVal()); } void LLVMVisitor::visit(const FloatConst *x) { - builder.SetInsertPoint(block); - value = llvm::ConstantFP::get(builder.getDoubleTy(), x->getVal()); + B->SetInsertPoint(block); + value = llvm::ConstantFP::get(B->getDoubleTy(), x->getVal()); } void LLVMVisitor::visit(const BoolConst *x) { - builder.SetInsertPoint(block); - value = builder.getInt8(x->getVal() ? 1 : 0); + B->SetInsertPoint(block); + value = B->getInt8(x->getVal() ? 1 : 0); } void LLVMVisitor::visit(const StringConst *x) { - builder.SetInsertPoint(block); + B->SetInsertPoint(block); std::string s = x->getVal(); auto *strVar = new llvm::GlobalVariable( - *module, llvm::ArrayType::get(builder.getInt8Ty(), s.length() + 1), + *M, llvm::ArrayType::get(B->getInt8Ty(), s.length() + 1), /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, - llvm::ConstantDataArray::getString(context, s), "str_literal"); - auto *strType = llvm::StructType::get(builder.getInt64Ty(), builder.getInt8PtrTy()); - llvm::Value *ptr = builder.CreateBitCast(strVar, builder.getInt8PtrTy()); - llvm::Value *len = builder.getInt64(s.length()); + llvm::ConstantDataArray::getString(*context, s), "str_literal"); + auto *strType = llvm::StructType::get(B->getInt64Ty(), B->getInt8PtrTy()); + llvm::Value *ptr = B->CreateBitCast(strVar, B->getInt8PtrTy()); + llvm::Value *len = B->getInt64(s.length()); llvm::Value *str = llvm::UndefValue::get(strType); - str = builder.CreateInsertValue(str, len, 0); - str = builder.CreateInsertValue(str, ptr, 1); + str = B->CreateInsertValue(str, len, 0); + str = B->CreateInsertValue(str, ptr, 1); value = str; } @@ -1359,55 +1461,55 @@ void LLVMVisitor::visit(const SeriesFlow *x) { } void LLVMVisitor::visit(const IfFlow *x) { - auto *trueBlock = llvm::BasicBlock::Create(context, "if.true", func); - auto *falseBlock = llvm::BasicBlock::Create(context, "if.false", func); - auto *exitBlock = llvm::BasicBlock::Create(context, "if.exit", func); + auto *trueBlock = llvm::BasicBlock::Create(*context, "if.true", func); + auto *falseBlock = llvm::BasicBlock::Create(*context, "if.false", func); + auto *exitBlock = llvm::BasicBlock::Create(*context, "if.exit", func); process(x->getCond()); llvm::Value *cond = value; - builder.SetInsertPoint(block); - cond = builder.CreateTrunc(cond, builder.getInt1Ty()); - builder.CreateCondBr(cond, trueBlock, falseBlock); + B->SetInsertPoint(block); + cond = B->CreateTrunc(cond, B->getInt1Ty()); + B->CreateCondBr(cond, trueBlock, falseBlock); block = trueBlock; if (x->getTrueBranch()) { process(x->getTrueBranch()); } - builder.SetInsertPoint(block); - builder.CreateBr(exitBlock); + B->SetInsertPoint(block); + B->CreateBr(exitBlock); block = falseBlock; if (x->getFalseBranch()) { process(x->getFalseBranch()); } - builder.SetInsertPoint(block); - builder.CreateBr(exitBlock); + B->SetInsertPoint(block); + B->CreateBr(exitBlock); block = exitBlock; } void LLVMVisitor::visit(const WhileFlow *x) { - auto *condBlock = llvm::BasicBlock::Create(context, "while.cond", func); - auto *bodyBlock = llvm::BasicBlock::Create(context, "while.body", func); - auto *exitBlock = llvm::BasicBlock::Create(context, "while.exit", func); + auto *condBlock = llvm::BasicBlock::Create(*context, "while.cond", func); + auto *bodyBlock = llvm::BasicBlock::Create(*context, "while.body", func); + auto *exitBlock = llvm::BasicBlock::Create(*context, "while.exit", func); - builder.SetInsertPoint(block); - builder.CreateBr(condBlock); + B->SetInsertPoint(block); + B->CreateBr(condBlock); block = condBlock; process(x->getCond()); llvm::Value *cond = value; - builder.SetInsertPoint(block); - cond = builder.CreateTrunc(cond, builder.getInt1Ty()); - builder.CreateCondBr(cond, bodyBlock, exitBlock); + B->SetInsertPoint(block); + cond = B->CreateTrunc(cond, B->getInt1Ty()); + B->CreateCondBr(cond, bodyBlock, exitBlock); block = bodyBlock; enterLoop( {/*breakBlock=*/exitBlock, /*continueBlock=*/condBlock, /*loopId=*/x->getId()}); process(x->getBody()); exitLoop(); - builder.SetInsertPoint(block); - builder.CreateBr(condBlock); + B->SetInsertPoint(block); + B->CreateBr(condBlock); block = exitBlock; } @@ -1415,45 +1517,45 @@ void LLVMVisitor::visit(const WhileFlow *x) { void LLVMVisitor::visit(const ForFlow *x) { seqassert(!x->isParallel(), "parallel for-loop not lowered"); llvm::Type *loopVarType = getLLVMType(x->getVar()->getType()); - llvm::Value *loopVar = vars[x->getVar()]; + llvm::Value *loopVar = getVar(x->getVar()); seqassert(loopVar, "{} loop variable not found", *x); - auto *condBlock = llvm::BasicBlock::Create(context, "for.cond", func); - auto *bodyBlock = llvm::BasicBlock::Create(context, "for.body", func); - auto *cleanupBlock = llvm::BasicBlock::Create(context, "for.cleanup", func); - auto *exitBlock = llvm::BasicBlock::Create(context, "for.exit", func); + auto *condBlock = llvm::BasicBlock::Create(*context, "for.cond", func); + auto *bodyBlock = llvm::BasicBlock::Create(*context, "for.body", func); + auto *cleanupBlock = llvm::BasicBlock::Create(*context, "for.cleanup", func); + auto *exitBlock = llvm::BasicBlock::Create(*context, "for.exit", func); // LLVM coroutine intrinsics // https://prereleases.llvm.org/6.0.0/rc3/docs/Coroutines.html llvm::FunctionCallee coroResume = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_resume); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_resume); llvm::FunctionCallee coroDone = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_done); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_done); llvm::FunctionCallee coroPromise = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_promise); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_promise); llvm::FunctionCallee coroDestroy = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_destroy); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_destroy); process(x->getIter()); llvm::Value *iter = value; - builder.SetInsertPoint(block); - builder.CreateBr(condBlock); + B->SetInsertPoint(block); + B->CreateBr(condBlock); block = condBlock; call(coroResume, {iter}); - builder.SetInsertPoint(block); - llvm::Value *done = builder.CreateCall(coroDone, iter); - builder.CreateCondBr(done, cleanupBlock, bodyBlock); + B->SetInsertPoint(block); + llvm::Value *done = B->CreateCall(coroDone, iter); + B->CreateCondBr(done, cleanupBlock, bodyBlock); if (!loopVarType->isVoidTy()) { - builder.SetInsertPoint(bodyBlock); + B->SetInsertPoint(bodyBlock); llvm::Value *alignment = - builder.getInt32(module->getDataLayout().getPrefTypeAlignment(loopVarType)); - llvm::Value *from = builder.getFalse(); - llvm::Value *promise = builder.CreateCall(coroPromise, {iter, alignment, from}); - promise = builder.CreateBitCast(promise, loopVarType->getPointerTo()); - llvm::Value *generatedValue = builder.CreateLoad(promise); - builder.CreateStore(generatedValue, loopVar); + B->getInt32(M->getDataLayout().getPrefTypeAlignment(loopVarType)); + llvm::Value *from = B->getFalse(); + llvm::Value *promise = B->CreateCall(coroPromise, {iter, alignment, from}); + promise = B->CreateBitCast(promise, loopVarType->getPointerTo()); + llvm::Value *generatedValue = B->CreateLoad(promise); + B->CreateStore(generatedValue, loopVar); } block = bodyBlock; @@ -1461,57 +1563,56 @@ void LLVMVisitor::visit(const ForFlow *x) { {/*breakBlock=*/exitBlock, /*continueBlock=*/condBlock, /*loopId=*/x->getId()}); process(x->getBody()); exitLoop(); - builder.SetInsertPoint(block); - builder.CreateBr(condBlock); + B->SetInsertPoint(block); + B->CreateBr(condBlock); - builder.SetInsertPoint(cleanupBlock); - builder.CreateCall(coroDestroy, iter); - builder.CreateBr(exitBlock); + B->SetInsertPoint(cleanupBlock); + B->CreateCall(coroDestroy, iter); + B->CreateBr(exitBlock); block = exitBlock; } void LLVMVisitor::visit(const ImperativeForFlow *x) { seqassert(!x->isParallel(), "parallel for-loop not lowered"); - llvm::Value *loopVar = vars[x->getVar()]; + llvm::Value *loopVar = getVar(x->getVar()); seqassert(loopVar, "{} loop variable not found", *x); seqassert(x->getStep() != 0, "step cannot be 0"); - auto *condBlock = llvm::BasicBlock::Create(context, "imp_for.cond", func); - auto *bodyBlock = llvm::BasicBlock::Create(context, "imp_for.body", func); - auto *updateBlock = llvm::BasicBlock::Create(context, "imp_for.update", func); - auto *exitBlock = llvm::BasicBlock::Create(context, "imp_for.exit", func); + auto *condBlock = llvm::BasicBlock::Create(*context, "imp_for.cond", func); + auto *bodyBlock = llvm::BasicBlock::Create(*context, "imp_for.body", func); + auto *updateBlock = llvm::BasicBlock::Create(*context, "imp_for.update", func); + auto *exitBlock = llvm::BasicBlock::Create(*context, "imp_for.exit", func); process(x->getStart()); - builder.SetInsertPoint(block); - builder.CreateStore(value, loopVar); + B->SetInsertPoint(block); + B->CreateStore(value, loopVar); process(x->getEnd()); auto *end = value; - builder.SetInsertPoint(block); - builder.CreateBr(condBlock); - builder.SetInsertPoint(condBlock); + B->SetInsertPoint(block); + B->CreateBr(condBlock); + B->SetInsertPoint(condBlock); llvm::Value *done; if (x->getStep() > 0) - done = builder.CreateICmpSGE(builder.CreateLoad(loopVar), end); + done = B->CreateICmpSGE(B->CreateLoad(loopVar), end); else - done = builder.CreateICmpSLE(builder.CreateLoad(loopVar), end); + done = B->CreateICmpSLE(B->CreateLoad(loopVar), end); - builder.CreateCondBr(done, exitBlock, bodyBlock); + B->CreateCondBr(done, exitBlock, bodyBlock); block = bodyBlock; enterLoop( {/*breakBlock=*/exitBlock, /*continueBlock=*/updateBlock, /*loopId=*/x->getId()}); process(x->getBody()); exitLoop(); - builder.SetInsertPoint(block); - builder.CreateBr(updateBlock); + B->SetInsertPoint(block); + B->CreateBr(updateBlock); - builder.SetInsertPoint(updateBlock); - builder.CreateStore( - builder.CreateAdd(builder.CreateLoad(loopVar), builder.getInt64(x->getStep())), - loopVar); - builder.CreateBr(condBlock); + B->SetInsertPoint(updateBlock); + B->CreateStore(B->CreateAdd(B->CreateLoad(loopVar), B->getInt64(x->getStep())), + loopVar); + B->CreateBr(condBlock); block = exitBlock; } @@ -1536,48 +1637,47 @@ bool anyMatch(types::Type *type, std::vector types) { void LLVMVisitor::visit(const TryCatchFlow *x) { const bool isRoot = trycatch.empty(); const bool supportBreakAndContinue = !loops.empty(); - builder.SetInsertPoint(block); - auto *entryBlock = llvm::BasicBlock::Create(context, "trycatch.entry", func); - builder.CreateBr(entryBlock); + B->SetInsertPoint(block); + auto *entryBlock = llvm::BasicBlock::Create(*context, "trycatch.entry", func); + B->CreateBr(entryBlock); TryCatchData tc; - tc.exceptionBlock = llvm::BasicBlock::Create(context, "trycatch.exception", func); + tc.exceptionBlock = llvm::BasicBlock::Create(*context, "trycatch.exception", func); tc.exceptionRouteBlock = - llvm::BasicBlock::Create(context, "trycatch.exception_route", func); - tc.finallyBlock = llvm::BasicBlock::Create(context, "trycatch.finally", func); + llvm::BasicBlock::Create(*context, "trycatch.exception_route", func); + tc.finallyBlock = llvm::BasicBlock::Create(*context, "trycatch.finally", func); auto *externalExcBlock = - llvm::BasicBlock::Create(context, "trycatch.exception_external", func); + llvm::BasicBlock::Create(*context, "trycatch.exception_external", func); auto *unwindResumeBlock = - llvm::BasicBlock::Create(context, "trycatch.unwind_resume", func); - auto *endBlock = llvm::BasicBlock::Create(context, "trycatch.end", func); + llvm::BasicBlock::Create(*context, "trycatch.unwind_resume", func); + auto *endBlock = llvm::BasicBlock::Create(*context, "trycatch.end", func); - builder.SetInsertPoint(func->getEntryBlock().getTerminator()); - auto *excStateNotThrown = builder.getInt8(TryCatchData::State::NOT_THROWN); - auto *excStateThrown = builder.getInt8(TryCatchData::State::THROWN); - auto *excStateCaught = builder.getInt8(TryCatchData::State::CAUGHT); - auto *excStateReturn = builder.getInt8(TryCatchData::State::RETURN); - auto *excStateBreak = builder.getInt8(TryCatchData::State::BREAK); - auto *excStateContinue = builder.getInt8(TryCatchData::State::CONTINUE); + B->SetInsertPoint(func->getEntryBlock().getTerminator()); + auto *excStateNotThrown = B->getInt8(TryCatchData::State::NOT_THROWN); + auto *excStateThrown = B->getInt8(TryCatchData::State::THROWN); + auto *excStateCaught = B->getInt8(TryCatchData::State::CAUGHT); + auto *excStateReturn = B->getInt8(TryCatchData::State::RETURN); + auto *excStateBreak = B->getInt8(TryCatchData::State::BREAK); + auto *excStateContinue = B->getInt8(TryCatchData::State::CONTINUE); llvm::StructType *padType = getPadType(); - llvm::StructType *unwindType = - llvm::StructType::get(builder.getInt64Ty()); // header only + llvm::StructType *unwindType = llvm::StructType::get(B->getInt64Ty()); // header only llvm::StructType *excType = - llvm::StructType::get(getTypeInfoType(), builder.getInt8PtrTy()); + llvm::StructType::get(getTypeInfoType(), B->getInt8PtrTy()); if (isRoot) { - tc.excFlag = builder.CreateAlloca(builder.getInt8Ty()); - tc.catchStore = builder.CreateAlloca(padType); - tc.delegateDepth = builder.CreateAlloca(builder.getInt64Ty()); + tc.excFlag = B->CreateAlloca(B->getInt8Ty()); + tc.catchStore = B->CreateAlloca(padType); + tc.delegateDepth = B->CreateAlloca(B->getInt64Ty()); tc.retStore = (coro.exit || func->getReturnType()->isVoidTy()) ? nullptr - : builder.CreateAlloca(func->getReturnType()); - tc.loopSequence = builder.CreateAlloca(builder.getInt64Ty()); - builder.CreateStore(excStateNotThrown, tc.excFlag); - builder.CreateStore(llvm::ConstantAggregateZero::get(padType), tc.catchStore); - builder.CreateStore(builder.getInt64(0), tc.delegateDepth); - builder.CreateStore(builder.getInt64(-1), tc.loopSequence); + : B->CreateAlloca(func->getReturnType()); + tc.loopSequence = B->CreateAlloca(B->getInt64Ty()); + B->CreateStore(excStateNotThrown, tc.excFlag); + B->CreateStore(llvm::ConstantAggregateZero::get(padType), tc.catchStore); + B->CreateStore(B->getInt64(0), tc.delegateDepth); + B->CreateStore(B->getInt64(-1), tc.loopSequence); } else { tc.excFlag = trycatch[0].excFlag; tc.catchStore = trycatch[0].catchStore; @@ -1590,47 +1690,47 @@ void LLVMVisitor::visit(const TryCatchFlow *x) { block = tc.finallyBlock; process(x->getFinally()); auto *finallyBlock = block; - builder.SetInsertPoint(finallyBlock); - llvm::Value *excFlagRead = builder.CreateLoad(tc.excFlag); + B->SetInsertPoint(finallyBlock); + llvm::Value *excFlagRead = B->CreateLoad(tc.excFlag); if (!isRoot) { - llvm::Value *depthRead = builder.CreateLoad(tc.delegateDepth); - llvm::Value *delegate = builder.CreateICmpSGT(depthRead, builder.getInt64(0)); + llvm::Value *depthRead = B->CreateLoad(tc.delegateDepth); + llvm::Value *delegate = B->CreateICmpSGT(depthRead, B->getInt64(0)); auto *finallyNormal = - llvm::BasicBlock::Create(context, "trycatch.finally.normal", func); + llvm::BasicBlock::Create(*context, "trycatch.finally.normal", func); auto *finallyDelegate = - llvm::BasicBlock::Create(context, "trycatch.finally.delegate", func); - builder.CreateCondBr(delegate, finallyDelegate, finallyNormal); + llvm::BasicBlock::Create(*context, "trycatch.finally.delegate", func); + B->CreateCondBr(delegate, finallyDelegate, finallyNormal); - builder.SetInsertPoint(finallyDelegate); - llvm::Value *depthNew = builder.CreateSub(depthRead, builder.getInt64(1)); - llvm::Value *delegateNew = builder.CreateICmpSGT(depthNew, builder.getInt64(0)); - builder.CreateStore(depthNew, tc.delegateDepth); - builder.CreateCondBr(delegateNew, trycatch.back().finallyBlock, - trycatch.back().exceptionRouteBlock); + B->SetInsertPoint(finallyDelegate); + llvm::Value *depthNew = B->CreateSub(depthRead, B->getInt64(1)); + llvm::Value *delegateNew = B->CreateICmpSGT(depthNew, B->getInt64(0)); + B->CreateStore(depthNew, tc.delegateDepth); + B->CreateCondBr(delegateNew, trycatch.back().finallyBlock, + trycatch.back().exceptionRouteBlock); finallyBlock = finallyNormal; - builder.SetInsertPoint(finallyNormal); + B->SetInsertPoint(finallyNormal); } - builder.SetInsertPoint(finallyBlock); + B->SetInsertPoint(finallyBlock); llvm::SwitchInst *theSwitch = - builder.CreateSwitch(excFlagRead, endBlock, supportBreakAndContinue ? 5 : 3); + B->CreateSwitch(excFlagRead, endBlock, supportBreakAndContinue ? 5 : 3); theSwitch->addCase(excStateCaught, endBlock); theSwitch->addCase(excStateThrown, unwindResumeBlock); if (isRoot) { auto *finallyReturn = - llvm::BasicBlock::Create(context, "trycatch.finally.return", func); + llvm::BasicBlock::Create(*context, "trycatch.finally.return", func); theSwitch->addCase(excStateReturn, finallyReturn); - builder.SetInsertPoint(finallyReturn); + B->SetInsertPoint(finallyReturn); if (coro.exit) { - builder.CreateBr(coro.exit); + B->CreateBr(coro.exit); } else if (tc.retStore) { - llvm::Value *retVal = builder.CreateLoad(tc.retStore); - builder.CreateRet(retVal); + llvm::Value *retVal = B->CreateLoad(tc.retStore); + B->CreateRet(retVal); } else { - builder.CreateRetVoid(); + B->CreateRetVoid(); } } else { theSwitch->addCase(excStateReturn, trycatch.back().finallyBlock); @@ -1640,43 +1740,39 @@ void LLVMVisitor::visit(const TryCatchFlow *x) { auto prevSeq = isRoot ? -1 : trycatch.back().sequenceNumber; auto *finallyBreak = - llvm::BasicBlock::Create(context, "trycatch.finally.break", func); + llvm::BasicBlock::Create(*context, "trycatch.finally.break", func); auto *finallyBreakDone = - llvm::BasicBlock::Create(context, "trycatch.finally.break.done", func); + llvm::BasicBlock::Create(*context, "trycatch.finally.break.done", func); auto *finallyContinue = - llvm::BasicBlock::Create(context, "trycatch.finally.continue", func); + llvm::BasicBlock::Create(*context, "trycatch.finally.continue", func); auto *finallyContinueDone = - llvm::BasicBlock::Create(context, "trycatch.finally.continue.done", func); + llvm::BasicBlock::Create(*context, "trycatch.finally.continue.done", func); - builder.SetInsertPoint(finallyBreak); - auto *breakSwitch = - builder.CreateSwitch(builder.CreateLoad(tc.loopSequence), endBlock, 0); - builder.SetInsertPoint(finallyBreakDone); - builder.CreateStore(excStateNotThrown, tc.excFlag); + B->SetInsertPoint(finallyBreak); + auto *breakSwitch = B->CreateSwitch(B->CreateLoad(tc.loopSequence), endBlock, 0); + B->SetInsertPoint(finallyBreakDone); + B->CreateStore(excStateNotThrown, tc.excFlag); auto *breakDoneSwitch = - builder.CreateSwitch(builder.CreateLoad(tc.loopSequence), endBlock, 0); + B->CreateSwitch(B->CreateLoad(tc.loopSequence), endBlock, 0); - builder.SetInsertPoint(finallyContinue); - auto *continueSwitch = - builder.CreateSwitch(builder.CreateLoad(tc.loopSequence), endBlock, 0); - builder.SetInsertPoint(finallyContinueDone); - builder.CreateStore(excStateNotThrown, tc.excFlag); + B->SetInsertPoint(finallyContinue); + auto *continueSwitch = B->CreateSwitch(B->CreateLoad(tc.loopSequence), endBlock, 0); + B->SetInsertPoint(finallyContinueDone); + B->CreateStore(excStateNotThrown, tc.excFlag); auto *continueDoneSwitch = - builder.CreateSwitch(builder.CreateLoad(tc.loopSequence), endBlock, 0); + B->CreateSwitch(B->CreateLoad(tc.loopSequence), endBlock, 0); for (auto &l : loops) { if (!trycatch.empty() && l.sequenceNumber < prevSeq) { - breakSwitch->addCase(builder.getInt64(l.sequenceNumber), + breakSwitch->addCase(B->getInt64(l.sequenceNumber), trycatch.back().finallyBlock); - continueSwitch->addCase(builder.getInt64(l.sequenceNumber), + continueSwitch->addCase(B->getInt64(l.sequenceNumber), trycatch.back().finallyBlock); } else { - breakSwitch->addCase(builder.getInt64(l.sequenceNumber), finallyBreakDone); - breakDoneSwitch->addCase(builder.getInt64(l.sequenceNumber), l.breakBlock); - continueSwitch->addCase(builder.getInt64(l.sequenceNumber), - finallyContinueDone); - continueDoneSwitch->addCase(builder.getInt64(l.sequenceNumber), - l.continueBlock); + breakSwitch->addCase(B->getInt64(l.sequenceNumber), finallyBreakDone); + breakDoneSwitch->addCase(B->getInt64(l.sequenceNumber), l.breakBlock); + continueSwitch->addCase(B->getInt64(l.sequenceNumber), finallyContinueDone); + continueDoneSwitch->addCase(B->getInt64(l.sequenceNumber), l.continueBlock); } } theSwitch->addCase(excStateBreak, finallyBreak); @@ -1691,7 +1787,7 @@ void LLVMVisitor::visit(const TryCatchFlow *x) { llvm::BasicBlock *catchAll = nullptr; for (auto *c : catches) { - auto *catchBlock = llvm::BasicBlock::Create(context, "trycatch.catch", func); + auto *catchBlock = llvm::BasicBlock::Create(*context, "trycatch.catch", func); tc.catchTypes.push_back(c->getType()); tc.handlers.push_back(catchBlock); @@ -1708,12 +1804,12 @@ void LLVMVisitor::visit(const TryCatchFlow *x) { exitTryCatch(); // make sure we always get to finally block - builder.SetInsertPoint(block); - builder.CreateBr(tc.finallyBlock); + B->SetInsertPoint(block); + B->CreateBr(tc.finallyBlock); // rethrow if uncaught - builder.SetInsertPoint(unwindResumeBlock); - builder.CreateResume(builder.CreateLoad(tc.catchStore)); + B->SetInsertPoint(unwindResumeBlock); + B->CreateResume(B->CreateLoad(tc.catchStore)); // make sure we delegate to parent try-catch if necessary std::vector catchTypesFull(tc.catchTypes); @@ -1735,10 +1831,10 @@ void LLVMVisitor::visit(const TryCatchFlow *x) { if (!it->catchTypes[i] && !catchAll) { // catch-all is in parent; set finally depth catchAll = - llvm::BasicBlock::Create(context, "trycatch.fdepth_catchall", func); - builder.SetInsertPoint(catchAll); - builder.CreateStore(builder.getInt64(depth), tc.delegateDepth); - builder.CreateBr(it->handlers[i]); + llvm::BasicBlock::Create(*context, "trycatch.fdepth_catchall", func); + B->SetInsertPoint(catchAll); + B->CreateStore(B->getInt64(depth), tc.delegateDepth); + B->CreateBr(it->handlers[i]); handlersFull.push_back(catchAll); catchAllDepth = depth; } else { @@ -1750,9 +1846,8 @@ void LLVMVisitor::visit(const TryCatchFlow *x) { } // exception handling - builder.SetInsertPoint(tc.exceptionBlock); - llvm::LandingPadInst *caughtResult = - builder.CreateLandingPad(padType, catches.size()); + B->SetInsertPoint(tc.exceptionBlock); + llvm::LandingPadInst *caughtResult = B->CreateLandingPad(padType, catches.size()); caughtResult->setCleanup(true); std::vector typeIndices; @@ -1765,78 +1860,76 @@ void LLVMVisitor::visit(const TryCatchFlow *x) { caughtResult->addClause(tidx); } - llvm::Value *unwindException = builder.CreateExtractValue(caughtResult, 0); - builder.CreateStore(caughtResult, tc.catchStore); - builder.CreateStore(excStateThrown, tc.excFlag); - llvm::Value *depthMax = builder.getInt64(trycatch.size()); - builder.CreateStore(depthMax, tc.delegateDepth); + llvm::Value *unwindException = B->CreateExtractValue(caughtResult, 0); + B->CreateStore(caughtResult, tc.catchStore); + B->CreateStore(excStateThrown, tc.excFlag); + llvm::Value *depthMax = B->getInt64(trycatch.size()); + B->CreateStore(depthMax, tc.delegateDepth); - llvm::Value *unwindExceptionClass = builder.CreateLoad(builder.CreateStructGEP( - unwindType, - builder.CreatePointerCast(unwindException, unwindType->getPointerTo()), 0)); + llvm::Value *unwindExceptionClass = B->CreateLoad(B->CreateStructGEP( + unwindType, B->CreatePointerCast(unwindException, unwindType->getPointerTo()), + 0)); // check for foreign exceptions - builder.CreateCondBr( - builder.CreateICmpEQ(unwindExceptionClass, builder.getInt64(seq_exc_class())), - tc.exceptionRouteBlock, externalExcBlock); + B->CreateCondBr(B->CreateICmpEQ(unwindExceptionClass, B->getInt64(seq_exc_class())), + tc.exceptionRouteBlock, externalExcBlock); // external exception (currently assumed to be unreachable) - builder.SetInsertPoint(externalExcBlock); - builder.CreateUnreachable(); + B->SetInsertPoint(externalExcBlock); + B->CreateUnreachable(); // reroute Codon exceptions - builder.SetInsertPoint(tc.exceptionRouteBlock); - unwindException = builder.CreateExtractValue(builder.CreateLoad(tc.catchStore), 0); - llvm::Value *excVal = builder.CreatePointerCast( - builder.CreateConstGEP1_64(unwindException, (uint64_t)seq_exc_offset()), + B->SetInsertPoint(tc.exceptionRouteBlock); + unwindException = B->CreateExtractValue(B->CreateLoad(tc.catchStore), 0); + llvm::Value *excVal = B->CreatePointerCast( + B->CreateConstGEP1_64(unwindException, (uint64_t)seq_exc_offset()), excType->getPointerTo()); - llvm::Value *loadedExc = builder.CreateLoad(excVal); - llvm::Value *objType = builder.CreateExtractValue(loadedExc, 0); - objType = builder.CreateExtractValue(objType, 0); - llvm::Value *objPtr = builder.CreateExtractValue(loadedExc, 1); + llvm::Value *loadedExc = B->CreateLoad(excVal); + llvm::Value *objType = B->CreateExtractValue(loadedExc, 0); + objType = B->CreateExtractValue(objType, 0); + llvm::Value *objPtr = B->CreateExtractValue(loadedExc, 1); // set depth when catch-all entered - auto *defaultRouteBlock = llvm::BasicBlock::Create(context, "trycatch.fdepth", func); - builder.SetInsertPoint(defaultRouteBlock); + auto *defaultRouteBlock = llvm::BasicBlock::Create(*context, "trycatch.fdepth", func); + B->SetInsertPoint(defaultRouteBlock); if (catchAll) - builder.CreateStore(builder.getInt64(catchAllDepth), tc.delegateDepth); - builder.CreateBr(catchAll ? (catchAllDepth > 0 ? tc.finallyBlock : catchAll) - : tc.finallyBlock); + B->CreateStore(B->getInt64(catchAllDepth), tc.delegateDepth); + B->CreateBr(catchAll ? (catchAllDepth > 0 ? tc.finallyBlock : catchAll) + : tc.finallyBlock); - builder.SetInsertPoint(tc.exceptionRouteBlock); + B->SetInsertPoint(tc.exceptionRouteBlock); llvm::SwitchInst *switchToCatchBlock = - builder.CreateSwitch(objType, defaultRouteBlock, (unsigned)handlersFull.size()); + B->CreateSwitch(objType, defaultRouteBlock, (unsigned)handlersFull.size()); for (unsigned i = 0; i < handlersFull.size(); i++) { // set finally depth - auto *depthSet = llvm::BasicBlock::Create(context, "trycatch.fdepth", func); - builder.SetInsertPoint(depthSet); - builder.CreateStore(builder.getInt64(depths[i]), tc.delegateDepth); - builder.CreateBr((i < tc.handlers.size()) ? handlersFull[i] : tc.finallyBlock); + auto *depthSet = llvm::BasicBlock::Create(*context, "trycatch.fdepth", func); + B->SetInsertPoint(depthSet); + B->CreateStore(B->getInt64(depths[i]), tc.delegateDepth); + B->CreateBr((i < tc.handlers.size()) ? handlersFull[i] : tc.finallyBlock); if (catchTypesFull[i]) { - switchToCatchBlock->addCase( - builder.getInt32((uint64_t)getTypeIdx(catchTypesFull[i])), depthSet); + switchToCatchBlock->addCase(B->getInt32((uint64_t)getTypeIdx(catchTypesFull[i])), + depthSet); } // translate catch body if this block is ours (vs. a parent's) if (i < catches.size()) { block = handlersFull[i]; - builder.SetInsertPoint(block); + B->SetInsertPoint(block); const Var *var = catches[i]->getVar(); if (var) { - llvm::Value *obj = - builder.CreateBitCast(objPtr, getLLVMType(catches[i]->getType())); - llvm::Value *varPtr = vars[var]; + llvm::Value *obj = B->CreateBitCast(objPtr, getLLVMType(catches[i]->getType())); + llvm::Value *varPtr = getVar(var); seqassert(varPtr, "could not get catch var"); - builder.CreateStore(obj, varPtr); + B->CreateStore(obj, varPtr); } - builder.CreateStore(excStateCaught, tc.excFlag); + B->CreateStore(excStateCaught, tc.excFlag); process(catches[i]->getHandler()); - builder.SetInsertPoint(block); - builder.CreateBr(tc.finallyBlock); + B->SetInsertPoint(block); + B->CreateBr(tc.finallyBlock); } } @@ -1883,50 +1976,50 @@ void LLVMVisitor::codegenPipeline( seqassert(generatorType, "{} is not a generator type", *prevStage->getOutputType()); auto *baseType = getLLVMType(generatorType->getBase()); - auto *condBlock = llvm::BasicBlock::Create(context, "pipeline.cond", func); - auto *bodyBlock = llvm::BasicBlock::Create(context, "pipeline.body", func); - auto *cleanupBlock = llvm::BasicBlock::Create(context, "pipeline.cleanup", func); - auto *exitBlock = llvm::BasicBlock::Create(context, "pipeline.exit", func); + auto *condBlock = llvm::BasicBlock::Create(*context, "pipeline.cond", func); + auto *bodyBlock = llvm::BasicBlock::Create(*context, "pipeline.body", func); + auto *cleanupBlock = llvm::BasicBlock::Create(*context, "pipeline.cleanup", func); + auto *exitBlock = llvm::BasicBlock::Create(*context, "pipeline.exit", func); // LLVM coroutine intrinsics // https://prereleases.llvm.org/6.0.0/rc3/docs/Coroutines.html llvm::FunctionCallee coroResume = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_resume); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_resume); llvm::FunctionCallee coroDone = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_done); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_done); llvm::FunctionCallee coroPromise = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_promise); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_promise); llvm::FunctionCallee coroDestroy = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_destroy); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_destroy); llvm::Value *iter = value; - builder.SetInsertPoint(block); - builder.CreateBr(condBlock); + B->SetInsertPoint(block); + B->CreateBr(condBlock); block = condBlock; call(coroResume, {iter}); - builder.SetInsertPoint(block); - llvm::Value *done = builder.CreateCall(coroDone, iter); - builder.CreateCondBr(done, cleanupBlock, bodyBlock); + B->SetInsertPoint(block); + llvm::Value *done = B->CreateCall(coroDone, iter); + B->CreateCondBr(done, cleanupBlock, bodyBlock); - builder.SetInsertPoint(bodyBlock); + B->SetInsertPoint(bodyBlock); llvm::Value *alignment = - builder.getInt32(module->getDataLayout().getPrefTypeAlignment(baseType)); - llvm::Value *from = builder.getFalse(); - llvm::Value *promise = builder.CreateCall(coroPromise, {iter, alignment, from}); - promise = builder.CreateBitCast(promise, baseType->getPointerTo()); - value = builder.CreateLoad(promise); + B->getInt32(M->getDataLayout().getPrefTypeAlignment(baseType)); + llvm::Value *from = B->getFalse(); + llvm::Value *promise = B->CreateCall(coroPromise, {iter, alignment, from}); + promise = B->CreateBitCast(promise, baseType->getPointerTo()); + value = B->CreateLoad(promise); block = bodyBlock; callStage(stage); codegenPipeline(stages, where + 1); - builder.SetInsertPoint(block); - builder.CreateBr(condBlock); + B->SetInsertPoint(block); + B->CreateBr(condBlock); - builder.SetInsertPoint(cleanupBlock); - builder.CreateCall(coroDestroy, iter); - builder.CreateBr(exitBlock); + B->SetInsertPoint(cleanupBlock); + B->CreateCall(coroDestroy, iter); + B->CreateBr(exitBlock); block = exitBlock; } else { @@ -1944,7 +2037,7 @@ void LLVMVisitor::visit(const PipelineFlow *x) { } void LLVMVisitor::visit(const dsl::CustomFlow *x) { - builder.SetInsertPoint(block); + B->SetInsertPoint(block); value = x->getBuilder()->buildValue(this); } @@ -1953,12 +2046,12 @@ void LLVMVisitor::visit(const dsl::CustomFlow *x) { */ void LLVMVisitor::visit(const AssignInstr *x) { - llvm::Value *var = vars[x->getLhs()]; + llvm::Value *var = getVar(x->getLhs()); seqassert(var, "could not find {} var", *x); process(x->getRhs()); - if (var != getDummyVoidValue(context)) { - builder.SetInsertPoint(block); - builder.CreateStore(value, var); + if (var != getDummyVoidValue()) { + B->SetInsertPoint(block); + B->CreateStore(value, var); } } @@ -1969,13 +2062,13 @@ void LLVMVisitor::visit(const ExtractInstr *x) { seqassert(index >= 0, "invalid index"); process(x->getVal()); - builder.SetInsertPoint(block); + B->SetInsertPoint(block); if (auto *refType = cast(memberedType)) { - value = builder.CreateBitCast(value, - getLLVMType(refType->getContents())->getPointerTo()); - value = builder.CreateLoad(value); + value = + B->CreateBitCast(value, getLLVMType(refType->getContents())->getPointerTo()); + value = B->CreateLoad(value); } - value = builder.CreateExtractValue(value, index); + value = B->CreateExtractValue(value, index); } void LLVMVisitor::visit(const InsertInstr *x) { @@ -1989,21 +2082,21 @@ void LLVMVisitor::visit(const InsertInstr *x) { process(x->getRhs()); llvm::Value *rhs = value; - builder.SetInsertPoint(block); - lhs = builder.CreateBitCast(lhs, getLLVMType(refType->getContents())->getPointerTo()); - llvm::Value *load = builder.CreateLoad(lhs); - load = builder.CreateInsertValue(load, rhs, index); - builder.CreateStore(load, lhs); + B->SetInsertPoint(block); + lhs = B->CreateBitCast(lhs, getLLVMType(refType->getContents())->getPointerTo()); + llvm::Value *load = B->CreateLoad(lhs); + load = B->CreateInsertValue(load, rhs, index); + B->CreateStore(load, lhs); } void LLVMVisitor::visit(const CallInstr *x) { - builder.SetInsertPoint(block); + B->SetInsertPoint(block); process(x->getCallee()); llvm::Value *f = value; std::vector args; for (auto *arg : *x) { - builder.SetInsertPoint(block); + B->SetInsertPoint(block); process(arg); args.push_back(value); } @@ -2013,14 +2106,14 @@ void LLVMVisitor::visit(const CallInstr *x) { } void LLVMVisitor::visit(const TypePropertyInstr *x) { - builder.SetInsertPoint(block); + B->SetInsertPoint(block); switch (x->getProperty()) { case TypePropertyInstr::Property::SIZEOF: - value = builder.getInt64( - module->getDataLayout().getTypeAllocSize(getLLVMType(x->getInspectType()))); + value = B->getInt64( + M->getDataLayout().getTypeAllocSize(getLLVMType(x->getInspectType()))); break; case TypePropertyInstr::Property::IS_ATOMIC: - value = builder.getInt8(x->getInspectType()->isAtomic() ? 1 : 0); + value = B->getInt8(x->getInspectType()->isAtomic() ? 1 : 0); break; default: seqassert(0, "unknown type property"); @@ -2028,65 +2121,65 @@ void LLVMVisitor::visit(const TypePropertyInstr *x) { } void LLVMVisitor::visit(const YieldInInstr *x) { - builder.SetInsertPoint(block); + B->SetInsertPoint(block); if (x->isSuspending()) { llvm::FunctionCallee coroSuspend = - llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::coro_suspend); - llvm::Value *tok = llvm::ConstantTokenNone::get(context); - llvm::Value *final = builder.getFalse(); - llvm::Value *susp = builder.CreateCall(coroSuspend, {tok, final}); + llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_suspend); + llvm::Value *tok = llvm::ConstantTokenNone::get(*context); + llvm::Value *final = B->getFalse(); + llvm::Value *susp = B->CreateCall(coroSuspend, {tok, final}); - block = llvm::BasicBlock::Create(context, "yieldin.new", func); - llvm::SwitchInst *inst = builder.CreateSwitch(susp, coro.suspend, 2); - inst->addCase(builder.getInt8(0), block); - inst->addCase(builder.getInt8(1), coro.cleanup); - builder.SetInsertPoint(block); + block = llvm::BasicBlock::Create(*context, "yieldin.new", func); + llvm::SwitchInst *inst = B->CreateSwitch(susp, coro.suspend, 2); + inst->addCase(B->getInt8(0), block); + inst->addCase(B->getInt8(1), coro.cleanup); + B->SetInsertPoint(block); } - value = builder.CreateLoad(coro.promise); + value = B->CreateLoad(coro.promise); } void LLVMVisitor::visit(const StackAllocInstr *x) { auto *arrayType = llvm::cast(getLLVMType(x->getType())); - builder.SetInsertPoint(func->getEntryBlock().getTerminator()); - llvm::Value *len = builder.getInt64(x->getCount()); - llvm::Value *ptr = builder.CreateAlloca( + B->SetInsertPoint(func->getEntryBlock().getTerminator()); + llvm::Value *len = B->getInt64(x->getCount()); + llvm::Value *ptr = B->CreateAlloca( llvm::cast(arrayType->getElementType(1))->getElementType(), len); llvm::Value *arr = llvm::UndefValue::get(arrayType); - arr = builder.CreateInsertValue(arr, len, 0); - arr = builder.CreateInsertValue(arr, ptr, 1); + arr = B->CreateInsertValue(arr, len, 0); + arr = B->CreateInsertValue(arr, ptr, 1); value = arr; } void LLVMVisitor::visit(const TernaryInstr *x) { - auto *trueBlock = llvm::BasicBlock::Create(context, "ternary.true", func); - auto *falseBlock = llvm::BasicBlock::Create(context, "ternary.false", func); - auto *exitBlock = llvm::BasicBlock::Create(context, "ternary.exit", func); + auto *trueBlock = llvm::BasicBlock::Create(*context, "ternary.true", func); + auto *falseBlock = llvm::BasicBlock::Create(*context, "ternary.false", func); + auto *exitBlock = llvm::BasicBlock::Create(*context, "ternary.exit", func); llvm::Type *valueType = getLLVMType(x->getType()); process(x->getCond()); llvm::Value *cond = value; - builder.SetInsertPoint(block); - cond = builder.CreateTrunc(cond, builder.getInt1Ty()); - builder.CreateCondBr(cond, trueBlock, falseBlock); + B->SetInsertPoint(block); + cond = B->CreateTrunc(cond, B->getInt1Ty()); + B->CreateCondBr(cond, trueBlock, falseBlock); block = trueBlock; process(x->getTrueValue()); llvm::Value *trueValue = value; trueBlock = block; - builder.SetInsertPoint(trueBlock); - builder.CreateBr(exitBlock); + B->SetInsertPoint(trueBlock); + B->CreateBr(exitBlock); block = falseBlock; process(x->getFalseValue()); llvm::Value *falseValue = value; falseBlock = block; - builder.SetInsertPoint(falseBlock); - builder.CreateBr(exitBlock); + B->SetInsertPoint(falseBlock); + B->CreateBr(exitBlock); - builder.SetInsertPoint(exitBlock); - llvm::PHINode *phi = builder.CreatePHI(valueType, 2); + B->SetInsertPoint(exitBlock); + llvm::PHINode *phi = B->CreatePHI(valueType, 2); phi->addIncoming(trueValue, trueBlock); phi->addIncoming(falseValue, falseBlock); value = phi; @@ -2095,72 +2188,72 @@ void LLVMVisitor::visit(const TernaryInstr *x) { void LLVMVisitor::visit(const BreakInstr *x) { seqassert(!loops.empty(), "not in a loop"); - builder.SetInsertPoint(block); + B->SetInsertPoint(block); auto *loop = !x->getLoop() ? &loops.back() : getLoopData(x->getLoop()->getId()); if (trycatch.empty() || trycatch.back().sequenceNumber < loop->sequenceNumber) { - builder.CreateBr(loop->breakBlock); + B->CreateBr(loop->breakBlock); } else { auto *tc = &trycatch.back(); - auto *excStateBreak = builder.getInt8(TryCatchData::State::BREAK); - builder.CreateStore(excStateBreak, tc->excFlag); - builder.CreateStore(builder.getInt64(loop->sequenceNumber), tc->loopSequence); - builder.CreateBr(tc->finallyBlock); + auto *excStateBreak = B->getInt8(TryCatchData::State::BREAK); + B->CreateStore(excStateBreak, tc->excFlag); + B->CreateStore(B->getInt64(loop->sequenceNumber), tc->loopSequence); + B->CreateBr(tc->finallyBlock); } - block = llvm::BasicBlock::Create(context, "break.new", func); + block = llvm::BasicBlock::Create(*context, "break.new", func); } void LLVMVisitor::visit(const ContinueInstr *x) { seqassert(!loops.empty(), "not in a loop"); - builder.SetInsertPoint(block); + B->SetInsertPoint(block); auto *loop = !x->getLoop() ? &loops.back() : getLoopData(x->getLoop()->getId()); if (trycatch.empty() || trycatch.back().sequenceNumber < loop->sequenceNumber) { - builder.CreateBr(loop->continueBlock); + B->CreateBr(loop->continueBlock); } else { auto *tc = &trycatch.back(); - auto *excStateContinue = builder.getInt8(TryCatchData::State::CONTINUE); - builder.CreateStore(excStateContinue, tc->excFlag); - builder.CreateStore(builder.getInt64(loop->sequenceNumber), tc->loopSequence); - builder.CreateBr(tc->finallyBlock); + auto *excStateContinue = B->getInt8(TryCatchData::State::CONTINUE); + B->CreateStore(excStateContinue, tc->excFlag); + B->CreateStore(B->getInt64(loop->sequenceNumber), tc->loopSequence); + B->CreateBr(tc->finallyBlock); } - block = llvm::BasicBlock::Create(context, "continue.new", func); + block = llvm::BasicBlock::Create(*context, "continue.new", func); } void LLVMVisitor::visit(const ReturnInstr *x) { if (x->getValue()) { process(x->getValue()); } - builder.SetInsertPoint(block); + B->SetInsertPoint(block); if (coro.exit) { if (auto *tc = getInnermostTryCatch()) { - auto *excStateReturn = builder.getInt8(TryCatchData::State::RETURN); - builder.CreateStore(excStateReturn, tc->excFlag); - builder.CreateBr(tc->finallyBlock); + auto *excStateReturn = B->getInt8(TryCatchData::State::RETURN); + B->CreateStore(excStateReturn, tc->excFlag); + B->CreateBr(tc->finallyBlock); } else { - builder.CreateBr(coro.exit); + B->CreateBr(coro.exit); } } else { if (auto *tc = getInnermostTryCatch()) { - auto *excStateReturn = builder.getInt8(TryCatchData::State::RETURN); - builder.CreateStore(excStateReturn, tc->excFlag); + auto *excStateReturn = B->getInt8(TryCatchData::State::RETURN); + B->CreateStore(excStateReturn, tc->excFlag); if (tc->retStore) { seqassert(value, "no return value storage"); - builder.CreateStore(value, tc->retStore); + B->CreateStore(value, tc->retStore); } - builder.CreateBr(tc->finallyBlock); + B->CreateBr(tc->finallyBlock); } else { if (x->getValue()) { - builder.CreateRet(value); + B->CreateRet(value); } else { - builder.CreateRetVoid(); + B->CreateRetVoid(); } } } - block = llvm::BasicBlock::Create(context, "return.new", func); + block = llvm::BasicBlock::Create(*context, "return.new", func); } void LLVMVisitor::visit(const YieldInstr *x) { @@ -2168,18 +2261,18 @@ void LLVMVisitor::visit(const YieldInstr *x) { if (x->getValue()) { seqassert(coro.promise, "no coroutine promise"); process(x->getValue()); - builder.SetInsertPoint(block); - builder.CreateStore(value, coro.promise); + B->SetInsertPoint(block); + B->CreateStore(value, coro.promise); } - builder.SetInsertPoint(block); + B->SetInsertPoint(block); if (auto *tc = getInnermostTryCatch()) { - auto *excStateReturn = builder.getInt8(TryCatchData::State::RETURN); - builder.CreateStore(excStateReturn, tc->excFlag); - builder.CreateBr(tc->finallyBlock); + auto *excStateReturn = B->getInt8(TryCatchData::State::RETURN); + B->CreateStore(excStateReturn, tc->excFlag); + B->CreateBr(tc->finallyBlock); } else { - builder.CreateBr(coro.exit); + B->CreateBr(coro.exit); } - block = llvm::BasicBlock::Create(context, "yield.new", func); + block = llvm::BasicBlock::Create(*context, "yield.new", func); } else { if (x->getValue()) { process(x->getValue()); @@ -2195,9 +2288,9 @@ void LLVMVisitor::visit(const ThrowInstr *x) { auto excAllocFunc = makeExcAllocFunc(); auto throwFunc = makeThrowFunc(); process(x->getValue()); - builder.SetInsertPoint(block); - llvm::Value *exc = builder.CreateCall( - excAllocFunc, {builder.getInt32(getTypeIdx(x->getValue()->getType())), value}); + B->SetInsertPoint(block); + llvm::Value *exc = B->CreateCall( + excAllocFunc, {B->getInt32(getTypeIdx(x->getValue()->getType())), value}); call(throwFunc, exc); } @@ -2207,7 +2300,7 @@ void LLVMVisitor::visit(const FlowInstr *x) { } void LLVMVisitor::visit(const dsl::CustomInstr *x) { - builder.SetInsertPoint(block); + B->SetInsertPoint(block); value = x->getBuilder()->buildValue(this); } diff --git a/codon/sir/llvm/llvisitor.h b/codon/sir/llvm/llvisitor.h index 902d17a1..0191daa2 100644 --- a/codon/sir/llvm/llvisitor.h +++ b/codon/sir/llvm/llvisitor.h @@ -3,9 +3,11 @@ #include "codon/dsl/plugins.h" #include "codon/sir/llvm/llvm.h" #include "codon/sir/sir.h" +#include "codon/util/common.h" #include #include +#include #include namespace codon { @@ -13,19 +15,6 @@ namespace ir { class LLVMVisitor : public util::ConstVisitor { private: - template using CacheBase = std::unordered_map; - template class Cache : public CacheBase { - public: - using CacheBase::CacheBase; - - V *operator[](const K *key) { - auto it = CacheBase::find(key->getId()); - return (it != CacheBase::end()) ? it->second : nullptr; - } - - void insert(const K *key, V *value) { CacheBase::emplace(key->getId(), value); } - }; - struct CoroData { /// Coroutine promise (where yielded values are stored) llvm::Value *promise; @@ -37,6 +26,8 @@ private: llvm::BasicBlock *suspend; /// Coroutine exit block llvm::BasicBlock *exit; + + void reset() { promise = handle = cleanup = suspend = exit = nullptr; } }; struct NestableData { @@ -56,6 +47,8 @@ private: LoopData(llvm::BasicBlock *breakBlock, llvm::BasicBlock *continueBlock, id_t loopId) : NestableData(), breakBlock(breakBlock), continueBlock(continueBlock), loopId(loopId) {} + + void reset() { breakBlock = continueBlock = nullptr; } }; struct TryCatchData : NestableData { @@ -87,6 +80,13 @@ private: finallyBlock(nullptr), catchTypes(), handlers(), excFlag(nullptr), catchStore(nullptr), delegateDepth(nullptr), retStore(nullptr), loopSequence(nullptr) {} + + void reset() { + exceptionBlock = exceptionRouteBlock = finallyBlock = nullptr; + catchTypes.clear(); + handlers.clear(); + excFlag = catchStore = delegateDepth = loopSequence = nullptr; + } }; struct DebugInfo { @@ -96,21 +96,31 @@ private: llvm::DICompileUnit *unit; /// Whether we are compiling in debug mode bool debug; + /// Whether we are compiling in JIT mode + bool jit; + /// Whether we are compiling a standalone object/executable + bool standalone; /// Program command-line flags std::string flags; - explicit DebugInfo(bool debug, const std::string &flags) - : builder(), unit(nullptr), debug(debug), flags(flags) {} + DebugInfo() + : builder(), unit(nullptr), debug(false), jit(false), standalone(false), + flags() {} llvm::DIFile *getFile(const std::string &path); + + void reset() { + builder = {}; + unit = nullptr; + } }; /// LLVM context used for compilation - llvm::LLVMContext context; - /// LLVM IR builder used for constructing LLVM IR - llvm::IRBuilder<> builder; + std::unique_ptr context; /// Module we are compiling - std::unique_ptr module; + std::unique_ptr M; + /// LLVM IR builder used for constructing LLVM IR + std::unique_ptr> B; /// Current function we are compiling llvm::Function *func; /// Current basic block we are compiling @@ -118,9 +128,9 @@ private: /// Last compiled value llvm::Value *value; /// LLVM values corresponding to IR variables - Cache vars; + std::unordered_map vars; /// LLVM functions corresponding to IR functions - Cache funcs; + std::unordered_map funcs; /// Coroutine data, if current function is a coroutine CoroData coro; /// Loop data stack, containing break/continue blocks @@ -175,17 +185,82 @@ private: // LLVM passes void runLLVMPipeline(); -public: - LLVMVisitor(bool debug = false, const std::string &flags = ""); + llvm::Value *getVar(const Var *var); + void insertVar(const Var *var, llvm::Value *x) { vars.emplace(var->getId(), x); } + llvm::Function *getFunc(const Func *func); + void insertFunc(const Func *func, llvm::Function *x) { + funcs.emplace(func->getId(), x); + } + llvm::Value *getDummyVoidValue() { return llvm::ConstantTokenNone::get(*context); } + llvm::DISubprogram *getDISubprogramForFunc(const Func *x); - llvm::LLVMContext &getContext() { return context; } - llvm::IRBuilder<> &getBuilder() { return builder; } - llvm::Module *getModule() { return module.get(); } +public: + static std::string getNameForFunction(const Func *x) { + if (isA(x)) { + return x->getUnmangledName(); + } else { + return x->referenceString(); + } + } + + static std::string getDebugNameForVariable(const Var *x) { + std::string name = x->getName(); + auto pos = name.find("."); + if (pos != 0 && pos != std::string::npos) { + return name.substr(0, pos); + } else { + return name; + } + } + + static const SrcInfo *getDefaultSrcInfo() { + static SrcInfo defaultSrcInfo("", 0, 0, 0); + return &defaultSrcInfo; + } + + static const SrcInfo *getSrcInfo(const Node *x) { + if (auto *srcInfo = x->getAttribute()) { + return &srcInfo->info; + } else { + return getDefaultSrcInfo(); + } + } + + /// Constructs an LLVM visitor. + LLVMVisitor(); + + /// @return true if in debug mode, false otherwise + bool getDebug() const { return db.debug; } + /// Sets debug status. + /// @param d true if debug mode + void setDebug(bool d = true) { db.debug = d; } + + /// @return true if in JIT mode, false otherwise + bool getJIT() const { return db.jit; } + /// Sets JIT status. + /// @param j true if JIT mode + void setJIT(bool j = true) { db.jit = j; } + + /// @return true if in standalone mode, false otherwise + bool getStandalone() const { return db.standalone; } + /// Sets standalone status. + /// @param s true if standalone + void setStandalone(bool s = true) { db.standalone = s; } + + /// @return program flags + std::string getFlags() const { return db.flags; } + /// Sets program flags. + /// @param f flags + void setFlags(const std::string &f) { db.flags = f; } + + llvm::LLVMContext &getContext() { return *context; } + llvm::IRBuilder<> &getBuilder() { return *B; } + llvm::Module *getModule() { return M.get(); } llvm::FunctionCallee getFunc() { return func; } llvm::BasicBlock *getBlock() { return block; } llvm::Value *getValue() { return value; } - Cache &getVars() { return vars; } - Cache &getFuncs() { return funcs; } + std::unordered_map &getVars() { return vars; } + std::unordered_map &getFuncs() { return funcs; } CoroData &getCoro() { return coro; } std::vector &getLoops() { return loops; } std::vector &getTryCatch() { return trycatch; } @@ -195,6 +270,31 @@ public: void setBlock(llvm::BasicBlock *b) { block = b; } void setValue(llvm::Value *v) { value = v; } + /// Registers a new global variable or function with + /// this visitor. + /// @param var the global variable (or function) to register + void registerGlobal(const Var *var); + + /// Returns the default LLVM linkage type for the module. + /// @return LLVM linkage type + llvm::GlobalValue::LinkageTypes getDefaultLinkage(); + + /// Returns a new LLVM module initialized for the host + /// architecture. + /// @param context LLVM context used for creating module + /// @param src source information for the new module + /// @return a new module + std::unique_ptr makeModule(llvm::LLVMContext &context, + const SrcInfo *src = nullptr); + + /// Returns the current module/LLVM context and replaces them + /// with new, fresh ones. References to variables or functions + /// from the old module will be included as "external". + /// @param src source information for the new module + /// @return the current module/context, replaced internally + std::pair, std::unique_ptr> + takeModule(const SrcInfo *src = nullptr); + /// Sets current debug info based on a given node. /// @param node the node whose debug info to use void setDebugInfoForNode(const Node *node); diff --git a/codon/sir/llvm/llvm.h b/codon/sir/llvm/llvm.h index d73ef056..d3e2f3e5 100644 --- a/codon/sir/llvm/llvm.h +++ b/codon/sir/llvm/llvm.h @@ -12,8 +12,10 @@ #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/ExecutionEngine/JITEventListener.h" #include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/RuntimeDyld.h" diff --git a/codon/sir/llvm/optimize.cpp b/codon/sir/llvm/optimize.cpp index 2ca7a00a..b561c2f1 100644 --- a/codon/sir/llvm/optimize.cpp +++ b/codon/sir/llvm/optimize.cpp @@ -1,7 +1,5 @@ #include "optimize.h" -#include - #include "codon/sir/llvm/coro/Coroutines.h" #include "codon/util/common.h" #include "llvm/CodeGen/CommandFlags.h" @@ -51,15 +49,14 @@ std::unique_ptr getTargetMachine(llvm::Module *module, } namespace { -void applyDebugTransformations(llvm::Module *module, bool debug) { +void applyDebugTransformations(llvm::Module *module, bool debug, bool jit) { if (debug) { // remove tail calls and fix linkage for stack traces for (auto &f : *module) { - f.setLinkage(llvm::GlobalValue::ExternalLinkage); - if (f.hasFnAttribute(llvm::Attribute::AttrKind::AlwaysInline)) { - f.removeFnAttr(llvm::Attribute::AttrKind::AlwaysInline); - } - f.addFnAttr(llvm::Attribute::AttrKind::NoInline); + if (!jit) + f.setLinkage(llvm::GlobalValue::ExternalLinkage); + if (!f.hasFnAttribute(llvm::Attribute::AttrKind::AlwaysInline)) + f.addFnAttr(llvm::Attribute::AttrKind::NoInline); f.setHasUWTable(); f.addFnAttr("no-frame-pointer-elim", "true"); f.addFnAttr("no-frame-pointer-elim-non-leaf"); @@ -163,9 +160,9 @@ char CoroBranchSimplifier::ID = 0; llvm::RegisterPass X("coro-br-simpl", "Coroutine Branch Simplifier"); -void runLLVMOptimizationPasses(llvm::Module *module, bool debug, +void runLLVMOptimizationPasses(llvm::Module *module, bool debug, bool jit, PluginManager *plugins) { - applyDebugTransformations(module, debug); + applyDebugTransformations(module, debug, jit); llvm::Triple moduleTriple(module->getTargetTriple()); llvm::TargetLibraryInfoImpl tlii(moduleTriple); @@ -227,7 +224,7 @@ void runLLVMOptimizationPasses(llvm::Module *module, bool debug, } fpm->doFinalization(); pm->run(*module); - applyDebugTransformations(module, debug); + applyDebugTransformations(module, debug, jit); } void verify(llvm::Module *module) { @@ -237,22 +234,15 @@ void verify(llvm::Module *module) { } // namespace -void optimize(llvm::Module *module, bool debug, PluginManager *plugins) { - using std::chrono::duration_cast; - using std::chrono::high_resolution_clock; - using std::chrono::milliseconds; - auto t = high_resolution_clock::now(); +void optimize(llvm::Module *module, bool debug, bool jit, PluginManager *plugins) { verify(module); - runLLVMOptimizationPasses(module, debug, plugins); - LOG_TIME("[T] llvm/opt = {:.1f}", - duration_cast(high_resolution_clock::now() - t).count() / - 1000.0); + { + TIME("llvm/opt"); + runLLVMOptimizationPasses(module, debug, jit, plugins); + } if (!debug) { - t = high_resolution_clock::now(); - runLLVMOptimizationPasses(module, debug, plugins); - LOG_TIME("[T] llvm/opt2 = {:.1f}", - duration_cast(high_resolution_clock::now() - t).count() / - 1000.0); + TIME("llvm/opt2"); + runLLVMOptimizationPasses(module, debug, jit, plugins); } verify(module); } diff --git a/codon/sir/llvm/optimize.h b/codon/sir/llvm/optimize.h index 0dcc11a1..0a3ac638 100644 --- a/codon/sir/llvm/optimize.h +++ b/codon/sir/llvm/optimize.h @@ -14,6 +14,7 @@ getTargetMachine(llvm::Triple triple, llvm::StringRef cpuStr, std::unique_ptr getTargetMachine(llvm::Module *module, bool setFunctionAttributes = false); -void optimize(llvm::Module *module, bool debug, PluginManager *plugins = nullptr); +void optimize(llvm::Module *module, bool debug, bool jit = false, + PluginManager *plugins = nullptr); } // namespace ir } // namespace codon diff --git a/codon/sir/module.cpp b/codon/sir/module.cpp index c73f9f7c..db42b406 100644 --- a/codon/sir/module.cpp +++ b/codon/sir/module.cpp @@ -100,8 +100,7 @@ const std::string Module::INIT_MAGIC_NAME = "__init__"; const char Module::NodeId = 0; -Module::Module(std::string name, std::shared_ptr cache) - : AcceptorExtend(std::move(name)), cache(std::move(cache)) { +Module::Module(const std::string &name) : AcceptorExtend(name) { mainFunc = std::make_unique("main"); mainFunc->realize(cast(unsafeGetDummyFuncType()), {}); mainFunc->setModule(this); diff --git a/codon/sir/module.h b/codon/sir/module.h index 826144dd..a24dfaf4 100644 --- a/codon/sir/module.h +++ b/codon/sir/module.h @@ -92,15 +92,14 @@ private: typesMap; /// the type-checker cache - std::shared_ptr cache; + ast::Cache *cache = nullptr; public: static const char NodeId; /// Constructs an SIR module. /// @param name the module name - /// @param cache the type-checker cache - explicit Module(std::string name, std::shared_ptr cache = nullptr); + explicit Module(const std::string &name = ""); virtual ~Module() noexcept = default; @@ -259,9 +258,10 @@ public: } /// @return the type-checker cache - std::shared_ptr getCache() { return cache; } - /// @return the type-checker cache - std::shared_ptr getCache() const { return cache; } + ast::Cache *getCache() const { return cache; } + /// Sets the type-checker cache. + /// @param c the cache + void setCache(ast::Cache *c) { cache = c; } /// Gets or realizes a method. /// @param parent the parent class diff --git a/codon/sir/transform/cleanup/global_demote.cpp b/codon/sir/transform/cleanup/global_demote.cpp index f8270e0f..90fb44b2 100644 --- a/codon/sir/transform/cleanup/global_demote.cpp +++ b/codon/sir/transform/cleanup/global_demote.cpp @@ -45,7 +45,7 @@ void GlobalDemotionPass::run(Module *M) { } for (auto it : localGlobals) { - if (!it.second) + if (!it.second || it.first->getId() == M->getArgVar()->getId()) continue; seqassert(it.first->isGlobal(), "var was not global"); it.first->setGlobal(false); diff --git a/codon/sir/transform/folding/const_prop.h b/codon/sir/transform/folding/const_prop.h index 3ef08568..54a2ec16 100644 --- a/codon/sir/transform/folding/const_prop.h +++ b/codon/sir/transform/folding/const_prop.h @@ -20,6 +20,7 @@ public: /// Constructs a constant propagation pass. /// @param reachingDefKey the reaching definition analysis' key + /// @param globalVarsKey global variables analysis' key ConstPropPass(const std::string &reachingDefKey, const std::string &globalVarsKey) : reachingDefKey(reachingDefKey), globalVarsKey(globalVarsKey) {} diff --git a/codon/sir/transform/manager.cpp b/codon/sir/transform/manager.cpp index a59db415..8e5d12bb 100644 --- a/codon/sir/transform/manager.cpp +++ b/codon/sir/transform/manager.cpp @@ -1,6 +1,5 @@ #include "manager.h" -#include #include #include "codon/sir/analyze/analysis.h" @@ -49,7 +48,7 @@ std::string PassManager::registerPass(std::unique_ptr pass, key = km.getUniqueKey(key); for (const auto &req : reqs) { - assert(deps.find(req) != deps.end()); + seqassert(deps.find(req) != deps.end(), "required key '{}' not found", req); deps[req].push_back(key); } @@ -76,7 +75,7 @@ std::string PassManager::registerAnalysis(std::unique_ptr ana key = km.getUniqueKey(key); for (const auto &req : reqs) { - assert(deps.find(req) != deps.end()); + seqassert(deps.find(req) != deps.end(), "required key '{}' not found", req); deps[req].push_back(key); } diff --git a/codon/sir/util/cloning.cpp b/codon/sir/util/cloning.cpp index 74269f28..5acd086a 100644 --- a/codon/sir/util/cloning.cpp +++ b/codon/sir/util/cloning.cpp @@ -35,7 +35,7 @@ void CloneVisitor::visit(const BodiedFunc *v) { if (v->getBody()) res->setBody(clone(v->getBody())); - res->setBuiltin(v->isBuiltin()); + res->setJIT(v->isJIT()); result = res; } diff --git a/codon/sir/util/matching.cpp b/codon/sir/util/matching.cpp index 38f6aedf..82e51a9f 100644 --- a/codon/sir/util/matching.cpp +++ b/codon/sir/util/matching.cpp @@ -47,7 +47,7 @@ public: result = compareFuncs(x, y) && std::equal(x->begin(), x->end(), y->begin(), y->end(), [this](auto *x, auto *y) { return process(x, y); }) && - process(x->getBody(), y->getBody()) && x->isBuiltin() == y->isBuiltin(); + process(x->getBody(), y->getBody()) && x->isJIT() == y->isJIT(); } VISIT(ExternalFunc); void handle(const ExternalFunc *x, const ExternalFunc *y) { diff --git a/codon/util/common.cpp b/codon/util/common.cpp index c2372b3e..20366b3c 100644 --- a/codon/util/common.cpp +++ b/codon/util/common.cpp @@ -1,24 +1,33 @@ -#include "codon/util/common.h" +#include "common.h" + #include #include +#include +#include namespace codon { namespace { void compilationMessage(const std::string &header, const std::string &msg, const std::string &file, int line, int col) { - assert(!(file.empty() && (line > 0 || col > 0))); - assert(!(col > 0 && line <= 0)); - std::cerr << "\033[1m"; + auto &out = getLogger().err; + seqassert(!(file.empty() && (line > 0 || col > 0)), + "empty filename with non-zero line/col: file={}, line={}, col={}", file, + line, col); + seqassert(!(col > 0 && line <= 0), "col but no line: file={}, line={}, col={}", file, + line, col); + out << "\033[1m"; if (!file.empty()) - std::cerr << file.substr(file.rfind('/') + 1); + out << file.substr(file.rfind('/') + 1); if (line > 0) - std::cerr << ":" << line; + out << ":" << line; if (col > 0) - std::cerr << ":" << col; + out << ":" << col; if (!file.empty()) - std::cerr << ": "; - std::cerr << header << "\033[1m " << msg << "\033[0m" << std::endl; + out << ": "; + out << header << "\033[1m " << msg << "\033[0m" << std::endl; } + +std::vector loggers; } // namespace void compilationError(const std::string &msg, const std::string &file, int line, @@ -34,12 +43,36 @@ void compilationWarning(const std::string &msg, const std::string &file, int lin if (terminate) exit(EXIT_FAILURE); } + +void Logger::parse(const std::string &s) { + flags |= s.find('t') != std::string::npos ? FLAG_TIME : 0; + flags |= s.find('r') != std::string::npos ? FLAG_REALIZE : 0; + flags |= s.find('T') != std::string::npos ? FLAG_TYPECHECK : 0; + flags |= s.find('i') != std::string::npos ? FLAG_IR : 0; + flags |= s.find('l') != std::string::npos ? FLAG_USER : 0; +} } // namespace codon -void _seqassert(const char *expr_str, const char *file, int line, - const std::string &msg) { - std::cerr << "Assert failed:\t" << msg << "\n" - << "Expression:\t" << expr_str << "\n" - << "Source:\t\t" << file << ":" << line << "\n"; +codon::Logger &codon::getLogger() { + if (loggers.empty()) + loggers.emplace_back(); + return loggers.back(); +} + +void codon::pushLogger() { loggers.emplace_back(); } + +bool codon::popLogger() { + if (loggers.empty()) + return false; + loggers.pop_back(); + return true; +} + +void codon::assertionFailure(const char *expr_str, const char *file, int line, + const std::string &msg) { + auto &out = getLogger().err; + out << "Assert failed:\t" << msg << "\n" + << "Expression:\t" << expr_str << "\n" + << "Source:\t\t" << file << ":" << line << "\n"; abort(); } diff --git a/codon/util/common.h b/codon/util/common.h index 31e464bf..780ae25c 100644 --- a/codon/util/common.h +++ b/codon/util/common.h @@ -1,13 +1,9 @@ #pragma once -#include -#include -#include -#include -#include +#include +#include +#include #include -#include -#include #include "codon/config/config.h" #include "codon/util/fmt/format.h" @@ -16,68 +12,122 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" -extern int _dbg_level; -extern int _level; -#define DBG(c, ...) fmt::print("{}" c "\n", std::string(2 * _level, ' '), ##__VA_ARGS__) +#define DBG(c, ...) \ + fmt::print(codon::getLogger().log, "{}" c "\n", \ + std::string(2 * codon::getLogger().level, ' '), ##__VA_ARGS__) #define LOG(c, ...) DBG(c, ##__VA_ARGS__) #define LOG_TIME(c, ...) \ { \ - if (_dbg_level & (1 << 0)) \ + if (codon::getLogger().flags & codon::Logger::FLAG_TIME) \ DBG(c, ##__VA_ARGS__); \ } #define LOG_REALIZE(c, ...) \ { \ - if (_dbg_level & (1 << 2)) \ + if (codon::getLogger().flags & codon::Logger::FLAG_REALIZE) \ DBG(c, ##__VA_ARGS__); \ } #define LOG_TYPECHECK(c, ...) \ { \ - if (_dbg_level & (1 << 4)) \ + if (codon::getLogger().flags & codon::Logger::FLAG_TYPECHECK) \ DBG(c, ##__VA_ARGS__); \ } #define LOG_IR(c, ...) \ { \ - if (_dbg_level & (1 << 6)) \ + if (codon::getLogger().flags & codon::Logger::FLAG_IR) \ DBG(c, ##__VA_ARGS__); \ } #define LOG_USER(c, ...) \ { \ - if (_dbg_level & (1 << 7)) \ + if (codon::getLogger().flags & codon::Logger::FLAG_USER) \ DBG(c, ##__VA_ARGS__); \ } -#define CAST(s, T) dynamic_cast(s.get()) + +#define TIME(name) codon::Timer __timer(name) #ifndef NDEBUG #define seqassert(expr, msg, ...) \ ((expr) ? (void)(0) \ - : _seqassert(#expr, __FILE__, __LINE__, fmt::format(msg, ##__VA_ARGS__))) + : codon::assertionFailure(#expr, __FILE__, __LINE__, \ + fmt::format(msg, ##__VA_ARGS__))) #else #define seqassert(expr, msg, ...) ; #endif #pragma clang diagnostic pop -void _seqassert(const char *expr_str, const char *file, int line, - const std::string &msg); namespace codon { + +void assertionFailure(const char *expr_str, const char *file, int line, + const std::string &msg); + +struct Logger { + static constexpr int FLAG_TIME = (1 << 0); + static constexpr int FLAG_REALIZE = (1 << 1); + static constexpr int FLAG_TYPECHECK = (1 << 2); + static constexpr int FLAG_IR = (1 << 3); + static constexpr int FLAG_USER = (1 << 4); + + int flags; + int level; + std::ostream &out; + std::ostream &err; + std::ostream &log; + + Logger() : flags(0), level(0), out(std::cout), err(std::cerr), log(std::clog) {} + + void parse(const std::string &logs); +}; + +Logger &getLogger(); +void pushLogger(); +bool popLogger(); + +class Timer { +private: + using clock_type = std::chrono::high_resolution_clock; + std::string name; + std::chrono::time_point start, end; + bool logged; + +public: + void log() { + if (!logged) { + end = clock_type::now(); + auto elapsed = + std::chrono::duration_cast(end - start).count() / + 1000.0; + LOG_TIME("[T] {} = {:.1f}", name, elapsed); + logged = true; + } + } + + Timer(std::string name) : name(std::move(name)), start(), end(), logged(false) { + start = clock_type::now(); + } + + ~Timer() { log(); } +}; + struct SrcInfo { std::string file; int line; int col; int len; - int id; /// used to differentiate different + int id; /// used to differentiate different instances + SrcInfo(std::string file, int line, int col, int len) - : file(std::move(file)), line(line), col(col), len(len) { - static int _id(0); - id = _id++; + : file(std::move(file)), line(line), col(col), len(len), id(0) { + static int nextId = 0; + id = nextId++; }; - SrcInfo() : SrcInfo("", 0, 0, 0){}; - friend std::ostream &operator<<(std::ostream &out, const codon::SrcInfo &c) { - char buf[PATH_MAX + 1]; - strncpy(buf, c.file.c_str(), PATH_MAX); - auto f = basename(buf); - out << f << ":" << c.line << ":" << c.col; + + SrcInfo() : SrcInfo("", 0, 0, 0) {} + + friend std::ostream &operator<<(std::ostream &out, const codon::SrcInfo &src) { + out << std::filesystem::path(src.file).filename() << ":" << src.line << ":" + << src.col; return out; } + bool operator==(const SrcInfo &src) const { return id == src.id; } }; @@ -101,4 +151,5 @@ void compilationError(const std::string &msg, const std::string &file = "", void compilationWarning(const std::string &msg, const std::string &file = "", int line = 0, int col = 0, bool terminate = false); + } // namespace codon diff --git a/extra/jupyter/share/jupyter/kernels/codon/kernel.json.in b/extra/jupyter/share/jupyter/kernels/codon/kernel.json.in new file mode 100644 index 00000000..df194200 --- /dev/null +++ b/extra/jupyter/share/jupyter/kernels/codon/kernel.json.in @@ -0,0 +1,9 @@ +{ + "display_name": "Codon", + "argv": [ + "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/@EXECUTABLE_NAME@", + "jupyter", + "{connection_file}" + ], + "language": "python" +} diff --git a/extra/jupyter/src/codon.cpp b/extra/jupyter/src/codon.cpp new file mode 100644 index 00000000..fa960ca0 --- /dev/null +++ b/extra/jupyter/src/codon.cpp @@ -0,0 +1,102 @@ +#include "codon.h" + +#ifdef CODON_JUPYTER +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "codon/compiler/compiler.h" +#include "codon/compiler/error.h" +#include "codon/compiler/jit.h" +#include "codon/parser/common.h" +#include "codon/util/common.h" + +using std::move; +using std::string; + +namespace nl = nlohmann; +namespace codon { + +CodonJupyter::CodonJupyter(const std::string &argv0) : argv0(argv0) {} + +nl::json CodonJupyter::execute_request_impl(int execution_counter, const string &code, + bool silent, bool store_history, + nl::json user_expressions, + bool allow_stdin) { + auto result = jit->exec(code); + string failed; + llvm::handleAllErrors( + result.takeError(), + [&](const codon::error::ParserErrorInfo &e) { + std::vector backtrace; + for (auto &msg : e) + backtrace.push_back(msg.getMessage()); + string err = backtrace[0]; + backtrace.erase(backtrace.begin()); + failed = fmt::format("Compile error: {}\nBacktrace:\n{}", err, + ast::join(backtrace, " \n")); + }, + [&](const codon::error::RuntimeErrorInfo &e) { + auto backtrace = e.getBacktrace(); + failed = fmt::format("Runtime error: {}\nBacktrace:\n{}", e.getMessage(), + ast::join(backtrace, " \n")); + }); + if (failed.empty()) { + nl::json pub_data; + pub_data["text/plain"] = *result; + publish_execution_result(execution_counter, move(pub_data), nl::json::object()); + return nl::json{{"status", "ok"}, + {"payload", nl::json::array()}, + {"user_expressions", nl::json::object()}}; + } else { + publish_stream("stderr", failed); + return nl::json{{"status", "error"}}; + } +} + +void CodonJupyter::configure_impl() { + jit = std::make_unique(argv0, "jupyter"); + llvm::cantFail(jit->init()); +} + +nl::json CodonJupyter::complete_request_impl(const string &code, int cursor_pos) { + return nl::json{{"status", "ok"}}; +} + +nl::json CodonJupyter::inspect_request_impl(const string &code, int cursor_pos, + int detail_level) { + return nl::json{{"status", "ok"}}; +} + +nl::json CodonJupyter::is_complete_request_impl(const string &code) { + return nl::json{{"status", "complete"}}; +} + +nl::json CodonJupyter::kernel_info_request_impl() { + return xeus::create_info_reply("1.0", "codon_kernel", "0.1.0", "python", "3.7", + "text/x-python", ".seq", "python", "", "", + "Codon Kernel"); +} + +void CodonJupyter::shutdown_request_impl() {} + +int startJupyterKernel(const std::string &argv0, const std::string &configPath) { + xeus::xconfiguration config = xeus::load_configuration(configPath); + + auto context = xeus::make_context(); + auto interpreter = std::make_unique(argv0); + xeus::xkernel kernel(config, xeus::get_user_name(), move(context), move(interpreter), + xeus::make_xserver_zmq); + kernel.start(); + + return 0; +} + +} // namespace codon +#endif \ No newline at end of file diff --git a/extra/jupyter/src/codon.h b/extra/jupyter/src/codon.h new file mode 100644 index 00000000..ccf8b135 --- /dev/null +++ b/extra/jupyter/src/codon.h @@ -0,0 +1,40 @@ +#pragma once +#ifdef CODON_JUPYTER +#include +#include +#include + +using xeus::xinterpreter; +namespace nl = nlohmann; + +namespace codon { +class CodonJupyter : public xinterpreter { + std::unique_ptr jit; + std::string argv0; + +public: + CodonJupyter(const std::string &argv0); + +private: + void configure_impl() override; + + nl::json execute_request_impl(int execution_counter, const std::string &code, + bool silent, bool store_history, + nl::json user_expressions, bool allow_stdin) override; + + nl::json complete_request_impl(const std::string &code, int cursor_pos) override; + + nl::json inspect_request_impl(const std::string &code, int cursor_pos, + int detail_level) override; + + nl::json is_complete_request_impl(const std::string &code) override; + + nl::json kernel_info_request_impl() override; + + void shutdown_request_impl() override; +}; + +int startJupyterKernel(const std::string &argv0, const std::string &configPath); + +} // namespace codon +#endif \ No newline at end of file diff --git a/stdlib/internal/__init__.codon b/stdlib/internal/__init__.codon index 511d2984..be82cae3 100644 --- a/stdlib/internal/__init__.codon +++ b/stdlib/internal/__init__.codon @@ -25,6 +25,7 @@ from internal.types.collections.dict import * import internal.c_stubs as _C from internal.builtin import * +from internal.builtin import _jit_display from internal.box import Box from internal.str import * diff --git a/stdlib/internal/builtin.codon b/stdlib/internal/builtin.codon index e4d4122b..0822b797 100644 --- a/stdlib/internal/builtin.codon +++ b/stdlib/internal/builtin.codon @@ -329,3 +329,14 @@ class int: raise ValueError("invalid literal for int() with base " + str(base) + ": " + s) return result + + +def _jit_display(x, s: Static[str]): + if hasattr(x, "__repr_pretty__") and s == "jupyter": + return x.__repr_pretty__() + elif hasattr(x, "__repr__"): + return x.__repr__() + elif hasattr(x, "__str__"): + return x.__str__() + else: + return '' \ No newline at end of file diff --git a/stdlib/internal/dlopen.codon b/stdlib/internal/dlopen.codon index 4af004f3..576b79df 100644 --- a/stdlib/internal/dlopen.codon +++ b/stdlib/internal/dlopen.codon @@ -9,6 +9,7 @@ from C import dlsym(cobj, cobj) -> cobj as c_dlsym from C import dlclose(cobj) -> i32 as c_dlclose RTLD_NOW = 2 RTLD_GLOBAL = 8 if seq_is_macos() else 256 +RTLD_LOCAL = 0 if seq_is_macos() else 256 def dlext(): if seq_is_macos(): @@ -26,8 +27,12 @@ def dlopen(name: str, flag: int = RTLD_NOW | RTLD_GLOBAL) -> cobj: raise CError(dlerror()) return h -def dlsym[Fn](lib: str, name: str) -> Fn: - h = dlopen(lib) +def dlsym[Fn](lib, name: str) -> Fn: + h = cobj() + if isinstance(lib, str): + h = dlopen(lib) + else: + h = lib fn = c_dlsym(h, name.c_str()) if fn == cobj(): raise CError(dlerror()) diff --git a/stdlib/internal/python.codon b/stdlib/internal/python.codon index dc941207..c5614649 100644 --- a/stdlib/internal/python.codon +++ b/stdlib/internal/python.codon @@ -1,5 +1,5 @@ import os -from internal.dlopen import dlext +from internal.dlopen import * PyUnicode_AsEncodedString = Function[[cobj, cobj, cobj], cobj](cobj()) PyBytes_AsString = Function[[cobj], cobj](cobj()) @@ -45,75 +45,76 @@ def init(): return LD = os.getenv('CODON_PYTHON', default='libpython.' + dlext()) + hnd = dlopen(LD, RTLD_LOCAL | RTLD_NOW) global PyUnicode_AsEncodedString - PyUnicode_AsEncodedString = _dlsym(LD, "PyUnicode_AsEncodedString") + PyUnicode_AsEncodedString = dlsym(hnd, "PyUnicode_AsEncodedString") global PyBytes_AsString - PyBytes_AsString = _dlsym(LD, "PyBytes_AsString") + PyBytes_AsString = dlsym(hnd, "PyBytes_AsString") global PyErr_Fetch - PyErr_Fetch = _dlsym(LD, "PyErr_Fetch") + PyErr_Fetch = dlsym(hnd, "PyErr_Fetch") global PyObject_GetAttrString - PyObject_GetAttrString = _dlsym(LD, "PyObject_GetAttrString") + PyObject_GetAttrString = dlsym(hnd, "PyObject_GetAttrString") global PyObject_GetAttr - PyObject_GetAttr = _dlsym(LD, "PyObject_GetAttr") + PyObject_GetAttr = dlsym(hnd, "PyObject_GetAttr") global PyObject_Str - PyObject_Str = _dlsym(LD, "PyObject_Str") + PyObject_Str = dlsym(hnd, "PyObject_Str") global PyRun_SimpleString - PyRun_SimpleString = _dlsym(LD, "PyRun_SimpleString") + PyRun_SimpleString = dlsym(hnd, "PyRun_SimpleString") global Py_IncRef - Py_IncRef = _dlsym(LD, "Py_IncRef") + Py_IncRef = dlsym(hnd, "Py_IncRef") global Py_DecRef - Py_DecRef = _dlsym(LD, "Py_DecRef") + Py_DecRef = dlsym(hnd, "Py_DecRef") global PyObject_Call - PyObject_Call = _dlsym(LD, "PyObject_Call") + PyObject_Call = dlsym(hnd, "PyObject_Call") global PyObject_SetAttrString - PyObject_SetAttrString = _dlsym(LD, "PyObject_SetAttrString") + PyObject_SetAttrString = dlsym(hnd, "PyObject_SetAttrString") global PyObject_Length - PyObject_Length = _dlsym(LD, "PyObject_Length") + PyObject_Length = dlsym(hnd, "PyObject_Length") global Py_Initialize - Py_Initialize = _dlsym(LD, "Py_Initialize") + Py_Initialize = dlsym(hnd, "Py_Initialize") global PyImport_ImportModule - PyImport_ImportModule = _dlsym(LD, "PyImport_ImportModule") + PyImport_ImportModule = dlsym(hnd, "PyImport_ImportModule") global PyLong_FromLong - PyLong_FromLong = _dlsym(LD, "PyLong_FromLong") + PyLong_FromLong = dlsym(hnd, "PyLong_FromLong") global PyLong_AsLong - PyLong_AsLong = _dlsym(LD, "PyLong_AsLong") + PyLong_AsLong = dlsym(hnd, "PyLong_AsLong") global PyFloat_FromDouble - PyFloat_FromDouble = _dlsym(LD, "PyFloat_FromDouble") + PyFloat_FromDouble = dlsym(hnd, "PyFloat_FromDouble") global PyFloat_AsDouble - PyFloat_AsDouble = _dlsym(LD, "PyFloat_AsDouble") + PyFloat_AsDouble = dlsym(hnd, "PyFloat_AsDouble") global PyBool_FromLong - PyBool_FromLong = _dlsym(LD, "PyBool_FromLong") + PyBool_FromLong = dlsym(hnd, "PyBool_FromLong") global PyObject_IsTrue - PyObject_IsTrue = _dlsym(LD, "PyObject_IsTrue") + PyObject_IsTrue = dlsym(hnd, "PyObject_IsTrue") global PyUnicode_DecodeFSDefaultAndSize - PyUnicode_DecodeFSDefaultAndSize = _dlsym(LD, "PyUnicode_DecodeFSDefaultAndSize") + PyUnicode_DecodeFSDefaultAndSize = dlsym(hnd, "PyUnicode_DecodeFSDefaultAndSize") global PyTuple_New - PyTuple_New = _dlsym(LD, "PyTuple_New") + PyTuple_New = dlsym(hnd, "PyTuple_New") global PyTuple_SetItem - PyTuple_SetItem = _dlsym(LD, "PyTuple_SetItem") + PyTuple_SetItem = dlsym(hnd, "PyTuple_SetItem") global PyTuple_GetItem - PyTuple_GetItem = _dlsym(LD, "PyTuple_GetItem") + PyTuple_GetItem = dlsym(hnd, "PyTuple_GetItem") global PyList_New - PyList_New = _dlsym(LD, "PyList_New") + PyList_New = dlsym(hnd, "PyList_New") global PyList_SetItem - PyList_SetItem = _dlsym(LD, "PyList_SetItem") + PyList_SetItem = dlsym(hnd, "PyList_SetItem") global PyList_GetItem - PyList_GetItem = _dlsym(LD, "PyList_GetItem") + PyList_GetItem = dlsym(hnd, "PyList_GetItem") global PySet_New - PySet_New = _dlsym(LD, "PySet_New") + PySet_New = dlsym(hnd, "PySet_New") global PySet_Add - PySet_Add = _dlsym(LD, "PySet_Add") + PySet_Add = dlsym(hnd, "PySet_Add") global PyDict_New - PyDict_New = _dlsym(LD, "PyDict_New") + PyDict_New = dlsym(hnd, "PyDict_New") global PyDict_SetItem - PyDict_SetItem = _dlsym(LD, "PyDict_SetItem") + PyDict_SetItem = dlsym(hnd, "PyDict_SetItem") global PyDict_Next - PyDict_Next = _dlsym(LD, "PyDict_Next") + PyDict_Next = dlsym(hnd, "PyDict_Next") global PyObject_GetIter - PyObject_GetIter = _dlsym(LD, "PyObject_GetIter") + PyObject_GetIter = dlsym(hnd, "PyObject_GetIter") global PyIter_Next - PyIter_Next = _dlsym(LD, "PyIter_Next") + PyIter_Next = dlsym(hnd, "PyIter_Next") Py_Initialize() _PY_INITIALIZED = True diff --git a/test/main.cpp b/test/main.cpp index d757503c..12d484d3 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -12,15 +13,14 @@ #include #include +#include "codon/compiler/compiler.h" +#include "codon/compiler/error.h" #include "codon/parser/common.h" -#include "codon/parser/parser.h" -#include "codon/sir/llvm/llvisitor.h" -#include "codon/sir/transform/manager.h" -#include "codon/sir/transform/pass.h" #include "codon/sir/util/inlining.h" #include "codon/sir/util/irtools.h" #include "codon/sir/util/outlining.h" #include "codon/util/common.h" + #include "gtest/gtest.h" using namespace codon; @@ -178,22 +178,33 @@ public: close(out_pipe[1]); auto file = getFilename(get<0>(GetParam())); + bool debug = get<1>(GetParam()); auto code = get<3>(GetParam()); auto startLine = get<4>(GetParam()); - auto *module = parse(argv0, file, code, !code.empty(), - /* isTest */ 1 + get<5>(GetParam()), startLine); - if (!module) - exit(EXIT_FAILURE); + int testFlags = 1 + get<5>(GetParam()); - ir::transform::PassManager pm; - pm.registerPass(std::make_unique()); - pm.registerPass(std::make_unique()); - pm.run(module); + auto compiler = std::make_unique( + argv0, debug, /*disabledPasses=*/std::vector{}, /*isTest=*/true); + compiler->getLLVMVisitor()->setStandalone( + true); // make sure we abort() on runtime error + llvm::handleAllErrors(code.empty() + ? compiler->parseFile(file, testFlags) + : compiler->parseCode(file, code, startLine, testFlags), + [](const error::ParserErrorInfo &e) { + for (auto &msg : e) { + getLogger().level = 0; + printf("%s\n", msg.getMessage().c_str()); + } + fflush(stdout); + exit(EXIT_FAILURE); + }); - ir::LLVMVisitor visitor(/*debug=*/get<1>(GetParam())); - visitor.visit(module); - visitor.run({file}); + auto *pm = compiler->getPassManager(); + pm->registerPass(std::make_unique()); + pm->registerPass(std::make_unique()); + llvm::cantFail(compiler->compile()); + compiler->getLLVMVisitor()->run({file}); fflush(stdout); exit(EXIT_SUCCESS); } else { diff --git a/test/sir/func.cpp b/test/sir/func.cpp index dfa57923..e3615633 100644 --- a/test/sir/func.cpp +++ b/test/sir/func.cpp @@ -32,8 +32,8 @@ TEST_F(SIRCoreTest, FuncRealizationAndVarInsertionEraseAndIterators) { TEST_F(SIRCoreTest, BodiedFuncQueryAndReplace) { auto *fn = module->Nr(); fn->realize(module->unsafeGetDummyFuncType(), {}); - fn->setBuiltin(); - ASSERT_TRUE(fn->isBuiltin()); + fn->setJIT(); + ASSERT_TRUE(fn->isJIT()); auto *body = fn->getBody(); ASSERT_FALSE(body); @@ -63,7 +63,7 @@ TEST_F(SIRCoreTest, BodiedFuncCloning) { auto *fn = module->Nr("fn"); fn->realize(module->unsafeGetDummyFuncType(), {}); - fn->setBuiltin(); + fn->setJIT(); fn->setBody(module->Nr()); ASSERT_TRUE(util::match(fn, cv->clone(fn))); } diff --git a/test/sir/util/matching.cpp b/test/sir/util/matching.cpp index 38257f5f..00aef9ba 100644 --- a/test/sir/util/matching.cpp +++ b/test/sir/util/matching.cpp @@ -25,8 +25,8 @@ TEST_F(SIRCoreTest, MatchingEquivalentFunc) { auto *second = module->Nr(); second->realize(module->unsafeGetDummyFuncType(), {}); - first->setBuiltin(); - second->setBuiltin(); + first->setJIT(); + second->setJIT(); ASSERT_TRUE(util::match(first, second)); } @@ -58,7 +58,7 @@ TEST_F(SIRCoreTest, MatchingNonEquivalentFunc) { auto *second = module->Nr(); second->realize(module->unsafeGetDummyFuncType(), {}); - first->setBuiltin(); + first->setJIT(); ASSERT_FALSE(util::match(first, second)); } diff --git a/test/types.cpp b/test/types.cpp index 4c7d4b2e..067fa59a 100644 --- a/test/types.cpp +++ b/test/types.cpp @@ -13,7 +13,6 @@ #include #include "codon/parser/common.h" -#include "codon/parser/parser.h" #include "codon/util/common.h" #include "gtest/gtest.h"