diff --git a/src/CodeGenerator.cpp b/src/CodeGenerator.cpp index fe95f9e..f235289 100644 --- a/src/CodeGenerator.cpp +++ b/src/CodeGenerator.cpp @@ -86,7 +86,7 @@ void CodeGenerator::switch_append_fde( ostringstream case_oss; gen_of_row_content(fde.rows[fde_row_id], case_oss); - sw_case.code = case_oss.str(); + sw_case.content.code = case_oss.str(); sw.cases.push_back(sw_case); } diff --git a/src/FactoredSwitchCompiler.cpp b/src/FactoredSwitchCompiler.cpp new file mode 100644 index 0000000..bf57102 --- /dev/null +++ b/src/FactoredSwitchCompiler.cpp @@ -0,0 +1,94 @@ +#include "FactoredSwitchCompiler.hpp" + +#include +#include +using namespace std; + +FactoredSwitchCompiler::FactoredSwitchCompiler(int indent): + AbstractSwitchCompiler(indent), cur_label_id(0) +{ +} + +void FactoredSwitchCompiler::to_stream( + std::ostream& os, const SwitchStatement& sw) +{ + JumpPointMap jump_points; + + gen_binsearch_tree(os, jump_points, sw.switch_var, + sw.cases.begin(), sw.cases.end()); + + os << indent_str(sw.default_case) << "\n" + << indent() << "/* ===== LABELS ============================== */\n\n"; + + gen_jump_points_code(os, jump_points); +} + +FactoredSwitchCompiler::FactorJumpPoint +FactoredSwitchCompiler::get_jump_point( + FactoredSwitchCompiler::JumpPointMap& jump_map, + const SwitchStatement::SwitchCaseContent& sw_case) +{ +#ifdef STATS + stats.refer_count++; +#endif//STATS + + auto pregen = jump_map.find(sw_case); + if(pregen != jump_map.end()) // Was previously generated + return pregen->second; + +#ifdef STATS + stats.generated_count++; +#endif//STATS + + // Wasn't generated previously -- we'll generate it here + size_t label_id = cur_label_id++; + ostringstream label_ss; + label_ss << "_factor_" << label_id; + FactorJumpPoint label_name = label_ss.str(); + + jump_map.insert(make_pair(sw_case, label_name)); + return label_name; +} + +void FactoredSwitchCompiler::gen_jump_points_code(std::ostream& os, + const FactoredSwitchCompiler::JumpPointMap& jump_map) +{ + for(const auto& block: jump_map) { + os << indent() << block.second << ":\n" + << indent_str(block.first.code) << "\n\n"; + } + os << indent() << "assert(0);\n"; +} + +void FactoredSwitchCompiler::gen_binsearch_tree( + std::ostream& os, + FactoredSwitchCompiler::JumpPointMap& jump_map, + const std::string& sw_var, + const FactoredSwitchCompiler::case_iterator_t& begin, + const FactoredSwitchCompiler::case_iterator_t& end) +{ + size_t iter_delta = end - begin; + if(iter_delta == 0) + os << indent() << "assert(0);\n"; + else if(iter_delta == 1) { + FactorJumpPoint jump_point = get_jump_point( + jump_map, begin->content); + os << indent() << "// IP=0x" << hex << begin->low_bound + << " ... 0x" << begin->high_bound << dec << "\n" + << indent() << "goto " << jump_point << ";\n"; + } + else { + const case_iterator_t mid = begin + iter_delta / 2; + + os << indent() << "if(" << sw_var << " < 0x" + << hex << mid->low_bound << dec << ") {\n"; + indent_count++; + gen_binsearch_tree(os, jump_map, sw_var, begin, mid); + indent_count--; + os << indent() << "} else {\n"; + indent_count++; + gen_binsearch_tree(os, jump_map, sw_var, mid, end); + indent_count--; + os << indent() << "}\n"; + } +} diff --git a/src/FactoredSwitchCompiler.hpp b/src/FactoredSwitchCompiler.hpp new file mode 100644 index 0000000..b86dd5d --- /dev/null +++ b/src/FactoredSwitchCompiler.hpp @@ -0,0 +1,49 @@ +/** A switch generator that tries to factor out most of the redundancy between + * switch blocks, generating manually a switch-like template */ + +#pragma once + +#include "SwitchStatement.hpp" +#include + +class FactoredSwitchCompiler: public AbstractSwitchCompiler { + public: +#ifdef STATS + struct Stats { + Stats(): generated_count(0), refer_count(0) {} + int generated_count, refer_count; + }; + + const Stats& get_stats() const { return stats; } +#endif + FactoredSwitchCompiler(int indent=0); + + private: + typedef std::string FactorJumpPoint; + typedef std::map + JumpPointMap; + typedef std::vector::const_iterator + case_iterator_t; + + private: + virtual void to_stream(std::ostream& os, const SwitchStatement& sw); + + FactorJumpPoint get_jump_point(JumpPointMap& jump_map, + const SwitchStatement::SwitchCaseContent& sw_case); + + void gen_jump_points_code(std::ostream& os, + const JumpPointMap& jump_map); + + void gen_binsearch_tree( + std::ostream& os, + JumpPointMap& jump_map, + const std::string& sw_var, + const case_iterator_t& begin, + const case_iterator_t& end); + + size_t cur_label_id; + +#ifdef STATS + Stats stats; +#endif//STATS +}; diff --git a/src/Makefile b/src/Makefile index 24e0fb1..d6e93bd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,7 @@ CXX=g++ CXXLOCS?=-L. -I. -CXXFLAGS=$(CXXLOCS) -Wall -Wextra -std=c++14 -O2 -g +CXXFL?= +CXXFLAGS=$(CXXLOCS) -Wall -Wextra -std=c++14 -O2 -g $(CXXFL) CXXLIBS=-lelf -ldwarf -ldwarfpp -lsrk31c++ -lc++fileno TARGET=dwarf-assembly @@ -14,6 +15,7 @@ OBJS=\ ConseqEquivFilter.o \ SwitchStatement.o \ NativeSwitchCompiler.o \ + FactoredSwitchCompiler.o \ settings.o \ main.o diff --git a/src/NativeSwitchCompiler.cpp b/src/NativeSwitchCompiler.cpp index d46f94e..c8f5d5d 100644 --- a/src/NativeSwitchCompiler.cpp +++ b/src/NativeSwitchCompiler.cpp @@ -16,7 +16,7 @@ void NativeSwitchCompiler::to_stream(ostream& os, const SwitchStatement& sw) { << hex << cur_case.low_bound << " ... 0x" << cur_case.high_bound << dec << ":\n"; indent_count++; - os << indent_str(cur_case.code); + os << indent_str(cur_case.content.code); indent_count--; } diff --git a/src/NativeSwitchCompiler.hpp b/src/NativeSwitchCompiler.hpp index c726ddd..bd28fb1 100644 --- a/src/NativeSwitchCompiler.hpp +++ b/src/NativeSwitchCompiler.hpp @@ -1,5 +1,7 @@ /** Compiles a SwitchStatement to a native C switch */ +#pragma once + #include "SwitchStatement.hpp" class NativeSwitchCompiler: public AbstractSwitchCompiler { diff --git a/src/SwitchStatement.hpp b/src/SwitchStatement.hpp index 68029e8..166b4d5 100644 --- a/src/SwitchStatement.hpp +++ b/src/SwitchStatement.hpp @@ -9,9 +9,19 @@ #include struct SwitchStatement { + struct SwitchCaseContent { + std::string code; + + bool operator==(const SwitchCaseContent& oth) const { + return code == oth.code; + } + bool operator<(const SwitchCaseContent& oth) const { + return code < oth.code; + } + }; struct SwitchCase { uintptr_t low_bound, high_bound; - std::string code; + SwitchCaseContent content; }; std::string switch_var; diff --git a/src/main.cpp b/src/main.cpp index 4bf58be..cef3866 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,7 @@ #include "CodeGenerator.hpp" #include "SwitchStatement.hpp" #include "NativeSwitchCompiler.hpp" +#include "FactoredSwitchCompiler.hpp" #include "PcHoleFiller.hpp" #include "ConseqEquivFilter.hpp" @@ -101,6 +102,7 @@ int main(int argc, char** argv) { ConseqEquivFilter()( parsed_dwarf)); + FactoredSwitchCompiler* sw_compiler = new FactoredSwitchCompiler(1); CodeGenerator code_gen( filtered_dwarf, cout, @@ -109,8 +111,22 @@ int main(int argc, char** argv) { ss << "_fde_" << fde.beg_ip; return ss.str(); }, - new NativeSwitchCompiler()); + //new NativeSwitchCompiler() + sw_compiler + ); + code_gen.generate(); +#ifdef STATS + cerr << "Factoring stats:\nRefers: " + << sw_compiler->get_stats().refer_count + << "\nGenerated: " + << sw_compiler->get_stats().generated_count + << "\nAvoided: " + << sw_compiler->get_stats().refer_count + - sw_compiler->get_stats().generated_count + << "\n"; +#endif + return 0; }