aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/avr
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2023-06-21 11:04:04 -0700
committerIan Lance Taylor <iant@golang.org>2023-06-21 11:04:04 -0700
commit97e31a0a2a2d2273687fcdb4e5416aab1a2186e1 (patch)
treed5c1cae4de436a0fe54a5f0a2a197d309f3d654c /gcc/config/avr
parent6612f4f8cb9b0d5af18ec69ad04e56debc3e6ced (diff)
parent577223aebc7acdd31e62b33c1682fe54a622ae27 (diff)
downloadgcc-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.md22
-rw-r--r--gcc/config/avr/avr-passes.def20
-rw-r--r--gcc/config/avr/avr-protos.h10
-rw-r--r--gcc/config/avr/avr.cc1314
-rw-r--r--gcc/config/avr/avr.md1739
-rw-r--r--gcc/config/avr/avr.opt4
-rw-r--r--gcc/config/avr/constraints.md25
-rw-r--r--gcc/config/avr/gen-avr-mmcu-specs.cc2
-rw-r--r--gcc/config/avr/predicates.md50
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")