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