First common arch-independent IR prototype (no structs or arrays yet)
This commit is contained in:
parent
1a0dd2a48f
commit
25672d3f05
12 changed files with 982 additions and 3 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#include <pslang/ast/statement.hpp>
|
||||
#include <pslang/ast/preprocess.hpp>
|
||||
#include <pslang/ast/print.hpp>
|
||||
#include <pslang/ir/compiler.hpp>
|
||||
#include <pslang/ir/print.hpp>
|
||||
#include <pslang/jit/jit.hpp>
|
||||
#include <pslang/jit/executable.hpp>
|
||||
#include <pslang/jit/foreign.hpp>
|
||||
|
|
@ -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<std::string> 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
|
||||
|
|
|
|||
7
examples/ir_test.psl
Normal file
7
examples/ir_test.psl
Normal file
|
|
@ -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)
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
6
libs/ir/CMakeLists.txt
Normal file
6
libs/ir/CMakeLists.txt
Normal file
|
|
@ -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)
|
||||
30
libs/ir/include/pslang/ir/compiler.hpp
Normal file
30
libs/ir/include/pslang/ir/compiler.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/ast/statement_fwd.hpp>
|
||||
#include <pslang/ir/node.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace pslang::ast
|
||||
{
|
||||
|
||||
struct function_definition;
|
||||
|
||||
}
|
||||
|
||||
namespace pslang::ir
|
||||
{
|
||||
|
||||
struct module_context
|
||||
{
|
||||
node_list nodes;
|
||||
|
||||
std::unordered_map<node const *, std::string> labels;
|
||||
|
||||
std::unordered_map<ast::function_definition const *, node_ref> symbols;
|
||||
node_ref entry_point;
|
||||
};
|
||||
|
||||
void compile(module_context & context, ast::statement_list_ptr const & statements);
|
||||
|
||||
}
|
||||
119
libs/ir/include/pslang/ir/node.hpp
Normal file
119
libs/ir/include/pslang/ir/node.hpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/ir/node_fwd.hpp>
|
||||
#include <pslang/ast/literal.hpp>
|
||||
#include <pslang/ast/operation.hpp>
|
||||
#include <pslang/types/type.hpp>
|
||||
|
||||
#include <variant>
|
||||
|
||||
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<node_ref> arguments;
|
||||
};
|
||||
|
||||
// Call to a pointer stored in a node
|
||||
struct call_pointer
|
||||
{
|
||||
node_ref pointer;
|
||||
std::vector<node_ref> arguments;
|
||||
};
|
||||
|
||||
struct return_value
|
||||
{
|
||||
std::optional<node_ref> 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;
|
||||
};
|
||||
|
||||
}
|
||||
12
libs/ir/include/pslang/ir/node_fwd.hpp
Normal file
12
libs/ir/include/pslang/ir/node_fwd.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace pslang::ir
|
||||
{
|
||||
|
||||
struct node;
|
||||
using node_list = std::list<node>;
|
||||
using node_ref = node_list::iterator;
|
||||
|
||||
}
|
||||
15
libs/ir/include/pslang/ir/print.hpp
Normal file
15
libs/ir/include/pslang/ir/print.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/ir/node_fwd.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace pslang::ir
|
||||
{
|
||||
|
||||
struct module_context;
|
||||
|
||||
void print(std::ostream & out, node_list const & nodes);
|
||||
void print(std::ostream & out, module_context const & context);
|
||||
|
||||
}
|
||||
462
libs/ir/source/compiler.cpp
Normal file
462
libs/ir/source/compiler.cpp
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
#include <pslang/ir/compiler.hpp>
|
||||
#include <pslang/ir/node.hpp>
|
||||
#include <pslang/ast/statement_visitor.hpp>
|
||||
#include <pslang/ast/expression_visitor.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace pslang::ir
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
struct local_context
|
||||
{
|
||||
std::unordered_map<ast::function_definition const *, node_ref> functions;
|
||||
|
||||
struct scope
|
||||
{
|
||||
std::string label_prefix;
|
||||
|
||||
std::unordered_map<std::string, node_ref> variables;
|
||||
std::unordered_set<std::string> foreign_functions;
|
||||
std::unordered_map<std::string, ast::function_definition const *> functions;
|
||||
std::unordered_map<std::string, ast::struct_definition const *> structs;
|
||||
};
|
||||
|
||||
std::vector<scope> 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_data> resolve_address;
|
||||
std::vector<resolve_call_data> 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<populate_symbols_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<compile_function_visitor>
|
||||
, ast::const_expression_visitor<compile_function_visitor>
|
||||
{
|
||||
using const_statement_visitor::apply;
|
||||
using const_expression_visitor::apply;
|
||||
|
||||
module_context & mcontext;
|
||||
local_context & lcontext;
|
||||
|
||||
template <typename Node>
|
||||
node_ref apply(Node const & node)
|
||||
{
|
||||
throw std::runtime_error(std::string("IR compile visitor not implemented for ") + typeid(Node).name());
|
||||
}
|
||||
|
||||
// Expressions
|
||||
|
||||
template <typename T>
|
||||
node_ref apply(ast::primitive_literal_base<T> 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_if_zero>(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_if_zero>(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<node_ref> arguments;
|
||||
|
||||
for (auto const & argument : node.arguments)
|
||||
arguments.push_back(apply(*argument));
|
||||
|
||||
if (auto identifier = std::get_if<ast::identifier>(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<node_ref> jumps_to_end;
|
||||
for (auto const & block : node.blocks)
|
||||
{
|
||||
std::optional<node_ref> 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_if_zero>((*jump_to_next)->instruction).target = last();
|
||||
}
|
||||
|
||||
auto end = last();
|
||||
for (auto const & jump_to_end : jumps_to_end)
|
||||
std::get<jump>(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<jump_if_zero>(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::type>(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::type>(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<compile_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::statement>(ast::function_definition{
|
||||
{
|
||||
{},
|
||||
{},
|
||||
std::make_shared<ast::type>(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<address>(resolve.address->instruction).target = lcontext.functions.at(resolve.target);
|
||||
|
||||
for (auto const & resolve : lcontext.resolve_call)
|
||||
std::get<call>(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<ast::function_definition>(root.get()));
|
||||
}
|
||||
|
||||
}
|
||||
302
libs/ir/source/print.cpp
Normal file
302
libs/ir/source/print.cpp
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
#include <pslang/ir/print.hpp>
|
||||
#include <pslang/ir/node.hpp>
|
||||
#include <pslang/ir/compiler.hpp>
|
||||
#include <pslang/types/print.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <iomanip>
|
||||
|
||||
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 const *, std::size_t> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue