This is the compare part to fix PR50447. Just like the preceding changes to fix PR50447 it's a micro-optimization to smarter print-out of instructions.
The patch covers comparisons against HI and SI integers. The byte-wide comparison allows reusing the value in the scratch register and individual treatment of zero. Moreover, ADIW can be used in some rare situations. And there is a bit of code clean-up. Tested without regressions. Ok to commit? Johann 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.
Index: config/avr/avr.md =================================================================== --- config/avr/avr.md (revision 179116) +++ config/avr/avr.md (working copy) @@ -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")]) ;; ---------------------------------------------------------------------- Index: config/avr/avr-protos.h =================================================================== --- config/avr/avr-protos.h (revision 179115) +++ config/avr/avr-protos.h (working copy) @@ -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); Index: config/avr/avr.c =================================================================== --- config/avr/avr.c (revision 179115) +++ config/avr/avr.c (working copy) @@ -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)