diff --git a/CMakeLists.txt b/CMakeLists.txt index dddfb22..b21f801 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD 23) add_subdirectory(libs/types) add_subdirectory(libs/ast) add_subdirectory(libs/parser) +add_subdirectory(libs/ir) add_subdirectory(libs/jit) add_subdirectory(libs/interpreter) diff --git a/apps/interpreter/source/main.cpp b/apps/interpreter/source/main.cpp index 0f1d0ca..9aa285d 100644 --- a/apps/interpreter/source/main.cpp +++ b/apps/interpreter/source/main.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include #include @@ -77,6 +79,7 @@ int main(int argc, char ** argv) std::cout << " -t, --trace Trace each line of execution\n"; std::cout << " -d, --dump Dump all variables after processing all files\n"; std::cout << " -p, --print Print the AST after parsing each file\n"; + std::cout << " -i, --ir Print the IR after parsing each file\n"; std::cout << " -j, --jit Just-in-time compile & execute the code instead of interpreting\n"; return 0; } @@ -88,6 +91,7 @@ int main(int argc, char ** argv) bool dump = false; bool dump_ast = false; + bool dump_ir = false; bool jit = false; std::vector filenames; @@ -113,6 +117,12 @@ int main(int argc, char ** argv) continue; } + if (std::strcmp(argv[arg], "-i") == 0 || std::strcmp(argv[arg], "--ir") == 0) + { + dump_ir = true; + continue; + } + if (std::strcmp(argv[arg], "-j") == 0 || std::strcmp(argv[arg], "--jit") == 0) { jit = true; @@ -177,6 +187,19 @@ int main(int argc, char ** argv) std::cout << std::flush; } + // NB: IR isn't used in JIT or interpreter right now. + if (dump_ir) + { + for (std::size_t i = 0; i < filenames.size(); ++i) + { + std::cout << "Input file " << filenames[i] << " IR dump:\n\n"; + ir::module_context context; + ir::compile(context, parsed[i]); + ir::print(std::cout, context); + std::cout << "\n"; + } + } + if (jit) { jit::program_context pcontext diff --git a/examples/ir_test.psl b/examples/ir_test.psl new file mode 100644 index 0000000..95af5b4 --- /dev/null +++ b/examples/ir_test.psl @@ -0,0 +1,7 @@ +func test(x: f32) -> f32: + foreign func sin(x: f32) -> f32 + let pi = 3.1415926535 + return sin(pi * x) + +let myfunc = test +let x = myfunc(7.5) diff --git a/examples/short-circuit.psl b/examples/short-circuit.psl index cd2176f..e5f5b6c 100644 --- a/examples/short-circuit.psl +++ b/examples/short-circuit.psl @@ -4,6 +4,8 @@ func g() -> u16: func h() -> bool: return h() -func test() -> u16: - return 65535us || g() +func test_and() -> u16: + return 0us && g() +func test_or() -> u16: + return 65535us || g() diff --git a/libs/ir/CMakeLists.txt b/libs/ir/CMakeLists.txt new file mode 100644 index 0000000..15c0bc7 --- /dev/null +++ b/libs/ir/CMakeLists.txt @@ -0,0 +1,6 @@ +file(GLOB_RECURSE PSLANG_IR_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp") +file(GLOB_RECURSE PSLANG_IR_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") + +add_library(pslang-ir STATIC ${PSLANG_IR_HEADERS} ${PSLANG_IR_SOURCES}) +target_include_directories(pslang-ir PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(pslang-ir PUBLIC pslang-types pslang-ast) diff --git a/libs/ir/include/pslang/ir/compiler.hpp b/libs/ir/include/pslang/ir/compiler.hpp new file mode 100644 index 0000000..95bef71 --- /dev/null +++ b/libs/ir/include/pslang/ir/compiler.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#include + +namespace pslang::ast +{ + + struct function_definition; + +} + +namespace pslang::ir +{ + + struct module_context + { + node_list nodes; + + std::unordered_map labels; + + std::unordered_map symbols; + node_ref entry_point; + }; + + void compile(module_context & context, ast::statement_list_ptr const & statements); + +} \ No newline at end of file diff --git a/libs/ir/include/pslang/ir/node.hpp b/libs/ir/include/pslang/ir/node.hpp new file mode 100644 index 0000000..0ab7071 --- /dev/null +++ b/libs/ir/include/pslang/ir/node.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace pslang::ir +{ + + // Used primarily as jump target + struct nop + {}; + + struct literal + { + // NB: no unit values - there's no point in storing them anywhere + ast::literal value; + }; + + struct unary_operation + { + ast::unary_operation_type type; + node_ref arg1; + }; + + struct binary_operation + { + ast::binary_operation_type type; + node_ref arg1; + node_ref arg2; + }; + + struct cast_operation + { + node_ref arg1; + types::type_ptr target_type; + }; + + struct argument + { + std::size_t index; + }; + + struct address + { + node_ref target; + }; + + struct extern_symbol + { + std::string name; + }; + + struct assignment + { + node_ref lhs; + node_ref rhs; + }; + + struct jump + { + node_ref target; + }; + + struct jump_if_zero + { + // Condition must be a bool-valued node + node_ref condition; + node_ref target; + }; + + // Call to a specific node + struct call + { + node_ref target; + std::vector arguments; + }; + + // Call to a pointer stored in a node + struct call_pointer + { + node_ref pointer; + std::vector arguments; + }; + + struct return_value + { + std::optional value; + }; + + using instruction = std::variant< + nop, + literal, + unary_operation, + binary_operation, + cast_operation, + argument, + address, + extern_symbol, + assignment, + jump, + jump_if_zero, + call, + call_pointer, + return_value + >; + + struct node + { + instruction instruction; + + // Null for non-value instructions + types::type_ptr inferred_type = nullptr; + }; + +} \ No newline at end of file diff --git a/libs/ir/include/pslang/ir/node_fwd.hpp b/libs/ir/include/pslang/ir/node_fwd.hpp new file mode 100644 index 0000000..cfa9788 --- /dev/null +++ b/libs/ir/include/pslang/ir/node_fwd.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace pslang::ir +{ + + struct node; + using node_list = std::list; + using node_ref = node_list::iterator; + +} \ No newline at end of file diff --git a/libs/ir/include/pslang/ir/print.hpp b/libs/ir/include/pslang/ir/print.hpp new file mode 100644 index 0000000..ef7f085 --- /dev/null +++ b/libs/ir/include/pslang/ir/print.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include + +namespace pslang::ir +{ + + struct module_context; + + void print(std::ostream & out, node_list const & nodes); + void print(std::ostream & out, module_context const & context); + +} \ No newline at end of file diff --git a/libs/ir/source/compiler.cpp b/libs/ir/source/compiler.cpp new file mode 100644 index 0000000..0660438 --- /dev/null +++ b/libs/ir/source/compiler.cpp @@ -0,0 +1,462 @@ +#include +#include +#include +#include + +#include +#include + +namespace pslang::ir +{ + + namespace + { + struct local_context + { + std::unordered_map functions; + + struct scope + { + std::string label_prefix; + + std::unordered_map variables; + std::unordered_set foreign_functions; + std::unordered_map functions; + std::unordered_map structs; + }; + + std::vector scopes; + + struct resolve_address_data + { + node_ref address; + ast::function_definition const * target; + }; + + struct resolve_call_data + { + node_ref call; + ast::function_definition const * target; + }; + + std::vector resolve_address; + std::vector resolve_call; + }; + + // Iterate over a single scope (i.e. not visiting subscopes recursively) + // and add all defined functions & foreign functions to the current scope + struct populate_symbols_visitor + : ast::const_statement_visitor + { + local_context & lcontext; + + using const_statement_visitor::apply; + + void apply(ast::expression_ptr const &) {} + + void apply(ast::assignment const &) {} + + void apply(ast::variable_declaration const &) {} + + void apply(ast::if_block const &) {} + + void apply(ast::else_block const &) {} + + void apply(ast::else_if_block const &) {} + + void apply(ast::if_chain const &) {} + + void apply(ast::while_block const &) {} + + void apply(ast::function_definition const & node) + { + lcontext.scopes.back().functions[node.name] = &node; + } + + void apply(ast::foreign_function_declaration const & node) + { + lcontext.scopes.back().foreign_functions.insert(node.name); + } + + void apply(ast::return_statement const &) {} + + void apply(ast::field_definition const &) {} + + void apply(ast::struct_definition const & node) + { + lcontext.scopes.back().structs[node.name] = &node; + } + }; + + // Compile a single function and store the entry point node_ref + // in local_context + struct compile_function_visitor + : ast::const_statement_visitor + , ast::const_expression_visitor + { + using const_statement_visitor::apply; + using const_expression_visitor::apply; + + module_context & mcontext; + local_context & lcontext; + + template + node_ref apply(Node const & node) + { + throw std::runtime_error(std::string("IR compile visitor not implemented for ") + typeid(Node).name()); + } + + // Expressions + + template + node_ref apply(ast::primitive_literal_base const & node) + { + mcontext.nodes.emplace_back(literal{node}, ast::get_type(node)); + return last(); + } + + node_ref apply(ast::identifier const & node) + { + for (auto it = lcontext.scopes.rbegin(); it != lcontext.scopes.rend(); ++it) + { + if (auto jt = it->variables.find(node.name); jt != it->variables.end()) + return jt->second; + + if (auto jt = it->foreign_functions.find(node.name); jt != it->foreign_functions.end()) + { + mcontext.nodes.emplace_back(extern_symbol{node.name}, node.inferred_type); + return last(); + } + + if (auto jt = it->functions.find(node.name); jt != it->functions.end()) + { + mcontext.nodes.emplace_back(address{}, node.inferred_type); + lcontext.resolve_address.emplace_back(last(), jt->second); + return last(); + } + } + + throw std::runtime_error("Unknown identifier \"" + node.name + "\""); + } + + node_ref apply(ast::unary_operation const & node) + { + auto arg1 = apply(*node.arg1); + mcontext.nodes.emplace_back(unary_operation{node.type, arg1}, node.inferred_type); + return last(); + } + + node_ref apply(ast::binary_operation const & node) + { + auto arg1 = apply(*node.arg1); + + if (node.type == ast::binary_operation_type::logical_and) + { + mcontext.nodes.emplace_back(jump_if_zero{arg1}); + auto jump = last(); + auto arg2 = apply(*node.arg2); + mcontext.nodes.emplace_back(binary_operation{ast::binary_operation_type::binary_and, arg1, arg2}, node.inferred_type); + mcontext.nodes.emplace_back(assignment{arg1, last()}); + mcontext.nodes.emplace_back(nop{}); + std::get(jump->instruction).target = last(); + return arg1; + } + + if (node.type == ast::binary_operation_type::logical_or) + { + mcontext.nodes.emplace_back(unary_operation{ast::unary_operation_type::logical_not, arg1}, node.inferred_type); + mcontext.nodes.emplace_back(jump_if_zero{last()}); + auto jump = last(); + auto arg2 = apply(*node.arg2); + mcontext.nodes.emplace_back(binary_operation{ast::binary_operation_type::binary_or, arg1, arg2}, node.inferred_type); + mcontext.nodes.emplace_back(assignment{arg1, last()}); + mcontext.nodes.emplace_back(nop{}); + std::get(jump->instruction).target = last(); + return arg1; + } + + auto arg2 = apply(*node.arg2); + mcontext.nodes.emplace_back(binary_operation{node.type, arg1, arg2}, node.inferred_type); + return last(); + } + + node_ref apply(ast::cast_operation const & node) + { + auto arg = apply(*node.expression); + mcontext.nodes.emplace_back(cast_operation{arg, node.inferred_type}, node.inferred_type); + return last(); + } + + node_ref apply(ast::function_call const & node) + { + if (node.function) + { + std::vector arguments; + + for (auto const & argument : node.arguments) + arguments.push_back(apply(*argument)); + + if (auto identifier = std::get_if(node.function.get())) + { + for (auto it = lcontext.scopes.rbegin(); it != lcontext.scopes.rend(); ++it) + { + if (auto jt = it->functions.find(identifier->name); jt != it->functions.end()) + { + mcontext.nodes.emplace_back(call{{}, std::move(arguments)}, node.inferred_type); + lcontext.resolve_call.emplace_back(last(), jt->second); + return last(); + } + } + } + + auto function = apply(*node.function); + mcontext.nodes.emplace_back(call_pointer{function, std::move(arguments)}, node.inferred_type); + return last(); + } + else // if (node.type) + { + throw std::runtime_error("IR compile visitor not implemented for type constructors"); + } + } + + // TODO: array, array_access, field_access + + // Statements + + node_ref apply(ast::expression_ptr const & node) + { + apply(*node); + return last(); + } + + node_ref apply(ast::assignment const & node) + { + auto rhs = apply(*node.rhs); + auto lhs = apply(*node.lhs); + mcontext.nodes.emplace_back(assignment{lhs, rhs}, ast::get_type(*node.rhs)); + return last(); + } + + node_ref apply(ast::variable_declaration const & node) + { + apply(*node.initializer); + lcontext.scopes.back().variables[node.name] = last(); + return last(); + } + + node_ref apply(ast::if_chain const & node) + { + std::vector jumps_to_end; + for (auto const & block : node.blocks) + { + std::optional jump_to_next; + + if (block.condition) + { + auto condition = apply(*block.condition); + mcontext.nodes.emplace_back(jump_if_zero{condition, {}}); + jump_to_next = last(); + } + + lcontext.scopes.emplace_back(); + apply(*block.statements); + lcontext.scopes.pop_back(); + + mcontext.nodes.emplace_back(jump{}); + jumps_to_end.push_back(last()); + + mcontext.nodes.emplace_back(nop{}); + if (jump_to_next) + std::get((*jump_to_next)->instruction).target = last(); + } + + auto end = last(); + for (auto const & jump_to_end : jumps_to_end) + std::get(jump_to_end->instruction).target = end; + + return end; + } + + node_ref apply(ast::while_block const & node) + { + auto before = last(); + auto condition = apply(*node.condition); + mcontext.nodes.emplace_back(jump_if_zero{condition, {}}); + auto jump1 = last(); + + lcontext.scopes.emplace_back(); + apply(*node.statements); + lcontext.scopes.pop_back(); + + mcontext.nodes.emplace_back(jump{std::next(before)}); + mcontext.nodes.emplace_back(nop{}); + std::get(jump1->instruction).target = last(); + return last(); + } + + node_ref apply(ast::function_definition const & node) + { + return last(); + } + + node_ref apply(ast::foreign_function_declaration const & node) + { + return last(); + } + + node_ref apply(ast::return_statement const & node) + { + if (node.value) + { + auto value = apply(*node.value); + mcontext.nodes.emplace_back(return_value{value}, value->inferred_type); + } + else + mcontext.nodes.emplace_back(return_value{}, std::make_shared(types::unit_type{})); + return last(); + } + + // TODO: struct_definition, field_definition + + // Statement list + + void apply(ast::statement_list const & list) + { + populate_symbols_visitor{{}, lcontext}.apply(list); + for (auto const & statement : list.statements) + apply(*statement); + } + + void do_apply(ast::function_definition const & node) + { + auto before = last(); + + lcontext.scopes.emplace_back(); + for (std::size_t i = 0; i < node.arguments.size(); ++i) + { + mcontext.nodes.emplace_back(argument{i}, ast::get_type(*node.arguments[i].type)); + lcontext.scopes.back().variables[node.arguments[i].name] = last(); + } + apply(*node.statements); + + if (types::equal(*ast::get_type(*node.return_type), types::unit_type{})) + mcontext.nodes.emplace_back(return_value{}, std::make_shared(types::unit_type{})); + + lcontext.scopes.pop_back(); + + auto entry = std::next(before); + lcontext.functions[&node] = entry; + mcontext.labels[&(*entry)] = lcontext.scopes.back().label_prefix + node.name; + } + + private: + node_ref last() + { + return std::prev(mcontext.nodes.end()); + } + }; + + // Main compilation visitor + struct compile_visitor + : ast::const_statement_visitor + { + module_context & mcontext; + local_context & lcontext; + + using const_statement_visitor::apply; + + void apply(ast::expression_ptr const &) {} + + void apply(ast::assignment const &) {} + + void apply(ast::variable_declaration const &) {} + + void apply(ast::if_block const &) {} + + void apply(ast::else_block const &) {} + + void apply(ast::else_if_block const &) {} + + void apply(ast::if_chain const & node) + { + for (auto const & block : node.blocks) + { + lcontext.scopes.emplace_back(); + apply(*block.statements); + lcontext.scopes.pop_back(); + } + } + + void apply(ast::while_block const & node) + { + lcontext.scopes.emplace_back(); + apply(*node.statements); + lcontext.scopes.pop_back(); + } + + void apply(ast::function_definition const & node) + { + compile_function_visitor{{}, {}, mcontext, lcontext}.do_apply(node); + std::string label_prefix; + if (!lcontext.scopes.empty()) + label_prefix = lcontext.scopes.back().label_prefix + node.name + "."; + lcontext.scopes.emplace_back(std::move(label_prefix)); + apply(*node.statements); + + // Don't pop_back entry point scope + if (lcontext.scopes.size() > 1) + lcontext.scopes.pop_back(); + } + + void apply(ast::foreign_function_declaration const &) {} + + void apply(ast::return_statement const &) {} + + void apply(ast::field_definition const &) {} + + void apply(ast::struct_definition const &) {} + + void apply(ast::statement_list const & list) + { + populate_symbols_visitor{{}, lcontext}.apply(list); + for (auto const & statement : list.statements) + apply(*statement); + } + }; + + } + + void compile(module_context & mcontext, ast::statement_list_ptr const & statements) + { + // Add a fake AST node for the entry point + auto root = std::make_shared(ast::function_definition{ + { + {}, + {}, + std::make_shared(types::unit_type{}), + {}, + }, + statements, + {}, + }); + + mcontext.nodes.emplace_back(nop{}); + auto extra_nop = std::prev(mcontext.nodes.end()); + local_context lcontext; + compile_visitor{{}, mcontext, lcontext}.apply(*root); + mcontext.labels[&(*std::next(extra_nop))] = "[entry point]"; + mcontext.nodes.erase(extra_nop); + + for (auto const & resolve : lcontext.resolve_address) + std::get
(resolve.address->instruction).target = lcontext.functions.at(resolve.target); + + for (auto const & resolve : lcontext.resolve_call) + std::get(resolve.call->instruction).target = lcontext.functions.at(resolve.target); + + for (auto const & symbol : lcontext.scopes.front().functions) + mcontext.symbols[symbol.second] = lcontext.functions.at(symbol.second); + + mcontext.entry_point = lcontext.functions.at(std::get_if(root.get())); + } + +} \ No newline at end of file diff --git a/libs/ir/source/print.cpp b/libs/ir/source/print.cpp new file mode 100644 index 0000000..c338303 --- /dev/null +++ b/libs/ir/source/print.cpp @@ -0,0 +1,302 @@ +#include +#include +#include +#include + +#include +#include + +namespace pslang::ir +{ + + namespace + { + + void print(std::ostream & out, ast::unary_operation_type type) + { + switch (type) + { + case ast::unary_operation_type::negation: + out << "neg"; + break; + case ast::unary_operation_type::logical_not: + out << "not"; + break; + } + } + + void print(std::ostream & out, ast::binary_operation_type type) + { + switch (type) + { + case ast::binary_operation_type::addition: + out << "add"; + break; + case ast::binary_operation_type::subtraction: + out << "sub"; + break; + case ast::binary_operation_type::multiplication: + out << "mul"; + break; + case ast::binary_operation_type::division: + out << "div"; + break; + case ast::binary_operation_type::remainder: + out << "rem"; + break; + case ast::binary_operation_type::binary_and: + out << "and"; + break; + case ast::binary_operation_type::logical_and: + out << "and sc"; + break; + case ast::binary_operation_type::binary_or: + out << "or"; + break; + case ast::binary_operation_type::logical_or: + out << "or sc"; + break; + case ast::binary_operation_type::logical_xor: + out << "xor"; + break; + case ast::binary_operation_type::equals: + out << "eq"; + break; + case ast::binary_operation_type::not_equals: + out << "neq"; + break; + case ast::binary_operation_type::less: + out << "lt"; + break; + case ast::binary_operation_type::greater: + out << "gt"; + break; + case ast::binary_operation_type::less_equals: + out << "leq"; + break; + case ast::binary_operation_type::greater_equals: + out << "geq"; + break; + } + } + + struct print_literal_visitor + { + std::ostream & out; + + void operator()(ast::bool_literal const & literal) + { + out << (literal.value ? "true" : "false"); + } + + void operator()(ast::i8_literal const & literal) + { + out << int(literal.value); + } + + void operator()(ast::i16_literal const & literal) + { + out << literal.value; + } + + void operator()(ast::i32_literal const & literal) + { + out << literal.value; + } + + void operator()(ast::i64_literal const & literal) + { + out << literal.value; + } + + void operator()(ast::u8_literal const & literal) + { + out << int(literal.value); + } + + void operator()(ast::u16_literal const & literal) + { + out << literal.value; + } + + void operator()(ast::u32_literal const & literal) + { + out << literal.value; + } + + void operator()(ast::u64_literal const & literal) + { + out << literal.value; + } + + void operator()(ast::f16_literal const & literal) + { + out << literal.value.repr; + } + + void operator()(ast::f32_literal const & literal) + { + out << literal.value; + } + + void operator()(ast::f64_literal const & literal) + { + out << literal.value; + } + }; + + struct print_visitor + { + std::ostream & out; + std::unordered_map node_index; + std::size_t current_index = 0; + std::size_t indent = 0; + + void fill_index(node_list const & nodes) + { + std::size_t index = 0; + for (auto const & node : nodes) + node_index[&node] = index++; + + if (!node_index.empty()) + { + std::size_t max_index = node_index.size() - 1; + while (max_index > 0) + { + ++indent; + max_index /= 10; + } + } + } + + std::size_t get_index(node_ref ref) + { + return node_index.at(&(*ref)); + } + + void prelude() + { + out << std::right << std::setfill(' ') << std::setw(indent); + } + + void operator()(nop const &) + { + out << "nop"; + } + + void operator()(literal const & instruction) + { + out << "lit "; + std::visit(print_literal_visitor{out}, instruction.value); + } + + void operator()(unary_operation const & instruction) + { + print(out, instruction.type); + out << " $" << get_index(instruction.arg1); + } + + void operator()(binary_operation const & instruction) + { + print(out, instruction.type); + out << " $" << get_index(instruction.arg1) << " $" << get_index(instruction.arg2); + } + + void operator()(cast_operation const & instruction) + { + out << "cast $" << get_index(instruction.arg1); + } + + void operator()(argument const & instruction) + { + out << "arg #" << instruction.index; + } + + void operator()(address const & instruction) + { + out << "addr $" << get_index(instruction.target); + } + + void operator()(extern_symbol const & instruction) + { + out << "extern \"" << instruction.name << "\""; + } + + void operator()(assignment const & instruction) + { + out << "assign $" << get_index(instruction.lhs) << " $" << get_index(instruction.rhs); + } + + void operator()(jump const & instruction) + { + out << "jump $" << get_index(instruction.target); + } + + void operator()(jump_if_zero const & instruction) + { + out << "jz $" << get_index(instruction.condition) << " $" << get_index(instruction.target); + } + + void operator()(call const & instruction) + { + out << "call $" << get_index(instruction.target); + for (auto const & arument : instruction.arguments) + out << " $" << get_index(arument); + } + + void operator()(call_pointer const & instruction) + { + out << "callp $" << get_index(instruction.pointer); + for (auto const & arument : instruction.arguments) + out << " $" << get_index(arument); + } + + void operator()(return_value const & instruction) + { + out << "ret "; + if (instruction.value) + out << "$" << get_index(*instruction.value); + } + }; + + void print_impl(std::ostream & out, node_list const & nodes, module_context const * context) + { + print_visitor visitor{out}; + visitor.fill_index(nodes); + + std::size_t index = 0; + for (auto const & node : nodes) + { + if (context && context->labels.contains(&node)) + { + if (index > 0) out << '\n'; + out << context->labels.at(&node) << ":\n"; + } + if (context) + out << " "; + visitor.prelude(); + out << index << ": "; + ++index; + std::visit(visitor, node.instruction); + if (node.inferred_type) + { + out << " : "; + types::print(out, *node.inferred_type); + } + out << '\n'; + } + } + + } + + void print(std::ostream & out, node_list const & nodes) + { + print_impl(out, nodes, nullptr); + } + + void print(std::ostream & out, module_context const & context) + { + print_impl(out, context.nodes, &context); + } + +} \ No newline at end of file diff --git a/libs/jit/CMakeLists.txt b/libs/jit/CMakeLists.txt index fe266bf..d2215eb 100644 --- a/libs/jit/CMakeLists.txt +++ b/libs/jit/CMakeLists.txt @@ -3,4 +3,4 @@ file(GLOB_RECURSE PSLANG_JIT_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") add_library(pslang-jit STATIC ${PSLANG_JIT_HEADERS} ${PSLANG_JIT_SOURCES}) target_include_directories(pslang-jit PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") -target_link_libraries(pslang-jit PUBLIC pslang-ast) +target_link_libraries(pslang-jit PUBLIC pslang-ast pslang-ir)