chore: flat out the file structure
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
#ifndef FURC_BACK_FURVM_HPP
|
||||
#define FURC_BACK_FURVM_HPP
|
||||
|
||||
#include "furc/middle/ir.hpp"
|
||||
#include "furvm/module.hpp"
|
||||
|
||||
namespace furc {
|
||||
|
||||
class furvm_generator final {
|
||||
public:
|
||||
static furvm::mod generate(const ir_module& mod);
|
||||
};
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_BACK_FURVM_HPP
|
||||
@@ -0,0 +1,346 @@
|
||||
#ifndef FURC_FRONT_AST_HPP
|
||||
#define FURC_FRONT_AST_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace furc {
|
||||
|
||||
struct comp_stmt_node;
|
||||
struct if_stmt_node;
|
||||
struct while_stmt_node;
|
||||
struct return_stmt_node;
|
||||
struct var_decl_node;
|
||||
struct func_decl_node;
|
||||
struct var_read_expr_node;
|
||||
struct func_call_expr_node;
|
||||
struct group_expr_node;
|
||||
struct binary_op_expr_node;
|
||||
struct unary_op_expr_node;
|
||||
struct if_expr_node;
|
||||
struct int_lit_node;
|
||||
struct char_lit_node;
|
||||
|
||||
struct ast_visitor {
|
||||
ast_visitor() = default;
|
||||
virtual ~ast_visitor() = default;
|
||||
|
||||
ast_visitor(ast_visitor&&) noexcept = default;
|
||||
ast_visitor& operator=(ast_visitor&&) noexcept = default;
|
||||
|
||||
ast_visitor(const ast_visitor&) = default;
|
||||
ast_visitor& operator=(const ast_visitor&) = default;
|
||||
|
||||
virtual void visit_comp_stmt_node(const comp_stmt_node& node) {}
|
||||
virtual void visit_if_stmt_node(const if_stmt_node& node) {}
|
||||
virtual void visit_while_stmt_node(const while_stmt_node& node) {}
|
||||
virtual void visit_return_stmt_node(const return_stmt_node& node) {}
|
||||
|
||||
virtual void visit_var_decl_node(const var_decl_node& node) {}
|
||||
virtual void visit_func_decl_node(const func_decl_node& node) {}
|
||||
|
||||
virtual void visit_var_read_expr_node(const var_read_expr_node& node) {}
|
||||
virtual void visit_func_call_expr_node(const func_call_expr_node& node) {}
|
||||
virtual void visit_group_expr_node(const group_expr_node& node) {}
|
||||
virtual void visit_binary_op_expr_node(const binary_op_expr_node& node) {}
|
||||
virtual void visit_unary_op_expr_node(const unary_op_expr_node& node) {}
|
||||
virtual void visit_if_expr_node(const if_expr_node& node) {}
|
||||
|
||||
virtual void visit_int_lit_node(const int_lit_node& node) {}
|
||||
virtual void visit_char_lit_node(const char_lit_node& node) {}
|
||||
};
|
||||
|
||||
struct ast_type {
|
||||
enum type_e {
|
||||
Void = 0,
|
||||
S8,
|
||||
U8,
|
||||
S16,
|
||||
U16,
|
||||
S32,
|
||||
U32,
|
||||
S64,
|
||||
U64,
|
||||
} type = Void;
|
||||
};
|
||||
|
||||
class ast_node {
|
||||
public:
|
||||
enum category_e {
|
||||
Statement,
|
||||
Declaration,
|
||||
Expression,
|
||||
Literal,
|
||||
};
|
||||
public:
|
||||
ast_node() = default;
|
||||
virtual ~ast_node() = default;
|
||||
|
||||
ast_node(ast_node&&) noexcept = default;
|
||||
ast_node& operator=(ast_node&&) noexcept = default;
|
||||
|
||||
ast_node(const ast_node&) = delete;
|
||||
ast_node& operator=(const ast_node&) = delete;
|
||||
public:
|
||||
virtual category_e category() const = 0;
|
||||
|
||||
virtual void accept(ast_visitor& visitor) const = 0;
|
||||
};
|
||||
|
||||
using ast_node_cat = ast_node::category_e;
|
||||
|
||||
class stmt_node : public ast_node {
|
||||
public:
|
||||
enum stmt_type_e {
|
||||
Declaration = 0,
|
||||
Expression,
|
||||
|
||||
Compound,
|
||||
If,
|
||||
While,
|
||||
Return,
|
||||
};
|
||||
public:
|
||||
category_e category() const override { return ast_node_cat::Statement; }
|
||||
|
||||
virtual stmt_type_e stmt_type() const = 0;
|
||||
};
|
||||
|
||||
struct comp_stmt_node final : public stmt_node {
|
||||
stmt_type_e stmt_type() const override { return Compound; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_comp_stmt_node(*this); }
|
||||
|
||||
std::vector<stmt_node*> stmts;
|
||||
};
|
||||
|
||||
class expr_node;
|
||||
|
||||
struct if_stmt_node final : public stmt_node {
|
||||
stmt_type_e stmt_type() const override { return If; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_if_stmt_node(*this); }
|
||||
|
||||
expr_node* cond = nullptr;
|
||||
stmt_node* thenBranch = nullptr;
|
||||
stmt_node* elseBranch = nullptr;
|
||||
};
|
||||
|
||||
struct while_stmt_node final : public stmt_node {
|
||||
stmt_type_e stmt_type() const override { return While; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_while_stmt_node(*this); }
|
||||
|
||||
expr_node* cond = nullptr;
|
||||
stmt_node* body = nullptr;
|
||||
};
|
||||
|
||||
struct return_stmt_node final : public stmt_node {
|
||||
stmt_type_e stmt_type() const override { return Return; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_return_stmt_node(*this); }
|
||||
|
||||
expr_node* value = nullptr;
|
||||
};
|
||||
|
||||
class decl_node : public stmt_node {
|
||||
public:
|
||||
enum decl_type_e {
|
||||
Variable,
|
||||
Function
|
||||
};
|
||||
public:
|
||||
category_e category() const override { return ast_node_cat::Declaration; }
|
||||
|
||||
stmt_type_e stmt_type() const override { return Declaration; }
|
||||
|
||||
virtual decl_type_e decl_type() const = 0;
|
||||
};
|
||||
|
||||
struct var_decl_node final : public decl_node {
|
||||
decl_type_e decl_type() const override { return Variable; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_var_decl_node(*this); }
|
||||
|
||||
std::string name;
|
||||
ast_type type;
|
||||
expr_node* init = nullptr;
|
||||
};
|
||||
|
||||
struct func_decl_node final : public decl_node {
|
||||
decl_type_e decl_type() const override { return Function; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_func_decl_node(*this); }
|
||||
|
||||
struct def_s {
|
||||
comp_stmt_node body;
|
||||
std::vector<expr_node*> preConds;
|
||||
std::vector<expr_node*> postConds;
|
||||
};
|
||||
|
||||
std::string name;
|
||||
ast_type type;
|
||||
std::vector<var_decl_node> params;
|
||||
std::optional<def_s> def;
|
||||
};
|
||||
|
||||
class expr_node : public stmt_node {
|
||||
public:
|
||||
enum expr_type_e {
|
||||
Literal,
|
||||
|
||||
VarRead,
|
||||
FunctionCall,
|
||||
Group,
|
||||
BinaryOp,
|
||||
UnaryOp,
|
||||
If,
|
||||
};
|
||||
public:
|
||||
category_e category() const override { return ast_node_cat::Expression; }
|
||||
|
||||
stmt_type_e stmt_type() const override { return Expression; }
|
||||
|
||||
virtual expr_type_e expr_type() const = 0;
|
||||
};
|
||||
|
||||
struct var_read_expr_node final : public expr_node {
|
||||
expr_type_e expr_type() const override { return VarRead; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_var_read_expr_node(*this); }
|
||||
|
||||
std::string name;
|
||||
|
||||
var_read_expr_node(std::string&& name)
|
||||
: name(std::move(name)) {}
|
||||
};
|
||||
|
||||
struct func_call_expr_node final : public expr_node {
|
||||
expr_type_e expr_type() const override { return FunctionCall; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_func_call_expr_node(*this); }
|
||||
|
||||
expr_node* lhs = nullptr;
|
||||
std::vector<expr_node*> args;
|
||||
};
|
||||
|
||||
struct group_expr_node final : public expr_node {
|
||||
expr_type_e expr_type() const override { return Group; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_group_expr_node(*this); }
|
||||
|
||||
expr_node* inner = nullptr;
|
||||
};
|
||||
|
||||
struct binary_op_expr_node final : public expr_node {
|
||||
enum binary_op_type {
|
||||
Add = 0,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
|
||||
Shl,
|
||||
Shr,
|
||||
BinAnd,
|
||||
BinOr,
|
||||
BinXor,
|
||||
And,
|
||||
Or,
|
||||
|
||||
Equals,
|
||||
NotEquals,
|
||||
LessThan,
|
||||
LessEquals,
|
||||
GreaterThan,
|
||||
GreaterEquals,
|
||||
};
|
||||
|
||||
expr_type_e expr_type() const override { return BinaryOp; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_binary_op_expr_node(*this); }
|
||||
|
||||
expr_node* lhs = nullptr;
|
||||
expr_node* rhs = nullptr;
|
||||
binary_op_type type = Add;
|
||||
};
|
||||
|
||||
struct unary_op_expr_node final : public expr_node {
|
||||
enum unary_op_type {
|
||||
Positive = 0,
|
||||
Negative,
|
||||
PreInc,
|
||||
PreDec,
|
||||
PostInc,
|
||||
PostDec,
|
||||
BinNot,
|
||||
Not,
|
||||
|
||||
Sizeof,
|
||||
Pointerof,
|
||||
Lengthof,
|
||||
};
|
||||
|
||||
expr_type_e expr_type() const override { return UnaryOp; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_unary_op_expr_node(*this); }
|
||||
|
||||
expr_node* lhs = nullptr;
|
||||
unary_op_type type = Positive;
|
||||
};
|
||||
|
||||
struct if_expr_node final : public expr_node {
|
||||
expr_type_e expr_type() const override { return If; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_if_expr_node(*this); }
|
||||
|
||||
expr_node* cond = nullptr;
|
||||
expr_node* thenExpr = nullptr;
|
||||
expr_node* elseExpr = nullptr;
|
||||
};
|
||||
|
||||
class lit_node : public expr_node {
|
||||
public:
|
||||
enum lit_type_e {
|
||||
Integer,
|
||||
Char,
|
||||
};
|
||||
public:
|
||||
category_e category() const override { return ast_node_cat::Literal; }
|
||||
|
||||
expr_type_e expr_type() const override { return Literal; }
|
||||
|
||||
virtual lit_type_e lit_type() const = 0;
|
||||
};
|
||||
|
||||
struct int_lit_node final : public lit_node {
|
||||
int_lit_node(std::uint64_t value)
|
||||
: value(value) {}
|
||||
|
||||
lit_type_e lit_type() const override { return Integer; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_int_lit_node(*this); }
|
||||
|
||||
std::uint64_t value;
|
||||
};
|
||||
|
||||
struct char_lit_node final : public lit_node {
|
||||
char_lit_node(char value)
|
||||
: value(value) {}
|
||||
|
||||
lit_type_e lit_type() const override { return Char; }
|
||||
|
||||
void accept(ast_visitor& visitor) const override { visitor.visit_char_lit_node(*this); }
|
||||
|
||||
char value;
|
||||
};
|
||||
|
||||
struct ast {
|
||||
std::vector<decl_node*> decls;
|
||||
};
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_FRONT_AST_HPP
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef FURC_FRONT_LEXER_HPP
|
||||
#define FURC_FRONT_LEXER_HPP
|
||||
|
||||
#include "furc/front/token.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <deque>
|
||||
#include <string_view>
|
||||
|
||||
namespace furc {
|
||||
|
||||
class lexer {
|
||||
public:
|
||||
lexer(std::string_view filepath, std::string_view content)
|
||||
: m_filepath(filepath), m_content(content) {}
|
||||
|
||||
~lexer() = default;
|
||||
|
||||
lexer(lexer&&) noexcept = default;
|
||||
lexer& operator=(lexer&&) noexcept = default;
|
||||
|
||||
lexer(const lexer&) = delete;
|
||||
lexer& operator=(const lexer&) = delete;
|
||||
public:
|
||||
token next_token();
|
||||
token peek_token(std::size_t offset = 0);
|
||||
private:
|
||||
token get_token();
|
||||
|
||||
void next();
|
||||
constexpr char get(std::size_t offset = 0) const;
|
||||
void skip_spaces();
|
||||
|
||||
constexpr token::location location() const;
|
||||
private:
|
||||
std::string_view m_filepath;
|
||||
std::string_view m_content;
|
||||
std::size_t m_cursor = 0;
|
||||
std::size_t m_row = 0;
|
||||
std::size_t m_lineStart = 0;
|
||||
|
||||
std::deque<token> m_peekToken;
|
||||
};
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_FRONT_LEXER_HPP
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef FURC_FRONT_PARSER_HPP
|
||||
#define FURC_FRONT_PARSER_HPP
|
||||
|
||||
#include "furc/front/ast.hpp"
|
||||
#include "furc/front/lexer.hpp"
|
||||
#include "furlang/arena.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace furc {
|
||||
|
||||
class parser {
|
||||
public:
|
||||
parser(lexer&& lexer, furlang::arena& arena)
|
||||
: m_lexer(std::move(lexer)), m_arena(&arena) {}
|
||||
|
||||
~parser() = default;
|
||||
|
||||
parser(parser&&) noexcept = default;
|
||||
parser& operator=(parser&&) noexcept = default;
|
||||
|
||||
parser(const parser&) = delete;
|
||||
parser& operator=(const parser&) = delete;
|
||||
public:
|
||||
ast parse();
|
||||
private:
|
||||
stmt_node* parse_stmt();
|
||||
decl_node* parse_decl();
|
||||
expr_node* parse_expr();
|
||||
|
||||
ast_type parse_type();
|
||||
comp_stmt_node parse_comp();
|
||||
|
||||
expr_node* parse_expr_primary();
|
||||
expr_node* parse_expr_unary();
|
||||
expr_node* parse_expr_right(expr_node* lhs, std::uint32_t precedence = 15);
|
||||
private:
|
||||
template <typename... Types>
|
||||
token eat_token(Types... types) {
|
||||
auto token = m_lexer.next_token();
|
||||
if (((token.type == types) || ...)) return token;
|
||||
throw std::runtime_error("unexpected token");
|
||||
}
|
||||
private:
|
||||
lexer m_lexer;
|
||||
furlang::arena* m_arena;
|
||||
};
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_FRONT_PARSER_HPP
|
||||
@@ -0,0 +1,218 @@
|
||||
#ifndef FURC_FRONT_TOKEN_HPP
|
||||
#define FURC_FRONT_TOKEN_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
#include <string_view>
|
||||
|
||||
namespace furc {
|
||||
|
||||
struct token {
|
||||
enum type {
|
||||
Identifier = 0,
|
||||
Integer,
|
||||
String,
|
||||
Char,
|
||||
|
||||
LParen, /**< `(` */
|
||||
RParen, /**< `)` */
|
||||
LBrace, /**< `{` */
|
||||
RBrace, /**< `}` */
|
||||
LBracket, /**< `[` */
|
||||
RBracket, /**< `]` */
|
||||
Semicolon, /**< `;` */
|
||||
Colon, /**< `:` */
|
||||
Comma, /**< `,` */
|
||||
Dot, /**< `.` */
|
||||
|
||||
Plus, /**< `+` */
|
||||
Minus, /**< `-` */
|
||||
Star, /**< `*` */
|
||||
Slash, /**< `/` */
|
||||
Percent, /**< `%` */
|
||||
DblLT, /**< `<<` */
|
||||
DblGT, /**< `>>` */
|
||||
Ampersand, /**< `&` */
|
||||
Pipe, /**< `|` */
|
||||
Hat, /**< `^` */
|
||||
DblAmpersand, /**< `&&` */
|
||||
DblPipe, /**< `||` */
|
||||
|
||||
DblPlus, /**< `++` */
|
||||
DblMinus, /**< `--` */
|
||||
Tilde, /**< `~` */
|
||||
ExMark, /**< `!` */
|
||||
CatEars, /**< `^^` */
|
||||
|
||||
Equals, /**< `=` */
|
||||
PlusEquals, /**< `+=` */
|
||||
MinusEquals, /**< `-=` */
|
||||
StarEquals, /**< `*=` */
|
||||
SlashEquals, /**< `/=` */
|
||||
PercentEquals, /**< `%=` */
|
||||
AmpersandEquals, /**< `&=` */
|
||||
PipeEquals, /**< `|=` */
|
||||
HatEquals, /**< `^=` */
|
||||
|
||||
DblEquals, /**< `==` */
|
||||
ExEquals, /**< `!=` */
|
||||
LessThan, /**< `<` */
|
||||
LessEquals, /**< `<=` */
|
||||
GreaterThan, /**< `>` */
|
||||
GreaterEquals, /**< `>=` */
|
||||
|
||||
SlimArrow, /**< `->` */
|
||||
// My brother just another me
|
||||
FatArrow, /**< `=>` */
|
||||
|
||||
Monkey, /**< `@` */
|
||||
Sha256, /**< `#` */
|
||||
|
||||
Func, /**< `func` */
|
||||
Return, /**< `return` */
|
||||
If, /**< `if` */
|
||||
Else, /**< `else` */
|
||||
While, /**< `while` */
|
||||
Public, /**< `public` */
|
||||
Private, /**< `private` */
|
||||
Pre, /**< `pre` */
|
||||
Post, /**< `post` */
|
||||
|
||||
Pointerof, /**< `pointerof` */
|
||||
Sizeof, /**< `sizeof` */
|
||||
Lengthof, /**< `lengthof` */
|
||||
|
||||
S8, /**< `s8` */
|
||||
U8, /**< `u8` */
|
||||
S16, /**< `s16` */
|
||||
U16, /**< `u16` */
|
||||
S32, /**< `s32` */
|
||||
U32, /**< `u32` */
|
||||
S64, /**< `s64` */
|
||||
U64, /**< `u64` */
|
||||
|
||||
// Errors:
|
||||
UnexpectedCharacter,
|
||||
UnexpectedEOF,
|
||||
InvalidInteger,
|
||||
EndOfFile,
|
||||
} type;
|
||||
union value {
|
||||
std::nullptr_t null = nullptr;
|
||||
std::uint64_t integer;
|
||||
std::string_view string;
|
||||
char character;
|
||||
} value;
|
||||
|
||||
struct location {
|
||||
std::string_view filepath;
|
||||
std::size_t row = 0;
|
||||
std::size_t col = 0;
|
||||
} loc;
|
||||
|
||||
token(location loc, enum type type)
|
||||
: loc(loc), type(type) {}
|
||||
|
||||
token(location loc, std::uint64_t integer)
|
||||
: loc(loc), type(Integer) {
|
||||
value.integer = integer;
|
||||
}
|
||||
|
||||
token(location loc, enum type type, std::string_view string)
|
||||
: loc(loc), type(type) {
|
||||
value.string = string;
|
||||
}
|
||||
|
||||
token(location loc, enum type type, char character)
|
||||
: loc(loc), type(type) {
|
||||
value.character = character;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const token& token) {
|
||||
switch (token.type) {
|
||||
case token::Identifier: return os << token.value.string;
|
||||
case token::String: return os << '"' << token.value.string << '"';
|
||||
case token::Char: return os << '\'' << token.value.character << '\'';
|
||||
case token::Integer: return os << token.value.integer;
|
||||
case token::LParen: return os << "(";
|
||||
case token::RParen: return os << ")";
|
||||
case token::LBrace: return os << "{";
|
||||
case token::RBrace: return os << "}";
|
||||
case token::LBracket: return os << "[";
|
||||
case token::RBracket: return os << "]";
|
||||
case token::Semicolon: return os << ";";
|
||||
case token::Colon: return os << ":";
|
||||
case token::Comma: return os << ",";
|
||||
case token::Dot: return os << ".";
|
||||
case token::Plus: return os << "+";
|
||||
case token::Minus: return os << "-";
|
||||
case token::Star: return os << "*";
|
||||
case token::Slash: return os << "/";
|
||||
case token::Percent: return os << "%";
|
||||
case token::DblLT: return os << "<<";
|
||||
case token::DblGT: return os << ">>";
|
||||
case token::Ampersand: return os << "&";
|
||||
case token::Pipe: return os << "|";
|
||||
case token::Hat: return os << "^";
|
||||
case token::DblAmpersand: return os << "&&";
|
||||
case token::DblPipe: return os << "||";
|
||||
case token::DblPlus: return os << "++";
|
||||
case token::DblMinus: return os << "--";
|
||||
case token::Tilde: return os << "~";
|
||||
case token::ExMark: return os << "!";
|
||||
case token::CatEars: return os << "^^";
|
||||
case token::Equals: return os << "=";
|
||||
case token::PlusEquals: return os << "+=";
|
||||
case token::MinusEquals: return os << "-=";
|
||||
case token::StarEquals: return os << "*=";
|
||||
case token::SlashEquals: return os << "/=";
|
||||
case token::PercentEquals: return os << "%=";
|
||||
case token::AmpersandEquals: return os << "&=";
|
||||
case token::PipeEquals: return os << "|=";
|
||||
case token::HatEquals: return os << "^=";
|
||||
case token::DblEquals: return os << "==";
|
||||
case token::ExEquals: return os << "!=";
|
||||
case token::LessThan: return os << "<";
|
||||
case token::LessEquals: return os << "<=";
|
||||
case token::GreaterThan: return os << ">";
|
||||
case token::GreaterEquals: return os << ">=";
|
||||
case token::SlimArrow: return os << "->";
|
||||
case token::FatArrow: return os << "=>";
|
||||
case token::Monkey: return os << "@";
|
||||
case token::Sha256: return os << "#";
|
||||
case token::Func: return os << "func";
|
||||
case token::Return: return os << "return";
|
||||
case token::If: return os << "if";
|
||||
case token::Else: return os << "else";
|
||||
case token::While: return os << "while";
|
||||
case token::Public: return os << "public";
|
||||
case token::Private: return os << "private";
|
||||
case token::Pre: return os << "pre";
|
||||
case token::Post: return os << "post";
|
||||
case token::Pointerof: return os << "pointerof";
|
||||
case token::Sizeof: return os << "sizeof";
|
||||
case token::Lengthof: return os << "lengthof";
|
||||
case token::S8: return os << "s8";
|
||||
case token::U8: return os << "u8";
|
||||
case token::S16: return os << "s16";
|
||||
case token::U16: return os << "u16";
|
||||
case token::S32: return os << "s32";
|
||||
case token::U32: return os << "u32";
|
||||
case token::S64: return os << "s64";
|
||||
case token::U64: return os << "u64";
|
||||
case token::UnexpectedCharacter: return os << "Unexpected character `" << token.value.character << "`";
|
||||
case token::UnexpectedEOF: return os << "Unexpected End Of File";
|
||||
case token::InvalidInteger: return os << "Invalid Integer";
|
||||
case token::EndOfFile: return os << "End Of File";
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
using token_t = enum token::type;
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_FRONT_TOKEN_HPP
|
||||
@@ -0,0 +1,455 @@
|
||||
#ifndef FURC_MIDDLE_IR_HPP
|
||||
#define FURC_MIDDLE_IR_HPP
|
||||
|
||||
#include "furc/front/ast.hpp"
|
||||
#include "furlang/arena.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <optional>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace furc {
|
||||
|
||||
struct ir_operand {
|
||||
enum type_e {
|
||||
Integer = 0,
|
||||
Register,
|
||||
Variable,
|
||||
Global,
|
||||
Function,
|
||||
Block,
|
||||
BlockPair,
|
||||
PhiPair,
|
||||
} type;
|
||||
union value_u {
|
||||
std::uint64_t integer;
|
||||
struct register_s {
|
||||
std::uint64_t name : 54;
|
||||
std::uint64_t ver : 10;
|
||||
|
||||
register_s() = default;
|
||||
|
||||
register_s(std::uint64_t id)
|
||||
: name((id >> 10) & ((1ULL << 54) - 1)), ver((id >> 0) & ((1 << 10) - 1)) {}
|
||||
|
||||
register_s(std::uint64_t name, std::uint64_t ver)
|
||||
: name(name), ver(ver) {}
|
||||
|
||||
operator std::uint64_t() const { return name << 10 | ver; }
|
||||
} reg;
|
||||
std::uint16_t variable;
|
||||
std::uint16_t global;
|
||||
std::uint64_t function;
|
||||
std::uint64_t block;
|
||||
struct block_pair_s {
|
||||
std::uint64_t first;
|
||||
std::uint64_t second;
|
||||
} blockPair;
|
||||
struct phi_pair_s {
|
||||
register_s reg;
|
||||
std::uint64_t block;
|
||||
} phiPair;
|
||||
|
||||
value_u() = default;
|
||||
|
||||
value_u(std::uint64_t integer)
|
||||
: integer(integer) {}
|
||||
|
||||
value_u(std::uint16_t variable)
|
||||
: variable(variable) {}
|
||||
|
||||
value_u(register_s reg)
|
||||
: reg(reg) {}
|
||||
|
||||
value_u(std::uint64_t first, std::uint64_t second)
|
||||
: blockPair({ first, second }) {}
|
||||
|
||||
value_u(register_s reg, std::uint64_t block)
|
||||
: phiPair({ reg, block }) {}
|
||||
} value;
|
||||
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<value_u, Args...>>>
|
||||
ir_operand(type_e type, Args&&... args)
|
||||
: type(type), value(std::forward<Args>(args)...) {}
|
||||
|
||||
static ir_operand reg(std::uint64_t name, std::uint64_t ver) {
|
||||
return { Register, value_u::register_s{ name, ver } };
|
||||
}
|
||||
|
||||
bool operator==(const ir_operand& rhs) const {
|
||||
if (type != rhs.type) return false;
|
||||
switch (type) {
|
||||
case Integer: return value.integer == rhs.value.integer;
|
||||
case Register: return value.reg.name == rhs.value.reg.name && value.reg.ver == rhs.value.reg.ver;
|
||||
case Variable: return value.variable == rhs.value.variable;
|
||||
case Global: return value.global == rhs.value.global;
|
||||
case Function: return value.function == rhs.value.function;
|
||||
case Block: return value.block == rhs.value.block;
|
||||
case BlockPair:
|
||||
return value.blockPair.first == rhs.value.blockPair.first &&
|
||||
value.blockPair.second == rhs.value.blockPair.second;
|
||||
case PhiPair:
|
||||
return value.phiPair.block == rhs.value.phiPair.block &&
|
||||
value.phiPair.reg.name == rhs.value.phiPair.reg.name &&
|
||||
value.phiPair.reg.ver == rhs.value.phiPair.reg.ver;
|
||||
}
|
||||
throw std::runtime_error("unreachable");
|
||||
}
|
||||
};
|
||||
|
||||
struct ir_type {
|
||||
enum type_e {
|
||||
Void = 0,
|
||||
S8,
|
||||
U8,
|
||||
S16,
|
||||
U16,
|
||||
S32,
|
||||
U32,
|
||||
S64,
|
||||
U64,
|
||||
} type = Void;
|
||||
};
|
||||
|
||||
// TODO: Add data types to instructions (like mov QWORD ... in x86 assembly)
|
||||
struct ir_instruction {
|
||||
enum type_e {
|
||||
Move = 0,
|
||||
Call,
|
||||
Branch,
|
||||
BranchCond,
|
||||
Return,
|
||||
Phi,
|
||||
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
|
||||
Shl,
|
||||
Shr,
|
||||
BinAnd,
|
||||
BinOr,
|
||||
BinXor,
|
||||
And,
|
||||
Or,
|
||||
|
||||
Eq,
|
||||
NotEq,
|
||||
LessThan,
|
||||
LessEq,
|
||||
GreaterThan,
|
||||
GreaterEq,
|
||||
|
||||
Positive,
|
||||
Negative,
|
||||
Increment,
|
||||
Decrement,
|
||||
BinNot,
|
||||
Not,
|
||||
|
||||
Sizeof,
|
||||
Pointerof,
|
||||
Lenof,
|
||||
} type;
|
||||
std::optional<ir_operand> destination;
|
||||
std::vector<ir_operand> sources;
|
||||
|
||||
ir_instruction(type_e type,
|
||||
std::optional<ir_operand> destination = {},
|
||||
std::initializer_list<ir_operand> sources = {})
|
||||
: type(type), destination(destination), sources(sources) {}
|
||||
|
||||
static constexpr bool is_terminating(type_e type) {
|
||||
switch (type) {
|
||||
case Branch:
|
||||
case BranchCond:
|
||||
case Return: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const ir_instruction& rhs) const {
|
||||
return type == rhs.type && destination == rhs.destination && sources == rhs.sources;
|
||||
}
|
||||
};
|
||||
|
||||
struct ir_basic_block {
|
||||
std::vector<ir_instruction> instructions;
|
||||
|
||||
bool is_terminated() const {
|
||||
return !instructions.empty() && ir_instruction::is_terminating(instructions.back().type);
|
||||
}
|
||||
};
|
||||
|
||||
struct ir_variable {
|
||||
ir_variable() = default;
|
||||
|
||||
ir_variable(ir_type type)
|
||||
: type(type) {}
|
||||
|
||||
virtual ~ir_variable() = default;
|
||||
|
||||
ir_variable(ir_variable&&) noexcept = default;
|
||||
ir_variable& operator=(ir_variable&&) noexcept = default;
|
||||
|
||||
ir_variable(const ir_variable&) = default;
|
||||
ir_variable& operator=(const ir_variable&) = default;
|
||||
|
||||
ir_type type;
|
||||
|
||||
virtual ir_operand operand() const = 0;
|
||||
};
|
||||
|
||||
struct ir_module_variable : ir_variable {
|
||||
ir_module_variable(ir_type type, std::uint16_t name)
|
||||
: ir_variable(type), name(name) {}
|
||||
|
||||
std::uint16_t name;
|
||||
|
||||
ir_operand operand() const final { return { ir_operand::Global, name }; }
|
||||
};
|
||||
|
||||
struct ir_function_variable : ir_variable {
|
||||
ir_function_variable(ir_type type, std::uint64_t name)
|
||||
: ir_variable(type), name(name) {}
|
||||
|
||||
std::uint64_t name;
|
||||
|
||||
ir_operand operand() const final { return { ir_operand::Variable, name }; }
|
||||
};
|
||||
|
||||
struct ir_scope {
|
||||
ir_scope() = default;
|
||||
virtual ~ir_scope() = default;
|
||||
|
||||
ir_scope(ir_scope&&) noexcept = default;
|
||||
ir_scope& operator=(ir_scope&&) noexcept = default;
|
||||
|
||||
ir_scope(const ir_scope&) = default;
|
||||
ir_scope& operator=(const ir_scope&) = default;
|
||||
|
||||
ir_scope* previous = nullptr;
|
||||
|
||||
std::unordered_map<std::string, ir_variable*> variables;
|
||||
|
||||
const ir_variable* variable(const std::string& name) const {
|
||||
if (auto it = variables.find(name); it != variables.end()) return it->second;
|
||||
return (previous != nullptr) ? previous->variable(name) : nullptr;
|
||||
}
|
||||
|
||||
virtual const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) = 0;
|
||||
};
|
||||
|
||||
struct ir_function : ir_scope {
|
||||
enum type_e {
|
||||
Normal = 0,
|
||||
Import,
|
||||
Native,
|
||||
} type = Normal;
|
||||
enum access_e {
|
||||
Public = 0,
|
||||
Private,
|
||||
} access = Public;
|
||||
|
||||
std::string name;
|
||||
std::vector<ir_type> params;
|
||||
ir_type retType;
|
||||
std::vector<ir_basic_block> blocks;
|
||||
|
||||
std::uint64_t regCount = 0;
|
||||
std::uint64_t varCount = 0;
|
||||
|
||||
const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) final {
|
||||
return variables[name] = arena.allocate<ir_function_variable>(type, varCount++);
|
||||
}
|
||||
|
||||
static ir_function from_name(std::string&& name) {
|
||||
ir_function func;
|
||||
func.name = std::move(name);
|
||||
return func;
|
||||
}
|
||||
};
|
||||
|
||||
struct ir_module : ir_scope {
|
||||
std::vector<ir_function*> functions;
|
||||
furlang::arena arena;
|
||||
|
||||
std::uint16_t varCount = 0;
|
||||
|
||||
const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) final {
|
||||
return variables[name] = arena.allocate<ir_module_variable>(type, varCount);
|
||||
}
|
||||
|
||||
ir_function* add_function(ir_function&& function) {
|
||||
return functions.emplace_back(arena.allocate<ir_function>(std::move(function)));
|
||||
}
|
||||
};
|
||||
|
||||
struct ir_context {
|
||||
ir_context(ir_function* function)
|
||||
: function(function) {
|
||||
if (function->blocks.empty()) new_last();
|
||||
blockPtr = &function->blocks.front();
|
||||
}
|
||||
|
||||
~ir_context() {
|
||||
if (blockPtr == nullptr) return;
|
||||
if (!blockPtr->is_terminated()) {
|
||||
if (blockIdx + 1 == function->blocks.size()) {
|
||||
add_instr(ir_instruction::Return);
|
||||
} else {
|
||||
add_instr(ir_instruction::Branch, ir_operand{ ir_operand::Block, blockIdx + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ir_context(ir_context&& other) noexcept
|
||||
: function(other.function), blockIdx(other.blockIdx), blockPtr(other.blockPtr) {
|
||||
other.function = nullptr;
|
||||
other.blockIdx = 0;
|
||||
other.blockPtr = nullptr;
|
||||
}
|
||||
|
||||
ir_context& operator=(ir_context&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
function = other.function;
|
||||
blockIdx = other.blockIdx;
|
||||
blockPtr = other.blockPtr;
|
||||
|
||||
other.function = nullptr;
|
||||
other.blockIdx = 0;
|
||||
other.blockPtr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ir_context(const ir_context&) = delete;
|
||||
ir_context& operator=(const ir_context&) = delete;
|
||||
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<ir_instruction, Args...>>>
|
||||
ir_instruction& add_instr(Args&&... args) {
|
||||
auto it = blockPtr->instructions.end();
|
||||
if (!blockPtr->instructions.empty() && ir_instruction::is_terminating(blockPtr->instructions.back().type)) --it;
|
||||
it = blockPtr->instructions.emplace(it, std::forward<Args>(args)...);
|
||||
if (ir_instruction::is_terminating(it->type) && it + 1 != blockPtr->instructions.end())
|
||||
blockPtr->instructions.pop_back();
|
||||
return *it;
|
||||
}
|
||||
|
||||
void terminate() { add_instr(ir_instruction::Return); }
|
||||
|
||||
void terminate(ir_operand value) {
|
||||
ir_instruction instr = { ir_instruction::Return };
|
||||
instr.sources.emplace_back(value);
|
||||
add_instr(std::move(instr));
|
||||
}
|
||||
|
||||
void terminate(std::uint64_t block) { add_instr(ir_instruction::Branch, ir_operand{ ir_operand::Block, block }); }
|
||||
|
||||
ir_instruction* terminate(ir_operand cond, std::uint64_t thenBranch, std::uint64_t elseBranch) {
|
||||
return &add_instr(ir_instruction{ ir_instruction::BranchCond,
|
||||
ir_operand{ ir_operand::BlockPair, thenBranch, elseBranch },
|
||||
{ cond } });
|
||||
}
|
||||
|
||||
ir_context& new_next() {
|
||||
if (blockPtr->instructions.empty()) return *this;
|
||||
auto it = function->blocks.begin() + static_cast<std::ptrdiff_t>(++blockIdx);
|
||||
if (!blockPtr->is_terminated()) terminate(blockIdx);
|
||||
blockPtr = &*function->blocks.emplace(it);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ir_context& new_last() {
|
||||
blockIdx = function->blocks.size();
|
||||
blockPtr = &*function->blocks.emplace(function->blocks.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
ir_context& go(std::uint64_t block) {
|
||||
blockIdx = std::min(block, function->blocks.size() - 1);
|
||||
blockPtr = function->blocks.data() + static_cast<std::ptrdiff_t>(blockIdx);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ir_context& go_next() { return go(blockIdx + 1); }
|
||||
ir_context& go_last() { return go(std::min<std::uint64_t>(0, function->blocks.size() - 1)); }
|
||||
|
||||
ir_operand last_register() const { return { ir_operand::Register, function->regCount - 1 }; }
|
||||
ir_operand next_register() const { return { ir_operand::Register, function->regCount++ }; }
|
||||
|
||||
static ir_operand block_op(std::uint64_t blockIdx) { return { ir_operand::Block, blockIdx }; }
|
||||
|
||||
ir_function* function = nullptr;
|
||||
std::uint64_t blockIdx = 0;
|
||||
ir_basic_block* blockPtr = nullptr;
|
||||
};
|
||||
|
||||
class ir_generator final : public ast_visitor {
|
||||
public:
|
||||
ir_generator()
|
||||
: m_initContext(m_module.add_function(ir_function::from_name("module$init"))) {}
|
||||
|
||||
void finalize() {
|
||||
m_module.functions.front()->blocks.emplace_back().instructions.push_back(
|
||||
ir_instruction{ ir_instruction::Return });
|
||||
}
|
||||
|
||||
ir_module build() {
|
||||
m_scope = nullptr;
|
||||
m_context = {};
|
||||
m_initContext.blockPtr = nullptr;
|
||||
return std::move(m_module);
|
||||
}
|
||||
|
||||
static ir_module generate(const ast_node& node) {
|
||||
ir_generator gen;
|
||||
node.accept(gen);
|
||||
gen.finalize();
|
||||
return gen.build();
|
||||
}
|
||||
|
||||
static ir_module generate(const ast& tree) {
|
||||
ir_generator gen;
|
||||
for (const auto& node : tree.decls)
|
||||
node->accept(gen);
|
||||
gen.finalize();
|
||||
return gen.build();
|
||||
}
|
||||
private:
|
||||
void visit_comp_stmt_node(const comp_stmt_node& node) override;
|
||||
void visit_if_stmt_node(const if_stmt_node& node) override;
|
||||
void visit_while_stmt_node(const while_stmt_node& node) override;
|
||||
void visit_return_stmt_node(const return_stmt_node& node) override;
|
||||
void visit_var_decl_node(const var_decl_node& node) override;
|
||||
void visit_func_decl_node(const func_decl_node& node) override;
|
||||
void visit_var_read_expr_node(const var_read_expr_node& node) override;
|
||||
void visit_func_call_expr_node(const func_call_expr_node& node) override;
|
||||
void visit_group_expr_node(const group_expr_node& node) override;
|
||||
void visit_binary_op_expr_node(const binary_op_expr_node& node) override;
|
||||
void visit_unary_op_expr_node(const unary_op_expr_node& node) override;
|
||||
void visit_if_expr_node(const if_expr_node& node) override;
|
||||
void visit_int_lit_node(const int_lit_node& node) override;
|
||||
void visit_char_lit_node(const char_lit_node& node) override;
|
||||
private:
|
||||
ir_context& context() { return m_context.top(); }
|
||||
private:
|
||||
ir_module m_module;
|
||||
ir_scope* m_scope = &m_module;
|
||||
std::stack<ir_context> m_context;
|
||||
|
||||
ir_context m_initContext;
|
||||
};
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_MIDDLE_IR_HPP
|
||||
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* SSA destruction (out-of-SSA phase) for register-based targets based on "Mechanizing Conventional SSA for a
|
||||
* Verified Destruction with Coalescing" by Delphine Demange and Yon Fernandez de Retana
|
||||
* (https://dl.acm.org/doi/pdf/10.1145/2892208.2892222 09/11/2026).
|
||||
*/
|
||||
#ifndef FURC_MIDDLE_REG_GEN_HPP
|
||||
#define FURC_MIDDLE_REG_GEN_HPP
|
||||
|
||||
#include "furc/middle/ir.hpp"
|
||||
#include "furc/middle/ssa.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace furc {
|
||||
|
||||
class reg_gen {
|
||||
public:
|
||||
class disjoint_set {
|
||||
public:
|
||||
std::uint64_t find(std::uint64_t var) {
|
||||
if (m_parents.find(var) == m_parents.end()) m_parents.emplace(var, var);
|
||||
if (m_parents[var] == var) return var;
|
||||
return find(m_parents[var]);
|
||||
}
|
||||
|
||||
void unite(std::uint64_t var1, std::uint64_t var2) {
|
||||
std::uint64_t rep1 = find(var1);
|
||||
std::uint64_t rep2 = find(var2);
|
||||
if (rep1 != rep2) m_parents[rep1] = rep2;
|
||||
}
|
||||
private:
|
||||
std::unordered_map<std::uint64_t, std::uint64_t> m_parents;
|
||||
};
|
||||
public:
|
||||
struct block_info {
|
||||
std::unordered_set<std::uint64_t> defs;
|
||||
std::unordered_set<std::uint64_t> uses;
|
||||
std::unordered_set<std::uint64_t> liveIn;
|
||||
std::unordered_set<std::uint64_t> liveOut;
|
||||
};
|
||||
public:
|
||||
reg_gen(ir_function& func, ssa& ssa) {
|
||||
std::vector<block_info> lifeBlocks;
|
||||
live_analysis(lifeBlocks, func.blocks, ssa.cfgBlocks);
|
||||
remove_interference(func.blocks, lifeBlocks);
|
||||
merge(func.blocks);
|
||||
}
|
||||
public:
|
||||
static void live_analysis(std::vector<block_info>& lifeBlocks,
|
||||
const std::vector<ir_basic_block>& irBlocks,
|
||||
const std::vector<ssa::cfg_block>& cfgBlocks) {
|
||||
lifeBlocks.resize(irBlocks.size());
|
||||
for (std::uint64_t i = 0; i < irBlocks.size(); ++i) {
|
||||
const auto& irBlock = irBlocks[i];
|
||||
auto& block = lifeBlocks[i];
|
||||
|
||||
for (const auto& instr : irBlock.instructions) {
|
||||
for (const auto& op : instr.sources) {
|
||||
if (op.type != ir_operand::Register) continue;
|
||||
if (block.defs.find(op.value.reg) != block.defs.end()) continue;
|
||||
block.uses.insert(op.value.reg);
|
||||
}
|
||||
|
||||
if (!instr.destination.has_value() || instr.destination->type != ir_operand::Register) continue;
|
||||
block.defs.insert(instr.destination->value.reg);
|
||||
}
|
||||
}
|
||||
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
|
||||
for (std::uint64_t i = 0; i < irBlocks.size(); ++i) {
|
||||
const auto& irBlock = irBlocks[i];
|
||||
auto& block = lifeBlocks[i];
|
||||
|
||||
std::unordered_set<std::uint64_t> newSet;
|
||||
for (auto succ : cfgBlocks[i].sucs) {
|
||||
newSet.insert(lifeBlocks[succ].liveIn.begin(), lifeBlocks[succ].liveIn.end());
|
||||
}
|
||||
|
||||
if (newSet != block.liveOut) {
|
||||
block.liveOut = newSet;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
newSet.clear();
|
||||
newSet.insert(block.uses.begin(), block.uses.end());
|
||||
for (const auto& var : block.liveOut) {
|
||||
if (block.defs.find(var) != block.defs.end()) continue;
|
||||
newSet.insert(var);
|
||||
}
|
||||
|
||||
if (newSet != block.liveIn) {
|
||||
block.liveIn = newSet;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_interference(std::vector<ir_basic_block>& irBlocks, const std::vector<block_info>& lifeBlocks) {
|
||||
for (std::uint64_t i = 0; i < irBlocks.size(); ++i) {
|
||||
auto& irBlock = irBlocks[i];
|
||||
|
||||
for (auto it = irBlock.instructions.begin(), end = irBlock.instructions.end();
|
||||
it != end && it->type == ir_instruction::Phi;
|
||||
++it) {
|
||||
assert(it->destination.has_value() && it->destination->type == ir_operand::Register);
|
||||
const auto& phiDst = it->destination->value.reg;
|
||||
|
||||
for (auto& op : it->sources) {
|
||||
assert(op.type == ir_operand::PhiPair);
|
||||
const auto& predBlock = lifeBlocks[op.value.phiPair.block];
|
||||
auto& phiArg = op.value.phiPair.reg;
|
||||
if (predBlock.liveOut.count(phiArg) == 0 || phiArg == phiDst) continue;
|
||||
|
||||
auto oldArg = phiArg;
|
||||
phiArg.ver = 0; // TODO: Allocate temporary registers
|
||||
|
||||
auto& irBlock = irBlocks[op.value.phiPair.block];
|
||||
assert(!irBlock.instructions.empty());
|
||||
auto it = irBlock.instructions.end() - 1;
|
||||
if (ir_instruction::is_terminating(it->type)) --it;
|
||||
irBlock.instructions.emplace(it,
|
||||
ir_instruction{ ir_instruction::Move,
|
||||
ir_operand::reg(phiArg.name, phiArg.ver),
|
||||
{ ir_operand::reg(oldArg.name, oldArg.ver) } });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void merge(std::vector<ir_basic_block>& irBlocks) {
|
||||
disjoint_set dj;
|
||||
|
||||
for (const auto& block : irBlocks) {
|
||||
for (const auto& instr : block.instructions) {
|
||||
if (instr.type != ir_instruction::Phi) break;
|
||||
|
||||
assert(instr.destination.has_value() && instr.destination->type == ir_operand::Register);
|
||||
const auto& phiDst = instr.destination->value.reg;
|
||||
dj.find(phiDst);
|
||||
|
||||
for (const auto& op : instr.sources) {
|
||||
assert(op.type == ir_operand::PhiPair);
|
||||
const auto& predBlock = op.value.phiPair.block;
|
||||
const auto& phiArg = op.value.phiPair.reg;
|
||||
dj.unite(phiDst, phiArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& block : irBlocks) {
|
||||
auto it = block.instructions.begin();
|
||||
while (it != block.instructions.end() && it->type == ir_instruction::Phi) {
|
||||
it = block.instructions.erase(it);
|
||||
}
|
||||
for (; it != block.instructions.end(); ++it) {
|
||||
for (auto& op : it->sources) {
|
||||
if (op.type != ir_operand::Register) continue;
|
||||
op.value.reg = dj.find(op.value.reg);
|
||||
}
|
||||
|
||||
if (!it->destination.has_value() || it->destination->type != ir_operand::Register) continue;
|
||||
it->destination->value.reg = dj.find(it->destination->value.reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_MIDDLE_REG_GEN_HPP
|
||||
@@ -0,0 +1,101 @@
|
||||
#ifndef FURC_MIDDLE_SSA_HPP
|
||||
#define FURC_MIDDLE_SSA_HPP
|
||||
|
||||
#include "furc/middle/ir.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace furc {
|
||||
|
||||
class ssa {
|
||||
public:
|
||||
struct cfg_block {
|
||||
std::unordered_set<std::uint64_t> preds;
|
||||
std::unordered_set<std::uint64_t> sucs;
|
||||
};
|
||||
|
||||
struct ssa_block {
|
||||
std::size_t order = 0;
|
||||
|
||||
std::uint64_t idom = -1;
|
||||
|
||||
std::unordered_set<std::uint64_t> children; // Children of the block in dominator tree
|
||||
|
||||
// Dominance Frontiers
|
||||
std::unordered_set<std::uint64_t> df;
|
||||
};
|
||||
|
||||
struct register_info {
|
||||
std::unordered_set<std::uint64_t> sites; // Definition Sites
|
||||
};
|
||||
public:
|
||||
ssa(ir_function& func) {
|
||||
registers.resize(func.regCount);
|
||||
compute_cfg(func.blocks, cfgBlocks);
|
||||
collect_registers(func.blocks, registers, globals);
|
||||
|
||||
std::vector<std::uint64_t> order;
|
||||
compute_rpo(cfgBlocks, ssaBlocks, order);
|
||||
|
||||
build_dtree(cfgBlocks, ssaBlocks, order);
|
||||
compute_dfrontiers(cfgBlocks, ssaBlocks);
|
||||
|
||||
ssaification(func.blocks, cfgBlocks, ssaBlocks, registers, globals);
|
||||
rename(func.blocks, func.regCount, cfgBlocks, ssaBlocks, order);
|
||||
}
|
||||
public:
|
||||
static void compute_cfg(const std::vector<ir_basic_block>& irBlocks, std::vector<cfg_block>& cfgBlocks);
|
||||
|
||||
static void collect_registers(const std::vector<ir_basic_block>& irBlocks,
|
||||
std::vector<register_info>& registers,
|
||||
std::unordered_set<std::uint64_t>& globals);
|
||||
|
||||
static void build_dtree(const std::vector<cfg_block>& cfgBlocks,
|
||||
std::vector<ssa_block>& ssaBlocks,
|
||||
const std::vector<std::size_t>& order);
|
||||
|
||||
static void compute_dfrontiers(const std::vector<cfg_block>& cfgBlocks, std::vector<ssa_block>& ssaBlocks);
|
||||
|
||||
static void compute_rpo(std::vector<cfg_block>& cfgBlocks,
|
||||
std::vector<ssa_block>& ssaBlocks,
|
||||
std::vector<std::size_t>& order);
|
||||
|
||||
static void ssaification(std::vector<ir_basic_block>& irBlocks,
|
||||
const std::vector<cfg_block>& cfgBlocks,
|
||||
const std::vector<ssa_block>& ssaBlocks,
|
||||
const std::vector<register_info>& registers,
|
||||
const std::unordered_set<std::uint64_t>& globals);
|
||||
|
||||
static void rename(std::vector<ir_basic_block>& irBlocks,
|
||||
std::size_t regCount,
|
||||
const std::vector<cfg_block>& cfgBlocks,
|
||||
std::vector<ssa_block>& ssaBlocks,
|
||||
const std::vector<std::uint64_t>& order);
|
||||
private:
|
||||
static void rename_rec(std::vector<std::uint64_t>& counters,
|
||||
std::vector<std::stack<std::uint64_t>>& stacks,
|
||||
std::vector<ir_basic_block>& irBlocks,
|
||||
const std::vector<cfg_block>& cfgBlocks,
|
||||
const std::vector<ssa_block>& ssaBlocks,
|
||||
std::size_t blockIdx);
|
||||
private:
|
||||
static void rpo_dfs(std::unordered_set<std::size_t>& visited,
|
||||
std::vector<std::size_t>& order,
|
||||
std::size_t block,
|
||||
const std::vector<cfg_block>& blocks);
|
||||
|
||||
static std::size_t intersect(std::vector<ssa_block>& m_blocks, std::size_t b1, std::size_t b2);
|
||||
public:
|
||||
std::vector<cfg_block> cfgBlocks;
|
||||
std::vector<ssa_block> ssaBlocks;
|
||||
std::vector<register_info> registers;
|
||||
std::unordered_set<std::uint64_t> globals;
|
||||
};
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_MIDDLE_SSA_HPP
|
||||
Reference in New Issue
Block a user