On Thu, Mar 5, 2015 at 10:53 AM, Thomas Preud'homme
<thomas.preudho...@arm.com> wrote:
> Note: this is stage1 material.
>
> Currently loop2_invariant pass hoist instructions out of loop by creating a 
> new temporary for the destination register of that instruction and leaving 
> there a mov from new temporary to old register as shown below:
>
> loop header
> start of loop body
> //stuff
> (set (reg 128) (const_int 0))
> //other stuff
> end of loop body
>
> becomes:
>
> (set (reg 129) (const_int 0))
> loop header
> start of loop body
> //stuff
> (set (reg 128) (reg 128))
> //other stuff
> end of loop body
>
> This is one of the errors that led to a useless ldr ending up inside a loop 
> (PR64616). This patch fix this specific bit (some other bit was fixed in [1]) 
> by simply moving the instruction if it's known to be safe. This is decided by 
> looking at all the uses of the register set in the instruction and checking 
> that (i) they were all dominated by the instruction and (ii) there is no 
> other def in the loop that could end up reaching one of the use.

Why doesn't copy-propagation clean this up?  It's run after loop2.

Richard.

> [1] https://gcc.gnu.org/ml/gcc-patches/2015-02/msg00933.html
>
> ChangeLog entries are as follows:
>
> *** gcc/ChangeLog ***
>
> 2015-02-16  Thomas Preud'homme  <thomas.preudho...@arm.com>
>
>         * dominance.c (nearest_common_dominator_for_set): Fix A_Dominated_by_B
>         code in comment.
>         * loop-invariant.c (can_move_invariant_reg): New.
>         (move_invariant_reg): Call above new function to decide whether
>         instruction can just be moved, skipping creation of temporary
>         register.
>
> *** gcc/testsuite/ChangeLog ***
>
> 2015-02-16  Thomas Preud'homme  <thomas.preudho...@arm.com>
>
>         * gcc.dg/loop-7.c: Run on all targets and check for loop2_invariant
>         being able to move instructions without introducing new temporary
>         register.
>         * gcc.dg/loop-8.c: New test.
>
> diff --git a/gcc/dominance.c b/gcc/dominance.c
> index 33d4ae4..09c8c90 100644
> --- a/gcc/dominance.c
> +++ b/gcc/dominance.c
> @@ -982,7 +982,7 @@ nearest_common_dominator_for_set (enum cdi_direction dir, 
> bitmap blocks)
>
>     A_Dominated_by_B (node A, node B)
>     {
> -     return DFS_Number_In(A) >= DFS_Number_In(A)
> +     return DFS_Number_In(A) >= DFS_Number_In(B)
>              && DFS_Number_Out (A) <= DFS_Number_Out(B);
>     }  */
>
> diff --git a/gcc/loop-invariant.c b/gcc/loop-invariant.c
> index f79b497..ab2a45c 100644
> --- a/gcc/loop-invariant.c
> +++ b/gcc/loop-invariant.c
> @@ -1512,6 +1512,99 @@ replace_uses (struct invariant *inv, rtx reg, bool 
> in_group)
>    return 1;
>  }
>
> +/* Whether invariant INV setting REG can be moved out of LOOP, at the end of
> +   the block preceding its header.  */
> +
> +static bool
> +can_move_invariant_reg (struct loop *loop, struct invariant *inv, rtx reg)
> +{
> +  df_ref def, use;
> +  bool ret = false;
> +  unsigned int i, dest_regno, defs_in_loop_count = 0;
> +  rtx_insn *insn = inv->insn;
> +  bitmap may_exit, has_exit, always_executed;
> +  basic_block *body, bb = BLOCK_FOR_INSN (inv->insn);
> +
> +  /* We ignore hard register and memory access for cost and complexity 
> reasons.
> +     Hard register are few at this stage and expensive to consider as they
> +     require building a separate data flow.  Memory access would require 
> using
> +     df_simulate_* and can_move_insns_across functions and is more complex.  
> */
> +  if (!REG_P (reg) || HARD_REGISTER_P (reg))
> +    return false;
> +
> +  /* Check whether the set is always executed.  We could omit this condition 
> if
> +     we know that the register is unused outside of the loop, but it does not
> +     seem worth finding out.  */
> +  may_exit = BITMAP_ALLOC (NULL);
> +  has_exit = BITMAP_ALLOC (NULL);
> +  always_executed = BITMAP_ALLOC (NULL);
> +  body = get_loop_body_in_dom_order (loop);
> +  find_exits (loop, body, may_exit, has_exit);
> +  compute_always_reached (loop, body, has_exit, always_executed);
> +  /* Find bit position for basic block bb.  */
> +  for (i = 0; i < loop->num_nodes && body[i] != bb; i++);
> +  if (!bitmap_bit_p (always_executed, i))
> +    goto cleanup;
> +
> +  /* Check that all uses reached by the def in insn would still be reached
> +     it.  */
> +  dest_regno = REGNO (reg);
> +  for (use = DF_REG_USE_CHAIN (dest_regno); use; use = DF_REF_NEXT_REG (use))
> +    {
> +      rtx ref;
> +      basic_block use_bb;
> +
> +      ref = DF_REF_INSN (use);
> +      use_bb = BLOCK_FOR_INSN (ref);
> +
> +      /* Ignore instruction considered for moving.  */
> +      if (ref == insn)
> +       continue;
> +
> +      /* Don't consider uses outside loop.  */
> +      if (!flow_bb_inside_loop_p (loop, use_bb))
> +       continue;
> +
> +      /* Don't move if a use is not dominated by def in insn.  */
> +      if (use_bb == bb && DF_INSN_LUID (insn) > DF_INSN_LUID (ref))
> +       goto cleanup;
> +      if (!dominated_by_p (CDI_DOMINATORS, use_bb, bb))
> +       goto cleanup;
> +
> +      /* Check for other defs.  Any other def in the loop might reach a use
> +        currently reached by the def in insn.  */
> +      if (!defs_in_loop_count)
> +       {
> +         for (def = DF_REG_DEF_CHAIN (dest_regno); def; def = 
> DF_REF_NEXT_REG (def))
> +           {
> +             basic_block def_bb = BLOCK_FOR_INSN (DF_REF_INSN (def));
> +
> +             /* Defs in exit block cannot reach a use they weren't
> +                already.  */
> +             if (single_succ_p (def_bb))
> +               {
> +                 basic_block def_bb_succ;
> +
> +                 def_bb_succ = single_succ (def_bb);
> +                 if (!flow_bb_inside_loop_p (loop, def_bb_succ))
> +                   continue;
> +               }
> +
> +             if (++defs_in_loop_count > 1)
> +               goto cleanup;
> +           }
> +       }
> +    }
> +  ret = true;
> +
> +cleanup:
> +  BITMAP_FREE (always_executed);
> +  BITMAP_FREE (may_exit);
> +  BITMAP_FREE (has_exit);
> +  free (body);
> +  return ret;
> +}
> +
>  /* Move invariant INVNO out of the LOOP.  Returns true if this succeeds, 
> false
>     otherwise.  */
>
> @@ -1545,11 +1638,8 @@ move_invariant_reg (struct loop *loop, unsigned invno)
>             }
>         }
>
> -      /* Move the set out of the loop.  If the set is always executed (we 
> could
> -        omit this condition if we know that the register is unused outside of
> -        the loop, but it does not seem worth finding out) and it has no uses
> -        that would not be dominated by it, we may just move it (TODO).
> -        Otherwise we need to create a temporary register.  */
> +      /* If possible, just move the set out of the loop.  Otherwise, we
> +        need to create a temporary register.  */
>        set = single_set (inv->insn);
>        reg = dest = SET_DEST (set);
>        if (GET_CODE (reg) == SUBREG)
> @@ -1557,19 +1647,25 @@ move_invariant_reg (struct loop *loop, unsigned invno)
>        if (REG_P (reg))
>         regno = REGNO (reg);
>
> -      reg = gen_reg_rtx_and_attrs (dest);
> +      if (!can_move_invariant_reg (loop, inv, reg))
> +       {
> +         reg = gen_reg_rtx_and_attrs (dest);
>
> -      /* Try replacing the destination by a new pseudoregister.  */
> -      validate_change (inv->insn, &SET_DEST (set), reg, true);
> +         /* Try replacing the destination by a new pseudoregister.  */
> +         validate_change (inv->insn, &SET_DEST (set), reg, true);
>
> -      /* As well as all the dominated uses.  */
> -      replace_uses (inv, reg, true);
> +         /* As well as all the dominated uses.  */
> +         replace_uses (inv, reg, true);
>
> -      /* And validate all the changes.  */
> -      if (!apply_change_group ())
> -       goto fail;
> +         /* And validate all the changes.  */
> +         if (!apply_change_group ())
> +           goto fail;
>
> -      emit_insn_after (gen_move_insn (dest, reg), inv->insn);
> +         emit_insn_after (gen_move_insn (dest, reg), inv->insn);
> +       }
> +      else if (dump_file)
> +       fprintf (dump_file, "Invariant %d moved without introducing a new "
> +                           "temporary register\n", invno);
>        reorder_insns (inv->insn, inv->insn, BB_END (preheader));
>
>        /* If there is a REG_EQUAL note on the insn we just moved, and the
> diff --git a/gcc/testsuite/gcc.dg/loop-7.c b/gcc/testsuite/gcc.dg/loop-7.c
> index 0457add..eac645c 100644
> --- a/gcc/testsuite/gcc.dg/loop-7.c
> +++ b/gcc/testsuite/gcc.dg/loop-7.c
> @@ -1,6 +1,6 @@
>  /* PR rtl-optimization/31360  */
>
> -/* { dg-do compile { target { powerpc*-*-* } } } */
> +/* { dg-do compile { target } } */
>  /* { dg-options "-O1 -fdump-rtl-loop2_invariant" } */
>
>  void f(int *a)
> @@ -12,5 +12,6 @@ void f(int *a)
>
>  /* Load of 0 is moved out of the loop.  */
>  /* { dg-final { scan-rtl-dump-times "Decided" 1 "loop2_invariant" } } */
> +/* { dg-final { scan-rtl-dump-times "without introducing a new temporary 
> register" 1 "loop2_invariant" } } */
>  /* { dg-final { cleanup-rtl-dump "loop2_invariant" } } */
>
> diff --git a/gcc/testsuite/gcc.dg/loop-8.c b/gcc/testsuite/gcc.dg/loop-8.c
> new file mode 100644
> index 0000000..592e54c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/loop-8.c
> @@ -0,0 +1,24 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O1 -fdump-rtl-loop2_invariant" } */
> +
> +void
> +f (int *a, int *b)
> +{
> +  int i;
> +
> +  for (i = 0; i < 100; i++)
> +    {
> +      int d = 42;
> +
> +      a[i] = d;
> +      if (i % 2)
> +       d = i;
> +      b[i] = d;
> +    }
> +}
> +
> +/* Load of 42 is moved out of the loop, introducing a new pseudo register.  
> */
> +/* { dg-final { scan-rtl-dump-times "Decided" 1 "loop2_invariant" } } */
> +/* { dg-final { scan-rtl-dump-not "without introducing a new temporary 
> register" "loop2_invariant" } } */
> +/* { dg-final { cleanup-rtl-dump "loop2_invariant" } } */
> +
>
> Testsuite was run in QEMU when compiled by an arm-none-eabi GCC 
> cross-compiler targeting Cortex-M3 and a bootstrapped x86_64 native GCC 
> without any regression.
>
> Is this ok for stage1?
>
> Best regards,
>
> Thomas
>
>
>

Reply via email to