1  /* { dg-do compile { target arm*-*-* } } */
       2  /* { dg-require-effective-target arm_arm_ok } */
       3  /* { dg-require-effective-target arm_ldrd_strd_ok } */
       4  /* { dg-skip-if "Ensure only targetting arm with TARGET_LDRD" { *-*-* } { "-mthumb" } { "" } } */
       5  /* { dg-options "-O3 -marm -fdump-rtl-peephole2" } */
       6  
       7  /*
       8     Test file contains testcases that are there to check.
       9        1) Each peephole generates the expected patterns.
      10        2) These patterns match the expected define_insns and generate ldrd/strd.
      11        2) Memory alias information is not lost in the peephole transformation.
      12  
      13     I don't check the peephole pass on most of the functions here but just check
      14     the correct assembly is output.  The ldrd/strd peepholes only generate a
      15     different pattern to the ldm/stm peepholes in some specific cases, and those
      16     are checked.
      17  
      18     The exceptions are tested by the crafted testcases at the end of this file
      19     that are named in the pattern foo_x[[:digit:]].
      20  
      21     The first testcase (foo_mem_11) demonstrates bug 88714 is fixed by checking
      22     that both alias sets in the RTL are preserved.
      23  
      24     All other testcases are only checked to see that they generate a LDRD or
      25     STRD instruction accordingly.
      26   */
      27  
      28  
      29  /* Example of bugzilla 88714 -- memory aliasing info needs to be retained.  */
      30  int __RTL (startwith ("peephole2")) foo_mem_11 (int *a, int *b)
      31  {
      32  (function "foo_mem_11"
      33    (insn-chain
      34      (cnote 1 NOTE_INSN_DELETED)
      35      (block 2
      36        (edge-from entry (flags "FALLTHRU"))
      37        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
      38        (cinsn 101 (set (reg:SI r2)
      39  		      (mem/c:SI (reg:SI r0) [1 S4 A64])) "/home/matmal01/test.c":18)
      40        (cinsn 102 (set (reg:SI r3)
      41  		      (mem/c:SI (plus:SI (reg:SI r0) (const_int 4)) [2 S4 A32])) "/home/matmal01/test.c":18)
      42        (cinsn 103 (set (reg:SI r0)
      43  		      (plus:SI (reg:SI r2) (reg:SI r3))) "/home/matmal01/test.c":18)
      44        (edge-to exit (flags "FALLTHRU"))
      45      ) ;; block 2
      46    ) ;; insn-chain
      47    (crtl
      48      (return_rtx
      49        (reg/i:SI r0)
      50      ) ;; return_rtx
      51    ) ;; crtl
      52  ) ;; function "main"
      53  }
      54  /* { dg-final { scan-rtl-dump {Function foo_mem_11.*\(mem/c:SI[^\n]*\[1.*\(mem/c:SI[^\n]*\n[^\n]*\[2.*Function foo11} "peephole2" } } */
      55  
      56  /* ldrd plain peephole2.  */
      57  int __RTL (startwith ("peephole2")) foo11 (int *a)
      58  {
      59  (function "foo11"
      60    (insn-chain
      61      (cnote 1 NOTE_INSN_DELETED)
      62      (block 2
      63        (edge-from entry (flags "FALLTHRU"))
      64        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
      65        (cinsn 101 (set (reg:SI r2)
      66  		      (mem/c:SI (reg:SI r0) [0 S4 A64])) "/home/matmal01/test.c":18)
      67        (cinsn 102 (set (reg:SI r3)
      68  		      (mem/c:SI (plus:SI (reg:SI r0) (const_int 4)) [0 S4 A32])) "/home/matmal01/test.c":18)
      69        (cinsn 103 (set (reg:SI r0)
      70  		      (plus:SI (reg:SI r2) (reg:SI r3))) "/home/matmal01/test.c":18)
      71        (edge-to exit (flags "FALLTHRU"))
      72      ) ;; block 2
      73    ) ;; insn-chain
      74    (crtl
      75      (return_rtx
      76        (reg/i:SI r0)
      77      ) ;; return_rtx
      78    ) ;; crtl
      79  ) ;; function "main"
      80  }
      81  
      82  /* ldrd plain peephole2, which accepts insns initially out of order.  */
      83  int __RTL (startwith ("peephole2")) foo11_alt (int *a)
      84  {
      85  (function "foo11_alt"
      86    (insn-chain
      87      (cnote 1 NOTE_INSN_DELETED)
      88      (block 2
      89        (edge-from entry (flags "FALLTHRU"))
      90        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
      91        (cinsn 102 (set (reg:SI r3)
      92  		      (mem/c:SI (plus:SI (reg:SI r0) (const_int 4)) [0 S4 A32])) "/home/matmal01/test.c":18)
      93        (cinsn 101 (set (reg:SI r2)
      94  		      (mem/c:SI (reg:SI r0) [0 S4 A64])) "/home/matmal01/test.c":18)
      95        (cinsn 103 (set (reg:SI r0)
      96  		      (plus:SI (reg:SI r2) (reg:SI r3))) "/home/matmal01/test.c":18)
      97        (edge-to exit (flags "FALLTHRU"))
      98      ) ;; block 2
      99    ) ;; insn-chain
     100    (crtl
     101      (return_rtx
     102        (reg/i:SI r0)
     103      ) ;; return_rtx
     104    ) ;; crtl
     105  ) ;; function "main"
     106  }
     107  
     108  /* strd plain peephole2.  */
     109  int __RTL (startwith ("peephole2")) foo12 (int *a)
     110  {
     111  (function "foo12"
     112    (insn-chain
     113      (cnote 1 NOTE_INSN_DELETED)
     114      (block 2
     115        (edge-from entry (flags "FALLTHRU"))
     116        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
     117        (cinsn 101 (set (mem/c:SI (reg:SI r0) [0 S4 A64])
     118  		      (reg:SI r2)) "/home/matmal01/test.c":18)
     119        (cinsn 102 (set (mem/c:SI (plus:SI (reg:SI r0) (const_int 4)) [0 S4 A32])
     120  		      (reg:SI r3)) "/home/matmal01/test.c":18)
     121        (edge-to exit (flags "FALLTHRU"))
     122      ) ;; block 2
     123    ) ;; insn-chain
     124    (crtl
     125      (return_rtx
     126        (reg/i:SI r0)
     127      ) ;; return_rtx
     128    ) ;; crtl
     129  ) ;; function "main"
     130  }
     131  
     132  /* strd of constants -- store interleaved with constant move into register.
     133     Use same register twice to ensure we use the relevant pattern.  */
     134  int __RTL (startwith ("peephole2")) foo13 (int *a)
     135  {
     136  (function "foo13"
     137    (insn-chain
     138      (cnote 1 NOTE_INSN_DELETED)
     139      (block 2
     140        (edge-from entry (flags "FALLTHRU"))
     141        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
     142        (cinsn 99  (set (reg:SI r2)
     143  		      (const_int 1)) "/home/matmal01/test.c":18)
     144        (cinsn 101 (set (mem/c:SI (reg:SI r0) [0 S4 A64])
     145  		      (reg:SI r2)) "/home/matmal01/test.c":18)
     146        (cinsn 100 (set (reg:SI r2)
     147  		      (const_int 0)) "/home/matmal01/test.c":18)
     148        (cinsn 102 (set (mem/c:SI (plus:SI (reg:SI r0) (const_int 4)) [0 S4 A32])
     149  		      (reg:SI r2)) "/home/matmal01/test.c":18)
     150        (edge-to exit (flags "FALLTHRU"))
     151      ) ;; block 2
     152    ) ;; insn-chain
     153    (crtl
     154      (return_rtx
     155        (reg/i:SI r0)
     156      ) ;; return_rtx
     157    ) ;; crtl
     158  ) ;; function "main"
     159  }
     160  
     161  /* strd of constants -- stores after constant moves into registers.
     162     Use registers out of order, is only way to avoid plain strd while hitting
     163     this pattern.  */
     164  int __RTL (startwith ("peephole2")) foo14 (int *a)
     165  {
     166  (function "foo14"
     167    (insn-chain
     168      (cnote 1 NOTE_INSN_DELETED)
     169      (block 2
     170        (edge-from entry (flags "FALLTHRU"))
     171        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
     172        (cinsn 99  (set (reg:SI r3)
     173  		      (const_int 1)) "/home/matmal01/test.c":18)
     174        (cinsn 100 (set (reg:SI r2)
     175  		      (const_int 0)) "/home/matmal01/test.c":18)
     176        (cinsn 101 (set (mem/c:SI (reg:SI r0) [0 S4 A64])
     177  		      (reg:SI r3)) "/home/matmal01/test.c":18)
     178        (cinsn 102 (set (mem/c:SI (plus:SI (reg:SI r0) (const_int 4)) [0 S4 A32])
     179  		      (reg:SI r2)) "/home/matmal01/test.c":18)
     180        (edge-to exit (flags "FALLTHRU"))
     181      ) ;; block 2
     182    ) ;; insn-chain
     183    (crtl
     184      (return_rtx
     185        (reg/i:SI r0)
     186      ) ;; return_rtx
     187    ) ;; crtl
     188  ) ;; function "main"
     189  }
     190  
     191  /* swap the destination registers of two loads before a commutative operation.
     192     Here the commutative operation is what the peephole uses to know it can
     193     swap the register loads around.  */
     194  int __RTL (startwith ("peephole2")) foo15 (int *a)
     195  {
     196  (function "foo15"
     197    (insn-chain
     198      (cnote 1 NOTE_INSN_DELETED)
     199      (block 2
     200        (edge-from entry (flags "FALLTHRU"))
     201        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
     202        (cinsn 100 (set (reg:SI r3)
     203  		      (mem/c:SI (reg:SI r0) [0 S4 A64])) "/home/matmal01/test.c":18)
     204        (cinsn 101 (set (reg:SI r2)
     205  		      (mem/c:SI (plus:SI (reg:SI r0) (const_int 4)) [0 S4 A32])) "/home/matmal01/test.c":18)
     206        (cinsn 102 (set (reg:SI r0)
     207  		      (plus:SI (reg:SI r2) (reg:SI r3))) "/home/matmal01/test.c":18
     208         (expr_list:REG_DEAD (reg:SI r2)
     209  	(expr_list:REG_DEAD (reg:SI r3)
     210  	 (nil))))
     211        (edge-to exit (flags "FALLTHRU"))
     212      ) ;; block 2
     213    ) ;; insn-chain
     214    (crtl
     215      (return_rtx
     216        (reg/i:SI r0)
     217      ) ;; return_rtx
     218    ) ;; crtl
     219  ) ;; function "main"
     220  }
     221  
     222  
     223  /* swap the destination registers of two loads before a commutative operation
     224     that sets the flags.  */
     225  /*
     226     NOTE Can't make a testcase for this pattern since there are no insn patterns
     227     matching the parallel insn in the peephole.
     228  
     229     i.e. until some define_insn is defined matching that insn that peephole can
     230     never match in real code, and in artificial RTL code any pattern that can
     231     match it will cause an ICE.
     232  
     233  int __RTL (startwith ("peephole2")) foo16 (int *a)
     234  {
     235  (function "foo16"
     236    (insn-chain
     237      (cnote 1 NOTE_INSN_DELETED)
     238      (block 2
     239        (edge-from entry (flags "FALLTHRU"))
     240        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
     241        (cinsn 100 (set (reg:SI r3)
     242  		      (mem/c:SI (reg:SI r0) [0 S4 A64])) "/home/matmal01/test.c":18)
     243        (cinsn 101 (set (reg:SI r2)
     244  		      (mem/c:SI (plus:SI (reg:SI r0) (const_int 4)) [0 S4 A32])) "/home/matmal01/test.c":18)
     245        (cinsn 103 (parallel
     246  		  [(set (reg:SI r0)
     247  			(and:SI (reg:SI r3) (reg:SI r2)))
     248  		  (clobber (reg:CC cc))]) "/home/matmal01/test.c":18
     249         (expr_list:REG_DEAD (reg:SI r2)
     250  	(expr_list:REG_DEAD (reg:SI r3)
     251  	 (nil))))
     252        (edge-to exit (flags "FALLTHRU"))
     253      ) ;; block 2
     254    ) ;; insn-chain
     255    (crtl
     256      (return_rtx
     257        (reg/i:SI r0)
     258      ) ;; return_rtx
     259    ) ;; crtl
     260  ) ;; function "main"
     261  }
     262  */
     263  
     264  
     265  /* Making patterns that will behave differently between the LDM/STM peepholes
     266     and LDRD/STRD peepholes.
     267     gen_operands_ldrd_strd() uses peep2_find_free_register() to find spare
     268     registers to use.
     269     peep2_find_free_register() only ever returns registers marked in
     270     call_used_regs, hence we make sure to leave register 2 and 3 available (as
     271     they are always on in the defaults marked by CALL_USED_REGISTERS).  */
     272  
     273  /* gen_operands_ldrd_strd() purposefully finds an even register to look at
     274     which would treat the following pattern differently to the stm peepholes.
     275   */
     276  int __RTL (startwith ("peephole2")) foo_x1 (int *a)
     277  {
     278  (function "foo_x1"
     279    (insn-chain
     280      (cnote 1 NOTE_INSN_DELETED)
     281      (block 2
     282        (edge-from entry (flags "FALLTHRU"))
     283        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
     284        (cinsn 99  (set (reg:SI r5)
     285  		      (const_int 1)) "/home/matmal01/test.c":18)
     286        (cinsn 101 (set (mem/c:SI (reg:SI r0) [0 S4 A64])
     287  		      (reg:SI r5)) "/home/matmal01/test.c":18)
     288        (cinsn 100 (set (reg:SI r5)
     289  		      (const_int 0)) "/home/matmal01/test.c":18)
     290        (cinsn 102 (set (mem/c:SI (plus:SI (reg:SI r0) (const_int 4)) [0 S4 A32])
     291  		      (reg:SI r5)) "/home/matmal01/test.c":18
     292         (expr_list:REG_DEAD (reg:SI r5)
     293  	 (nil)))
     294        (edge-to exit (flags "FALLTHRU"))
     295      ) ;; block 2
     296    ) ;; insn-chain
     297    (crtl
     298      (return_rtx
     299        (reg/i:SI r0)
     300      ) ;; return_rtx
     301    ) ;; crtl
     302  ) ;; function "main"
     303  }
     304  /* Ensure we generated a parallel that started with a set from an even register.
     305     i.e.
     306     (parallel [
     307       (set (mem
     308  	  (reg:SI <even>
     309     */
     310  /* { dg-final { scan-rtl-dump {Function foo_x1.*\(parallel \[\n[^\n]*\(set \(mem[^\n]*\n[^\n]*\(reg:SI (?:[12])?[2468] r(?:[12])?[2468]\).*Function foo_x2} "peephole2" } } */
     311  
     312  /* Like above gen_operands_ldrd_strd() would look to start with an even
     313     register while gen_const_stm_seq() doesn't care.  */
     314  int __RTL (startwith ("peephole2")) foo_x2 (int *a)
     315  {
     316  (function "foo_x2"
     317    (insn-chain
     318      (cnote 1 NOTE_INSN_DELETED)
     319      (block 2
     320        (edge-from entry (flags "FALLTHRU"))
     321        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
     322        (cinsn 99  (set (reg:SI r5)
     323  		      (const_int 1)) "/home/matmal01/test.c":18)
     324        (cinsn 100 (set (reg:SI r6)
     325  		      (const_int 0)) "/home/matmal01/test.c":18)
     326        (cinsn 101 (set (mem/c:SI (reg:SI r0) [0 S4 A64])
     327  		      (reg:SI r5)) "/home/matmal01/test.c":18)
     328        (cinsn 102 (set (mem/c:SI (plus:SI (reg:SI r0) (const_int 4)) [0 S4 A32])
     329  		      (reg:SI r6)) "/home/matmal01/test.c":18)
     330        (edge-to exit (flags "FALLTHRU"))
     331      ) ;; block 2
     332    ) ;; insn-chain
     333    (crtl
     334      (return_rtx
     335        (reg/i:SI r0)
     336      ) ;; return_rtx
     337    ) ;; crtl
     338  ) ;; function "main"
     339  }
     340  /* Ensure generated parallel starts with a set from an even register (as foo_x1).  */
     341  /* { dg-final { scan-rtl-dump {Function foo_x2.*\(parallel \[\n[^\n]*\(set \(mem[^\n]*\n[^\n]*\(reg:SI (?:[12])?[2468] r(?:[12])?[2468]\).*Function foo_x3} "peephole2" } } */
     342  
     343  /* When storing multiple values into a register that will be used later, ldrd
     344     searches for another register to use instead of just giving up.  */
     345  int __RTL (startwith ("peephole2")) foo_x3 (int *a)
     346  {
     347  (function "foo_x3"
     348    (insn-chain
     349      (cnote 1 NOTE_INSN_DELETED)
     350      (block 2
     351        (edge-from entry (flags "FALLTHRU"))
     352        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
     353        (cinsn 99  (set (reg:SI r3)
     354  		      (const_int 1)) "/home/matmal01/test.c":18)
     355        (cinsn 101 (set (mem/c:SI (reg:SI r0) [0 S4 A64])
     356  		      (reg:SI r3)) "/home/matmal01/test.c":18)
     357        (cinsn 100 (set (reg:SI r3)
     358  		      (const_int 0)) "/home/matmal01/test.c":18)
     359        (cinsn 102 (set (mem/c:SI (plus:SI (reg:SI r0) (const_int 4)) [0 S4 A32])
     360  		      (reg:SI r3)) "/home/matmal01/test.c":18)
     361        (cinsn 103 (set (reg:SI r0)
     362  		      (plus:SI (reg:SI r0) (reg:SI r3))) "/home/matmal01/test.c":18)
     363        (edge-to exit (flags "FALLTHRU"))
     364      ) ;; block 2
     365    ) ;; insn-chain
     366    (crtl
     367      (return_rtx
     368        (reg/i:SI r0)
     369      ) ;; return_rtx
     370    ) ;; crtl
     371  ) ;; function "main"
     372  }
     373  /* Ensure generated parallel starts with a set from an even register (as foo_x1).  */
     374  /* { dg-final { scan-rtl-dump {Function foo_x3.*\(parallel \[\n[^\n]*\(set \(mem[^\n]*\n[^\n]*\(reg:SI (?:[12])?[2468] r(?:[12])?[2468]\).*Function foo_x4} "peephole2" } } */
     375  
     376  /* ldrd gen_peephole2_11 but using plus 8 and plus 12 in the offsets.  */
     377  int __RTL (startwith ("peephole2")) foo_x4 (int *a)
     378  {
     379  (function "foo_x4"
     380    (insn-chain
     381      (cnote 1 NOTE_INSN_DELETED)
     382      (block 2
     383        (edge-from entry (flags "FALLTHRU"))
     384        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
     385        (cinsn 101 (set (reg:SI r2)
     386  		      (mem/c:SI (plus:SI (reg:SI r0) (const_int 8)) [0 S4 A64])) "/home/matmal01/test.c":18)
     387        (cinsn 102 (set (reg:SI r3)
     388  		      (mem/c:SI (plus:SI (reg:SI r0) (const_int 12)) [0 S4 A32])) "/home/matmal01/test.c":18)
     389        (cinsn 103 (set (reg:SI r0)
     390  		      (plus:SI (reg:SI r2) (reg:SI r3))) "/home/matmal01/test.c":18)
     391        (edge-to exit (flags "FALLTHRU"))
     392      ) ;; block 2
     393    ) ;; insn-chain
     394    (crtl
     395      (return_rtx
     396        (reg/i:SI r0)
     397      ) ;; return_rtx
     398    ) ;; crtl
     399  ) ;; function "main"
     400  }
     401  /* Ensure generated parallel starts with a set from the appropriate offset from
     402     register 0.
     403  (parallel [
     404  	    (set (reg:SI ...
     405  		(mem/c:SI (plus:SI (reg:SI 0 r0)
     406  			(const_int 8 .*
     407  */
     408  /* { dg-final { scan-rtl-dump {Function foo_x4.*\(parallel \[\n[^\n]*\(set \(reg:SI[^\n]*\n *\(mem/c:SI \(plus:SI \(reg:SI 0 r0\)\n *\(const_int 8.*Function foo_x5} "peephole2" } } */
     409  
     410  /* strd gen_peephole2_12 but using plus 8 and plus 12 in the offsets.  */
     411  int __RTL (startwith ("peephole2")) foo_x5 (int *a)
     412  {
     413  (function "foo12"
     414    (insn-chain
     415      (cnote 1 NOTE_INSN_DELETED)
     416      (block 2
     417        (edge-from entry (flags "FALLTHRU"))
     418        (cnote 3 [bb 2] NOTE_INSN_BASIC_BLOCK)
     419        (cinsn 101 (set (mem/c:SI (plus:SI (reg:SI r0) (const_int 8)) [0 S4 A64])
     420  		      (reg:SI r2)) "/home/matmal01/test.c":18)
     421        (cinsn 102 (set (mem/c:SI (plus:SI (reg:SI r0) (const_int 12)) [0 S4 A32])
     422  		      (reg:SI r3)) "/home/matmal01/test.c":18)
     423        (edge-to exit (flags "FALLTHRU"))
     424      ) ;; block 2
     425    ) ;; insn-chain
     426    (crtl
     427      (return_rtx
     428        (reg/i:SI r0)
     429      ) ;; return_rtx
     430    ) ;; crtl
     431  ) ;; function "main"
     432  }
     433  /* Ensure generated parallel starts with a set to the appropriate offset from
     434     register 0.  */
     435  /* { dg-final { scan-rtl-dump {Function foo_x5.*\(parallel \[\n[^\n]*\(set \(mem/c:SI \(plus:SI \(reg:SI 0 r0\)\n *\(const_int 8.*$} "peephole2" } } */
     436  
     437  
     438  /* { dg-final { scan-assembler-not "ldm" } } */
     439  /* { dg-final { scan-assembler-not "stm" } } */
     440  /* { dg-final { scan-assembler-times {ldrd\tr[2468], \[r0\]} 4 } } */
     441  /* { dg-final { scan-assembler-times {ldrd\tr[2468], \[r0, #8\]} 1 } } */
     442  /* { dg-final { scan-assembler-times {strd\tr[2468], \[r0\]} 6 } } */
     443  /* { dg-final { scan-assembler-times {strd\tr[2468], \[r0, #8\]} 1 } } */