This is an overhaul of __builtin_avr_delay_cycles.

Ok for trunk?

Johann

--

AVR: Overhaul __builtin_avr_delay_cycles code generation.

- In 2-byte loops, don't force scratch regs into constraint "w".

- Use a C function for asm output.  It will emit SBIW if possible.

gcc/
        * config/avr/avr-protos.h (avr_out_delay_loop): New proto.
        * config/avr/avr.cc (avr_out_delay_loop): New function.
        (avr_adjust_insn_length) [ADJUST_LEN_DELAY_LOOP]: Handle case.
        (avr_expand_delay_cycles): Overhaul.  Allow loop counts of
        zero; they represent a power of 2.
        * config/avr/avr.md (adjust_len) [delay_loop]: Add.
        (*delay_cycles_1): Use avr_out_delay_loop for asm out.
        (*delay_cycles_4): Same.
        (*delay_cycles_3): Same.
        (*delay_cycles_2): Same.  Relax constraints to "d".
        Use two QImode scratch regs instead of one HImode one.
diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h
index 3b688bb25e5..ef71d3b98bc 100644
--- a/gcc/config/avr/avr-protos.h
+++ b/gcc/config/avr/avr-protos.h
@@ -119,6 +119,7 @@ extern const char* avr_out_xload (rtx_insn *, rtx*, int*);
 extern const char* avr_out_fload (rtx_insn *, rtx*, int*);
 extern const char* avr_out_cpymem (rtx_insn *, rtx*, int*);
 extern const char* avr_out_insert_bits (rtx*, int*);
+extern const char* avr_out_delay_loop (rtx_insn*, rtx*, int*);
 extern bool avr_popcount_each_byte (rtx, int, int);
 extern bool avr_xor_noclobber_dconst (rtx, int);
 extern bool avr_has_nibble_0xf (rtx);
diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc
index 90865ffa7da..4e50a28a9a3 100644
--- a/gcc/config/avr/avr.cc
+++ b/gcc/config/avr/avr.cc
@@ -11489,6 +11489,7 @@ avr_adjust_insn_length (rtx_insn *insn, int len)
     case ADJUST_LEN_ADD_GE0: avr_out_add_msb (insn, op, GE, &len); break;
     case ADJUST_LEN_ADD_LT0: avr_out_add_msb (insn, op, LT, &len); break;
 
+    case ADJUST_LEN_DELAY_LOOP: avr_out_delay_loop (insn, op, &len); break;
     case ADJUST_LEN_INSV_NOTBIT: avr_out_insert_notbit (insn, op, &len); break;
 
     default:
@@ -15800,62 +15801,124 @@ static void
 avr_expand_delay_cycles (rtx operands0)
 {
   unsigned HOST_WIDE_INT cycles = UINTVAL (operands0) & GET_MODE_MASK (SImode);
-  unsigned HOST_WIDE_INT cycles_used;
-  unsigned HOST_WIDE_INT loop_count;
+  rtx memv = avr_mem_clobber ();
 
-  if (IN_RANGE (cycles, 83886082, 0xFFFFFFFF))
+  if (cycles <= 6)
     {
-      loop_count = ((cycles - 9) / 6) + 1;
-      cycles_used = ((loop_count - 1) * 6) + 9;
-      emit_insn (gen_delay_cycles_4 (gen_int_mode (loop_count, SImode),
-				     avr_mem_clobber ()));
-      cycles -= cycles_used;
+      // No loop needed, a sequence of NOPs will do it.
     }
-
-  if (IN_RANGE (cycles, 262145, 83886081))
+  else if (cycles < (3u << 8) + 0 + 3)
     {
-      loop_count = ((cycles - 7) / 5) + 1;
-      if (loop_count > 0xFFFFFF)
-	loop_count = 0xFFFFFF;
-      cycles_used = ((loop_count - 1) * 5) + 7;
-      emit_insn (gen_delay_cycles_3 (gen_int_mode (loop_count, SImode),
-				     avr_mem_clobber ()));
-      cycles -= cycles_used;
+      uint64_t loop_count = cycles / 3;
+      cycles -= 3 * loop_count;
+      emit_insn (gen_delay_cycles_1 (gen_int_mode (loop_count, QImode), memv));
     }
-
-  if (IN_RANGE (cycles, 768, 262144))
+  else if (cycles < (4u << 16) + 1 + 4)
+    {
+      uint64_t loop_count = (cycles - 1) / 4;
+      cycles -= 4 * loop_count + 1;
+      emit_insn (gen_delay_cycles_2 (gen_int_mode (loop_count, HImode), memv));
+    }
+  else if (cycles < (5u << 24) + 2 + 5)
     {
-      loop_count = ((cycles - 5) / 4) + 1;
-      if (loop_count > 0xFFFF)
-	loop_count = 0xFFFF;
-      cycles_used = ((loop_count - 1) * 4) + 5;
-      emit_insn (gen_delay_cycles_2 (gen_int_mode (loop_count, HImode),
-				     avr_mem_clobber ()));
-      cycles -= cycles_used;
+      uint64_t loop_count = (cycles - 2) / 5;
+      cycles -= 5 * loop_count + 2;
+      emit_insn (gen_delay_cycles_3 (gen_int_mode (loop_count, SImode), memv));
     }
+  else if (cycles < ((uint64_t) 6u << 32) + 3 + 6)
+    {
+      uint64_t loop_count = (cycles - 3) / 6;
+      cycles -= 6 * loop_count + 3;
+      emit_insn (gen_delay_cycles_4 (gen_int_mode (loop_count, SImode), memv));
+    }
+
+  for (; cycles >= 2; cycles -= 2)
+    emit_insn (gen_nopv (GEN_INT (2)));
+
+  if (cycles == 1)
+    emit_insn (gen_nopv (GEN_INT (1)));
+}
+
+
+/* Output a delay loop generated by __builtin_avr_delay_cycles.
+   XOP[0] is the loop count.  A loop count of 0 represents a power of 2.
+   XOP[1] is a volatile memory tag and unused.
+   XOP[2]... are QImode scratch regs >= R16.
+   INSN is a PARALLEL with an UNSPECV_DELAY_CYCLES as its first element.
+   The 2nd element of that unspecv is a CONST_INT specifying the number
+   of loop bytes.   Returns "".
+   PLEN != 0: Set *PLEN to the code length in words.  Don't output anything.
+   PLEN == 0: Output instructions.  */
 
