From: Mihailo Stojanovic <mistojano...@wavecomp.com> gcc/ * config/mips/mips.cc (mips_rtx_costs): Reduce branch cost of conditional branches. (mips_prune_insertions_deletions): Target hook which checks whether a basic block is possibly if-convertible. Adjusts the insertion and deletion maps accordingly. (check_bb): Check whether a basic block is a THEN or ELSE block of IF-THEN-ELSE construct and whether it consists only of a single set instruction. This is a condition for marking the block as possibly if-convertible. (bb_valid_for_noce): Helper function. (last_active_insn): Same. (first_active_insn): Same. (insn_valid_noce_process_p): Same. (noce_operand_ok): Same. * config/mips/mips.opt: Add an option which disables the mips_prune_insertions_deletions hook. * doc/tm.texi.in: Add a macro definition for the new target hook. * gcse.c (compute_pre_data): Add the target hook call, which will modify the insertion and deletion bitmaps. * target.def: Define the target hook. * targhooks.h: Add default target hook prototype. * targhooks.c: Define the default target hook prototype. * doc/tm.texi: Regenerated.
Cherry-picked 64e5b4b4ff53872482454908a29c94665e40d25c from https://github.com/MIPS/gcc Signed-off-by: Mihailo Stojanovic <mistojano...@wavecomp.com> Signed-off-by: Faraz Shahbazker <fshahbaz...@wavecomp.com> Signed-off-by: Aleksandar Rakic <aleksandar.ra...@htecgroup.com> --- gcc/config/mips/mips.cc | 238 +++++++++++++++++++++++++++++++++++++++ gcc/config/mips/mips.opt | 3 + gcc/doc/tm.texi | 5 + gcc/doc/tm.texi.in | 2 + gcc/gcse.cc | 3 + gcc/target.def | 8 ++ gcc/targhooks.cc | 9 ++ gcc/targhooks.h | 5 + 8 files changed, 273 insertions(+) diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc index 4521cac15c7..d23c30a43be 100644 --- a/gcc/config/mips/mips.cc +++ b/gcc/config/mips/mips.cc @@ -5754,6 +5754,8 @@ mips_rtx_costs (rtx x, machine_mode mode, int outer_code, default: break; } + if (GET_CODE (SET_DEST (x)) != PC) + *total = 0; return false; case IF_THEN_ELSE: @@ -25872,6 +25874,239 @@ mips_noce_conversion_profitable_p (rtx_insn *seq, struct noce_if_info *if_info) return speed && cost <= if_info->max_seq_cost; } + +/* Return true if OP is ok for if-then-else processing. */ + +static int +noce_operand_ok (const_rtx op) +{ + if (side_effects_p (op)) + return FALSE; + + /* We special-case memories, so handle any of them with + no address side effects. */ + if (MEM_P (op)) + return ! side_effects_p (XEXP (op, 0)); + + return ! may_trap_p (op); +} + + +/* Helper for bb_valid_for_noce_process_p. Validate that + the rtx insn INSN is a single set that does not set + the conditional register CC and is in general valid for + if-conversion. */ + +static bool +insn_valid_noce_process_p (rtx_insn *insn) +{ + if (!insn + || !NONJUMP_INSN_P (insn)) + return false; + + rtx sset = single_set (insn); + + /* Currently support only simple single sets in test_bb. */ + if (!sset + || !noce_operand_ok (SET_DEST (sset)) + || !noce_operand_ok (SET_SRC (sset))) + return false; + + return true; +} + +/* Return the first non-jump active insn in the basic block. */ + +static rtx_insn * +first_active_insn (basic_block bb) +{ + rtx_insn *insn = BB_HEAD (bb); + + if (LABEL_P (insn)) + { + if (insn == BB_END (bb)) + return NULL; + insn = NEXT_INSN (insn); + } + + while (NOTE_P (insn) || DEBUG_INSN_P (insn)) + { + if (insn == BB_END (bb)) + return NULL; + insn = NEXT_INSN (insn); + } + + if (JUMP_P (insn)) + return NULL; + + return insn; +} + +static rtx_insn * +last_active_insn (basic_block bb, int skip_use_p) +{ + rtx_insn *insn = BB_END (bb); + rtx_insn *head = BB_HEAD (bb); + + while (NOTE_P (insn) + || JUMP_P (insn) + || DEBUG_INSN_P (insn) + || (skip_use_p + && NONJUMP_INSN_P (insn) + && GET_CODE (PATTERN (insn)) == USE)) + { + if (insn == head) + return NULL; + insn = PREV_INSN (insn); + } + + if (LABEL_P (insn)) + return NULL; + + return insn; +} + +static bool +bb_valid_for_noce (basic_block test_bb, bool *simple) +{ + if (!test_bb) + return false; + + rtx_insn *last_insn = last_active_insn (test_bb, FALSE); + + if (!insn_valid_noce_process_p (last_insn)) + return false; + + rtx_insn *first_insn = first_active_insn (test_bb); + rtx first_set = single_set (first_insn); + + if (!first_set) + return false; + + *simple = first_insn == last_insn; + return true; +} + +#define NULL_BLOCK ((basic_block) NULL) + +static bool +check_bb (basic_block test_bb, sbitmap *ifcv_blocks) +{ + /* The kind of block we're looking for has exactly two successors. */ + if (EDGE_COUNT (test_bb->succs) != 2) + return false; + + edge then_edge = EDGE_SUCC (test_bb, 0); + edge else_edge = EDGE_SUCC (test_bb, 1); + + /* The THEN edge is canonically the one that falls through. */ + if (then_edge->flags & EDGE_FALLTHRU) + ; + else if (else_edge->flags & EDGE_FALLTHRU) + std::swap (then_edge, else_edge); + else + /* Otherwise this must be a multiway branch of some sort. */ + return false; + + basic_block then_bb, else_bb; + + /* Recognize an IF-THEN-ELSE-JOIN block. */ + if (single_pred_p (then_edge->dest) + && single_succ_p (then_edge->dest) + && single_pred_p (else_edge->dest) + && single_succ_p (else_edge->dest) + && single_succ (then_edge->dest) == single_succ (else_edge->dest)) + { + then_bb = then_edge->dest; + else_bb = else_edge->dest; + } + /* Recognize an IF-THEN-JOIN block. */ + else if (single_pred_p (then_edge->dest) + && single_succ_p (then_edge->dest) + && single_succ (then_edge->dest) == else_edge->dest) + { + then_bb = then_edge->dest; + else_bb = NULL_BLOCK; + } + /* Recognize an IF-ELSE-JOIN block. We can have those because the order + of basic blocks in cfglayout mode does not matter, so the fallthrough + edge can go to any basic block (and not just to bb->next_bb, like in + cfgrtl mode). */ + else if (single_pred_p (else_edge->dest) + && single_succ_p (else_edge->dest) + && single_succ (else_edge->dest) == then_edge->dest) + { + /* The noce transformations do not apply to IF-ELSE-JOIN blocks. + To make this work, we have to invert the THEN and ELSE blocks + and reverse the jump condition. */ + then_bb = else_edge->dest; + else_bb = NULL_BLOCK; + } + else + /* Not a form we can handle. */ + return FALSE; + + bool then_simple = false; + bool else_simple = false; + + if (bb_valid_for_noce (then_bb, &then_simple) && then_simple) + bitmap_set_bit (*ifcv_blocks, then_bb->index); + if (bb_valid_for_noce (else_bb, &else_simple) && else_simple) + bitmap_set_bit (*ifcv_blocks, else_bb->index); + + return false; +} + +void +mips_prune_insertions_deletions (struct edge_list* edge_list, + unsigned int n_elems, + sbitmap *pre_insert_map, + sbitmap *pre_delete_map) +{ + basic_block bb; + unsigned int i, j; + sbitmap_iterator sbi; + unsigned int bb_num = (unsigned) last_basic_block_for_fn (cfun); + sbitmap ifcv_blocks = sbitmap_alloc (bb_num); + sbitmap insertions = sbitmap_alloc (n_elems); + bitmap_clear (ifcv_blocks); + bitmap_clear (insertions); + + if (TARGET_PRUNE_INSERT_DELETE) + return; + + FOR_EACH_BB_FN (bb, cfun) + check_bb (bb, &ifcv_blocks); + + int num_edges = NUM_EDGES (edge_list); + int e; + for (e = 0; e < num_edges; e++) + { + basic_block pred = INDEX_EDGE_PRED_BB (edge_list, e); + basic_block succ = INDEX_EDGE_SUCC_BB (edge_list, e); + + if (bitmap_bit_p (ifcv_blocks, pred->index) + || bitmap_bit_p (ifcv_blocks, succ->index)) + { + EXECUTE_IF_SET_IN_BITMAP (pre_insert_map[e], 0, i, sbi) + bitmap_set_bit (insertions, i); + + bitmap_clear (pre_insert_map[e]); + } + } + + for (i = 0; i < bb_num; i++) + { + EXECUTE_IF_SET_IN_BITMAP (pre_delete_map[i], 0, j, sbi) + { + if (bitmap_bit_p (insertions, j)) + bitmap_clear_bit (pre_delete_map[i], j); + } + } + + sbitmap_free (ifcv_blocks); + sbitmap_free (insertions); +} /* Initialize the GCC target structure. */ #undef TARGET_ASM_ALIGNED_HI_OP @@ -26217,6 +26452,9 @@ mips_noce_conversion_profitable_p (rtx_insn *seq, struct noce_if_info *if_info) #undef TARGET_NOCE_CONVERSION_PROFITABLE_P #define TARGET_NOCE_CONVERSION_PROFITABLE_P mips_noce_conversion_profitable_p +#undef TARGET_PRUNE_INSERTIONS_DELETIONS +#define TARGET_PRUNE_INSERTIONS_DELETIONS mips_prune_insertions_deletions + struct gcc_target targetm = TARGET_INITIALIZER; #include "gt-mips.h" diff --git a/gcc/config/mips/mips.opt b/gcc/config/mips/mips.opt index be347155286..804f4fecbc9 100644 --- a/gcc/config/mips/mips.opt +++ b/gcc/config/mips/mips.opt @@ -570,6 +570,9 @@ Target Undocumented Var(TARGET_USE_SAVE_RESTORE) Init(-1) muse-copyw-ucopyw Target Undocumented Var(TARGET_USE_COPYW_UCOPYW) Init(-1) +mno-prune-insert-delete +Target Undocumented Var(TARGET_PRUNE_INSERT_DELETE) + minline-intermix Target Var(TARGET_INLINE_INTERMIX) Allow inlining even if the compression flags differ between caller and callee. diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 109e40384b6..aac034524e7 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -7323,6 +7323,11 @@ The default implementation of this hook uses the and uses a multiple of @code{BRANCH_COST} otherwise. @end deftypefn +@deftypefn {Target Hook} void TARGET_PRUNE_INSERTIONS_DELETIONS (struct edge_list *@var{edge_list}, unsigned int @var{n_elems}, sbitmap *@var{pre_insert_map}, sbitmap *@var{pre_delete_map}) +This hook gives the target a possibility to stop the code motion during + GCSE pass for basic blocks which have a potential to be if-converted. +@end deftypefn + @deftypefn {Target Hook} bool TARGET_NOCE_CONVERSION_PROFITABLE_P (rtx_insn *@var{seq}, struct noce_if_info *@var{if_info}) This hook returns true if the instruction sequence @code{seq} is a good candidate as a replacement for the if-convertible sequence described in diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 93bcd747e37..4d81a1729de 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -4744,6 +4744,8 @@ Define this macro if a non-short-circuit operation produced by @hook TARGET_MAX_NOCE_IFCVT_SEQ_COST +@hook TARGET_PRUNE_INSERTIONS_DELETIONS + @hook TARGET_NOCE_CONVERSION_PROFITABLE_P @hook TARGET_NEW_ADDRESS_PROFITABLE_P diff --git a/gcc/gcse.cc b/gcc/gcse.cc index 31b92f30fa1..12e252d826d 100644 --- a/gcc/gcse.cc +++ b/gcc/gcse.cc @@ -1893,6 +1893,9 @@ compute_pre_data (void) prune_insertions_deletions (expr_hash_table.n_elems); + targetm.prune_insertions_deletions (edge_list, expr_hash_table.n_elems, + pre_insert_map, pre_delete_map); + return edge_list; } diff --git a/gcc/target.def b/gcc/target.def index 523ae7ec9aa..80f0f1ef53b 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -7056,6 +7056,14 @@ You need not define this hook if @code{WORD_REGISTER_OPERATIONS} is not\n\ defined to 1.", unsigned int, (void), default_min_arithmetic_precision) +/* Function to update PRE deletion and insertion bitmaps. */ +DEFHOOK +(prune_insertions_deletions, + "This hook gives the target a possibility to stop the code motion during\n\ + GCSE pass for basic blocks which have a potential to be if-converted.", + void, (struct edge_list *edge_list, unsigned int n_elems, sbitmap *pre_insert_map, sbitmap *pre_delete_map), + default_prune_insertions_deletions) + DEFHOOKPOD (atomic_test_and_set_trueval, "This value should be set if the result written by\n\ diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc index 304b35ed772..f1e0a157c9e 100644 --- a/gcc/targhooks.cc +++ b/gcc/targhooks.cc @@ -2843,4 +2843,13 @@ default_memtag_untagged_pointer (rtx tagged_pointer, rtx target) return untagged_base; } +void +default_prune_insertions_deletions (struct edge_list * + edge_list ATTRIBUTE_UNUSED, + unsigned int n_elems ATTRIBUTE_UNUSED, + sbitmap *pre_insert_map ATTRIBUTE_UNUSED, + sbitmap *pre_delete_map ATTRIBUTE_UNUSED) +{ +} + #include "gt-targhooks.h" diff --git a/gcc/targhooks.h b/gcc/targhooks.h index 2704d6008f1..2e3d05a1e92 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -309,4 +309,9 @@ extern rtx default_memtag_set_tag (rtx, rtx, rtx); extern rtx default_memtag_extract_tag (rtx, rtx); extern rtx default_memtag_untagged_pointer (rtx, rtx); +extern void default_prune_insertions_deletions (struct edge_list *edge_list, + unsigned int n_elems, + sbitmap *pre_insert_map, + sbitmap *pre_delete_map); + #endif /* GCC_TARGHOOKS_H */ -- 2.34.1