[55/61] Performance drop in mips-img-linux-gnu-gcc 7.x

Message ID 20250131171232.1018281-57-aleksandar.rakic@htecgroup.com
State New
Headers
Series Improve Mips target |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply

Commit Message

Aleksandar Rakic Jan. 31, 2025, 5:13 p.m. UTC
  From: Mihailo Stojanovic <mistojanovic@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 <mistojanovic@wavecomp.com>
Signed-off-by: Faraz Shahbazker <fshahbazker@wavecomp.com>
Signed-off-by: Aleksandar Rakic <aleksandar.rakic@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(+)
  

Comments

Richard Biener Feb. 3, 2025, 9:40 a.m. UTC | #1
On Fri, Jan 31, 2025 at 7:10 PM Aleksandar Rakic
<aleksandar.rakic@htecgroup.com> wrote:
>
> From: Mihailo Stojanovic <mistojanovic@wavecomp.com>

This looks like a target specific hack, this should be addressed generally
instead of opening up gcse internals to a target hook.

This should also at least come with a testcase.

> 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 <mistojanovic@wavecomp.com>
> Signed-off-by: Faraz Shahbazker <fshahbazker@wavecomp.com>
> Signed-off-by: Aleksandar Rakic <aleksandar.rakic@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
  

Patch

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 */