[avr,applied] ad target/114100 - Don't print unused frame pointer adjustments.

Message ID d402788a-f939-4f36-811b-a0f44e3824fe@gjlay.de
State Unresolved
Headers
Series [avr,applied] ad target/114100 - Don't print unused frame pointer adjustments. |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Georg-Johann Lay March 3, 2024, 5:15 p.m. UTC
  This addendum ports a corner case optimization from -mno-fuse-add
to -mfuse-add:  When a base register needs temporal adjustment,
and the base is the frame pointer, then there are cases where the
post-adjustment is not needed.

Passes without new regressions on ATtiny40.

Johann

--

AVR: ad target/114100 - Don't print unused frame pointer adjustments.

Without -mfuse-add, when fake reg+offset addressing is used, the
output routines are saving some instructions when the base reg
is unused after.  This patch adds that optimization for the case
when the base is the frame pointer and the frame pointer adjustments
are split away from the move insn by -mfuse-add in .split2.
    Direct usage of reg_unused_after is not possible because that
function looks at the destination of the current insn, which won't
work for offsetting the frame pointer in printing PLUS code.
It can use an extended version of _reg_unused_after though.

gcc/
	PR target/114100
	* config/avr/avr-protos.h (_reg_unused_after): Remove proto.
	* config/avr/avr.cc (_reg_unused_after): Make static.  And
	add 3rd argument to skip the current insn.
	(reg_unused_after): Adjust call of reg_unused_after.
	(avr_out_plus_1) [AVR_TINY && -mfuse-add >= 2]: Don't output
	unneeded frame pointer adjustments.
  

Patch

diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h
index f4f3ffd8f28..3e19409d636 100644
--- a/gcc/config/avr/avr-protos.h
+++ b/gcc/config/avr/avr-protos.h
@@ -110,7 +110,6 @@  extern const char* avr_out_reload_inpsi (rtx*, rtx, int*);
 extern const char* avr_out_lpm (rtx_insn *, rtx*, int*);
 extern void avr_notice_update_cc (rtx body, rtx_insn *insn);
 extern int reg_unused_after (rtx_insn *insn, rtx reg);
-extern int _reg_unused_after (rtx_insn *insn, rtx reg);
 extern int avr_jump_mode (rtx x, rtx_insn *insn);
 extern int test_hard_reg_class (enum reg_class rclass, rtx x);
 extern int jump_over_one_insn_p (rtx_insn *insn, rtx dest);
diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc
index 44d6e141b62..7df21432dda 100644
--- a/gcc/config/avr/avr.cc
+++ b/gcc/config/avr/avr.cc
@@ -163,6 +163,7 @@  static int avr_operand_rtx_cost (rtx, machine_mode, enum rtx_code,
 				 int, bool);
 static void output_reload_in_const (rtx *, rtx, int *, bool);
 static struct machine_function *avr_init_machine_status (void);
+static int _reg_unused_after (rtx_insn *insn, rtx reg, bool look_at_insn);
 
 
 /* Prototypes for hook implementors if needed before their implementation.  */
@@ -8825,7 +8826,7 @@  lshrsi3_out (rtx_insn *insn, rtx operands[], int *len)
    fixed-point rounding, cf. `avr_out_round'.  */
 
 static void
-avr_out_plus_1 (rtx /*insn*/, rtx *xop, int *plen, enum rtx_code code,
+avr_out_plus_1 (rtx insn, rtx *xop, int *plen, enum rtx_code code,
 		enum rtx_code code_sat, int sign, bool out_label)
 {
   /* MODE of the operation.  */
@@ -8973,6 +8974,10 @@  avr_out_plus_1 (rtx /*insn*/, rtx *xop, int *plen, enum rtx_code code,
 	  && frame_pointer_needed
 	  && REGNO (xop[0]) == FRAME_POINTER_REGNUM)
 	{
+	  if (INSN_P (insn)
+	      && _reg_unused_after (as_a <rtx_insn *> (insn), xop[0], false))
+	    return;
+
 	  if (AVR_HAVE_8BIT_SP)
 	    {
 	      avr_asm_len ("subi %A0,%n2", xop, plen, 1);
@@ -10818,31 +10823,32 @@  int
 reg_unused_after (rtx_insn *insn, rtx reg)
 {
   return (dead_or_set_p (insn, reg)
-	  || (REG_P (reg) && _reg_unused_after (insn, reg)));
+	  || (REG_P (reg) && _reg_unused_after (insn, reg, true)));
 }
 
-/* Return nonzero if REG is not used after INSN.
+/* A helper for the previous function.
+   Return nonzero if REG is not used after INSN.
    We assume REG is a reload reg, and therefore does
    not live past labels.  It may live past calls or jumps though.  */
 
 int
-_reg_unused_after (rtx_insn *insn, rtx reg)
+_reg_unused_after (rtx_insn *insn, rtx reg, bool look_at_insn)
 {
-  enum rtx_code code;
-  rtx set;
-
-  /* If the reg is set by this instruction, then it is safe for our
-     case.  Disregard the case where this is a store to memory, since
-     we are checking a register used in the store address.  */
-  set = single_set (insn);
-  if (set && !MEM_P (SET_DEST (set))
-      && reg_overlap_mentioned_p (reg, SET_DEST (set)))
-    return 1;
+  if (look_at_insn)
+    {
+      /* If the reg is set by this instruction, then it is safe for our
+	 case.  Disregard the case where this is a store to memory, since
+	 we are checking a register used in the store address.  */
+      rtx set = single_set (insn);
+      if (set && !MEM_P (SET_DEST (set))
+	  && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+	return 1;
+    }
 
   while ((insn = NEXT_INSN (insn)))
     {
       rtx set;
-      code = GET_CODE (insn);
+      enum rtx_code code = GET_CODE (insn);
 
 #if 0
       /* If this is a label that existed before reload, then the register