Am 10.07.24 um 01:17 schrieb Jeff Law:
On 7/9/24 4:03 AM, Georg-Johann Lay wrote:
Hi Jeff,
This patch adds peephole2s and insns to make better use of
instructions that set condition code (SREG) as a byproduct.
Of course with cc0 all this was *much* simpler... so here we go;
adding CCNmode and CCZNmode, and extra insns that do arith + CC.
No new regressions.
Ok for master?
Johann
--
AVR: target/115830 - Make better use of SREG.N and SREG.Z.
This patch adds new CC modes CCN and CCZN for operations that
set SREG.N, resp. SREG.Z and SREG.N. Add a bunch of peephole2
patterns to generate new compute + branch insns that make use
of the Z and N flags. Most of these patterns need their own
asm output routines that don't do all the micro-optimizations
that the ordinary outputs may perform, as the latter have no
requirement to set CC in a usable way. Pass peephole2 is run
a second time so all patterns get a chance to match.
PR target/115830
gcc/
* config/avr/avr-modes.def (CCN, CCZN): New CC_MODEs.
* config/avr/avr-protos.h (ret_cond_branch): Adjust.
(avr_out_plus_set_N, avr_op8_ZN_operator,
avr_out_op8_set_ZN, avr_len_op8_set_ZN): New protos.
* config/avr/avr.cc (ret_cond_branch): Remove "reverse"
argument (was always false) and respective code.
Pass cc_overflow_unusable as an argument.
(cond_string): Add bool cc_overflow_unusable argument.
(avr_print_operand) ['L']: Like 'j' but overflow unusable.
['K']: Like 'k' but overflow unusable.
(avr_out_plus_set_ZN): Also support adding -2 and +2.
(avr_out_plus_set_N, avr_op8_ZN_operator): New functions.
(avr_out_op8_set_ZN, avr_len_op8_set_ZN): New functions.
(avr_adjust_insn_length) [ADJUST_LEN_ADD_SET_N]: Hande case.
(avr_class_max_nregs): All MODE_CCs occupy one hard reg.
(avr_hard_regno_nregs): Same.
(avr_hard_regno_mode_ok) [REG_CC]: Allow all MODE_CC.
(pass_manager.h): Include it.
(avr_option_override): Run peephole2 a second time.
* config/avr/avr.md (adjust_len) [add_set_N]: New.
(ALLCC, CCN_CCZN): New mode iterators.
(CCname): New mode attribute.
(eqnegtle, cmp_signed, op8_ZN): New code iterators.
(swap, SWAP, tstMSB): New code attributes.
(branch): Handle CCNmode and CCZNmode. Assimilate...
(difficult_branch): ...this insn.
(p1m1): Turn into p2m2.
(gen_add_for_<code>_<mode>): Adjust to CCNmode and CCZNmode.
Extend peephole2s that produce them.
(*add.for.eqne.<mode>): Extend to
*add.for.<CCN_CCZN:mode>.<QISI:mode>.
(*ashift.for.ccn.<mode>): New insns and peephole2s to make them.
(*op8.for.cczn.<code>): New insns and peephole2s to make them.
* config/avr/predicates.md (const_1_to_3_operand)
(abs1_abs2_operand, signed_comparison_operator)
(op8_ZN_operator): New predicates.
gcc/testsuite/
* gcc.target/avr/pr115830-add-c.c: New test.
* gcc.target/avr/pr115830-add-i.c: New test.
* gcc.target/avr/pr115830-and.c: New test.
* gcc.target/avr/pr115830-asl.c: New test.
* gcc.target/avr/pr115830-asr.c: New test.
* gcc.target/avr/pr115830-ior.c: New test.
* gcc.target/avr/pr115830-lsr.c: New test.
* gcc.target/avr/pr115830-asl32.c: New test.
I was going to throw this into my tester, but the avr.md part of the
patch failed. I'm guessing the patch needs minor updates due to some
kind of changes on the trunk.
Hi Jeff,
The previous change to avr.md was several days ago, and should not
interfere with this one. Anyway, I rebased the patch against
master and attached it below. The patch is atop the ref in the patch
file name : https://gcc.gnu.org/r15-1935
It looks like avr exposes the CC register early, creating references to
it during expansion to RTL. Presumably this means you've got a
reasonable way to reload values, particularly address arithmetic without
impacting the CC state?
No. CC only comes into existence after reload in .split2 and in some
cases in .avr-ifelse. reloads may clobber CC, so there is no way
to split cbranch insn prior to relaod.
It looks like you're relying heavily on peep2 patterns. Did you explore
using cmpelim?
Some question I have:
- compare-elim.cc states that the comparisons must look like
[(set (reg:CC) (compare:CC (reg) (reg_or_immediate)))]
which is not always the case, in particular the patterns
may have clobbers. compare-elim uses single_set, which
is true for the pattern with a clobber / scratch. However,
presence / absence of a clobber reg has to be taken into
account when deciding whether a transformation into cc
other than CCmode is possible. compare-elim only supplies
the SET_SRC, but not the scratch_operand to SELECT_CC_MODE.
- The internals says that some optimizations / transforms
will happen prior to reload (e.g. .combine). This is not
possible since CCmode exists only since .split2.
- Insn combine may have transformed some comparisons, e.g.
sign test are represented as a zero_extract + skip like
in the sbrx_branch insns. This means that compares might
have a non-canonical form at .cmpelim. But a CCNmode
compare is better still than an sbrx_branch. Moreover,
CANONICALIZE_COMPARISON also runs already at insn combine,
so that compares have a form not recognized by cmpelim.
- Comparisons may look like if (0 <code> ref) which is
a form not supported by compare-elim.
- The avr-ifelse pass performs some optimizations on
code as produced by if-else decision trees from switch-case.
I don't see how that would fit into cmpelim because not
all information is provided by compare-elim that's required
to perform the required optimization. FYI this pass is a
replacement of machine dependent reorg due to PR109650.
So as is appears, the current peep2 approach will yield
the best results?
I would add some notes to the commit message why we do
not use cmpelim?
pr115830-1.diff
diff --git a/gcc/config/avr/avr-modes.def b/gcc/config/avr/avr-modes.def
index e0633d680d5..77b05dd8509 100644
--- a/gcc/config/avr/avr-modes.def
+++ b/gcc/config/avr/avr-modes.def
@@ -18,6 +18,11 @@
FRACTIONAL_INT_MODE (PSI, 24, 3);
+/* Used when the N (and Z) flag(s) of SREG are set.
+ The N flag indicates whether the value is negative. */
+CC_MODE (CCN);
+CC_MODE (CCZN);
N and Z are probably the easiest to model. C is usually worth modeling
as well. But obviously that could be a follow-up.
+ if (ival >= +1) avr_asm_len ("inc %0", xop, plen, 1);
+ if (ival == +2) avr_asm_len ("inc %0", xop, plen, 1);
+ if (ival <= -1) avr_asm_len ("dec %0", xop, plen, 1);
+ if (ival == -2) avr_asm_len ("dec %0", xop, plen, 1);
Formatting issue. Bring the avr_asm_len calls down to their own lines,
indented appropriately. However, if this is a common style in avr.cc,
particularly in that same function then it's OK to leave as-is.
It is not very common. In some cases I find such code sequences easier
to grasp. I can use strict formatting of course.
Ok with that change?
Not really ready to ACK or NAK at this point.
jeff
So I guess mainly due to the cmpelim (non-)alternative?
Johann
commit 0537fd9b3f564358b4b90ff2e8c6d615b574d184
AVR: target/115830 - Make better use of SREG.N and SREG.Z.
This patch adds new CC modes CCN and CCZN for operations that
set SREG.N, resp. SREG.Z and SREG.N. Add a bunch of peephole2
patterns to generate new compute + branch insns that make use
of the Z and N flags. Most of these pattern need their own
asm output routines that don't do all the micro-optimizations
that the ordinary outputs may perform, as the latter have no
requirement to set CC in a usable way. Pass peephole2 is run
a second time so all patterns get a chance to match.
PR target/115830
gcc/
* config/avr/avr-modes.def (CCN, CCZN): New CC_MODEs.
* config/avr/avr-protos.h (ret_cond_branch): Adjust.
(avr_out_plus_set_N, avr_op8_ZN_operator,
avr_out_op8_set_ZN, avr_len_op8_set_ZN): New protos.
* config/avr/avr.cc (ret_cond_branch): Remove "reverse"
argument (was always false) and respective code.
Pass cc_overflow_unusable as an argument.
(cond_string): Add bool cc_overflow_unusable argument.
(avr_print_operand) ['L']: Like 'j' but overflow unusable.
['K']: Like 'k' but overflow unusable.
(avr_out_plus_set_ZN): Also support adding -2 and +2.
(avr_out_plus_set_N, avr_op8_ZN_operator): New functions.
(avr_out_op8_set_ZN, avr_len_op8_set_ZN): New functions.
(avr_adjust_insn_length) [ADJUST_LEN_ADD_SET_N]: Hande case.
(avr_class_max_nregs): All MODE_CCs occupy one hard reg.
(avr_hard_regno_nregs): Same.
(avr_hard_regno_mode_ok) [REG_CC]: Allow all MODE_CC.
(pass_manager.h): Include it.
(avr_option_override): Run peephole2 a second time.
* config/avr/avr.md (adjust_len) [add_set_N]: New.
(ALLCC, CCN_CCZN): New mode iterators.
(CCname): New mode attribute.
(eqnegtle, cmp_signed, op8_ZN): New code iterators.
(swap, SWAP, tstMSB): New code attributes.
(branch): Handle CCNmode and CCZNmode. Assimilate...
(difficult_branch): ...this insn.
(p1m1): Turn into p2m2.
(gen_add_for_<code>_<mode>): Adjust to CCNmode and CCZNmode.
Extend peephole2s that produce them.
(*add.for.eqne.<mode>): Extend to *add.for.<CCN_CCZN:mode>.<QISI:mode>.
(*ashift.for.ccn.<mode>): New insns and peephole2s to make them.
(*op8.for.cczn.<code>): New insns and peephole2s to make them.
* config/avr/predicates.md (const_1_to_3_operand)
(abs1_abs2_operand, signed_comparison_operator)
(op8_ZN_operator): New predicates.
gcc/testsuite/
* gcc.target/avr/pr115830-add-c.c: New test.
* gcc.target/avr/pr115830-add-i.c: New test.
* gcc.target/avr/pr115830-and.c: New test.
* gcc.target/avr/pr115830-asl.c: New test.
* gcc.target/avr/pr115830-asr.c: New test.
* gcc.target/avr/pr115830-ior.c: New test.
* gcc.target/avr/pr115830-lsr.c: New test.
* gcc.target/avr/pr115830-asl32.c: New test.
(cherry picked from commit 5ceef98bef85dac0ee00499379478c9929fb8d1e)
diff --git a/gcc/config/avr/avr-modes.def b/gcc/config/avr/avr-modes.def
index e0633d680d5..77b05dd8509 100644
--- a/gcc/config/avr/avr-modes.def
+++ b/gcc/config/avr/avr-modes.def
@@ -18,6 +18,11 @@
FRACTIONAL_INT_MODE (PSI, 24, 3);
+/* Used when the N (and Z) flag(s) of SREG are set.
+ The N flag indicates whether the value is negative. */
+CC_MODE (CCN);
+CC_MODE (CCZN);
+
/* Make TA and UTA 64 bits wide.
128 bit wide modes would be insane on a 8-bit machine.
This needs special treatment in avr.cc and avr-lib.h. */
diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h
index dc23cfbf461..664513864fe 100644
--- a/gcc/config/avr/avr-protos.h
+++ b/gcc/config/avr/avr-protos.h
@@ -54,7 +54,7 @@ extern const char *avr_out_tsthi (rtx_insn *, rtx*, int*);
extern const char *avr_out_tstpsi (rtx_insn *, rtx*, int*);
extern const char *avr_out_compare (rtx_insn *, rtx*, int*);
extern const char *avr_out_compare64 (rtx_insn *, rtx*, int*);
-extern const char *ret_cond_branch (rtx x, int len, int reverse);
+extern const char *ret_cond_branch (rtx x, int len, machine_mode);
extern const char *avr_out_movpsi (rtx_insn *, rtx*, int*);
extern const char *avr_out_sign_extend (rtx_insn *, rtx*, int*);
extern const char *avr_out_insert_notbit (rtx_insn *, rtx*, int*);
@@ -62,6 +62,10 @@ extern const char *avr_out_insv (rtx_insn *, rtx*, int*);
extern const char *avr_out_extr (rtx_insn *, rtx*, int*);
extern const char *avr_out_extr_not (rtx_insn *, rtx*, int*);
extern const char *avr_out_plus_set_ZN (rtx*, int*);
+extern const char *avr_out_plus_set_N (rtx*, int*);
+extern const char *avr_out_op8_set_ZN (RTX_CODE, rtx*, int*);
+extern int avr_len_op8_set_ZN (RTX_CODE, rtx*);
+extern bool avr_op8_ZN_operator (rtx);
extern const char *avr_out_cmp_ext (rtx*, enum rtx_code, int*);
extern const char *ashlqi3_out (rtx_insn *insn, rtx operands[], int *len);
diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc
index d299fceb782..270fa0f49ba 100644
--- a/gcc/config/avr/avr.cc
+++ b/gcc/config/avr/avr.cc
@@ -54,6 +54,7 @@
#include "builtins.h"
#include "context.h"
#include "tree-pass.h"
+#include "pass_manager.h"
#include "print-rtl.h"
#include "rtl-iter.h"
@@ -157,7 +158,6 @@ static const char *out_movsi_mr_r (rtx_insn *, rtx[], int *);
static int get_sequence_length (rtx_insn *insns);
static int sequent_regs_live (void);
static const char *ptrreg_to_str (int);
-static const char *cond_string (enum rtx_code);
static int avr_num_arg_regs (machine_mode, const_tree);
static int avr_operand_rtx_cost (rtx, machine_mode, enum rtx_code,
int, bool);
@@ -1185,6 +1185,18 @@ avr_option_override (void)
init_machine_status = avr_init_machine_status;
avr_log_set_avr_log();
+
+ /* As long as peep2_rescan is not implemented, see
+ http://gcc.gnu.org/ml/gcc-patches/2011-10/msg02819.html
+ we add a second peephole2 run to get best results. */
+ {
+ opt_pass *extra_peephole2
+ = g->get_passes ()->get_pass_peephole2 ()->clone ();
+ struct register_pass_info peep2_2_info
+ = { extra_peephole2, "peephole2", 1, PASS_POS_INSERT_AFTER };
+
+ register_pass (&peep2_2_info);
+ }
}
/* Function to set up the backend function structure. */
@@ -3623,10 +3635,8 @@ ptrreg_to_str (int regno)
Used in conditional jump constructing */
static const char *
-cond_string (enum rtx_code code)
+cond_string (enum rtx_code code, bool cc_overflow_unusable)
{
- bool cc_overflow_unusable = false;
-
switch (code)
{
case NE:
@@ -3955,10 +3965,11 @@ avr_print_operand (FILE *file, rtx x, int code)
}
else if (GET_CODE (x) == CONST_STRING)
fputs (XSTR (x, 0), file);
- else if (code == 'j')
- fputs (cond_string (GET_CODE (x)), file);
- else if (code == 'k')
- fputs (cond_string (reverse_condition (GET_CODE (x))), file);
+ else if (code == 'j' || code == 'L')
+ fputs (cond_string (GET_CODE (x), code == 'L'), file);
+ else if (code == 'k' || code == 'K')
+ fputs (cond_string (reverse_condition (GET_CODE (x)), code == 'K'),
+ file);
else
avr_print_operand_address (file, VOIDmode, x);
}
@@ -4008,13 +4019,17 @@ avr_jump_mode (rtx x, rtx_insn *insn)
/* Return an AVR condition jump commands.
X is a comparison RTX.
LEN is a number returned by avr_jump_mode function.
- If REVERSE nonzero then condition code in X must be reversed. */
+ CCMODE is the mode of the comparison in CCmode, CCNmode, CCZNmode. */
const char *
-ret_cond_branch (rtx x, int len, int reverse)
+ret_cond_branch (rtx x, int len, machine_mode ccmode)
{
- RTX_CODE cond = reverse ? reverse_condition (GET_CODE (x)) : GET_CODE (x);
- bool cc_overflow_unusable = false;
+ bool cc_overflow_unusable = ccmode != CCmode;
+ RTX_CODE cond = GET_CODE (x);
+
+ if (ccmode == CCNmode)
+ // The N flag can only do < 0 and >= 0.
+ gcc_assert (cond == GE || cond == LT);
switch (cond)
{
@@ -4076,33 +4091,20 @@ ret_cond_branch (rtx x, int len, int reverse)
"brsh .+4" CR_TAB
"jmp %0"));
default:
- if (reverse)
+ switch (len)
{
- switch (len)
- {
- case 1:
- return "br%k1 %0";
- case 2:
- return ("br%j1 .+2" CR_TAB
- "rjmp %0");
- default:
- return ("br%j1 .+4" CR_TAB
- "jmp %0");
- }
- }
- else
- {
- switch (len)
- {
- case 1:
- return "br%j1 %0";
- case 2:
- return ("br%k1 .+2" CR_TAB
- "rjmp %0");
- default:
- return ("br%k1 .+4" CR_TAB
- "jmp %0");
- }
+ case 1:
+ return cc_overflow_unusable
+ ? "br%L1 %0"
+ : "br%j1 %0";
+ case 2:
+ return cc_overflow_unusable
+ ? "br%K1 .+2" CR_TAB "rjmp %0"
+ : "br%k1 .+2" CR_TAB "rjmp %0";
+ default:
+ return cc_overflow_unusable
+ ? "br%K1 .+4" CR_TAB "jmp %0"
+ : "br%k1 .+4" CR_TAB "jmp %0";
}
}
return "";
@@ -9455,6 +9457,41 @@ avr_out_plus (rtx insn, rtx *xop, int *plen, bool out_label)
}
+/* Output an addition with a compile-time constant that sets SREG.N:
+
+ XOP[0] += XOP[1]
+
+ where XOP[0] is a QI, HI, PSI or SI register, and XOP[1] is a compile-time
+ constant. XOP[2] is SCRATCH or a QI clobber reg. Return "".
+
+ If PLEN == NULL output the instructions.
+ If PLEN != NULL set *PLEN to the length of the sequence in words. */
+
+const char *
+avr_out_plus_set_N (rtx *xop, int *plen)
+{
+ gcc_assert (xop[1] != const0_rtx);
+
+ if (GET_MODE_SIZE (GET_MODE (xop[0])) == 1
+ && ! test_hard_reg_class (LD_REGS, xop[0]))
+ return avr_out_plus_set_ZN (xop, plen);
+
+ // The output function for vanilla additions, avr_out_plus_1, can be
+ // used because it always issues an operation on the MSB (except when
+ // the addend is zero).
+
+ rtx op[] = { xop[0], xop[0], xop[1], xop[2] };
+ int len_plus, len_minus;
+
+ avr_out_plus_1 (NULL_RTX, op, &len_plus, PLUS, UNKNOWN, 0, false);
+ avr_out_plus_1 (NULL_RTX, op, &len_minus, MINUS, UNKNOWN, 0, false);
+
+ avr_out_plus_1 (NULL_RTX, op, plen, len_minus < len_plus ? MINUS : PLUS,
+ UNKNOWN, 0, false);
+ return "";
+}
+
+
/* Output an instruction sequence for addition of REG in XOP[0] and CONST_INT
in XOP[1] in such a way that SREG.Z and SREG.N are set according to the
result. XOP[2] might be a d-regs clobber register. If XOP[2] is SCRATCH,
@@ -9478,13 +9515,17 @@ avr_out_plus_set_ZN (rtx *xop, int *plen)
// Number of bytes to operate on.
int n_bytes = GET_MODE_SIZE (mode);
- if (n_bytes == 1)
+ if (n_bytes == 1
+ && ! test_hard_reg_class (LD_REGS, xreg))
{
- if (INTVAL (xval) == 1)
- return avr_asm_len ("inc %0", xop, plen, 1);
+ gcc_assert (abs1_abs2_operand (xval, QImode));
+ int ival = (int) INTVAL (xval);
- if (INTVAL (xval) == -1)
- return avr_asm_len ("dec %0", xop, plen, 1);
+ if (ival >= +1) avr_asm_len ("inc %0", xop, plen, 1);
+ if (ival == +2) avr_asm_len ("inc %0", xop, plen, 1);
+ if (ival <= -1) avr_asm_len ("dec %0", xop, plen, 1);
+ if (ival == -2) avr_asm_len ("dec %0", xop, plen, 1);
+ return "";
}
if (n_bytes == 2
@@ -9571,6 +9612,136 @@ avr_out_plus_set_ZN (rtx *xop, int *plen)
}
+/* A helper worker for op8_ZN_operator. Allow
+
+ OP0 <code> op1
+
+ QImode operations that set SREG.N and SREG.Z in a usable way.
+ these are:
+
+ * OP0 is a QImode register, and
+ * OP1 is a QImode register or CONST_INT, and
+
+ the allowed operations is one of:
+
+ * SHIFTs with a const_int offset in { 1, 2, 3 }.
+ * MINUS and XOR with a register operand
+ * IOR and AND with a register operand, or d-reg + const_int
+ * PLUS with a register operand, or d-reg + const_int,
+ or a const_int in { -2, -1, 1, 2 }. */
+
+bool
+avr_op8_ZN_operator (rtx op)
+{
+ const RTX_CODE code = GET_CODE (op);
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ if (! register_operand (op0, QImode)
+ || ! (register_operand (op1, QImode)
+ || const_int_operand (op1, QImode)))
+ return false;
+
+ const bool reg1_p = REG_P (op1);
+ const bool ld_reg0_p = test_hard_reg_class (LD_REGS, op0);
+
+ switch (code)
+ {
+ default:
+ break;
+
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ return const_1_to_3_operand (op1, QImode);
+
+ case MINUS:
+ case XOR:
+ return reg1_p;
+
+ case IOR:
+ case AND:
+ return reg1_p || ld_reg0_p;
+
+ case PLUS:
+ return reg1_p || ld_reg0_p || abs1_abs2_operand (op1, QImode);
+ }
+
+ return false;
+}
+
+
+/* Output a QImode instruction sequence for
+
+ XOP[0] = XOP[0] <CODE> XOP[2]
+
+ where XOP[0] is a register, and the possible operands and CODEs
+ are according to avr_op8_ZN_operator() from above. Return "".
+
+ If PLEN == NULL, then output the instructions.
+ If PLEN != NULL, then set *PLEN to the length of the sequence in words. */
+
+const char *
+avr_out_op8_set_ZN (RTX_CODE code, rtx *xop, int *plen)
+{
+ const bool reg2_p = REG_P (xop[2]);
+ const int ival = CONST_INT_P (xop[2]) ? (int) INTVAL (xop[2]) : 0;
+
+ gcc_assert (op8_ZN_operator (gen_rtx_fmt_ee (code, QImode, xop[0], xop[2]),
+ QImode));
+ if (plen)
+ *plen = 0;
+
+ const char *tpl = nullptr;
+ int times = 1;
+
+ if (code == ASHIFT)
+ tpl = "lsl %0", times = ival;
+ else if (code == LSHIFTRT)
+ tpl = "lsr %0", times = ival;
+ else if (code == ASHIFTRT)
+ tpl = "asr %0", times = ival;
+ else if (code == MINUS)
+ tpl = "sub %0,%2";
+ else if (code == XOR)
+ tpl = "eor %0,%2";
+ else if (code == AND)
+ tpl = reg2_p ? "and %0,%2" : "andi %0,lo8(%2)";
+ else if (code == IOR)
+ tpl = reg2_p ? "or %0,%2" : "ori %0,lo8(%2)";
+ else if (code == PLUS)
+ {
+ if (ival
+ && ! test_hard_reg_class (LD_REGS, xop[0]))
+ {
+ tpl = ival > 0 ? "inc %0" : "dec %0";
+ times = ival > 0 ? ival : -ival;
+ }
+ else
+ tpl = reg2_p ? "add %0,%2" : "subi %0,lo8(%n2)";
+ }
+ else
+ gcc_unreachable();
+
+ for (int i = 0; i < times; ++i)
+ avr_asm_len (tpl, xop, plen, 1);
+
+ return "";
+}
+
+
+/* Used in the "length" attribute of insn "*op8.for.cczn.<code>". */
+
+int
+avr_len_op8_set_ZN (RTX_CODE code, rtx *xop)
+{
+ int len;
+ (void) avr_out_op8_set_ZN (code, xop, &len);
+
+ return len;
+}
+
+
/* Output bit operation (IOR, AND, XOR) with register XOP[0] and compile
time constant XOP[2]:
@@ -11022,6 +11193,7 @@ avr_adjust_insn_length (rtx_insn *insn, int len)
case ADJUST_LEN_INSERT_BITS: avr_out_insert_bits (op, &len); break;
case ADJUST_LEN_ADD_SET_ZN: avr_out_plus_set_ZN (op, &len); break;
+ case ADJUST_LEN_ADD_SET_N: avr_out_plus_set_N (op, &len); break;
case ADJUST_LEN_INSV_NOTBIT: avr_out_insert_notbit (insn, op, &len); break;
@@ -11210,7 +11382,7 @@ avr_assemble_integer (rtx x, unsigned int size, int aligned_p)
static unsigned char
avr_class_max_nregs (reg_class_t rclass, machine_mode mode)
{
- if (rclass == CC_REG && mode == CCmode)
+ if (rclass == CC_REG && GET_MODE_CLASS (mode) == MODE_CC)
return 1;
return CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD);
@@ -13750,7 +13922,7 @@ jump_over_one_insn_p (rtx_insn *insn, rtx dest)
static unsigned int
avr_hard_regno_nregs (unsigned int regno, machine_mode mode)
{
- if (regno == REG_CC && mode == CCmode)
+ if (regno == REG_CC && GET_MODE_CLASS (mode) == MODE_CC)
return 1;
return CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD);
@@ -13765,7 +13937,7 @@ static bool
avr_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
{
if (regno == REG_CC)
- return mode == CCmode;
+ return GET_MODE_CLASS (mode) == MODE_CC;
/* NOTE: 8-bit values must not be disallowed for R28 or R29.
Disallowing QI et al. in these regs might lead to code like
diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md
index 2783b8c986f..e4f55fb7f4f 100644
--- a/gcc/config/avr/avr.md
+++ b/gcc/config/avr/avr.md
@@ -171,7 +171,7 @@ (define_attr "adjust_len"
ashlsi, ashrsi, lshrsi,
ashlpsi, ashrpsi, lshrpsi,
insert_bits, insv_notbit, insv,
- add_set_ZN, cmp_uext, cmp_sext,
+ add_set_ZN, add_set_N, cmp_uext, cmp_sext,
no"
(const_string "no"))
@@ -277,6 +277,11 @@ (define_mode_iterator ALLs4 [SI SQ SA])
(define_mode_iterator ALLs234 [HI SI PSI
HQ HA SQ SA])
+(define_mode_iterator ALLCC [CC CCN CCZN])
+(define_mode_iterator CCN_CCZN [CCN CCZN])
+
+(define_mode_attr CCname [(CC "") (CCN "_N") (CCZN "_ZN")])
+
;; All supported move-modes
(define_mode_iterator MOVMODE [QI QQ UQQ
HI HQ UHQ HA UHA
@@ -319,6 +324,9 @@ (define_code_iterator bitop [xor ior and])
(define_code_iterator xior [xor ior])
(define_code_iterator eqne [eq ne])
(define_code_iterator gelt [ge lt])
+(define_code_iterator eqnegtle [eq ne gt le])
+(define_code_iterator cmp_signed [eq ne ge lt gt le])
+(define_code_iterator op8_ZN [plus minus and ior xor ashift ashiftrt lshiftrt])
(define_code_iterator ss_addsub [ss_plus ss_minus])
(define_code_iterator us_addsub [us_plus us_minus])
@@ -350,6 +358,20 @@ (define_code_attr gelt_eqne
[(ge "eq")
(lt "ne")])
+(define_code_attr swap
+ [(eq "eq") (ne "ne")
+ (lt "gt") (gt "lt")
+ (le "ge") (ge "le")])
+
+(define_code_attr SWAP
+ [(eq "EQ") (ne "NE")
+ (lt "GT") (gt "LT")
+ (le "GE") (ge "LE")])
+
+;; Convert test of MSB to < 0 resp. >= 0.
+(define_code_attr tstMSB
+ [(eq "ge") (ne "lt")])
+
;; Map RTX code to its standard insn name
(define_code_attr code_stdname
[(ashift "ashl")
@@ -7199,32 +7221,26 @@ (define_peephole2 ; "*sbrx_branch<mode>"
;; Compare with 0 (test) jumps
;; ************************************************************************
-(define_insn "branch"
+;; "branch"
+;; "branch_N"
+;; "branch_ZN"
+(define_insn "branch<CCname>"
[(set (pc)
- (if_then_else (match_operator 1 "simple_comparison_operator"
- [(reg:CC REG_CC)
+ (if_then_else (match_operator 1 "ordered_comparison_operator"
+ [(reg:ALLCC REG_CC)
(const_int 0)])
(label_ref (match_operand 0))
(pc)))]
"reload_completed"
{
- return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0);
- }
- [(set_attr "type" "branch")])
-
-
-(define_insn "difficult_branch"
- [(set (pc)
- (if_then_else (match_operator 1 "difficult_comparison_operator"
- [(reg:CC REG_CC)
- (const_int 0)])
- (label_ref (match_operand 0 "" ""))
- (pc)))]
- "reload_completed"
- {
- return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0);
+ return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn),
+ <MODE>mode);
}
- [(set_attr "type" "branch1")])
+ [(set (attr "type")
+ (if_then_else
+ (match_test "simple_comparison_operator (operands[1], VOIDmode)")
+ (const_string "branch")
+ (const_string "branch1")))])
;; **************************************************************************
@@ -9547,8 +9563,8 @@ (define_peephole2
;; by a comparison of the result against zero, we can output the addition
;; in such a way that SREG.N and SREG.Z are set according to the result.
-;; { -1, +1 } for QImode, otherwise the empty set.
-(define_mode_attr p1m1 [(QI "N P")
+;; { -2, -1, +1, +2 } for QImode, otherwise the empty set.
+(define_mode_attr p2m2 [(QI "Cm2 N P K")
(HI "Yxx") (PSI "Yxx") (SI "Yxx")])
;; FIXME: reload1.cc::do_output_reload() does not support output reloads
@@ -9587,46 +9603,72 @@ (define_mode_attr p1m1 [(QI "N P")
;; doloop_end doesn't reload either, so doloop_end also won't work.
(define_expand "gen_add_for_<code>_<mode>"
- ; "*add.for.eqne.<mode>"
- [(parallel [(set (reg:CC REG_CC)
- (compare:CC (plus:QISI (match_operand:QISI 0 "register_operand")
- (match_operand:QISI 1 "const_int_operand"))
- (const_int 0)))
+ ; "*add.for.cczn.<mode>"
+ [(parallel [(set (reg:CCZN REG_CC)
+ (compare:CCZN (plus:QISI (match_operand:QISI 0 "register_operand")
+ (match_operand:QISI 1 "const_int_operand"))
+ (const_int 0)))
(set (match_dup 0)
(plus:QISI (match_dup 0)
(match_dup 1)))
(clobber (match_operand:QI 3))])
- ; "branch"
+ ; "branch_ZN"
(set (pc)
- (if_then_else (eqne (reg:CC REG_CC)
+ (if_then_else (eqnegtle (reg:CCZN REG_CC)
+ (const_int 0))
+ (label_ref (match_dup 2))
+ (pc)))])
+
+(define_expand "gen_add_for_<code>_<mode>"
+ ; "*add.for.ccn.<mode>"
+ [(parallel [(set (reg:CCN REG_CC)
+ (compare:CCN (plus:QISI (match_operand:QISI 0 "register_operand")
+ (match_operand:QISI 1 "const_int_operand"))
+ (const_int 0)))
+ (set (match_dup 0)
+ (plus:QISI (match_dup 0)
+ (match_dup 1)))
+ (clobber (match_operand:QI 3))])
+ ; "branch_N"
+ (set (pc)
+ (if_then_else (gelt (reg:CCN REG_CC)
(const_int 0))
(label_ref (match_dup 2))
(pc)))])
-;; 1/3: A version without clobber: d-reg or 8-bit adds +/-1.
+;; 1/3: A version without clobber: d-reg or 8-bit adds -2, -1, 1 or 2.
(define_peephole2
[(parallel [(set (match_operand:QISI 0 "register_operand")
(plus:QISI (match_dup 0)
(match_operand:QISI 1 "const_int_operand")))
(clobber (reg:CC REG_CC))])
(set (reg:CC REG_CC)
- (compare:CC (match_dup 0)
- (const_int 0)))
+ (compare:CC (match_operand:QISI 3 "reg_or_0_operand") ; 0 or $0
+ (match_operand:QISI 4 "reg_or_0_operand"))) ; 0 or $0
(set (pc)
- (if_then_else (eqne (reg:CC REG_CC)
- (const_int 0))
+ (if_then_else (cmp_signed (reg:CC REG_CC)
+ (const_int 0))
(label_ref (match_operand 2))
(pc)))]
"peep2_regno_dead_p (3, REG_CC)
&& (d_register_operand (operands[0], <MODE>mode)
|| (<MODE>mode == QImode
- && (INTVAL (operands[1]) == 1
- || INTVAL (operands[1]) == -1)))"
+ && abs1_abs2_operand (operands[1], QImode)))"
[(scratch)]
{
- emit (gen_gen_add_for_<code>_<mode> (operands[0], operands[1], operands[2],
- gen_rtx_SCRATCH (QImode)));
+ rtx (*gen)(rtx, rtx, rtx, rtx);
+
+ if (rtx_equal_p (operands[0], operands[3])
+ && const0_operand (operands[4], <MODE>mode))
+ gen = gen_gen_add_for_<code>_<mode>;
+ else if (const0_operand (operands[3], <MODE>mode)
+ && rtx_equal_p (operands[0], operands[4]))
+ gen = gen_gen_add_for_<cmp_signed:swap>_<mode>;
+ else
+ FAIL;
+
+ emit (gen (operands[0], operands[1], operands[2], gen_rtx_SCRATCH (QImode)));
DONE;
})
@@ -9638,30 +9680,39 @@ (define_peephole2
(clobber (match_operand:QI 3 "scratch_or_d_register_operand"))
(clobber (reg:CC REG_CC))])
(parallel [(set (reg:CC REG_CC)
- (compare:CC (match_dup 0)
- (const_int 0)))
+ (compare:CC (match_operand:QISI 5 "reg_or_0_operand") ; 0 or $0
+ (match_operand:QISI 6 "reg_or_0_operand"))) ; 0 or $0
(clobber (match_operand:QI 4 "scratch_or_d_register_operand"))])
(set (pc)
- (if_then_else (eqne (reg:CC REG_CC)
- (const_int 0))
+ (if_then_else (cmp_signed (reg:CC REG_CC)
+ (const_int 0))
(label_ref (match_operand 2))
(pc)))]
"peep2_regno_dead_p (3, REG_CC)"
[(scratch)]
{
rtx scratch = REG_P (operands[3]) ? operands[3] : operands[4];
+ rtx (*gen)(rtx, rtx, rtx, rtx);
// We need either a d-register or a scratch register to clobber.
if (! REG_P (scratch)
&& ! d_register_operand (operands[0], <MODE>mode)
&& ! (QImode == <MODE>mode
- && (INTVAL (operands[1]) == 1
- || INTVAL (operands[1]) == -1)))
+ && abs1_abs2_operand (operands[1], QImode)))
{
FAIL;
}
- emit (gen_gen_add_for_<code>_<mode> (operands[0], operands[1], operands[2],
- scratch));
+
+ if (rtx_equal_p (operands[0], operands[5])
+ && const0_operand (operands[6], <MODE>mode))
+ gen = gen_gen_add_for_<code>_<mode>;
+ else if (const0_operand (operands[5], <MODE>mode)
+ && rtx_equal_p (operands[0], operands[6]))
+ gen = gen_gen_add_for_<cmp_signed:swap>_<mode>;
+ else
+ FAIL;
+
+ emit (gen (operands[0], operands[1], operands[2], scratch));
DONE;
})
@@ -9673,41 +9724,255 @@ (define_peephole2
(match_operand:QISI 1 "const_int_operand")))
(clobber (reg:CC REG_CC))])
(set (reg:CC REG_CC)
- (compare:CC (match_dup 0)
- (const_int 0)))
+ (compare:CC (match_operand:QISI 4 "reg_or_0_operand") ; 0 or $0
+ (match_operand:QISI 5 "reg_or_0_operand"))) ; 0 or $0
(set (pc)
- (if_then_else (eqne (reg:CC REG_CC)
- (const_int 0))
+ (if_then_else (cmp_signed (reg:CC REG_CC)
+ (const_int 0))
(label_ref (match_operand 2))
(pc)))]
"peep2_regno_dead_p (3, REG_CC)"
[(scratch)]
{
- emit (gen_gen_add_for_<code>_<mode> (operands[0], operands[1], operands[2],
- operands[3]));
+ rtx (*gen)(rtx, rtx, rtx, rtx);
+
+ if (rtx_equal_p (operands[0], operands[4])
+ && const0_operand (operands[5], <MODE>mode))
+ gen = gen_gen_add_for_<code>_<mode>;
+ else if (const0_operand (operands[4], <MODE>mode)
+ && rtx_equal_p (operands[0], operands[5]))
+ gen = gen_gen_add_for_<cmp_signed:swap>_<mode>;
+ else
+ FAIL;
+
+ emit (gen (operands[0], operands[1], operands[2], operands[3]));
DONE;
})
;; Result of the above three peepholes is an addition that also
-;; performs an EQ or NE comparison (of the result) against zero.
+;; performs a signed comparison (of the result) against zero.
;; FIXME: Using (match_dup 0) instead of operands[3/4] makes rnregs
;; barf in regrename.cc::merge_overlapping_regs(). For now, use the
;; fix from PR50788: Constrain as "0".
-(define_insn "*add.for.eqne.<mode>"
- [(set (reg:CC REG_CC)
- (compare:CC
- (plus:QISI (match_operand:QISI 3 "register_operand" "0,0 ,0")
- (match_operand:QISI 1 "const_int_operand" "n,<p1m1>,n"))
+; "*add.for.ccn.qi" "*add.for.cczn.qi"
+; "*add.for.ccn.hi" "*add.for.cczn.hi"
+; "*add.for.ccn.psi" "*add.for.cczn.psi"
+; "*add.for.ccn.si" "*add.for.cczn.si"
+(define_insn "*add.for.<CCN_CCZN:mode>.<QISI:mode>"
+ [(set (reg:CCN_CCZN REG_CC)
+ (compare:CCN_CCZN
+ (plus:QISI (match_operand:QISI 3 "register_operand" "0,0 ,0")
+ (match_operand:QISI 1 "const_int_operand" "n,<QISI:p2m2>,n"))
(const_int 0)))
- (set (match_operand:QISI 0 "register_operand" "=d,*r ,r")
- (plus:QISI (match_operand:QISI 4 "register_operand" "0,0 ,0")
+ (set (match_operand:QISI 0 "register_operand" "=d,*r ,r")
+ (plus:QISI (match_operand:QISI 4 "register_operand" "0,0 ,0")
(match_dup 1)))
- (clobber (match_scratch:QI 2 "=X,X ,&d"))]
+ (clobber (match_scratch:QI 2 "=X,X ,&d"))]
+ "reload_completed"
+ {
+ return avr_out_plus_set<CCname> (operands, nullptr);
+ }
+ [(set_attr "adjust_len" "add_set<CCname>")])
+
+
+
+;; Operations other that PLUS can set the condition code in
+;; a meaningful way, too.
+
+;; 1/2 Left shift sets the N bit.
+(define_peephole2
+ [(parallel [(set (match_operand:HISI 0 "register_operand")
+ (ashift:HISI (match_dup 0)
+ (const_int 1)))
+ (clobber (match_operand:QI 3 "scratch_operand"))
+ (clobber (reg:CC REG_CC))])
+ (set (reg:CC REG_CC)
+ (compare:CC (match_dup 0)
+ (const_int 0)))
+ (set (pc)
+ (if_then_else (gelt (reg:CC REG_CC)
+ (const_int 0))
+ (label_ref (match_operand 2))
+ (pc)))]
+ "peep2_regno_dead_p (3, REG_CC)"
+ [; "*ashift.for.ccn.<mode>"
+ (parallel [(set (reg:CCN REG_CC)
+ (compare:CCN (ashift:HISI (match_dup 0)
+ (const_int 1))
+ (const_int 0)))
+ (set (match_dup 0)
+ (ashift:HISI (match_dup 0)
+ (const_int 1)))])
+ ; "branch_N"
+ (set (pc)
+ (if_then_else (gelt (reg:CCN REG_CC)
+ (const_int 0))
+ (label_ref (match_operand 2))
+ (pc)))])
+
+
+;; 2/2 Left shift sets the N bit. Same like above, but with ZERO_EXTRACT
+(define_peephole2
+ [(parallel [(set (match_operand:HISI 0 "register_operand")
+ (ashift:HISI (match_dup 0)
+ (const_int 1)))
+ (clobber (match_operand:QI 3 "scratch_operand"))
+ (clobber (reg:CC REG_CC))])
+ (parallel [(set (pc)
+ (if_then_else
+ (eqne (zero_extract (match_dup 0)
+ (const_int 1)
+ (match_operand 4 "const<MSB>_operand"))
+ (const_int 0))
+ (label_ref (match_operand 2))
+ (pc)))
+ (clobber (reg:CC REG_CC))])]
+ ""
+ [; "*ashift.for.ccn.<mode>"
+ (parallel [(set (reg:CCN REG_CC)
+ (compare:CCN (ashift:HISI (match_dup 0)
+ (const_int 1))
+ (const_int 0)))
+ (set (match_dup 0)
+ (ashift:HISI (match_dup 0)
+ (const_int 1)))])
+ ; "branch_N"
+ (set (pc)
+ (if_then_else (<tstMSB> (reg:CCN REG_CC)
+ (const_int 0))
+ (label_ref (match_operand 2))
+ (pc)))])
+
+
+(define_insn "*ashift.for.ccn.<mode>"
+ [(set (reg:CCN REG_CC)
+ (compare:CCN (ashift:HISI (match_operand:HISI 2 "register_operand" "0")
+ (const_int 1))
+ (const_int 0)))
+ (set (match_operand:HISI 0 "register_operand" "=r")
+ (ashift:HISI (match_operand:HISI 1 "register_operand" "0")
+ (const_int 1)))]
"reload_completed"
{
- return avr_out_plus_set_ZN (operands, nullptr);
+ output_asm_insn ("lsl %A0", operands);
+ if (<SIZE> >= 2) output_asm_insn ("rol %B0", operands);
+ if (<SIZE> >= 3) output_asm_insn ("rol %C0", operands);
+ if (<SIZE> >= 4) output_asm_insn ("rol %D0", operands);
+ return "";
+ }
+ [(set_attr "length" "<SIZE>")])
+
+
+;; 1/2 QImode operations that set Z and N in a meaningful way.
+(define_peephole2
+ [(parallel [(set (match_operand:QI 0 "register_operand")
+ (match_operator:QI 2 "op8_ZN_operator" [(match_dup 0)
+ (match_operand:QI 1)]))
+ (clobber (reg:CC REG_CC))])
+ (parallel [(set (pc)
+ (if_then_else (eqne (zero_extract (match_dup 0)
+ (const_int 1)
+ (const_int 7))
+ (const_int 0))
+ (label_ref (match_operand 3))
+ (pc)))
+ (clobber (reg:CC REG_CC))])]
+ ""
+ [; "*op8.for.cczn.<code>"
+ (parallel [(set (reg:CCZN REG_CC)
+ (compare:CCZN (match_op_dup 2 [(match_dup 0)
+ (match_dup 1)])
+ (const_int 0)))
+ (set (match_dup 0)
+ (match_op_dup 2 [(match_dup 0)
+ (match_dup 1)]))])
+ ; "branch_ZN"
+ (set (pc)
+ (if_then_else (<tstMSB> (reg:CCZN REG_CC)
+ (const_int 0))
+ (label_ref (match_operand 3))
+ (pc)))])
+
+
+;; 2/2 QImode operations that set Z and N in a meaningful way.
+(define_peephole2
+ [(parallel [(set (match_operand:QI 0 "register_operand")
+ (match_operator:QI 2 "op8_ZN_operator" [(match_dup 0)
+ (match_operand:QI 1)]))
+ (clobber (reg:CC REG_CC))])
+ (set (reg:CC REG_CC)
+ (compare:CC (match_operand:QI 4 "reg_or_0_operand") ; 0 or $0
+ (match_operand:QI 5 "reg_or_0_operand"))) ; 0 or $0
+ (set (pc)
+ (if_then_else (cmp_signed (reg:CC REG_CC)
+ (const_int 0))
+ (label_ref (match_operand 3))
+ (pc)))]
+ "peep2_regno_dead_p (3, REG_CC)"
+ [; "*op8.for.cczn.<code>"
+ (parallel [(set (reg:CCZN REG_CC)
+ (compare:CCZN (match_op_dup 2 [(match_dup 0)
+ (match_dup 1)])
+ (const_int 0)))
+ (set (match_dup 0)
+ (match_op_dup 2 [(match_dup 0)
+ (match_dup 1)]))])
+ ; "branch_ZN"
+ (set (pc)
+ (if_then_else (match_dup 6)
+ (label_ref (match_operand 3))
+ (pc)))]
+ {
+ RTX_CODE code = UNKNOWN;
+
+ if (rtx_equal_p (operands[4], operands[0])
+ && const0_operand (operands[5], QImode))
+ code = <cmp_signed:CODE>;
+ else if (const0_operand (operands[4], QImode)
+ && rtx_equal_p (operands[5], operands[0]))
+ code = <cmp_signed:SWAP>;
+ else
+ FAIL;
+
+ operands[6] = gen_rtx_fmt_ee (code, VOIDmode,
+ gen_rtx_REG (CCZNmode, REG_CC), const0_rtx);
+ })
+
+;; Constraints and predicate for the insn below.
+;; This is what op8_ZN_operator allows.
+;; Note again that due to nregs, match_dup's won't work.
+(define_code_attr c0_op8
+ [(minus "r") (xor "r") (and "d,r") (ior "d,r") (plus "d,r") (ashift "r") (ashiftrt "r") (lshiftrt "r")])
+(define_code_attr c1_op8
+ [(minus "0") (xor "0") (and "0,0") (ior "0,0") (plus "0,0") (ashift "0") (ashiftrt "0") (lshiftrt "0")])
+(define_code_attr c4_op8
+ [(minus "2") (xor "2") (and "2,2") (ior "2,2") (plus "2,2") (ashift "2") (ashiftrt "2") (lshiftrt "2")])
+(define_code_attr c2_op8
+ [(minus "r") (xor "r") (and "n,r") (ior "n,r") (plus "n,rPNKCm2")
+ (ashift "PKC03") (ashiftrt "PKC03") (lshiftrt "PKC03")])
+
+(define_code_attr p2_op8
+ [(ashift "const_1_to_3") (ashiftrt "const_1_to_3") (lshiftrt "const_1_to_3")
+ (minus "register") (xor "register")
+ (plus "nonmemory") (and "nonmemory") (ior "nonmemory")])
+
+;; Result of the two peephole2's above: An 8-bit operation that
+;; sets Z and N. The allowed operations are:
+;; PLUS, MINUS, AND, IOR, XOR and SHIFTs.
+(define_insn "*op8.for.cczn.<code>"
+ [(parallel [(set (reg:CCZN REG_CC)
+ (compare:CCZN (op8_ZN:QI (match_operand:QI 3 "register_operand" "<c1_op8>")
+ (match_operand:QI 4 "<p2_op8>_operand" "<c4_op8>"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "register_operand" "=<c0_op8>")
+ (op8_ZN:QI (match_operand:QI 1 "register_operand" "<c1_op8>")
+ (match_operand:QI 2 "<p2_op8>_operand" "<c2_op8>")))])]
+ "reload_completed"
+ {
+ return avr_out_op8_set_ZN (<CODE>, operands, nullptr);
}
- [(set_attr "adjust_len" "add_set_ZN")])
+ [(set (attr "length")
+ (symbol_ref ("avr_len_op8_set_ZN (<CODE>, operands)")))])
;; Swapping both comparison and branch condition. This can turn difficult
diff --git a/gcc/config/avr/predicates.md b/gcc/config/avr/predicates.md
index 12013660ed1..0a35c8c31c1 100644
--- a/gcc/config/avr/predicates.md
+++ b/gcc/config/avr/predicates.md
@@ -147,6 +147,11 @@ (define_predicate "const_2_to_7_operand"
(and (match_code "const_int")
(match_test "IN_RANGE (INTVAL (op), 2, 7)")))
+;; Return true if OP is constant integer 1..3 for MODE.
+(define_predicate "const_1_to_3_operand"
+ (and (match_code "const_int")
+ (match_test "IN_RANGE (INTVAL (op), 1, 3)")))
+
;; Return 1 if OP is constant integer 1..6 for MODE.
(define_predicate "const_1_to_6_operand"
(and (match_code "const_int")
@@ -162,6 +167,12 @@ (define_predicate "const_m255_to_m1_operand"
(and (match_code "const_int")
(match_test "IN_RANGE (INTVAL (op), -255, -1)")))
+;; Return true if OP is a CONST_INT in { -2, -1, 1, 2 }.
+(define_predicate "abs1_abs2_operand"
+ (and (match_code "const_int")
+ (match_test "INTVAL (op) != 0")
+ (match_test "IN_RANGE (INTVAL (op), -2, 2)")))
+
;; Returns true if OP is either the constant zero or a register.
(define_predicate "reg_or_0_operand"
(ior (match_operand 0 "register_operand")
@@ -228,10 +239,30 @@ (define_predicate "simple_comparison_operator"
(and (match_operand 0 "comparison_operator")
(not (match_code "gt,gtu,le,leu"))))
+;; True for EQ, NE, GE, LT, GT, LE
+(define_predicate "signed_comparison_operator"
+ (match_code "eq,ne,ge,lt,gt,le"))
+
;; True for SIGN_EXTEND, ZERO_EXTEND.
(define_predicate "extend_operator"
(match_code "sign_extend,zero_extend"))
+;; True for 8-bit operations that set SREG.N and SREG.Z in a
+;; usable way:
+;; * OP0 is a QImode register, and
+;; * OP1 is a QImode register or CONST_INT, and
+;;
+;; the allowed operations is one of:
+;;
+;; * SHIFTs with a const_int offset in { 1, 2, 3 }.
+;; * MINUS and XOR with a register operand
+;; * IOR and AND with a register operand, or d-reg + const_int
+;; * PLUS with a register operand, or d-reg + const_int,
+;; or a const_int in { -2, -1, 1, 2 }. */
+(define_predicate "op8_ZN_operator"
+ (and (match_code "plus,minus,ashift,ashiftrt,lshiftrt,and,ior,xor")
+ (match_test "avr_op8_ZN_operator (op)")))
+
;; Return true if OP is a valid call operand.
(define_predicate "call_insn_operand"
(and (match_code "mem")
diff --git a/gcc/testsuite/gcc.target/avr/pr115830-add-c.c b/gcc/testsuite/gcc.target/avr/pr115830-add-c.c
new file mode 100644
index 00000000000..4f269293590
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/pr115830-add-c.c
@@ -0,0 +1,79 @@
+/* { dg-do run } */
+/* { dg-additional-options { -std=c99 -fwrapv -Os } } */
+
+typedef __UINT8_TYPE__ uint8_t;
+typedef __INT8_TYPE__ int8_t;
+
+#define AI static inline __attribute__((always_inline))
+#define NI __attribute__((noinline,noclone,no_icf))
+
+#define TYP int8_t
+
+TYP volatile v;
+
+#define MK_FUN(ID, OP, TST) \
+NI TYP func1_##ID (TYP c) \
+{ \
+ v = 42; \
+ c OP; \
+ if (c TST) \
+ v = c; \
+ return v; \
+} \
+ \
+NI TYP func2_##ID (TYP c) \
+{ \
+ TYP v = 42; \
+ c OP; \
+ __asm ("" : "+r" (c)); \
+ if (c TST) \
+ v = c; \
+ return v; \
+}
+
+MK_FUN (ADD_01, += 1, != 0)
+MK_FUN (ADD_02, += 1, == 0)
+MK_FUN (ADD_03, += 1, >= 0)
+MK_FUN (ADD_04, += 1, <= 0)
+MK_FUN (ADD_05, += 1, > 0)
+MK_FUN (ADD_06, += 1, < 0)
+MK_FUN (ADD_07, -= 2, != 0)
+MK_FUN (ADD_08, -= 2, == 0)
+MK_FUN (ADD_09, -= 2, >= 0)
+MK_FUN (ADD_10, -= 2, <= 0)
+MK_FUN (ADD_11, -= 2, > 0)
+MK_FUN (ADD_12, -= 2, < 0)
+MK_FUN (ADD_13, += 42, != 0)
+MK_FUN (ADD_14, += 42, == 0)
+MK_FUN (ADD_15, += 42, >= 0)
+MK_FUN (ADD_16, += 42, <= 0)
+MK_FUN (ADD_17, += 42, > 0)
+MK_FUN (ADD_18, += 42, < 0)
+
+
+int main (void)
+{
+ uint8_t c = 0;
+ do {
+ if (func1_ADD_01 (c) != func2_ADD_01 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_02 (c) != func2_ADD_02 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_03 (c) != func2_ADD_03 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_04 (c) != func2_ADD_04 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_05 (c) != func2_ADD_05 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_06 (c) != func2_ADD_06 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_07 (c) != func2_ADD_07 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_08 (c) != func2_ADD_08 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_09 (c) != func2_ADD_09 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_10 (c) != func2_ADD_10 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_11 (c) != func2_ADD_11 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_12 (c) != func2_ADD_12 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_13 (c) != func2_ADD_13 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_14 (c) != func2_ADD_14 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_15 (c) != func2_ADD_15 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_16 (c) != func2_ADD_16 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_17 (c) != func2_ADD_17 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_18 (c) != func2_ADD_18 (c)) __builtin_exit (__LINE__);
+ } while (++c);
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/pr115830-add-i.c b/gcc/testsuite/gcc.target/avr/pr115830-add-i.c
new file mode 100644
index 00000000000..f3b4df95fd7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/pr115830-add-i.c
@@ -0,0 +1,103 @@
+/* { dg-do run } */
+/* { dg-additional-options { -std=c99 -fwrapv -Os } } */
+
+typedef __UINT16_TYPE__ uint16_t;
+typedef __INT16_TYPE__ int16_t;
+
+#define AI static inline __attribute__((always_inline))
+#define NI __attribute__((noinline,noclone,no_icf))
+
+#define TYP int16_t
+
+TYP volatile v;
+
+#define MK_FUN(ID, OP, TST) \
+NI TYP func1_##ID (TYP c) \
+{ \
+ v = 42; \
+ c OP; \
+ if (c TST) \
+ v = c; \
+ return v; \
+} \
+ \
+NI TYP func2_##ID (TYP c) \
+{ \
+ TYP v = 42; \
+ c OP; \
+ __asm ("" : "+r" (c)); \
+ if (c TST) \
+ v = c; \
+ return v; \
+}
+
+MK_FUN (ADD_01, += 1, != 0)
+MK_FUN (ADD_02, += 1, == 0)
+MK_FUN (ADD_03, += 1, >= 0)
+MK_FUN (ADD_04, += 1, <= 0)
+MK_FUN (ADD_05, += 1, > 0)
+MK_FUN (ADD_06, += 1, < 0)
+MK_FUN (ADD_07, -= 2, != 0)
+MK_FUN (ADD_08, -= 2, == 0)
+MK_FUN (ADD_09, -= 2, >= 0)
+MK_FUN (ADD_10, -= 2, <= 0)
+MK_FUN (ADD_11, -= 2, > 0)
+MK_FUN (ADD_12, -= 2, < 0)
+MK_FUN (ADD_13, += 42, != 0)
+MK_FUN (ADD_14, += 42, == 0)
+MK_FUN (ADD_15, += 42, >= 0)
+MK_FUN (ADD_16, += 42, <= 0)
+MK_FUN (ADD_17, += 42, > 0)
+MK_FUN (ADD_18, += 42, < 0)
+MK_FUN (ADD_19, += 256, != 0)
+MK_FUN (ADD_20, += 256, == 0)
+MK_FUN (ADD_21, += 256, >= 0)
+MK_FUN (ADD_22, += 256, <= 0)
+MK_FUN (ADD_23, += 256, > 0)
+MK_FUN (ADD_24, += 256, < 0)
+MK_FUN (ADD_25, += 512, != 0)
+MK_FUN (ADD_26, += 512, == 0)
+MK_FUN (ADD_27, += 512, >= 0)
+MK_FUN (ADD_28, += 512, <= 0)
+MK_FUN (ADD_29, += 512, > 0)
+MK_FUN (ADD_30, += 512, < 0)
+
+
+int main (void)
+{
+ uint16_t c = 0;
+ do {
+ if (func1_ADD_01 (c) != func2_ADD_01 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_02 (c) != func2_ADD_02 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_03 (c) != func2_ADD_03 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_04 (c) != func2_ADD_04 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_05 (c) != func2_ADD_05 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_06 (c) != func2_ADD_06 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_07 (c) != func2_ADD_07 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_08 (c) != func2_ADD_08 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_09 (c) != func2_ADD_09 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_10 (c) != func2_ADD_10 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_11 (c) != func2_ADD_11 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_12 (c) != func2_ADD_12 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_13 (c) != func2_ADD_13 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_14 (c) != func2_ADD_14 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_15 (c) != func2_ADD_15 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_16 (c) != func2_ADD_16 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_17 (c) != func2_ADD_17 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_18 (c) != func2_ADD_18 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_19 (c) != func2_ADD_19 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_20 (c) != func2_ADD_20 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_21 (c) != func2_ADD_21 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_22 (c) != func2_ADD_22 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_23 (c) != func2_ADD_23 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_24 (c) != func2_ADD_24 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_25 (c) != func2_ADD_25 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_26 (c) != func2_ADD_26 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_27 (c) != func2_ADD_27 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_28 (c) != func2_ADD_28 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_29 (c) != func2_ADD_29 (c)) __builtin_exit (__LINE__);
+ if (func1_ADD_30 (c) != func2_ADD_30 (c)) __builtin_exit (__LINE__);
+ } while (++c);
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/pr115830-and.c b/gcc/testsuite/gcc.target/avr/pr115830-and.c
new file mode 100644
index 00000000000..317486feb03
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/pr115830-and.c
@@ -0,0 +1,67 @@
+/* { dg-do run } */
+/* { dg-additional-options { -std=c99 -fwrapv -Os } } */
+
+typedef __UINT8_TYPE__ uint8_t;
+typedef __INT8_TYPE__ int8_t;
+
+#define AI static inline __attribute__((always_inline))
+#define NI __attribute__((noinline,noclone,no_icf))
+
+#define TYP int8_t
+
+TYP volatile v;
+
+#define MK_FUN(ID, OP, TST) \
+NI TYP func1_##ID (TYP c) \
+{ \
+ v = 42; \
+ c OP; \
+ if (c TST) \
+ v = c; \
+ return v; \
+} \
+ \
+NI TYP func2_##ID (TYP c) \
+{ \
+ TYP v = 42; \
+ c OP; \
+ __asm ("" : "+r" (c)); \
+ if (c TST) \
+ v = c; \
+ return v; \
+}
+
+MK_FUN (AND_01, &= 0x80, != 0)
+MK_FUN (AND_02, &= 0x80, == 0)
+MK_FUN (AND_03, &= 0x80, >= 0)
+MK_FUN (AND_04, &= 0x80, <= 0)
+MK_FUN (AND_05, &= 0x80, > 0)
+MK_FUN (AND_06, &= 0x80, < 0)
+MK_FUN (AND_07, &= 0xef, != 0)
+MK_FUN (AND_08, &= 0xef, == 0)
+MK_FUN (AND_09, &= 0xef, >= 0)
+MK_FUN (AND_10, &= 0xef, <= 0)
+MK_FUN (AND_11, &= 0xef, > 0)
+MK_FUN (AND_12, &= 0xef, < 0)
+
+
+int main (void)
+{
+ uint8_t c = 0;
+ do {
+ if (func1_AND_01 (c) != func2_AND_01 (c)) __builtin_exit (__LINE__);
+ if (func1_AND_02 (c) != func2_AND_02 (c)) __builtin_exit (__LINE__);
+ if (func1_AND_03 (c) != func2_AND_03 (c)) __builtin_exit (__LINE__);
+ if (func1_AND_04 (c) != func2_AND_04 (c)) __builtin_exit (__LINE__);
+ if (func1_AND_05 (c) != func2_AND_05 (c)) __builtin_exit (__LINE__);
+ if (func1_AND_06 (c) != func2_AND_06 (c)) __builtin_exit (__LINE__);
+ if (func1_AND_07 (c) != func2_AND_07 (c)) __builtin_exit (__LINE__);
+ if (func1_AND_08 (c) != func2_AND_08 (c)) __builtin_exit (__LINE__);
+ if (func1_AND_09 (c) != func2_AND_09 (c)) __builtin_exit (__LINE__);
+ if (func1_AND_10 (c) != func2_AND_10 (c)) __builtin_exit (__LINE__);
+ if (func1_AND_11 (c) != func2_AND_11 (c)) __builtin_exit (__LINE__);
+ if (func1_AND_12 (c) != func2_AND_12 (c)) __builtin_exit (__LINE__);
+ } while (++c);
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/pr115830-asl.c b/gcc/testsuite/gcc.target/avr/pr115830-asl.c
new file mode 100644
index 00000000000..09a4495caac
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/pr115830-asl.c
@@ -0,0 +1,78 @@
+/* { dg-do run } */
+/* { dg-additional-options { -std=c99 -fwrapv -Os } } */
+
+typedef __UINT8_TYPE__ uint8_t;
+typedef __INT8_TYPE__ int8_t;
+
+#define AI static inline __attribute__((always_inline))
+#define NI __attribute__((noinline,noclone,no_icf))
+
+#define TYP int8_t
+
+TYP volatile v;
+
+#define MK_FUN(ID, OP, TST) \
+NI TYP func1_##ID (TYP c) \
+{ \
+ v = 42; \
+ c OP; \
+ if (c TST) \
+ v = c; \
+ return v; \
+} \
+ \
+NI TYP func2_##ID (TYP c) \
+{ \
+ TYP v = 42; \
+ c OP; \
+ __asm ("" : "+r" (c)); \
+ if (c TST) \
+ v = c; \
+ return v; \
+}
+
+MK_FUN (ASL_01, <<= 1, != 0)
+MK_FUN (ASL_02, <<= 2, != 0)
+MK_FUN (ASL_03, <<= 3, != 0)
+MK_FUN (ASL_04, <<= 1, == 0)
+MK_FUN (ASL_05, <<= 2, == 0)
+MK_FUN (ASL_06, <<= 3, == 0)
+MK_FUN (ASL_07, <<= 1, >= 0)
+MK_FUN (ASL_08, <<= 2, >= 0)
+MK_FUN (ASL_09, <<= 3, >= 0)
+MK_FUN (ASL_10, <<= 1, <= 0)
+MK_FUN (ASL_11, <<= 2, <= 0)
+MK_FUN (ASL_12, <<= 3, <= 0)
+MK_FUN (ASL_13, <<= 1, > 0)
+MK_FUN (ASL_14, <<= 2, > 0)
+MK_FUN (ASL_15, <<= 3, > 0)
+MK_FUN (ASL_16, <<= 1, < 0)
+MK_FUN (ASL_17, <<= 2, < 0)
+MK_FUN (ASL_18, <<= 3, < 0)
+
+int main (void)
+{
+ uint8_t c = 0;
+ do {
+ if (func1_ASL_01 (c) != func2_ASL_01 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_02 (c) != func2_ASL_02 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_03 (c) != func2_ASL_03 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_04 (c) != func2_ASL_04 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_05 (c) != func2_ASL_05 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_06 (c) != func2_ASL_06 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_07 (c) != func2_ASL_07 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_08 (c) != func2_ASL_08 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_09 (c) != func2_ASL_09 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_10 (c) != func2_ASL_10 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_11 (c) != func2_ASL_11 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_12 (c) != func2_ASL_12 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_13 (c) != func2_ASL_13 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_14 (c) != func2_ASL_14 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_15 (c) != func2_ASL_15 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_16 (c) != func2_ASL_16 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_17 (c) != func2_ASL_17 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_18 (c) != func2_ASL_18 (c)) __builtin_exit (__LINE__);
+ } while (++c);
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/pr115830-asl32.c b/gcc/testsuite/gcc.target/avr/pr115830-asl32.c
new file mode 100644
index 00000000000..6e8100f5f58
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/pr115830-asl32.c
@@ -0,0 +1,57 @@
+/* { dg-do run } */
+/* { dg-additional-options { -std=c99 -fwrapv -Os } } */
+
+typedef __UINT32_TYPE__ uint32_t;
+typedef __INT32_TYPE__ int32_t;
+
+#define AI static inline __attribute__((always_inline))
+#define NI __attribute__((noinline,noclone,no_icf))
+
+#define TYP int32_t
+
+TYP volatile v;
+
+#define MK_FUN(ID, OP, TST) \
+NI TYP func1_##ID (TYP c) \
+{ \
+ v = 0x77665544; \
+ c OP; \
+ if (c TST) \
+ v = c; \
+ return v; \
+} \
+ \
+NI TYP func2_##ID (TYP c) \
+{ \
+ TYP v = 0x77665544; \
+ c OP; \
+ __asm ("" : "+r" (c)); \
+ if (c TST) \
+ v = c; \
+ return v; \
+}
+
+MK_FUN (ASL_03, <<= 1, >= 0)
+MK_FUN (ASL_06, <<= 1, < 0)
+
+NI void test_asl (uint32_t c)
+{
+ if (func1_ASL_03 (c) != func2_ASL_03 (c)) __builtin_exit (__LINE__);
+ if (func1_ASL_06 (c) != func2_ASL_06 (c)) __builtin_exit (__LINE__);
+}
+
+int main (void)
+{
+ test_asl (0);
+ test_asl (0x80000000);
+ test_asl (0x80000001);
+ test_asl (0xc0000000);
+ test_asl (0xc0000001);
+ test_asl (0);
+ test_asl (0xff00ff00);
+ test_asl (0x00ff00ff);
+ test_asl (0xff00ff00 >> 1);
+ test_asl (0x00ff00ff >> 1);
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/pr115830-asr.c b/gcc/testsuite/gcc.target/avr/pr115830-asr.c
new file mode 100644
index 00000000000..1ceafb9a537
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/pr115830-asr.c
@@ -0,0 +1,78 @@
+/* { dg-do run } */
+/* { dg-additional-options { -std=c99 -fwrapv -Os } } */
+
+typedef __UINT8_TYPE__ uint8_t;
+typedef __INT8_TYPE__ int8_t;
+
+#define AI static inline __attribute__((always_inline))
+#define NI __attribute__((noinline,noclone,no_icf))
+
+#define TYP int8_t
+
+TYP volatile v;
+
+#define MK_FUN(ID, OP, TST) \
+NI TYP func1_##ID (TYP c) \
+{ \
+ v = 42; \
+ c OP; \
+ if (c TST) \
+ v = c; \
+ return v; \
+} \
+ \
+NI TYP func2_##ID (TYP c) \
+{ \
+ TYP v = 42; \
+ c OP; \
+ __asm ("" : "+r" (c)); \
+ if (c TST) \
+ v = c; \
+ return v; \
+}
+
+MK_FUN (ASR_01, >>= 1, != 0)
+MK_FUN (ASR_02, >>= 2, != 0)
+MK_FUN (ASR_03, >>= 3, != 0)
+MK_FUN (ASR_04, >>= 1, == 0)
+MK_FUN (ASR_05, >>= 2, == 0)
+MK_FUN (ASR_06, >>= 3, == 0)
+MK_FUN (ASR_07, >>= 1, >= 0)
+MK_FUN (ASR_08, >>= 2, >= 0)
+MK_FUN (ASR_09, >>= 3, >= 0)
+MK_FUN (ASR_10, >>= 1, <= 0)
+MK_FUN (ASR_11, >>= 2, <= 0)
+MK_FUN (ASR_12, >>= 3, <= 0)
+MK_FUN (ASR_13, >>= 1, > 0)
+MK_FUN (ASR_14, >>= 2, > 0)
+MK_FUN (ASR_15, >>= 3, > 0)
+MK_FUN (ASR_16, >>= 1, < 0)
+MK_FUN (ASR_17, >>= 2, < 0)
+MK_FUN (ASR_18, >>= 3, < 0)
+
+int main (void)
+{
+ uint8_t c = 0;
+ do {
+ if (func1_ASR_01 (c) != func2_ASR_01 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_02 (c) != func2_ASR_02 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_03 (c) != func2_ASR_03 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_04 (c) != func2_ASR_04 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_05 (c) != func2_ASR_05 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_06 (c) != func2_ASR_06 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_07 (c) != func2_ASR_07 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_08 (c) != func2_ASR_08 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_09 (c) != func2_ASR_09 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_10 (c) != func2_ASR_10 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_11 (c) != func2_ASR_11 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_12 (c) != func2_ASR_12 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_13 (c) != func2_ASR_13 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_14 (c) != func2_ASR_14 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_15 (c) != func2_ASR_15 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_16 (c) != func2_ASR_16 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_17 (c) != func2_ASR_17 (c)) __builtin_exit (__LINE__);
+ if (func1_ASR_18 (c) != func2_ASR_18 (c)) __builtin_exit (__LINE__);
+ } while (++c);
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/pr115830-ior.c b/gcc/testsuite/gcc.target/avr/pr115830-ior.c
new file mode 100644
index 00000000000..51451b4f32f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/pr115830-ior.c
@@ -0,0 +1,67 @@
+/* { dg-do run } */
+/* { dg-additional-options { -std=c99 -fwrapv -Os } } */
+
+typedef __UINT8_TYPE__ uint8_t;
+typedef __INT8_TYPE__ int8_t;
+
+#define AI static inline __attribute__((always_inline))
+#define NI __attribute__((noinline,noclone,no_icf))
+
+#define TYP int8_t
+
+TYP volatile v;
+
+#define MK_FUN(ID, OP, TST) \
+NI TYP func1_##ID (TYP c) \
+{ \
+ v = 42; \
+ c OP; \
+ if (c TST) \
+ v = c; \
+ return v; \
+} \
+ \
+NI TYP func2_##ID (TYP c) \
+{ \
+ TYP v = 42; \
+ c OP; \
+ __asm ("" : "+r" (c)); \
+ if (c TST) \
+ v = c; \
+ return v; \
+}
+
+MK_FUN (IOR_01, &= 0x8, != 0)
+MK_FUN (IOR_02, &= 0x8, == 0)
+MK_FUN (IOR_03, &= 0x8, >= 0)
+MK_FUN (IOR_04, &= 0x8, <= 0)
+MK_FUN (IOR_05, &= 0x8, > 0)
+MK_FUN (IOR_06, &= 0x8, < 0)
+MK_FUN (IOR_07, &= 0x7f, != 0)
+MK_FUN (IOR_08, &= 0x7f, == 0)
+MK_FUN (IOR_09, &= 0x7f, >= 0)
+MK_FUN (IOR_10, &= 0x7f, <= 0)
+MK_FUN (IOR_11, &= 0x7f, > 0)
+MK_FUN (IOR_12, &= 0x7f, < 0)
+
+
+int main (void)
+{
+ uint8_t c = 0;
+ do {
+ if (func1_IOR_01 (c) != func2_IOR_01 (c)) __builtin_exit (__LINE__);
+ if (func1_IOR_02 (c) != func2_IOR_02 (c)) __builtin_exit (__LINE__);
+ if (func1_IOR_03 (c) != func2_IOR_03 (c)) __builtin_exit (__LINE__);
+ if (func1_IOR_04 (c) != func2_IOR_04 (c)) __builtin_exit (__LINE__);
+ if (func1_IOR_05 (c) != func2_IOR_05 (c)) __builtin_exit (__LINE__);
+ if (func1_IOR_06 (c) != func2_IOR_06 (c)) __builtin_exit (__LINE__);
+ if (func1_IOR_07 (c) != func2_IOR_07 (c)) __builtin_exit (__LINE__);
+ if (func1_IOR_08 (c) != func2_IOR_08 (c)) __builtin_exit (__LINE__);
+ if (func1_IOR_09 (c) != func2_IOR_09 (c)) __builtin_exit (__LINE__);
+ if (func1_IOR_10 (c) != func2_IOR_10 (c)) __builtin_exit (__LINE__);
+ if (func1_IOR_11 (c) != func2_IOR_11 (c)) __builtin_exit (__LINE__);
+ if (func1_IOR_12 (c) != func2_IOR_12 (c)) __builtin_exit (__LINE__);
+ } while (++c);
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/pr115830-lsr.c b/gcc/testsuite/gcc.target/avr/pr115830-lsr.c
new file mode 100644
index 00000000000..1e027154c73
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/pr115830-lsr.c
@@ -0,0 +1,60 @@
+/* { dg-do run } */
+/* { dg-additional-options { -std=c99 -fwrapv -Os } } */
+
+typedef __UINT8_TYPE__ uint8_t;
+typedef __INT8_TYPE__ int8_t;
+
+#define AI static inline __attribute__((always_inline))
+#define NI __attribute__((noinline,noclone,no_icf))
+
+#define TYP uint8_t
+
+TYP volatile v;
+
+#define MK_FUN(ID, OP, TST) \
+NI TYP func1_##ID (TYP c) \
+{ \
+ v = 42; \
+ c OP; \
+ if (c TST) \
+ v = c; \
+ return v; \
+} \
+ \
+NI TYP func2_##ID (TYP c) \
+{ \
+ TYP v = 42; \
+ c OP; \
+ __asm ("" : "+r" (c)); \
+ if (c TST) \
+ v = c; \
+ return v; \
+}
+
+MK_FUN (LSR_01, >>= 1, != 0)
+MK_FUN (LSR_02, >>= 2, != 0)
+MK_FUN (LSR_03, >>= 3, != 0)
+MK_FUN (LSR_04, >>= 1, == 0)
+MK_FUN (LSR_05, >>= 2, == 0)
+MK_FUN (LSR_06, >>= 3, == 0)
+MK_FUN (LSR_13, >>= 1, > 0)
+MK_FUN (LSR_14, >>= 2, > 0)
+MK_FUN (LSR_15, >>= 3, > 0)
+
+int main (void)
+{
+ uint8_t c = 0;
+ do {
+ if (func1_LSR_01 (c) != func2_LSR_01 (c)) __builtin_exit (__LINE__);
+ if (func1_LSR_02 (c) != func2_LSR_02 (c)) __builtin_exit (__LINE__);
+ if (func1_LSR_03 (c) != func2_LSR_03 (c)) __builtin_exit (__LINE__);
+ if (func1_LSR_04 (c) != func2_LSR_04 (c)) __builtin_exit (__LINE__);
+ if (func1_LSR_05 (c) != func2_LSR_05 (c)) __builtin_exit (__LINE__);
+ if (func1_LSR_06 (c) != func2_LSR_06 (c)) __builtin_exit (__LINE__);
+ if (func1_LSR_13 (c) != func2_LSR_13 (c)) __builtin_exit (__LINE__);
+ if (func1_LSR_14 (c) != func2_LSR_14 (c)) __builtin_exit (__LINE__);
+ if (func1_LSR_15 (c) != func2_LSR_15 (c)) __builtin_exit (__LINE__);
+ } while (++c);
+
+ return 0;
+}