Compare commits

...

2 Commits

Author SHA1 Message Date
CHatingPython 3b98f77c08 chore: flat out the file structure 2026-09-11 18:48:16 +02:00
CHatingPython 959d0a7773 feat(furc): implement SSA destruction 2026-09-11 18:12:54 +02:00
78 changed files with 279 additions and 54 deletions
+2 -2
View File
@@ -1,3 +1,3 @@
[submodule "furdb/deps/isocline"] [submodule "deps/isocline"]
path = furdb/deps/isocline path = deps/isocline
url = https://github.com/daanx/isocline url = https://github.com/daanx/isocline
+10 -6
View File
@@ -11,12 +11,16 @@ find_package(Doxygen)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(furlang) add_subdirectory(src/furlang)
add_subdirectory(furvm) add_subdirectory(src/furvm)
add_subdirectory(furc) add_subdirectory(src/furc)
add_subdirectory(furas) add_subdirectory(src/furas)
add_subdirectory(disfuras) add_subdirectory(src/disfuras)
add_subdirectory(furdb) add_subdirectory(src/furdb)
# Dependencies
set(IC_USE_CXX ON)
add_subdirectory(${CMAKE_SOURCE_DIR}/deps/isocline)
if(DOXYGEN_FOUND) if(DOXYGEN_FOUND)
set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/docs) set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/docs)
View File
View File
-5
View File
@@ -1,5 +0,0 @@
file(GLOB_RECURSE DISFURAS_SRCS "src/**.cpp")
file(GLOB_RECURSE DISFURAS_HDRS "include/**.hpp")
add_executable(disfuras ${DISFURAS_SRCS} ${DISFURAS_HDRS})
target_include_directories(disfuras PRIVATE include/)
target_link_libraries(disfuras PUBLIC furlang libfurc libfurvm)
-5
View File
@@ -1,5 +0,0 @@
file(GLOB_RECURSE FURAS_SRCS "src/**.cpp")
file(GLOB_RECURSE FURAS_HDRS "include/**.hpp")
add_executable(furas ${FURAS_SRCS} ${FURAS_HDRS})
target_include_directories(furas PUBLIC include/)
target_link_libraries(furas PRIVATE furlang libfurvm)
-8
View File
@@ -1,8 +0,0 @@
set(IC_USE_CXX ON)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/deps/isocline)
file(GLOB_RECURSE FURDB_SRCS "src/**.cpp")
file(GLOB_RECURSE FURDB_HDRS "include/**.hpp")
add_executable(furdb ${FURDB_SRCS} ${FURDB_HDRS})
target_include_directories(furdb PRIVATE include/)
target_link_libraries(furdb PUBLIC furlang libfurc libfurvm isocline)
-4
View File
@@ -1,4 +0,0 @@
file(GLOB_RECURSE FURLANG_SRCS "src/**.cpp")
file(GLOB_RECURSE FURLANG_HDRS "include/**.hpp")
add_library(furlang STATIC ${FURLANG_SRCS} ${FURLANG_HDRS})
target_include_directories(furlang PUBLIC include/)
+16
View File
@@ -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
@@ -34,6 +34,16 @@ struct ir_operand {
struct register_s { struct register_s {
std::uint64_t name : 54; std::uint64_t name : 54;
std::uint64_t ver : 10; 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; } reg;
std::uint16_t variable; std::uint16_t variable;
std::uint16_t global; std::uint16_t global;
+177
View File
@@ -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
@@ -34,18 +34,18 @@ public:
}; };
public: public:
ssa(ir_function& func) { ssa(ir_function& func) {
m_registers.resize(func.regCount); registers.resize(func.regCount);
compute_cfg(func.blocks, m_cfgBlocks); compute_cfg(func.blocks, cfgBlocks);
collect_registers(func.blocks, m_registers, m_globals); collect_registers(func.blocks, registers, globals);
std::vector<std::uint64_t> order; std::vector<std::uint64_t> order;
compute_rpo(m_cfgBlocks, m_ssaBlocks, order); compute_rpo(cfgBlocks, ssaBlocks, order);
build_dtree(m_cfgBlocks, m_ssaBlocks, order); build_dtree(cfgBlocks, ssaBlocks, order);
compute_dfrontiers(m_cfgBlocks, m_ssaBlocks); compute_dfrontiers(cfgBlocks, ssaBlocks);
ssaification(func.blocks, m_cfgBlocks, m_ssaBlocks, m_registers, m_globals); ssaification(func.blocks, cfgBlocks, ssaBlocks, registers, globals);
rename(func.blocks, func.regCount, m_cfgBlocks, m_ssaBlocks, order); rename(func.blocks, func.regCount, cfgBlocks, ssaBlocks, order);
} }
public: public:
static void compute_cfg(const std::vector<ir_basic_block>& irBlocks, std::vector<cfg_block>& cfgBlocks); static void compute_cfg(const std::vector<ir_basic_block>& irBlocks, std::vector<cfg_block>& cfgBlocks);
@@ -89,11 +89,11 @@ private:
const std::vector<cfg_block>& blocks); 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); static std::size_t intersect(std::vector<ssa_block>& m_blocks, std::size_t b1, std::size_t b2);
private: public:
std::vector<cfg_block> m_cfgBlocks; std::vector<cfg_block> cfgBlocks;
std::vector<ssa_block> m_ssaBlocks; std::vector<ssa_block> ssaBlocks;
std::vector<register_info> m_registers; std::vector<register_info> registers;
std::unordered_set<std::uint64_t> m_globals; std::unordered_set<std::uint64_t> globals;
}; };
} // namespace furc } // namespace furc
+4
View File
@@ -0,0 +1,4 @@
file(GLOB_RECURSE DISFURAS_SRCS "${CMAKE_SOURCE_DIR}/src/disfuras/**.cpp")
add_executable(disfuras ${DISFURAS_SRCS})
target_include_directories(disfuras PRIVATE ${CMAKE_SOURCE_DIR}/include/)
target_link_libraries(disfuras PUBLIC furlang libfurc libfurvm)
+5
View File
@@ -0,0 +1,5 @@
file(GLOB_RECURSE FURAS_SRCS "${CMAKE_SOURCE_DIR}/src/furas/**.cpp")
file(GLOB_RECURSE FURAS_HDRS "${CMAKE_SOURCE_DIR}/include/furas/**.hpp")
add_executable(furas ${FURAS_SRCS} ${FURAS_HDRS})
target_include_directories(furas PUBLIC ${CMAKE_SOURCE_DIR}/include/)
target_link_libraries(furas PRIVATE furlang libfurvm)
@@ -1,15 +1,15 @@
file(GLOB_RECURSE FURC_SRCS "src/**.cpp") file(GLOB_RECURSE FURC_SRCS "${CMAKE_SOURCE_DIR}/src/furc/**.cpp")
file(GLOB_RECURSE FURC_HDRS "include/**.hpp") file(GLOB_RECURSE FURC_HDRS "${CMAKE_SOURCE_DIR}/include/furc/**.hpp")
add_library(libfurc ${FURC_SRCS} ${FURC_HDRS}) add_library(libfurc ${FURC_SRCS} ${FURC_HDRS})
target_include_directories(libfurc PUBLIC include/) target_include_directories(libfurc PUBLIC ${CMAKE_SOURCE_DIR}/include/)
target_compile_definitions(libfurc PRIVATE LIBFURC) target_compile_definitions(libfurc PRIVATE LIBFURC)
set_target_properties(libfurc PROPERTIES PREFIX "") set_target_properties(libfurc PROPERTIES PREFIX "")
target_link_libraries(libfurc PUBLIC furlang libfurvm) target_link_libraries(libfurc PUBLIC furlang libfurvm)
add_executable(furc src/main.cpp) add_executable(furc "${CMAKE_SOURCE_DIR}/src/furc/main.cpp")
target_link_libraries(furc PRIVATE libfurc) target_link_libraries(furc PRIVATE libfurc)
include(GoogleTest) include(GoogleTest)
add_executable(furc_tests "test/ssa.cpp") add_executable(furc_tests "${CMAKE_SOURCE_DIR}/test/furc/ssa.cpp")
target_link_libraries(furc_tests PRIVATE libfurc GTest::gtest_main) target_link_libraries(furc_tests PRIVATE libfurc GTest::gtest_main)
gtest_discover_tests(furc_tests) gtest_discover_tests(furc_tests)
+20
View File
@@ -0,0 +1,20 @@
#include "furc/back/furvm/target.hpp"
#include "furvm/function.hpp"
#include <stdexcept>
namespace furc {
furvm::mod furvm_generator::generate(const ir_module& mod) {
if (!mod.variables.empty()) throw std::runtime_error("global variables are not supported for furvm yet");
furvm::mod furvmMod;
for (const auto& func : mod.functions) {
furvmMod.emplace_function(furvm::function_sig{ {}, {} }, furvmMod.bytecode().size());
}
return furvmMod;
}
} // namespace furc
+2
View File
@@ -3,6 +3,7 @@
#include "furc/front/lexer.hpp" #include "furc/front/lexer.hpp"
#include "furc/front/parser.hpp" #include "furc/front/parser.hpp"
#include "furc/middle/ir.hpp" #include "furc/middle/ir.hpp"
#include "furc/middle/reg_gen.hpp"
#include "furc/middle/ssa.hpp" #include "furc/middle/ssa.hpp"
#include "furlang/arena.hpp" #include "furlang/arena.hpp"
@@ -21,6 +22,7 @@ int main(void) {
furc::ir_module irModule = furc::ir_generator::generate(parser.parse()); furc::ir_module irModule = furc::ir_generator::generate(parser.parse());
for (auto& func : irModule.functions) { for (auto& func : irModule.functions) {
furc::ssa ssa(*func); furc::ssa ssa(*func);
furc::reg_gen gen(*func, ssa);
} }
return 0; return 0;
+5
View File
@@ -0,0 +1,5 @@
file(GLOB_RECURSE FURDB_SRCS "${CMAKE_SOURCE_DIR}/src/furdb/**.cpp")
file(GLOB_RECURSE FURDB_HDRS "${CMAKE_SOURCE_DIR}/include/furdb/**.hpp")
add_executable(furdb ${FURDB_SRCS} ${FURDB_HDRS})
target_include_directories(furdb PRIVATE ${CMAKE_SOURCE_DIR}/include/)
target_link_libraries(furdb PUBLIC furlang libfurc libfurvm isocline)
+4
View File
@@ -0,0 +1,4 @@
file(GLOB_RECURSE FURLANG_SRCS "${CMAKE_SOURCE_DIR}/src/furlang/**.cpp")
file(GLOB_RECURSE FURLANG_HDRS "${CMAKE_SOURCE_DIR}/include/furlang/**.hpp")
add_library(furlang STATIC ${FURLANG_SRCS} ${FURLANG_HDRS})
target_include_directories(furlang PUBLIC ${CMAKE_SOURCE_DIR}/include/)
@@ -1,16 +1,16 @@
file(GLOB_RECURSE FURVM_SRCS "src/**.cpp") file(GLOB_RECURSE FURVM_SRCS "${CMAKE_SOURCE_DIR}/src/furvm/**.cpp")
file(GLOB_RECURSE FURVM_HDRS "include/**.hpp") file(GLOB_RECURSE FURVM_HDRS "${CMAKE_SOURCE_DIR}/include/furvm/**.hpp")
add_library(libfurvm ${FURVM_SRCS} ${FURVM_HDRS}) add_library(libfurvm ${FURVM_SRCS} ${FURVM_HDRS})
target_include_directories(libfurvm PUBLIC include/) target_include_directories(libfurvm PUBLIC ${CMAKE_SOURCE_DIR}/include/)
target_compile_definitions(libfurvm PRIVATE LIBFURVM) target_compile_definitions(libfurvm PRIVATE LIBFURVM)
set_target_properties(libfurvm PROPERTIES PREFIX "") set_target_properties(libfurvm PROPERTIES PREFIX "")
target_link_libraries(libfurvm PUBLIC furlang) target_link_libraries(libfurvm PUBLIC furlang)
add_executable(furvm src/main.cpp) add_executable(furvm "${CMAKE_SOURCE_DIR}/src/furvm/main.cpp")
target_link_libraries(furvm PRIVATE libfurvm) target_link_libraries(furvm PRIVATE libfurvm)
include(GoogleTest) include(GoogleTest)
file(GLOB_RECURSE FURVM_TESTS "test/**.cpp") file(GLOB_RECURSE FURVM_TESTS "${CMAKE_SOURCE_DIR}/test/furvm/**.cpp")
add_executable(furvm_tests ${FURVM_TESTS}) add_executable(furvm_tests ${FURVM_TESTS})
target_link_libraries(furvm_tests PRIVATE libfurvm GTest::gtest_main) target_link_libraries(furvm_tests PRIVATE libfurvm GTest::gtest_main)
gtest_discover_tests(furvm_tests) gtest_discover_tests(furvm_tests)