There are cases, where opportunities to use POST_INC addressing
only occur very late in the compilation process.  Take for example
the following function from AVR-LibC's qsort:

void swapfunc (char *a, char *b, int n)
{
    do
    {
        char t = *a;
        *a++ = *b;
        *b++ = t;
    } while (--n > 0);
}


which  -mmcu=avrtiny -S -Os -dp  compiles to:

swapfunc:
        push r28                 ;  72  [c=4 l=1]  pushqi1/0
        push r29                 ;  73  [c=4 l=1]  pushqi1/0
/* prologue: function */
/* frame size = 0 */
/* stack size = 2 */
        mov r26,r24      ;  66  [c=4 l=1]  movqi_insn/0
        mov r27,r25      ;  67  [c=4 l=1]  movqi_insn/0
        mov r30,r22      ;  68  [c=4 l=1]  movqi_insn/0
        mov r31,r23      ;  69  [c=4 l=1]  movqi_insn/0
        mov r22,r20      ;  70  [c=4 l=1]  movqi_insn/0
        mov r23,r21      ;  71  [c=4 l=1]  movqi_insn/0
.L2:
        ld r20,X                 ;  55  [c=4 l=1]  movqi_insn/3
        ld r21,Z                 ;  57  [c=4 l=1]  movqi_insn/3
        st X,r21                 ;  58  [c=4 l=1]  movqi_insn/2
        subi r26,-1      ;  59  [c=4 l=2]  *addhi3_clobber/0
        sbci r27,-1
        st Z,r20                 ;  61  [c=4 l=1]  movqi_insn/2
        subi r30,-1      ;  62  [c=4 l=2]  *addhi3_clobber/0
        sbci r31,-1
        subi r22, 1      ;  81  [c=4 l=2]  *add.for.cczn.hi/0
        sbci r23, 0
        breq .+2         ;  82  [c=8 l=2]  branch_ZN
        brpl .L2
/* epilogue start */
        pop r29          ;  76  [c=4 l=1]  popqi
        pop r28          ;  77  [c=4 l=1]  popqi
        ret              ;  78  [c=0 l=1]  return_from_epilogue

Insn 56+57 and insns 61+62 are post-inc stores.  They are not recognized
because the code prior to cprop_hardreg is a bit of a mess that moves
addresses back and forth (including Y).  Only after cprop_hardreg the
code is simple enough so post-inc can be detected by avr-fuse-add.

Hence this patch runs a 2nd instance of that pass late after
cprop_hardreg (the 1st instance runs prior to RTL peephole).

It renames avr_split_tiny_move to avr_split_fake_addressing_move
because that function also splits some insns on non-avrtiny.

The patch removes a define_split that's no more needed because
such splits are performed by avr_split_fake_addressing_move.

Passed without new regressions.  Ok for trunk?

Johann

p.s. post-inc etc. optimizations is basically non-existent in GCC.
One reasons seems to be SSA because each SSA variable gets its own
register, which results in a jungle of required registers which
even pass auto-inc-dec cannot penetrate...

Take for example the following code, that would require 12 instructions
as indicated by the comments:

void add4 (uint8_t *aa, const __flash uint8_t *bb, uint8_t nn)
{
    // Set Z (R30) to bb (1 MOVW or 2 MOVs)
    // Set X (R26) to aa (1 MOVW or 2 MOVs)
    do
    {
        uint8_t sum = 0;
        sum += *bb++; // 1 instruction: POST_INC load
        sum += *bb++; // 2 instructions: POST_INC load + add
        sum += *bb++; // 2 instructions: POST_INC load + add
        sum += *bb++; // 2 instructions: POST_INC load + add
        *aa++ = sum;  // 1 instruction: POST_INC store
    } while (--nn);   // 2 instructions: dec + branch
}

But  -mmcu=avr4 -Os -S -dp  compiles this to madness that
requires more than a dozen registers and computes each
intermediate in its own 16-bit register, coming out with
code that requires 34 instructions instead of 12.

--

AVR: Run pass avr-fuse-add a second time after pass_cprop_hardreg.

