diff --git a/.clang-tidy b/.clang-tidy index 88d2f52..a37eb72 100755 --- a/.clang-tidy +++ b/.clang-tidy @@ -11,6 +11,7 @@ Checks: > -bugprone-assignment-in-if-condition, -bugprone-easily-swappable-parameters, -bugprone-multi-level-implicit-pointer-conversion, + -bugprone-unintended-char-ostream-output, -readability-function-cognitive-complexity, -readability-magic-numbers, -readability-redundant-access-specifiers, @@ -28,7 +29,8 @@ Checks: > -cppcoreguidelines-macro-usage, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes, - -cppcoreguidelines-pro-bounds-avoid-unchecked-container-access + -cppcoreguidelines-pro-bounds-avoid-unchecked-container-access, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay WarningsAsErrors: "*" @@ -95,4 +97,4 @@ CheckOptions: - key: readability-identifier-naming.EnumConstantCase value: CamelCase - key: readability-identifier-naming.ScopedEnumConstantCase - value: CamelCase \ No newline at end of file + value: CamelCase diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp index ea3adba..ec745cf 100644 --- a/furc/include/furc/front/ir_generator.hpp +++ b/furc/include/furc/front/ir_generator.hpp @@ -22,7 +22,7 @@ public: ir_generator(const ir_generator&) = delete; ir_generator& operator=(const ir_generator&) = delete; public: - furlang::ir::module&& move_module() { return std::move(m_module); } + furlang::ir::mod&& move_module() { return std::move(m_module); } public: void visit(const ast::function_definition_node& funcDef) override; void visit(const ast::return_statement_node& returnStmt) override; @@ -45,7 +45,7 @@ private: furlang::ir::block_index push_block(bool validate = true); private: - furlang::ir::module m_module; + furlang::ir::mod m_module; std::unique_ptr m_currentFunction; std::shared_ptr m_currentBlock; ir_register m_registerCounter = 0; diff --git a/furc/include/furc/front/parser.hpp b/furc/include/furc/front/parser.hpp index 40468ae..d9218df 100644 --- a/furc/include/furc/front/parser.hpp +++ b/furc/include/furc/front/parser.hpp @@ -23,7 +23,7 @@ public: * @param filename Filename for debugging. * @param content Content. */ - parser(std::string_view filename, std::string_view content); + parser(furlang::arena& arena, std::string_view filename, std::string_view content); /** * @brief Construct a new parser from file. @@ -32,7 +32,8 @@ public: * * @param filename Name of the file. */ - parser(std::string_view filename); + parser(furlang::arena& arena, std::string_view filename); + ~parser() = default; /** @@ -40,13 +41,12 @@ public: */ parser(parser&&) = default; - parser(const parser&) = delete; - /** * @brief Move constructor. */ parser& operator=(parser&&) = default; + parser(const parser&) = delete; parser& operator=(const parser&) = delete; public: /** @@ -73,11 +73,11 @@ private: std::string m_filename; std::string m_content; lexer m_lexer; - furlang::arena m_arena; + furlang::arena* m_arena; std::vector m_peekBuffer; }; } // namespace front } // namespace furc -#endif // FURC_FRONT_PARSER_HPP \ No newline at end of file +#endif // FURC_FRONT_PARSER_HPP diff --git a/furc/include/furc/front/post_process.hpp b/furc/include/furc/front/post_process.hpp new file mode 100644 index 0000000..1145fb4 --- /dev/null +++ b/furc/include/furc/front/post_process.hpp @@ -0,0 +1,41 @@ +#ifndef FURC_FRONT_POST_PROCESS_HPP +#define FURC_FRONT_POST_PROCESS_HPP + +#include "furlang/ir/module.hpp" + +#include + +namespace furc { +namespace front { + +/** + * @brief Post process pipeline. + */ +class post_process { +public: + enum stage { // NOLINT + Ssa, + Sccp, + Adce, + DeSsa, + }; +public: + post_process() = default; + ~post_process() = default; + + post_process(post_process&&) noexcept = default; + post_process& operator=(post_process&&) noexcept = default; + post_process(const post_process&) = delete; + post_process& operator=(const post_process&) = delete; +public: + void push_stage(stage stage) { m_stages.push_back(stage); } +public: + void process(furlang::ir::mod& mod); +private: + std::vector m_stages; +}; + +} // namespace front +} // namespace furc + +#endif // FURC_FRONT_POST_PROCESS_HPP diff --git a/furc/include/furc/front/ssa.hpp b/furc/include/furc/front/ssa.hpp deleted file mode 100644 index 0c99b78..0000000 --- a/furc/include/furc/front/ssa.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef FURC_FRONT_SSA_HPP -#define FURC_FRONT_SSA_HPP - -#include "furlang/ir/function.hpp" -#include "furlang/ir/instruction.hpp" -#include "furlang/ir/module.hpp" - -#include -#include -#include -#include - -namespace furc { -namespace front { - -class ssa { - using block_map_t = std::unordered_map>; -public: - static void optimize(furlang::ir::module& mod); -private: - static void optimize(const std::unique_ptr& func); - - static void de_ssa(const std::unique_ptr& func); - - static void constant_propagation(const std::unique_ptr& func); - - static void dead_code_elimination(const std::unique_ptr& func); - - static void copy_propagation(const std::unique_ptr& func); - - static void dfs_rpo(furlang::ir::block_index block, - const block_map_t& successors, - std::unordered_set& visited, - std::vector& rpo); -}; - -} // namespace front -} // namespace furc - -#endif // FURC_FRONT_SSA_HPP diff --git a/furc/src/front/parser.cpp b/furc/src/front/parser.cpp index b983e69..b2828b8 100644 --- a/furc/src/front/parser.cpp +++ b/furc/src/front/parser.cpp @@ -16,11 +16,11 @@ namespace furc::front { using namespace std::string_literals; -parser::parser(std::string_view filename, std::string_view content) - : m_filename(filename), m_content(content), m_lexer(m_filename, m_content) {} +parser::parser(furlang::arena& arena, std::string_view filename, std::string_view content) + : m_filename(filename), m_content(content), m_lexer(m_filename, m_content), m_arena(&arena) {} -parser::parser(std::string_view filename) - : m_filename(filename) { +parser::parser(furlang::arena& arena, std::string_view filename) + : m_filename(filename), m_arena(&arena) { std::ifstream file(m_filename, std::ios_base::binary | std::ios_base::ate); if (!file.is_open()) throw std::runtime_error("failed to open file "s.append(m_filename)); std::streampos size = file.tellg(); @@ -32,7 +32,7 @@ parser::parser(std::string_view filename) } ast::program_node_r parser::parse() & { - auto program = m_arena.allocate_shared(location{ m_filename }); + auto program = m_arena->allocate_shared(location{ m_filename }); while (peek_token().has_value()) { auto decl = parse_declaration(); @@ -67,13 +67,13 @@ ast::declaration_node_r parser::parse_declaration() { case token_t::LBrace: { ast::body_r body = parse_body(); if (body.has_error()) return ast::declaration_node_r(ast::error{ body.error().location }); - return m_arena.allocate_shared(first->location, + return m_arena->allocate_shared(first->location, name->value.string, std::move(body.value())); } case token_t::Semicolon: { m_peekBuffer.clear(); - return m_arena.allocate_shared(first->location, name->value.string); + return m_arena->allocate_shared(first->location, name->value.string); } default: return ast::declaration_node_r(ast::error{ tok->location }); } @@ -109,13 +109,13 @@ ast::statement_node_r parser::parse_statement() { auto tok = next_token(); if (peek_token()->type == token_t::Semicolon) { next_token(); - return m_arena.allocate_shared(location); + return m_arena->allocate_shared(location); } auto value = parse_expression(); auto err = eat_token(token_t::Semicolon); if (err.has_error()) return ast::statement_node_r(ast::error{ err.error().location }); - return m_arena.allocate_shared(location, std::move(value.value())); + return m_arena->allocate_shared(location, std::move(value.value())); } case keyword_token::If: { auto tok = next_token(); @@ -136,13 +136,13 @@ ast::statement_node_r parser::parse_statement() { auto elseBody = parse_statement(); if (elseBody.has_error()) return ast::statement_node_r(ast::error{ elseBody.error().location }); - return m_arena.allocate_shared(location, + return m_arena->allocate_shared(location, std::move(cond.value()), std::move(then.value()), std::move(elseBody.value())); } - return m_arena.allocate_shared(location, + return m_arena->allocate_shared(location, std::move(cond.value()), std::move(then.value())); } @@ -160,7 +160,7 @@ ast::statement_node_r parser::parse_statement() { auto body = parse_statement(); if (body.has_error()) return ast::statement_node_r(ast::error{ body.error().location }); - return m_arena.allocate_shared(location, + return m_arena->allocate_shared(location, std::move(cond.value()), std::move(body.value())); } @@ -172,7 +172,7 @@ ast::statement_node_r parser::parse_statement() { case token_t::LBrace: { auto body = parse_body(); if (body.has_error()) return ast::statement_node_r(ast::error{ body.error().location }); - return m_arena.allocate_shared(location, std::move(body.value())); + return m_arena->allocate_shared(location, std::move(body.value())); } default: break; } @@ -204,7 +204,7 @@ ast::expression_node_r parser::parse_expression_primary() { case token_t::Identifier: { auto tok = next_token(); if (tok.has_error()) return ast::expression_node_r(ast::error{ tok.error().location }); - return m_arena.allocate_shared(tok->location, (*tok)->string); + return m_arena->allocate_shared(tok->location, (*tok)->string); } case token_t::LParen: { auto tok = next_token(); @@ -216,12 +216,12 @@ ast::expression_node_r parser::parse_expression_primary() { case token_t::String: { auto tok = next_token(); if (tok.has_error()) return ast::expression_node_r(ast::error{ tok.error().location }); - return m_arena.allocate_shared(tok->location, (*tok)->string); + return m_arena->allocate_shared(tok->location, (*tok)->string); } case token_t::Integer: { auto tok = next_token(); if (tok.has_error()) return ast::expression_node_r(ast::error{ tok.error().location }); - return m_arena.allocate_shared(tok->location, (*tok)->integer); + return m_arena->allocate_shared(tok->location, (*tok)->integer); } default: { return ast::expression_node_r(ast::error{ tok->location }); @@ -262,7 +262,7 @@ ast::expression_node_r parser::parse_expression_unary(std::uint32_t precedence) expression = std::move(std::move(expr.value())); } - result = m_arena.allocate_shared(token->location, + result = m_arena->allocate_shared(token->location, current.type, std::move(expression)); } @@ -389,18 +389,18 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini switch (current.type) { case rhsop_info_t::Unaryop: - lhs = m_arena.allocate_shared(opToken->location, + lhs = m_arena->allocate_shared(opToken->location, current.unary, std::move(lhs)); break; case rhsop_info_t::Binop: - lhs = m_arena.allocate_shared(opToken->location, + lhs = m_arena->allocate_shared(opToken->location, current.binary, std::move(lhs), std::move(rhs)); break; case rhsop_info_t::Assignment: - lhs = m_arena.allocate_shared(opToken->location, + lhs = m_arena->allocate_shared(opToken->location, current.assignment, std::move(lhs), std::move(rhs)); diff --git a/furc/src/front/post_process.cpp b/furc/src/front/post_process.cpp new file mode 100644 index 0000000..31281b2 --- /dev/null +++ b/furc/src/front/post_process.cpp @@ -0,0 +1,599 @@ +#include "furc/front/post_process.hpp" + +#include "furlang/ir/function.hpp" +#include "furlang/ir/instruction.hpp" +#include "furlang/ir/operand.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace furc::front { + +using block_idx = furlang::ir::block_index; +using register_t = furlang::ir::register_t; +using register_op = furlang::ir::register_operand; + +static constexpr block_idx INVALID_BLOCK = std::numeric_limits::max(); + +struct block_info { + std::size_t rpoIndex{ 0 }; + + std::vector predecessors; + std::vector successors; + + block_idx idom = INVALID_BLOCK; + std::vector doms; + std::unordered_set domFrontiers; +}; + +struct register_info { + std::unordered_set defSites; + std::stack renameStack; + std::uint32_t nextVersion{ 0 }; +}; + +struct function_context { + explicit function_context(furlang::ir::function* function) + : function(function) { + build_cfg(); + compute_rpo(); + } + + void build_cfg() { + for (block_idx idx = 0; idx < function->blocks().size(); ++idx) { + const auto& block = function->blocks()[idx]; + + for (const auto& instr : block->instructions()) { + for (const auto& operand : instr->sources()) { + if (operand->type() != furlang::ir::operand_t::Register) continue; + auto reg = operand->reg(); + if (registers[reg].defSites.find(idx) != registers[reg].defSites.end()) continue; + globalRegisters.insert(reg); + } + } + + for (const auto& instr : block->instructions()) { + if (!instr->has_destination() || instr->destination().type() != furlang::ir::operand_t::Register) + continue; + auto reg = instr->destination().reg(); + registers[reg].defSites.insert(idx); + } + + for (const auto& operand : block->exit()->sources()) { + if (operand->type() != furlang::ir::operand_t::Register) continue; + auto reg = operand->reg(); + if (registers[reg].defSites.find(idx) != registers[reg].defSites.end()) continue; + globalRegisters.insert(reg); + } + + const auto& exit = block->exit(); + switch (exit->type()) { + case furlang::ir::instruction_t::Branch: { + const auto& br = dynamic_cast(*exit); + blocks[br.block()].predecessors.push_back(idx); + blocks[idx].successors.push_back(br.block()); + } break; + case furlang::ir::instruction_t::BranchCond: { + const auto& br = dynamic_cast(*exit); + blocks[br.if_block()].predecessors.push_back(idx); + blocks[br.else_block()].predecessors.push_back(idx); + blocks[idx].successors.push_back(br.if_block()); + blocks[idx].successors.push_back(br.else_block()); + } break; + default: break; + } + } + } + + void compute_rpo() { + std::unordered_set visited; + + auto dfs = [&](auto& self, block_idx block) -> void { + visited.insert(block); + for (auto succ : blocks[block].successors) { + if (visited.find(succ) != visited.end()) continue; + self(self, succ); + } + rpoOrder.push_back(block); + }; + + if (!function->blocks().empty()) dfs(dfs, 0); + + std::reverse(rpoOrder.begin(), rpoOrder.end()); + for (std::size_t i = 0; i < rpoOrder.size(); ++i) { + blocks[rpoOrder[i]].rpoIndex = i; + } + } + + void compute_dominance() { + if (rpoOrder.empty()) return; + + const block_idx entry = rpoOrder.front(); + blocks[entry].idom = entry; + + bool changed = true; + while (changed) { + changed = false; + + for (block_idx idx : rpoOrder) { + if (idx == entry) continue; + + block_idx newIdom = INVALID_BLOCK; + bool found = false; + for (auto pred : blocks[idx].predecessors) { + if (blocks[pred].idom == INVALID_BLOCK) continue; + if (found) { + newIdom = intersect(pred, newIdom); + } else { + newIdom = pred; + found = true; + } + } + + if (blocks[idx].idom != newIdom) { + blocks[idx].idom = newIdom; + changed = true; + } + } + } + + for (auto idx : rpoOrder) { + if (idx == entry) continue; + const block_idx parent = blocks[idx].idom; + if (parent != INVALID_BLOCK) blocks[parent].doms.push_back(idx); + } + + for (auto idx : rpoOrder) { + if (blocks[idx].predecessors.size() < 2) continue; + for (auto cur : blocks[idx].predecessors) { + while (cur != blocks[idx].idom) { + blocks[cur].domFrontiers.insert(idx); + cur = blocks[cur].idom; + } + } + } + } + + furlang::ir::function* function; + std::vector rpoOrder; + std::unordered_map blocks; + std::unordered_map registers; + std::unordered_set globalRegisters; +private: + block_idx intersect(block_idx block1, block_idx block2) { + while (block1 != block2) { + while (blocks[block1].rpoIndex > blocks[block2].rpoIndex) + block1 = blocks[block1].idom; + while (blocks[block2].rpoIndex > blocks[block1].rpoIndex) + block2 = blocks[block2].idom; + } + return block1; + } +}; + +static void ssa_stage_rename_block(function_context& ctx, + block_idx idx, + std::unordered_map& regVers, + std::unordered_map>& regVerStacks) { + std::unordered_map pushed; + + const auto& block = ctx.function->blocks()[idx]; + auto it = block->instructions().begin(); + for (; it != block->instructions().end(); ++it) { + auto& instr = *it; + if (instr->type() != furlang::ir::instruction_t::Phi) break; + + const register_t orig = instr->destination().reg(); + const std::uint32_t newVer = regVers[orig]++; + + instr->destination().reg().ver = newVer; + regVerStacks[orig].push(newVer); + ++pushed[orig]; + } + + for (; it != block->instructions().end(); ++it) { + auto& instr = *it; + for (auto& operand : instr->sources()) { + if (operand->type() != furlang::ir::operand_t::Register) continue; + + const register_t orig = operand->reg(); + if (regVerStacks[orig].empty()) continue; + operand->reg().ver = regVerStacks[orig].top(); + } + + if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) { + const register_t orig = instr->destination().reg(); + const std::uint32_t newVer = regVers[orig]++; + + instr->destination().reg().ver = newVer; + regVerStacks[orig].push(newVer); + ++pushed[orig]; + } + } + + for (auto& operand : block->exit()->sources()) { + if (operand->type() != furlang::ir::operand_t::Register) continue; + + const auto orig = operand->reg(); + if (regVerStacks[orig].empty()) continue; + operand->reg().ver = regVerStacks[orig].top(); + } + + for (auto succIdx : ctx.blocks[idx].successors) { + const auto& succ = ctx.function->blocks()[succIdx]; + for (auto& instr : succ->instructions()) { + if (instr->type() != furlang::ir::instruction_t::Phi) break; + + auto& phi = dynamic_cast(*instr); + for (auto& pair : phi.labels()) { + if (pair.second != idx) continue; + + auto orig = pair.first.reg(); + if (auto it = regVerStacks.find(orig); it != regVerStacks.end()) + pair.first.reg().ver = it->second.top(); + } + } + } + + for (const auto& child : ctx.blocks[idx].doms) { + ssa_stage_rename_block(ctx, child, regVers, regVerStacks); + } + + for (const auto& [reg, count] : pushed) { + for (std::size_t i = 0; i < count; ++i) + regVerStacks[reg].pop(); + } +} + +static void ssa_stage(function_context& ctx) { + ctx.compute_dominance(); + + std::vector worklist; + for (const auto& [reg, info] : ctx.registers) { + if (info.defSites.size() < 2 || ctx.globalRegisters.find(reg) == ctx.globalRegisters.end()) continue; + + worklist.clear(); + worklist.insert(worklist.end(), info.defSites.begin(), info.defSites.end()); + + std::unordered_set added; + while (!worklist.empty()) { + const auto idx = worklist.back(); + worklist.pop_back(); + for (auto frontier : ctx.blocks[idx].domFrontiers) { + if (added.find(frontier) != added.end()) continue; + added.insert(frontier); + + const auto& target = ctx.function->blocks()[frontier]; + const auto& preds = ctx.blocks[frontier].predecessors; + + auto instr = std::make_unique(reg); + for (auto pred : preds) { + instr->labels().emplace_back(furlang::ir::operand::new_reg(reg), pred); + } + target->instructions().emplace(target->instructions().begin(), std::move(instr)); + + if (info.defSites.find(frontier) == info.defSites.end()) worklist.push_back(frontier); + } + } + } + + std::unordered_map regVers; + std::unordered_map> regVerStacks; + ssa_stage_rename_block(ctx, ctx.rpoOrder.front(), regVers, regVerStacks); +} + +static void dessa_stage(function_context& ctx) { + for (block_idx idx = 0; idx < ctx.function->blocks().size(); ++idx) { + const auto& block = ctx.function->blocks()[idx]; + auto& instrs = block->instructions(); + for (auto it = instrs.begin(); it != instrs.end() && (*it)->type() == furlang::ir::instruction_t::Phi; + it = instrs.erase(it)) { + auto& phi = dynamic_cast(**it); + auto dstReg = phi.destination().reg(); + for (auto& [srcOp, label] : phi.labels()) { + ctx.function->blocks()[label]->instructions().push_back( + std::make_unique(furlang::ir::operand::new_reg(srcOp.reg()), + furlang::ir::operand::new_reg(dstReg))); + } + } + } +} + +struct sccp_lattice { + enum lattice_t { // NOLINT + Top, + Constant, + Bottom, + } type = Top; + std::uint64_t constant = 0; + + bool operator==(const sccp_lattice& other) const { + return type == other.type && (type != Constant || constant == other.constant); + } + + bool operator!=(const sccp_lattice& other) const { return !this->operator==(other); } +}; + +sccp_lattice sccp_stage_get_lattice(std::unordered_map& latticeValues, + const furlang::ir::operand& op) { + if (op.type() == furlang::ir::operand_t::Integer) { + sccp_lattice lat; + lat.type = sccp_lattice::Constant; + lat.constant = op.integer(); + return lat; + } + if (op.type() == furlang::ir::operand_t::Register) { + auto reg = op.reg(); + if (auto it = latticeValues.find(reg); it != latticeValues.end()) return it->second; + return { sccp_lattice::Top }; + } + return { sccp_lattice::Bottom }; +}; + +static void sccp_stage(function_context& ctx) { + using lattice = sccp_lattice; + + std::unordered_map latticeValues; + std::unordered_map> edges; + std::unordered_set execBlocks; + std::set> execEdges; + + std::queue> cfgWorklist; + std::queue ssaWorklist; + + std::unordered_map blockMap; + + for (block_idx idx = 0; idx < ctx.function->blocks().size(); ++idx) { + const auto& block = ctx.function->blocks()[idx]; + auto& instrs = block->instructions(); + for (auto it = instrs.begin(); it != instrs.end(); ++it) { + const auto& instr = *it; + blockMap[instr.get()] = idx; + for (const auto& op : instr->sources()) { + if (op->type() != furlang::ir::operand_t::Register) continue; + edges[op->reg()].push_back(instr.get()); + } + } + + blockMap[block->exit().get()] = idx; + for (const auto& op : block->exit()->sources()) { + if (op->type() != furlang::ir::operand_t::Register) continue; + edges[op->reg()].push_back(block->exit().get()); + } + } + + cfgWorklist.push({ 0, 0 }); + while (!cfgWorklist.empty() || !ssaWorklist.empty()) { + if (!cfgWorklist.empty()) { + auto edge = cfgWorklist.front(); + cfgWorklist.pop(); + block_idx from = edge.first; + block_idx to = edge.second; + + if (execEdges.count(edge) != 0) continue; + execEdges.insert(edge); + + bool firstVisit = (execBlocks.find(to) == execBlocks.end()); + execBlocks.insert(to); + + const auto& block = ctx.function->blocks()[to]; + if (firstVisit) { + for (auto& instr : block->instructions()) { + ssaWorklist.push(instr.get()); + } + ssaWorklist.push(block->exit().get()); + } else { + for (auto& instr : block->instructions()) { + if (instr->type() != furlang::ir::instruction_t::Phi) break; + ssaWorklist.push(instr.get()); + } + } + } + + if (!ssaWorklist.empty()) { + auto* instr = ssaWorklist.front(); + ssaWorklist.pop(); + + block_idx blockIdx = blockMap[instr]; + if (execBlocks.find(blockIdx) == execBlocks.end()) continue; + + lattice newLat = { lattice::Top }; + + switch (instr->type()) { + case furlang::ir::instruction_t::Phi: { + auto& phi = dynamic_cast(*instr); + for (const auto& [op, label] : phi.labels()) { + if (execEdges.count({ label, blockIdx }) == 0) continue; + lattice opLat = sccp_stage_get_lattice(latticeValues, op); + if (opLat.type == lattice::Bottom) newLat.type = lattice::Bottom; + if (opLat.type == lattice::Constant) { + if (newLat.type == lattice::Top) { + newLat = opLat; + } else if (newLat.type == lattice::Constant && newLat.constant != opLat.constant) { + newLat.type = lattice::Bottom; + } + } + } + } break; + case furlang::ir::instruction_t::Assign: { + newLat = sccp_stage_get_lattice(latticeValues, *instr->sources().front()); + } break; + case furlang::ir::instruction_t::BinaryOp: { + lattice lhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[0]); + lattice rhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[1]); + + if (lhs.type == lattice::Bottom || rhs.type == lattice::Bottom) { + newLat.type = lattice::Bottom; + } else if (lhs.type == lattice::Constant && rhs.type == lattice::Constant) { + newLat.type = lattice::Constant; + switch (dynamic_cast(*instr).op_type()) { + case furlang::ir::binary_op_instruction_t::Add: + newLat.constant = lhs.constant + rhs.constant; + break; + case furlang::ir::binary_op_instruction_t::Sub: + newLat.constant = lhs.constant - rhs.constant; + break; + case furlang::ir::binary_op_instruction_t::Mul: + newLat.constant = lhs.constant * rhs.constant; + break; + case furlang::ir::binary_op_instruction_t::Div: + newLat.constant = lhs.constant / rhs.constant; + break; + case furlang::ir::binary_op_instruction_t::Mod: + newLat.constant = lhs.constant % rhs.constant; + break; + case furlang::ir::binary_op_instruction_t::Eq: + newLat.constant = (lhs.constant == rhs.constant) ? 1 : 0; + break; + case furlang::ir::binary_op_instruction_t::NotEq: + newLat.constant = (lhs.constant != rhs.constant) ? 1 : 0; + break; + case furlang::ir::binary_op_instruction_t::LessThan: + newLat.constant = (lhs.constant < rhs.constant) ? 1 : 0; + break; + case furlang::ir::binary_op_instruction_t::GreaterThan: + newLat.constant = (lhs.constant > rhs.constant) ? 1 : 0; + break; + case furlang::ir::binary_op_instruction_t::LessEq: + newLat.constant = (lhs.constant <= rhs.constant) ? 1 : 0; + break; + case furlang::ir::binary_op_instruction_t::GreaterEq: + newLat.constant = (lhs.constant >= rhs.constant) ? 1 : 0; + break; + } + } + } break; + default: break; + } + + if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) { + auto dst = instr->destination().reg(); + if (!(latticeValues[dst] == newLat)) { + latticeValues[dst] = newLat; + for (auto* uInstr : edges[dst]) + ssaWorklist.push(uInstr); + } + } + + if (instr == ctx.function->blocks()[blockIdx]->exit().get()) { + auto* exit = ctx.function->blocks()[blockIdx]->exit().get(); + if (exit->type() == furlang::ir::instruction_t::Branch) { + auto& br = dynamic_cast(*exit); + cfgWorklist.push({ blockIdx, br.block() }); + } else if (exit->type() == furlang::ir::instruction_t::BranchCond) { + auto& br = dynamic_cast(*exit); + lattice cond = sccp_stage_get_lattice(latticeValues, *exit->sources()[0]); + + if (cond.type == lattice::Constant) { + if (cond.constant != 0) + cfgWorklist.push({ blockIdx, br.if_block() }); + else + cfgWorklist.push({ blockIdx, br.else_block() }); + } else { + cfgWorklist.push({ blockIdx, br.if_block() }); + cfgWorklist.push({ blockIdx, br.else_block() }); + } + } + } + } + } + + for (block_idx i = 0; i < ctx.function->blocks().size(); ++i) { + if (execBlocks.find(i) == execBlocks.end()) { + ctx.function->blocks()[i]->instructions().clear(); + continue; + } + + const auto& block = ctx.function->blocks()[i]; + + for (auto& instr : block->instructions()) { + for (auto& op : instr->sources()) { + if (op->type() != furlang::ir::operand_t::Register) continue; + auto reg = op->reg(); + if (latticeValues[reg].type != lattice::Constant) continue; + *op = furlang::ir::operand::new_integer(latticeValues[reg].constant); + } + } + + auto* exit = block->exit().get(); + if (exit->type() != furlang::ir::instruction_t::BranchCond) continue; + auto& br = dynamic_cast(*exit); + lattice cond = sccp_stage_get_lattice(latticeValues, *exit->sources()[0]); + if (cond.type != lattice::Constant) continue; + block_idx target = (cond.constant != 0) ? br.if_block() : br.else_block(); + block->exit() = std::make_unique(target); + } +} + +static void adce_stage(function_context& ctx) { + std::unordered_map defMap; + std::unordered_set alive; + std::queue worklist; + + for (block_idx blockIdx = 0; blockIdx < ctx.function->blocks().size(); ++blockIdx) { + const auto& block = ctx.function->blocks()[blockIdx]; + + for (auto& instr : block->instructions()) { + if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) { + defMap[instr->destination().reg()] = instr.get(); + } + } + + auto* exit = block->exit().get(); + alive.insert(exit); + worklist.push(exit); + } + + while (!worklist.empty()) { + const auto* instr = worklist.front(); + worklist.pop(); + + for (const auto& op : instr->sources()) { + if (op->type() != furlang::ir::operand_t::Register) continue; + auto src = op->reg(); + + if (defMap.find(src) == defMap.end()) continue; + auto* defInstr = defMap[src]; + if (alive.insert(defInstr).second) { + worklist.push(defInstr); + } + } + } + + for (block_idx blockIdx = 0; blockIdx < ctx.function->blocks().size(); ++blockIdx) { + const auto& block = ctx.function->blocks()[blockIdx]; + auto& instrs = block->instructions(); + + auto it = instrs.begin(); + while (it != instrs.end()) { + it = (alive.find(it->get()) != alive.end()) ? it + 1 : instrs.erase(it); + } + } +} + +void post_process::process(furlang::ir::mod& mod) { + for (const auto& func : mod.functions()) { + if (!func || func->blocks().empty()) continue; + function_context ctx{ func.get() }; + + for (const auto& stage : m_stages) { + switch (stage) { + case Ssa: ssa_stage(ctx); break; + case Sccp: sccp_stage(ctx); break; + case Adce: adce_stage(ctx); break; + case DeSsa: dessa_stage(ctx); break; + } + } + } +} + +} // namespace furc::front diff --git a/furc/src/front/ssa.cpp b/furc/src/front/ssa.cpp deleted file mode 100644 index 8fd8888..0000000 --- a/furc/src/front/ssa.cpp +++ /dev/null @@ -1,640 +0,0 @@ -#include "furc/front/ssa.hpp" - -#include "furlang/ir/instruction.hpp" -#include "furlang/ir/operand.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace furc::front { - -void ssa::optimize(furlang::ir::module& mod) { - for (const auto& func : mod.functions()) { - ssa::optimize(func); - ssa::constant_propagation(func); - ssa::dead_code_elimination(func); - ssa::copy_propagation(func); - ssa::dead_code_elimination(func); - ssa::de_ssa(func); - } -} - -void ssa::optimize(const std::unique_ptr& func) { - block_map_t predecessors; - block_map_t successors; - - std::unordered_map> regSites; - - std::unordered_map idoms; - - std::unordered_map regVers; - std::unordered_map> regVerStacks; - std::unordered_map> domTree; - - for (furlang::ir::block_index i = 0; i < func->blocks().size(); ++i) { - const auto& block = func->blocks()[i]; - - for (const auto& instr : block->instructions()) { - if (instr->has_destination()) { - if (instr->destination().type() == furlang::ir::operand_t::Register) { - regSites[instr->destination().reg()].insert(i); - } - } - } - - const auto& exit = block->exit(); - switch (exit->type()) { - case furlang::ir::instruction_t::Branch: { - const auto& br = dynamic_cast(*exit); - predecessors[br.block()].push_back(i); - successors[i].push_back(br.block()); - } break; - case furlang::ir::instruction_t::BranchCond: { - const auto& br = dynamic_cast(*exit); - predecessors[br.if_block()].push_back(i); - predecessors[br.else_block()].push_back(i); - successors[i].push_back(br.if_block()); - successors[i].push_back(br.else_block()); - } break; - default: break; - } - } - - std::unordered_set globalRegs; - for (furlang::ir::block_index i = 0; i < func->blocks().size(); ++i) { - const auto& block = func->blocks()[i]; - for (const auto& instr : block->instructions()) { - for (const auto& operand : instr->sources()) { - if (operand->type() == furlang::ir::operand_t::Register) { - auto reg = operand->reg(); - if (regSites[reg].find(i) == regSites[reg].end()) { - globalRegs.insert(reg); - } - } - } - } - for (const auto& operand : block->exit()->sources()) { - if (operand->type() == furlang::ir::operand_t::Register) { - auto reg = operand->reg(); - if (regSites[reg].find(i) == regSites[reg].end()) { - globalRegs.insert(reg); - } - } - } - } - - std::unordered_set visited; - std::vector rpoOrder; - dfs_rpo(0, successors, visited, rpoOrder); - std::reverse(rpoOrder.begin(), rpoOrder.end()); - - std::unordered_map rpoIndex; - for (std::size_t i = 0; i < rpoOrder.size(); ++i) { - rpoIndex[rpoOrder[i]] = i; - } - - auto intersect = [&](furlang::ir::block_index block1, furlang::ir::block_index block2) { - while (block1 != block2) { - while (rpoIndex[block1] > rpoIndex[block2]) { - block1 = idoms[block1]; - } - while (rpoIndex[block2] > rpoIndex[block1]) { - block2 = idoms[block2]; - } - } - return block1; - }; - - if (rpoOrder.empty()) return; - auto entry = rpoOrder.front(); - idoms[entry] = entry; - - bool changed = true; - while (changed) { - changed = false; - - for (std::size_t i = 1; i < rpoOrder.size(); ++i) { - auto block = rpoOrder[i]; - - furlang::ir::block_index newIdom = 0; - bool found = false; - for (auto pred : predecessors[block]) { - if (idoms.find(pred) == idoms.end()) continue; - if (!found) { - newIdom = pred; - found = true; - } else { - newIdom = intersect(pred, newIdom); - } - } - - if (idoms.find(block) == idoms.end() || idoms[block] != newIdom) { - idoms[block] = newIdom; - changed = true; - } - } - } - - std::unordered_map> df; - for (auto block : rpoOrder) { - df[block] = std::unordered_set{}; - } - - for (auto block : rpoOrder) { - if (predecessors[block].size() < 2) continue; - - for (auto pred : predecessors[block]) { - auto runner = pred; - while (runner != idoms[block]) { - df[runner].insert(block); - runner = idoms[runner]; - } - } - } - - std::unordered_map> phis; - - for (const auto& [reg, blocks] : regSites) { - if (globalRegs.find(reg) == globalRegs.end()) continue; - - std::vector worklist(blocks.begin(), blocks.end()); - std::unordered_set added; - while (!worklist.empty()) { - auto block = worklist.back(); - worklist.pop_back(); - for (auto frontier : df[block]) { - if (added.find(frontier) != added.end()) continue; - phis[frontier].insert(reg); - added.insert(frontier); - if (blocks.find(frontier) == blocks.end()) { - worklist.push_back(frontier); - } - } - } - } - - for (furlang::ir::block_index i = 0; i < func->blocks().size(); ++i) { - if (phis.find(i) == phis.end()) continue; - const auto& block = func->blocks()[i]; - const auto& preds = predecessors[i]; - - for (auto reg : phis[i]) { - auto phiInstr = std::make_unique(reg); - for (auto pred : preds) { - phiInstr->labels().emplace_back(furlang::ir::operand::new_reg(reg), pred); - } - block->instructions().emplace(block->instructions().begin(), std::move(phiInstr)); - } - } - - for (const auto& [block, idom] : idoms) { - if (block != idom) domTree[idom].push_back(block); - } - - std::function renameBlock = [&](furlang::ir::block_index blockIndex) -> void { - std::unordered_map pushed; - - const auto& block = func->blocks()[blockIndex]; - for (auto& instr : block->instructions()) { - if (instr->type() != furlang::ir::instruction_t::Phi) continue; - auto orig = instr->destination().reg(); - std::uint32_t newVer = regVers[orig]++; - instr->destination().reg().ver = newVer; - regVerStacks[orig].push(newVer); - ++pushed[orig]; - } - - for (auto& instr : block->instructions()) { - if (instr->type() == furlang::ir::instruction_t::Phi) continue; - - for (auto& operand : instr->sources()) { - if (operand->type() != furlang::ir::operand_t::Register) continue; - auto orig = operand->reg(); - if (!regVerStacks[orig].empty()) { - operand->reg().ver = regVerStacks[orig].top(); - } - } - - if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) { - auto orig = instr->destination().reg(); - std::uint32_t newVer = regVers[orig]++; - instr->destination().reg().ver = newVer; - regVerStacks[orig].push(newVer); - ++pushed[orig]; - } - } - - for (auto& operand : block->exit()->sources()) { - if (operand->type() != furlang::ir::operand_t::Register) continue; - auto orig = operand->reg(); - if (!regVerStacks[orig].empty()) { - operand->reg().ver = regVerStacks[orig].top(); - } - } - - for (auto succIndex : successors[blockIndex]) { - const auto& succ = func->blocks()[succIndex]; - for (auto& instr : succ->instructions()) { - if (instr->type() != furlang::ir::instruction_t::Phi) break; - auto& phi = dynamic_cast(*instr); - for (auto& [op, bl] : phi.labels()) { - if (bl != blockIndex) continue; - if (regVerStacks[op.reg()].empty()) continue; - op.reg().ver = regVerStacks[op.reg()].top(); - } - } - } - - for (const auto& child : domTree[blockIndex]) { - renameBlock(child); - } - - for (const auto& [reg, count] : pushed) { - for (std::uint32_t i = 0; i < count; ++i) { - regVerStacks[reg].pop(); - } - } - }; - - renameBlock(entry); -} - -void ssa::de_ssa(const std::unique_ptr& func) { - using namespace furlang::ir; - - std::unordered_map>> copies; - for (block_index blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - auto& instrs = block->instructions(); - for (auto it = instrs.begin(); it != instrs.end();) { - if ((*it)->type() != furlang::ir::instruction_t::Phi) { - ++it; - continue; - } - auto& phi = dynamic_cast(**it); - auto dstReg = phi.destination().reg(); - for (auto& [srcOp, label] : phi.labels()) { - copies[label].emplace_back( - std::make_unique(std::move(srcOp), operand::new_reg(dstReg))); - } - it = instrs.erase(it); - } - } - - for (auto& [blockIdx, cpys] : copies) { - if (blockIdx >= func->blocks().size()) continue; - const auto& block = func->blocks()[blockIdx]; - auto& instrs = block->instructions(); - for (auto& copy : cpys) { - instrs.push_back(std::move(copy)); - } - } -} - -enum class lattice_t { - Top, - Constant, - Bottom, -}; - -struct lattice { - lattice_t type = lattice_t::Top; - std::uint64_t value = 0; - - bool operator==(const lattice& rhs) const { - if (type != rhs.type) return false; - return type != lattice_t::Constant || value == rhs.value; - } -}; - -void ssa::constant_propagation(const std::unique_ptr& func) { - using block_idx = furlang::ir::block_index; - using reg_t = furlang::ir::register_operand; - using reg2_t = furlang::ir::register_t; - - std::unordered_map latVals; - std::unordered_map> edges; - std::unordered_set executableBlocks; - std::set> executedEdges; - - std::queue> cfgWorklist; - std::queue ssaWorklist; - - std::unordered_map blockMap; - - auto getOperandLattice = [&](const furlang::ir::operand& op) -> lattice { - if (op.type() == furlang::ir::operand_t::Integer) { - lattice lat; - lat.type = lattice_t::Constant; - lat.value = op.integer(); - return lat; - } - if (op.type() == furlang::ir::operand_t::Register) { - auto reg = op.reg(); - if (latVals.find(reg) == latVals.end()) return { lattice_t::Top }; - return latVals[reg]; - } - return { lattice_t::Bottom }; - }; - - for (block_idx blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - auto& instrs = block->instructions(); - for (auto it = instrs.begin(); it != instrs.end(); ++it) { - const auto& instr = *it; - blockMap[instr.get()] = blockIdx; - for (const auto& op : instr->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - edges[op->reg()].push_back(instr.get()); - } - } - - blockMap[block->exit().get()] = blockIdx; - for (const auto& op : block->exit()->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - edges[op->reg()].push_back(block->exit().get()); - } - } - - cfgWorklist.push({ 0, 0 }); - while (!cfgWorklist.empty() || !ssaWorklist.empty()) { - if (!cfgWorklist.empty()) { - auto edge = cfgWorklist.front(); - cfgWorklist.pop(); - block_idx from = edge.first; - block_idx to = edge.second; - - if (executedEdges.count(edge) != 0) continue; - executedEdges.insert(edge); - - bool firstVisit = (executableBlocks.find(to) == executableBlocks.end()); - executableBlocks.insert(to); - - const auto& block = func->blocks()[to]; - if (firstVisit) { - for (auto& instr : block->instructions()) { - ssaWorklist.push(instr.get()); - } - ssaWorklist.push(block->exit().get()); - } else { - for (auto& instr : block->instructions()) { - if (instr->type() != furlang::ir::instruction_t::Phi) break; - ssaWorklist.push(instr.get()); - } - } - } - - if (!ssaWorklist.empty()) { - auto* instr = ssaWorklist.front(); - ssaWorklist.pop(); - - block_idx blockIdx = blockMap[instr]; - if (executableBlocks.find(blockIdx) == executableBlocks.end()) continue; - - lattice newLat = { lattice_t::Top }; - - switch (instr->type()) { - case furlang::ir::instruction_t::Phi: { - auto& phi = dynamic_cast(*instr); - for (const auto& [op, label] : phi.labels()) { - if (executedEdges.count({ label, blockIdx }) == 0) continue; - lattice opLat = getOperandLattice(op); - if (opLat.type == lattice_t::Bottom) newLat.type = lattice_t::Bottom; - if (opLat.type == lattice_t::Constant) { - if (newLat.type == lattice_t::Top) { - newLat = opLat; - } else if (newLat.type == lattice_t::Constant && newLat.value != opLat.value) { - newLat.type = lattice_t::Bottom; - } - } - } - } break; - case furlang::ir::instruction_t::Assign: { - newLat = getOperandLattice(*instr->sources().front()); - } break; - case furlang::ir::instruction_t::BinaryOp: { - lattice lhs = getOperandLattice(*instr->sources()[0]); - lattice rhs = getOperandLattice(*instr->sources()[1]); - - if (lhs.type == lattice_t::Bottom || rhs.type == lattice_t::Bottom) { - newLat.type = lattice_t::Bottom; - } else if (lhs.type == lattice_t::Constant && rhs.type == lattice_t::Constant) { - newLat.type = lattice_t::Constant; - switch (dynamic_cast(*instr).op_type()) { - case furlang::ir::binary_op_instruction_t::Add: newLat.value = lhs.value + rhs.value; break; - case furlang::ir::binary_op_instruction_t::Sub: newLat.value = lhs.value - rhs.value; break; - case furlang::ir::binary_op_instruction_t::Mul: newLat.value = lhs.value * rhs.value; break; - case furlang::ir::binary_op_instruction_t::Div: newLat.value = lhs.value / rhs.value; break; - case furlang::ir::binary_op_instruction_t::Mod: newLat.value = lhs.value % rhs.value; break; - case furlang::ir::binary_op_instruction_t::Eq: - newLat.value = (lhs.value == rhs.value) ? 1 : 0; - break; - case furlang::ir::binary_op_instruction_t::NotEq: - newLat.value = (lhs.value != rhs.value) ? 1 : 0; - break; - case furlang::ir::binary_op_instruction_t::LessThan: - newLat.value = (lhs.value < rhs.value) ? 1 : 0; - break; - case furlang::ir::binary_op_instruction_t::GreaterThan: - newLat.value = (lhs.value > rhs.value) ? 1 : 0; - break; - case furlang::ir::binary_op_instruction_t::LessEq: - newLat.value = (lhs.value <= rhs.value) ? 1 : 0; - break; - case furlang::ir::binary_op_instruction_t::GreaterEq: - newLat.value = (lhs.value >= rhs.value) ? 1 : 0; - break; - } - } - } break; - default: break; - } - - if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) { - auto dst = instr->destination().reg(); - if (!(latVals[dst] == newLat)) { - latVals[dst] = newLat; - for (auto* uInstr : edges[dst]) - ssaWorklist.push(uInstr); - } - } - - if (instr == func->blocks()[blockIdx]->exit().get()) { - auto* exit = func->blocks()[blockIdx]->exit().get(); - if (exit->type() == furlang::ir::instruction_t::Branch) { - auto& br = dynamic_cast(*exit); - cfgWorklist.push({ blockIdx, br.block() }); - } else if (exit->type() == furlang::ir::instruction_t::BranchCond) { - auto& br = dynamic_cast(*exit); - lattice cond = getOperandLattice(*exit->sources()[0]); - - if (cond.type == lattice_t::Constant) { - if (cond.value != 0) - cfgWorklist.push({ blockIdx, br.if_block() }); - else - cfgWorklist.push({ blockIdx, br.else_block() }); - } else { - cfgWorklist.push({ blockIdx, br.if_block() }); - cfgWorklist.push({ blockIdx, br.else_block() }); - } - } - } - } - } - - for (block_idx i = 0; i < func->blocks().size(); ++i) { - if (executableBlocks.find(i) == executableBlocks.end()) { - func->blocks()[i]->instructions().clear(); - continue; - } - - const auto& block = func->blocks()[i]; - - for (auto& instr : block->instructions()) { - for (auto& op : instr->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - auto reg = op->reg(); - if (latVals[reg].type != lattice_t::Constant) continue; - *op = furlang::ir::operand::new_integer(latVals[reg].value); - } - } - - auto* exit = block->exit().get(); - if (exit->type() != furlang::ir::instruction_t::BranchCond) continue; - auto& br = dynamic_cast(*exit); - lattice cond = getOperandLattice(*exit->sources()[0]); - if (cond.type != lattice_t::Constant) continue; - block_idx target = (cond.value != 0) ? br.if_block() : br.else_block(); - block->exit() = std::make_unique(target); - } -} - -void ssa::dead_code_elimination(const std::unique_ptr& func) { - using block_idx = furlang::ir::block_index; - using reg_t = furlang::ir::register_operand; - - std::unordered_map defMap; - std::unordered_set alive; - std::queue worklist; - - for (block_idx blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - - for (auto& instr : block->instructions()) { - if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) { - defMap[instr->destination().reg()] = instr.get(); - } - } - - auto* exit = block->exit().get(); - alive.insert(exit); - worklist.push(exit); - } - - while (!worklist.empty()) { - const auto* instr = worklist.front(); - worklist.pop(); - - for (const auto& op : instr->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - auto src = op->reg(); - - if (defMap.find(src) == defMap.end()) continue; - auto* defInstr = defMap[src]; - if (alive.insert(defInstr).second) { - worklist.push(defInstr); - } - } - } - - for (block_idx blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - auto& instrs = block->instructions(); - - for (auto it = instrs.begin(); it != instrs.end();) { - if (alive.find(it->get()) == alive.end()) { - it = instrs.erase(it); - } else { - ++it; - } - } - } -} - -void ssa::copy_propagation(const std::unique_ptr& func) { - using block_idx = furlang::ir::block_index; - using reg_t = furlang::ir::register_operand; - - std::unordered_map aliasMap; - - std::function findRep = [&](const reg_t& reg) -> reg_t { - auto it = aliasMap.find(reg); - if (it == aliasMap.end()) return reg; - reg_t act = findRep(it->second); - aliasMap[reg] = act; - return act; - }; - - for (block_idx blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - for (auto& instr : block->instructions()) { - if (instr->type() != furlang::ir::instruction_t::Assign) continue; - auto& srcOp = *instr->sources().front(); - if (srcOp.type() != furlang::ir::operand_t::Register || - instr->destination().type() != furlang::ir::operand_t::Register) - continue; - reg_t dstReg = instr->destination().reg(); - reg_t srcReg = srcOp.reg(); - reg_t repSrc = findRep(srcReg); - reg_t repDst = findRep(dstReg); - if (repSrc == repDst) continue; - aliasMap[repDst] = repSrc; - } - } - - for (block_idx blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - for (auto& instr : block->instructions()) { - for (auto& op : instr->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - op->reg() = findRep(op->reg()); - } - if (instr->type() != furlang::ir::instruction_t::Phi) continue; - auto& phi = dynamic_cast(*instr); - for (auto& [op, label] : phi.labels()) { - if (op.type() != furlang::ir::operand_t::Register) continue; - op.reg() = findRep(op.reg()); - } - } - - for (auto& op : block->exit()->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - op->reg() = findRep(op->reg()); - } - } -} - -void ssa::dfs_rpo(furlang::ir::block_index block, - const block_map_t& successors, - std::unordered_set& visited, - std::vector& rpo) { - visited.insert(block); - if (auto it = successors.find(block); it != successors.end()) { - for (auto successor : it->second) { - if (visited.find(successor) == visited.end()) { - dfs_rpo(successor, successors, visited, rpo); - } - } - } - rpo.push_back(block); -} - -} // namespace furc::front diff --git a/furc/src/main.cpp b/furc/src/main.cpp index 1849438..8865671 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -3,7 +3,8 @@ #include "furc/ast/program.hpp" #include "furc/front/ir_generator.hpp" #include "furc/front/parser.hpp" -#include "furc/front/ssa.hpp" +#include "furc/front/post_process.hpp" +#include "furlang/arena.hpp" #include @@ -19,7 +20,8 @@ int main(void) { } } )"; - furc::front::parser parser("", programStr); + furlang::arena arena{}; + furc::front::parser parser(arena, "", programStr); furc::front::ir_generator generator; auto programResult = parser.parse(); @@ -30,11 +32,17 @@ int main(void) { const auto& program = *programResult; program->accept(generator); - auto module = std::move(generator.move_module()); - furc::front::ssa::optimize(module); + auto mod = std::move(generator.move_module()); + + furc::front::post_process postProcess; + postProcess.push_stage(furc::front::post_process::Ssa); + postProcess.push_stage(furc::front::post_process::Sccp); + postProcess.push_stage(furc::front::post_process::Adce); + postProcess.push_stage(furc::front::post_process::DeSsa); + postProcess.process(mod); std::cout << "Generated IR:\n"; - for (const auto& function : module.functions()) { + for (const auto& function : mod.functions()) { std::cout << function->name() << ":\n"; furlang::ir::block_index blockIndex = 0; for (const auto& block : function->blocks()) { diff --git a/furlang/include/furlang/arena.hpp b/furlang/include/furlang/arena.hpp index ef87d97..58dce98 100644 --- a/furlang/include/furlang/arena.hpp +++ b/furlang/include/furlang/arena.hpp @@ -106,6 +106,55 @@ private: region* m_tail = nullptr; }; +template +class arena_allocator { + template + friend class arena_allocator; +public: + using value_type = T; +public: + explicit arena_allocator(arena& arena) noexcept + : m_arena(&arena) {} + + template + arena_allocator(const arena_allocator& other) noexcept + : m_arena(other.m_arena) {} + + template + arena_allocator& operator=(const arena_allocator& other) noexcept { + if (this == &other) return *this; + m_arena = other.m_arena; + return *this; + } + + template + arena_allocator(arena_allocator&& other) noexcept + : m_arena(std::move(other.m_arena)) {} + + template + arena_allocator& operator=(arena_allocator&& other) noexcept { + if (this == &other) return *this; + m_arena = std::move(other.m_arena); + return *this; + } +public: + [[nodiscard]] T* allocate(std::size_t count = 1) { return m_arena->allocate(count); } + + void deallocate(T* ptr, std::size_t count) noexcept {} +public: + template + bool operator==(const arena_allocator& other) const noexcept { + return m_arena == other.m_arena; + } + + template + bool operator!=(const arena_allocator& other) const noexcept { + return m_arena != other.m_arena; + } +private: + arena* m_arena; +}; + } // namespace furlang -#endif // FURLANG_ARENA_HPP \ No newline at end of file +#endif // FURLANG_ARENA_HPP diff --git a/furlang/include/furlang/ir/module.hpp b/furlang/include/furlang/ir/module.hpp index 90e2a3f..2940bb8 100644 --- a/furlang/include/furlang/ir/module.hpp +++ b/furlang/include/furlang/ir/module.hpp @@ -12,11 +12,11 @@ namespace ir { /** * @brief IR module */ -class module { +class mod { public: using value_type = std::unique_ptr; /**< Value type. */ public: - module() = default; + mod() = default; public: /** * @brief Pushes and returns a new IR function. @@ -49,4 +49,4 @@ private: } // namespace ir } // namespace furlang -#endif // FURLANG_IR_MODULE_HPP \ No newline at end of file +#endif // FURLANG_IR_MODULE_HPP diff --git a/furvm/include/furvm/constant.hpp b/furvm/include/furvm/constant.hpp index 419aba5..70ed4b7 100644 --- a/furvm/include/furvm/constant.hpp +++ b/furvm/include/furvm/constant.hpp @@ -1,49 +1,13 @@ #ifndef FURVM_CONSTANT_HPP #define FURVM_CONSTANT_HPP +#include "furvm/exceptions.hpp" #include "furvm/fwd.hpp" -#include #include namespace furvm { -/** - * @brief Bad constant access exception. - */ -class bad_constant_access : public std::exception { -public: - bad_constant_access() = default; - ~bad_constant_access() override = default; - - /** - * @brief Move constructor. - */ - bad_constant_access(bad_constant_access&&) noexcept = default; - - /** - * @brief Move constructor. - */ - bad_constant_access& operator=(bad_constant_access&&) noexcept = default; - - /** - * @brief Copy constructor. - */ - bad_constant_access(const bad_constant_access&) = default; - - /** - * @brief Copy constructor. - */ - bad_constant_access& operator=(const bad_constant_access&) = default; -public: - /** - * @brief Returns a C-style string describing the cause of the error. - * - * @return The cause of the error. - */ - const char* what() const noexcept override { return "bad constant access"; } -}; - enum class constant_t : std::uint8_t { String, /**< String constant. */ }; @@ -109,4 +73,4 @@ private: } // namespace furvm -#endif // FURVM_CONSTANT_HPP \ No newline at end of file +#endif // FURVM_CONSTANT_HPP diff --git a/furvm/include/furvm/context.hpp b/furvm/include/furvm/context.hpp index f9ae6ae..73bb4ce 100644 --- a/furvm/include/furvm/context.hpp +++ b/furvm/include/furvm/context.hpp @@ -2,21 +2,26 @@ #define FURVM_CONTEXT_HPP #include "furlang/arena.hpp" +#include "furvm/executor.hpp" #include "furvm/fwd.hpp" -#include "furvm/module.hpp" +#include "furvm/handle.hpp" +#include "furvm/module.hpp" // IWYU pragma: keep +#include "furvm/thing.hpp" // IWYU pragma: keep -#include -#include -#include +#include +#include namespace furvm { class context { public: friend class executor; - friend class thing; public: + /** + * @brief Constructs a context. + */ context(); + ~context() = default; /** @@ -33,81 +38,143 @@ public: context& operator=(const context&) = delete; public: /** - * @brief Adds a module to this context. + * @brief Emplaces a module in the context. * - * @param args Arguments to forward to module's constructor. - * @return An index to the emplaced module. + * @param args Arguments forwarded to the module constructor. + * @return The emplaced module. */ - template >> - constexpr const auto& emplace(Args&&... args) { - module_handle id = static_cast(m_modules.size()); - return m_modules.emplace_back(std::make_unique(id, std::forward(args)...)); + template + auto emplace_module(Args&&... args) { + return m_modules.emplace(std::forward(args)...); } /** - * @brief Erases a module from this context. + * @brief Returns a module from the context. * - * @param index Index to the module. - * @return Old value. + * @param args Module's id. + * @return A handle to the module. */ - mod_p erase(module_handle index) { - if (index >= m_modules.size()) return nullptr; - return std::move(m_modules[index]); + template + auto module_at(Args&&... args) { + return m_modules.at(std::forward(args)...); } /** - * @brief Returns a module of this context. + * @brief Returns a module from the context. * - * @param index Position of the module. - * @return The module. + * @param args Module's id. + * @return A handle to the module. */ - constexpr mod_p& operator[](module_handle index) { return m_modules[index]; } + template + auto module_at(Args&&... args) const { + return m_modules.at(std::forward(args)...); + } /** - * @brief Returns a module of this context. + * @brief Erases a module from the context. * - * @param index Position of the module. - * @return The module. + * @param args Module's id. */ - constexpr const mod_p& operator[](module_handle index) const { return m_modules[index]; } - - /** - * @brief Returns a module of this context. - * - * @param index Position of the module. - * @return The module. - */ - constexpr mod_p& at(module_handle index) { return m_modules.at(index); } - - /** - * @brief Returns a module of this context. - * - * @param index Position of the module. - * @return The module. - */ - constexpr const mod_p& at(module_handle index) const { return m_modules.at(index); } - - /** - * @brief Returns how many does this context have modules. - * - * @return The module count. - */ - constexpr size_t size() const { return m_modules.size(); } + template + void erase_module(Args&&... args) { + m_modules.erase(std::forward(args)...); + } public: /** - * @brief Removes unreferenced things from the thing list. + * @brief Emplaces an executor in the context. + * + * @param args Arguments forwarded to executor's constructor. + * @return A handle to the emplaced executor. */ - void collect(); -private: - std::vector m_modules; - std::vector m_things; - std::vector m_executors; + template + auto emplace_executor(Args&&... args) { + return m_executors.emplace_back(std::forward(args)...); + } - std::queue m_deadThings; - std::vector m_deadThingData; - furlang::arena m_thingArena; + /** + * @brief Returns an executor from the context. + * + * @param args Id of the executor. + * @return A handle to the executor. + */ + template + auto executor_at(Args&&... args) { + return m_executors.at(std::forward(args)...); + } + + /** + * @brief Returns an executor from the context. + * + * @param args Id of the executor. + * @return A handle to the executor. + */ + template + auto executor_at(Args&&... args) const { + return m_executors.at(std::forward(args)...); + } + + /** + * @brief Erases an executor from the context. + * + * @param args Id of the executor. + */ + template + void erase_executor(Args&&... args) { + m_executors.erase(std::forward(args)...); + } +public: + template + auto emplace_thing(Args&&... args) { + return m_things.emplace_back(std::forward(args)...); + } + + /** + * @brief Returns a thing from the context. + * + * @param args Id of the thing. + * @return A handle to the thing. + */ + template + auto thing_at(Args&&... args) { + return m_things.at(std::forward(args)...); + } + + /** + * @brief Returns a thing from the context. + * + * @param args Id of the thing. + * @return A handle to the thing. + */ + template + auto thing_at(Args&&... args) const { + return m_things.at(std::forward(args)...); + } + + /** + * @brief Erases a thing from the context. + * + * @param args Id of the thing. + */ + template + void erase_thing(Args&&... args) { + m_things.erase(std::forward(args)...); + } + + /** + * @brief Returns context's thing allocator. + * + * @return The thing allocator. + */ + thing_allocator thing_alloc() const { return m_thingAllocator; } +private: + handle_container m_modules; + handle_container m_things; + handle_container m_executors; + + furlang::arena m_thingArena; + thing_allocator m_thingAllocator; }; } // namespace furvm -#endif // FURVM_CONTEXT_HPP \ No newline at end of file +#endif // FURVM_CONTEXT_HPP diff --git a/furvm/include/furvm/detail/handle.hpp b/furvm/include/furvm/detail/handle.hpp new file mode 100644 index 0000000..25f5641 --- /dev/null +++ b/furvm/include/furvm/detail/handle.hpp @@ -0,0 +1,33 @@ +#ifndef FURVM_DETAIL_HANDLE_HPP +#define FURVM_DETAIL_HANDLE_HPP + +#include + +namespace furvm { +namespace detail { + +/** + * @brief Default specialization for header_has_refcount type trait. + */ +template +struct header_has_refcount : std::false_type {}; + +/** + * @brief Specialization for header_has_refcount type trait. + */ +template +struct header_has_refcount().acquire()), + decltype(std::declval().release()), + decltype(std::declval().reference_count())>> : std::true_type {}; + +/** + * @brief An alias for header_has_refcount's value. + */ +template +static constexpr auto header_has_refcount_v = header_has_refcount
::value; + +} // namespace detail +} // namespace furvm + +#endif // FURVM_DETAIL_HANDLE_HPP diff --git a/furvm/include/furvm/detail/serialization.hpp b/furvm/include/furvm/detail/serialization.hpp new file mode 100644 index 0000000..a406b1f --- /dev/null +++ b/furvm/include/furvm/detail/serialization.hpp @@ -0,0 +1,79 @@ +#ifndef FURVM_DETAIL_SERIALIZATION_HPP +#define FURVM_DETAIL_SERIALIZATION_HPP + +#include +#include +#include + +namespace furvm { +namespace detail { + +/** + * @brief Serializes an integer. + * + * @param os Output stream. + * @param value Integer. + * @return The output stream. + */ +std::ostream& serialize(std::ostream& os, std::int8_t value); +std::ostream& serialize(std::ostream& os, std::int16_t value); + +/** + * @brief Serializes an integer. + * + * @param os Output stream. + * @param value Integer. + * @return The output stream. + */ +std::ostream& serialize(std::ostream& os, std::int32_t value); +std::ostream& serialize(std::ostream& os, std::int64_t value); + +/** + * @brief Serializes an integer. + * + * @param os Output stream. + * @param value Integer. + * @return The output stream. + */ +std::ostream& serialize(std::ostream& os, std::uint8_t value); + +/** + * @brief Serializes an integer. + * + * @param os Output stream. + * @param value Integer. + * @return The output stream. + */ +std::ostream& serialize(std::ostream& os, std::uint16_t value); + +/** + * @brief Serializes an integer. + * + * @param os Output stream. + * @param value Integer. + * @return The output stream. + */ +std::ostream& serialize(std::ostream& os, std::uint32_t value); + +/** + * @brief Serializes an integer. + * + * @param os Output stream. + * @param value Integer. + * @return The output stream. + */ +std::ostream& serialize(std::ostream& os, std::uint64_t value); + +/** + * @brief Serializes a string. + * + * @param os Output stream. + * @param value String. + * @return The output stream. + */ +std::ostream& serialize(std::ostream& os, const std::string& value); + +} // namespace detail +} // namespace furvm + +#endif // FURVM_DETAIL_SERIALIZATION_HPP diff --git a/furvm/include/furvm/exceptions.hpp b/furvm/include/furvm/exceptions.hpp index e9a0c58..87efc79 100644 --- a/furvm/include/furvm/exceptions.hpp +++ b/furvm/include/furvm/exceptions.hpp @@ -5,6 +5,75 @@ namespace furvm { +class bad_thing_access : public std::exception { +public: + bad_thing_access() = default; + ~bad_thing_access() override = default; + + /** + * @brief Move constructor. + */ + bad_thing_access(bad_thing_access&&) noexcept = default; + + /** + * @brief Move constructor. + */ + bad_thing_access& operator=(bad_thing_access&&) noexcept = default; + + /** + * @brief Copy constructor. + */ + bad_thing_access(const bad_thing_access&) = default; + + /** + * @brief Copy constructor. + */ + bad_thing_access& operator=(const bad_thing_access&) = default; +public: + /** + * @brief Returns a C-style string describing the cause of the error. + * + * @return The cause of the error. + */ + const char* what() const noexcept override { return "bad thing access"; } +}; + +/** + * @brief Bad constant access exception. + */ +class bad_constant_access : public std::exception { +public: + bad_constant_access() = default; + ~bad_constant_access() override = default; + + /** + * @brief Move constructor. + */ + bad_constant_access(bad_constant_access&&) noexcept = default; + + /** + * @brief Move constructor. + */ + bad_constant_access& operator=(bad_constant_access&&) noexcept = default; + + /** + * @brief Copy constructor. + */ + bad_constant_access(const bad_constant_access&) = default; + + /** + * @brief Copy constructor. + */ + bad_constant_access& operator=(const bad_constant_access&) = default; +public: + /** + * @brief Returns a C-style string describing the cause of the error. + * + * @return The cause of the error. + */ + const char* what() const noexcept override { return "bad constant access"; } +}; + class stack_underflow : public std::exception { public: stack_underflow() = default; @@ -40,4 +109,4 @@ public: } // namespace furvm -#endif // FURVM_EXCEPTIONS_HPP \ No newline at end of file +#endif // FURVM_EXCEPTIONS_HPP diff --git a/furvm/include/furvm/executor.hpp b/furvm/include/furvm/executor.hpp index a2c264c..cb6a8ac 100644 --- a/furvm/include/furvm/executor.hpp +++ b/furvm/include/furvm/executor.hpp @@ -2,8 +2,11 @@ #define FURVM_EXECUTOR_HPP #include "furvm/fwd.hpp" +#include "furvm/module.hpp" // IWYU pragma: keep +#include "furvm/thing.hpp" // IWYU pragma: keep #include +#include #include namespace furvm { @@ -26,15 +29,6 @@ static inline executor_flags operator~(executor_flags flags) { } class executor { -private: - /** - * @brief A private token for the private constructor. - * - * Also `egzekutor` in Polish translates to `executor` I think. - */ - struct egzekutor { - explicit egzekutor() = default; - }; public: /** * @brief Executor frame. @@ -42,22 +36,22 @@ public: * Call frame. */ struct frame { - mod_p mod; /**< Shared pointer to a module with the bytecode. */ + mod_h mod; /**< Handle to the frame's module. */ std::size_t position; /**< Cursor to a current instruction in the bytecode. */ std::size_t stackBase; /**< Snapshot of the stack size before this frame. */ - std::vector variables; /**< Frame variables. */ + std::vector variables; /**< Frame variables. */ }; public: /** - * @brief Private constructor. + * @brief Returns a new executor. * - * @param id - * @param context + * @param context Context. */ - executor(egzekutor, executor_handle id, const context_p& context); + executor(const context_p& context) + : m_context(context) {} - ~executor(); + ~executor() = default; /** * @brief Move constructor. @@ -69,24 +63,16 @@ public: */ executor& operator=(executor&&) noexcept = default; - executor(const executor&) = delete; - executor& operator=(const executor&) = delete; -public: /** - * @brief Returns a new executor. - * - * @param context Furvm context. - * @return Shared pointer to the new executor. + * @brief Copy constructor. */ - static executor_p create(const context_p& context); -public: - /** - * @brief Returns an id of this executor. - * - * @return The id. - */ - executor_handle id() const { return m_id; } + executor(const executor&) = default; + /** + * @brief Copy constructor. + */ + executor& operator=(const executor&) = default; +public: /** * @brief Returns flags of this executor. * @@ -97,9 +83,10 @@ public: /** * @brief Pushes a new frame. * - * @param function Function. + * @param mod Handle to the frame's module. + * @param function Frame's function. */ - void push_frame(const function_p& function); + void push_frame(const mod_h& mod, function function); /** * @brief Pops the top frame. @@ -116,68 +103,73 @@ public: frame frame() const; public: /** - * @brief Pushes a thing onto the stack. + * @brief Pushes a thing handle onto the stack. * - * @param thing The thing to push. + * @param handle Thing handle. */ - void push_thing(const thing_p& thing); + template + void push_thing(HandleFwd&& handle) { + m_stack.emplace(std::forward(handle)); + } /** * @brief Pushes a thing onto the stack. * - * @param thing The thing to push. + * Registers a new thing and pushes its handle onto the stack. + * + * @param thing Thing. + * @return The pushed handle. */ - void push_thing(thing_p&& thing); + thing_h push_thing(class thing<>&& thing); /** * @brief Pops a thing from the stack. * - * @return The popped thing. + * @return A handle to the popped thing. */ - thing_p pop_thing(); + thing_h pop_thing(); /** * @brief Returns the top thing on the stack. * - * @return The thing. + * @return A handle to the top thing. */ - thing_p thing() const; + thing_h thing() const; public: /** - * @brief Stores a thing in a variable. + * @brief Stores a thing in a frame variable. * - * @param variable Variable to store the thing in. - * @param thing Thing to store. + * @param variable Id of the variable in which the handle will be put. + * @param thing Thing handle. */ - void store_thing(variable_t variable, const thing_p& thing); + void store_thing(variable_t variable, const thing_h& thing); /** - * @brief Stores a thing in a variable. + * @brief Stores a thing in a frame variable. * - * @param variable Variable to store the thing in. - * @param thing Thing to store. + * @param variable Id of the variable in which the handle will be put. + * @param thing Thing handle. */ - void store_thing(variable_t variable, thing_p&& thing); + void store_thing(variable_t variable, thing_h&& thing); /** * @brief Returns a thing stored in a variable. * - * @param variable Variable where the thing is stored. - * @return The thing stored in the variable. + * @param variable Id of the variable from which the handle will be fetched. + * @return A handle stored in the variable. */ - thing_p load_thing(variable_t variable) const; + thing_h load_thing(variable_t variable) const; public: /** * @brief Executes next instruction. */ void step(); private: - executor_handle m_id; - executor_flags m_flags{}; // NOLINT(bugprone-invalid-enum-default-initialization) - context_p m_context; + executor_flags m_flags{}; // NOLINT(bugprone-invalid-enum-default-initialization) + context_p m_context; std::stack m_frames; - std::stack m_stack; + std::stack m_stack; }; } // namespace furvm diff --git a/furvm/include/furvm/function.hpp b/furvm/include/furvm/function.hpp index 2b3fb93..89ebd01 100644 --- a/furvm/include/furvm/function.hpp +++ b/furvm/include/furvm/function.hpp @@ -2,10 +2,11 @@ #define FURVM_FUNCTION_HPP #include "furvm/fwd.hpp" -#include "furvm/module.hpp" // IWYU pragma: keep #include #include +#include +#include namespace furvm { @@ -15,38 +16,55 @@ enum class function_t : std::uint8_t { Import, /**< A function imported from another module. */ }; +/** + * @brief A native function. + */ using native_function = std::function; +/** + * @brief A function import. + */ +struct import_function { + mod_id mod; + function_id function; +}; + class function { -private: - /** - * @brief A private token for the private constructor. - * - * Also `funkcja` in Polish translates to `function` from what I heard. - */ - struct funkcja { - explicit funkcja() = default; - }; public: /** - * @brief Private constructor. + * @brief Constructs a normal function. * - * @param id - * @param position - * @param mod + * @param name Name of the function. + * @param position Offset in bytecode of the function. */ - function(funkcja, function_handle id, std::size_t position, const mod_p& mod); + template + function(Name&& name, bytecode_pos position) + : m_name(std::forward(name)), m_type(function_t::Normal), m_value(position) {} /** - * @brief Private constructor. + * @brief Constructs a native function. * - * @param id - * @param native - * @param mod + * @param name Name of the function. + * @param native Native function. */ - function(funkcja, function_handle id, const native_function& native, const mod_p& mod); + template + function(Name&& name, const native_function& native) + : m_name(std::forward(name)), m_type(function_t::Native), m_value(native) {} - ~function() = default; + /** + * @brief Constructs an import function. + * + * @param name Name of the function. + * @param imp Import function. + */ + template + function(Name&& name, const import_function& imp) + : m_name(std::forward(name)), m_type(function_t::Import), m_value(imp) {} + + /** + * @brief Destructs a function. + */ + ~function(); /** * @brief Move constructor. @@ -58,32 +76,22 @@ public: */ function& operator=(function&&) noexcept; - function(const function&) = delete; - function& operator=(const function&) = delete; -public: /** - * @brief Returns a new function. - * - * @param mod Module. - * @param args Arguments to pass to the function constructor. - * @return The new function. + * @brief Copy constructor. */ - template >> - static function_p create(const mod_p& mod, Args&&... args) { - function_handle id = mod->m_functions.size(); + function(const function&); - auto func = std::make_shared(funkcja{}, id, std::forward(args)..., mod); - mod->m_functions.emplace(mod->m_functions.begin() + id, func); - return std::move(func); - } + /** + * @brief Copy constructor. + */ + function& operator=(const function&); public: /** - * @brief Returns an id of this function. + * @brief Returns a name of this function. * - * @return The id. + * @return The name. */ - constexpr function_handle id() const { return m_id; } + constexpr const std::string& name() const { return m_name; } /** * @brief Returns a type of this function. @@ -91,16 +99,9 @@ public: * @return The type. */ constexpr function_t type() const { return m_type; } - - /** - * @brief Returns a parent module of this function. - * - * @return A shared pointer to the module. - */ - const mod_p& mod() const { return m_module; } public: /** - * @brief Returns a value for normal function. + * @brief Returns normal function's value. * * @return The value. */ @@ -110,7 +111,7 @@ public: } /** - * @brief Returns a value for native function. + * @brief Returns native function's value. * * @return The value. */ @@ -120,36 +121,22 @@ public: } /** - * @brief Returns a module of imported function. + * @brief Returns import function's value. * - * @return A handle to the module. + * @return The name. */ - module_handle imported_module() const { + const import_function& imp() const { if (m_type != function_t::Import) throw std::runtime_error("function type mismatch"); - return m_value.imp.mod; - } - - /** - * @brief Returns a module of imported function. - * - * @return A handle to the module. - */ - function_handle imported_function() const { - if (m_type != function_t::Import) throw std::runtime_error("function type mismatch"); - return m_value.imp.function; + return m_value.imp; } private: - function_handle m_id; - function_t m_type; - mod_p m_module; + std::string m_name; + function_t m_type; union value { std::size_t position = 0; native_function native; - struct { - module_handle mod; - function_handle function; - } imp; + import_function imp; value() = default; @@ -159,8 +146,8 @@ private: value(const native_function& native) : native(native) {} - value(module_handle mod, function_handle function) - : imp({ mod, function }) {} + value(const import_function& imp) + : imp(imp) {} ~value() {} diff --git a/furvm/include/furvm/furvm.hpp b/furvm/include/furvm/furvm.hpp new file mode 100644 index 0000000..b25da42 --- /dev/null +++ b/furvm/include/furvm/furvm.hpp @@ -0,0 +1,12 @@ +#ifndef FURVM_HPP +#define FURVM_HPP + +#include "furvm/context.hpp" // IWYU pragma: export +#include "furvm/executor.hpp" // IWYU pragma: export +#include "furvm/function.hpp" // IWYU pragma: export +#include "furvm/fwd.hpp" // IWYU pragma: export +#include "furvm/handle.hpp" // IWYU pragma: export +#include "furvm/instruction.hpp" // IWYU pragma: export +#include "furvm/thing.hpp" // IWYU pragma: export + +#endif // FURVM_HPP diff --git a/furvm/include/furvm/fwd.hpp b/furvm/include/furvm/fwd.hpp index aaa832a..a0d9c1b 100644 --- a/furvm/include/furvm/fwd.hpp +++ b/furvm/include/furvm/fwd.hpp @@ -4,6 +4,7 @@ #include // IWYU pragma: export #include // IWYU pragma: export #include +#include /** * @brief Furlang's virtual machine. @@ -17,6 +18,40 @@ namespace furvm { */ using byte = std::uint8_t; +/** + * @brief An offset into bytecode. + */ +using bytecode_pos = std::uint64_t; + +/** + * @brief Handle header with reference count. + */ +template +class refcount_header; + +/** + * @brief Generic handle header. + */ +template +class generic_header; + +/** + * @brief Generic furvm object handle. + * + * @tparam Value Type of the handle's value. + * @tparam Header Type of the handle's header. + */ +template +class handle; + +/** + * @brief Container for the handles. + * + * @tparam Handle Type of the container's handle. + */ +template +class handle_container; + // constant.hpp /** @@ -68,15 +103,15 @@ enum class function_t : std::uint8_t; */ class function; -/** - * @brief An alias to a function shared pointer. - */ -using function_p = std::shared_ptr; - /** * @brief Furvm function's index. */ -using function_handle = std::uint16_t; +using function_id = std::uint16_t; + +/** + * @brief A handle to a furvm function. + */ +using function_h = handle>; // module.hpp @@ -94,9 +129,14 @@ class mod; using mod_p = std::shared_ptr; /** - * @brief Furvm module's index. + * @brief An alias for a module's identifier. */ -using module_handle = std::uint32_t; +using mod_id = std::string; + +/** + * @brief A handle to a furvm module. + */ +using mod_h = handle>; // thing.hpp @@ -112,23 +152,27 @@ enum class thing_t : std::uint8_t; */ class bad_thing_access; +template +class thing_allocator; + /** * @class thing * @brief Furvm thing. * * A stack element. Think of it like of a value in C++ or I guess a class in java. */ +template