@@ -18,6 +18,12 @@
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.
+ The Z flag indicates whether the value is zero. */
+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. */
@@ -55,7 +55,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 *avr_cond_branch (rtx_insn *, rtx *);
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*);
@@ -63,6 +63,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);
@@ -154,6 +158,8 @@ extern rtx zero_reg_rtx;
extern rtx all_regs_rtx[32];
extern rtx rampz_rtx;
extern rtx cc_reg_rtx;
+extern rtx ccn_reg_rtx;
+extern rtx cczn_reg_rtx;
#endif /* RTX_CODE */
@@ -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);
@@ -196,7 +196,11 @@ rtx zero_reg_rtx;
/* Condition Code register RTX (reg:CC REG_CC) */
extern GTY(()) rtx cc_reg_rtx;
+extern GTY(()) rtx ccn_reg_rtx;
+extern GTY(()) rtx cczn_reg_rtx;
rtx cc_reg_rtx;
+rtx ccn_reg_rtx;
+rtx cczn_reg_rtx;
/* RTXs for all general purpose registers as QImode */
extern GTY(()) rtx all_regs_rtx[REG_32];
@@ -1185,6 +1189,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. */
@@ -1209,7 +1225,9 @@ avr_init_expanders (void)
tmp_reg_rtx = all_regs_rtx[AVR_TMP_REGNO];
zero_reg_rtx = all_regs_rtx[AVR_ZERO_REGNO];
- cc_reg_rtx = gen_rtx_REG (CCmode, REG_CC);
+ cc_reg_rtx = gen_rtx_REG (CCmode, REG_CC);
+ ccn_reg_rtx = gen_rtx_REG (CCNmode, REG_CC);
+ cczn_reg_rtx = gen_rtx_REG (CCZNmode, REG_CC);
lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z);
@@ -3756,14 +3774,13 @@ ptrreg_to_str (int regno)
return NULL;
}
-/* Return the condition name as a string.
- Used in conditional jump constructing */
+
+/* Return the condition name as a string to be used in a BR** instruction.
+ Used in conditional jump constructing. */
static const char *
-cond_string (enum rtx_code code)
+avr_cond_string (rtx_code code, bool cc_overflow_unusable)
{
- bool cc_overflow_unusable = false;
-
switch (code)
{
case NE:
@@ -4092,10 +4109,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 (avr_cond_string (GET_CODE (x), code == 'L'), file);
+ else if (code == 'k' || code == 'K')
+ fputs (avr_cond_string (reverse_condition (GET_CODE (x)), code == 'K'),
+ file);
else
avr_print_operand_address (file, VOIDmode, x);
}
@@ -4144,14 +4162,24 @@ 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. */
+ LEN in { 1, 2, 3 } is a number returned by `avr_jump_mode()'.
+ CCMODE is the mode of the comparison in { CCmode, CCNmode, CCZNmode }. */
+
+/* Return the asm code for a conditional branch where XOP[0] is the
+ label and XOP[1] is a comparison operator of REG_CC against 0.
+*/
const char *
-ret_cond_branch (rtx x, int len, int reverse)
+avr_cond_branch (rtx_insn *insn, rtx *xop)
{
- RTX_CODE cond = reverse ? reverse_condition (GET_CODE (x)) : GET_CODE (x);
- bool cc_overflow_unusable = false;
+ machine_mode ccmode = GET_MODE (XEXP (xop[1], 0));
+ rtx_code cond = GET_CODE (xop[1]);
+ bool cc_overflow_unusable = ccmode != CCmode;
+ int len = avr_jump_mode (xop[0], insn);
+
+ if (ccmode == CCNmode)
+ // The N flag can only do < 0 and >= 0.
+ gcc_assert (cond == GE || cond == LT);
switch (cond)
{
@@ -4213,33 +4241,20 @@ ret_cond_branch (rtx x, int len, int reverse)
"brsh .+4" CR_TAB
"jmp %0"));
default:
- if (reverse)
- {
- 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)
{
- 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 "";
@@ -8980,6 +8995,34 @@ lshrsi3_out (rtx_insn *insn, rtx operands[], int *len)
}
+/* When INSN is a PARALLEL with two SETs, a SET of REG_CC and a SET of a
+ GPR, then return the second SET and set *CCMODE to the first SET's mode.
+ Otherwise, return single_set and set *CCMODE to VOIDmode. */
+
+static rtx
+avr_cc_set (rtx_insn *insn, machine_mode *ccmode)
+{
+ // single_set() not only depends on the anatomy of an insn but also
+ // on REG_UNUSED notes, thus we have to analyze by hand so that the
+ // result only depends on the pattern.
+
+ rtx pat = PATTERN (insn);
+
+ if (GET_CODE (pat) == PARALLEL
+ && XVECLEN (pat, 0) == 2
+ && GET_CODE (XVECEXP (pat, 0, 0)) == SET
+ && GET_CODE (XVECEXP (pat, 0, 1)) == SET)
+ {
+ rtx ccset = XVECEXP (pat, 0, 0);
+ *ccmode = GET_MODE (SET_DEST (ccset));
+ return XVECEXP (pat, 0, 1);
+ }
+
+ *ccmode = VOIDmode;
+ return single_set (insn);
+}
+
+
/* Output addition of registers YOP[0] and YOP[1]
YOP[0] += extend (YOP[1])
@@ -8988,8 +9031,11 @@ lshrsi3_out (rtx_insn *insn, rtx operands[], int *len)
YOP[0] -= extend (YOP[2])
- where the integer modes satisfy SI >= YOP[0].mode > YOP[1/2].mode >= QI,
- and the extension may be sign- or zero-extend. Returns "".
+ where the integer modes satisfy SI >= YOP[0].mode >= YOP[1/2].mode >= QI,
+ and the extension may be sign-extend, zero-extend or reg (no extend).
+ INSN is either a single_set or a true parallel insn. In the latter case,
+ INSN has two SETs: A SET of REG_CC and a SET like in the single_set case.
+ Returns "".
If PLEN == NULL output the instructions.
If PLEN != NULL set *PLEN to the length of the sequence in words. */
@@ -8998,8 +9044,13 @@ const char *
avr_out_plus_ext (rtx_insn *insn, rtx *yop, int *plen)
{
rtx regs[2];
+ machine_mode ccmode;
- const rtx src = SET_SRC (single_set (insn));
+ /* Ouch! Whether or not an insn is a single_set does not only depend
+ on the anatomy of the pattern, but also on REG_UNUSED notes.
+ Hence we have to dig by hand... */
+
+ const rtx src = SET_SRC (avr_cc_set (insn, &ccmode));
const RTX_CODE add = GET_CODE (src);
gcc_assert (GET_CODE (src) == PLUS || GET_CODE (src) == MINUS);
@@ -9010,7 +9061,7 @@ avr_out_plus_ext (rtx_insn *insn, rtx *yop, int *plen)
const RTX_CODE ext = GET_CODE (xext);
gcc_assert (REG_P (xreg)
- && (ext == ZERO_EXTEND || ext == SIGN_EXTEND));
+ && (ext == ZERO_EXTEND || ext == SIGN_EXTEND || ext == REG));
const int n_bytes0 = GET_MODE_SIZE (GET_MODE (xop[0]));
const int n_bytes1 = GET_MODE_SIZE (GET_MODE (xop[1]));
@@ -9030,7 +9081,9 @@ avr_out_plus_ext (rtx_insn *insn, rtx *yop, int *plen)
if (ext == SIGN_EXTEND
&& (n_bytes0 > 1 + n_bytes1
- || reg_overlap_mentioned_p (msb1, xop[0])))
+ || reg_overlap_mentioned_p (msb1, xop[0])
+ // The insn also wants to set SREG.N and SREG.Z.
+ || ccmode == CCZNmode))
{
// Sign-extending more than one byte: Set tmp_reg to 0 or -1
// depending on $1.msb. Same for the pathological case where
@@ -9688,10 +9741,51 @@ 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 HI, PSI or SI register, and XOP[1] is a register or 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);
+
+ // 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] };
+
+ if (REG_P (xop[1]))
+ {
+ avr_out_plus_1 (NULL_RTX, op, plen, PLUS, UNKNOWN, 0, false);
+ }
+ else
+ {
+ 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,
- then the addition can be performed without a clobber reg. Return "".
+ result. The mode is HI, PSI or SI. XOP[2] might be a d-regs clobber
+ register. If XOP[2] is SCRATCH, then the addition can be performed
+ without a clobber reg. Return "".
If PLEN == NULL, then output the instructions.
If PLEN != NULL, then set *PLEN to the length of the sequence in words. */
@@ -9711,15 +9805,6 @@ 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 (INTVAL (xval) == 1)
- return avr_asm_len ("inc %0", xop, plen, 1);
-
- if (INTVAL (xval) == -1)
- return avr_asm_len ("dec %0", xop, plen, 1);
- }
-
if (n_bytes == 2
&& avr_adiw_reg_p (xreg)
&& IN_RANGE (INTVAL (xval), 1, 63))
@@ -9804,6 +9889,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 = std::abs (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]:
@@ -11308,6 +11523,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;
@@ -11496,7 +11712,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);
@@ -14110,7 +14326,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);
@@ -14125,7 +14341,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
@@ -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"))
@@ -261,6 +261,7 @@ (define_mode_iterator QISI [QI HI PSI SI])
(define_mode_iterator QIDI [QI HI PSI SI DI])
(define_mode_iterator QIPSI [QI HI PSI])
(define_mode_iterator HISI [HI PSI SI])
+(define_mode_iterator HI_SI [HI SI])
;; Ordered integral and fixed-point modes of specific sizes.
(define_mode_iterator ALL1 [QI QQ UQQ])
@@ -277,6 +278,10 @@ (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_attr CCname [(CC "") (CCN "_N") (CCZN "_ZN")])
+
;; All supported move-modes
(define_mode_iterator MOVMODE [QI QQ UQQ
HI HQ UHQ HA UHA
@@ -320,6 +325,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])
@@ -6885,6 +6893,469 @@ (define_insn_and_split "*cbranch<HISI:mode>.<code><QIPSI:mode>.0"
})
+;; Try optimize decrement-and-branch. When we have an addition followed
+;; 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.
+;; The comparisons are split2 from their cbranch insns and before
+;; peephole2 patterns like for swapped_tst and sbrx_branch have been applied.
+
+;; We do NOT use cmpelim / SELECT_CC_MODE because it has many shortcomings
+;; and is by no means equipollent to the removed cc0 framework -- at least
+;; with regard to the avr backend: Whether or not the result of a comparison
+;; can be obtained as a byproduct of an operation might depend on the
+;; availability of a scratch register: There are cases where we need a
+;; scratch register to optimize away a comparison, and where the operation
+;; without a comparison does not require a scratch. With the peep2 approach
+;; below, we can get a scratch from the peep2 framework without increasing
+;; the register pressure, whereas cmpelim doesn't offer such a feature.
+;; When no scratch is available, then we just don't perform the optimizaton,
+;; i.e. the comparison against 0 won't be optimized away, which is preferred
+;; over increasing the register pressure -- in many cases without reason --
+;; which might result in additional spills.
+;; What we definitely do not want is to pop a scratch without need, and
+;; in some arithmetic insn we won't know whether it might also be considered
+;; for CCmode generation, at least not prior to register allocation:
+;; CCmode only comes into existence after register allocation.
+;; cmpelim has more shortcomings, for example some comparisons may not
+;; be available, and it does not handle several of the forms supported below,
+;; just to mention two. A solution for the former would be to return VOIDmode
+;; in SELECT_CC_MODE, but cmpelim doesn't handle that. Anyway, it's pointless
+;; to speculate about how other shortcomings could be fixed when the scratch
+;; problem is unsoved in cmpelim.
+;; Apart from that, compare-elim.cc lists some demands that are not
+;; compatible with this bachend. For example, it assumes that when an insn
+;; can set the condition code, it is always of the form compare:CCM, i.e.
+;; all comparisons are supported. This is not the case for AVR, see the
+;; peep2 conditions below. There is no way (at least not a documented one)
+;; to express that in SELECT_CC_MODE.
+;; Apart from that passes running before register allocation (and thus
+;; before split2) have #ifdef SELECT_CC_MODE, and nowhere there is an
+;; explanation on how to handle that.
+;; Skipping cmpelim is accomplished by not defining TARGET_FLAGS_REGNUM.
+
+;; Note: reload1.cc::do_output_reload() does not support output reloads
+;; for JUMP_INSNs, hence letting combine doing decrement-and-branch might
+;; run into an ICE. Doing reloads by hand is too painful, hence, stick with
+;; RTL peepholes for now.
+
+(define_expand "gen_add_for_<code>_<mode>"
+ [;; "*add.for.cczn.<mode>"
+ (parallel [(set (reg:CCZN REG_CC)
+ (compare:CCZN (plus:HISI (match_operand:HISI 0 "register_operand")
+ (match_operand:HISI 1 "const_int_operand"))
+ (const_int 0)))
+ (set (match_dup 0)
+ (plus:HISI (match_dup 0)
+ (match_dup 1)))
+ (clobber (match_operand:QI 3))])
+ ;; "branch_ZN"
+ (set (pc)
+ (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:HISI (match_operand:HISI 0 "register_operand")
+ (match_operand:HISI 1 "nonmemory_operand"))
+ (const_int 0)))
+ (set (match_dup 0)
+ (plus:HISI (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: Additions without a scratch register.
+(define_peephole2
+ [(parallel [(set (match_operand:HISI 0 "register_operand")
+ (plus:HISI (match_dup 0)
+ (match_operand:HISI 1 "nonmemory_operand")))
+ (clobber (reg:CC REG_CC))])
+ (parallel [(set (reg:CC REG_CC)
+ (compare:CC (match_dup 0)
+ (match_operand:HISI 3 "const0_operand")))
+ (clobber (scratch:QI))])
+ (set (pc)
+ (if_then_else (cmp_signed (reg:CC REG_CC)
+ (const_int 0))
+ (label_ref (match_operand 2))
+ (pc)))]
+ "// Multi-byte reg-reg additions only set the N flag.
+ (<CODE> == GE || <CODE> == LT || ! REG_P (operands[1]))
+ // Needs a const or a d-reg.
+ && (REG_P (operands[1]) || d_register_operand (operands[0], <MODE>mode))
+ && peep2_regno_dead_p (3, REG_CC)"
+ [(scratch)]
+ {
+ emit (gen_gen_add_for_<code>_<mode> (operands[0], operands[1], operands[2],
+ gen_rtx_SCRATCH (QImode)));
+ DONE;
+ })
+
+;; 2/3: Additions with a scratch register from the insn.
+(define_peephole2
+ [(parallel [(set (match_operand:HISI 0 "register_operand")
+ (plus:HISI (match_dup 0)
+ (match_operand:HISI 1 "nonmemory_operand")))
+ (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)
+ (match_operand:HISI 4 "const0_operand")))
+ (clobber (scratch:QI))])
+ (set (pc)
+ (if_then_else (cmp_signed (reg:CC REG_CC)
+ (const_int 0))
+ (label_ref (match_operand 2))
+ (pc)))]
+ "// Multi-byte reg-reg additions only set the N flag.
+ (<CODE> == GE || <CODE> == LT || ! REG_P (operands[1]))
+ && peep2_regno_dead_p (3, REG_CC)"
+ [(scratch)]
+ {
+ rtx scratch = operands[3];
+
+ // We need either a d-register or a scratch register
+ // when $1 is not a register.
+ if (! REG_P (operands[1])
+ && ! REG_P (scratch)
+ && ! d_register_operand (operands[0], <MODE>mode))
+ FAIL;
+
+ emit (gen_gen_add_for_<code>_<mode> (operands[0], operands[1], operands[2],
+ scratch));
+ DONE;
+ })
+
+;; 3/3: Additions with a scratch register from peephole2.
+(define_peephole2
+ [(match_scratch:QI 3 "d")
+ (parallel [(set (match_operand:HISI 0 "register_operand")
+ (plus:HISI (match_dup 0)
+ (match_operand:HISI 1 "const_int_operand")))
+ (clobber (reg:CC REG_CC))])
+ (parallel [(set (reg:CC REG_CC)
+ (compare:CC (match_dup 0)
+ (match_operand:HISI 4 "const0_operand")))
+ (clobber (scratch:QI))])
+ (set (pc)
+ (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]));
+ DONE;
+ })
+
+;; Result of the above three peepholes is an addition that also
+;; 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".
+
+;; "*add.for.cczn.hi" "*add.for.cczn.psi" "*add.for.cczn.si"
+(define_insn "*add.for.cczn.<mode>"
+ [(set (reg:CCZN REG_CC)
+ (compare:CCZN
+ (plus:HISI (match_operand:HISI 3 "register_operand" "0 ,0")
+ (match_operand:HISI 1 "const_int_operand" "n ,n"))
+ (const_int 0)))
+ (set (match_operand:HISI 0 "register_operand" "=d ,r")
+ (plus:HISI (match_operand:HISI 4 "register_operand" "0 ,0")
+ (match_operand:HISI 5 "const_int_operand" "1 ,1")))
+ (clobber (match_scratch:QI 2 "=X ,&d"))]
+ "reload_completed"
+ {
+ return avr_out_plus_set_ZN (operands, nullptr);
+ }
+ [(set (attr "length")
+ (symbol_ref "<SIZE> * (1 + REG_P (operands[2]))"))
+ (set_attr "adjust_len" "add_set_ZN")])
+
+;; "*add.for.ccn.hi" "*add.for.ccn.psi" "*add.for.ccn.si"
+(define_insn "*add.for.ccn.<mode>"
+ [(set (reg:CCN REG_CC)
+ (compare:CCN
+ (plus:HISI (match_operand:HISI 3 "register_operand" "0 ,0 ,0")
+ (match_operand:HISI 1 "nonmemory_operand" "n ,n ,r"))
+ (const_int 0)))
+ (set (match_operand:HISI 0 "register_operand" "=d ,r ,r")
+ (plus:HISI (match_operand:HISI 4 "register_operand" "0 ,0 ,0")
+ (match_operand:HISI 5 "nonmemory_operand" "1 ,1 ,1")))
+ (clobber (match_scratch:QI 2 "=X ,&d,X"))]
+ "reload_completed"
+ {
+ return avr_out_plus_set_N (operands, nullptr);
+ }
+ [(set (attr "length")
+ (symbol_ref "<SIZE> * (1 + REG_P (operands[2]))"))
+ (set_attr "adjust_len" "add_set_N")])
+
+
+;; 1/3: Subtractions with REG subtrahend set Z and N in a meaningful way.
+;; The QI and PSI cases are handled below because they don't have a scratch:QI.
+(define_peephole2
+ [(parallel [(set (match_operand:HI_SI 0 "register_operand")
+ (minus:HI_SI (match_dup 0)
+ (match_operand:HI_SI 1 "register_operand")))
+ (clobber (scratch:QI))
+ (clobber (reg:CC REG_CC))])
+ (parallel [(set (reg:CC REG_CC)
+ (compare:CC (match_dup 0)
+ (match_operand:HI_SI 3 "const0_operand")))
+ (clobber (scratch:QI))])
+ (set (pc)
+ (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)"
+ [;; "*sub.for.cczn.<mode>"
+ (parallel [(set (reg:CCZN REG_CC)
+ (compare:CCZN (minus:HI_SI (match_dup 0)
+ (match_dup 1))
+ (const_int 0)))
+ (set (match_dup 0)
+ (minus:HI_SI (match_dup 0)
+ (match_dup 1)))])
+ ;; "branch_ZN"
+ (set (pc)
+ (if_then_else (cmp_signed (reg:CCZN REG_CC)
+ (const_int 0))
+ (label_ref (match_dup 2))
+ (pc)))])
+
+;; 2/3: Subtractions with a PSImode REG: no scratch:QI.
+(define_peephole2
+ [(parallel [(set (match_operand:PSI 0 "register_operand")
+ (minus:PSI (match_dup 0)
+ (match_operand:PSI 1 "register_operand")))
+ (clobber (reg:CC REG_CC))])
+ (parallel [(set (reg:CC REG_CC)
+ (compare:CC (match_dup 0)
+ (match_operand:PSI 3 "const0_operand")))
+ (clobber (scratch:QI))])
+ (set (pc)
+ (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)"
+ [;; "*sub.for.cczn.psi"
+ (parallel [(set (reg:CCZN REG_CC)
+ (compare:CCZN (minus:PSI (match_dup 0)
+ (match_dup 1))
+ (const_int 0)))
+ (set (match_dup 0)
+ (minus:PSI (match_dup 0)
+ (match_dup 1)))])
+ ;; "branch_ZN"
+ (set (pc)
+ (if_then_else (cmp_signed (reg:CCZN REG_CC)
+ (const_int 0))
+ (label_ref (match_dup 2))
+ (pc)))])
+
+;; 3/3: Subtractions that extend the subtrahend.
+(define_peephole2
+ [(parallel [(set (match_operand:HISI 0 "register_operand")
+ (minus:HISI (match_dup 0)
+ (any_extend:HISI (match_operand:QIPSI 1 "register_operand"))))
+ (clobber (reg:CC REG_CC))])
+ (parallel [(set (reg:CC REG_CC)
+ (compare:CC (match_dup 0)
+ (match_operand:HISI 3 "const0_operand")))
+ (clobber (scratch:QI))])
+ (set (pc)
+ (if_then_else (cmp_signed (reg:CC REG_CC)
+ (const_int 0))
+ (label_ref (match_operand 2))
+ (pc)))]
+ "<HISI:SIZE> > <QIPSI:SIZE>
+ && peep2_regno_dead_p (3, REG_CC)"
+ [;; "*sub-extend<QIPSI:mode>.for.cczn.<HISI:mode>"
+ (parallel [(set (reg:CCZN REG_CC)
+ (compare:CCZN (minus:HISI (match_dup 0)
+ (any_extend:HISI (match_dup 1)))
+ (const_int 0)))
+ (set (match_dup 0)
+ (minus:HISI (match_dup 0)
+ (any_extend:HISI (match_dup 1))))])
+ ;; "branch_ZN"
+ (set (pc)
+ (if_then_else (cmp_signed (reg:CCZN REG_CC)
+ (const_int 0))
+ (label_ref (match_dup 2))
+ (pc)))])
+
+;; "*sub.for.cczn.hi"
+;; "*sub.for.cczn.psi"
+;; "*sub.for.cczn.si"
+(define_insn "*sub.for.cczn.<mode>"
+ [(set (reg:CCZN REG_CC)
+ (compare:CCZN (minus:HISI (match_operand:HISI 3 "register_operand" "1")
+ (match_operand:HISI 4 "register_operand" "2"))
+ (const_int 0)))
+ (set (match_operand:HISI 0 "register_operand" "=r")
+ (minus:HISI (match_operand:HISI 1 "register_operand" "0")
+ (match_operand:HISI 2 "register_operand" "r")))]
+ "reload_completed"
+ {
+ return avr_out_plus_ext (insn, operands, nullptr);
+ }
+ [(set_attr "length" "<SIZE>")])
+
+
+(define_insn "*sub-extend<QIPSI:mode>.for.cczn.<HISI:mode>"
+ [(set (reg:CCZN REG_CC)
+ (compare:CCZN (minus:HISI (match_operand:HISI 3 "register_operand" "0")
+ (any_extend:HISI
+ (match_operand:QIPSI 4 "register_operand" "2")))
+ (const_int 0)))
+ (set (match_operand:HISI 0 "register_operand" "=r")
+ (minus:HISI (match_operand:HISI 1 "register_operand" "0")
+ (any_extend:HISI (match_operand:QIPSI 2 "register_operand" "r"))))]
+ "reload_completed
+ && <HISI:SIZE> > <QIPSI:SIZE>"
+ {
+ return avr_out_plus_ext (insn, operands, nullptr);
+ }
+ [(set (attr "length")
+ (symbol_ref "<HISI:SIZE> + 3 * (<CODE> == SIGN_EXTEND)"))])
+
+
+;; Operations other that PLUS can set the condition code in
+;; a meaningful way, too.
+
+;; 1/1 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))])
+ (parallel [(set (reg:CC REG_CC)
+ (compare:CC (match_dup 0)
+ (const_int 0)))
+ (clobber (scratch:QI))])
+ (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)))])
+
+(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"
+ {
+ output_asm_insn ("lsl %A0", operands);
+ 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/1 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_dup 0)
+ (match_operand:QI 4 "const0_operand")))
+ (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 (cmp_signed (reg:CCZN REG_CC)
+ (const_int 0))
+ (label_ref (match_operand 3))
+ (pc)))])
+
+;; Constraints and predicate for the insn below. This is what op8_ZN_operator
+;; allows. Constraints are written in such a way that all cases have two
+;; alternatives (shifts, XOR and MINUS have effectively just one alternative).
+;; Note again that due to nregs, match_dup's won't work.
+(define_code_attr c0_op8
+ [(xor "r,r") (minus "r,r") (ashift "r,r") (ashiftrt "r,r") (lshiftrt "r,r")
+ (and "d,r") (ior "d,r") (plus "d,r")])
+
+(define_code_attr c2_op8
+ [(xor "r,r") (minus "r,r") (and "n,r") (ior "n,r") (plus "n,r P N K Cm2")
+ (ashift "P K,C03") (ashiftrt "P K,C03") (lshiftrt "P K,C03")])
+
+(define_code_attr p2_op8
+ [(ashift "const_1_to_3") (ashiftrt "const_1_to_3") (lshiftrt "const_1_to_3")
+ (xor "register") (minus "register")
+ (plus "nonmemory") (and "nonmemory") (ior "nonmemory")])
+
+;; Result of the peephole2 above: An 8-bit operation that sets Z and N.
+;; The allowed operations are: PLUS, MINUS, AND, IOR, XOR and SHIFTs
+;; with operands according to op8_ZN_operator.
+(define_insn "*op8.for.cczn.<code>"
+ [(set (reg:CCZN REG_CC)
+ (compare:CCZN (op8_ZN:QI (match_operand:QI 3 "register_operand" "0,0")
+ (match_operand:QI 4 "<p2_op8>_operand" "2,2"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "register_operand" "=<c0_op8>")
+ (op8_ZN:QI (match_operand:QI 1 "register_operand" "0,0")
+ (match_operand:QI 2 "<p2_op8>_operand" "<c2_op8>")))]
+ "reload_completed"
+ {
+ return avr_out_op8_set_ZN (<CODE>, operands, nullptr);
+ }
+ [(set (attr "length")
+ (symbol_ref "avr_len_op8_set_ZN (<CODE>, operands)"))])
+
+
;; Test a single bit in a QI/HI/SImode register.
;; Combine will create zero-extract patterns for single-bit tests.
;; Permit any mode in source pattern by using VOIDmode.
@@ -7046,32 +7517,25 @@ (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 avr_cond_branch (insn, operands);
}
- [(set_attr "type" "branch1")])
+ [(set (attr "type")
+ (if_then_else
+ (match_test "simple_comparison_operator (operands[1], VOIDmode)")
+ (const_string "branch")
+ (const_string "branch1")))])
;; **************************************************************************
@@ -9551,173 +10015,6 @@ (define_peephole2
(clobber (reg:CC REG_CC))])])
-;; Try optimize decrement-and-branch. When we have an addition followed
-;; 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")
- (HI "Yxx") (PSI "Yxx") (SI "Yxx")])
-
-;; FIXME: reload1.cc::do_output_reload() does not support output reloads
-;; for JUMP_INSNs, hence letting combine doing decrement-and-branch like
-;; the following might run into ICE. Doing reloads by hand is too painful...
-;
-; (define_insn_and_split "*add.for.eqne.<mode>.cbranch"
-; [(set (pc)
-; (if_then_else (eqne (match_operand:QISI 1 "register_operand" "0")
-; (match_operand:QISI 2 "const_int_operand" "n"))
-; (label_ref (match_operand 4))
-; (pc)))
-; (set (match_operand:QISI 0 "register_operand" "=r")
-; (plus:QISI (match_dup 1)
-; (match_operand:QISI 3 "const_int_operand" "n")))]
-; ;; No clobber for now as combine might not have one handy.
-; ;; We pop a scatch in split1.
-; "!reload_completed
-; && const0_rtx == simplify_binary_operation (PLUS, <MODE>mode,
-; operands[2], operands[3])"
-; { gcc_unreachable(); }
-; "&& 1"
-; [(parallel [(set (pc)
-; (if_then_else (eqne (match_dup 1)
-; (match_dup 2))
-; (label_ref (match_dup 4))
-; (pc)))
-; (set (match_dup 0)
-; (plus:QISI (match_dup 1)
-; (match_dup 3)))
-; (clobber (scratch:QI))])])
-;
-;; ...Hence, stick with RTL peepholes for now. Unfortunately, there is no
-;; canonical form, and if reload shuffles registers around, we might miss
-;; opportunities to match a decrement-and-branch.
-;; 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)))
- (set (match_dup 0)
- (plus:QISI (match_dup 0)
- (match_dup 1)))
- (clobber (match_operand:QI 3))])
- ; "branch"
- (set (pc)
- (if_then_else (eqne (reg:CC REG_CC)
- (const_int 0))
- (label_ref (match_dup 2))
- (pc)))])
-
-
-;; 1/3: A version without clobber: d-reg or 8-bit adds +/-1.
-(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)))
- (set (pc)
- (if_then_else (eqne (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)))"
- [(scratch)]
- {
- emit (gen_gen_add_for_<code>_<mode> (operands[0], operands[1], operands[2],
- gen_rtx_SCRATCH (QImode)));
- DONE;
- })
-
-;; 2/3: A version with clobber from the insn.
-(define_peephole2
- [(parallel [(set (match_operand:QISI 0 "register_operand")
- (plus:QISI (match_dup 0)
- (match_operand:QISI 1 "const_int_operand")))
- (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)))
- (clobber (match_operand:QI 4 "scratch_or_d_register_operand"))])
- (set (pc)
- (if_then_else (eqne (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];
-
- // 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)))
- {
- FAIL;
- }
- emit (gen_gen_add_for_<code>_<mode> (operands[0], operands[1], operands[2],
- scratch));
- DONE;
- })
-
-;; 3/3 A version with a clobber from peephole2.
-(define_peephole2
- [(match_scratch:QI 3 "d")
- (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)))
- (set (pc)
- (if_then_else (eqne (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]));
- DONE;
- })
-
-;; Result of the above three peepholes is an addition that also
-;; performs an EQ or NE 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"))
- (const_int 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"))]
- "reload_completed"
- {
- return avr_out_plus_set_ZN (operands, nullptr);
- }
- [(set_attr "adjust_len" "add_set_ZN")])
-
-
;; Swapping both comparison and branch condition. This can turn difficult
;; branches to easy ones. And in some cases, a comparison against one can
;; be turned into a comparison against zero.
@@ -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")
@@ -242,10 +253,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")
new file mode 100644
@@ -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__((noipa))
+
+#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;
+}
new file mode 100644
@@ -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__((noipa))
+
+#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;
+}
new file mode 100644
@@ -0,0 +1,76 @@
+/* { dg-do run } */
+/* { dg-additional-options { -std=c99 -Os } } */
+
+typedef __UINT8_TYPE__ u8;
+typedef __INT8_TYPE__ i8;
+typedef __UINT16_TYPE__ u16;
+typedef __INT16_TYPE__ i16;
+typedef __uint24 u24;
+typedef __int24 i24;
+typedef __UINT32_TYPE__ u32;
+typedef __INT32_TYPE__ i32;
+
+#define AI static inline __attribute__((always_inline))
+#define NI __attribute__((noipa))
+
+u8 volatile v;
+u8 v1;
+
+#define MK_FUN(TYP, OP, TST) \
+NI TYP add1_##TYP##_##OP (TYP a, TYP b) \
+{ \
+ a += b; \
+ if (a TST) \
+ v = 0; \
+ return a; \
+} \
+ \
+NI TYP add2_##TYP##_##OP (TYP a, TYP b) \
+{ \
+ a += b; \
+ __asm ("" : "+r" (a)); \
+ if (a TST) \
+ v = 0; \
+ return a; \
+} \
+ \
+NI void test_##TYP##_##OP (TYP a, TYP b)\
+{ \
+ v = 1; \
+ TYP c1 = add1_##TYP##_##OP (a, b); \
+ v1 = v; \
+ v = 1; \
+ TYP c2 = add2_##TYP##_##OP (a, b); \
+ if (c1 != c2) \
+ __builtin_abort(); \
+ if (v1 != v) \
+ __builtin_abort(); \
+}
+
+MK_FUN (i16, ge, >= 0)
+MK_FUN (i24, ge, >= 0)
+MK_FUN (i32, ge, >= 0)
+
+MK_FUN (i16, lt, < 0)
+MK_FUN (i24, lt, < 0)
+MK_FUN (i32, lt, < 0)
+
+
+int main (void)
+{
+ for (i8 a = -5; a <= 5; ++a)
+ {
+ for (i8 b = -5; b <= 5; ++b)
+ {
+ test_i16_ge (a, b);
+ test_i24_ge (a, b);
+ test_i32_ge (a, b);
+
+ test_i16_lt (a, b);
+ test_i24_lt (a, b);
+ test_i32_lt (a, b);
+ }
+ }
+
+ return 0;
+}
new file mode 100644
@@ -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__((noipa))
+
+#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;
+}
new file mode 100644
@@ -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__((noipa))
+
+#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;
+}
new file mode 100644
@@ -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__((noipa))
+
+#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;
+}
new file mode 100644
@@ -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__((noipa))
+
+#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;
+}
new file mode 100644
@@ -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__((noipa))
+
+#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;
+}
new file mode 100644
@@ -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__((noipa))
+
+#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;
+}
new file mode 100644
@@ -0,0 +1,100 @@
+/* { dg-do run } */
+/* { dg-additional-options { -std=c99 -Os } } */
+
+typedef __UINT8_TYPE__ u8;
+typedef __INT8_TYPE__ i8;
+typedef __UINT16_TYPE__ u16;
+typedef __INT16_TYPE__ i16;
+typedef __uint24 u24;
+typedef __int24 i24;
+typedef __UINT32_TYPE__ u32;
+typedef __INT32_TYPE__ i32;
+
+#define AI static inline __attribute__((always_inline))
+#define NI __attribute__((noipa))
+
+u8 volatile v;
+u8 v1;
+
+#define MK_FUN(A, B, OP, TST) \
+NI A sub1_##A##_##B##_##OP (A a, B b) \
+{ \
+ a -= b; \
+ if (a TST) \
+ v = 0; \
+ return a; \
+} \
+ \
+NI A sub2_##A##_##B##_##OP (A a, B b) \
+{ \
+ a -= b; \
+ __asm ("" : "+r" (a)); \
+ if (a TST) \
+ v = 0; \
+ return a; \
+} \
+ \
+NI void test_##A##_##B##_##OP (A a, B b)\
+{ \
+ v = 1; \
+ A c1 = sub1_##A##_##B##_##OP (a, b); \
+ v1 = v; \
+ v = 1; \
+ A c2 = sub2_##A##_##B##_##OP (a, b); \
+ if (c1 != c2) \
+ __builtin_abort(); \
+ if (v1 != v) \
+ __builtin_abort(); \
+}
+
+MK_FUN (u16, u8, ne, != 0)
+MK_FUN (u24, u8, eq, == 0)
+MK_FUN (u32, u16, ne, != 0)
+
+MK_FUN (i16, i8, ge, >= 0)
+MK_FUN (i24, i8, ge, >= 0)
+MK_FUN (i32, i16, ge, >= 0)
+
+MK_FUN (i16, i8, lt, < 0)
+MK_FUN (i24, i8, lt, < 0)
+MK_FUN (i32, i16, lt, < 0)
+
+MK_FUN (i16, i8, le, <= 0)
+MK_FUN (i24, i8, le, <= 0)
+MK_FUN (i32, i16, le, <= 0)
+
+MK_FUN (i16, i8, gt, > 0)
+MK_FUN (i24, i8, gt, > 0)
+MK_FUN (i32, i16, gt, > 0)
+
+
+int main (void)
+{
+ for (i8 a = -5; a <= 5; ++a)
+ {
+ for (i8 b = -5; b <= 5; ++b)
+ {
+ test_u16_u8_ne (a, b);
+ test_u24_u8_eq (a, b);
+ test_u32_u16_ne (a, b);
+
+ test_i16_i8_ge (a, b);
+ test_i24_i8_ge (a, b);
+ test_i32_i16_ge (a, b);
+
+ test_i16_i8_lt (a, b);
+ test_i24_i8_lt (a, b);
+ test_i32_i16_lt (a, b);
+
+ test_i16_i8_gt (a, b);
+ test_i24_i8_gt (a, b);
+ test_i32_i16_gt (a, b);
+
+ test_i16_i8_le (a, b);
+ test_i24_i8_le (a, b);
+ test_i32_i16_le (a, b);
+ }
+ }
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,100 @@
+/* { dg-do run } */
+/* { dg-additional-options { -std=c99 -Os } } */
+
+typedef __UINT8_TYPE__ u8;
+typedef __INT8_TYPE__ i8;
+typedef __UINT16_TYPE__ u16;
+typedef __INT16_TYPE__ i16;
+typedef __uint24 u24;
+typedef __int24 i24;
+typedef __UINT32_TYPE__ u32;
+typedef __INT32_TYPE__ i32;
+
+#define AI static inline __attribute__((always_inline))
+#define NI __attribute__((noipa))
+
+u8 volatile v;
+u8 v1;
+
+#define MK_FUN(TYP, OP, TST) \
+NI TYP sub1_##TYP##_##OP (TYP a, TYP b) \
+{ \
+ a -= b; \
+ if (a TST) \
+ v = 0; \
+ return a; \
+} \
+ \
+NI TYP sub2_##TYP##_##OP (TYP a, TYP b) \
+{ \
+ a -= b; \
+ __asm ("" : "+r" (a)); \
+ if (a TST) \
+ v = 0; \
+ return a; \
+} \
+ \
+NI void test_##TYP##_##OP (TYP a, TYP b)\
+{ \
+ v = 1; \
+ TYP c1 = sub1_##TYP##_##OP (a, b); \
+ v1 = v; \
+ v = 1; \
+ TYP c2 = sub2_##TYP##_##OP (a, b); \
+ if (c1 != c2) \
+ __builtin_abort(); \
+ if (v1 != v) \
+ __builtin_abort(); \
+}
+
+MK_FUN (i8, ge, >= 0)
+MK_FUN (i16, ge, >= 0)
+MK_FUN (i24, ge, >= 0)
+MK_FUN (i32, ge, >= 0)
+
+MK_FUN (i8, lt, < 0)
+MK_FUN (i16, lt, < 0)
+MK_FUN (i24, lt, < 0)
+MK_FUN (i32, lt, < 0)
+
+MK_FUN (i8, le, <= 0)
+MK_FUN (i16, le, <= 0)
+MK_FUN (i24, le, <= 0)
+MK_FUN (i32, le, <= 0)
+
+MK_FUN (i8, gt, > 0)
+MK_FUN (i16, gt, > 0)
+MK_FUN (i24, gt, > 0)
+MK_FUN (i32, gt, > 0)
+
+
+int main (void)
+{
+ for (i8 a = -5; a <= 5; ++a)
+ {
+ for (i8 b = -5; b <= 5; ++b)
+ {
+ test_i8_ge (a, b);
+ test_i16_ge (a, b);
+ test_i24_ge (a, b);
+ test_i32_ge (a, b);
+
+ test_i8_lt (a, b);
+ test_i16_lt (a, b);
+ test_i24_lt (a, b);
+ test_i32_lt (a, b);
+
+ test_i8_gt (a, b);
+ test_i16_gt (a, b);
+ test_i24_gt (a, b);
+ test_i32_gt (a, b);
+
+ test_i8_le (a, b);
+ test_i16_le (a, b);
+ test_i24_le (a, b);
+ test_i32_le (a, b);
+ }
+ }
+
+ return 0;
+}