This is fix for a minor performance regression introduced by my changes in trunk r175956: To load a 32-bit constant like 1 into R2, 4.6 uses
CLR R2 CLR R3 MOVW R4,R2 INC R2 whereas trunk prints the longer CLR R2 INC R2 CLR R3 CLR R4 CLR R5 This patch fixes it. The insns affected (*reload_insi and *reload_insf) whose instruction length must be adjusted now use an insn attribute to express how the adjustment has to be performed. Tested without regressions. Ok to commit? Johann PR target/50449 * config/avr/avr.md (adjust_len): New insn attribute. (*reload_insi, *reload_insf): Use it. (*movsi, *movsf): Use new interface of output_movsisf. * config/avr/avr-protos.h (output_movsisf): Change prototype. * config/avr/avr.c (output_movsisf): Ditto. (adjust_insn_length): Use insn attribute "adjust_len" to adjust lengths of insns *reload_insi, *reload_insf. (output_reload_insisf_1): New static function. (output_reload_insisf): Use it.
Index: config/avr/avr.md =================================================================== --- config/avr/avr.md (revision 178999) +++ config/avr/avr.md (working copy) @@ -128,6 +128,14 @@ (define_attr "length" "" (const_int 2))] (const_int 2))) +;; Some complex insns don't need length adjustment and thererfore +;; the length of these insns need not/must not be altered. +;; It is easier to state this in an insn attribute +;; than to clutter up avr.c:adjust_insn_length(). +(define_attr "adjust_len" + "yes,no,reload_in32" + (const_string "yes")) + ;; Define mode iterators (define_mode_iterator QIHI [(QI "") (HI "")]) (define_mode_iterator QIHI2 [(QI "") (HI "")]) @@ -456,7 +464,7 @@ (define_insn "*reload_insi" { return output_reload_insisf (insn, operands, operands[2], NULL); } - [(set_attr "length" "8") + [(set_attr "adjust_len" "reload_in32") (set_attr "cc" "clobber")]) @@ -466,7 +474,7 @@ (define_insn "*movsi" "(register_operand (operands[0],SImode) || register_operand (operands[1],SImode) || const0_rtx == operands[1])" { - return output_movsisf (insn, operands, NULL_RTX, NULL); + return output_movsisf (insn, operands, NULL); } [(set_attr "length" "4,4,8,9,4,10") (set_attr "cc" "none,set_zn,clobber,clobber,clobber,clobber")]) @@ -495,7 +503,7 @@ (define_insn "*movsf" || register_operand (operands[1], SFmode) || operands[1] == CONST0_RTX (SFmode)" { - return output_movsisf (insn, operands, NULL_RTX, NULL); + return output_movsisf (insn, operands, NULL); } [(set_attr "length" "4,4,8,9,4,10") (set_attr "cc" "none,set_zn,clobber,clobber,clobber,clobber")]) @@ -520,7 +528,7 @@ (define_insn "*reload_insf" { return output_reload_insisf (insn, operands, operands[2], NULL); } - [(set_attr "length" "8") + [(set_attr "adjust_len" "reload_in32") (set_attr "cc" "clobber")]) ;;========================================================================= Index: config/avr/avr-protos.h =================================================================== --- config/avr/avr-protos.h (revision 178999) +++ config/avr/avr-protos.h (working copy) @@ -56,7 +56,7 @@ extern const char *out_movhi_r_mr (rtx i 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[], rtx clobber, 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 *ret_cond_branch (rtx x, int len, int reverse); Index: config/avr/avr.c =================================================================== --- config/avr/avr.c (revision 178999) +++ config/avr/avr.c (working copy) @@ -2673,7 +2673,7 @@ out_movsi_mr_r (rtx insn, rtx op[], int } const char * -output_movsisf (rtx insn, rtx operands[], rtx clobber_reg, int *l) +output_movsisf (rtx insn, rtx operands[], int *l) { int dummy; rtx dest = operands[0]; @@ -2719,7 +2719,7 @@ output_movsisf (rtx insn, rtx operands[] else if (CONST_INT_P (src) || CONST_DOUBLE_P (src)) { - return output_reload_insisf (insn, operands, clobber_reg, real_l); + return output_reload_insisf (insn, operands, NULL_RTX, real_l); } else if (CONSTANT_P (src)) { @@ -4616,8 +4616,56 @@ avr_rotate_bytes (rtx operands[]) int adjust_insn_length (rtx insn, int len) { - rtx patt = PATTERN (insn); - rtx set; + rtx patt, set; + enum attr_adjust_len adjust_len; + + /* Some complex insns don't need length adjustment and therefore + the length need not/must not be adjusted for these insns. + It is easier to state this in an insn attribute "adjust_len" than + to clutter up code here... */ + + if (-1 == recog_memoized (insn)) + { + return len; + } + + /* Read from insn attribute "adjust_len" if/how length is to be adjusted. */ + + adjust_len = get_attr_adjust_len (insn); + + if (adjust_len != ADJUST_LEN_YES) + { + rtx *op = recog_data.operand; + + if (adjust_len == ADJUST_LEN_NO) + { + /* Nothing to adjust: The length from attribute "length" is fine. */ + + return len; + } + + /* Extract insn's operands. */ + + extract_insn_cached (insn); + + /* Dispatch to right function. */ + + switch (adjust_len) + { + case ADJUST_LEN_RELOAD_IN32: + output_reload_insisf (insn, op, op[2], &len); + break; + + default: + gcc_unreachable(); + } + + return len; + } /* adjust_length != ADJUST_LEN_YES */ + + /* adjust_len == "yes": Analyse insn by hand. */ + + patt = PATTERN (insn); if (GET_CODE (patt) == SET) { @@ -4637,7 +4685,7 @@ adjust_insn_length (rtx insn, int len) break; case SImode: case SFmode: - output_movsisf (insn, op, NULL_RTX, &len); + output_movsisf (insn, op, &len); break; default: break; @@ -4708,7 +4756,8 @@ adjust_insn_length (rtx insn, int len) break; case SImode: case SFmode: - output_reload_insisf (insn, op, XEXP (op[2], 0), &len); + /* Handled by ADJUST_LEN_RELOAD_INSISF above. */ + gcc_unreachable(); break; default: break; @@ -6698,21 +6747,17 @@ output_reload_inhi (rtx insn ATTRIBUTE_U } -/* Reload a SI or SF compile time constant (OP[1]) into a GPR (OP[0]). - CLOBBER_REG is a QI clobber reg needed to move vast majority of consts - into a NO_LD_REGS. If CLOBBER_REG is NULL_RTX we either don't need a - clobber reg or have to cook one up. - - LEN == NULL: Output instructions. - - LEN != NULL: Output nothing. Increment *LEN by number of words occupied - by the insns printed. - - Return "". */ +/* A helper for `output_reload_insisf'. */ +/* Set 32-bit register OP[0] to compile-time constant OP[1]. + CLOBBER_REG is a QI clobber register or NULL_RTX. + LEN == NULL: output instructions. + LEN != NULL: set *LEN to the length of the instruction sequence + (in words) printed with LEN = NULL. + If CLEAR_P is true, OP[0] had been cleard to Zero already. + If CLEAR_P is false, nothing is known about OP[0]. */ -const char * -output_reload_insisf (rtx insn ATTRIBUTE_UNUSED, - rtx *op, rtx clobber_reg, int *len) +static void +output_reload_insisf_1 (rtx *op, rtx clobber_reg, int *len, bool clear_p) { rtx src = op[1]; rtx dest = op[0]; @@ -6787,7 +6832,12 @@ output_reload_insisf (rtx insn ATTRIBUTE if (INTVAL (lo16) == INTVAL (hi16)) { - avr_asm_len ("movw %C0,%A0", &op[0], len, 1); + if (0 != INTVAL (lo16) + || !clear_p) + { + avr_asm_len ("movw %C0,%A0", &op[0], len, 1); + } + break; } } @@ -6797,7 +6847,9 @@ output_reload_insisf (rtx insn ATTRIBUTE if (ival[n] == 0) { - avr_asm_len ("clr %0", &xdest[n], len, 1); + if (!clear_p) + avr_asm_len ("clr %0", &xdest[n], len, 1); + continue; } @@ -6837,8 +6889,18 @@ output_reload_insisf (rtx insn ATTRIBUTE if (-1 == ival[n]) { - avr_asm_len ("clr %0" CR_TAB - "dec %0", &xdest[n], len, 2); + if (!clear_p) + avr_asm_len ("clr %0", &xdest[n], len, 1); + + avr_asm_len ("dec %0", &xdest[n], len, 1); + continue; + } + else if (1 == ival[n]) + { + if (!clear_p) + avr_asm_len ("clr %0", &xdest[n], len, 1); + + avr_asm_len ("inc %0", &xdest[n], len, 1); continue; } @@ -6848,13 +6910,6 @@ output_reload_insisf (rtx insn ATTRIBUTE if (NULL_RTX == clobber_reg && single_one_operand (xval, QImode)) { - if (1 == ival[n]) - { - avr_asm_len ("clr %0" CR_TAB - "inc %0", &xdest[n], len, 2); - continue; - } - xop[0] = xdest[n]; xop[1] = GEN_INT (exact_log2 (ival[n] & GET_MODE_MASK (QImode))); @@ -6866,8 +6921,10 @@ output_reload_insisf (rtx insn ATTRIBUTE avr_asm_len ("set", xop, len, 1); } - avr_asm_len ("clr %0" CR_TAB - "bld %0,%1", xop, len, 2); + if (!clear_p) + avr_asm_len ("clr %0", xop, len, 1); + + avr_asm_len ("bld %0,%1", xop, len, 1); continue; } @@ -6890,7 +6947,68 @@ output_reload_insisf (rtx insn ATTRIBUTE { avr_asm_len ("mov %0,__tmp_reg__", &clobber_reg, len, 1); } - +} + + +/* Reload a SI or SF compile time constant OP[1] into the register OP[0]. + CLOBBER_REG is a QI clobber reg needed to move vast majority of consts + into a NO_LD_REGS register. If CLOBBER_REG is NULL_RTX we either don't + need a clobber reg or have to cook one up. + + LEN == NULL: Output instructions. + + LEN != NULL: Output nothing. Increment *LEN by number of words occupied + by the insns printed. + + Return "". */ + +const char * +output_reload_insisf (rtx insn ATTRIBUTE_UNUSED, + rtx *op, rtx clobber_reg, int *len) +{ + gcc_assert (REG_P (op[0]) + && CONSTANT_P (op[1])); + + if (AVR_HAVE_MOVW + && !test_hard_reg_class (LD_REGS, op[0])) + { + int len_clr, len_noclr; + + /* In some cases it is better to clear the destination beforehand, e.g. + + CLR R2 CLR R3 MOVW R4,R2 INC R2 + + is shorther than + + CLR R2 INC R2 CLR R3 CLR R4 CLR R5 + + We find it too tedious to work that out in the print function. + Instead, we call the print function twice to get the lengths of + both methods and use the shortest one. */ + + output_reload_insisf_1 (op, clobber_reg, &len_clr, true); + output_reload_insisf_1 (op, clobber_reg, &len_noclr, false); + + if (len_noclr - len_clr == 4) + { + /* Default needs 4 CLR instructions: clear register beforehand. */ + + avr_asm_len ("clr %A0" CR_TAB + "clr %B0" CR_TAB + "movw %C0,%A0", &op[0], len, 3); + + output_reload_insisf_1 (op, clobber_reg, len, true); + + if (len) + *len += 3; + + return ""; + } + } + + /* Default: destination not pre-cleared. */ + + output_reload_insisf_1 (op, clobber_reg, len, false); return ""; }