gcc/
        * config/avr/avr-protos.h (avr_split_tiny_move): Rename to
        avr_split_fake_addressing_move.
        * config/avr/avr-passes.cc: Same.
        (avr_pass_data_fuse_add) <tv_id>: Set to TV_MACH_DEP.
        (avr_pass_fuse_add) <clone>: Override.
        * config/avr/avr-passes.def (avr_pass_fuse_add): Run again
        after pass_cprop_hardreg.
        * config/avr/avr.md (split-lpmx): Remove a define_split.  Such
        splits are performed by avr_split_fake_addressing_move.
diff --git a/gcc/config/avr/avr-passes.cc b/gcc/config/avr/avr-passes.cc
index 8b018ff6a05..8a71b57ada1 100644
--- a/gcc/config/avr/avr-passes.cc
+++ b/gcc/config/avr/avr-passes.cc
@@ -1009,7 +1009,7 @@ static const pass_data avr_pass_data_fuse_add =
   RTL_PASS,	    // type
   "",		    // name (will be patched)
   OPTGROUP_NONE,    // optinfo_flags
-  TV_DF_SCAN,	    // tv_id
+  TV_MACH_DEP,	    // tv_id
   0,		    // properties_required
   0,		    // properties_provided
   0,		    // properties_destroyed
@@ -1026,6 +1026,13 @@ public:
     this->name = name;
   }
 
