https://gcc.gnu.org/g:28c1702afc0b1a99333d1e5bef6c302348141ab5
commit 28c1702afc0b1a99333d1e5bef6c302348141ab5 Author: Ondřej Machota <ondrejmach...@gmail.com> Date: Sun Mar 9 23:02:03 2025 +0100 rtl-ssa: dce pass simple testcase Diff: --- gcc/cse.cc | 2 +- gcc/dce.cc | 233 +++++++++++++++++++++++++-------------------------------- gcc/passes.def | 3 +- 3 files changed, 104 insertions(+), 134 deletions(-) diff --git a/gcc/cse.cc b/gcc/cse.cc index 70d5caac4cac..9c0924d73e1d 100644 --- a/gcc/cse.cc +++ b/gcc/cse.cc @@ -7639,7 +7639,7 @@ rest_of_handle_cse2 (void) bypassed safely. */ cse_condition_code_reg (); - delete_trivially_dead_insns (get_insns (), max_reg_num ()); + // delete_trivially_dead_insns (get_insns (), max_reg_num ()); if (tem == 2) { diff --git a/gcc/dce.cc b/gcc/dce.cc index 9a217a795b76..d3b569a74ca0 100644 --- a/gcc/dce.cc +++ b/gcc/dce.cc @@ -1325,12 +1325,12 @@ bool sets_global_register(rtx_insn* insn) { rtx dest = SET_DEST(set); // TODO : rewrite to simple return - std::cerr << "first pseudo: " << FIRST_PSEUDO_REGISTER << '\n'; - std::cerr << "register: " << REGNO(dest) << "\n"; - debug(insn); + //std::cerr << "first pseudo: " << FIRST_PSEUDO_REGISTER << '\n'; + //std::cerr << "register: " << REGNO(dest) << "\n"; + //debug(insn); // If I understand correctly, global_regs[i] is 1 iff reg i is used if (REG_P(dest) && HARD_REGISTER_NUM_P(REGNO(dest))) { // && global_regs[REGNO(dest)] - std::cerr << "sets_global_register: true\n"; + //std::cerr << "sets_global_register: true\n"; return true; } @@ -1356,24 +1356,88 @@ bool is_control_flow(rtx_code code) { } } -bool is_unary_mem_modification(rtx_code code) { - switch (code) { - case PRE_DEC: +bool side_effects_with_mem (const_rtx x) +{ + const RTX_CODE code = GET_CODE (x); + switch (code) + { + case LABEL_REF: + case SYMBOL_REF: + case CONST: + CASE_CONST_ANY: + case PC: + case REG: + case SCRATCH: + case ADDR_VEC: + case ADDR_DIFF_VEC: + case VAR_LOCATION: + return false; + + case CLOBBER: + /* Reject CLOBBER with a non-VOID mode. These are made by combine.cc + when some combination can't be done. If we see one, don't think + that we can simplify the expression. */ + return (GET_MODE (x) != VOIDmode); + case PRE_INC: - case POST_DEC: + case PRE_DEC: case POST_INC: + case POST_DEC: case PRE_MODIFY: case POST_MODIFY: + case CALL: + case UNSPEC_VOLATILE: + return true; + + case MEM: // We might want tu return true iff volatile or mem is a destination + case ASM_INPUT: + case ASM_OPERANDS: + return true; + + case USE: return true; default: - return false; + break; + } + + /* Recursively scan the operands of this expression. */ + + { + const char *fmt = GET_RTX_FORMAT (code); + int i; + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (side_effects_with_mem (XEXP (x, i))) + return true; + } + else if (fmt[i] == 'E') + { + int j; + for (j = 0; j < XVECLEN (x, i); j++) + if (side_effects_with_mem (XVECEXP (x, i, j))) + return true; + } + } } + return false; } bool is_rtx_insn_prelive(rtx_insn *insn) { gcc_assert(insn != nullptr); + // Jumps, notes, barriers should not be deleted + // According to the docs, rtl ssa does not contain noteS and barrierS + if (!NONJUMP_INSN_P (insn)) + { + std::cerr << "found jump instruction\n"; + debug(insn); + return true; + } + // TODO : handle calls correctly if (CALL_P (insn) /* We cannot delete pure or const sibling calls because it is @@ -1387,15 +1451,6 @@ bool is_rtx_insn_prelive(rtx_insn *insn) { return true; // return !find_call_stack_args (as_a <rtx_call_insn *> (insn), false, fast, arg_stores); - // Jumps, notes, barriers should not be deleted - // According to the docs, rtl ssa does not contain noteS and barrierS - if (!NONJUMP_INSN_P (insn)) - { - std::cerr << "found jump instruction\n"; - debug(insn); - return true; - } - // Only rtx_insn should be handled here auto code = GET_CODE(insn); gcc_assert(code == INSN); @@ -1411,51 +1466,17 @@ bool is_rtx_insn_prelive(rtx_insn *insn) { if (RTX_FRAME_RELATED_P (insn) && crtl->shrink_wrapped_separate && find_reg_note (insn, REG_CFA_RESTORE, NULL)) return true; - // if (is_control_flow(code)) - // return true; - // Mark set of a global register if (sets_global_register(insn)) // check rtx_class with GET_RTX_CLASS if RTX_ISNS and convert if needed return true; rtx body = PATTERN(insn); - if (side_effects_p(body) || can_throw_internal(body)) - return true; - - if (is_unary_mem_modification(code)) + if (side_effects_with_mem(body) || can_throw_internal(body)) return true; // TODO : parallel, {pre,post}_{int,dec}, {pre,post}_modify, may_trap_or_fault_p // Parallel is handled by volatile_refs_p - switch (code) { - case MEM: - case ASM_INPUT: - case ASM_OPERANDS: - return true; - - case PARALLEL: - for (int i = XVECLEN (body, 0) - 1; i >= 0; i--) - if (!deletable_insn_p_1 (XVECEXP (body, 0, i))) - return true; - return false; - break; - - default: - break; - } - - // const char *const fmt = GET_RTX_FORMAT (code); - // for (size_t i = 0; i < GET_RTX_LENGTH(code); ++i) { - // if (fmt[i] == 'e' && is_rtx_insn_prelive(XEXP(rtx, i))) { - // return true; - // } else if (fmt[i] == 'E') { - // for (size_t j = 0; j < XVECLEN(rtx, i); ++j) { - // if (is_rtx_insn_prelive(XVECEXP(rtx, i, j))) - // return true; - // } - // } - // } return false; } @@ -1491,34 +1512,10 @@ bool is_prelive(insn_info *insn) if (!INSN_P(rtl)) // This might be useless return false; - return is_rtx_insn_prelive(rtl); - - // TODO : join if statements - // We need to describe all possible prelive instructions, a list of all the instructions is inside `rtl.def` - - // Control flow - auto code = GET_CODE(rtl); - if (is_control_flow(code)) - return true; - - // Mark set of a global register - if (sets_global_register(rtl)) - return true; - - // Call is inside side_effects_p - std::cerr << "Prelive: " << GET_RTX_NAME(code) << '\n'; - // debug(insn); - debug(rtl); - if (side_effects_p(rtl) || can_throw_internal(rtl) || BARRIER_P(rtl) || code == PREFETCH) - return true; + auto res = is_rtx_insn_prelive(rtl); + std::cerr << "Trying to mark insn: " << insn->uid() << " as prelive: " << res << '\n'; - if (code == PARALLEL) { - - } - - // TODO : handle parallel, {pre,post}_{int,dec}, {pre,post}_modify - - return false; + return is_rtx_insn_prelive(rtl); } static void @@ -1568,13 +1565,6 @@ rtl_ssa_dce_prelive(std::unordered_set<insn_info *> &marked) 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 - - - * 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)) @@ -1601,13 +1591,24 @@ rtl_ssa_dce_mark() while (!worklist.is_empty()) { insn_info *insn = worklist.pop(); - def_array defs = insn->defs(); // array - because of phi? + use_array uses = insn->uses(); + if (dump_file) - fprintf(dump_file, "Looking at: %d, defs: %d\n", insn->uid(), defs.size()); - // I hope that the ssa form uses defs and we don't have to scan inner content - for (size_t i = 0; i < defs.size(); i++) + fprintf(dump_file, "Looking at: %d, uses: %d\n", insn->uid(), uses.size()); + + std::cerr << "Insn: " << insn->uid() << ", uses: " << uses.size() << '\n'; + for (size_t i = 0; i < uses.size(); i++) { - insn_info *parent_insn = defs[i]->insn(); + // debug(uses[i]); + use_info* use = uses[i]; + // debug(use->def()); + // if (use->def() != nullptr) { + // std::cerr << use->def()->insn()->uid() << '\n'; + // } + // std::cerr << '\n'; + // debug(use); + // std::cerr << '\n'; + insn_info *parent_insn = use->def()->insn(); int parent_insn_uid = parent_insn->uid(); // propage that some instruction in chain is live from bottom to top if (dump_file) @@ -1615,6 +1616,7 @@ rtl_ssa_dce_mark() // not yet marked if (!(marked.count(parent_insn) > 0)) { + std::cerr << "Adding: " << parent_insn_uid << " to worklist"; if (dump_file) fprintf(dump_file, " Adding insn %d to worklist - mark\n", parent_insn_uid); worklist.safe_push(parent_insn); @@ -1625,6 +1627,10 @@ rtl_ssa_dce_mark() // auto bb = insn->bb(); } + for (auto&& insn : marked) { + std::cerr << "Marked insn: " << insn->uid() << '\n'; + } + // TODO : control dependence return marked; } @@ -1635,7 +1641,6 @@ 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 *> auto attempt = crtl->ssa->new_change_attempt (); @@ -1653,7 +1658,8 @@ rtl_ssa_dce_sweep(std::unordered_set<insn_info *> marked) fprintf(dump_file, " Sweeping insn %d\n", insn->uid()); } - if (insn->rtl()) + // Skip artificial insns (or uid() < 0) + if (insn->is_real()) { auto change = insn_change::delete_insn(insn); // crtl->ssa->possibly_queue_changes(change); @@ -1679,46 +1685,9 @@ rtl_ssa_dce_sweep(std::unordered_set<insn_info *> marked) static unsigned int rtl_ssa_dce() { - // I can do something like invert phi uid and shift it - // unordered_set seems to be the easiest option rtl_ssa_dce_init(); + debug(crtl->ssa); - for (rtl_ssa::bb_info* bb : crtl->ssa->bbs()) { - // debug(bb); - std::cerr << "BASIC BLOCK: " << bb->index() << '\n'; - std::cerr << "PRINTING INSNS:\n"; - for (rtl_ssa::insn_info* insn : bb->all_insns()) { - std::cerr << "PRINTING INSN\n"; - debug(insn); - if (insn->is_real()) { - std::cerr << "RTL PART\n"; - debug(insn->rtl()); - } else { - std::cerr << "NON REAL INSN\n"; - } - - rtl_ssa::use_array ua = insn->uses(); - std::cerr << "USES\n"; - for (size_t i = 0; i < ua.size(); i++) - { - std::cerr << "USE: " << i << '\n'; - ua[i]->def(); - debug(ua[i]->insn()); - } - - // rtl_ssa::def_array df = insn->defs(); - // std::cerr << "DEFS\n"; - // for (size_t i = 0; i < df.size(); i++) - // { - // std::cerr << "DEF: " << i << '\n'; - // debug(df[i]->insn()); - // } - } - - std::cerr << "\n\n\n"; - } - - // debug(crtl->ssa); std::cerr << "Next phase: prelive + mark: \n"; std::unordered_set<insn_info *> marked = rtl_ssa_dce_mark(); rtl_ssa_dce_sweep(marked); diff --git a/gcc/passes.def b/gcc/passes.def index 51ce7276da28..f90a15bf7ddf 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -459,6 +459,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_lower_subreg); NEXT_PASS (pass_df_initialize_opt); NEXT_PASS (pass_cse); + NEXT_PASS (pass_rtl_ssa_dce); NEXT_PASS (pass_rtl_fwprop); NEXT_PASS (pass_rtl_cprop); NEXT_PASS (pass_rtl_pre); @@ -531,7 +532,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_regrename); NEXT_PASS (pass_fold_mem_offsets); NEXT_PASS (pass_cprop_hardreg); - NEXT_PASS (pass_rtl_ssa_dce); + // NEXT_PASS (pass_rtl_ssa_dce); NEXT_PASS (pass_reorder_blocks); NEXT_PASS (pass_leaf_regs); NEXT_PASS (pass_split_before_sched2);