From 70375af0681d41808597c925309921d42059a3ff Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Thu, 10 Sep 2026 21:49:25 +0200 Subject: [PATCH] test(furc/ssa): implement tests for SSA --- furc/CMakeLists.txt | 5 +- furc/test/ssa.cpp | 141 +++++++++++++++++++++++++++++++++++++++++++ furvm/CMakeLists.txt | 2 +- 3 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 furc/test/ssa.cpp diff --git a/furc/CMakeLists.txt b/furc/CMakeLists.txt index 527bb5f..60dc724 100644 --- a/furc/CMakeLists.txt +++ b/furc/CMakeLists.txt @@ -10,7 +10,6 @@ target_link_libraries(furc PRIVATE libfurc) include(GoogleTest) -file(GLOB_RECURSE FURC_TESTS "test/**.cpp") -add_executable(furc_tests ${FURC_TESTS}) +add_executable(furc_tests "test/ssa.cpp") target_link_libraries(furc_tests PRIVATE libfurc GTest::gtest_main) -# gtest_discover_tests(furc_tests) +gtest_discover_tests(furc_tests) diff --git a/furc/test/ssa.cpp b/furc/test/ssa.cpp new file mode 100644 index 0000000..ba4d878 --- /dev/null +++ b/furc/test/ssa.cpp @@ -0,0 +1,141 @@ +// NOLINTBEGIN(readability-identifier-naming) + +#include "gtest/gtest.h" +#include +#include +#include +#include + +namespace furc::test { + +class SsaCfg : public testing::Test { +public: + SsaCfg() + : p_context(&p_function) {} +protected: + void compute() { ssa::compute_cfg(p_function.blocks, p_cfgBlocks); } +protected: + std::vector p_cfgBlocks; + + ir_function p_function; + ir_context p_context; +}; + +TEST_F(SsaCfg, Diamond) { + ir_operand null = { ir_operand::Integer, static_cast(0) }; + + // Entry + p_context.terminate(null, 1, 2); + p_context.new_next(); + // A + p_context.terminate(3); + p_context.new_next(); + // B + p_context.terminate(3); + p_context.new_next(); + // C + p_context.terminate(4); + p_context.new_next(); + // Exit + p_context.terminate(); + + compute(); + + EXPECT_EQ(p_cfgBlocks[0].sucs.size(), 2); + EXPECT_NE(p_cfgBlocks[0].sucs.find(1), p_cfgBlocks[0].sucs.end()); + EXPECT_NE(p_cfgBlocks[0].sucs.find(2), p_cfgBlocks[0].sucs.end()); + EXPECT_EQ(p_cfgBlocks[3].preds, p_cfgBlocks[0].sucs); + EXPECT_EQ(p_cfgBlocks[3].sucs.size(), 1); + EXPECT_NE(p_cfgBlocks[3].sucs.find(4), p_cfgBlocks[3].sucs.end()); +} + +class SsaDom : public testing::Test { +public: + SsaDom() + : p_context(&p_function) {} +protected: + void compute() { + ssa::compute_cfg(p_function.blocks, p_cfgBlocks); + p_ssaRegisters.resize(p_function.regCount); + ssa::collect_registers(p_function.blocks, p_ssaRegisters, p_ssaGlobals); + + std::vector order; + ssa::compute_rpo(p_cfgBlocks, p_ssaBlocks, order); + + ssa::build_dtree(p_cfgBlocks, p_ssaBlocks, order); + ssa::compute_dfrontiers(p_cfgBlocks, p_ssaBlocks); + + ssa::ssaification(p_function.blocks, p_cfgBlocks, p_ssaBlocks, p_ssaRegisters, p_ssaGlobals); + ssa::rename(p_function.blocks, p_function.regCount, p_cfgBlocks, p_ssaBlocks, order); + } +protected: + std::vector p_cfgBlocks; + std::vector p_ssaBlocks; + std::vector p_ssaRegisters; + std::unordered_set p_ssaGlobals; + + ir_function p_function; + ir_context p_context; +}; + +TEST_F(SsaDom, Diamond) { + ir_operand null = { ir_operand::Integer, static_cast(0) }; + + // Entry + p_context.terminate(null, 1, 2); + p_context.new_next(); + // A + p_context.add_instr(ir_instruction{ ir_instruction::Move, + ir_operand{ ir_operand::Register, static_cast(0) }, + { null } }); + p_context.terminate(3); + p_context.new_next(); + // B + p_context.add_instr(ir_instruction{ ir_instruction::Move, + ir_operand{ ir_operand::Register, static_cast(0) }, + { null } }); + p_context.terminate(3); + p_context.new_next(); + // C + p_context.add_instr(ir_instruction{ ir_instruction::Add, + ir_operand{ ir_operand::Register, static_cast(0) }, + { ir_operand{ ir_operand::Register, static_cast(0) } } }); + p_context.new_next(); + // Exit + p_context.terminate(); + + p_function.regCount = 1; + + compute(); + + EXPECT_TRUE(p_ssaBlocks[0].df.empty()); + + EXPECT_EQ(p_ssaBlocks[1].idom, 0); + EXPECT_EQ(p_ssaBlocks[1].df.size(), 1); + EXPECT_NE(p_ssaBlocks[1].df.find(3), p_ssaBlocks[1].df.end()); + + EXPECT_EQ(p_ssaBlocks[2].idom, 0); + EXPECT_EQ(p_ssaBlocks[2].df.size(), 1); + EXPECT_NE(p_ssaBlocks[2].df.find(3), p_ssaBlocks[2].df.end()); + + EXPECT_EQ(p_ssaBlocks[3].idom, 0); + EXPECT_TRUE(p_ssaBlocks[3].df.empty()); + + EXPECT_EQ(p_ssaBlocks[4].idom, 3); + EXPECT_TRUE(p_ssaBlocks[4].df.empty()); + + // Renaming + EXPECT_EQ(p_function.blocks[1].instructions.front(), + (ir_instruction{ ir_instruction::Move, ir_operand::reg(0, 3), { null } })); + EXPECT_EQ(p_function.blocks[2].instructions.front(), + (ir_instruction{ ir_instruction::Move, ir_operand::reg(0, 2), { null } })); + EXPECT_EQ(p_function.blocks[3].instructions.front(), + (ir_instruction{ ir_instruction::Phi, + ir_operand::reg(0, 0), + { ir_operand{ ir_operand::PhiPair, ir_operand::value_u::register_s{ 0, 2 }, 2 }, + ir_operand{ ir_operand::PhiPair, ir_operand::value_u::register_s{ 0, 3 }, 1 } } })); +} + +} // namespace furc::test + +// NOLINTEND(readability-identifier-naming) diff --git a/furvm/CMakeLists.txt b/furvm/CMakeLists.txt index c96afda..58dd70e 100644 --- a/furvm/CMakeLists.txt +++ b/furvm/CMakeLists.txt @@ -13,4 +13,4 @@ include(GoogleTest) file(GLOB_RECURSE FURVM_TESTS "test/**.cpp") add_executable(furvm_tests ${FURVM_TESTS}) target_link_libraries(furvm_tests PRIVATE libfurvm GTest::gtest_main) -gtest_discover_tests(furvm_tests) \ No newline at end of file +gtest_discover_tests(furvm_tests)