https://gcc.gnu.org/g:a7cb3a2146967afb3ea16590347e7197090cd18a
commit a7cb3a2146967afb3ea16590347e7197090cd18a Author: Ondřej Machota <ondrejmach...@gmail.com> Date: Fri Feb 21 14:11:36 2025 +0100 rtl-ssa: dce some prelive conditions Diff: --- gcc/dce.cc | 321 +++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 239 insertions(+), 82 deletions(-) diff --git a/gcc/dce.cc b/gcc/dce.cc index ed3231d91404..909e47b99195 100644 --- a/gcc/dce.cc +++ b/gcc/dce.cc @@ -17,8 +17,10 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ +#include <cassert> #define INCLUDE_ALGORITHM #define INCLUDE_FUNCTIONAL +#define INCLUDE_ARRAY #include "config.h" #include "system.h" #include "coretypes.h" @@ -30,7 +32,7 @@ along with GCC; see the file COPYING3. If not see #include "rtl-ssa.h" #include "memmodel.h" #include "tm_p.h" -#include "emit-rtl.h" /* FIXME: Can go away once crtl is moved to rtl.h. */ +#include "emit-rtl.h" /* FIXME: Can go away once crtl is moved to rtl.h. */ #include "cfgrtl.h" #include "cfgbuild.h" #include "cfgcleanup.h" @@ -39,6 +41,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-pass.h" #include "dbgcnt.h" #include "rtl-iter.h" +#include <unordered_set> using namespace rtl_ssa; @@ -1303,128 +1306,282 @@ public: } // namespace -bool is_inherently_live(insn_info *insn) { +bool sets_global_register(rtx_insn* insn) { + rtx set = single_set(insn); + if (!set) + return false; + rtx dest = SET_DEST(set); + if (REG_P(dest) && HARD_REGISTER_NUM_P(REGNO(dest)) && global_regs[REGNO(dest)]) { + return true; + } + + return false; } -static void rti_ssa_dce_() { +bool is_prelive(insn_info *insn) +{ + if (insn->is_artificial()) // phis are never prelive + return false; + + /* + * There are a few functions we can use to detect if an instruction is + * inherently live: + * rtlanal.cc: + * bool side_effects_p (const_rtx x); + * bool volatile_insn_p (const_rtx x); + * + * rtlanal.h + * bool has_side_effects (); inside class rtx_properties + * + * dce.cc: + * static bool deletable_insn_p_1(rtx body); uses bool volatile_insn_p (const_rtx x); + * static bool deletable_insn_p(rtx_insn *insn, bool fast, bitmap arg_stores); + * + * Possibly the most accurate way would be to rewrite `static bool + * deletable_insn_p(rtx_insn *insn, bool fast, bitmap arg_stores);` + * + */ + + // Now, we only have to handle rtx insns + assert(insn->is_real()); + auto rtl = insn->rtl(); + + if (!INSN_P(rtl)) // This might be useless + return false; + + rtx pat = PATTERN(rtl); // if we use this instead of rtl, then rtl notes wont be checked + // TODO : join if statements + + if (JUMP_P(rtl)) + return true; + + // We need to describe all possible prelive instructions, a list of all the instructions is inside `rtl.def` + + // Mark set of a global register + if (sets_global_register(rtl)) + return true; + + // Call is inside side_effects_p + if (side_effects_p(rtl) || volatile_refs_p(rtl) || can_throw_internal(rtl)) + return true; + + return false; } static void -rtl_ssa_dce_init () +rtl_ssa_dce_init() { - calculate_dominance_info (CDI_DOMINATORS); - crtl->ssa = new rtl_ssa::function_info (cfun); + calculate_dominance_info(CDI_DOMINATORS); + // here we create ssa form for function + crtl->ssa = new rtl_ssa::function_info(cfun); } static void -rtl_ssa_dce_done () +rtl_ssa_dce_done() { - free_dominance_info (CDI_DOMINATORS); - if (crtl->ssa->perform_pending_updates ()) - cleanup_cfg (0); + free_dominance_info(CDI_DOMINATORS); + if (crtl->ssa->perform_pending_updates()) + cleanup_cfg(0); - delete crtl->ssa; - crtl->ssa = nullptr; + delete crtl->ssa; + crtl->ssa = nullptr; - if (dump_file) - fprintf (dump_file, "\nFinished running rtl_ssa_dce\n\n"); + if (dump_file) + fprintf(dump_file, "\nFinished running rtl_ssa_dce\n\n"); } -static unsigned int -rtl_ssa_dce () +static void +rtl_ssa_dce_mark_live(insn_info *info, vec<insn_info *> &worklist, std::unordered_set<insn_info *> marked) { - rtl_ssa_dce_init (); + int info_uid = info->uid(); + if (dump_file) + { + fprintf(dump_file, " Adding insn %d to worklist\n", info_uid); + } - insn_info *next; - sbitmap marked; - auto_vec<insn_info *> worklist; - for (insn_info *insn = crtl->ssa->first_insn (); insn; insn = next) + marked.emplace(info); + worklist.safe_push(info); +} + +static auto_vec<insn_info *> +rtl_ssa_dce_prelive(std::unordered_set<insn_info *> marked) +{ + insn_info *next; + auto_vec<insn_info *> worklist; + for (insn_info *insn = crtl->ssa->first_insn(); insn; insn = next) + { + if (dump_file) { - next = insn->next_any_insn (); - auto *rtl = insn->rtl(); - /* - I would like to mark visited instruction with something like plf (Pass local flags) as in gimple - - This file contains some useful functions: e.g. marked_insn_p, mark_insn - mark_insn does much more than I want now... - It does quite a useful job. If rtl_insn is a call and it is obsolete, it will find call arguments. - */ - // insn.defs() // UD chain - this is what I want - reach the ancestors\ - // insn.uses() // DU chain - if (is_inherently_live(insn)) { - if (dump_file) - fprintf (dump_file, " Adding insn %d to worklist\n", INSN_UID (insn)); - worklist.safe_push (insn); - bitmap_set_bit(marked, INSN_UID (insn)); - } + fprintf(dump_file, "Insn: %d\n", insn->uid()); + } + next = insn->next_any_insn(); + /* + I would like to mark visited instruction with something like plf (Pass local flags) as in gimple + + This file contains some useful functions: e.g. marked_insn_p, mark_insn + mark_insn does much more than I want now... + It does quite a useful job. If rtl_insn is a call and it is obsolete, it will find call arguments. - //if (insn->can_be_optimized () || insn->is_debug_insn ()) - // if (fwprop_insn (insn, fwprop_addr_p)) - // worklist.safe_push (insn); + insn.defs() // UD chain - this is what I want - reach the ancestors\ + insn.uses() // DU chain + + + * For marking phi nodes, which don't have uid (insn->rtl() is null) by definition, use a dictionary and store their addresses + * Is seems, that insn->uid() is uniq enough + */ + + if (is_prelive(insn)) + { + rtl_ssa_dce_mark_live(insn, worklist, marked); } - while (!worklist.is_empty()) + // if (insn->can_be_optimized () || insn->is_debug_insn ()) + // if (fwprop_insn (insn, fwprop_addr_p)) + // worklist.safe_push (insn); + } + + return worklist; +} + +static void +rtl_ssa_dce_mark(std::unordered_set<insn_info *> marked) +{ + auto worklist = rtl_ssa_dce_prelive(marked); + + if (dump_file) + fprintf(dump_file, "Finished inherently live, marking parents\n"); + while (!worklist.is_empty()) + { + insn_info *insn = worklist.pop(); + def_array defs = insn->defs(); // array - because of phi? + if (dump_file) + fprintf(dump_file, "Looking at: %d, defs: %d\n", insn->uid(), defs.size()); + for (size_t i = 0; i < defs.size(); i++) { - insn_info *insn = worklist.pop(); - def_array defs = insn->defs(); // array - because of phi? - for (size_t i = 0; i < defs.size(); i++) + insn_info *parent_insn = defs[i]->insn(); + int parent_insn_uid = parent_insn->uid(); + // propage that some instruction in chain is live from bottom to top + if (dump_file) + fprintf(dump_file, "Trying to add: %d\n", parent_insn_uid); + // not yet marked + if (!(marked.count(parent_insn) > 0)) { + if (dump_file) + fprintf(dump_file, " Adding insn %d to worklist - mark\n", parent_insn_uid); + worklist.safe_push(parent_insn); + marked.emplace(parent_insn); + } + } - insn_info* parent_insn = defs[i]->insn(); + auto bb = insn->bb(); + } - if (!bitmap_bit_p(INSN_UID (parent_insn))) { - if (dump_file) - fprintf (dump_file, " Adding insn %d to worklist\n", INSN_UID (parent_insn)); - worklist.safe_push(parent_insn); - bitmap_set_bit(marked, INSN_UID (parent_insn)); - } + // TODO : control dependence +} + +static void +rtl_ssa_dce_sweep(std::unordered_set<insn_info *> marked) +{ + insn_info *next; + auto_vec<insn_change> to_delete; + // try to rewrite it with iterator? + // it might be better to iterate over `marked` + // we can get the number of items in the set and create an array of changes + // which will hopefully have constructor for array_slice<insn_info *> + for (insn_info *insn = crtl->ssa->first_insn(); insn; insn = next) + { + if (dump_file) + { + fprintf(dump_file, "Insn: %d\n", insn->uid()); + } + next = insn->next_any_insn(); + if (!(marked.count(insn) > 0)) + { + if (dump_file) + { + fprintf(dump_file, " Sweeping insn %d\n", insn->uid()); } + // rtx_insn* rtl = insn->rtl(); + // How to delete phis? + // if (rtl != nullptr) { + // delete_insn(rtl); + // } + if (insn->rtl()) + { + auto change = insn_change::delete_insn(insn); + // crtl->ssa->possibly_queue_changes(change); + to_delete.safe_push(change); + // crtl->ssa->change_insn(change); + } + // insn->rtl()->set_deleted(); } - - rtl_ssa_dce_done (); - return 0; + } + + auto_vec<insn_change*> changes{to_delete.length()}; + for (auto i = 0; i != to_delete.length(); ++i) { + changes[i] = &to_delete[i]; + } + + // if (verify_insn_changes()) + crtl->ssa->change_insns(changes); } -rtl_opt_pass * -make_pass_fast_rtl_dce (gcc::context *ctxt) +static unsigned int +rtl_ssa_dce() { - return new pass_fast_rtl_dce (ctxt); -} + // I can do something like invert phi uid and shift it + // unordered_set seems to be the easiest option + std::unordered_set<insn_info *> marked; + rtl_ssa_dce_init(); + rtl_ssa_dce_mark(marked); + rtl_ssa_dce_sweep(marked); + rtl_ssa_dce_done(); -namespace { + return 0; +} -const pass_data pass_data_rtl_ssa_dce = { - RTL_PASS, /* type */ - "rtl_ssa_dce", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - TV_DCE, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - TODO_df_finish, /* todo_flags_finish */ -}; +rtl_opt_pass * +make_pass_fast_rtl_dce(gcc::context *ctxt) +{ + return new pass_fast_rtl_dce(ctxt); +} -class pass_rtl_ssa_dce : public rtl_opt_pass +namespace { -public: - pass_rtl_ssa_dce (gcc::context *ctxt) - : rtl_opt_pass (pass_data_rtl_ssa_dce, ctxt) - {} - /* opt_pass methods: */ - bool gate (function *) final override { return flag_dce; } + const pass_data pass_data_rtl_ssa_dce = { + RTL_PASS, /* type */ + "rtl_ssa_dce", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_DCE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_df_finish, /* todo_flags_finish */ + }; + + class pass_rtl_ssa_dce : public rtl_opt_pass + { + public: + pass_rtl_ssa_dce(gcc::context *ctxt) + : rtl_opt_pass(pass_data_rtl_ssa_dce, ctxt) + { + } - unsigned int execute (function *) final override { return rtl_ssa_dce (); } + /* opt_pass methods: */ + bool gate(function *) final override { return flag_dce; } -}; // class pass_fast_rtl_dce + unsigned int execute(function *) final override { return rtl_ssa_dce(); } + + }; // class pass_fast_rtl_dce } // namespace rtl_opt_pass * -make_pass_rtl_ssa_dce (gcc::context *ctxt) +make_pass_rtl_ssa_dce(gcc::context *ctxt) { - return new pass_rtl_ssa_dce (ctxt); + return new pass_rtl_ssa_dce(ctxt); }