+  // Cloning is required because we are running one instance of the pass
+  // before peephole2. and a second one after cprop_hardreg.
+  opt_pass * clone () final override
+  {
+    return make_avr_pass_fuse_add (m_ctxt);
+  }
+
   bool gate (function *) final override
   {
     return optimize && avr_fuse_add > 0;
@@ -1503,8 +1510,8 @@ avr_pass_fuse_add::fuse_mem_add (Mem_Insn &mem, Add_Insn &add)
    - PLUS insn of that kind.
    - Indirect loads and stores.
    In almost all cases, combine opportunities arise from the preparation
-   done by `avr_split_tiny_move', but in some rare cases combinations are
-   found for the ordinary cores, too.
+   done by `avr_split_fake_addressing_move', but in some rare cases combinations
+   are found for the ordinary cores, too.
       As we consider at most one Mem insn per try, there may still be missed
    optimizations like  POST_INC + PLUS + POST_INC  might be performed
    as  PRE_DEC + PRE_DEC  for two adjacent locations.  */
@@ -1714,7 +1721,7 @@ public:
    core's capabilities.  This sets the stage for pass .avr-fuse-add.  */
 
 bool
-avr_split_tiny_move (rtx_insn * /*insn*/, rtx *xop)
+avr_split_fake_addressing_move (rtx_insn * /*insn*/, rtx *xop)
 {
   bool store_p = false;
   rtx mem, reg_or_0;
diff --git a/gcc/config/avr/avr-passes.def b/gcc/config/avr/avr-passes.def
index 748260edaef..cd89d673727 100644
--- a/gcc/config/avr/avr-passes.def
+++ b/gcc/config/avr/avr-passes.def
@@ -20,12 +20,33 @@
 /* A post reload optimization pass that fuses PLUS insns with CONST_INT
    addend with a load or store insn to get POST_INC or PRE_DEC addressing.
    It can also fuse two PLUSes to a single one, which may occur due to
-   splits from `avr_split_tiny_move'.  We do this in an own pass because
-   it can find more cases than peephole2, for example when there are
-   unrelated insns between the interesting ones.  */
+   splits from `avr_split_fake_addressing_move'.  We do this in an own
+   pass because it can find more cases than peephole2, for example when
+   there are unrelated insns between the interesting ones.  */
 
 INSERT_PASS_BEFORE (pass_peephole2, 1, avr_pass_fuse_add);
 
+/* There are cases where avr-fuse-add doesn't find POST_INC cases because
+   the RTL code at that time is too long-winded, and moves registers back and
+   forth (which seems to be the same reason for why pass auto_inc_dec cannot
+   find POST_INC, either).  Some of that long-windedness is cleaned up very
+   late in pass cprop_hardreg, which opens up new opportunities to find post
+   increments.  An example is the following function from AVR-LibC's qsort:
+
+   void swapfunc (char *a, char *b, int n)
+   {
+       do
+       {
+           char tmp = *a;
+           *a++ = *b;
+           *b++ = tmp;
+       } while (--n > 0);
+   }
+
+   Hence, run avr-fuse-add twice; the second time after cprop_hardreg.  */
+
+INSERT_PASS_AFTER (pass_cprop_hardreg, 1, avr_pass_fuse_add);
+
 /* An analysis pass that runs prior to prologue / epilogue generation.
    Computes cfun->machine->gasisr.maybe which is used in prologue and
    epilogue generation provided -mgas-isr-prologues is on.  */
@@ -47,9 +68,9 @@ INSERT_PASS_BEFORE (pass_free_cfg, 1, avr_pass_recompute_notes);
    tries to fix such situations by operating on the original mode.  This
    reduces code size and register pressure.
 
-   The assertion is that the code generated by casesi is unaltered and a
+   The assertion is that the code generated by casesi is unaltered and
    a sign-extend or zero-extend from QImode or HImode precedes the casesi
-   insns withaout any insns in between.  */
+   insns without any insns in between.  */
 
 INSERT_PASS_AFTER (pass_expand, 1, avr_pass_casesi);
 
diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h
index 289c80cab55..d6f4abbac66 100644
--- a/gcc/config/avr/avr-protos.h
+++ b/gcc/config/avr/avr-protos.h
@@ -179,7 +179,7 @@ extern rtl_opt_pass *make_avr_pass_casesi (gcc::context *);
 extern rtl_opt_pass *make_avr_pass_ifelse (gcc::context *);
 #ifdef RTX_CODE
 extern bool avr_casei_sequence_check_operands (rtx *xop);
-extern bool avr_split_tiny_move (rtx_insn *insn, rtx *operands);
+extern bool avr_split_fake_addressing_move (rtx_insn *insn, rtx *operands);
 #endif /* RTX_CODE */
 
 /* From avr-log.cc */
diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md
index f477ac170ea..520f1fe41a2 100644
--- a/gcc/config/avr/avr.md
+++ b/gcc/config/avr/avr.md
@@ -993,41 +993,10 @@ (define_peephole2
               (clobber (reg:CC REG_CC))])])
 
 
-;; For LPM loads from AS1 we split
-;;    R = *Z
-;; to
-;;    R = *Z++
-;;    Z = Z - sizeof (R)
-;;
-;; so that the second instruction can be optimized out.
-
-(define_split ; "split-lpmx"
-  [(set (match_operand:HISI 0 "register_operand" "")
-        (match_operand:HISI 1 "memory_operand" ""))]
-  "reload_completed
-   && AVR_HAVE_LPMX
-   && avr_mem_flash_p (operands[1])
-   && REG_P (XEXP (operands[1], 0))
-   && !reg_overlap_mentioned_p (XEXP (operands[1], 0), operands[0])"
-  [(set (match_dup 0)
-        (match_dup 2))
-   (set (match_dup 3)
-        (plus:HI (match_dup 3)
-                 (match_dup 4)))]
-  {
-     rtx addr = XEXP (operands[1], 0);
-
-    operands[2] = replace_equiv_address (operands[1],
-                                         gen_rtx_POST_INC (Pmode, addr));
-    operands[3] = addr;
-    operands[4] = gen_int_mode (-<SIZE>, HImode);
-  })
-
-
 ;; Legitimate address and stuff allows way more addressing modes than
 ;; Reduced Tiny actually supports.  Split them now so that we get
 ;; closer to real instructions which may result in some optimization
-;; opportunities.
+;; opportunities.  This applies also to fake X + offset addressing.
 (define_split
   [(parallel [(set (match_operand:MOVMODE 0 "nonimmediate_operand")
                    (match_operand:MOVMODE 1 "general_operand"))
@@ -1040,7 +1009,7 @@ (define_split
    && (MEM_P (operands[0]) || MEM_P (operands[1]))"
   [(scratch)]
   {
-    if (avr_split_tiny_move (curr_insn, operands))
+    if (avr_split_fake_addressing_move (curr_insn, operands))
       DONE;
     FAIL;
   })

Reply via email to