diff options
author | Ian Lance Taylor <iant@golang.org> | 2023-06-21 11:04:04 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2023-06-21 11:04:04 -0700 |
commit | 97e31a0a2a2d2273687fcdb4e5416aab1a2186e1 (patch) | |
tree | d5c1cae4de436a0fe54a5f0a2a197d309f3d654c /gcc/config/avr | |
parent | 6612f4f8cb9b0d5af18ec69ad04e56debc3e6ced (diff) | |
parent | 577223aebc7acdd31e62b33c1682fe54a622ae27 (diff) | |
download | gcc-97e31a0a2a2d2273687fcdb4e5416aab1a2186e1.zip gcc-97e31a0a2a2d2273687fcdb4e5416aab1a2186e1.tar.gz gcc-97e31a0a2a2d2273687fcdb4e5416aab1a2186e1.tar.bz2 |
Merge from trunk revision 577223aebc7acdd31e62b33c1682fe54a622ae27.
Diffstat (limited to 'gcc/config/avr')
-rw-r--r-- | gcc/config/avr/avr-dimode.md | 22 | ||||
-rw-r--r-- | gcc/config/avr/avr-passes.def | 20 | ||||
-rw-r--r-- | gcc/config/avr/avr-protos.h | 10 | ||||
-rw-r--r-- | gcc/config/avr/avr.cc | 1314 | ||||
-rw-r--r-- | gcc/config/avr/avr.md | 1739 | ||||
-rw-r--r-- | gcc/config/avr/avr.opt | 4 | ||||
-rw-r--r-- | gcc/config/avr/constraints.md | 25 | ||||
-rw-r--r-- | gcc/config/avr/gen-avr-mmcu-specs.cc | 2 | ||||
-rw-r--r-- | gcc/config/avr/predicates.md | 50 |
9 files changed, 2099 insertions, 1087 deletions
diff --git a/gcc/config/avr/avr-dimode.md b/gcc/config/avr/avr-dimode.md index c0bb04f..91f0d39 100644 --- a/gcc/config/avr/avr-dimode.md +++ b/gcc/config/avr/avr-dimode.md @@ -455,12 +455,18 @@ (define_expand "cbranch<mode>4" [(set (pc) (if_then_else (match_operator 0 "ordered_comparison_operator" - [(match_operand:ALL8 1 "register_operand" "") - (match_operand:ALL8 2 "nonmemory_operand" "")]) - (label_ref (match_operand 3 "" "")) - (pc)))] + [(match_operand:ALL8 1 "register_operand") + (match_operand:ALL8 2 "nonmemory_operand")]) + (label_ref (match_operand 3)) + (pc)))] "avr_have_dimode" { + int icode = (int) GET_CODE (operands[0]); + + targetm.canonicalize_comparison (&icode, &operands[1], &operands[2], false); + operands[0] = gen_rtx_fmt_ee ((enum rtx_code) icode, + VOIDmode, operands[1], operands[2]); + rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A); avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A)); @@ -490,8 +496,8 @@ (if_then_else (match_operator 0 "ordered_comparison_operator" [(reg:ALL8 ACC_A) (reg:ALL8 ACC_B)]) - (label_ref (match_operand 1 "" "")) - (pc)))] + (label_ref (match_operand 1)) + (pc)))] "avr_have_dimode" "#" "&& reload_completed" @@ -544,8 +550,8 @@ (if_then_else (match_operator 0 "ordered_comparison_operator" [(reg:ALL8 ACC_A) (match_operand:ALL8 1 "const_operand" "n Ynn")]) - (label_ref (match_operand 2 "" "")) - (pc))) + (label_ref (match_operand 2 "" "")) + (pc))) (clobber (match_scratch:QI 3 "=&d"))] "avr_have_dimode && !s8_operand (operands[1], VOIDmode)" diff --git a/gcc/config/avr/avr-passes.def b/gcc/config/avr/avr-passes.def index 02ca737..9ff9b23f 100644 --- a/gcc/config/avr/avr-passes.def +++ b/gcc/config/avr/avr-passes.def @@ -43,3 +43,23 @@ INSERT_PASS_BEFORE (pass_free_cfg, 1, avr_pass_recompute_notes); insns withaout any insns in between. */ INSERT_PASS_AFTER (pass_expand, 1, avr_pass_casesi); + +/* If-else decision trees generated for switch / case may produce sequences + like + + SREG = compare (reg, val); + if (SREG == 0) goto label1; + SREG = compare (reg, 1 + val); + if (SREG >= 0) goto label2; + + which can be optimized to + + SREG = compare (reg, val); + if (SREG == 0) goto label1; + if (SREG >= 0) goto label2; + + The optimal place for such a pass would be directly after expand, but + it's not possible for a jump insn to target more than one code label. + Hence, run a mini pass right before split2 which introduces REG_CC. */ + +INSERT_PASS_BEFORE (pass_split_after_reload, 1, avr_pass_ifelse); diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index ec96fd4..5c1343f 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -57,7 +57,11 @@ extern const char *avr_out_compare64 (rtx_insn *, rtx*, int*); extern const char *ret_cond_branch (rtx x, int len, int reverse); extern const char *avr_out_movpsi (rtx_insn *, rtx*, int*); extern const char *avr_out_sign_extend (rtx_insn *, rtx*, int*); -extern const char *avr_out_insert_notbit (rtx_insn *, rtx*, rtx, int*); +extern const char *avr_out_insert_notbit (rtx_insn *, rtx*, int*); +extern const char *avr_out_extr (rtx_insn *, rtx*, int*); +extern const char *avr_out_extr_not (rtx_insn *, rtx*, int*); +extern const char *avr_out_plus_set_ZN (rtx*, int*); +extern const char *avr_out_cmp_ext (rtx*, enum rtx_code, int*); extern const char *ashlqi3_out (rtx_insn *insn, rtx operands[], int *len); extern const char *ashlhi3_out (rtx_insn *insn, rtx operands[], int *len); @@ -112,8 +116,6 @@ extern int jump_over_one_insn_p (rtx_insn *insn, rtx dest); extern void avr_final_prescan_insn (rtx_insn *insn, rtx *operand, int num_operands); -extern int avr_simplify_comparison_p (machine_mode mode, - RTX_CODE op, rtx x); extern RTX_CODE avr_normalize_condition (RTX_CODE condition); extern void out_shift_with_cnt (const char *templ, rtx_insn *insn, rtx operands[], int *len, int t_len); @@ -145,6 +147,7 @@ extern rtx tmp_reg_rtx; extern rtx zero_reg_rtx; extern rtx all_regs_rtx[32]; extern rtx rampz_rtx; +extern rtx cc_reg_rtx; #endif /* RTX_CODE */ @@ -160,6 +163,7 @@ class rtl_opt_pass; extern rtl_opt_pass *make_avr_pass_pre_proep (gcc::context *); extern rtl_opt_pass *make_avr_pass_recompute_notes (gcc::context *); extern rtl_opt_pass *make_avr_pass_casesi (gcc::context *); +extern rtl_opt_pass *make_avr_pass_ifelse (gcc::context *); /* From avr-log.cc */ diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc index c193430..0447641 100644 --- a/gcc/config/avr/avr.cc +++ b/gcc/config/avr/avr.cc @@ -359,6 +359,41 @@ public: } }; // avr_pass_casesi + +static const pass_data avr_pass_data_ifelse = +{ + RTL_PASS, // type + "", // name (will be patched) + OPTGROUP_NONE, // optinfo_flags + TV_DF_SCAN, // tv_id + 0, // properties_required + 0, // properties_provided + 0, // properties_destroyed + 0, // todo_flags_start + TODO_df_finish | TODO_df_verify // todo_flags_finish +}; + +class avr_pass_ifelse : public rtl_opt_pass +{ +public: + avr_pass_ifelse (gcc::context *ctxt, const char *name) + : rtl_opt_pass (avr_pass_data_ifelse, ctxt) + { + this->name = name; + } + + void avr_rest_of_handle_ifelse (function*); + + virtual bool gate (function*) { return optimize > 0; } + + virtual unsigned int execute (function *func) + { + avr_rest_of_handle_ifelse (func); + + return 0; + } +}; // avr_pass_ifelse + } // anon namespace rtl_opt_pass* @@ -373,6 +408,12 @@ make_avr_pass_casesi (gcc::context *ctxt) return new avr_pass_casesi (ctxt, "avr-casesi"); } +rtl_opt_pass* +make_avr_pass_ifelse (gcc::context *ctxt) +{ + return new avr_pass_ifelse (ctxt, "avr-ifelse"); +} + /* Make one parallel insn with all the patterns from insns i[0]..i[5]. */ @@ -686,6 +727,304 @@ avr_pass_casesi::avr_rest_of_handle_casesi (function *func) } +/* A helper for the next method. Suppose we have two conditional branches + + if (reg <cond1> xval1) goto label1; + if (reg <cond2> xval2) goto label2; + + If the second comparison is redundant and there is a code <cond> such + that the sequence can be performed as + + REG_CC = compare (reg, xval1); + if (REG_CC <cond1> 0) goto label1; + if (REG_CC <cond> 0) goto label2; + + then return <cond>. Otherwise, return UNKNOWN. + xval1 and xval2 are CONST_INT, and mode is the scalar int mode in which + the comparison will be carried out. reverse_cond1 can be set to reverse + condition cond1. This is useful if the second comparison does not follow + the first one, but is located after label1 like in: + + if (reg <cond1> xval1) goto label1; + ... + label1: + if (reg <cond2> xval2) goto label2; */ + +static enum rtx_code +avr_redundant_compare (enum rtx_code cond1, rtx xval1, + enum rtx_code cond2, rtx xval2, + machine_mode mode, bool reverse_cond1) +{ + HOST_WIDE_INT ival1 = INTVAL (xval1); + HOST_WIDE_INT ival2 = INTVAL (xval2); + + unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode); + unsigned HOST_WIDE_INT uval1 = mask & UINTVAL (xval1); + unsigned HOST_WIDE_INT uval2 = mask & UINTVAL (xval2); + + if (reverse_cond1) + cond1 = reverse_condition (cond1); + + if (cond1 == EQ) + { + //////////////////////////////////////////////// + // A sequence like + // if (reg == val) goto label1; + // if (reg > val) goto label2; + // can be re-written using the same, simple comparison like in: + // REG_CC = compare (reg, val) + // if (REG_CC == 0) goto label1; + // if (REG_CC >= 0) goto label2; + if (ival1 == ival2 + && (cond2 == GT || cond2 == GTU)) + return avr_normalize_condition (cond2); + + // Similar, but the input sequence is like + // if (reg == val) goto label1; + // if (reg >= val) goto label2; + if (ival1 == ival2 + && (cond2 == GE || cond2 == GEU)) + return cond2; + + // Similar, but the input sequence is like + // if (reg == val) goto label1; + // if (reg >= val + 1) goto label2; + if ((cond2 == GE && ival2 == 1 + ival1) + || (cond2 == GEU && uval2 == 1 + uval1)) + return cond2; + + // Similar, but the input sequence is like + // if (reg == val) goto label1; + // if (reg > val - 1) goto label2; + if ((cond2 == GT && ival2 == ival1 - 1) + || (cond2 == GTU && uval2 == uval1 - 1)) + return avr_normalize_condition (cond2); + + ///////////////////////////////////////////////////////// + // A sequence like + // if (reg == val) goto label1; + // if (reg < 1 + val) goto label2; + // can be re-written as + // REG_CC = compare (reg, val) + // if (REG_CC == 0) goto label1; + // if (REG_CC < 0) goto label2; + if ((cond2 == LT && ival2 == 1 + ival1) + || (cond2 == LTU && uval2 == 1 + uval1)) + return cond2; + + // Similar, but with an input sequence like + // if (reg == val) goto label1; + // if (reg <= val) goto label2; + if (ival1 == ival2 + && (cond2 == LE || cond2 == LEU)) + return avr_normalize_condition (cond2); + + // Similar, but with an input sequence like + // if (reg == val) goto label1; + // if (reg < val) goto label2; + if (ival1 == ival2 + && (cond2 == LT || cond2 == LTU)) + return cond2; + + // Similar, but with an input sequence like + // if (reg == val) goto label1; + // if (reg <= val - 1) goto label2; + if ((cond2 == LE && ival2 == ival1 - 1) + || (cond2 == LEU && uval2 == uval1 - 1)) + return avr_normalize_condition (cond2); + + } // cond1 == EQ + + return UNKNOWN; +} + + +/* If-else decision trees generated for switch / case may produce sequences + like + + SREG = compare (reg, val); + if (SREG == 0) goto label1; + SREG = compare (reg, 1 + val); + if (SREG >= 0) goto label2; + + which can be optimized to + + SREG = compare (reg, val); + if (SREG == 0) goto label1; + if (SREG >= 0) goto label2; + + The optimal place for such a pass would be directly after expand, but + it's not possible for a jump insn to target more than one code label. + Hence, run a mini pass right before split2 which introduces REG_CC. */ + +void +avr_pass_ifelse::avr_rest_of_handle_ifelse (function*) +{ + rtx_insn *next_insn; + + for (rtx_insn *insn = get_insns(); insn; insn = next_insn) + { + next_insn = next_nonnote_nondebug_insn (insn); + + if (! next_insn) + break; + + // Search for two cbranch insns. The first one is a cbranch. + // Filter for "cbranch<mode>4_insn" with mode in QI, HI, PSI, SI. + + if (! JUMP_P (insn)) + continue; + + int icode1 = recog_memoized (insn); + + if (icode1 != CODE_FOR_cbranchqi4_insn + && icode1 != CODE_FOR_cbranchhi4_insn + && icode1 != CODE_FOR_cbranchpsi4_insn + && icode1 != CODE_FOR_cbranchsi4_insn) + continue; + + rtx_jump_insn *insn1 = as_a<rtx_jump_insn *> (insn); + rtx_jump_insn *insn2 = nullptr; + bool follow_label1 = false; + + // Extract the operands of the first insn: + // $0 = comparison operator ($1, $2) + // $1 = reg + // $2 = reg or const_int + // $3 = code_label + // $4 = optional SCRATCH for HI, PSI, SI cases. + + const auto &op = recog_data.operand; + + extract_insn (insn1); + rtx xop1[5] = { op[0], op[1], op[2], op[3], op[4] }; + int n_operands = recog_data.n_operands; + + // For now, we can optimize cbranches that follow an EQ cbranch, + // and cbranches that follow the label of a NE cbranch. + + if (GET_CODE (xop1[0]) == EQ + && JUMP_P (next_insn) + && recog_memoized (next_insn) == icode1) + { + // The 2nd cbranch insn follows insn1, i.e. is located in the + // fallthrough path of insn1. + + insn2 = as_a<rtx_jump_insn *> (next_insn); + } + else if (GET_CODE (xop1[0]) == NE) + { + // insn1 might branch to a label followed by a cbranch. + + rtx target1 = JUMP_LABEL (insn1); + rtx_insn *code_label1 = JUMP_LABEL_AS_INSN (insn1); + rtx_insn *next = next_nonnote_nondebug_insn (code_label1); + rtx_insn *barrier = prev_nonnote_nondebug_insn (code_label1); + + if (// Target label of insn1 is used exactly once and + // is not a fallthru, i.e. is preceded by a barrier. + LABEL_NUSES (target1) == 1 + && barrier + && BARRIER_P (barrier) + // Following the target label is a cbranch of the same kind. + && next + && JUMP_P (next) + && recog_memoized (next) == icode1) + { + follow_label1 = true; + insn2 = as_a<rtx_jump_insn *> (next); + } + } + + if (! insn2) + continue; + + // Also extract operands of insn2, and filter for REG + CONST_INT + // comparsons against the same register. + + extract_insn (insn2); + rtx xop2[5] = { op[0], op[1], op[2], op[3], op[4] }; + + if (! rtx_equal_p (xop1[1], xop2[1]) + || ! CONST_INT_P (xop1[2]) + || ! CONST_INT_P (xop2[2])) + continue; + + machine_mode mode = GET_MODE (xop1[1]); + enum rtx_code code1 = GET_CODE (xop1[0]); + enum rtx_code code2 = GET_CODE (xop2[0]); + + code2 = avr_redundant_compare (code1, xop1[2], code2, xop2[2], + mode, follow_label1); + if (code2 == UNKNOWN) + continue; + + ////////////////////////////////////////////////////// + // Found a replacement. + + if (dump_file) + { + fprintf (dump_file, "\n;; Found chain of jump_insn %d and" + " jump_insn %d, follow_label1=%d:\n", + INSN_UID (insn1), INSN_UID (insn2), follow_label1); + print_rtl_single (dump_file, PATTERN (insn1)); + print_rtl_single (dump_file, PATTERN (insn2)); + } + + if (! follow_label1) + next_insn = next_nonnote_nondebug_insn (insn2); + + // Pop the new branch conditions and the new comparison. + // Prematurely split into compare + branch so that we can drop + // the 2nd comparison. The following pass, split2, splits all + // insns for REG_CC, and it should still work as usual even when + // there are already some REG_CC insns around. + + rtx xcond1 = gen_rtx_fmt_ee (code1, VOIDmode, cc_reg_rtx, const0_rtx); + rtx xcond2 = gen_rtx_fmt_ee (code2, VOIDmode, cc_reg_rtx, const0_rtx); + rtx xpat1 = gen_branch (xop1[3], xcond1); + rtx xpat2 = gen_branch (xop2[3], xcond2); + rtx xcompare = NULL_RTX; + + if (mode == QImode) + { + gcc_assert (n_operands == 4); + xcompare = gen_cmpqi3 (xop1[1], xop1[2]); + } + else + { + gcc_assert (n_operands == 5); + rtx (*gen_cmp)(rtx,rtx,rtx) + = mode == HImode ? gen_gen_comparehi + : mode == PSImode ? gen_gen_comparepsi + : gen_gen_comparesi; // SImode + xcompare = gen_cmp (xop1[1], xop1[2], xop1[4]); + } + + // Emit that stuff. + + rtx_insn *cmp = emit_insn_before (xcompare, insn1); + rtx_jump_insn *branch1 = emit_jump_insn_before (xpat1, insn1); + rtx_jump_insn *branch2 = emit_jump_insn_before (xpat2, insn2); + + JUMP_LABEL (branch1) = xop1[3]; + JUMP_LABEL (branch2) = xop2[3]; + // delete_insn() decrements LABEL_NUSES when deleting a JUMP_INSN, but + // when we pop a new JUMP_INSN, do it by hand. + ++LABEL_NUSES (xop1[3]); + ++LABEL_NUSES (xop2[3]); + + delete_insn (insn1); + delete_insn (insn2); + + // As a side effect, also recog the new insns. + gcc_assert (valid_insn_p (cmp)); + gcc_assert (valid_insn_p (branch1)); + gcc_assert (valid_insn_p (branch2)); + } // loop insns +} + + /* Set `avr_arch' as specified by `-mmcu='. Return true on success. */ @@ -756,6 +1095,10 @@ avr_option_override (void) flag_omit_frame_pointer = 0; } + /* Disable flag_delete_null_pointer_checks if zero is a valid address. */ + if (targetm.addr_space.zero_address_valid (ADDR_SPACE_GENERIC)) + flag_delete_null_pointer_checks = 0; + if (flag_pic == 1) warning (OPT_fpic, "%<-fpic%> is not supported"); if (flag_pic == 2) @@ -961,8 +1304,7 @@ avr_lookup_function_attribute1 (const_tree func, const char *name) func = TREE_TYPE (func); } - gcc_assert (TREE_CODE (func) == FUNCTION_TYPE - || TREE_CODE (func) == METHOD_TYPE); + gcc_assert (FUNC_OR_METHOD_TYPE_P (func)); return NULL_TREE != lookup_attribute (name, TYPE_ATTRIBUTES (func)); } @@ -1019,6 +1361,19 @@ avr_no_gccisr_function_p (tree func) return avr_lookup_function_attribute1 (func, "no_gccisr"); } + +/* Implement `TARGET_CAN_INLINE_P'. */ +/* Some options like -mgas_isr_prologues depend on optimization level, + and the inliner might think that due to different options, inlining + is not permitted; see PR104327. */ + +static bool +avr_can_inline_p (tree /* caller */, tree /* callee */) +{ + // No restrictions whatsoever. + return true; +} + /* Implement `TARGET_SET_CURRENT_FUNCTION'. */ /* Sanity cheching for above function attributes. */ @@ -3173,28 +3528,6 @@ avr_asm_final_postscan_insn (FILE *stream, rtx_insn *insn, rtx*, int) } -/* Return 0 if undefined, 1 if always true or always false. */ - -int -avr_simplify_comparison_p (machine_mode mode, RTX_CODE op, rtx x) -{ - unsigned int max = (mode == QImode ? 0xff : - mode == HImode ? 0xffff : - mode == PSImode ? 0xffffff : - mode == SImode ? 0xffffffff : 0); - if (max && op && CONST_INT_P (x)) - { - if (unsigned_condition (op) != op) - max >>= 1; - - if (max != (INTVAL (x) & max) - && INTVAL (x) != 0xff) - return 1; - } - return 0; -} - - /* Worker function for `FUNCTION_ARG_REGNO_P'. */ /* Returns nonzero if REGNO is the number of a hard register in which function arguments are sometimes passed. */ @@ -5677,29 +6010,36 @@ avr_frame_pointer_required_p (void) || get_frame_size () > 0); } -/* Returns the condition of compare insn INSN, or UNKNOWN. */ + +/* Returns the condition of the branch following INSN, where INSN is some + comparison. If the next insn is not a branch or the condition code set + by INSN might be used by more insns than the next one, return UNKNOWN. + For now, just look at the next insn, which misses some opportunities like + following jumps. */ static RTX_CODE compare_condition (rtx_insn *insn) { - rtx_insn *next = next_real_insn (insn); + rtx set; + rtx_insn *next = next_real_nondebug_insn (insn); - if (next && JUMP_P (next)) + if (next + && JUMP_P (next) + // If SREG does not die in the next insn, it is used in more than one + // branch. This can happen due to pass .avr-ifelse optimizations. + && dead_or_set_regno_p (next, REG_CC) + // Branches are (set (pc) (if_then_else (COND (...)))). + && (set = single_set (next)) + && GET_CODE (SET_SRC (set)) == IF_THEN_ELSE) { - rtx pat = PATTERN (next); - if (GET_CODE (pat) == PARALLEL) - pat = XVECEXP (pat, 0, 0); - rtx src = SET_SRC (pat); - - if (IF_THEN_ELSE == GET_CODE (src)) - return GET_CODE (XEXP (src, 0)); + return GET_CODE (XEXP (SET_SRC (set), 0)); } return UNKNOWN; } -/* Returns true iff INSN is a tst insn that only tests the sign. */ +/* Returns true if INSN is a tst insn that only tests the sign. */ static bool compare_sign_p (rtx_insn *insn) @@ -5709,23 +6049,95 @@ compare_sign_p (rtx_insn *insn) } -/* Returns true iff the next insn is a JUMP_INSN with a condition - that needs to be swapped (GT, GTU, LE, LEU). */ +/* Returns true if INSN is a compare insn with the EQ or NE condition. */ static bool -compare_diff_p (rtx_insn *insn) +compare_eq_p (rtx_insn *insn) { RTX_CODE cond = compare_condition (insn); - return (cond == GT || cond == GTU || cond == LE || cond == LEU) ? cond : 0; + return (cond == EQ || cond == NE); } -/* Returns true iff INSN is a compare insn with the EQ or NE condition. */ -static bool -compare_eq_p (rtx_insn *insn) +/* Implement `TARGET_CANONICALIZE_COMPARISON'. */ +/* Basically tries to convert "difficult" comparisons like GT[U] + and LE[U] to simple ones. Some asymmetric comparisons can be + transformed to EQ or NE against zero. */ + +static void +avr_canonicalize_comparison (int *icode, rtx *op0, rtx *op1, bool op0_fixed) { - RTX_CODE cond = compare_condition (insn); - return (cond == EQ || cond == NE); + enum rtx_code code = (enum rtx_code) *icode; + machine_mode mode = GET_MODE (*op0); + + bool signed_p = code == GT || code == LE; + bool unsigned_p = code == GTU || code == LEU; + bool difficult_p = signed_p || unsigned_p; + + if (// Only do integers and fixed-points. + (! SCALAR_INT_MODE_P (mode) + && ! ALL_SCALAR_FIXED_POINT_MODE_P (mode)) + // Only do comparisons against a register. + || ! register_operand (*op0, mode)) + return; + + // Canonicalize "difficult" reg-reg comparisons. + + if (! op0_fixed + && difficult_p + && register_operand (*op1, mode)) + { + std::swap (*op0, *op1); + *icode = (int) swap_condition (code); + return; + } + + // Canonicalize comparisons against compile-time constants. + + if (CONST_INT_P (*op1) + || CONST_FIXED_P (*op1)) + { + // INT_MODE of the same size. + scalar_int_mode imode = int_mode_for_mode (mode).require (); + + unsigned HOST_WIDE_INT mask = GET_MODE_MASK (imode); + unsigned HOST_WIDE_INT maxval = signed_p ? mask >> 1 : mask; + + // Convert value *op1 to imode. + rtx xval = simplify_gen_subreg (imode, *op1, mode, 0); + + // Canonicalize difficult comparisons against const. + if (difficult_p + && (UINTVAL (xval) & mask) != maxval) + { + // Convert *op0 > *op1 to *op0 >= 1 + *op1. + // Convert *op0 <= *op1 to *op0 < 1 + *op1. + xval = simplify_binary_operation (PLUS, imode, xval, const1_rtx); + + // Convert value back to its original mode. + *op1 = simplify_gen_subreg (mode, xval, imode, 0); + + // Map > to >= and <= to <. + *icode = (int) avr_normalize_condition (code); + + return; + } + + // Some asymmetric comparisons can be turned into EQ or NE. + if (code == LTU && xval == const1_rtx) + { + *icode = (int) EQ; + *op1 = CONST0_RTX (mode); + return; + } + + if (code == GEU && xval == const1_rtx) + { + *icode = (int) NE; + *op1 = CONST0_RTX (mode); + return; + } + } } @@ -6018,6 +6430,68 @@ avr_out_tstsi (rtx_insn *insn, rtx *op, int *plen) } +/* Output a comparison of a zero- or sign-extended register against a + plain register. CODE is SIGN_EXTEND or ZERO_EXTEND. Return "". + + PLEN != 0: Set *PLEN to the code length in words. Don't output anything. + PLEN == 0: Print instructions. */ + +const char* +avr_out_cmp_ext (rtx xop[], enum rtx_code code, int *plen) +{ + // The smaller reg is the one that's to be extended. Get its index as z. + int z = GET_MODE_SIZE (GET_MODE (xop[1])) < GET_MODE_SIZE (GET_MODE (xop[0])); + rtx zreg = xop[z]; + rtx reg = xop[1 - z]; + machine_mode mode = GET_MODE (reg); + machine_mode zmode = GET_MODE (zreg); + rtx zex; + + if (plen) + *plen = 0; + + // zex holds the extended bytes above zreg. This is 0 for ZERO_EXTEND, + // and 0 or -1 for SIGN_EXTEND. + + if (code == SIGN_EXTEND) + { + // Sign-extend the high-byte of zreg to tmp_reg. + int zmsb = GET_MODE_SIZE (zmode) - 1; + rtx xzmsb = simplify_gen_subreg (QImode, zreg, zmode, zmsb); + + avr_asm_len ("mov __tmp_reg__,%0" CR_TAB + "rol __tmp_reg__" CR_TAB + "sbc __tmp_reg__,__tmp_reg__", &xzmsb, plen, 3); + zex = tmp_reg_rtx; + } + else if (code == ZERO_EXTEND) + { + zex = zero_reg_rtx; + } + else + gcc_unreachable(); + + // Now output n_bytes bytes of the very comparison. + + int n_bytes = GET_MODE_SIZE (mode); + + avr_asm_len ("cp %0,%1", xop, plen, 1); + + for (int b = 1; b < n_bytes; ++b) + { + rtx regs[2]; + regs[1 - z] = simplify_gen_subreg (QImode, reg, mode, b); + regs[z] = (b < GET_MODE_SIZE (zmode) + ? simplify_gen_subreg (QImode, zreg, zmode, b) + : zex); + + avr_asm_len ("cpc %0,%1", regs, plen, 1); + } + + return ""; +} + + /* Generate asm equivalent for various shifts. This only handles cases that are not already carefully hand-optimized in ?sh??i3_out. @@ -7130,9 +7604,9 @@ lshrqi3_out (rtx_insn *insn, rtx operands[], int *len) case 7: *len = 3; - return ("rol %0" CR_TAB - "clr %0" CR_TAB - "rol %0"); + return ("bst %1,7" CR_TAB + "clr %0" CR_TAB + "bld %0,0"); } } else if (CONSTANT_P (operands[2])) @@ -7389,10 +7863,10 @@ lshrhi3_out (rtx_insn *insn, rtx operands[], int *len) case 15: *len = 4; - return ("clr %A0" CR_TAB - "lsl %B0" CR_TAB - "rol %A0" CR_TAB - "clr %B0"); + return ("bst %B1,7" CR_TAB + "clr %A0" CR_TAB + "clr %B0" CR_TAB + "bld %A0,0"); } len = t; } @@ -7441,11 +7915,11 @@ avr_out_lshrpsi3 (rtx_insn *insn, rtx *op, int *plen) /* fall through */ case 23: - return avr_asm_len ("clr %A0" CR_TAB - "sbrc %C0,7" CR_TAB - "inc %A0" CR_TAB - "clr %B0" CR_TAB - "clr %C0", op, plen, 5); + return avr_asm_len ("bst %C1,7" CR_TAB + "clr %A0" CR_TAB + "clr %B0" CR_TAB + "clr %C0" CR_TAB + "bld %A0,0", op, plen, 5); } /* switch */ } @@ -7528,13 +8002,19 @@ lshrsi3_out (rtx_insn *insn, rtx operands[], int *len) "clr %D0"); case 31: + if (AVR_HAVE_MOVW) + return *len = 5, ("bst %D1,7" CR_TAB + "clr %A0" CR_TAB + "clr %B0" CR_TAB + "movw %C0,%A0" CR_TAB + "bld %A0,0"); *len = 6; - return ("clr %A0" CR_TAB - "sbrc %D0,7" CR_TAB - "inc %A0" CR_TAB - "clr %B0" CR_TAB - "clr %C0" CR_TAB - "clr %D0"); + return ("bst %D1,7" CR_TAB + "clr %A0" CR_TAB + "clr %B0" CR_TAB + "clr %C0" CR_TAB + "clr %D0" CR_TAB + "bld %A0,0"); } len = t; } @@ -8160,6 +8640,122 @@ avr_out_plus (rtx insn, rtx *xop, int *plen, int *pcc, bool out_label) } +/* Output an instruction sequence for addition of REG in XOP[0] and CONST_INT + in XOP[1] in such a way that SREG.Z and SREG.N are set according to the + result. XOP[2] might be a d-regs clobber register. If XOP[2] is SCRATCH, + then the addition can be performed without a clobber reg. Return "". + + If PLEN == NULL, then output the instructions. + If PLEN != NULL, then set *PLEN to the length of the sequence in words. */ + +const char* +avr_out_plus_set_ZN (rtx *xop, int *plen) +{ + if (plen) + *plen = 0; + + // Register to compare and value to compare against. + rtx xreg = xop[0]; + rtx xval = xop[1]; + + machine_mode mode = GET_MODE (xreg); + + // Number of bytes to operate on. + int n_bytes = GET_MODE_SIZE (mode); + + if (n_bytes == 1) + { + if (INTVAL (xval) == 1) + return avr_asm_len ("inc %0", xop, plen, 1); + + if (INTVAL (xval) == -1) + return avr_asm_len ("dec %0", xop, plen, 1); + } + + if (n_bytes == 2 + && test_hard_reg_class (ADDW_REGS, xreg) + && IN_RANGE (INTVAL (xval), 1, 63)) + { + // Add 16-bit value in [1..63] to a w register. + return avr_asm_len ("adiw %0, %1", xop, plen, 1); + } + + // Addition won't work; subtract the negative of XVAL instead. + xval = simplify_unary_operation (NEG, mode, xval, mode); + + // Value (0..0xff) held in clobber register xop[2] or -1 if unknown. + int clobber_val = -1; + + // [0] = Current sub-register. + // [1] = Current partial xval. + // [2] = 8-bit clobber d-register or SCRATCH. + rtx op[3]; + op[2] = xop[2]; + + // Work byte-wise from LSB to MSB. The lower two bytes might be + // SBIW'ed in one go. + for (int i = 0; i < n_bytes; ++i) + { + op[0] = simplify_gen_subreg (QImode, xreg, mode, i); + + if (i == 0 + && n_bytes >= 2 + && test_hard_reg_class (ADDW_REGS, op[0])) + { + op[1] = simplify_gen_subreg (HImode, xval, mode, 0); + if (IN_RANGE (INTVAL (op[1]), 0, 63)) + { + // SBIW can handle the lower 16 bits. + avr_asm_len ("sbiw %0, %1", op, plen, 1); + + // Next byte has already been handled: Skip it. + ++i; + continue; + } + } + + op[1] = simplify_gen_subreg (QImode, xval, mode, i); + + if (test_hard_reg_class (LD_REGS, op[0])) + { + // d-regs can subtract immediates. + avr_asm_len (i == 0 + ? "subi %0, %1" + : "sbci %0, %1", op, plen, 1); + } + else + { + int val8 = 0xff & INTVAL (op[1]); + if (val8 == 0) + { + // Any register can subtract 0. + avr_asm_len (i == 0 + ? "sub %0, __zero_reg__" + : "sbc %0, __zero_reg__", op, plen, 1); + } + else + { + // Use d-register to hold partial xval. + + if (val8 != clobber_val) + { + // Load partial xval to QI clobber reg and memoize for later. + gcc_assert (REG_P (op[2])); + avr_asm_len ("ldi %2, %1", op, plen, 1); + clobber_val = val8; + } + + avr_asm_len (i == 0 + ? "sub %0, %2" + : "sbc %0, %2", op, plen, 1); + } + } + } // Loop bytes. + + return ""; +} + + /* Output bit operation (IOR, AND, XOR) with register XOP[0] and compile time constant XOP[2]: @@ -8403,20 +8999,15 @@ avr_out_addto_sp (rtx *op, int *plen) } -/* Output instructions to insert an inverted bit into OPERANDS[0]: - $0.$1 = ~$2.$3 if XBITNO = NULL - $0.$1 = ~$2.XBITNO if XBITNO != NULL. +/* Output instructions to insert an inverted bit into OP[0]: $0.$1 = ~$2.$3. If PLEN = NULL then output the respective instruction sequence which is a combination of BST / BLD and some instruction(s) to invert the bit. If PLEN != NULL then store the length of the sequence (in words) in *PLEN. Return "". */ const char* -avr_out_insert_notbit (rtx_insn *insn, rtx operands[], rtx xbitno, int *plen) +avr_out_insert_notbit (rtx_insn *insn, rtx op[], int *plen) { - rtx op[4] = { operands[0], operands[1], operands[2], - xbitno == NULL_RTX ? operands [3] : xbitno }; - if (INTVAL (op[1]) == 7 && test_hard_reg_class (LD_REGS, op[0])) { @@ -8473,6 +9064,135 @@ avr_out_insert_notbit (rtx_insn *insn, rtx operands[], rtx xbitno, int *plen) } +/* Output instructions to extract a bit to 8-bit register XOP[0]. + The input XOP[1] is a register or an 8-bit MEM in the lower I/O range. + XOP[2] is the const_int bit position. Return "". + + PLEN != 0: Set *PLEN to the code length in words. Don't output anything. + PLEN == 0: Output instructions. */ + +const char* +avr_out_extr (rtx_insn *insn, rtx xop[], int *plen) +{ + rtx dest = xop[0]; + rtx src = xop[1]; + int bit = INTVAL (xop[2]); + + if (GET_MODE (src) != QImode) + { + src = xop[1] = simplify_gen_subreg (QImode, src, GET_MODE (src), bit / 8); + bit %= 8; + xop[2] = GEN_INT (bit); + } + + if (MEM_P (src)) + { + xop[1] = XEXP (src, 0); // address + gcc_assert (low_io_address_operand (xop[1], Pmode)); + + return avr_asm_len ("clr %0" CR_TAB + "sbic %i1,%2" CR_TAB + "inc %0", xop, plen, -3); + } + + gcc_assert (REG_P (src)); + + bool ld_dest_p = test_hard_reg_class (LD_REGS, dest); + bool ld_src_p = test_hard_reg_class (LD_REGS, src); + + if (ld_dest_p + && REGNO (src) == REGNO (dest)) + { + if (bit == 0) + return avr_asm_len ("andi %0,1", xop, plen, -1); + if (bit == 1) + return avr_asm_len ("lsr %0" CR_TAB + "andi %0,1", xop, plen, -2); + if (bit == 4) + return avr_asm_len ("swap %0" CR_TAB + "andi %0,1", xop, plen, -2); + } + + if (bit == 0 + && REGNO (src) != REGNO (dest)) + { + if (ld_dest_p) + return avr_asm_len ("mov %0,%1" CR_TAB + "andi %0,1", xop, plen, -2); + if (ld_src_p + && reg_unused_after (insn, src)) + return avr_asm_len ("andi %1,1" CR_TAB + "mov %0,%1", xop, plen, -2); + } + + return avr_asm_len ("bst %1,%2" CR_TAB + "clr %0" CR_TAB + "bld %0,0", xop, plen, -3); +} + + +/* Output instructions to extract a negated bit to 8-bit register XOP[0]. + The input XOP[1] is an 8-bit register or MEM in the lower I/O range. + XOP[2] is the const_int bit position. Return "". + + PLEN != 0: Set *PLEN to the code length in words. Don't output anything. + PLEN == 0: Output instructions. */ + +const char* +avr_out_extr_not (rtx_insn* /* insn */, rtx xop[], int *plen) +{ + rtx dest = xop[0]; + rtx src = xop[1]; + int bit = INTVAL (xop[2]); + + if (MEM_P (src)) + { + xop[1] = XEXP (src, 0); // address + gcc_assert (low_io_address_operand (xop[1], Pmode)); + + return avr_asm_len ("clr %0" CR_TAB + "sbis %i1,%2" CR_TAB + "inc %0", xop, plen, -3); + } + + gcc_assert (REG_P (src)); + + bool ld_src_p = test_hard_reg_class (LD_REGS, src); + + if (ld_src_p + && REGNO (src) == REGNO (dest)) + { + if (bit == 0) + return avr_asm_len ("inc %0" CR_TAB + "andi %0,1", xop, plen, -2); + if (bit == 1) + return avr_asm_len ("lsr %0" CR_TAB + "inc %0" CR_TAB + "andi %0,1", xop, plen, -3); + if (bit == 4) + return avr_asm_len ("swap %0" CR_TAB + "inc %0" CR_TAB + "andi %0,1", xop, plen, -3); + } + + if (bit == 7 + && ld_src_p) + return avr_asm_len ("cpi %1,0x80" CR_TAB + "sbc %0,%0" CR_TAB + "neg %0", xop, plen, -3); + + if (REGNO (src) != REGNO (dest)) + return avr_asm_len ("clr %0" CR_TAB + "sbrs %1,%2" CR_TAB + "inc %0", xop, plen, -3); + + return avr_asm_len ("clr __tmp_reg__" CR_TAB + "sbrs %1,%2" CR_TAB + "inc __tmp_reg__" CR_TAB + "mov %0,__tmp_reg__", xop, plen, -4); +} + + /* Outputs instructions needed for fixed point type conversion. This includes converting between any fixed point type, as well as converting to any integer type. Conversion between integer @@ -9270,6 +9990,8 @@ avr_adjust_insn_length (rtx_insn *insn, int len) case ADJUST_LEN_RELOAD_IN32: output_reload_insisf (op, op[2], &len); break; case ADJUST_LEN_OUT_BITOP: avr_out_bitop (insn, op, &len); break; + case ADJUST_LEN_EXTR_NOT: avr_out_extr_not (insn, op, &len); break; + case ADJUST_LEN_EXTR: avr_out_extr (insn, op, &len); break; case ADJUST_LEN_PLUS: avr_out_plus (insn, op, &len); break; case ADJUST_LEN_ADDTO_SP: avr_out_addto_sp (op, &len); break; @@ -9291,6 +10013,8 @@ avr_adjust_insn_length (rtx_insn *insn, int len) case ADJUST_LEN_TSTSI: avr_out_tstsi (insn, op, &len); break; case ADJUST_LEN_COMPARE: avr_out_compare (insn, op, &len); break; case ADJUST_LEN_COMPARE64: avr_out_compare64 (insn, op, &len); break; + case ADJUST_LEN_CMP_UEXT: avr_out_cmp_ext (op, ZERO_EXTEND, &len); break; + case ADJUST_LEN_CMP_SEXT: avr_out_cmp_ext (op, SIGN_EXTEND, &len); break; case ADJUST_LEN_LSHRQI: lshrqi3_out (insn, op, &len); break; case ADJUST_LEN_LSHRHI: lshrhi3_out (insn, op, &len); break; @@ -9311,16 +10035,9 @@ avr_adjust_insn_length (rtx_insn *insn, int len) case ADJUST_LEN_CALL: len = AVR_HAVE_JMP_CALL ? 2 : 1; break; case ADJUST_LEN_INSERT_BITS: avr_out_insert_bits (op, &len); break; + case ADJUST_LEN_ADD_SET_ZN: avr_out_plus_set_ZN (op, &len); break; - case ADJUST_LEN_INSV_NOTBIT: - avr_out_insert_notbit (insn, op, NULL_RTX, &len); - break; - case ADJUST_LEN_INSV_NOTBIT_0: - avr_out_insert_notbit (insn, op, const0_rtx, &len); - break; - case ADJUST_LEN_INSV_NOTBIT_7: - avr_out_insert_notbit (insn, op, GEN_INT (7), &len); - break; + case ADJUST_LEN_INSV_NOTBIT: avr_out_insert_notbit (insn, op, &len); break; default: gcc_unreachable(); @@ -9788,6 +10505,16 @@ avr_addr_space_diagnose_usage (addr_space_t as, location_t loc) (void) avr_addr_space_supported_p (as, loc); } +/* Implement `TARGET_ADDR_SPACE_ZERO_ADDRESS_VALID. Zero is a valid + address in all address spaces. Even in ADDR_SPACE_FLASH1 etc.., + a zero address is valid and means 0x<RAMPZ val>0000, where RAMPZ is + set to the appropriate segment value. */ + +static bool +avr_addr_space_zero_address_valid (addr_space_t) +{ + return true; +} /* Look if DECL shall be placed in program memory space by means of attribute `progmem' or some address-space qualifier. @@ -9839,7 +10566,7 @@ avr_progmem_p (tree decl, tree attributes) static bool avr_decl_absdata_p (tree decl, tree attributes) { - return (TREE_CODE (decl) == VAR_DECL + return (VAR_P (decl) && NULL_TREE != lookup_attribute ("absdata", attributes)); } @@ -9976,7 +10703,7 @@ avr_insert_attributes (tree node, tree *attributes) /* Add the section attribute if the variable is in progmem. */ - if (TREE_CODE (node) == VAR_DECL + if (VAR_P (node) && (TREE_STATIC (node) || DECL_EXTERNAL (node)) && avr_progmem_p (node, *attributes)) { @@ -10190,7 +10917,7 @@ avr_section_type_flags (tree decl, const char *name, int reloc) if (startswith (name, ".noinit")) { - if (decl && TREE_CODE (decl) == VAR_DECL + if (decl && VAR_P (decl) && DECL_INITIAL (decl) == NULL_TREE) flags |= SECTION_BSS; /* @nobits */ else @@ -10338,7 +11065,7 @@ avr_encode_section_info (tree decl, rtx rtl, int new_decl_p) if (AVR_TINY && decl - && VAR_DECL == TREE_CODE (decl) + && VAR_P (decl) && MEM_P (rtl) && SYMBOL_REF_P (XEXP (rtl, 0))) { @@ -10607,6 +11334,58 @@ avr_mul_highpart_cost (rtx x, int) } +/* Return the expected cost of a conditional branch like + (set (pc) + (if_then_else (X) + (label_ref *) + (pc))) + where X is some comparison operator. */ + +static int +avr_cbranch_cost (rtx x) +{ + bool difficult_p = difficult_comparison_operator (x, VOIDmode); + + if (reload_completed) + { + // After reload, we basically just have plain branches. + return COSTS_N_INSNS (1 + difficult_p); + } + + rtx xreg = XEXP (x, 0); + rtx xval = XEXP (x, 1); + machine_mode mode = GET_MODE (xreg); + if (mode == VOIDmode) + mode = GET_MODE (xval); + int size = GET_MODE_SIZE (mode); + + if (GET_CODE (xreg) == ZERO_EXTEND + || GET_CODE (xval) == ZERO_EXTEND) + { + // *cbranch<HISI:mode>.<code><QIPSI:mode>.0/1, code = zero_extend. + return COSTS_N_INSNS (size + 1); + } + + if (GET_CODE (xreg) == SIGN_EXTEND + || GET_CODE (xval) == SIGN_EXTEND) + { + // *cbranch<HISI:mode>.<code><QIPSI:mode>.0/1, code = sign_extend. + // Make it a bit cheaper than it actually is (less reg pressure). + return COSTS_N_INSNS (size + 1 + 1); + } + + bool reg_p = register_operand (xreg, mode); + bool reg_or_0_p = reg_or_0_operand (xval, mode); + + return COSTS_N_INSNS (size + // For the branch + + 1 + difficult_p + // Combine might propagate constants other than zero + // into the 2nd operand. Make that more expensive. + + 1 * (!reg_p || !reg_or_0_p)); +} + + /* Mutually recursive subroutine of avr_rtx_cost for calculating the cost of an RTX operand given its context. X is the rtx of the operand, MODE is its mode, and OUTER is the rtx_code of this @@ -10844,6 +11623,25 @@ avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code, *total += COSTS_N_INSNS (1); return true; } + if (IOR == code + && AND == GET_CODE (XEXP (x, 0)) + && AND == GET_CODE (XEXP (x, 1)) + && single_zero_operand (XEXP (XEXP (x, 0), 1), mode)) + { + // Open-coded bit transfer. + *total = COSTS_N_INSNS (2); + return true; + } + if (AND == code + && single_one_operand (XEXP (x, 1), mode) + && (ASHIFT == GET_CODE (XEXP (x, 0)) + || ASHIFTRT == GET_CODE (XEXP (x, 0)) + || LSHIFTRT == GET_CODE (XEXP (x, 0)))) + { + // "*insv.any_shift.<mode> + *total = COSTS_N_INSNS (1 + GET_MODE_SIZE (mode)); + return true; + } *total = COSTS_N_INSNS (GET_MODE_SIZE (mode)); *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); if (!CONST_INT_P (XEXP (x, 1))) @@ -11490,6 +12288,15 @@ avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code, } break; + case IF_THEN_ELSE: + if (outer_code == SET + && XEXP (x, 2) == pc_rtx + && ordered_comparison_operator (XEXP (x, 0), VOIDmode)) + { + *total = avr_cbranch_cost (XEXP (x, 0)); + return true; + } + default: break; } @@ -11515,6 +12322,52 @@ avr_rtx_costs (rtx x, machine_mode mode, int outer_code, } +/* Implement `TARGET_INSN_COST'. */ +/* For some insns, it is not enough to look at the cost of the SET_SRC. + In that case, have a look at the entire insn, e.g. during insn combine. */ + +static int +avr_insn_cost (rtx_insn *insn, bool speed) +{ + const int unknown_cost = -1; + int cost = unknown_cost; + + rtx set = single_set (insn); + + if (set + && ZERO_EXTRACT == GET_CODE (SET_DEST (set))) + { + // Try find anything that would flip the extracted bit. + bool not_bit_p = false; + + subrtx_iterator::array_type array; + FOR_EACH_SUBRTX (iter, array, SET_SRC (set), NONCONST) + { + enum rtx_code code = GET_CODE (*iter); + not_bit_p |= code == NOT || code == XOR || code == GE; + } + + // Don't go too deep into the analysis. In almost all cases, + // using BLD/BST is the best we can do for single-bit moves, + // even considering CSE. + cost = COSTS_N_INSNS (2 + not_bit_p); + } + + if (cost != unknown_cost) + { + if (avr_log.rtx_costs) + avr_edump ("\n%? (%s) insn_cost=%d\n%r\n", + speed ? "speed" : "size", cost, insn); + return cost; + } + + // Resort to what rtlanal.cc::insn_cost() implements as a default + // when targetm.insn_cost() is not implemented. + + return pattern_cost (PATTERN (insn), speed); +} + + /* Implement `TARGET_ADDRESS_COST'. */ static int @@ -11602,281 +12455,6 @@ avr_normalize_condition (RTX_CODE condition) } } -/* Helper function for `avr_reorg'. */ - -static rtx -avr_compare_pattern (rtx_insn *insn) -{ - rtx pattern = single_set (insn); - - if (pattern - && NONJUMP_INSN_P (insn) - && REG_P (SET_DEST (pattern)) - && REGNO (SET_DEST (pattern)) == REG_CC - && GET_CODE (SET_SRC (pattern)) == COMPARE) - { - machine_mode mode0 = GET_MODE (XEXP (SET_SRC (pattern), 0)); - machine_mode mode1 = GET_MODE (XEXP (SET_SRC (pattern), 1)); - - /* The 64-bit comparisons have fixed operands ACC_A and ACC_B. - They must not be swapped, thus skip them. */ - - if ((mode0 == VOIDmode || GET_MODE_SIZE (mode0) <= 4) - && (mode1 == VOIDmode || GET_MODE_SIZE (mode1) <= 4)) - return pattern; - } - - return NULL_RTX; -} - -/* Helper function for `avr_reorg'. */ - -/* Expansion of switch/case decision trees leads to code like - - REG_CC = compare (Reg, Num) - if (REG_CC == 0) - goto L1 - - REG_CC = compare (Reg, Num) - if (REG_CC > 0) - goto L2 - - The second comparison is superfluous and can be deleted. - The second jump condition can be transformed from a - "difficult" one to a "simple" one because "REG_CC > 0" and - "REG_CC >= 0" will have the same effect here. - - This function relies on the way switch/case is being expaned - as binary decision tree. For example code see PR 49903. - - Return TRUE if optimization performed. - Return FALSE if nothing changed. - - INSN1 is a comparison, i.e. avr_compare_pattern != 0. - - We don't want to do this in text peephole because it is - tedious to work out jump offsets there and the second comparison - might have been transormed by `avr_reorg'. - - RTL peephole won't do because peephole2 does not scan across - basic blocks. */ - -static bool -avr_reorg_remove_redundant_compare (rtx_insn *insn1) -{ - rtx comp1, ifelse1, xcond1; - rtx_insn *branch1; - rtx comp2, ifelse2, xcond2; - rtx_insn *branch2, *insn2; - enum rtx_code code; - rtx_insn *jump; - rtx target, cond; - - /* Look out for: compare1 - branch1 - compare2 - branch2 */ - - branch1 = next_nonnote_nondebug_insn (insn1); - if (!branch1 || !JUMP_P (branch1)) - return false; - - insn2 = next_nonnote_nondebug_insn (branch1); - if (!insn2 || !avr_compare_pattern (insn2)) - return false; - - branch2 = next_nonnote_nondebug_insn (insn2); - if (!branch2 || !JUMP_P (branch2)) - return false; - - comp1 = avr_compare_pattern (insn1); - comp2 = avr_compare_pattern (insn2); - xcond1 = single_set (branch1); - xcond2 = single_set (branch2); - - if (!comp1 || !comp2 - || !rtx_equal_p (comp1, comp2) - || !xcond1 || SET_DEST (xcond1) != pc_rtx - || !xcond2 || SET_DEST (xcond2) != pc_rtx - || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond1)) - || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond2))) - { - return false; - } - - comp1 = SET_SRC (comp1); - ifelse1 = SET_SRC (xcond1); - ifelse2 = SET_SRC (xcond2); - - /* comp<n> is COMPARE now and ifelse<n> is IF_THEN_ELSE. */ - - if (EQ != GET_CODE (XEXP (ifelse1, 0)) - || !REG_P (XEXP (comp1, 0)) - || !CONST_INT_P (XEXP (comp1, 1)) - || XEXP (ifelse1, 2) != pc_rtx - || XEXP (ifelse2, 2) != pc_rtx - || LABEL_REF != GET_CODE (XEXP (ifelse1, 1)) - || LABEL_REF != GET_CODE (XEXP (ifelse2, 1)) - || !COMPARISON_P (XEXP (ifelse2, 0)) - || REG_CC != REGNO (XEXP (XEXP (ifelse1, 0), 0)) - || REG_CC != REGNO (XEXP (XEXP (ifelse2, 0), 0)) - || const0_rtx != XEXP (XEXP (ifelse1, 0), 1) - || const0_rtx != XEXP (XEXP (ifelse2, 0), 1)) - { - return false; - } - - /* We filtered the insn sequence to look like - - (set (reg:CC cc) - (compare (reg:M N) - (const_int VAL))) - (set (pc) - (if_then_else (eq (reg:CC cc) - (const_int 0)) - (label_ref L1) - (pc))) - - (set (reg:CC cc) - (compare (reg:M N) - (const_int VAL))) - (set (pc) - (if_then_else (CODE (reg:CC cc) - (const_int 0)) - (label_ref L2) - (pc))) - */ - - code = GET_CODE (XEXP (ifelse2, 0)); - - /* Map GT/GTU to GE/GEU which is easier for AVR. - The first two instructions compare/branch on EQ - so we may replace the difficult - - if (x == VAL) goto L1; - if (x > VAL) goto L2; - - with easy - - if (x == VAL) goto L1; - if (x >= VAL) goto L2; - - Similarly, replace LE/LEU by LT/LTU. */ - - switch (code) - { - case EQ: - case LT: case LTU: - case GE: case GEU: - break; - - case LE: case LEU: - case GT: case GTU: - code = avr_normalize_condition (code); - break; - - default: - return false; - } - - /* Wrap the branches into UNSPECs so they won't be changed or - optimized in the remainder. */ - - target = XEXP (XEXP (ifelse1, 1), 0); - cond = XEXP (ifelse1, 0); - jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn1); - - JUMP_LABEL (jump) = JUMP_LABEL (branch1); - - target = XEXP (XEXP (ifelse2, 1), 0); - cond = gen_rtx_fmt_ee (code, VOIDmode, cc_reg_rtx, const0_rtx); - jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn2); - - JUMP_LABEL (jump) = JUMP_LABEL (branch2); - - /* The comparisons in insn1 and insn2 are exactly the same; - insn2 is superfluous so delete it. */ - - delete_insn (insn2); - delete_insn (branch1); - delete_insn (branch2); - - return true; -} - - -/* Implement `TARGET_MACHINE_DEPENDENT_REORG'. */ -/* Optimize conditional jumps. */ - -static void -avr_reorg (void) -{ - rtx_insn *insn = get_insns(); - - for (insn = next_real_insn (insn); insn; insn = next_real_insn (insn)) - { - rtx pattern = avr_compare_pattern (insn); - - if (!pattern) - continue; - - if (optimize - && avr_reorg_remove_redundant_compare (insn)) - { - continue; - } - - if (compare_diff_p (insn)) - { - /* Now we work under compare insn with difficult branch. */ - - rtx_insn *next = next_real_insn (insn); - rtx pat = PATTERN (next); - if (GET_CODE (pat) == PARALLEL) - pat = XVECEXP (pat, 0, 0); - - pattern = SET_SRC (pattern); - - if (true_regnum (XEXP (pattern, 0)) >= 0 - && true_regnum (XEXP (pattern, 1)) >= 0) - { - rtx x = XEXP (pattern, 0); - rtx src = SET_SRC (pat); - rtx t = XEXP (src, 0); - PUT_CODE (t, swap_condition (GET_CODE (t))); - XEXP (pattern, 0) = XEXP (pattern, 1); - XEXP (pattern, 1) = x; - INSN_CODE (next) = -1; - } - else if (true_regnum (XEXP (pattern, 0)) >= 0 - && XEXP (pattern, 1) == const0_rtx) - { - /* This is a tst insn, we can reverse it. */ - rtx src = SET_SRC (pat); - rtx t = XEXP (src, 0); - - PUT_CODE (t, swap_condition (GET_CODE (t))); - XEXP (pattern, 1) = XEXP (pattern, 0); - XEXP (pattern, 0) = const0_rtx; - INSN_CODE (next) = -1; - INSN_CODE (insn) = -1; - } - else if (true_regnum (XEXP (pattern, 0)) >= 0 - && CONST_INT_P (XEXP (pattern, 1))) - { - rtx x = XEXP (pattern, 1); - rtx src = SET_SRC (pat); - rtx t = XEXP (src, 0); - machine_mode mode = GET_MODE (XEXP (pattern, 0)); - - if (avr_simplify_comparison_p (mode, GET_CODE (t), x)) - { - XEXP (pattern, 1) = gen_int_mode (INTVAL (x) + 1, mode); - PUT_CODE (t, avr_normalize_condition (GET_CODE (t))); - INSN_CODE (next) = -1; - INSN_CODE (insn) = -1; - } - } - } - } -} /* Returns register number for function return value.*/ @@ -14426,10 +15004,13 @@ avr_fold_builtin (tree fndecl, int n_args ATTRIBUTE_UNUSED, tree *arg, if (changed) return build_call_expr (fndecl, 3, tmap, tbits, tval); - /* If bits don't change their position we can use vanilla logic - to merge the two arguments. */ + /* If bits don't change their position, we can use vanilla logic + to merge the two arguments... */ - if (avr_map_metric (map, MAP_NONFIXED_0_7) == 0) + if (avr_map_metric (map, MAP_NONFIXED_0_7) == 0 + // ...except when we are copying just one bit. In that + // case, BLD/BST is better than XOR/AND/XOR, see PR90622. + && avr_map_metric (map, MAP_FIXED_0_7) != 1) { int mask_f = avr_map_metric (map, MAP_MASK_PREIMAGE_F); tree tres, tmask = build_int_cst (val_type, mask_f ^ 0xff); @@ -14572,6 +15153,8 @@ avr_float_lib_compare_returns_bool (machine_mode mode, enum rtx_code) #undef TARGET_ASM_FINAL_POSTSCAN_INSN #define TARGET_ASM_FINAL_POSTSCAN_INSN avr_asm_final_postscan_insn +#undef TARGET_INSN_COST +#define TARGET_INSN_COST avr_insn_cost #undef TARGET_REGISTER_MOVE_COST #define TARGET_REGISTER_MOVE_COST avr_register_move_cost #undef TARGET_MEMORY_MOVE_COST @@ -14580,8 +15163,6 @@ avr_float_lib_compare_returns_bool (machine_mode mode, enum rtx_code) #define TARGET_RTX_COSTS avr_rtx_costs #undef TARGET_ADDRESS_COST #define TARGET_ADDRESS_COST avr_address_cost -#undef TARGET_MACHINE_DEPENDENT_REORG -#define TARGET_MACHINE_DEPENDENT_REORG avr_reorg #undef TARGET_FUNCTION_ARG #define TARGET_FUNCTION_ARG avr_function_arg #undef TARGET_FUNCTION_ARG_ADVANCE @@ -14688,6 +15269,9 @@ avr_float_lib_compare_returns_bool (machine_mode mode, enum rtx_code) #undef TARGET_ADDR_SPACE_DIAGNOSE_USAGE #define TARGET_ADDR_SPACE_DIAGNOSE_USAGE avr_addr_space_diagnose_usage +#undef TARGET_ADDR_SPACE_ZERO_ADDRESS_VALID +#define TARGET_ADDR_SPACE_ZERO_ADDRESS_VALID avr_addr_space_zero_address_valid + #undef TARGET_MODE_DEPENDENT_ADDRESS_P #define TARGET_MODE_DEPENDENT_ADDRESS_P avr_mode_dependent_address_p @@ -14711,6 +15295,12 @@ avr_float_lib_compare_returns_bool (machine_mode mode, enum rtx_code) #undef TARGET_MD_ASM_ADJUST #define TARGET_MD_ASM_ADJUST avr_md_asm_adjust +#undef TARGET_CAN_INLINE_P +#define TARGET_CAN_INLINE_P avr_can_inline_p + +#undef TARGET_CANONICALIZE_COMPARISON +#define TARGET_CANONICALIZE_COMPARISON avr_canonicalize_comparison + struct gcc_target targetm = TARGET_INITIALIZER; diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index e581e95..83dd150 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -77,7 +77,6 @@ UNSPEC_FMULS UNSPEC_FMULSU UNSPEC_COPYSIGN - UNSPEC_IDENTITY UNSPEC_INSERT_BITS UNSPEC_ROUND ]) @@ -155,7 +154,7 @@ ;; Otherwise do special processing depending on the attribute. (define_attr "adjust_len" - "out_bitop, plus, addto_sp, sext, + "out_bitop, plus, addto_sp, sext, extr, extr_not, tsthi, tstpsi, tstsi, compare, compare64, call, mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32, ufract, sfract, round, @@ -164,7 +163,8 @@ ashlhi, ashrhi, lshrhi, ashlsi, ashrsi, lshrsi, ashlpsi, ashrpsi, lshrpsi, - insert_bits, insv_notbit, insv_notbit_0, insv_notbit_7, + insert_bits, insv_notbit, + add_set_ZN, cmp_uext, cmp_sext, no" (const_string "no")) @@ -251,11 +251,23 @@ (define_mode_iterator QIHI2 [QI HI]) (define_mode_iterator QISI [QI HI PSI SI]) (define_mode_iterator QIDI [QI HI PSI SI DI]) +(define_mode_iterator QIPSI [QI HI PSI]) (define_mode_iterator HISI [HI PSI SI]) +;; Ordered integral and fixed-point modes of specific sizes. (define_mode_iterator ALL1 [QI QQ UQQ]) (define_mode_iterator ALL2 [HI HQ UHQ HA UHA]) (define_mode_iterator ALL4 [SI SQ USQ SA USA]) +(define_mode_iterator ALL234 [HI SI PSI + HQ UHQ HA UHA + SQ USQ SA USA]) + +;; Ordered signed integral and signed fixed-point modes of specific sizes. +(define_mode_iterator ALLs1 [QI QQ]) +(define_mode_iterator ALLs2 [HI HQ HA]) +(define_mode_iterator ALLs4 [SI SQ SA]) +(define_mode_iterator ALLs234 [HI SI PSI + HQ HA SQ SA]) ;; All supported move-modes (define_mode_iterator MOVMODE [QI QQ UQQ @@ -272,16 +284,31 @@ (define_mode_iterator SPLIT34 [SI SF PSI SQ USQ SA USA]) +;; Where the most significant bit is located. +(define_mode_attr MSB [(QI "7") (QQ "7") (UQQ "7") + (HI "15") (HQ "15") (UHQ "15") (HA "15") (UHA "15") + (PSI "23") + (SI "31") (SQ "31") (USQ "31") (SA "31") (USA "31") (SF "31")]) + +;; Size in bytes of the mode. +(define_mode_attr SIZE [(QI "1") (QQ "1") (UQQ "1") + (HI "2") (HQ "2") (UHQ "2") (HA "2") (UHA "2") + (PSI "3") + (SI "4") (SQ "4") (USQ "4") (SA "4") (USA "4") (SF "4")]) + ;; Define code iterators -;; Define two incarnations so that we can build the cross product. +;; Define two incarnations so that we can build the cartesian product. (define_code_iterator any_extend [sign_extend zero_extend]) (define_code_iterator any_extend2 [sign_extend zero_extend]) (define_code_iterator any_extract [sign_extract zero_extract]) (define_code_iterator any_shiftrt [lshiftrt ashiftrt]) +(define_code_iterator any_shift [lshiftrt ashiftrt ashift]) +(define_code_iterator piaop [plus ior and]) (define_code_iterator bitop [xor ior and]) (define_code_iterator xior [xor ior]) (define_code_iterator eqne [eq ne]) +(define_code_iterator gelt [ge lt]) (define_code_iterator ss_addsub [ss_plus ss_minus]) (define_code_iterator us_addsub [us_plus us_minus]) @@ -309,6 +336,10 @@ [(ss_minus "") (us_minus "") (ss_plus "%") (us_plus "%")]) +(define_code_attr gelt_eqne + [(ge "eq") + (lt "ne")]) + ;; Map RTX code to its standard insn name (define_code_attr code_stdname [(ashift "ashl") @@ -417,8 +448,7 @@ operands[0] = copy_to_mode_reg (<MODE>mode, operands[0]); } else if (REG_P (operands[0]) - && IN_RANGE (REGNO (operands[0]), FIRST_VIRTUAL_REGISTER, - LAST_VIRTUAL_REGISTER)) + && VIRTUAL_REGISTER_P (operands[0])) { // Byte-wise pushing of virtual regs might result in something like // @@ -1529,9 +1559,8 @@ "#" "&& reload_completed" [(parallel [(set (match_dup 0) - (plus:HI - (zero_extend:HI (match_dup 1)) - (zero_extend:HI (match_dup 2)))) + (plus:HI (zero_extend:HI (match_dup 1)) + (zero_extend:HI (match_dup 2)))) (clobber (reg:CC REG_CC))])]) @@ -2152,7 +2181,8 @@ (define_expand "mulqi3_call" [(set (reg:QI 24) (match_operand:QI 1 "register_operand" "")) (set (reg:QI 22) (match_operand:QI 2 "register_operand" "")) - (parallel [(set (reg:QI 24) (mult:QI (reg:QI 24) (reg:QI 22))) + (parallel [(set (reg:QI 24) + (mult:QI (reg:QI 24) (reg:QI 22))) (clobber (reg:QI 22))]) (set (match_operand:QI 0 "register_operand" "") (reg:QI 24))] "" @@ -2166,12 +2196,14 @@ "!AVR_HAVE_MUL" "#" "&& reload_completed" - [(parallel [(set (reg:QI 24) (mult:QI (reg:QI 24) (reg:QI 22))) + [(parallel [(set (reg:QI 24) + (mult:QI (reg:QI 24) (reg:QI 22))) (clobber (reg:QI 22)) (clobber (reg:CC REG_CC))])]) (define_insn "*mulqi3_call" - [(set (reg:QI 24) (mult:QI (reg:QI 24) (reg:QI 22))) + [(set (reg:QI 24) + (mult:QI (reg:QI 24) (reg:QI 22))) (clobber (reg:QI 22)) (clobber (reg:CC REG_CC))] "!AVR_HAVE_MUL && reload_completed" @@ -2307,7 +2339,7 @@ [(set (match_operand:PSI 0 "register_operand" "=r") (plus:PSI (lshiftrt:PSI (match_operand:PSI 1 "register_operand" "r") (const_int 23)) - (match_operand:PSI 2 "register_operand" "0"))) + (match_operand:PSI 2 "register_operand" "0"))) (clobber (reg:CC REG_CC))] "reload_completed" "mov __tmp_reg__,%C1\;lsl __tmp_reg__ @@ -2433,7 +2465,7 @@ [(set (match_operand:HI 0 "register_operand" "=r") (mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "a")) (zero_extend:HI (match_operand:QI 2 "register_operand" "a")))) - (clobber (reg:CC REG_CC))] + (clobber (reg:CC REG_CC))] "AVR_HAVE_MUL && reload_completed" "mulsu %1,%2 movw %0,r0 @@ -3088,7 +3120,7 @@ [(parallel [(set (match_dup 0) (mult:HI (zero_extend:HI (match_dup 1)) (match_dup 2))) - (clobber (reg:CC REG_CC))])]) + (clobber (reg:CC REG_CC))])]) (define_insn "*muluqihi3" [(set (match_operand:HI 0 "register_operand" "=&r") @@ -3706,17 +3738,17 @@ ;; CSE has problems to operate on hard regs. ;; (define_insn_and_split "divmodqi4" - [(set (match_operand:QI 0 "pseudo_register_operand" "") - (div:QI (match_operand:QI 1 "pseudo_register_operand" "") - (match_operand:QI 2 "pseudo_register_operand" ""))) - (set (match_operand:QI 3 "pseudo_register_operand" "") + [(set (match_operand:QI 0 "pseudo_register_operand") + (div:QI (match_operand:QI 1 "pseudo_register_operand") + (match_operand:QI 2 "pseudo_register_operand"))) + (set (match_operand:QI 3 "pseudo_register_operand") (mod:QI (match_dup 1) (match_dup 2))) (clobber (reg:QI 22)) (clobber (reg:QI 23)) (clobber (reg:QI 24)) (clobber (reg:QI 25))] "" - "this divmodqi4 pattern should have been splitted;" + { gcc_unreachable(); } "" [(set (reg:QI 24) (match_dup 1)) (set (reg:QI 22) (match_dup 2)) @@ -3752,17 +3784,17 @@ [(set_attr "type" "xcall")]) (define_insn_and_split "udivmodqi4" - [(set (match_operand:QI 0 "pseudo_register_operand" "") - (udiv:QI (match_operand:QI 1 "pseudo_register_operand" "") - (match_operand:QI 2 "pseudo_register_operand" ""))) - (set (match_operand:QI 3 "pseudo_register_operand" "") - (umod:QI (match_dup 1) (match_dup 2))) - (clobber (reg:QI 22)) - (clobber (reg:QI 23)) - (clobber (reg:QI 24)) - (clobber (reg:QI 25))] - "" - "this udivmodqi4 pattern should have been splitted;" + [(set (match_operand:QI 0 "pseudo_register_operand") + (udiv:QI (match_operand:QI 1 "pseudo_register_operand") + (match_operand:QI 2 "pseudo_register_operand"))) + (set (match_operand:QI 3 "pseudo_register_operand") + (umod:QI (match_dup 1) (match_dup 2))) + (clobber (reg:QI 22)) + (clobber (reg:QI 23)) + (clobber (reg:QI 24)) + (clobber (reg:QI 25))] + "" + { gcc_unreachable(); } "" [(set (reg:QI 24) (match_dup 1)) (set (reg:QI 22) (match_dup 2)) @@ -3794,17 +3826,17 @@ [(set_attr "type" "xcall")]) (define_insn_and_split "divmodhi4" - [(set (match_operand:HI 0 "pseudo_register_operand" "") - (div:HI (match_operand:HI 1 "pseudo_register_operand" "") - (match_operand:HI 2 "pseudo_register_operand" ""))) - (set (match_operand:HI 3 "pseudo_register_operand" "") + [(set (match_operand:HI 0 "pseudo_register_operand") + (div:HI (match_operand:HI 1 "pseudo_register_operand") + (match_operand:HI 2 "pseudo_register_operand"))) + (set (match_operand:HI 3 "pseudo_register_operand") (mod:HI (match_dup 1) (match_dup 2))) (clobber (reg:QI 21)) (clobber (reg:HI 22)) (clobber (reg:HI 24)) (clobber (reg:HI 26))] "" - "this should have been splitted;" + { gcc_unreachable(); } "" [(set (reg:HI 24) (match_dup 1)) (set (reg:HI 22) (match_dup 2)) @@ -3840,17 +3872,17 @@ [(set_attr "type" "xcall")]) (define_insn_and_split "udivmodhi4" - [(set (match_operand:HI 0 "pseudo_register_operand" "") - (udiv:HI (match_operand:HI 1 "pseudo_register_operand" "") - (match_operand:HI 2 "pseudo_register_operand" ""))) - (set (match_operand:HI 3 "pseudo_register_operand" "") + [(set (match_operand:HI 0 "pseudo_register_operand") + (udiv:HI (match_operand:HI 1 "pseudo_register_operand") + (match_operand:HI 2 "pseudo_register_operand"))) + (set (match_operand:HI 3 "pseudo_register_operand") (umod:HI (match_dup 1) (match_dup 2))) (clobber (reg:QI 21)) (clobber (reg:HI 22)) (clobber (reg:HI 24)) (clobber (reg:HI 26))] "" - "this udivmodhi4 pattern should have been splitted.;" + { gcc_unreachable(); } "" [(set (reg:HI 24) (match_dup 1)) (set (reg:HI 22) (match_dup 2)) @@ -3925,7 +3957,7 @@ [(parallel [(set (match_dup 0) (mult:PSI (zero_extend:PSI (match_dup 1)) (zero_extend:PSI (match_dup 2)))) - (clobber (reg:CC REG_CC))])]) + (clobber (reg:CC REG_CC))])]) (define_insn "*umulqihipsi3" [(set (match_operand:PSI 0 "register_operand" "=&r") @@ -4091,14 +4123,14 @@ ;; implementation works the other way round. (define_insn_and_split "divmodpsi4" - [(parallel [(set (match_operand:PSI 0 "pseudo_register_operand" "") - (div:PSI (match_operand:PSI 1 "pseudo_register_operand" "") - (match_operand:PSI 2 "pseudo_register_operand" ""))) - (set (match_operand:PSI 3 "pseudo_register_operand" "") - (mod:PSI (match_dup 1) - (match_dup 2))) - (clobber (reg:DI 18)) - (clobber (reg:QI 26))])] + [(set (match_operand:PSI 0 "pseudo_register_operand") + (div:PSI (match_operand:PSI 1 "pseudo_register_operand") + (match_operand:PSI 2 "pseudo_register_operand"))) + (set (match_operand:PSI 3 "pseudo_register_operand") + (mod:PSI (match_dup 1) + (match_dup 2))) + (clobber (reg:DI 18)) + (clobber (reg:QI 26))] "" { gcc_unreachable(); } "" @@ -4140,14 +4172,14 @@ [(set_attr "type" "xcall")]) (define_insn_and_split "udivmodpsi4" - [(parallel [(set (match_operand:PSI 0 "pseudo_register_operand" "") - (udiv:PSI (match_operand:PSI 1 "pseudo_register_operand" "") - (match_operand:PSI 2 "pseudo_register_operand" ""))) - (set (match_operand:PSI 3 "pseudo_register_operand" "") - (umod:PSI (match_dup 1) - (match_dup 2))) - (clobber (reg:DI 18)) - (clobber (reg:QI 26))])] + [(set (match_operand:PSI 0 "pseudo_register_operand") + (udiv:PSI (match_operand:PSI 1 "pseudo_register_operand") + (match_operand:PSI 2 "pseudo_register_operand"))) + (set (match_operand:PSI 3 "pseudo_register_operand") + (umod:PSI (match_dup 1) + (match_dup 2))) + (clobber (reg:DI 18)) + (clobber (reg:QI 26))] "" { gcc_unreachable(); } "" @@ -4191,17 +4223,18 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define_insn_and_split "divmodsi4" - [(parallel [(set (match_operand:SI 0 "pseudo_register_operand" "") - (div:SI (match_operand:SI 1 "pseudo_register_operand" "") - (match_operand:SI 2 "pseudo_register_operand" ""))) - (set (match_operand:SI 3 "pseudo_register_operand" "") - (mod:SI (match_dup 1) (match_dup 2))) - (clobber (reg:SI 18)) - (clobber (reg:SI 22)) - (clobber (reg:HI 26)) - (clobber (reg:HI 30))])] + [(set (match_operand:SI 0 "pseudo_register_operand") + (div:SI (match_operand:SI 1 "pseudo_register_operand") + (match_operand:SI 2 "pseudo_register_operand"))) + (set (match_operand:SI 3 "pseudo_register_operand") + (mod:SI (match_dup 1) + (match_dup 2))) + (clobber (reg:SI 18)) + (clobber (reg:SI 22)) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))] "" - "this divmodsi4 pattern should have been splitted;" + { gcc_unreachable(); } "" [(set (reg:SI 22) (match_dup 1)) (set (reg:SI 18) (match_dup 2)) @@ -4237,17 +4270,18 @@ [(set_attr "type" "xcall")]) (define_insn_and_split "udivmodsi4" - [(parallel [(set (match_operand:SI 0 "pseudo_register_operand" "") - (udiv:SI (match_operand:SI 1 "pseudo_register_operand" "") - (match_operand:SI 2 "pseudo_register_operand" ""))) - (set (match_operand:SI 3 "pseudo_register_operand" "") - (umod:SI (match_dup 1) (match_dup 2))) - (clobber (reg:SI 18)) - (clobber (reg:SI 22)) - (clobber (reg:HI 26)) - (clobber (reg:HI 30))])] + [(set (match_operand:SI 0 "pseudo_register_operand") + (udiv:SI (match_operand:SI 1 "pseudo_register_operand") + (match_operand:SI 2 "pseudo_register_operand"))) + (set (match_operand:SI 3 "pseudo_register_operand") + (umod:SI (match_dup 1) + (match_dup 2))) + (clobber (reg:SI 18)) + (clobber (reg:SI 22)) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))] "" - "this udivmodsi4 pattern should have been splitted;" + { gcc_unreachable(); } "" [(set (reg:SI 22) (match_dup 1)) (set (reg:SI 18) (match_dup 2)) @@ -4712,7 +4746,8 @@ [(parallel [(set (match_operand:HISI 0 "register_operand") (bitop:HISI (match_dup 0) (match_operand:HISI 1 "register_operand"))) - (clobber (scratch:QI))])] + (clobber (scratch:QI)) + (clobber (reg:CC REG_CC))])] "optimize && reload_completed" [(const_int 1)] @@ -4726,6 +4761,43 @@ DONE; }) +;; If $0 = $0 <op> const requires a QI scratch, and d-reg $1 dies after +;; the first insn, then we can replace +;; $0 = $1 +;; $0 = $0 <op> const +;; by +;; $1 = $1 <op> const +;; $0 = $1 +;; This transorms constraint alternative "r,0,n,&d" of the first operation +;; to alternative "d,0,n,X". +;; "*addhi3_clobber" "*addpsi3" "*addsi3" +;; "*addhq3" "*adduhq3" "*addha3" "*adduha3" +;; "*addsq3" "*addusq3" "*addsa3" "*addusa3" +;; "*iorhi3" "*iorpsi3" "*iorsi3" +;; "*andhi3" "*andpsi3" "*andsi3" +(define_peephole2 + [(parallel [(set (match_operand:ORDERED234 0 "register_operand") + (match_operand:ORDERED234 1 "d_register_operand")) + (clobber (reg:CC REG_CC))]) + (parallel [(set (match_dup 0) + (piaop:ORDERED234 (match_dup 0) + (match_operand:ORDERED234 2 "const_operand"))) + ; A d-reg as scratch tells that this insn is expensive, and + ; that $0 is not a d-register: l-reg or something like SI:14 etc. + (clobber (match_operand:QI 3 "d_register_operand")) + (clobber (reg:CC REG_CC))])] + "peep2_reg_dead_p (1, operands[1])" + [(parallel [(set (match_dup 1) + (piaop:ORDERED234 (match_dup 1) + (match_dup 2))) + (clobber (scratch:QI)) + (clobber (reg:CC REG_CC))]) + ; Unfortunately, the following insn misses a REG_DEAD note for $1, + ; so this peep2 works only once. + (parallel [(set (match_dup 0) + (match_dup 1)) + (clobber (reg:CC REG_CC))])]) + ;; swap swap swap swap swap swap swap swap swap swap swap swap swap swap swap ;; swap @@ -5656,9 +5728,9 @@ ;; "*lshrqq3" ;; "*lshruqq3" (define_insn_and_split "*lshr<mode>3_split" - [(set (match_operand:ALL1 0 "register_operand" "=r,r,r,r,!d,r,r") - (lshiftrt:ALL1 (match_operand:ALL1 1 "register_operand" "0,0,0,0,0 ,0,0") - (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm")))] + [(set (match_operand:ALL1 0 "register_operand" "=r,r,r,r,r ,!d,r,r") + (lshiftrt:ALL1 (match_operand:ALL1 1 "register_operand" "0,0,0,0,r ,0 ,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,K,C07,n ,n,Qm")))] "" "#" "&& reload_completed" @@ -5668,24 +5740,23 @@ (clobber (reg:CC REG_CC))])]) (define_insn "*lshr<mode>3" - [(set (match_operand:ALL1 0 "register_operand" "=r,r,r,r,!d,r,r") - (lshiftrt:ALL1 (match_operand:ALL1 1 "register_operand" "0,0,0,0,0 ,0,0") - (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm"))) + [(set (match_operand:ALL1 0 "register_operand" "=r,r,r,r,r ,!d,r,r") + (lshiftrt:ALL1 (match_operand:ALL1 1 "register_operand" "0,0,0,0,r ,0 ,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,K,C07,n ,n,Qm"))) (clobber (reg:CC REG_CC))] "reload_completed" { return lshrqi3_out (insn, operands, NULL); } - [(set_attr "length" "5,0,1,2,4,6,9") - (set_attr "adjust_len" "lshrqi")]) + [(set_attr "adjust_len" "lshrqi")]) ;; "lshrhi3" ;; "lshrhq3" "lshruhq3" ;; "lshrha3" "lshruha3" (define_insn_and_split "lshr<mode>3" - [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r,r,r") - (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r ,r,r,r") + (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,0,r,r ,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,C15,K,n,Qm")))] "" "#" "&& reload_completed" @@ -5695,22 +5766,21 @@ (clobber (reg:CC REG_CC))])]) (define_insn "*lshr<mode>3" - [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r,r,r") - (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm"))) + [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r ,r,r,r") + (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,0,r,r ,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,C15,K,n,Qm"))) (clobber (reg:CC REG_CC))] "reload_completed" { return lshrhi3_out (insn, operands, NULL); } - [(set_attr "length" "6,0,2,2,4,10,10") - (set_attr "adjust_len" "lshrhi")]) + [(set_attr "adjust_len" "lshrhi")]) (define_insn_and_split "lshrpsi3" - [(set (match_operand:PSI 0 "register_operand" "=r,r,r,r,r") - (lshiftrt:PSI (match_operand:PSI 1 "register_operand" "0,0,r,0,0") - (match_operand:QI 2 "nonmemory_operand" "r,P,O,K,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + [(set (match_operand:PSI 0 "register_operand" "=r,r,r,r ,r,r") + (lshiftrt:PSI (match_operand:PSI 1 "register_operand" "0,0,r,r ,0,0") + (match_operand:QI 2 "nonmemory_operand" "r,P,O,C23,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X ,X,&d"))] "" "#" "&& reload_completed" @@ -5721,10 +5791,10 @@ (clobber (reg:CC REG_CC))])]) (define_insn "*lshrpsi3" - [(set (match_operand:PSI 0 "register_operand" "=r,r,r,r,r") - (lshiftrt:PSI (match_operand:PSI 1 "register_operand" "0,0,r,0,0") - (match_operand:QI 2 "nonmemory_operand" "r,P,O,K,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,X,&d")) + [(set (match_operand:PSI 0 "register_operand" "=r,r,r,r ,r,r") + (lshiftrt:PSI (match_operand:PSI 1 "register_operand" "0,0,r,r ,0,0") + (match_operand:QI 2 "nonmemory_operand" "r,P,O,C23,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X ,X,&d")) (clobber (reg:CC REG_CC))] "reload_completed" { @@ -5736,9 +5806,9 @@ ;; "lshrsq3" "lshrusq3" ;; "lshrsa3" "lshrusa3" (define_insn_and_split "lshr<mode>3" - [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r,r,r,r") - (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r,r,r ,r,r") + (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,0,r,0,r ,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,C31,n,Qm")))] "" "#" "&& reload_completed" @@ -5748,16 +5818,15 @@ (clobber (reg:CC REG_CC))])]) (define_insn "*lshr<mode>3" - [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r,r,r,r") - (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm"))) + [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r,r,r ,r,r") + (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,0,r,0,r ,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,C31,n,Qm"))) (clobber (reg:CC REG_CC))] "reload_completed" { return lshrsi3_out (insn, operands, NULL); } - [(set_attr "length" "8,0,4,4,8,10,12") - (set_attr "adjust_len" "lshrsi")]) + [(set_attr "adjust_len" "lshrsi")]) ;; Optimize if a scratch register from LD_REGS happens to be available. @@ -5816,7 +5885,7 @@ operands[2] = avr_to_int_mode (operands[0]); }) -(define_peephole2 +(define_peephole2 ; "*lshrhi3_const" [(match_scratch:QI 3 "d") (parallel [(set (match_operand:ALL2 0 "register_operand" "") (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "") @@ -5833,10 +5902,10 @@ ;; "*lshrhq3_const" "*lshruhq3_const" ;; "*lshrha3_const" "*lshruha3_const" (define_insn_and_split "*lshr<mode>3_const_split" - [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r") - (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,r,0,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r ,r,r") + (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,r,r ,0,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,C15,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X ,X,&d"))] "reload_completed" "#" "&& reload_completed" @@ -5847,19 +5916,18 @@ (clobber (reg:CC REG_CC))])]) (define_insn "*lshr<mode>3_const" - [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r,r") - (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,r,0,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,X,&d")) + [(set (match_operand:ALL2 0 "register_operand" "=r,r,r,r ,r,r") + (lshiftrt:ALL2 (match_operand:ALL2 1 "register_operand" "0,0,r,r ,0,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,C15,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X ,X,&d")) (clobber (reg:CC REG_CC))] "reload_completed" { return lshrhi3_out (insn, operands, NULL); } - [(set_attr "length" "0,2,2,4,10") - (set_attr "adjust_len" "lshrhi")]) + [(set_attr "adjust_len" "lshrhi")]) -(define_peephole2 +(define_peephole2 ; "*lshrsi3_const" [(match_scratch:QI 3 "d") (parallel [(set (match_operand:ALL4 0 "register_operand" "") (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "") @@ -5876,10 +5944,10 @@ ;; "*lshrsq3_const" "*lshrusq3_const" ;; "*lshrsa3_const" "*lshrusa3_const" (define_insn_and_split "*lshr<mode>3_const_split" - [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r") - (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,r,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r ,r") + (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,r,r ,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,C31,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X ,&d"))] "reload_completed" "#" "&& reload_completed" @@ -5890,17 +5958,16 @@ (clobber (reg:CC REG_CC))])]) (define_insn "*lshr<mode>3_const" - [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r") - (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,r,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,&d")) + [(set (match_operand:ALL4 0 "register_operand" "=r,r,r,r ,r") + (lshiftrt:ALL4 (match_operand:ALL4 1 "register_operand" "0,0,r,r ,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,C31,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X ,&d")) (clobber (reg:CC REG_CC))] "reload_completed" { return lshrsi3_out (insn, operands, NULL); } - [(set_attr "length" "0,4,4,10") - (set_attr "adjust_len" "lshrsi")]) + [(set_attr "adjust_len" "lshrsi")]) ;; abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) abs(x) ;; abs @@ -6449,80 +6516,41 @@ ;;<=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=><=> ;; compare -; Optimize negated tests into reverse compare if overflow is undefined. -(define_insn "*negated_tstqi" - [(set (reg:CC REG_CC) - (compare:CC (neg:QI (match_operand:QI 0 "register_operand" "r")) - (const_int 0)))] - "reload_completed && !flag_wrapv && !flag_trapv" - "cp __zero_reg__,%0" - [(set_attr "length" "1")]) - -(define_insn "*reversed_tstqi" +;; "*swapped_tstqi" "*swapped_tstqq" +(define_insn "*swapped_tst<mode>" [(set (reg:CC REG_CC) - (compare:CC (const_int 0) - (match_operand:QI 0 "register_operand" "r")))] + (compare:CC (match_operand:ALLs1 0 "const0_operand" "Y00") + (match_operand:ALLs1 1 "register_operand" "r")))] "reload_completed" - "cp __zero_reg__,%0" -[(set_attr "length" "2")]) + "cp __zero_reg__,%1" +[(set_attr "length" "1")]) -(define_insn "*negated_tsthi" - [(set (reg:CC REG_CC) - (compare:CC (neg:HI (match_operand:HI 0 "register_operand" "r")) - (const_int 0)))] - "reload_completed && !flag_wrapv && !flag_trapv" - "cp __zero_reg__,%A0 - cpc __zero_reg__,%B0" -[(set_attr "length" "2")]) - -;; Leave here the clobber used by the cmphi pattern for simplicity, even -;; though it is unused, because this pattern is synthesized by avr_reorg. -(define_insn "*reversed_tsthi" + +;; "*swapped_tsthi" "*swapped_tsthq" "*swapped_tstha" +(define_insn "*swapped_tst<mode>" [(set (reg:CC REG_CC) - (compare:CC (const_int 0) - (match_operand:HI 0 "register_operand" "r"))) - (clobber (match_scratch:QI 1 "=X"))] + (compare:CC (match_operand:ALLs2 0 "const0_operand" "Y00") + (match_operand:ALLs2 1 "register_operand" "r")))] "reload_completed" - "cp __zero_reg__,%A0 - cpc __zero_reg__,%B0" -[(set_attr "length" "2")]) + "cp __zero_reg__,%A1 + cpc __zero_reg__,%B1" + [(set_attr "length" "2")]) -(define_insn "*negated_tstpsi" - [(set (reg:CC REG_CC) - (compare:CC (neg:PSI (match_operand:PSI 0 "register_operand" "r")) - (const_int 0)))] - "reload_completed && !flag_wrapv && !flag_trapv" - "cp __zero_reg__,%A0\;cpc __zero_reg__,%B0\;cpc __zero_reg__,%C0" - [(set_attr "length" "3")]) -(define_insn "*reversed_tstpsi" +(define_insn "*swapped_tstpsi" [(set (reg:CC REG_CC) (compare:CC (const_int 0) - (match_operand:PSI 0 "register_operand" "r"))) - (clobber (match_scratch:QI 1 "=X"))] + (match_operand:PSI 0 "register_operand" "r")))] "reload_completed" "cp __zero_reg__,%A0\;cpc __zero_reg__,%B0\;cpc __zero_reg__,%C0" [(set_attr "length" "3")]) -(define_insn "*negated_tstsi" - [(set (reg:CC REG_CC) - (compare:CC (neg:SI (match_operand:SI 0 "register_operand" "r")) - (const_int 0)))] - "reload_completed && !flag_wrapv && !flag_trapv" - "cp __zero_reg__,%A0 - cpc __zero_reg__,%B0 - cpc __zero_reg__,%C0 - cpc __zero_reg__,%D0" - [(set_attr "length" "4")]) -;; "*reversed_tstsi" -;; "*reversed_tstsq" "*reversed_tstusq" -;; "*reversed_tstsa" "*reversed_tstusa" -(define_insn "*reversed_tst<mode>" +;; "*swapped_tstsi" "*swapped_tstsq" "*swapped_tstsa" +(define_insn "*swapped_tst<mode>" [(set (reg:CC REG_CC) - (compare:CC (match_operand:ALL4 0 "const0_operand" "Y00") - (match_operand:ALL4 1 "register_operand" "r"))) - (clobber (match_scratch:QI 2 "=X"))] + (compare:CC (match_operand:ALLs4 0 "const0_operand" "Y00") + (match_operand:ALLs4 1 "register_operand" "r")))] "reload_completed" "cp __zero_reg__,%A1 cpc __zero_reg__,%B1 @@ -6536,38 +6564,40 @@ (define_insn "cmp<mode>3" [(set (reg:CC REG_CC) (compare:CC (match_operand:ALL1 0 "register_operand" "r ,r,d") - (match_operand:ALL1 1 "nonmemory_operand" "Y00,r,i")))] + (match_operand:ALL1 1 "nonmemory_operand" "Y00,r,i")))] "reload_completed" "@ - tst %0 + cp %0, __zero_reg__ cp %0,%1 cpi %0,lo8(%1)" [(set_attr "length" "1,1,1")]) -(define_insn "*cmpqi_sign_extend" - [(set (reg:CC REG_CC) - (compare:CC (sign_extend:HI (match_operand:QI 0 "register_operand" "d")) - (match_operand:HI 1 "s8_operand" "n")))] - "reload_completed" - "cpi %0,lo8(%1)" - [(set_attr "length" "1")]) - -(define_insn "*cmphi.zero-extend.0" +;; May be generated by "*cbranch<HISI:mode>.<code><QIPSI:mode>.0/1". +(define_insn "*cmp<HISI:mode>.<code><QIPSI:mode>.0" [(set (reg:CC REG_CC) - (compare:CC (zero_extend:HI (match_operand:QI 0 "register_operand" "r")) - (match_operand:HI 1 "register_operand" "r")))] - "reload_completed" - "cp %0,%A1\;cpc __zero_reg__,%B1" - [(set_attr "length" "2")]) + (compare:CC (any_extend:HISI (match_operand:QIPSI 0 "register_operand" "r")) + (match_operand:HISI 1 "register_operand" "r")))] + "reload_completed + && GET_MODE_SIZE (<HISI:MODE>mode) > GET_MODE_SIZE (<QIPSI:MODE>mode)" + { + return avr_out_cmp_ext (operands, <CODE>, nullptr); + } + [(set_attr "adjust_len" "cmp_<extend_su>ext")]) -(define_insn "*cmphi.zero-extend.1" +;; Swapped version of the above. +;; May be generated by "*cbranch<HISI:mode>.<code><QIPSI:mode>.0/1". +(define_insn "*cmp<HISI:mode>.<code><QIPSI:mode>.1" [(set (reg:CC REG_CC) - (compare:CC (match_operand:HI 0 "register_operand" "r") - (zero_extend:HI (match_operand:QI 1 "register_operand" "r"))))] - "reload_completed" - "cp %A0,%1\;cpc %B0,__zero_reg__" - [(set_attr "length" "2")]) + (compare:CC (match_operand:HISI 0 "register_operand" "r") + (any_extend:HISI (match_operand:QIPSI 1 "register_operand" "r"))))] + "reload_completed + && GET_MODE_SIZE (<HISI:MODE>mode) > GET_MODE_SIZE (<QIPSI:MODE>mode)" + { + return avr_out_cmp_ext (operands, <CODE>, nullptr); + } + [(set_attr "adjust_len" "cmp_<extend_su>ext")]) + ;; "cmphi3" ;; "cmphq3" "cmpuhq3" @@ -6575,8 +6605,8 @@ (define_insn "cmp<mode>3" [(set (reg:CC REG_CC) (compare:CC (match_operand:ALL2 0 "register_operand" "!w ,r ,r,d ,r ,d,r") - (match_operand:ALL2 1 "nonmemory_operand" "Y00,Y00,r,s ,s ,M,n Ynn"))) - (clobber (match_scratch:QI 2 "=X ,X ,X,&d,&d ,X,&d"))] + (match_operand:ALL2 1 "nonmemory_operand" "Y00,Y00,r,s ,s ,M,n Ynn"))) + (clobber (match_scratch:QI 2 "=X ,X ,X,&d,&d ,X,&d"))] "reload_completed" { switch (which_alternative) @@ -6603,14 +6633,14 @@ return avr_out_compare (insn, operands, NULL); } - [(set_attr "length" "1,2,2,3,4,2,4") + [(set_attr "length" "2,2,2,3,4,2,4") (set_attr "adjust_len" "tsthi,tsthi,*,*,*,compare,compare")]) (define_insn "*cmppsi" [(set (reg:CC REG_CC) (compare:CC (match_operand:PSI 0 "register_operand" "r,r,d ,r ,d,r") - (match_operand:PSI 1 "nonmemory_operand" "L,r,s ,s ,M,n"))) - (clobber (match_scratch:QI 2 "=X,X,&d,&d ,X,&d"))] + (match_operand:PSI 1 "nonmemory_operand" "L,r,s ,s ,M,n"))) + (clobber (match_scratch:QI 2 "=X,X,&d,&d ,X,&d"))] "reload_completed" { switch (which_alternative) @@ -6641,8 +6671,8 @@ (define_insn "*cmp<mode>" [(set (reg:CC REG_CC) (compare:CC (match_operand:ALL4 0 "register_operand" "r ,r ,d,r ,r") - (match_operand:ALL4 1 "nonmemory_operand" "Y00,r ,M,M ,n Ynn"))) - (clobber (match_scratch:QI 2 "=X ,X ,X,&d,&d"))] + (match_operand:ALL4 1 "nonmemory_operand" "Y00,r ,M,M ,n Ynn"))) + (clobber (match_scratch:QI 2 "=X ,X ,X,&d,&d"))] "reload_completed" { if (0 == which_alternative) @@ -6656,6 +6686,13 @@ (set_attr "adjust_len" "tstsi,*,compare,compare,compare")]) +;; A helper for avr_pass_ifelse::avr_rest_of_handle_ifelse(). +(define_expand "gen_compare<mode>" + [(parallel [(set (reg:CC REG_CC) + (compare:CC (match_operand:HISI 0 "register_operand") + (match_operand:HISI 1 "const_int_operand"))) + (clobber (match_operand:QI 2 "scratch_operand"))])]) + ;; ---------------------------------------------------------------------- ;; JUMP INSTRUCTIONS ;; ---------------------------------------------------------------------- @@ -6664,53 +6701,67 @@ (define_expand "cbranch<mode>4" [(set (pc) (if_then_else (match_operator 0 "ordered_comparison_operator" - [(match_operand:ALL1 1 "register_operand" "") - (match_operand:ALL1 2 "nonmemory_operand" "")]) - (label_ref (match_operand 3 "" "")) - (pc)))]) + [(match_operand:ALL1 1 "register_operand") + (match_operand:ALL1 2 "nonmemory_operand")]) + (label_ref (match_operand 3)) + (pc)))] + "" + { + int icode = (int) GET_CODE (operands[0]); + + targetm.canonicalize_comparison (&icode, &operands[1], &operands[2], false); + PUT_CODE (operands[0], (enum rtx_code) icode); + }) (define_expand "cbranch<mode>4" [(parallel [(set (pc) - (if_then_else - (match_operator 0 "ordered_comparison_operator" - [(match_operand:ORDERED234 1 "register_operand" "") - (match_operand:ORDERED234 2 "nonmemory_operand" "")]) - (label_ref (match_operand 3 "" "")) - (pc))) - (clobber (match_scratch:QI 4 ""))])]) - -;; "*cbranchqi4" -;; "*cbranchqq4" "*cbranchuqq4" -(define_insn_and_split "*cbranch<mode>4" + (if_then_else (match_operator 0 "ordered_comparison_operator" + [(match_operand:ALL234 1 "register_operand") + (match_operand:ALL234 2 "nonmemory_operand")]) + (label_ref (match_operand 3)) + (pc))) + (clobber (match_scratch:QI 4))])] + "" + { + int icode = (int) GET_CODE (operands[0]); + + targetm.canonicalize_comparison (&icode, &operands[1], &operands[2], false); + PUT_CODE (operands[0], (enum rtx_code) icode); + }) + + +;; "cbranchqi4_insn" +;; "cbranchqq4_insn" "cbranchuqq4_insn" +(define_insn_and_split "cbranch<mode>4_insn" [(set (pc) (if_then_else (match_operator 0 "ordered_comparison_operator" - [(match_operand:ALL1 1 "register_operand" "r ,r,d") + [(match_operand:ALL1 1 "register_operand" "r ,r,d") (match_operand:ALL1 2 "nonmemory_operand" "Y00,r,i")]) - (label_ref (match_operand 3 "" "")) - (pc)))] + (label_ref (match_operand 3)) + (pc)))] "" "#" "reload_completed" [(set (reg:CC REG_CC) - (compare:CC (match_dup 1) (match_dup 2))) + (compare:CC (match_dup 1) (match_dup 2))) (set (pc) (if_then_else (match_op_dup 0 [(reg:CC REG_CC) (const_int 0)]) (label_ref (match_dup 3)) - (pc)))] - "") + (pc)))]) -;; "*cbranchsi4" "*cbranchsq4" "*cbranchusq4" "*cbranchsa4" "*cbranchusa4" -(define_insn_and_split "*cbranch<mode>4" +;; "cbranchsi4_insn" +;; "cbranchsq4_insn" "cbranchusq4_insn" "cbranchsa4_insn" "cbranchusa4_insn" +(define_insn_and_split "cbranch<mode>4_insn" [(set (pc) - (if_then_else - (match_operator 0 "ordered_comparison_operator" - [(match_operand:ALL4 1 "register_operand" "r ,r ,d,r ,r") - (match_operand:ALL4 2 "nonmemory_operand" "Y00,r ,M,M ,n Ynn")]) - (label_ref (match_operand 3 "" "")) - (pc))) - (clobber (match_scratch:QI 4 "=X ,X ,X,&d,&d"))] + (if_then_else + (match_operator 0 "ordered_comparison_operator" + [(match_operand:ALL4 1 "register_operand" "r ,r,d,r ,r") + (match_operand:ALL4 2 "nonmemory_operand" "Y00,r,M,M ,n Ynn")]) + (label_ref (match_operand 3)) + (pc))) + (clobber (match_scratch:QI 4 "=X ,X,X,&d,&d"))] "" "#" "reload_completed" @@ -6721,19 +6772,18 @@ (if_then_else (match_op_dup 0 [(reg:CC REG_CC) (const_int 0)]) (label_ref (match_dup 3)) - (pc)))] - "") + (pc)))]) -;; "*cbranchpsi4" -(define_insn_and_split "*cbranchpsi4" +;; "cbranchpsi4_insn" +(define_insn_and_split "cbranchpsi4_insn" [(set (pc) - (if_then_else - (match_operator 0 "ordered_comparison_operator" - [(match_operand:PSI 1 "register_operand" "r,r,d ,r ,d,r") - (match_operand:PSI 2 "nonmemory_operand" "L,r,s ,s ,M,n")]) - (label_ref (match_operand 3 "" "")) - (pc))) - (clobber (match_scratch:QI 4 "=X,X,&d,&d ,X,&d"))] + (if_then_else + (match_operator 0 "ordered_comparison_operator" + [(match_operand:PSI 1 "register_operand" "r,r,d ,r ,d,r") + (match_operand:PSI 2 "nonmemory_operand" "L,r,s ,s ,M,n")]) + (label_ref (match_operand 3)) + (pc))) + (clobber (match_scratch:QI 4 "=X,X,&d,&d,X,&d"))] "" "#" "reload_completed" @@ -6744,19 +6794,19 @@ (if_then_else (match_op_dup 0 [(reg:CC REG_CC) (const_int 0)]) (label_ref (match_dup 3)) - (pc)))] - "") + (pc)))]) -;; "*cbranchhi4" "*cbranchhq4" "*cbranchuhq4" "*cbranchha4" "*cbranchuha4" -(define_insn_and_split "*cbranch<mode>4" +;; "cbranchhi4_insn" +;; "cbranchhq4_insn" "cbranchuhq4_insn" "cbranchha4_insn" "cbranchuha4_insn" +(define_insn_and_split "cbranch<mode>4_insn" [(set (pc) - (if_then_else - (match_operator 0 "ordered_comparison_operator" - [(match_operand:ALL2 1 "register_operand" "!w ,r ,r,d ,r ,d,r") - (match_operand:ALL2 2 "nonmemory_operand" "Y00,Y00,r,s ,s ,M,n Ynn")]) - (label_ref (match_operand 3 "" "")) - (pc))) - (clobber (match_scratch:QI 4 "=X ,X ,X,&d,&d ,X,&d"))] + (if_then_else + (match_operator 0 "ordered_comparison_operator" + [(match_operand:ALL2 1 "register_operand" "!w ,r ,r,d ,r ,d,r") + (match_operand:ALL2 2 "nonmemory_operand" "Y00,Y00,r,s ,s ,M,n Ynn")]) + (label_ref (match_operand 3)) + (pc))) + (clobber (match_scratch:QI 4 "=X ,X ,X,&d,&d,X,&d"))] "" "#" "reload_completed" @@ -6767,8 +6817,71 @@ (if_then_else (match_op_dup 0 [(reg:CC REG_CC) (const_int 0)]) (label_ref (match_dup 3)) - (pc)))] - "") + (pc)))]) + +;; Combiner pattern to compare sign- or zero-extended register against +;; a wider register, like comparing uint8_t against uint16_t. +(define_insn_and_split "*cbranch<HISI:mode>.<code><QIPSI:mode>.0" + [(set (pc) + (if_then_else (match_operator 0 "ordered_comparison_operator" + [(any_extend:HISI (match_operand:QIPSI 1 "register_operand" "r")) + (match_operand:HISI 2 "register_operand" "r")]) + (label_ref (match_operand 3)) + (pc)))] + "optimize + && GET_MODE_SIZE (<HISI:MODE>mode) > GET_MODE_SIZE (<QIPSI:MODE>mode)" + "#" + "&& reload_completed" + [; "*cmp<HISI:mode>.<code><QIPSI:mode>.0" + (set (reg:CC REG_CC) + (compare:CC (match_dup 1) + (match_dup 2))) + ; "branch" + (set (pc) + (if_then_else (match_op_dup 0 [(reg:CC REG_CC) + (const_int 0)]) + (label_ref (match_dup 3)) + (pc)))] + { + operands[1] = gen_rtx_<CODE> (<HISI:MODE>mode, operands[1]); + if (difficult_comparison_operator (operands[0], VOIDmode)) + { + PUT_CODE (operands[0], swap_condition (GET_CODE (operands[0]))); + std::swap (operands[1], operands[2]); + } + }) + +;; Same combiner pattern, but with swapped operands. +(define_insn_and_split "*cbranch<HISI:mode>.<code><QIPSI:mode>.0" + [(set (pc) + (if_then_else (match_operator 0 "ordered_comparison_operator" + [(match_operand:HISI 1 "register_operand" "r") + (any_extend:HISI (match_operand:QIPSI 2 "register_operand" "r"))]) + (label_ref (match_operand 3)) + (pc)))] + "optimize + && GET_MODE_SIZE (<HISI:MODE>mode) > GET_MODE_SIZE (<QIPSI:MODE>mode)" + "#" + "&& reload_completed" + [; "*cmp<HISI:mode>.<code><QIPSI:mode>.0" + (set (reg:CC REG_CC) + (compare:CC (match_dup 1) + (match_dup 2))) + ; "branch" + (set (pc) + (if_then_else (match_op_dup 0 [(reg:CC REG_CC) + (const_int 0)]) + (label_ref (match_dup 3)) + (pc)))] + { + operands[2] = gen_rtx_<CODE> (<HISI:MODE>mode, operands[2]); + if (difficult_comparison_operator (operands[0], VOIDmode)) + { + PUT_CODE (operands[0], swap_condition (GET_CODE (operands[0]))); + std::swap (operands[1], operands[2]); + } + }) + ;; Test a single bit in a QI/HI/SImode register. ;; Combine will create zero extract patterns for single bit tests. @@ -6842,14 +6955,11 @@ "#" "&& reload_completed" [(parallel [(set (pc) - (if_then_else - (match_op_dup 0 - [(and:QISI - (match_dup 1) - (match_dup 2)) - (const_int 0)]) - (label_ref (match_dup 3)) - (pc))) + (if_then_else (match_op_dup 0 [(and:QISI (match_dup 1) + (match_dup 2)) + (const_int 0)]) + (label_ref (match_dup 3)) + (pc))) (clobber (reg:CC REG_CC))])]) (define_insn "*sbrx_and_branch<mode>" @@ -6878,163 +6988,77 @@ (const_int 2) (const_int 4))))]) -;; Convert sign tests to bit 7/15/31 tests that match the above insns. -(define_peephole2 - [(set (reg:CC REG_CC) (compare:CC (match_operand:QI 0 "register_operand" "") - (const_int 0))) - (parallel [(set (pc) (if_then_else (ge (reg:CC REG_CC) (const_int 0)) - (label_ref (match_operand 1 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "" - [(parallel [(set (pc) (if_then_else (eq (zero_extract:HI (match_dup 0) - (const_int 1) - (const_int 7)) - (const_int 0)) - (label_ref (match_dup 1)) - (pc))) - (clobber (reg:CC REG_CC))])]) - -(define_peephole2 - [(set (reg:CC REG_CC) (compare:CC (match_operand:QI 0 "register_operand" "") - (const_int 0))) - (parallel [(set (pc) (if_then_else (lt (reg:CC REG_CC) (const_int 0)) - (label_ref (match_operand 1 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "" - [(parallel [(set (pc) (if_then_else (ne (zero_extract:HI (match_dup 0) - (const_int 1) - (const_int 7)) - (const_int 0)) - (label_ref (match_dup 1)) - (pc))) - (clobber (reg:CC REG_CC))])]) - -(define_peephole2 - [(parallel [(set (reg:CC REG_CC) (compare:CC (match_operand:HI 0 "register_operand" "") - (const_int 0))) - (clobber (match_operand:HI 2 ""))]) - (parallel [(set (pc) (if_then_else (ge (reg:CC REG_CC) (const_int 0)) - (label_ref (match_operand 1 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "" - [(parallel [(set (pc) (if_then_else (eq (and:HI (match_dup 0) (const_int -32768)) - (const_int 0)) - (label_ref (match_dup 1)) - (pc))) - (clobber (reg:CC REG_CC))])]) -(define_peephole2 - [(parallel [(set (reg:CC REG_CC) (compare:CC (match_operand:HI 0 "register_operand" "") - (const_int 0))) - (clobber (match_operand:HI 2 ""))]) - (parallel [(set (pc) (if_then_else (lt (reg:CC REG_CC) (const_int 0)) - (label_ref (match_operand 1 "" "")) - (pc))) +;; Convert sign tests to bit 7 tests that match the above insns. +(define_peephole2 ; "*sbrx_branch<mode>" + [(set (reg:CC REG_CC) + (compare:CC (match_operand:ALLs1 0 "register_operand") + (match_operand:ALLs1 1 "const0_operand"))) + (set (pc) + (if_then_else (gelt (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 2)) + (pc)))] + "peep2_regno_dead_p (2, REG_CC)" + [(parallel [(set (pc) + (if_then_else (<gelt_eqne> (zero_extract:HI (match_dup 0) + (const_int 1) + (match_dup 1)) + (const_int 0)) + (label_ref (match_dup 2)) + (pc))) (clobber (reg:CC REG_CC))])] - "" - [(parallel [(set (pc) (if_then_else (ne (and:HI (match_dup 0) (const_int -32768)) - (const_int 0)) - (label_ref (match_dup 1)) - (pc))) - (clobber (reg:CC REG_CC))])]) + { + operands[0] = avr_to_int_mode (operands[0]); + operands[1] = GEN_INT (GET_MODE_BITSIZE (<MODE>mode) - 1); + }) -(define_peephole2 - [(parallel [(set (reg:CC REG_CC) (compare:CC (match_operand:SI 0 "register_operand" "") - (const_int 0))) - (clobber (match_operand:SI 2 ""))]) - (parallel [(set (pc) (if_then_else (ge (reg:CC REG_CC) (const_int 0)) - (label_ref (match_operand 1 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "" - [(parallel [(set (pc) (if_then_else (eq (and:SI (match_dup 0) (match_dup 2)) - (const_int 0)) - (label_ref (match_dup 1)) - (pc))) +;; Convert sign tests to bit 15/23/31 tests that match the above insns. +(define_peephole2 ; "*sbrx_branch<mode>" + [(parallel [(set (reg:CC REG_CC) + (compare:CC (match_operand:ALLs234 0 "register_operand") + (match_operand:ALLs234 1 "const0_operand"))) + (clobber (match_operand:QI 3 "scratch_operand"))]) + (set (pc) + (if_then_else (gelt (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 2)) + (pc)))] + "peep2_regno_dead_p (2, REG_CC)" + [(parallel [(set (pc) + (if_then_else (<gelt_eqne> (zero_extract:HI (match_dup 0) + (const_int 1) + (match_dup 1)) + (const_int 0)) + (label_ref (match_dup 2)) + (pc))) (clobber (reg:CC REG_CC))])] - "operands[2] = gen_int_mode (-2147483647 - 1, SImode);") + { + operands[0] = avr_to_int_mode (operands[0]); + operands[1] = GEN_INT (GET_MODE_BITSIZE (<MODE>mode) - 1); + }) -(define_peephole2 - [(parallel [(set (reg:CC REG_CC) (compare:CC (match_operand:SI 0 "register_operand" "") - (const_int 0))) - (clobber (match_operand:SI 2 ""))]) - (parallel [(set (pc) (if_then_else (lt (reg:CC REG_CC) (const_int 0)) - (label_ref (match_operand 1 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "" - [(parallel [(set (pc) (if_then_else (ne (and:SI (match_dup 0) (match_dup 2)) - (const_int 0)) - (label_ref (match_dup 1)) - (pc))) - (clobber (reg:CC REG_CC))])] - "operands[2] = gen_int_mode (-2147483647 - 1, SImode);") ;; ************************************************************************ ;; Implementation of conditional jumps here. ;; Compare with 0 (test) jumps ;; ************************************************************************ -(define_insn_and_split "branch" +(define_insn "branch" [(set (pc) (if_then_else (match_operator 1 "simple_comparison_operator" - [(reg:CC REG_CC) - (const_int 0)]) - (label_ref (match_operand 0 "" "")) + [(reg:CC REG_CC) + (const_int 0)]) + (label_ref (match_operand 0)) (pc)))] "reload_completed" - "#" - "&& reload_completed" - [(parallel [(set (pc) - (if_then_else (match_op_dup 1 - [(reg:CC REG_CC) - (const_int 0)]) - (label_ref (match_dup 0)) - (pc))) - (clobber (reg:CC REG_CC))])]) - -(define_insn "*branch" - [(set (pc) - (if_then_else (match_operator 1 "simple_comparison_operator" - [(reg:CC REG_CC) - (const_int 0)]) - (label_ref (match_operand 0 "" "")) - (pc))) - (clobber (reg:CC REG_CC))] - "reload_completed" - { - return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0); - } - [(set_attr "type" "branch")]) - - -;; Same as above but wrap SET_SRC so that this branch won't be transformed -;; or optimized in the remainder. - -(define_insn "branch_unspec" - [(set (pc) - (unspec [(if_then_else (match_operator 1 "simple_comparison_operator" - [(reg:CC REG_CC) - (const_int 0)]) - (label_ref (match_operand 0 "" "")) - (pc)) - ] UNSPEC_IDENTITY)) - (clobber (reg:CC REG_CC))] - "reload_completed" { return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0); } [(set_attr "type" "branch")]) -;; **************************************************************** -;; AVR does not have following conditional jumps: LE,LEU,GT,GTU. -;; Convert them all to proper jumps. -;; ****************************************************************/ -(define_insn_and_split "difficult_branch" +(define_insn "difficult_branch" [(set (pc) (if_then_else (match_operator 1 "difficult_comparison_operator" [(reg:CC REG_CC) @@ -7042,95 +7066,11 @@ (label_ref (match_operand 0 "" "")) (pc)))] "reload_completed" - "#" - "&& reload_completed" - [(parallel [(set (pc) - (if_then_else (match_op_dup 1 - [(reg:CC REG_CC) - (const_int 0)]) - (label_ref (match_dup 0)) - (pc))) - (clobber (reg:CC REG_CC))])]) - -(define_insn "*difficult_branch" - [(set (pc) - (if_then_else (match_operator 1 "difficult_comparison_operator" - [(reg:CC REG_CC) - (const_int 0)]) - (label_ref (match_operand 0 "" "")) - (pc))) - (clobber (reg:CC REG_CC))] - "reload_completed" { return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0); } [(set_attr "type" "branch1")]) -;; revers branch - -(define_insn_and_split "rvbranch" - [(set (pc) - (if_then_else (match_operator 1 "simple_comparison_operator" - [(reg:CC REG_CC) - (const_int 0)]) - (pc) - (label_ref (match_operand 0 "" ""))))] - "reload_completed" - "#" - "&& reload_completed" - [(parallel [(set (pc) - (if_then_else (match_op_dup 1 - [(reg:CC REG_CC) - (const_int 0)]) - (pc) - (label_ref (match_dup 0)))) - (clobber (reg:CC REG_CC))])]) - -(define_insn "*rvbranch" - [(set (pc) - (if_then_else (match_operator 1 "simple_comparison_operator" - [(reg:CC REG_CC) - (const_int 0)]) - (pc) - (label_ref (match_operand 0 "" "")))) - (clobber (reg:CC REG_CC))] - "reload_completed" - { - return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 1); - } - [(set_attr "type" "branch1")]) - -(define_insn_and_split "difficult_rvbranch" - [(set (pc) - (if_then_else (match_operator 1 "difficult_comparison_operator" - [(reg:CC REG_CC) - (const_int 0)]) - (pc) - (label_ref (match_operand 0 "" ""))))] - "reload_completed" - "#" - "&& reload_completed" - [(parallel [(set (pc) - (if_then_else (match_op_dup 1 - [(reg:CC REG_CC) - (const_int 0)]) - (pc) - (label_ref (match_dup 0)))) - (clobber (reg:CC REG_CC))])]) - -(define_insn "*difficult_rvbranch" - [(set (pc) - (if_then_else (match_operator 1 "difficult_comparison_operator" - [(reg:CC REG_CC) - (const_int 0)]) - (pc) - (label_ref (match_operand 0 "" "")))) - (clobber (reg:CC REG_CC))] - "reload_completed" - { - return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 1); - } - [(set_attr "type" "branch")]) ;; ************************************************************************** ;; Unconditional and other jump instructions. @@ -7656,15 +7596,14 @@ (clobber (reg:CC REG_CC))]) (parallel [(set (reg:CC REG_CC) (compare:CC (match_dup 0) - (const_int -1))) - (clobber (match_operand:QI 1 "d_register_operand" ""))]) - (parallel [(set (pc) - (if_then_else (eqne (reg:CC REG_CC) - (const_int 0)) - (label_ref (match_operand 2 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "" + (const_int -1))) + (clobber (match_operand:QI 1 "scratch_or_d_register_operand"))]) + (set (pc) + (if_then_else (eqne (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 2)) + (pc)))] + "dead_or_set_regno_p (insn, REG_CC)" { const char *op; int jump_mode; @@ -7700,15 +7639,14 @@ (clobber (reg:CC REG_CC))]) (parallel [(set (reg:CC REG_CC) (compare:CC (match_dup 0) - (const_int -1))) + (const_int -1))) (clobber (match_operand:QI 1 "d_register_operand" ""))]) - (parallel [(set (pc) - (if_then_else (eqne (reg:CC REG_CC) - (const_int 0)) - (label_ref (match_operand 2 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "" + (set (pc) + (if_then_else (eqne (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 2)) + (pc)))] + "dead_or_set_regno_p (insn, REG_CC)" { const char *op; int jump_mode; @@ -7742,15 +7680,14 @@ (clobber (reg:CC REG_CC))]) (parallel [(set (reg:CC REG_CC) (compare:CC (match_dup 0) - (const_int -1))) - (clobber (match_operand:QI 1 "d_register_operand" ""))]) - (parallel [(set (pc) - (if_then_else (eqne (reg:CC REG_CC) - (const_int 0)) - (label_ref (match_operand 2 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "" + (const_int -1))) + (clobber (match_operand:QI 1 "scratch_or_d_register_operand"))]) + (set (pc) + (if_then_else (eqne (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 2)) + (pc)))] + "dead_or_set_regno_p (insn, REG_CC)" { const char *op; int jump_mode; @@ -7784,15 +7721,14 @@ (clobber (reg:CC REG_CC))]) (parallel [(set (reg:CC REG_CC) (compare:CC (match_dup 0) - (const_int -1))) + (const_int -1))) (clobber (match_operand:QI 1 "d_register_operand" ""))]) - (parallel [(set (pc) - (if_then_else (eqne (reg:CC REG_CC) - (const_int 0)) - (label_ref (match_operand 2 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "" + (set (pc) + (if_then_else (eqne (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 2)) + (pc)))] + "dead_or_set_regno_p (insn, REG_CC)" { const char *op; int jump_mode; @@ -7822,14 +7758,13 @@ (clobber (reg:CC REG_CC))]) (set (reg:CC REG_CC) (compare:CC (match_dup 0) - (const_int -1))) - (parallel [(set (pc) - (if_then_else (eqne (reg:CC REG_CC) - (const_int 0)) - (label_ref (match_operand 1 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "" + (const_int -1))) + (set (pc) + (if_then_else (eqne (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 1)) + (pc)))] + "dead_or_set_regno_p (insn, REG_CC)" { const char *op; int jump_mode; @@ -7855,14 +7790,14 @@ (define_peephole ; "*cpse.eq" [(set (reg:CC REG_CC) (compare:CC (match_operand:ALL1 1 "register_operand" "r,r") - (match_operand:ALL1 2 "reg_or_0_operand" "r,Y00"))) - (parallel [(set (pc) - (if_then_else (eq (reg:CC REG_CC) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "jump_over_one_insn_p (insn, operands[0])" + (match_operand:ALL1 2 "reg_or_0_operand" "r,Y00"))) + (set (pc) + (if_then_else (eq (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 0)) + (pc)))] + "jump_over_one_insn_p (insn, operands[0]) + && dead_or_set_regno_p (insn, REG_CC)" "@ cpse %1,%2 cpse %1,__zero_reg__") @@ -7890,16 +7825,16 @@ (define_peephole ; "*cpse.ne" [(set (reg:CC REG_CC) - (compare:CC (match_operand:ALL1 1 "register_operand" "") - (match_operand:ALL1 2 "reg_or_0_operand" ""))) - (parallel [(set (pc) - (if_then_else (ne (reg:CC REG_CC) - (const_int 0)) - (label_ref (match_operand 0 "" "")) - (pc))) - (clobber (reg:CC REG_CC))])] - "!AVR_HAVE_JMP_CALL - || !TARGET_SKIP_BUG" + (compare:CC (match_operand:ALL1 1 "register_operand") + (match_operand:ALL1 2 "reg_or_0_operand"))) + (set (pc) + (if_then_else (ne (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 0)) + (pc)))] + "(!AVR_HAVE_JMP_CALL + || !TARGET_SKIP_BUG) + && dead_or_set_regno_p (insn, REG_CC)" { if (operands[2] == CONST0_RTX (<MODE>mode)) operands[2] = zero_reg_rtx; @@ -8094,7 +8029,7 @@ (const_int 1)] UNSPECV_DELAY_CYCLES) (set (match_dup 1) - (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) + (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) (clobber (match_dup 2)) (clobber (reg:CC REG_CC))])]) @@ -8126,7 +8061,7 @@ (const_int 2)] UNSPECV_DELAY_CYCLES) (set (match_dup 1) - (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) + (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) (clobber (match_dup 2)) (clobber (reg:CC REG_CC))])] "" @@ -8163,7 +8098,7 @@ (const_int 3)] UNSPECV_DELAY_CYCLES) (set (match_dup 1) - (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) + (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) (clobber (match_dup 2)) (clobber (match_dup 3)) (clobber (match_dup 4)) @@ -8206,7 +8141,7 @@ (const_int 4)] UNSPECV_DELAY_CYCLES) (set (match_dup 1) - (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) + (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) (clobber (match_dup 2)) (clobber (match_dup 3)) (clobber (match_dup 4)) @@ -9095,16 +9030,20 @@ "bst %3,0\;bld %0,%4" [(set_attr "length" "2")]) -;; Move bit $3.0 into bit $0.0. -;; For bit 0, combiner generates slightly different pattern. -(define_insn "*movbitqi.0" - [(set (match_operand:QI 0 "register_operand" "=r") - (ior:QI (and:QI (match_operand:QI 1 "register_operand" "0") - (match_operand:QI 2 "single_zero_operand" "n")) - (and:QI (match_operand:QI 3 "register_operand" "r") - (const_int 1))))] - "0 == exact_log2 (~INTVAL(operands[2]) & GET_MODE_MASK (QImode))" - "bst %3,0\;bld %0,0" +;; Move bit $3.x into bit $0.x. +(define_insn "*movbit<mode>.0-6" + [(set (match_operand:QISI 0 "register_operand" "=r") + (ior:QISI (and:QISI (match_operand:QISI 1 "register_operand" "0") + (match_operand:QISI 2 "single_zero_operand" "n")) + (and:QISI (match_operand:QISI 3 "register_operand" "r") + (match_operand:QISI 4 "single_one_operand" "n"))))] + "GET_MODE_MASK(<MODE>mode) + == (GET_MODE_MASK(<MODE>mode) & (INTVAL(operands[2]) ^ INTVAL(operands[4])))" + { + auto bitmask = GET_MODE_MASK (<MODE>mode) & UINTVAL (operands[4]); + operands[4] = GEN_INT (exact_log2 (bitmask)); + return "bst %T3%T4" CR_TAB "bld %T0%T4"; + } [(set_attr "length" "2")]) ;; Move bit $2.0 into bit $0.7. @@ -9213,6 +9152,21 @@ ;; Same, but with a NOT inverting the source bit. ;; Insert bit ~$2.$3 into $0.$1 +(define_insn "insv_notbit" + [(set (zero_extract:QI (match_operand:QI 0 "register_operand" "+r") + (const_int 1) + (match_operand:QI 1 "const_0_to_7_operand" "n")) + (not:QI (zero_extract:QI (match_operand:QI 2 "register_operand" "r") + (const_int 1) + (match_operand:QI 3 "const_0_to_7_operand" "n")))) + (clobber (reg:CC REG_CC))] + "reload_completed" + { + return avr_out_insert_notbit (insn, operands, NULL); + } + [(set_attr "adjust_len" "insv_notbit")]) + +;; Insert bit ~$2.$3 into $0.$1 (define_insn_and_split "*insv.not-shiftrt_split" [(set (zero_extract:QI (match_operand:QI 0 "register_operand" "+r") (const_int 1) @@ -9222,25 +9176,11 @@ "" "#" "&& reload_completed" - [(parallel [(set (zero_extract:QI (match_dup 0) - (const_int 1) - (match_dup 1)) - (not:QI (any_shiftrt:QI (match_dup 2) - (match_dup 3)))) - (clobber (reg:CC REG_CC))])]) - -(define_insn "*insv.not-shiftrt" - [(set (zero_extract:QI (match_operand:QI 0 "register_operand" "+r") - (const_int 1) - (match_operand:QI 1 "const_0_to_7_operand" "n")) - (not:QI (any_shiftrt:QI (match_operand:QI 2 "register_operand" "r") - (match_operand:QI 3 "const_0_to_7_operand" "n")))) - (clobber (reg:CC REG_CC))] - "reload_completed" + [(scratch)] { - return avr_out_insert_notbit (insn, operands, NULL_RTX, NULL); - } - [(set_attr "adjust_len" "insv_notbit")]) + emit (gen_insv_notbit (operands[0], operands[1], operands[2], operands[3])); + DONE; + }) ;; Insert bit ~$2.0 into $0.$1 (define_insn_and_split "*insv.xor1-bit.0_split" @@ -9252,25 +9192,11 @@ "" "#" "&& reload_completed" - [(parallel [(set (zero_extract:QI (match_dup 0) - (const_int 1) - (match_dup 1)) - (xor:QI (match_dup 2) - (const_int 1))) - (clobber (reg:CC REG_CC))])]) - -(define_insn "*insv.xor1-bit.0" - [(set (zero_extract:QI (match_operand:QI 0 "register_operand" "+r") - (const_int 1) - (match_operand:QI 1 "const_0_to_7_operand" "n")) - (xor:QI (match_operand:QI 2 "register_operand" "r") - (const_int 1))) - (clobber (reg:CC REG_CC))] - "reload_completed" + [(scratch)] { - return avr_out_insert_notbit (insn, operands, const0_rtx, NULL); - } - [(set_attr "adjust_len" "insv_notbit_0")]) + emit (gen_insv_notbit (operands[0], operands[1], operands[2], const0_rtx)); + DONE; + }) ;; Insert bit ~$2.0 into $0.$1 (define_insn_and_split "*insv.not-bit.0_split" @@ -9281,23 +9207,11 @@ "" "#" "&& reload_completed" - [(parallel [(set (zero_extract:QI (match_dup 0) - (const_int 1) - (match_dup 1)) - (not:QI (match_dup 2))) - (clobber (reg:CC REG_CC))])]) - -(define_insn "*insv.not-bit.0" - [(set (zero_extract:QI (match_operand:QI 0 "register_operand" "+r") - (const_int 1) - (match_operand:QI 1 "const_0_to_7_operand" "n")) - (not:QI (match_operand:QI 2 "register_operand" "r"))) - (clobber (reg:CC REG_CC))] - "reload_completed" + [(scratch)] { - return avr_out_insert_notbit (insn, operands, const0_rtx, NULL); - } - [(set_attr "adjust_len" "insv_notbit_0")]) + emit (gen_insv_notbit (operands[0], operands[1], operands[2], const0_rtx)); + DONE; + }) ;; Insert bit ~$2.7 into $0.$1 (define_insn_and_split "*insv.not-bit.7_split" @@ -9309,25 +9223,11 @@ "" "#" "&& reload_completed" - [(parallel [(set (zero_extract:QI (match_dup 0) - (const_int 1) - (match_dup 1)) - (ge:QI (match_dup 2) - (const_int 0))) - (clobber (reg:CC REG_CC))])]) - -(define_insn "*insv.not-bit.7" - [(set (zero_extract:QI (match_operand:QI 0 "register_operand" "+r") - (const_int 1) - (match_operand:QI 1 "const_0_to_7_operand" "n")) - (ge:QI (match_operand:QI 2 "register_operand" "r") - (const_int 0))) - (clobber (reg:CC REG_CC))] - "reload_completed" + [(scratch)] { - return avr_out_insert_notbit (insn, operands, GEN_INT (7), NULL); - } - [(set_attr "adjust_len" "insv_notbit_7")]) + emit (gen_insv_notbit (operands[0], operands[1], operands[2], GEN_INT(7))); + DONE; + }) ;; Insert bit ~$2.$3 into $0.$1 (define_insn_and_split "*insv.xor-extract_split" @@ -9341,31 +9241,13 @@ "INTVAL (operands[4]) & (1 << INTVAL (operands[3]))" "#" "&& reload_completed" - [(parallel [(set (zero_extract:QI (match_dup 0) - (const_int 1) - (match_dup 1)) - (any_extract:QI (xor:QI (match_dup 2) - (match_dup 4)) - (const_int 1) - (match_dup 3))) - (clobber (reg:CC REG_CC))])]) - -(define_insn "*insv.xor-extract" - [(set (zero_extract:QI (match_operand:QI 0 "register_operand" "+r") - (const_int 1) - (match_operand:QI 1 "const_0_to_7_operand" "n")) - (any_extract:QI (xor:QI (match_operand:QI 2 "register_operand" "r") - (match_operand:QI 4 "const_int_operand" "n")) - (const_int 1) - (match_operand:QI 3 "const_0_to_7_operand" "n"))) - (clobber (reg:CC REG_CC))] - "INTVAL (operands[4]) & (1 << INTVAL (operands[3])) && reload_completed" + [(scratch)] { - return avr_out_insert_notbit (insn, operands, NULL_RTX, NULL); - } - [(set_attr "adjust_len" "insv_notbit")]) + emit (gen_insv_notbit (operands[0], operands[1], operands[2], operands[3])); + DONE; + }) + - ;; Some combine patterns that try to fix bad code when a value is composed ;; from byte parts like in PR27663. ;; The patterns give some release but the code still is not optimal, @@ -9489,17 +9371,269 @@ (clobber (reg:CC REG_CC))])]) -(define_expand "extzv" - [(set (match_operand:QI 0 "register_operand" "") - (zero_extract:QI (match_operand:QI 1 "register_operand" "") - (match_operand:QI 2 "const1_operand" "") - (match_operand:QI 3 "const_0_to_7_operand" "")))]) +;; Try optimize decrement-and-branch. When we have an addition followed +;; by a comparison of the result against zero, we can output the addition +;; in such a way that SREG.N and SREG.Z are set according to the result. + +;; { -1, +1 } for QImode, otherwise the empty set. +(define_mode_attr p1m1 [(QI "N P") + (HI "Yxx") (PSI "Yxx") (SI "Yxx")]) + +;; FIXME: reload1.cc::do_output_reload() does not support output reloads +;; for JUMP_INSNs, hence letting combine doing decrement-and-branch like +;; the following might run into ICE. Doing reloads by hand is too painful... +; +; (define_insn_and_split "*add.for.eqne.<mode>.cbranch" +; [(set (pc) +; (if_then_else (eqne (match_operand:QISI 1 "register_operand" "0") +; (match_operand:QISI 2 "const_int_operand" "n")) +; (label_ref (match_operand 4)) +; (pc))) +; (set (match_operand:QISI 0 "register_operand" "=r") +; (plus:QISI (match_dup 1) +; (match_operand:QISI 3 "const_int_operand" "n")))] +; ;; No clobber for now as combine might not have one handy. +; ;; We pop a scatch in split1. +; "!reload_completed +; && const0_rtx == simplify_binary_operation (PLUS, <MODE>mode, +; operands[2], operands[3])" +; { gcc_unreachable(); } +; "&& 1" +; [(parallel [(set (pc) +; (if_then_else (eqne (match_dup 1) +; (match_dup 2)) +; (label_ref (match_dup 4)) +; (pc))) +; (set (match_dup 0) +; (plus:QISI (match_dup 1) +; (match_dup 3))) +; (clobber (scratch:QI))])]) +; +;; ...Hence, stick with RTL peepholes for now. Unfortunately, there is no +;; canonical form, and if reload shuffles registers around, we might miss +;; opportunities to match a decrement-and-branch. +;; doloop_end doesn't reload either, so doloop_end also won't work. + +(define_expand "gen_add_for_<code>_<mode>" + ; "*add.for.eqne.<mode>" + [(parallel [(set (reg:CC REG_CC) + (compare:CC (plus:QISI (match_operand:QISI 0 "register_operand") + (match_operand:QISI 1 "const_int_operand")) + (const_int 0))) + (set (match_dup 0) + (plus:QISI (match_dup 0) + (match_dup 1))) + (clobber (match_operand:QI 3))]) + ; "branch" + (set (pc) + (if_then_else (eqne (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_dup 2)) + (pc)))]) + + +;; 1/3: A version without clobber: d-reg or 8-bit adds +/-1. +(define_peephole2 + [(parallel [(set (match_operand:QISI 0 "register_operand") + (plus:QISI (match_dup 0) + (match_operand:QISI 1 "const_int_operand"))) + (clobber (reg:CC REG_CC))]) + (set (reg:CC REG_CC) + (compare:CC (match_dup 0) + (const_int 0))) + (set (pc) + (if_then_else (eqne (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 2)) + (pc)))] + "peep2_regno_dead_p (3, REG_CC) + && (d_register_operand (operands[0], <MODE>mode) + || (<MODE>mode == QImode + && (INTVAL (operands[1]) == 1 + || INTVAL (operands[1]) == -1)))" + [(scratch)] + { + emit (gen_gen_add_for_<code>_<mode> (operands[0], operands[1], operands[2], + gen_rtx_SCRATCH (QImode))); + DONE; + }) + +;; 2/3: A version with clobber from the insn. +(define_peephole2 + [(parallel [(set (match_operand:QISI 0 "register_operand") + (plus:QISI (match_dup 0) + (match_operand:QISI 1 "const_int_operand"))) + (clobber (match_operand:QI 3 "scratch_or_d_register_operand")) + (clobber (reg:CC REG_CC))]) + (parallel [(set (reg:CC REG_CC) + (compare:CC (match_dup 0) + (const_int 0))) + (clobber (match_operand:QI 4 "scratch_or_d_register_operand"))]) + (set (pc) + (if_then_else (eqne (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 2)) + (pc)))] + "peep2_regno_dead_p (3, REG_CC)" + [(scratch)] + { + rtx scratch = REG_P (operands[3]) ? operands[3] : operands[4]; -(define_insn_and_split "*extzv_split" - [(set (match_operand:QI 0 "register_operand" "=*d,*d,*d,*d,r") - (zero_extract:QI (match_operand:QI 1 "register_operand" "0,r,0,0,r") + // We need either a d-register or a scratch register to clobber. + if (! REG_P (scratch) + && ! d_register_operand (operands[0], <MODE>mode) + && ! (QImode == <MODE>mode + && (INTVAL (operands[1]) == 1 + || INTVAL (operands[1]) == -1))) + { + FAIL; + } + emit (gen_gen_add_for_<code>_<mode> (operands[0], operands[1], operands[2], + scratch)); + DONE; + }) + +;; 3/3 A version with a clobber from peephole2. +(define_peephole2 + [(match_scratch:QI 3 "d") + (parallel [(set (match_operand:QISI 0 "register_operand") + (plus:QISI (match_dup 0) + (match_operand:QISI 1 "const_int_operand"))) + (clobber (reg:CC REG_CC))]) + (set (reg:CC REG_CC) + (compare:CC (match_dup 0) + (const_int 0))) + (set (pc) + (if_then_else (eqne (reg:CC REG_CC) + (const_int 0)) + (label_ref (match_operand 2)) + (pc)))] + "peep2_regno_dead_p (3, REG_CC)" + [(scratch)] + { + emit (gen_gen_add_for_<code>_<mode> (operands[0], operands[1], operands[2], + operands[3])); + DONE; + }) + +;; Result of the above three peepholes is an addition that also +;; performs an EQ or NE comparison (of the result) against zero. +;; FIXME: Using (match_dup 0) instead of operands[3/4] makes rnregs +;; barf in regrename.cc::merge_overlapping_regs(). For now, use the +;; fix from PR50788: Constrain as "0". +(define_insn "*add.for.eqne.<mode>" + [(set (reg:CC REG_CC) + (compare:CC + (plus:QISI (match_operand:QISI 3 "register_operand" "0,0 ,0") + (match_operand:QISI 1 "const_int_operand" "n,<p1m1>,n")) + (const_int 0))) + (set (match_operand:QISI 0 "register_operand" "=d,*r ,r") + (plus:QISI (match_operand:QISI 4 "register_operand" "0,0 ,0") + (match_dup 1))) + (clobber (match_scratch:QI 2 "=X,X ,&d"))] + "reload_completed" + { + return avr_out_plus_set_ZN (operands, nullptr); + } + [(set_attr "adjust_len" "add_set_ZN")]) + + +;; Swapping both comparison and branch condition. This can turn difficult +;; branches to easy ones. And in some cases, a comparison against one can +;; be turned into a comparison against zero. + +(define_peephole2 ; "*swapped_tst<mode>" + [(parallel [(set (reg:CC REG_CC) + (compare:CC (match_operand:ALLs234 1 "register_operand") + (match_operand:ALLs234 2 "const_operand"))) + (clobber (match_operand:QI 3 "scratch_operand"))]) + (set (pc) + (if_then_else (match_operator 0 "ordered_comparison_operator" + [(reg:CC REG_CC) + (const_int 0)]) + (label_ref (match_operand 4)) + (pc)))] + "peep2_regno_dead_p (2, REG_CC)" + [(set (reg:CC REG_CC) + (compare:CC (match_dup 2) + (match_dup 1))) + ; "branch" + (set (pc) + (if_then_else (match_op_dup 0 [(reg:CC REG_CC) + (const_int 0)]) + (label_ref (match_dup 4)) + (pc)))] + { + rtx xval = avr_to_int_mode (operands[2]); + enum rtx_code code = GET_CODE (operands[0]); + + if (code == GT && xval == const0_rtx) + code = LT; + else if (code == GE && xval == const1_rtx) + code = LT; + else if (code == LE && xval == const0_rtx) + code = GE; + else if (code == LT && xval == const1_rtx) + code = GE; + else + FAIL; + + operands[2] = CONST0_RTX (<MODE>mode); + PUT_CODE (operands[0], code); + }) + +;; Same, but for 8-bit modes which have no scratch reg. +(define_peephole2 ; "*swapped_tst<mode>" + [(set (reg:CC REG_CC) + (compare:CC (match_operand:ALLs1 1 "register_operand") + (match_operand:ALLs1 2 "const_operand"))) + (set (pc) + (if_then_else (match_operator 0 "ordered_comparison_operator" + [(reg:CC REG_CC) + (const_int 0)]) + (label_ref (match_operand 4)) + (pc)))] + "peep2_regno_dead_p (2, REG_CC)" + [(set (reg:CC REG_CC) + (compare:CC (match_dup 2) + (match_dup 1))) + ; "branch" + (set (pc) + (if_then_else (match_op_dup 0 [(reg:CC REG_CC) + (const_int 0)]) + (label_ref (match_dup 4)) + (pc)))] + { + rtx xval = avr_to_int_mode (operands[2]); + enum rtx_code code = GET_CODE (operands[0]); + + if (code == GT && xval == const0_rtx) + code = LT; + else if (code == GE && xval == const1_rtx) + code = LT; + else if (code == LE && xval == const0_rtx) + code = GE; + else if (code == LT && xval == const1_rtx) + code = GE; + else + FAIL; + + operands[2] = CONST0_RTX (<MODE>mode); + PUT_CODE (operands[0], code); + }) + + +(define_expand "extzv<mode>" + [(set (match_operand:QI 0 "register_operand") + (zero_extract:QI (match_operand:QISI 1 "register_operand") + (match_operand:QI 2 "const1_operand") + (match_operand:QI 3 "const_0_to_<MSB>_operand")))]) + +(define_insn_and_split "*extzv<mode>_split" + [(set (match_operand:QI 0 "register_operand" "=r") + (zero_extract:QI (match_operand:QISI 1 "reg_or_low_io_operand" "r Yil") (const_int 1) - (match_operand:QI 2 "const_0_to_7_operand" "L,L,P,C04,n")))] + (match_operand:QI 2 "const_0_to_<MSB>_operand" "n")))] "" "#" "&& reload_completed" @@ -9507,22 +9641,28 @@ (zero_extract:QI (match_dup 1) (const_int 1) (match_dup 2))) - (clobber (reg:CC REG_CC))])]) + (clobber (reg:CC REG_CC))])] + { + if (! MEM_P (operands[1])) + { + int bitno = INTVAL (operands[2]); + operands[1] = simplify_gen_subreg (QImode, operands[1], <MODE>mode, bitno / 8); + operands[2] = GEN_INT (bitno % 8); + } + }) -(define_insn "*extzv" - [(set (match_operand:QI 0 "register_operand" "=*d,*d,*d,*d,r") - (zero_extract:QI (match_operand:QI 1 "register_operand" "0,r,0,0,r") +(define_insn "*extzv<mode>" + [(set (match_operand:QI 0 "register_operand" "=r") + (zero_extract:QI (match_operand:QISI 1 "reg_or_low_io_operand" "r Yil") (const_int 1) - (match_operand:QI 2 "const_0_to_7_operand" "L,L,P,C04,n"))) + (match_operand:QI 2 "const_0_to_<MSB>_operand" "n"))) (clobber (reg:CC REG_CC))] "reload_completed" - "@ - andi %0,1 - mov %0,%1\;andi %0,1 - lsr %0\;andi %0,1 - swap %0\;andi %0,1 - bst %1,%2\;clr %0\;bld %0,0" - [(set_attr "length" "1,2,2,2,3")]) + { + return avr_out_extr (insn, operands, nullptr); + } + [(set_attr "adjust_len" "extr")]) + (define_insn_and_split "*extzv.qihi1" [(set (match_operand:HI 0 "register_operand" "=r") @@ -9543,16 +9683,197 @@ operands[4] = simplify_gen_subreg (QImode, operands[0], HImode, 1); }) -(define_insn_and_split "*extzv.qihi2" - [(set (match_operand:HI 0 "register_operand" "=r") - (zero_extend:HI - (zero_extract:QI (match_operand:QI 1 "register_operand" "r") +(define_insn_and_split "*extzv.not_split" + [(set (match_operand:QI 0 "register_operand" "=r") + (zero_extract:QI (not:QI (match_operand:QI 1 "reg_or_low_io_operand" "r Yil")) + (const_int 1) + (match_operand:QI 2 "const_0_to_7_operand" "n")))] + "" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) + (zero_extract:QI (not:QI (match_dup 1)) + (const_int 1) + (match_dup 2))) + (clobber (reg:CC REG_CC))])]) + +(define_insn "*extzv.not" + [(set (match_operand:QI 0 "register_operand" "=r") + (zero_extract:QI (not:QI (match_operand:QI 1 "reg_or_low_io_operand" "r Yil")) + (const_int 1) + (match_operand:QI 2 "const_0_to_7_operand" "n"))) + (clobber (reg:CC REG_CC))] + "reload_completed" + { + return avr_out_extr_not (insn, operands, nullptr); + } + [(set_attr "adjust_len" "extr_not")]) + +(define_insn_and_split "*extzv.subreg.<mode>" + [(set (match_operand:QI 0 "register_operand" "=r") + (subreg:QI (zero_extract:HISI (match_operand:HISI 1 "register_operand" "r") + (const_int 1) + (match_operand:QI 2 "const_0_to_<MSB>_operand" "n")) + 0))] + "! reload_completed" + { gcc_unreachable(); } + "&& 1" + [; "*extzv<mode>_split" + (set (match_dup 0) + (zero_extract:QI (match_dup 1) (const_int 1) - (match_operand:QI 2 "const_0_to_7_operand" "n"))))] + (match_dup 2)))]) + +;; Possible subreg bytes. +(define_int_iterator SuReB [0 1 2 3]) + +(define_insn_and_split "*extzv.<mode>.subreg<SuReB>" + [(set (match_operand:QI 0 "register_operand" "=r") + (zero_extract:QI (subreg:QI + (and:HISI (match_operand:HISI 1 "register_operand" "r") + (match_operand:HISI 2 "single_one_operand" "n")) + SuReB) + (const_int 1) + (match_operand:QI 3 "const_0_to_7_operand" "n")))] + "! reload_completed + && IN_RANGE (UINTVAL(operands[2]) & GET_MODE_MASK(<MODE>mode), + 1U << (8 * <SuReB>), 0x80U << (8 * <SuReB>)) + && exact_log2 (UINTVAL(operands[2]) & GET_MODE_MASK(<MODE>mode)) + == 8 * <SuReB> + INTVAL (operands[3])" + { gcc_unreachable(); } + "&& 1" + [; "*extzv<mode>_split" + (set (match_dup 0) + (zero_extract:QI (match_dup 1) + (const_int 1) + (match_dup 4)))] + { + operands[4] = plus_constant (QImode, operands[3], 8 * <SuReB>); + }) + + +(define_insn_and_split "*extzv.xor" + [(set (match_operand:QI 0 "register_operand") + (zero_extract:QI (xor:QI (match_operand:QI 1 "reg_or_low_io_operand") + (match_operand:QI 2 "single_one_operand")) + (const_int 1) + (match_operand:QI 3 "const_0_to_7_operand")))] + "! reload_completed + && ((1 << INTVAL (operands[3])) & INTVAL (operands[2])) != 0" + { gcc_unreachable(); } + "&& 1" + [; "*extzv.not_split" + (set (match_dup 0) + (zero_extract:QI (not:QI (match_dup 1)) + (const_int 1) + (match_dup 3)))]) + + +(define_insn_and_split "*extzv<mode>.ge" + [(set (match_operand:QI 0 "register_operand" "=r") + (ge:QI (match_operand:QISI 1 "reg_or_low_io_operand" "r Yil") + (match_operand:QISI 2 "const0_operand" "Y00")))] "" "#" + "reload_completed" + [; "*extzv.not" + (parallel [(set (match_dup 0) + (zero_extract:QI (not:QI (match_dup 1)) + (const_int 1) + (const_int 7))) + (clobber (reg:CC REG_CC))])] + { + if (! MEM_P (operands[1])) + { + int msb = <SIZE> - 1; + operands[1] = simplify_gen_subreg (QImode, operands[1], <MODE>mode, msb); + } + }) + +(define_insn_and_split "*neg.ashiftrt<mode>.msb" + [(set (match_operand:QI 0 "register_operand" "=r") + (neg:QI (subreg:QI + (ashiftrt:QISI (match_operand:QISI 1 "register_operand" "r") + (match_operand:QI 2 "const<MSB>_operand" "n")) + 0)))] + "! reload_completed" + { gcc_unreachable(); } + "&& 1" + [; "*extzv<mode>_split" + (set (match_dup 0) + (zero_extract:QI (match_dup 1) + (const_int 1) + (match_dup 2)))]) + +(define_insn_and_split "*extzv.io.lsr7" + [(set (match_operand:QI 0 "register_operand") + (lshiftrt:QI (match_operand:QI 1 "reg_or_low_io_operand") + (const_int 7)))] + "! reload_completed" + { gcc_unreachable(); } + "&& 1" + [; "*extzv_split" + (set (match_dup 0) + (zero_extract:QI (match_dup 1) + (const_int 1) + (const_int 7)))]) + +(define_insn_and_split "*insv.any_shift.<mode>_split" + [(set (match_operand:QISI 0 "register_operand" "=r") + (and:QISI (any_shift:QISI (match_operand:QISI 1 "register_operand" "r") + (match_operand:QI 2 "const_0_to_<MSB>_operand" "n")) + (match_operand:QISI 3 "single_one_operand" "n")))] "" - [(set (match_dup 3) + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) + (and:QISI (any_shift:QISI (match_dup 1) + (match_dup 2)) + (match_dup 3))) + (clobber (reg:CC REG_CC))])]) + +(define_insn "*insv.any_shift.<mode>" + [(set (match_operand:QISI 0 "register_operand" "=r") + (and:QISI (any_shift:QISI (match_operand:QISI 1 "register_operand" "r") + (match_operand:QI 2 "const_0_to_<MSB>_operand" "n")) + (match_operand:QISI 3 "single_one_operand" "n"))) + (clobber (reg:CC REG_CC))] + "reload_completed" + { + int shift = <CODE> == ASHIFT ? INTVAL (operands[2]) : -INTVAL (operands[2]); + int mask = GET_MODE_MASK (<MODE>mode) & INTVAL (operands[3]); + // Position of the output / input bit, respectively. + int obit = exact_log2 (mask); + int ibit = obit - shift; + gcc_assert (IN_RANGE (obit, 0, <MSB>)); + gcc_assert (IN_RANGE (ibit, 0, <MSB>)); + operands[3] = GEN_INT (obit); + operands[2] = GEN_INT (ibit); + + if (<SIZE> == 1) return "bst %T1%T2\;clr %0\;" "bld %T0%T3"; + if (<SIZE> == 2) return "bst %T1%T2\;clr %A0\;clr %B0\;" "bld %T0%T3"; + if (<SIZE> == 3) return "bst %T1%T2\;clr %A0\;clr %B0\;clr %C0\;bld %T0%T3"; + return AVR_HAVE_MOVW + ? "bst %T1%T2\;clr %A0\;clr %B0\;movw %C0,%A0\;" "bld %T0%T3" + : "bst %T1%T2\;clr %A0\;clr %B0\;clr %C0\;clr %D0\;bld %T0%T3"; + } + [(set (attr "length") + (minus (symbol_ref "2 + <SIZE>") + ; One less if we can use a MOVW to clear. + (symbol_ref "<SIZE> == 4 && AVR_HAVE_MOVW")))]) + + +(define_insn_and_split "*extzv.<mode>hi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (zero_extend:HI + (zero_extract:QI (match_operand:QISI 1 "register_operand" "r") + (const_int 1) + (match_operand:QI 2 "const_0_to_<MSB>_operand" "n"))))] + "! reload_completed" + { gcc_unreachable(); } + "&& 1" + [; "*extzv<mode>_split" + (set (match_dup 3) (zero_extract:QI (match_dup 1) (const_int 1) (match_dup 2))) @@ -9566,24 +9887,20 @@ ;; ??? do_store_flag emits a hard-coded right shift to extract a bit without ;; even considering rtx_costs, extzv, or a bit-test. See PR 55181 for an example. (define_insn_and_split "*extract.subreg.bit" - [(set (match_operand:QI 0 "register_operand" "=r") - (and:QI (subreg:QI (any_shiftrt:HISI (match_operand:HISI 1 "register_operand" "r") - (match_operand:QI 2 "const_int_operand" "n")) - 0) + [(set (match_operand:QI 0 "register_operand" "=r") + (and:QI (subreg:QI + (any_shiftrt:HISI (match_operand:HISI 1 "register_operand" "r") + (match_operand:QI 2 "const_0_to_<MSB>_operand" "n")) + 0) (const_int 1)))] - "INTVAL (operands[2]) < GET_MODE_BITSIZE (<MODE>mode)" + "! reload_completed" { gcc_unreachable(); } - "&& reload_completed" - [;; "*extzv" + "&& 1" + [;; "*extzv<mode>_split" (set (match_dup 0) - (zero_extract:QI (match_dup 3) + (zero_extract:QI (match_dup 1) (const_int 1) - (match_dup 4)))] - { - int bitno = INTVAL (operands[2]); - operands[3] = simplify_gen_subreg (QImode, operands[1], <MODE>mode, bitno / 8); - operands[4] = GEN_INT (bitno % 8); - }) + (match_dup 2)))]) ;; Fixed-point instructions diff --git a/gcc/config/avr/avr.opt b/gcc/config/avr/avr.opt index f62d746..5a0b465 100644 --- a/gcc/config/avr/avr.opt +++ b/gcc/config/avr/avr.opt @@ -27,7 +27,7 @@ Target RejectNegative Joined Var(avr_mmcu) MissingArgError(missing device or arc -mmcu=MCU Select the target MCU. mgas-isr-prologues -Target Var(avr_gasisr_prologues) UInteger Init(0) +Target Var(avr_gasisr_prologues) UInteger Init(0) Optimization Allow usage of __gcc_isr pseudo instructions in ISR prologues and epilogues. mn-flash= @@ -65,7 +65,7 @@ Target Joined RejectNegative UInteger Var(avr_branch_cost) Init(0) Set the branch costs for conditional branch instructions. Reasonable values are small, non-negative integers. The default branch cost is 0. mmain-is-OS_task -Target Mask(MAIN_IS_OS_TASK) +Target Mask(MAIN_IS_OS_TASK) Optimization Treat main as if it had attribute OS_task. morder1 diff --git a/gcc/config/avr/constraints.md b/gcc/config/avr/constraints.md index ac43678..8f97a48 100644 --- a/gcc/config/avr/constraints.md +++ b/gcc/config/avr/constraints.md @@ -133,6 +133,21 @@ (and (match_code "const_int") (match_test "ival == 7"))) +(define_constraint "C15" + "Constant integer 15." + (and (match_code "const_int") + (match_test "ival == 15"))) + +(define_constraint "C23" + "Constant integer 23." + (and (match_code "const_int") + (match_test "ival == 23"))) + +(define_constraint "C31" + "Constant integer 31." + (and (match_code "const_int") + (match_test "ival == 31"))) + (define_constraint "Ca1" "Constant 1-byte integer that allows AND by means of CLT + BLD." (and (match_code "const_int") @@ -245,6 +260,11 @@ (match_test "INTVAL (avr_to_int_mode (op)) == -2")) (match_test "satisfies_constraint_Cm2 (op)"))) +;; Constraint that's the empty set. Useful with mode and code iterators. +(define_constraint "Yxx" + "A constraints that is always false" + (match_test "false")) + (define_constraint "Yx2" "Fixed-point or integer constant not in the range @minus{}2 @dots{} 2" (and (ior (match_code "const_int") @@ -257,3 +277,8 @@ "Fixed-point constant from @minus{}0x003f to 0x003f." (and (match_code "const_fixed") (match_test "IN_RANGE (INTVAL (avr_to_int_mode (op)), -63, 63)"))) + +(define_constraint "Yil" + "Memory in the lower half of the I/O space." + (and (match_code "mem") + (match_test "low_io_address_operand (XEXP (op, 0), Pmode)"))) diff --git a/gcc/config/avr/gen-avr-mmcu-specs.cc b/gcc/config/avr/gen-avr-mmcu-specs.cc index 9344246..b9a5ad4 100644 --- a/gcc/config/avr/gen-avr-mmcu-specs.cc +++ b/gcc/config/avr/gen-avr-mmcu-specs.cc @@ -30,7 +30,7 @@ #include "avr-devices.cc" // Get rid of "defaults.h". We just need tm.h for `WITH_AVRLIBC' and -// and `WITH_RTEMS'. */ +// and `WITH_RTEMS'. #define GCC_DEFAULTS_H #include "tm.h" diff --git a/gcc/config/avr/predicates.md b/gcc/config/avr/predicates.md index e352326..c279e17 100644 --- a/gcc/config/avr/predicates.md +++ b/gcc/config/avr/predicates.md @@ -27,6 +27,11 @@ (and (match_code "reg") (match_test "REGNO (op) >= 16 && REGNO (op) <= 31"))) +(define_predicate "scratch_or_d_register_operand" + (ior (match_operand 0 "d_register_operand") + (and (match_code ("scratch")) + (match_operand 0 "scratch_operand")))) + (define_predicate "even_register_operand" (and (match_code "reg") (and (match_test "REGNO (op) <= 31") @@ -50,6 +55,16 @@ (and (match_code "symbol_ref") (match_test "SYMBOL_REF_FLAGS (op) & SYMBOL_FLAG_IO_LOW")))) +;; Return true if OP is a register_operand or low_io_operand. +(define_predicate "reg_or_low_io_operand" + (ior (match_operand 0 "register_operand") + (and (match_code "mem") + ; Deliberately only allow QImode no matter what the mode of + ; the operand is. This effectively disallows and I/O that + ; is not QImode for that operand. + (match_test "GET_MODE (op) == QImode") + (match_test "low_io_address_operand (XEXP (op, 0), Pmode)")))) + ;; Return true if OP is a valid address for high half of I/O space. (define_predicate "high_io_address_operand" (and (match_code "const_int") @@ -86,12 +101,47 @@ (and (match_code "const_int") (match_test "op == CONST1_RTX (mode)"))) +;; Return 1 if OP is the constant integer 7 for MODE. +(define_predicate "const7_operand" + (and (match_code "const_int") + (match_test "INTVAL(op) == 7"))) + +;; Return 1 if OP is the constant integer 15 for MODE. +(define_predicate "const15_operand" + (and (match_code "const_int") + (match_test "INTVAL(op) == 15"))) + +;; Return 1 if OP is the constant integer 23 for MODE. +(define_predicate "const23_operand" + (and (match_code "const_int") + (match_test "INTVAL(op) == 23"))) + +;; Return 1 if OP is the constant integer 31 for MODE. +(define_predicate "const31_operand" + (and (match_code "const_int") + (match_test "INTVAL(op) == 31"))) + ;; Return 1 if OP is constant integer 0..7 for MODE. (define_predicate "const_0_to_7_operand" (and (match_code "const_int") (match_test "IN_RANGE (INTVAL (op), 0, 7)"))) +;; Return 1 if OP is constant integer 0..15 for MODE. +(define_predicate "const_0_to_15_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 0, 15)"))) + +;; Return 1 if OP is constant integer 0..23 for MODE. +(define_predicate "const_0_to_23_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 0, 23)"))) + +;; Return 1 if OP is constant integer 0..31 for MODE. +(define_predicate "const_0_to_31_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 0, 31)"))) + ;; Return 1 if OP is constant integer 2..7 for MODE. (define_predicate "const_2_to_7_operand" (and (match_code "const_int") |