-  if (IN_RANGE (cycles, 6, 767))
+const char *
+avr_out_delay_loop (rtx_insn *insn, rtx *xop, int *plen)
+{
+  rtx xunspecv = XVECEXP (PATTERN (insn), 0, 0);
+  rtx xbytes = XVECEXP (xunspecv, 0, 1);
+  gcc_assert (XINT (xunspecv, 1) == UNSPECV_DELAY_CYCLES);
+  const int n_bytes = INTVAL (xbytes);
+
+  // Gather the scratch regs until rmask's bits represent all of them.
+  unsigned rmask = 0;
+  for (int i = 2; popcount_hwi (rmask) < n_bytes; ++i)
+    for (auto regno = REGNO (xop[i]); regno < END_REGNO (xop[i]); ++regno)
+      rmask |= 1u << regno;
+
+  // Set xregs[] to the scratch regs such that SBIW becomes more likely.
+  rtx xregs[4];
+
+  // Go search a reg that can do SBIW.
+  bool sbiw_p = false;
+  for (int regno = REG_24; regno < REG_32; regno += 2)
+    if (AVR_HAVE_ADIW && (3 & (rmask >> regno)) == 3)
+      {
+	rmask &= ~(3u << regno);
+	xregs[0] = all_regs_rtx[regno];
+	xregs[1] = all_regs_rtx[regno + 1];
+	sbiw_p = true;
+	break;
+      }
+
+  // Also gather the remaining regs into xregs[].
+  for (int i = 2 * sbiw_p; rmask != 0; ++i)
     {
-      loop_count = cycles / 3;
-      if (loop_count > 255)
-	loop_count = 255;
-      cycles_used = loop_count * 3;
-      emit_insn (gen_delay_cycles_1 (gen_int_mode (loop_count, QImode),
-				     avr_mem_clobber ()));
-      cycles -= cycles_used;
+      gcc_assert (i < n_bytes);
+      const int regno = ctz_hwi (rmask);
+      xregs[i] = all_regs_rtx[regno];
+      rmask &= ~(1u << regno);
     }
 
-  while (cycles >= 2)
+  // Now we have all the information needed for instruction output.
+
+  if (plen)
+    *plen = 0;
+
+  // Print the number of cycles consumed by the LDIs and the loop.
+  if (flag_verbose_asm || flag_print_asm_name)
     {
-      emit_insn (gen_nopv (GEN_INT (2)));
-      cycles -= 2;
+      const auto n_loops = xop[0] == const0_rtx
+	? (unsigned HOST_WIDE_INT) 1 << (8 * n_bytes)
+	: UINTVAL (xop[0]);
+      const uint64_t n_cycles = n_bytes + n_loops * (2 + n_bytes) - 1;
+      rtx xcycles = gen_int_mode (n_cycles, DImode);
+      avr_asm_len (";; cycles = %0", &xcycles, plen, 0);
     }
 
-  if (cycles == 1)
+  // Load the loop count into them scratch regs.
+  for (int i = 0; i < n_bytes; ++i)
     {
-      emit_insn (gen_nopv (GEN_INT (1)));
-      cycles--;
+      rtx op[2] = { xregs[i], avr_byte (xop[0], i) };
+      avr_asm_len ("ldi %0,%1", op, plen, 1);
     }
+
+  // Loop body: xregs[] -= 1.
+  avr_asm_len (sbiw_p
+	       ? "1: sbiw %0,1"
+	       : "1: subi %0,1", xregs, plen, 1);
+  for (int i = 1 + sbiw_p; i < n_bytes; ++i)
+    avr_asm_len ("sbci %0,0", &xregs[i], plen, 1);
+
+  return avr_asm_len ("brne 1b", xop, plen, 1);
 }
 
 
diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md
index 6f78290fe38..57d63a8f746 100644
--- a/gcc/config/avr/avr.md
+++ b/gcc/config/avr/avr.md
@@ -170,7 +170,7 @@ (define_attr "adjust_len"
    ashlsi, ashrsi, lshrsi,
    ashlpsi, ashrpsi, lshrpsi,
    add_lt0, add_ge0,
-   insert_bits, insv_notbit, insv, set_some,
+   insert_bits, insv_notbit, insv, set_some, delay_loop,
    add_set_ZN, add_set_N, cmp_uext, cmp_sext, cmp_lsr,
    no"
   (const_string "no"))
@@ -8312,39 +8312,39 @@ (define_insn "*delay_cycles_1"
    (clobber (match_scratch:QI 2 "=&d"))
    (clobber (reg:CC REG_CC))]
   "reload_completed"
-  "ldi %2,lo8(%0)
-1:	dec %2
-	brne 1b"
-  [(set_attr "length" "3")])
+  {
+    return avr_out_delay_loop (insn, operands, nullptr);
+  }
+  [(set_attr "adjust_len" "delay_loop")])
 
 (define_insn_and_split "delay_cycles_2"
-  [(unspec_volatile [(match_operand:HI 0 "const_int_operand" "n,n")
+  [(unspec_volatile [(match_operand:HI 0 "const_int_operand" "n")
                      (const_int 2)]
                     UNSPECV_DELAY_CYCLES)
    (set (match_operand:BLK 1 "" "")
         (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER))
-   (clobber (match_scratch:HI 2 "=&w,&d"))]
+   (clobber (match_scratch:QI 2 "=&d"))
+   (clobber (match_scratch:QI 3 "=&d"))]
   ""
   "#"
   "&& reload_completed"
   [(scratch)]
-  { DONE_ADD_CCC }
-  [(set_attr "isa" "adiw,no_adiw")])
+  { DONE_ADD_CCC })
 
 (define_insn "*delay_cycles_2"
-  [(unspec_volatile [(match_operand:HI 0 "const_int_operand" "n,n")
+  [(unspec_volatile [(match_operand:HI 0 "const_int_operand" "n")
                      (const_int 2)]
                     UNSPECV_DELAY_CYCLES)
    (set (match_operand:BLK 1 "" "")
         (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER))
-   (clobber (match_scratch:HI 2 "=&w,&d"))
+   (clobber (match_scratch:QI 2 "=&d"))
+   (clobber (match_scratch:QI 3 "=&d"))
    (clobber (reg:CC REG_CC))]
   "reload_completed"
-  "@
-	ldi %A2,lo8(%0)\;ldi %B2,hi8(%0)\n1:	sbiw %A2,1\;brne 1b
-	ldi %A2,lo8(%0)\;ldi %B2,hi8(%0)\n1:	subi %A2,1\;sbci %B2,0\;brne 1b"
-  [(set_attr "length" "4,5")
-   (set_attr "isa" "adiw,no_adiw")])
+  {
+    return avr_out_delay_loop (insn, operands, nullptr);
+  }
+  [(set_attr "adjust_len" "delay_loop")])
 
 (define_insn_and_split "delay_cycles_3"
   [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "n")
@@ -8372,14 +8372,10 @@ (define_insn "*delay_cycles_3"
    (clobber (match_scratch:QI 4 "=&d"))
    (clobber (reg:CC REG_CC))]
   "reload_completed"
-  "ldi %2,lo8(%0)
-	ldi %3,hi8(%0)
-	ldi %4,hlo8(%0)
-1:	subi %2,1
-	sbci %3,0
-	sbci %4,0
-	brne 1b"
-  [(set_attr "length" "7")])
+  {
+    return avr_out_delay_loop (insn, operands, nullptr);
+  }
+  [(set_attr "adjust_len" "delay_loop")])
 
 (define_insn_and_split "delay_cycles_4"
   [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "n")
@@ -8409,16 +8405,10 @@ (define_insn "*delay_cycles_4"
    (clobber (match_scratch:QI 5 "=&d"))
    (clobber (reg:CC REG_CC))]
   "reload_completed"
-  "ldi %2,lo8(%0)
-	ldi %3,hi8(%0)
-	ldi %4,hlo8(%0)
-	ldi %5,hhi8(%0)
-1:	subi %2,1
-	sbci %3,0
-	sbci %4,0
-	sbci %5,0
-	brne 1b"
-  [(set_attr "length" "9")])
+  {
+    return avr_out_delay_loop (insn, operands, nullptr);
+  }
+  [(set_attr "adjust_len" "delay_loop")])
 
 
 ;; __builtin_avr_insert_bits

Reply via email to