This patch improves IOR, XOR, AND for HI and SI. There is room for improvement in the current implementation:
- better analysis if a scratch is actually needed - add clobber operands to some insns - use 8-bit scratch instead of reload the constant - reuse value in scratch register if known - remove some hidden and thus dead insns - clean up code Passed without regression. Ok to install? Johann PR target/50447 PR target/50465 * config/avr/avr-protos.h (avr_out_bitop): New prototype. (avr_popcount_each_byte): New prototype. * config/avr/avr.c (avr_popcount): New static function. (avr_popcount_each_byte): New function. (avr_out_bitop): New function. (adjust_insn_length): ADJUST_LEN_OUT_BITOP dispatches to avr_out_bitop. Cleanup code. * config/avr/constraints.md (Ca2, Co2, Cx2): New constraints. (Ca4, Co4, Cx4): New constraints. * config/avr/avr.md (adjust_len): Add "out_bitop" insn attribute alternative. (andhi3, iorhi3, xorhi3): Rewrite insns using avr_out_bitop. (andsi3, iorsi3, xorsi3): Ditto. (*iorhi3_clobber, *iorsi3_clobber): Remove insns.
Index: config/avr/avr.md =================================================================== --- config/avr/avr.md (revision 179039) +++ 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" + "yes,no,reload_in32,out_bitop" (const_string "yes")) ;; Define mode iterators @@ -2238,71 +2238,41 @@ (define_insn "andqi3" (set_attr "cc" "set_zn,set_zn")]) (define_insn "andhi3" - [(set (match_operand:HI 0 "register_operand" "=r,d,r") - (and:HI (match_operand:HI 1 "register_operand" "%0,0,0") - (match_operand:HI 2 "nonmemory_operand" "r,i,M"))) - (clobber (match_scratch:QI 3 "=X,X,&d"))] - "" -{ - if (which_alternative==0) - return ("and %A0,%A2" CR_TAB - "and %B0,%B2"); - else if (which_alternative==1) - { - if (GET_CODE (operands[2]) == CONST_INT) - { - int mask = INTVAL (operands[2]); - if ((mask & 0xff) != 0xff) - output_asm_insn (AS2 (andi,%A0,lo8(%2)), operands); - if ((mask & 0xff00) != 0xff00) - output_asm_insn (AS2 (andi,%B0,hi8(%2)), operands); - return ""; - } - return (AS2 (andi,%A0,lo8(%2)) CR_TAB - AS2 (andi,%B0,hi8(%2))); - } - return (AS2 (ldi,%3,lo8(%2)) CR_TAB - "and %A0,%3" CR_TAB - AS1 (clr,%B0)); -} - [(set_attr "length" "2,2,3") - (set_attr "cc" "set_n,clobber,set_n")]) + [(set (match_operand:HI 0 "register_operand" "=r,d,d,r ,r") + (and:HI (match_operand:HI 1 "register_operand" "%0,0,0,0 ,0") + (match_operand:HI 2 "nonmemory_operand" "r,s,n,Ca2,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X ,&d"))] + "" + { + if (which_alternative == 0) + return "and %A0,%A2\;and %B0,%B2"; + else if (which_alternative == 1) + return "andi %A0,lo8(%2)\;andi %B0,hi8(%2)"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "2,2,2,4,4") + (set_attr "adjust_len" "no,no,out_bitop,out_bitop,out_bitop") + (set_attr "cc" "set_n,set_n,clobber,clobber,clobber")]) (define_insn "andsi3" - [(set (match_operand:SI 0 "register_operand" "=r,d") - (and:SI (match_operand:SI 1 "register_operand" "%0,0") - (match_operand:SI 2 "nonmemory_operand" "r,i")))] + [(set (match_operand:SI 0 "register_operand" "=r,d,r ,r") + (and:SI (match_operand:SI 1 "register_operand" "%0,0,0 ,0") + (match_operand:SI 2 "nonmemory_operand" "r,n,Ca4,n"))) + (clobber (match_scratch:QI 3 "=X,X,X ,&d"))] "" -{ - if (which_alternative==0) - return ("and %0,%2" CR_TAB - "and %B0,%B2" CR_TAB - "and %C0,%C2" CR_TAB - "and %D0,%D2"); - else if (which_alternative==1) - { - if (GET_CODE (operands[2]) == CONST_INT) - { - HOST_WIDE_INT mask = INTVAL (operands[2]); - if ((mask & 0xff) != 0xff) - output_asm_insn (AS2 (andi,%A0,lo8(%2)), operands); - if ((mask & 0xff00) != 0xff00) - output_asm_insn (AS2 (andi,%B0,hi8(%2)), operands); - if ((mask & 0xff0000L) != 0xff0000L) - output_asm_insn (AS2 (andi,%C0,hlo8(%2)), operands); - if ((mask & 0xff000000L) != 0xff000000L) - output_asm_insn (AS2 (andi,%D0,hhi8(%2)), operands); - return ""; - } - return (AS2 (andi, %A0,lo8(%2)) CR_TAB - AS2 (andi, %B0,hi8(%2)) CR_TAB - AS2 (andi, %C0,hlo8(%2)) CR_TAB - AS2 (andi, %D0,hhi8(%2))); - } - return "bug"; -} - [(set_attr "length" "4,4") - (set_attr "cc" "set_n,clobber")]) + { + if (which_alternative == 0) + return "and %0,%2" CR_TAB + "and %B0,%B2" CR_TAB + "and %C0,%C2" CR_TAB + "and %D0,%D2"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "4,4,8,8") + (set_attr "adjust_len" "no,out_bitop,out_bitop,out_bitop") + (set_attr "cc" "set_n,clobber,clobber,clobber")]) (define_peephole2 ; andi [(set (match_operand:QI 0 "d_register_operand" "") @@ -2332,84 +2302,41 @@ (define_insn "iorqi3" (set_attr "cc" "set_zn,set_zn")]) (define_insn "iorhi3" - [(set (match_operand:HI 0 "register_operand" "=r,d") - (ior:HI (match_operand:HI 1 "register_operand" "%0,0") - (match_operand:HI 2 "nonmemory_operand" "r,i")))] - "" -{ - if (which_alternative==0) - return ("or %A0,%A2" CR_TAB - "or %B0,%B2"); - if (GET_CODE (operands[2]) == CONST_INT) - { - int mask = INTVAL (operands[2]); - if (mask & 0xff) - output_asm_insn (AS2 (ori,%A0,lo8(%2)), operands); - if (mask & 0xff00) - output_asm_insn (AS2 (ori,%B0,hi8(%2)), operands); - return ""; - } - return (AS2 (ori,%0,lo8(%2)) CR_TAB - AS2 (ori,%B0,hi8(%2))); -} - [(set_attr "length" "2,2") - (set_attr "cc" "set_n,clobber")]) - -(define_insn "*iorhi3_clobber" - [(set (match_operand:HI 0 "register_operand" "=r,r") - (ior:HI (match_operand:HI 1 "register_operand" "%0,0") - (match_operand:HI 2 "immediate_operand" "M,i"))) - (clobber (match_scratch:QI 3 "=&d,&d"))] - "" - "@ - ldi %3,lo8(%2)\;or %A0,%3 - ldi %3,lo8(%2)\;or %A0,%3\;ldi %3,hi8(%2)\;or %B0,%3" - [(set_attr "length" "2,4") - (set_attr "cc" "clobber,set_n")]) + [(set (match_operand:HI 0 "register_operand" "=r,d,d,r ,r") + (ior:HI (match_operand:HI 1 "register_operand" "%0,0,0,0 ,0") + (match_operand:HI 2 "nonmemory_operand" "r,s,n,Co2,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X ,&d"))] + "" + { + if (which_alternative == 0) + return "or %A0,%A2\;or %B0,%B2"; + else if (which_alternative == 1) + return "ori %A0,lo8(%2)\;ori %B0,hi8(%2)"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "2,2,2,4,4") + (set_attr "adjust_len" "no,no,out_bitop,out_bitop,out_bitop") + (set_attr "cc" "set_n,set_n,clobber,clobber,clobber")]) (define_insn "iorsi3" - [(set (match_operand:SI 0 "register_operand" "=r,d") - (ior:SI (match_operand:SI 1 "register_operand" "%0,0") - (match_operand:SI 2 "nonmemory_operand" "r,i")))] + [(set (match_operand:SI 0 "register_operand" "=r,d,r ,r") + (ior:SI (match_operand:SI 1 "register_operand" "%0,0,0 ,0") + (match_operand:SI 2 "nonmemory_operand" "r,n,Co4,n"))) + (clobber (match_scratch:QI 3 "=X,X,X ,&d"))] "" -{ - if (which_alternative==0) - return ("or %0,%2" CR_TAB - "or %B0,%B2" CR_TAB - "or %C0,%C2" CR_TAB - "or %D0,%D2"); - if (GET_CODE (operands[2]) == CONST_INT) - { - HOST_WIDE_INT mask = INTVAL (operands[2]); - if (mask & 0xff) - output_asm_insn (AS2 (ori,%A0,lo8(%2)), operands); - if (mask & 0xff00) - output_asm_insn (AS2 (ori,%B0,hi8(%2)), operands); - if (mask & 0xff0000L) - output_asm_insn (AS2 (ori,%C0,hlo8(%2)), operands); - if (mask & 0xff000000L) - output_asm_insn (AS2 (ori,%D0,hhi8(%2)), operands); - return ""; - } - return (AS2 (ori, %A0,lo8(%2)) CR_TAB - AS2 (ori, %B0,hi8(%2)) CR_TAB - AS2 (ori, %C0,hlo8(%2)) CR_TAB - AS2 (ori, %D0,hhi8(%2))); -} - [(set_attr "length" "4,4") - (set_attr "cc" "set_n,clobber")]) + { + if (which_alternative == 0) + return "or %0,%2" CR_TAB + "or %B0,%B2" CR_TAB + "or %C0,%C2" CR_TAB + "or %D0,%D2"; -(define_insn "*iorsi3_clobber" - [(set (match_operand:SI 0 "register_operand" "=r,r") - (ior:SI (match_operand:SI 1 "register_operand" "%0,0") - (match_operand:SI 2 "immediate_operand" "M,i"))) - (clobber (match_scratch:QI 3 "=&d,&d"))] - "" - "@ - ldi %3,lo8(%2)\;or %A0,%3 - ldi %3,lo8(%2)\;or %A0,%3\;ldi %3,hi8(%2)\;or %B0,%3\;ldi %3,hlo8(%2)\;or %C0,%3\;ldi %3,hhi8(%2)\;or %D0,%3" - [(set_attr "length" "2,8") - (set_attr "cc" "clobber,set_n")]) + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "4,4,8,8") + (set_attr "adjust_len" "no,out_bitop,out_bitop,out_bitop") + (set_attr "cc" "set_n,clobber,clobber,clobber")]) ;;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ;; xor @@ -2424,26 +2351,39 @@ (define_insn "xorqi3" (set_attr "cc" "set_zn")]) (define_insn "xorhi3" - [(set (match_operand:HI 0 "register_operand" "=r") - (xor:HI (match_operand:HI 1 "register_operand" "%0") - (match_operand:HI 2 "register_operand" "r")))] + [(set (match_operand:HI 0 "register_operand" "=r,r ,r") + (xor:HI (match_operand:HI 1 "register_operand" "%0,0 ,0") + (match_operand:HI 2 "nonmemory_operand" "r,Cx2,n"))) + (clobber (match_scratch:QI 3 "=X,X ,&d"))] "" - "eor %0,%2 - eor %B0,%B2" - [(set_attr "length" "2") - (set_attr "cc" "set_n")]) + { + if (which_alternative == 0) + return "eor %A0,%A2\;eor %B0,%B2"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "2,2,4") + (set_attr "adjust_len" "no,out_bitop,out_bitop") + (set_attr "cc" "set_n,clobber,clobber")]) (define_insn "xorsi3" - [(set (match_operand:SI 0 "register_operand" "=r") - (xor:SI (match_operand:SI 1 "register_operand" "%0") - (match_operand:SI 2 "register_operand" "r")))] + [(set (match_operand:SI 0 "register_operand" "=r,r ,r") + (xor:SI (match_operand:SI 1 "register_operand" "%0,0 ,0") + (match_operand:SI 2 "nonmemory_operand" "r,Cx4,n"))) + (clobber (match_scratch:QI 3 "=X,X ,&d"))] "" - "eor %0,%2 - eor %B0,%B2 - eor %C0,%C2 - eor %D0,%D2" - [(set_attr "length" "4") - (set_attr "cc" "set_n")]) + { + if (which_alternative == 0) + return "eor %0,%2" CR_TAB + "eor %B0,%B2" CR_TAB + "eor %C0,%C2" CR_TAB + "eor %D0,%D2"; + + return avr_out_bitop (insn, operands, NULL); + } + [(set_attr "length" "4,8,8") + (set_attr "adjust_len" "no,out_bitop,out_bitop") + (set_attr "cc" "set_n,clobber,clobber")]) ;; swap swap swap swap swap swap swap swap swap swap swap swap swap swap swap ;; swap Index: config/avr/avr-protos.h =================================================================== --- config/avr/avr-protos.h (revision 179039) +++ config/avr/avr-protos.h (working copy) @@ -81,6 +81,8 @@ extern int avr_epilogue_uses (int regno) extern void avr_output_bld (rtx operands[], int bit_nr); extern void avr_output_addr_vec_elt (FILE *stream, int value); extern const char *avr_out_sbxx_branch (rtx insn, rtx operands[]); +extern const char* avr_out_bitop (rtx, rtx*, int*); +extern bool avr_popcount_each_byte (rtx, int, int); extern int extra_constraint_Q (rtx x); extern int adjust_insn_length (rtx insn, int len); Index: config/avr/constraints.md =================================================================== --- config/avr/constraints.md (revision 179039) +++ config/avr/constraints.md (working copy) @@ -112,3 +112,33 @@ (define_constraint "C04" "Constant integer 4." (and (match_code "const_int") (match_test "ival == 4"))) + +(define_constraint "Ca2" + "Constant 2-byte integer that allows AND without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 2, (1<<0) | (1<<7) | (1<<8))"))) + +(define_constraint "Ca4" + "Constant 4-byte integer that allows AND without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 4, (1<<0) | (1<<7) | (1<<8))"))) + +(define_constraint "Co2" + "Constant 2-byte integer that allows OR without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 2, (1<<0) | (1<<1) | (1<<8))"))) + +(define_constraint "Co4" + "Constant 4-byte integer that allows OR without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 4, (1<<0) | (1<<1) | (1<<8))"))) + +(define_constraint "Cx2" + "Constant 2-byte integer that allows XOR without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 2, (1<<0) | (1<<8))"))) + +(define_constraint "Cx4" + "Constant 4-byte integer that allows XOR without clobber register." + (and (match_code "const_int") + (match_test "avr_popcount_each_byte (op, 4, (1<<0) | (1<<8))"))) Index: config/avr/avr.c =================================================================== --- config/avr/avr.c (revision 179040) +++ config/avr/avr.c (working copy) @@ -303,6 +303,46 @@ avr_replace_prefix (const char *old_str, return (const char*) new_str; } + +/* Custom function to count number of set bits. */ + +static inline int +avr_popcount (unsigned int val) +{ + int pop = 0; + + while (val) + { + val &= val-1; + pop++; + } + + return pop; +} + + +/* Constraint helper function. XVAL is an CONST_INT. Return true if the least + significant N_BYTES bytes of XVAL all have a popcount in POP_MASK and false, + otherwise. POP_MASK represents a subset of integers which contains an + integer N iff bit N of POP_MASK is set. */ + +bool +avr_popcount_each_byte (rtx xval, int n_bytes, int pop_mask) +{ + int i; + + for (i = 0; i < n_bytes; i++) + { + rtx xval8 = simplify_gen_subreg (QImode, xval, SImode, i); + unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode); + + if (0 == (pop_mask & (1 << avr_popcount (val8)))) + return false; + } + + return true; +} + static void avr_option_override (void) { @@ -4462,6 +4502,157 @@ lshrsi3_out (rtx insn, rtx operands[], i return ""; } + +/* Output bit operation (IOR, AND, XOR) with register XOP[0] and compile + time constant XOP[2]: + + XOP[0] = XOP[0] <op> XOP[2] + + and return "". If PLEN == NULL, print assembler instructions to perform the + operation; otherwise, set *PLEN to the length of the instruction sequence + (in words) printed with PLEN == NULL. XOP[3] is either an 8-bit clobber + register or SCRATCH if no clobber register is needed for the operation. */ + +const char* +avr_out_bitop (rtx insn, rtx *xop, int *plen) +{ + /* CODE and MODE of the operation. */ + enum rtx_code code = GET_CODE (SET_SRC (single_set (insn))); + enum machine_mode mode = GET_MODE (xop[0]); + + /* Number of bytes to operate on. */ + int i, n_bytes = GET_MODE_SIZE (mode); + + /* Value of T-flag (0 or 1) or -1 if unknow. */ + int set_t = -1; + + /* Value (0..0xff) held in clobber register op[3] or -1 if unknown. */ + int clobber_val = -1; + + /* op[0]: 8-bit destination register + op[1]: 8-bit const int + op[2]: 8-bit clobber register or SCRATCH + op[3]: 8-bit register containing 0xff or NULL_RTX */ + rtx op[4]; + + op[2] = xop[3]; + op[3] = NULL_RTX; + + if (plen) + *plen = 0; + + for (i = 0; i < n_bytes; i++) + { + /* We operate byte-wise on the destination. */ + rtx reg8 = simplify_gen_subreg (QImode, xop[0], mode, i); + rtx xval8 = simplify_gen_subreg (QImode, xop[2], mode, i); + + /* 8-bit value to operate with this byte. */ + unsigned int val8 = UINTVAL (xval8) & GET_MODE_MASK (QImode); + + /* Number of bits set in the current byte of the constant. */ + int pop8 = avr_popcount (val8); + + /* Registers R16..R31 can operate with immediate. */ + bool ld_reg_p = test_hard_reg_class (LD_REGS, reg8); + + op[0] = reg8; + op[1] = GEN_INT (val8); + + switch (code) + { + case IOR: + + if (0 == pop8) + continue; + else if (ld_reg_p) + avr_asm_len ("ori %0,%1", op, plen, 1); + else if (1 == pop8) + { + if (set_t != 1) + avr_asm_len ("set", op, plen, 1); + set_t = 1; + + op[1] = GEN_INT (exact_log2 (val8)); + avr_asm_len ("bld %0,%1", op, plen, 1); + } + else if (8 == pop8) + { + if (op[3] != NULL_RTX) + avr_asm_len ("mov %0,%3", op, plen, 1); + else + avr_asm_len ("clr %0" CR_TAB + "dec %0", op, plen, 2); + + op[3] = op[0]; + } + else + { + if (clobber_val != (int) val8) + avr_asm_len ("ldi %2,%1", op, plen, 1); + clobber_val = (int) val8; + + avr_asm_len ("or %0,%2", op, plen, 1); + } + + continue; /* IOR */ + + case AND: + + if (8 == pop8) + continue; + else if (0 == pop8) + avr_asm_len ("clr %0", op, plen, 1); + else if (ld_reg_p) + avr_asm_len ("andi %0,%1", op, plen, 1); + else if (7 == pop8) + { + if (set_t != 0) + avr_asm_len ("clt", op, plen, 1); + set_t = 0; + + op[1] = GEN_INT (exact_log2 (GET_MODE_MASK (QImode) & ~val8)); + avr_asm_len ("bld %0,%1", op, plen, 1); + } + else + { + if (clobber_val != (int) val8) + avr_asm_len ("ldi %2,%1", op, plen, 1); + clobber_val = (int) val8; + + avr_asm_len ("and %0,%2", op, plen, 1); + } + + continue; /* AND */ + + case XOR: + + if (0 == pop8) + continue; + else if (8 == pop8) + avr_asm_len ("com %0", op, plen, 1); + else if (ld_reg_p && val8 == (1 << 7)) + avr_asm_len ("subi %0,%1", op, plen, 1); + else + { + if (clobber_val != (int) val8) + avr_asm_len ("ldi %2,%1", op, plen, 1); + clobber_val = (int) val8; + + avr_asm_len ("eor %0,%2", op, plen, 1); + } + + continue; /* XOR */ + + default: + /* Unknown rtx_code */ + gcc_unreachable(); + } + } /* for all sub-bytes */ + + return ""; +} + /* Create RTL split patterns for byte sized rotate expressions. This produces a series of move instructions and considers overlap situations. Overlapping non-HImode operands need a scratch register. */ @@ -4656,6 +4847,10 @@ adjust_insn_length (rtx insn, int len) output_reload_insisf (insn, op, op[2], &len); break; + case ADJUST_LEN_OUT_BITOP: + avr_out_bitop (insn, op, &len); + break; + default: gcc_unreachable(); } @@ -4700,36 +4895,6 @@ adjust_insn_length (rtx insn, int len) default: break; } } - else if (GET_CODE (op[1]) == AND) - { - if (GET_CODE (XEXP (op[1],1)) == CONST_INT) - { - HOST_WIDE_INT mask = INTVAL (XEXP (op[1],1)); - if (GET_MODE (op[1]) == SImode) - len = (((mask & 0xff) != 0xff) - + ((mask & 0xff00) != 0xff00) - + ((mask & 0xff0000L) != 0xff0000L) - + ((mask & 0xff000000L) != 0xff000000L)); - else if (GET_MODE (op[1]) == HImode) - len = (((mask & 0xff) != 0xff) - + ((mask & 0xff00) != 0xff00)); - } - } - else if (GET_CODE (op[1]) == IOR) - { - if (GET_CODE (XEXP (op[1],1)) == CONST_INT) - { - HOST_WIDE_INT mask = INTVAL (XEXP (op[1],1)); - if (GET_MODE (op[1]) == SImode) - len = (((mask & 0xff) != 0) - + ((mask & 0xff00) != 0) - + ((mask & 0xff0000L) != 0) - + ((mask & 0xff000000L) != 0)); - else if (GET_MODE (op[1]) == HImode) - len = (((mask & 0xff) != 0) - + ((mask & 0xff00) != 0)); - } - } } set = single_set (insn); if (set)