From db23ecaaeb5f1338bab6e993b2710afa6f687b5b Mon Sep 17 00:00:00 2001 From: Harrand Date: Fri, 17 May 2024 03:47:04 +0100 Subject: [PATCH] [cpp] codegen intro. now ready for actual codegen logic --- cpp/src/codegen.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++- cpp/src/codegen.hpp | 8 +++- cpp/src/error.hpp | 4 +- cpp/src/psyc_main.cpp | 8 +++- 4 files changed, 100 insertions(+), 5 deletions(-) diff --git a/cpp/src/codegen.cpp b/cpp/src/codegen.cpp index 4ec9a48..72fac43 100644 --- a/cpp/src/codegen.cpp +++ b/cpp/src/codegen.cpp @@ -42,12 +42,93 @@ namespace code diag::error(error_code::nyi, "writing to object file is not yet implemented. i was told to write to \"{}\"", object_filename); } - output generate(const semal::output& input, std::string module_name) + // global state: + static std::unique_ptr ctx = nullptr; + static std::unique_ptr program = nullptr; + static std::unique_ptr> builder = nullptr; + // global variables are RAII objects meaning we will have to deal with their lifetimes. to do that, we're just chucking them in there, and spreading the underlying ptr all around the place (as we're allocing we have pointer stability so its safe). + static std::vector> global_variable_storage = {}; + + void static_initialise() + { + ctx = std::make_unique(); + } + + void static_terminate() + { + diag::assert_that(program == nullptr, error_code::ice, "an LLVM Module was not destroyed prior to static terminate"); + ctx = nullptr; + } + + void cleanup() + { + // note: tearing down all this state is actually ridiculously error prone. + // cant unlink globals until the program stops referencing them. + // globals must all be destroyed before program itself dies. + // so: + // 1.) program stops referencing everything + if(program != nullptr) + { + program->dropAllReferences(); + } + // 2.) unlink globals from parent (but doesnt erase them) + for(auto& glob_ptr : global_variable_storage) + { + glob_ptr->removeFromParent(); + } + // 3.) kill globals and then program etc... + + global_variable_storage.clear(); + builder = nullptr; + program = nullptr; + } + + struct data { - return + const ast& tree; + const ast::node& node; + const ast::path_t& path; + const semal::output& semantic; + + void error(std::string msg) const + { + diag::error(error_code::codegen, "at: {}: {}", node.meta.to_string(), msg); + } + + void internal_error(std::string msg) const + { + diag::error(error_code::ice, "at: {}: {}", node.meta.to_string(), msg); + } + + void warning(std::string msg) const + { + diag::warning("at: {}: {}", node.meta.to_string(), msg); + } + + void assert_that(bool expr, std::string msg) const + { + if(!expr) + { + this->error(msg); + } + } + }; + + output generate(const ast& tree, const semal::output& input, std::string module_name) + { + diag::assert_that(program == nullptr && builder == nullptr, error_code::ice, "previous codegen state has not been erased and you want me to move onto codegening another file..."); + output ret { .codegen_handle = nullptr, .module_name = module_name }; + program = std::make_unique(module_name, *ctx); + builder = std::make_unique>(*ctx); + + // todo: codegen logic goes here. + + llvm::verifyModule(*program); + ret.codegen_handle = program.get(); + return ret; } } \ No newline at end of file diff --git a/cpp/src/codegen.hpp b/cpp/src/codegen.hpp index e4821ae..8b04d8f 100644 --- a/cpp/src/codegen.hpp +++ b/cpp/src/codegen.hpp @@ -13,7 +13,13 @@ namespace code void write_to_object_file(std::filesystem::path output_dir); }; - output generate(const semal::output& input, std::string module_name = ""); + // initialise and terminate *once*, at the beginning and end of the compiler program respectively. + void static_initialise(); + void static_terminate(); + + void cleanup(); + // perform code generation. `codegen_handle` member of the returned output remains valid until you invoke `code::cleanup()`. ideally, you generate, then dump-ir/write-object, cleanup and then codegen another file. + output generate(const ast& tree, const semal::output& input, std::string module_name = ""); struct state { diff --git a/cpp/src/error.hpp b/cpp/src/error.hpp index 6b39675..231c22e 100644 --- a/cpp/src/error.hpp +++ b/cpp/src/error.hpp @@ -11,6 +11,7 @@ enum class error_code parse, type, semal, + codegen, _count }; @@ -22,7 +23,8 @@ constexpr std::array error_names "lex", "parse", "type", - "semantic analysis" + "semantic analysis", + "code generation" }; #endif // PSYC_ERROR_HPP \ No newline at end of file diff --git a/cpp/src/psyc_main.cpp b/cpp/src/psyc_main.cpp index f086ef1..7d3c517 100644 --- a/cpp/src/psyc_main.cpp +++ b/cpp/src/psyc_main.cpp @@ -48,6 +48,8 @@ int main(int argc, char** argv) config::compiler_args args = parse_args(cli_args); timers t; + code::static_initialise(); + // lex timer::start(); lex::state lex; @@ -93,12 +95,14 @@ int main(int argc, char** argv) code::state codegen; for(const auto& [input_file, semantic_output] : semal.analysed_input_files) { - codegen.codegend_input_files[input_file] = code::generate(semantic_output, input_file.stem().string()); + codegen.codegend_input_files[input_file] = code::generate(parse.parsed_input_files[input_file], semantic_output, input_file.stem().string()); if(args.should_dump_ir) { std::cout << "==========================\n"; std::cout << "ir for " << input_file << ":\n"; std::cout << codegen.codegend_input_files[input_file].dump_ir(); + code::cleanup(); + codegen.codegend_input_files[input_file].codegen_handle = nullptr; std::cout << "\n==========================\n\n"; } } @@ -107,6 +111,8 @@ int main(int argc, char** argv) // link t.print(); + + code::static_terminate(); return 0; }