https://gcc.gnu.org/g:384164f5a8aa2e45df0d598d62af6b5de280b6a8
commit r17-1561-g384164f5a8aa2e45df0d598d62af6b5de280b6a8 Author: Georg-Johann Lay <[email protected]> Date: Mon Jun 15 10:51:23 2026 +0200 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: --- gcc/config/avr/avr-protos.h | 1 + gcc/config/avr/avr.cc | 157 +++++++++++++++++++++++++++++++++----------- gcc/config/avr/avr.md | 58 +++++++--------- 3 files changed, 142 insertions(+), 74 deletions(-) diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index 3b688bb25e5c..ef71d3b98bc6 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 bfa794c32f10..49624b5a6b86 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,138 @@ 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)) + // Including LDIs, an N-byte loop takes + // + // N + loop_count * (N + 2) - 1 cycles + // + // where loop_count denotes the start value of the N-byte loop counter. + // The maximum value that can be loaded into loop_count is 2^{8 * N}, + // where the maxmium value is realized by loop_count = 0. + // Up to N + 2 - 1 cycles can be added by trailing NOPs without + // impeding the loop_count calculation, so that we arrive at a + // condition of + // + // cycles < ((N + 2) << (8 * N)) + 2 * N + 1 + // + // for the maximal delay that can be achieved with an N-byte loop. + 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 here. + 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 6f78290fe38e..57d63a8f7463 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -170,7 +170,7 @@ 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 @@ (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 @@ (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 @@ (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
