===================================================================
@@ -136,7 +136,7 @@ (define_attr "length" ""
;; Otherwise do special processing depending on the attribute.
(define_attr "adjust_len"
- "yes,no,reload_in32,out_bitop"
+ "yes,no,reload_in32,out_bitop,tsthi,tstsi,compare"
(const_string "yes"))
;; Define mode iterators
@@ -3328,125 +3328,62 @@ (define_insn "*cmpqi"
(define_insn "*cmpqi_sign_extend"
[(set (cc0)
- (compare (sign_extend:HI
- (match_operand:QI 0 "register_operand" "d"))
- (match_operand:HI 1 "const_int_operand" "n")))]
- "INTVAL (operands[1]) >= -128 && INTVAL (operands[1]) <= 127"
+ (compare (sign_extend:HI (match_operand:QI 0 "register_operand" "d"))
+ (match_operand:HI 1 "s8_operand" "n")))]
+ ""
"cpi %0,lo8(%1)"
[(set_attr "cc" "compare")
(set_attr "length" "1")])
(define_insn "*cmphi"
[(set (cc0)
- (compare (match_operand:HI 0 "register_operand" "!w,r,r,d,d,r,r")
- (match_operand:HI 1 "nonmemory_operand" "L,L,r,M,i,M,i")))
- (clobber (match_scratch:QI 2 "=X,X,X,X,&d,&d,&d"))]
+ (compare (match_operand:HI 0 "register_operand" "!w,r,r,d ,r ,d,r")
+ (match_operand:HI 1 "nonmemory_operand" "L ,L,r,s ,s ,M,n")))
+ (clobber (match_scratch:QI 2 "=X ,X,X,&d,&d ,X,&d"))]
""
- "*{
- switch (which_alternative)
- {
- case 0: case 1:
- return out_tsthi (insn, operands[0], NULL);
-
- case 2:
- return (AS2 (cp,%A0,%A1) CR_TAB
- AS2 (cpc,%B0,%B1));
- case 3:
- if (reg_unused_after (insn, operands[0])
- && INTVAL (operands[1]) >= 0 && INTVAL (operands[1]) <= 63
- && test_hard_reg_class (ADDW_REGS, operands[0]))
- return AS2 (sbiw,%0,%1);
- else
- return (AS2 (cpi,%0,%1) CR_TAB
- AS2 (cpc,%B0,__zero_reg__));
- case 4:
- if (reg_unused_after (insn, operands[0]))
- return (AS2 (subi,%0,lo8(%1)) CR_TAB
- AS2 (sbci,%B0,hi8(%1)));
- else
- return (AS2 (ldi, %2,hi8(%1)) CR_TAB
- AS2 (cpi, %A0,lo8(%1)) CR_TAB
- AS2 (cpc, %B0,%2));
- case 5:
- return (AS2 (ldi, %2,lo8(%1)) CR_TAB
- AS2 (cp, %A0,%2) CR_TAB
- AS2 (cpc, %B0,__zero_reg__));
-
- case 6:
- return (AS2 (ldi, %2,lo8(%1)) CR_TAB
- AS2 (cp, %A0,%2) CR_TAB
- AS2 (ldi, %2,hi8(%1)) CR_TAB
- AS2 (cpc, %B0,%2));
- }
- return \"bug\";
-}"
- [(set_attr "cc" "compare,compare,compare,compare,compare,compare,compare")
- (set_attr "length" "1,2,2,2,3,3,4")])
+ {
+ switch (which_alternative)
+ {
+ case 0:
+ case 1:
+ return avr_out_tsthi (insn, operands, NULL);
+
+ case 2:
+ return "cp %A0,%A1\;cpc %B0,%B1";
+
+ case 3:
+ return reg_unused_after (insn, operands[0])
+ ? "subi %A0,lo8(%1)\;sbci %B0,hi8(%1)"
+ : "ldi %2,hi8(%1)\;cpi %A0,lo8(%1)\;cpc %B0,%2";
+
+ case 4:
+ return "ldi %2,lo8(%1)\;cp %A0,%2\;ldi %2,hi8(%1)\;cpc %B0,%2";
+ }
+
+ return avr_out_compare (insn, operands, NULL);
+ }
+ [(set_attr "cc" "compare")
+ (set_attr "length" "1,2,2,3,4,2,4")
+ (set_attr "adjust_len" "tsthi,tsthi,no,no,no,compare,compare")])
(define_insn "*cmpsi"
[(set (cc0)
- (compare (match_operand:SI 0 "register_operand" "r,r,d,d,r,r")
- (match_operand:SI 1 "nonmemory_operand" "L,r,M,i,M,i")))
- (clobber (match_scratch:QI 2 "=X,X,X,&d,&d,&d"))]
+ (compare (match_operand:SI 0 "register_operand" "r,r ,d,r ,r")
+ (match_operand:SI 1 "nonmemory_operand" "L,r ,M,M ,n")))
+ (clobber (match_scratch:QI 2 "=X,X ,X,&d,&d"))]
""
- "*{
- switch (which_alternative)
- {
- case 0:
- return out_tstsi (insn, operands[0], NULL);
-
- case 1:
- return (AS2 (cp,%A0,%A1) CR_TAB
- AS2 (cpc,%B0,%B1) CR_TAB
- AS2 (cpc,%C0,%C1) CR_TAB
- AS2 (cpc,%D0,%D1));
- case 2:
- if (reg_unused_after (insn, operands[0])
- && INTVAL (operands[1]) >= 0 && INTVAL (operands[1]) <= 63
- && test_hard_reg_class (ADDW_REGS, operands[0]))
- return (AS2 (sbiw,%0,%1) CR_TAB
- AS2 (cpc,%C0,__zero_reg__) CR_TAB
- AS2 (cpc,%D0,__zero_reg__));
- else
- return (AS2 (cpi,%A0,lo8(%1)) CR_TAB
- AS2 (cpc,%B0,__zero_reg__) CR_TAB
- AS2 (cpc,%C0,__zero_reg__) CR_TAB
- AS2 (cpc,%D0,__zero_reg__));
- case 3:
- if (reg_unused_after (insn, operands[0]))
- return (AS2 (subi,%A0,lo8(%1)) CR_TAB
- AS2 (sbci,%B0,hi8(%1)) CR_TAB
- AS2 (sbci,%C0,hlo8(%1)) CR_TAB
- AS2 (sbci,%D0,hhi8(%1)));
- else
- return (AS2 (cpi, %A0,lo8(%1)) CR_TAB
- AS2 (ldi, %2,hi8(%1)) CR_TAB
- AS2 (cpc, %B0,%2) CR_TAB
- AS2 (ldi, %2,hlo8(%1)) CR_TAB
- AS2 (cpc, %C0,%2) CR_TAB
- AS2 (ldi, %2,hhi8(%1)) CR_TAB
- AS2 (cpc, %D0,%2));
- case 4:
- return (AS2 (ldi,%2,lo8(%1)) CR_TAB
- AS2 (cp,%A0,%2) CR_TAB
- AS2 (cpc,%B0,__zero_reg__) CR_TAB
- AS2 (cpc,%C0,__zero_reg__) CR_TAB
- AS2 (cpc,%D0,__zero_reg__));
- case 5:
- return (AS2 (ldi, %2,lo8(%1)) CR_TAB
- AS2 (cp, %A0,%2) CR_TAB
- AS2 (ldi, %2,hi8(%1)) CR_TAB
- AS2 (cpc, %B0,%2) CR_TAB
- AS2 (ldi, %2,hlo8(%1)) CR_TAB
- AS2 (cpc, %C0,%2) CR_TAB
- AS2 (ldi, %2,hhi8(%1)) CR_TAB
- AS2 (cpc, %D0,%2));
- }
- return \"bug\";
-}"
- [(set_attr "cc" "compare,compare,compare,compare,compare,compare")
- (set_attr "length" "4,4,4,7,5,8")])
+ {
+ if (0 == which_alternative)
+ return avr_out_tstsi (insn, operands, NULL);
+ else if (1 == which_alternative)
+ return "cp %A0,%A1\;cpc %B0,%B1\;cpc %C0,%C1\;cpc %D0,%D1";
+
+ return avr_out_compare (insn, operands, NULL);
+ }
+ [(set_attr "cc" "compare")
+ (set_attr "length" "4,4,4,5,8")
+ (set_attr "adjust_len" "tstsi,no,compare,compare,compare")])
;; ----------------------------------------------------------------------
===================================================================
@@ -47,7 +47,6 @@ extern void init_cumulative_args (CUMULA
#ifdef RTX_CODE
extern void asm_output_external_libcall (FILE *file, rtx symref);
-extern int compare_diff_p (rtx insn);
extern const char *output_movqi (rtx insn, rtx operands[], int *l);
extern const char *output_movhi (rtx insn, rtx operands[], int *l);
extern const char *out_movqi_r_mr (rtx insn, rtx op[], int *l);
@@ -57,8 +56,9 @@ extern const char *out_movhi_mr_r (rtx i
extern const char *out_movsi_r_mr (rtx insn, rtx op[], int *l);
extern const char *out_movsi_mr_r (rtx insn, rtx op[], int *l);
extern const char *output_movsisf (rtx insn, rtx operands[], int *l);
-extern const char *out_tstsi (rtx insn, rtx src, int *l);
-extern const char *out_tsthi (rtx insn, rtx src, int *l);
+extern const char *avr_out_tstsi (rtx, rtx*, int*);
+extern const char *avr_out_tsthi (rtx, rtx*, int*);
+extern const char *avr_out_compare (rtx, rtx*, int*);
extern const char *ret_cond_branch (rtx x, int len, int reverse);
extern const char *ashlqi3_out (rtx insn, rtx operands[], int *len);
@@ -102,7 +102,6 @@ extern void final_prescan_insn (rtx insn
extern int avr_simplify_comparison_p (enum machine_mode mode,
RTX_CODE op, rtx x);
extern RTX_CODE avr_normalize_condition (RTX_CODE condition);
-extern int compare_eq_p (rtx insn);
extern void out_shift_with_cnt (const char *templ, rtx insn,
rtx operands[], int *len, int t_len);
extern rtx avr_incoming_return_addr_rtx (void);
===================================================================
@@ -69,9 +69,7 @@ static const char *ptrreg_to_str (int);
static const char *cond_string (enum rtx_code);
static int avr_num_arg_regs (enum machine_mode, const_tree);
-static RTX_CODE compare_condition (rtx insn);
static rtx avr_legitimize_address (rtx, rtx, enum machine_mode);
-static int compare_sign_p (rtx insn);
static tree avr_handle_progmem_attribute (tree *, tree, tree, int, bool *);
static tree avr_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
static tree avr_handle_fntype_attribute (tree *, tree, tree, int, bool *);
@@ -1291,7 +1289,8 @@ avr_legitimize_address (rtx x, rtx oldx,
by OPERANDS. This is just forwarding to output_asm_insn.
If PLEN != NULL:
- Add N_WORDS to *PLEN.
+ If N_WORDS >= 0 Add N_WORDS to *PLEN.
+ If N_WORDS < 0 Set *PLEN to -N_WORDS.
Don't output anything.
*/
@@ -1304,7 +1303,10 @@ avr_asm_len (const char* tpl, rtx* opera
}
else
{
- *plen += n_words;
+ if (n_words < 0)
+ *plen = -n_words;
+ else
+ *plen += n_words;
}
}
@@ -3052,28 +3054,30 @@ compare_condition (rtx insn)
return UNKNOWN;
}
-/* Returns nonzero if INSN is a tst insn that only tests the sign. */
-static int
+/* Returns true iff INSN is a tst insn that only tests the sign. */
+
+static bool
compare_sign_p (rtx insn)
{
RTX_CODE cond = compare_condition (insn);
return (cond == GE || cond == LT);
}
-/* Returns nonzero if the next insn is a JUMP_INSN with a condition
+
+/* Returns true iff the next insn is a JUMP_INSN with a condition
that needs to be swapped (GT, GTU, LE, LEU). */
-int
+static bool
compare_diff_p (rtx insn)
{
RTX_CODE cond = compare_condition (insn);
return (cond == GT || cond == GTU || cond == LE || cond == LEU) ? cond : 0;
}
-/* Returns nonzero if INSN is a compare insn with the EQ or NE condition. */
+/* Returns true iff INSN is a compare insn with the EQ or NE condition. */
-int
+static bool
compare_eq_p (rtx insn)
{
RTX_CODE cond = compare_condition (insn);
@@ -3081,56 +3085,173 @@ compare_eq_p (rtx insn)
}
+/* Output compare instruction
+
+ compare (XOP[0], XOP[1])
+
+ for an HI/SI register XOP[0] and an integer XOP[1]. Return "".
+ XOP[2] is an 8-bit scratch register as needed.
+
+ PLEN == NULL: Output instructions.
+ PLEN != NULL: Set *PLEN to the length (in words) of the sequence.
+ Don't output anything. */
+
+const char*
+avr_out_compare (rtx insn, rtx *xop, int *plen)
+{
+ /* Register to compare and value to compare against. */
+ rtx xreg = xop[0];
+ rtx xval = xop[1];
+
+ /* MODE of the comparison. */
+ enum machine_mode mode = GET_MODE (xreg);
+
+ /* Number of bytes to operate on. */
+ int i, n_bytes = GET_MODE_SIZE (mode);
+
+ /* Value (0..0xff) held in clobber register xop[2] or -1 if unknown. */
+ int clobber_val = -1;
+
+ gcc_assert (REG_P (xreg)
+ && CONST_INT_P (xval));
+
+ if (plen)
+ *plen = 0;
+
+ for (i = 0; i < n_bytes; i++)
+ {
+ /* We compare byte-wise. */
+ rtx reg8 = simplify_gen_subreg (QImode, xreg, mode, i);
+ rtx xval8 = simplify_gen_subreg (QImode, xval, mode, i);
+
+ /* 8-bit value to compare with this byte. */
+ unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode);
+
+ /* Registers R16..R31 can operate with immediate. */
+ bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8);
+
+ xop[0] = reg8;
+ xop[1] = gen_int_mode (val8, QImode);
+
+ /* Word registers >= R24 can use SBIW/ADIW with 0..63. */
+
+ if (i == 0
+ && test_hard_reg_class (ADDW_REGS, reg8))
+ {
+ int val16 = trunc_int_for_mode (INTVAL (xval), HImode);
+
+ if (IN_RANGE (val16, 0, 63)
+ && (val8 == 0
+ || reg_unused_after (insn, xreg)))
+ {
+ avr_asm_len ("sbiw %0,%1", xop, plen, 1);
+ i++;
+ continue;
+ }
+
+ if (n_bytes == 2
+ && IN_RANGE (val16, -63, -1)
+ && compare_eq_p (insn)
+ && reg_unused_after (insn, xreg))
+ {
+ avr_asm_len ("adiw %0,%n1", xop, plen, 1);
+ break;
+ }
+ }
+
+ /* Comparing against 0 is easy. */
+
+ if (val8 == 0)
+ {
+ avr_asm_len (i == 0
+ ? "cp %0,__zero_reg__"
+ : "cpc %0,__zero_reg__", xop, plen, 1);
+ continue;
+ }
+
+ /* Upper registers can compare and subtract-with-carry immediates.
+ Notice that compare instructions do the same as respective subtract
+ instruction; the only difference is that comparisons don't write
+ the result back to the target register. */
+
+ if (ld_reg_p)
+ {
+ if (i == 0)
+ {
+ avr_asm_len ("cpi %0,%1", xop, plen, 1);
+ continue;
+ }
+ else if (reg_unused_after (insn, xreg))
+ {
+ avr_asm_len ("sbci %0,%1", xop, plen, 1);
+ continue;
+ }
+ }
+
+ /* Must load the value into the scratch register. */
+
+ gcc_assert (REG_P (xop[2]));
+
+ if (clobber_val != (int) val8)
+ avr_asm_len ("ldi %2,%1", xop, plen, 1);
+ clobber_val = (int) val8;
+
+ avr_asm_len (i == 0
+ ? "cp %0,%2"
+ : "cpc %0,%2", xop, plen, 1);
+ }
+
+ return "";
+}
+
+
/* Output test instruction for HImode. */
-const char *
-out_tsthi (rtx insn, rtx op, int *l)
+const char*
+avr_out_tsthi (rtx insn, rtx *op, int *plen)
{
if (compare_sign_p (insn))
{
- if (l) *l = 1;
- return AS1 (tst,%B0);
+ avr_asm_len ("tst %B0", op, plen, -1);
}
- if (reg_unused_after (insn, op)
- && compare_eq_p (insn))
+ else if (reg_unused_after (insn, op[0])
+ && compare_eq_p (insn))
{
/* Faster than sbiw if we can clobber the operand. */
- if (l) *l = 1;
- return "or %A0,%B0";
+ avr_asm_len ("or %A0,%B0", op, plen, -1);
}
- if (test_hard_reg_class (ADDW_REGS, op))
+ else
{
- if (l) *l = 1;
- return AS2 (sbiw,%0,0);
+ avr_out_compare (insn, op, plen);
}
- if (l) *l = 2;
- return (AS2 (cp,%A0,__zero_reg__) CR_TAB
- AS2 (cpc,%B0,__zero_reg__));
+
+ return "";
}
/* Output test instruction for SImode. */
-const char *
-out_tstsi (rtx insn, rtx op, int *l)
+const char*
+avr_out_tstsi (rtx insn, rtx *op, int *plen)
{
if (compare_sign_p (insn))
{
- if (l) *l = 1;
- return AS1 (tst,%D0);
+ avr_asm_len ("tst %D0", op, plen, -1);
}
- if (test_hard_reg_class (ADDW_REGS, op))
+ else if (reg_unused_after (insn, op[0])
+ && compare_eq_p (insn))
{
- if (l) *l = 3;
- return (AS2 (sbiw,%A0,0) CR_TAB
- AS2 (cpc,%C0,__zero_reg__) CR_TAB
- AS2 (cpc,%D0,__zero_reg__));
- }
- if (l) *l = 4;
- return (AS2 (cp,%A0,__zero_reg__) CR_TAB
- AS2 (cpc,%B0,__zero_reg__) CR_TAB
- AS2 (cpc,%C0,__zero_reg__) CR_TAB
- AS2 (cpc,%D0,__zero_reg__));
+ /* Faster than sbiw if we can clobber the operand. */
+ avr_asm_len ("or %A0,%B0" CR_TAB
+ "or %A0,%C0" CR_TAB
+ "or %A0,%D0", op, plen, -3);
+ }
+ else
+ {
+ avr_out_compare (insn, op, plen);
+ }
+
+ return "";
}
@@ -4851,6 +4972,10 @@ adjust_insn_length (rtx insn, int len)
avr_out_bitop (insn, op, &len);
break;
+ case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break;
+ case ADJUST_LEN_TSTSI: avr_out_tstsi (insn, op, &len); break;
+ case ADJUST_LEN_COMPARE: avr_out_compare (insn, op, &len); break;
+
default:
gcc_unreachable();
}
@@ -4886,15 +5011,6 @@ adjust_insn_length (rtx insn, int len)
break;
}
}
- else if (op[0] == cc0_rtx && REG_P (op[1]))
- {
- switch (GET_MODE (op[1]))
- {
- case HImode: out_tsthi (insn, op[1], &len); break;
- case SImode: out_tstsi (insn, op[1], &len); break;
- default: break;
- }
- }
}
set = single_set (insn);
if (set)