Commit a7c0acd0 by Georg-Johann Lay Committed by Georg-Johann Lay

re PR target/50447 ([avr] Better support of AND, OR, XOR and PLUS with constant…

re PR target/50447 ([avr] Better support of AND, OR, XOR and PLUS with constant integers for 16- and 32-bit values)

	* config/avr/avr.md (adjust_len): Add alternatives "tsthi",
	"tstsi", "compare".
	(*cmpqi_sign_extend): Use s8_operand.
	(*cmphi, *cmpsi): Rewrite using avr_out_compare.
	* config/avr/avr-protos.h (compare_diff_p, compare_eq_p): Remove
	prototypes.
	(out_tsthi, out_tstsi): Remove prototypes.
	(avr_out_tsthi, avr_out_tstsi): New prototypes.
	* config/avr/avr.c (out_tsthi, out_tstsi): Remove functions.
	(avr_asm_len): Negative length now sets *plen to -length.
	(compare_sign_p): Return bool instead of int.
	(compare_diff_p, compare_eq_p): Ditto and make static.
	(avr_out_tsthi): New function.
	(avr_out_tstsi): New function.
	(avr_out_compare): New function.
	(adjust_insn_length): Handle ADJUST_LEN_TSTHI, ADJUST_LEN_TSTSI,
	ADJUST_LEN_COMPARE.
	PR target/50447

From-SVN: r179124
parent 59024515
2011-09-23 Georg-Johann Lay <avr@gjlay.de>
PR target/50447
* config/avr/avr.md (adjust_len): Add alternatives "tsthi",
"tstsi", "compare".
(*cmpqi_sign_extend): Use s8_operand.
(*cmphi, *cmpsi): Rewrite using avr_out_compare.
* config/avr/avr-protos.h (compare_diff_p, compare_eq_p): Remove
prototypes.
(out_tsthi, out_tstsi): Remove prototypes.
(avr_out_tsthi, avr_out_tstsi): New prototypes.
* config/avr/avr.c (out_tsthi, out_tstsi): Remove functions.
(avr_asm_len): Negative length now sets *plen to -length.
(compare_sign_p): Return bool instead of int.
(compare_diff_p, compare_eq_p): Ditto and make static.
(avr_out_tsthi): New function.
(avr_out_tstsi): New function.
(avr_out_compare): New function.
(adjust_insn_length): Handle ADJUST_LEN_TSTHI, ADJUST_LEN_TSTSI,
ADJUST_LEN_COMPARE.
2011-09-23 Georg-Johann Lay <avr@gjlay.de>
PR target/50447
* config/avr/avr.md: (adjust_len): Add alternative "out_plus".
(addsi3): Rewrite using QI scratch register. Adjust text
peepholes using plus:SI.
......@@ -47,7 +47,6 @@ extern void init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
#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 insn, rtx op[], int *l);
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);
......@@ -103,7 +103,6 @@ extern void final_prescan_insn (rtx insn, rtx *operand, int num_operands);
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, enum machine_mode mode)
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* operands, int* plen, int n_words)
}
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__));
/* 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);
}
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__));
return "";
}
......@@ -5016,6 +5137,10 @@ adjust_insn_length (rtx insn, int len)
avr_out_plus (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();
}
......@@ -5051,15 +5176,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)
......
......@@ -136,7 +136,7 @@
;; Otherwise do special processing depending on the attribute.
(define_attr "adjust_len"
"yes,no,reload_in32,out_bitop,out_plus"
"yes,no,reload_in32,out_bitop,out_plus,tsthi,tstsi,compare"
(const_string "yes"))
;; Define mode iterators
......@@ -3344,125 +3344,62 @@
(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")])
;; ----------------------------------------------------------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment