diff options
author | Georg-Johann Lay <avr@gjlay.de> | 2025-09-04 22:03:31 +0200 |
---|---|---|
committer | Georg-Johann Lay <avr@gjlay.de> | 2025-09-05 14:38:31 +0200 |
commit | e3d14112b266479da9f8fd9c304e0ae15bf0ad25 (patch) | |
tree | 09a38d9c0290e01a41d18af5da66ca3701876f71 | |
parent | 2965a67c7ef34de74904b600a3e4a4cc858ea37b (diff) | |
download | gcc-e3d14112b266479da9f8fd9c304e0ae15bf0ad25.zip gcc-e3d14112b266479da9f8fd9c304e0ae15bf0ad25.tar.gz gcc-e3d14112b266479da9f8fd9c304e0ae15bf0ad25.tar.bz2 |
AVR: target/121794 - Invoke zero_reg less.
There are some cases where involing zero_reg is not needed and
where there are other sequences with the same efficiency.
An example is to use SBCI R,0 instead of SBC R,__zero_reg__
when R >= R16. This may turn out to be better for small ISRs.
PR target/121794
gcc/
* config/avr/avr.cc (avr_out_compare): Only use zero_reg
when there is no other sequence of the same length.
(avr_out_plus_ext): Same.
(avr_out_plus_1): Same.
-rw-r--r-- | gcc/config/avr/avr.cc | 74 |
1 files changed, 47 insertions, 27 deletions
diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc index ae49d4d..6c7ad4f 100644 --- a/gcc/config/avr/avr.cc +++ b/gcc/config/avr/avr.cc @@ -6597,34 +6597,14 @@ avr_out_compare (rtx_insn *insn, rtx *xop, int *plen) } } - /* Comparing against 0 is easy. */ + /* Otherwise, compare a single byte with CP / CPC or equivalent. */ - if (val8 == 0) - { - avr_asm_len (i == start - ? "cp %0,__zero_reg__" - : "cpc %0,__zero_reg__", xop, plen, 1); - continue; - } + const bool ldreg_p = test_hard_reg_class (LD_REGS, xop[0]); - /* Upper registers can compare and subtract-with-carry immediates. - Notice that compare instructions do the same as respective subtract - instruction; the only difference is that comparisons don't write - the result back to the target register. */ - - if (test_hard_reg_class (LD_REGS, xop[0])) + if (ldreg_p && i == start) { - if (i == start) - { - avr_asm_len ("cpi %0,%1", xop, plen, 1); - continue; - } - else if (reg_unused_after (insn, xreg)) - { - avr_asm_len ("sbci %0,%1", xop, plen, 1); - changed[i] = true; - continue; - } + avr_asm_len ("cpi %0,%1", xop, plen, 1); + continue; } /* When byte comparisons for an EQ or NE comparison look like @@ -6641,7 +6621,7 @@ avr_out_compare (rtx_insn *insn, rtx *xop, int *plen) for (int j = start; j < i && ! found; ++j) if (val8 == avr_uint8 (xval, j) - // Make sure that we didn't clobber x[j] above. + // Make sure that we didn't clobber x[j] with SBCI. && ! changed[j]) { rtx op[] = { xop[0], avr_byte (xreg, j) }; @@ -6653,6 +6633,37 @@ avr_out_compare (rtx_insn *insn, rtx *xop, int *plen) continue; } + /* Upper registers can SBCI which has the same effect like CPC on + SREG, but it writes back the result of the subtraction to %0. + Therefore, SBCI can only replace CPC when %0 is unused after, + or when the comparison is against 0x..0000. */ + + if (ldreg_p && i > start) + { + bool zero_p = true; + + for (int j = start; j <= i; ++j) + zero_p &= avr_uint8 (xval, j) == 0; + + if (zero_p || reg_unused_after (insn, xreg)) + { + avr_asm_len ("sbci %0,%1", xop, plen, 1); + changed[i] = !zero_p; + continue; + } + } + + /* Comparing against 0 is easy, but only invoke __zero_reg__ when + nothing else has been found. This is better for small ISRs. */ + + if (val8 == 0) + { + avr_asm_len (i == start + ? "cp %0,__zero_reg__" + : "cpc %0,__zero_reg__", xop, plen, 1); + continue; + } + /* Must load the value into the scratch register. */ gcc_assert (REG_P (xop[2])); @@ -8533,11 +8544,17 @@ avr_out_plus_ext (rtx_insn *insn, rtx *yop, int *plen) const int n_bytes0 = GET_MODE_SIZE (GET_MODE (xop[0])); const int n_bytes1 = GET_MODE_SIZE (GET_MODE (xop[1])); rtx msb1 = all_regs_rtx[n_bytes1 - 1 + REGNO (xop[1])]; + // Prefer SBCI *,0 over SBC *,__zero_reg__. + const bool sbci_p = add == MINUS && n_bytes0 > n_bytes1 + ? test_hard_reg_class (LD_REGS, avr_byte (xop[0], n_bytes1)) + : false; const char *const s_ADD = add == PLUS ? "add %0,%1" : "sub %0,%1"; const char *const s_ADC = add == PLUS ? "adc %0,%1" : "sbc %0,%1"; const char *const s_DEC = add == PLUS ? "adc %0,__zero_reg__" CR_TAB "sbrc %1,7" CR_TAB "dec %0" + : sbci_p + ? "sbci %0,0" CR_TAB "sbrc %1,7" CR_TAB "inc %0" : "sbc %0,__zero_reg__" CR_TAB "sbrc %1,7" CR_TAB "inc %0"; // A register that containts 8 copies of $1.msb. @@ -8582,6 +8599,8 @@ avr_out_plus_ext (rtx_insn *insn, rtx *yop, int *plen) regs[1] = msb1; avr_asm_len (s_DEC, regs, plen, 3); } + else if (sbci_p && regs[1] == zero_reg_rtx) + avr_asm_len ("sbci %0,0", regs, plen, 1); else avr_asm_len (s_ADC, regs, plen, 1); } @@ -8890,7 +8909,8 @@ avr_out_plus_1 (rtx xinsn, rtx *xop, int *plen, rtx_code code, { if (started) avr_asm_len (code == PLUS - ? "adc %0,__zero_reg__" : "sbc %0,__zero_reg__", + ? "adc %0,__zero_reg__" + : ld_reg_p ? "sbci %0,0" : "sbc %0,__zero_reg__", op, plen, 1); continue; } |