static void sh_canonicalize_comparison (int *, rtx *, rtx *, bool);
static void sh_canonicalize_comparison (enum rtx_code&, rtx&, rtx&,
machine_mode, bool);
+static bool sh_legitimate_combined_insn (rtx_insn* insn);
+
static bool sh_fixed_condition_code_regs (unsigned int* p1, unsigned int* p2);
static void sh_init_sync_libfuncs (void) ATTRIBUTE_UNUSED;
#undef TARGET_CANONICALIZE_COMPARISON
#define TARGET_CANONICALIZE_COMPARISON sh_canonicalize_comparison
+#undef TARGET_LEGITIMATE_COMBINED_INSN
+#define TARGET_LEGITIMATE_COMBINED_INSN sh_legitimate_combined_insn
+
#undef TARGET_FIXED_CONDITION_CODE_REGS
#define TARGET_FIXED_CONDITION_CODE_REGS sh_fixed_condition_code_regs
*code = (int)tmp_code;
}
+/* This function implements the legitimate_combined_insn target hook,
+ which the combine pass uses to early reject combined insns, before
+ it tries to recog the insn and determine its cost. */
+static bool
+sh_legitimate_combined_insn (rtx_insn* insn)
+{
+ /* Reject combinations of memory loads and zero extensions, as these
+ interfere with other combine patterns such as zero extracts and bit
+ tests. The SH2A movu.{b|w} insns are formed later in the
+ 'sh_optimize_extu_exts' pass after combine/split1. */
+ rtx p = PATTERN (insn);
+ if (GET_CODE (p) == SET
+ && REG_P (XEXP (p, 0)) && GET_MODE (XEXP (p, 0)) == SImode
+ && GET_CODE (XEXP (p, 1)) == ZERO_EXTEND
+ && MEM_P (XEXP (XEXP (p, 1), 0)))
+ return false;
+
+ return true;
+}
+
bool
sh_fixed_condition_code_regs (unsigned int* p1, unsigned int* p2)
{
&& CONST_INT_P (XEXP (op1, 1)) && INTVAL (XEXP (op1, 1)) == 31)
return 1;
}
+ /* Let's assume that adding the result of an insns that stores into
+ the T bit is cheap. */
+ if (treg_set_expr (op1, SImode))
+ return 1;
+ if (treg_set_expr (op0, SImode))
+ return 1;
}
/* On SH1-4 we have only max. SImode operations.
true);
return true;
+ case IF_THEN_ELSE:
+ /* This case is required for the if_then_else negc pattern. */
+ if (treg_set_expr (XEXP (x, 0), SImode))
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ else
+ return false;
+
+ /* Zero extracts of single bits are usually combine patterns for the
+ tst insns. */
+ case ZERO_EXTRACT:
+ if (GET_CODE (XEXP (x, 0)) == XOR
+ && arith_reg_operand (XEXP (XEXP (x, 0), 0), VOIDmode)
+ && XEXP (x, 1) == const1_rtx
+ && CONST_INT_P (XEXP (x, 2))
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1))
+ /* Check that the xor constaint overlaps with the extracted bit. */
+ && (INTVAL (XEXP (XEXP (x, 0), 1)) & (1LL << INTVAL (XEXP (x, 2)))))
+ {
+ *total = 1; //COSTS_N_INSNS (1);
+ return true;
+ }
+ return false;
+
/* The cost of a sign or zero extend depends on whether the source is a
reg or a mem. In case of a mem take the address into acount. */
case SIGN_EXTEND:
- if (REG_P (XEXP (x, 0)))
+ if (arith_reg_operand (XEXP (x, 0), GET_MODE (XEXP (x, 0))))
{
*total = COSTS_N_INSNS (1);
return true;
return false;
case ZERO_EXTEND:
- if (REG_P (XEXP (x, 0)))
+ if (arith_reg_operand (XEXP (x, 0), GET_MODE (XEXP (x, 0))))
{
*total = COSTS_N_INSNS (1);
return true;
most likely going to be a TST #imm, R0 instruction.
Notice that this does not catch the zero_extract variants from
the md file. */
- if (GET_CODE (XEXP (x, 0)) == AND
- && CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) == 0)
+ if (XEXP (x, 1) == const0_rtx
+ && (GET_CODE (XEXP (x, 0)) == AND
+ || (SUBREG_P (XEXP (x, 0))
+ && GET_CODE (SUBREG_REG (XEXP (x, 0))) == AND)))
+ {
+ *total = 1;
+ return true;
+ }
+
+ else if (XEXP (x, 1) == const0_rtx
+ && GET_CODE (XEXP (x, 0)) == AND
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1))
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == ASHIFT
+ && arith_reg_operand (XEXP (XEXP (XEXP (x, 0), 0), 0), SImode)
+ && CONST_INT_P (XEXP (XEXP (XEXP (x, 0), 0), 1)))
{
*total = 1;
return true;
return true;
case AND:
+ /* Check for (and (not (reg)) (const_int 1)) which is a tst insn. */
+ if (GET_CODE (XEXP (x, 0)) == NOT && XEXP (x, 1) == const1_rtx)
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ /* Fall through. */
+
case XOR:
case IOR:
*total = COSTS_N_INSNS (and_xor_ior_costs (x, code));
/* Return true if the register operands of the specified insn are modified
between the specified from and to insns (exclusive of those two). */
-static bool
+bool
sh_insn_operands_modified_between_p (rtx_insn* operands_insn,
const rtx_insn* from,
const rtx_insn* to)
return false;
}
+/* Given an insn, determine whether it's a 'nott' insn, i.e. an insn that
+ negates the T bit and stores the result in the T bit. */
+bool
+sh_is_nott_insn (const rtx_insn* i)
+{
+ return i != NULL && GET_CODE (PATTERN (i)) == SET
+ && t_reg_operand (XEXP (PATTERN (i), 0), VOIDmode)
+ && negt_reg_operand (XEXP (PATTERN (i), 1), VOIDmode);
+}
+
+rtx
+sh_movt_set_dest (const rtx_insn* i)
+{
+ if (i == NULL)
+ return NULL;
+
+ const_rtx p = PATTERN (i);
+ return GET_CODE (p) == SET
+ && arith_reg_dest (XEXP (p, 0), SImode)
+ && t_reg_operand (XEXP (p, 1), VOIDmode) ? XEXP (p, 0) : NULL;
+}
+
+/* Given an insn, check whether it's a 'movrt' kind of insn, i.e. an insn
+ that stores the negated T bit in a register, and return the destination
+ register rtx, or null. */
+rtx
+sh_movrt_set_dest (const rtx_insn* i)
+{
+ if (i == NULL)
+ return NULL;
+
+ const_rtx p = PATTERN (i);
+
+ /* The negc movrt replacement is inside a parallel. */
+ if (GET_CODE (p) == PARALLEL)
+ p = XVECEXP (p, 0, 0);
+
+ return GET_CODE (p) == SET
+ && arith_reg_dest (XEXP (p, 0), SImode)
+ && negt_reg_operand (XEXP (p, 1), VOIDmode) ? XEXP (p, 0) : NULL;
+}
+
+/* Given an insn and a reg number, tell whether the reg dies or is unused
+ after the insn. */
+bool
+sh_reg_dead_or_unused_after_insn (const rtx_insn* i, int regno)
+{
+ return find_regno_note (i, REG_DEAD, regno) != NULL
+ || find_regno_note (i, REG_UNUSED, regno) != NULL;
+}
+
/* Given an insn and a reg number, remove reg dead or reg unused notes to
mark it as being used after the insn. */
void
}
}
+bool
+sh_extending_set_of_reg::can_use_as_unextended_reg (void) const
+{
+ if ((ext_code == SIGN_EXTEND || ext_code == ZERO_EXTEND)
+ && (from_mode == QImode || from_mode == HImode)
+ && set_src != NULL)
+ return arith_reg_operand (XEXP (set_src, 0), from_mode);
+ else
+ return false;
+}
+
+rtx
+sh_extending_set_of_reg::use_as_unextended_reg (rtx_insn* use_at_insn) const
+{
+ gcc_assert (can_use_as_unextended_reg ());
+
+ rtx r = XEXP (set_src, 0);
+ rtx r0 = simplify_gen_subreg (SImode, r, from_mode, 0);
+
+ if (modified_between_p (r, insn, use_at_insn))
+ {
+ rtx r1 = gen_reg_rtx (SImode);
+ emit_insn_after (gen_move_insn (r1, r0), insn);
+ return r1;
+ }
+ else
+ {
+ sh_remove_reg_dead_or_unused_notes (insn, SUBREG_P (r)
+ ? REGNO (SUBREG_REG (r))
+ : REGNO (r));
+ return r0;
+ }
+}
+
/* Given the current insn, which is assumed to be the *tst<mode>_t_subregs insn,
perform the necessary checks on the operands and split it accordingly. */
void
emit_insn (gen_tstsi_t (tmp0, operands[1]));
}
+/* A helper class to increment/decrement a counter variable each time a
+ function is entered/left. */
+class scope_counter
+{
+public:
+ scope_counter (int& counter) : m_counter (counter) { ++m_counter; }
+
+ ~scope_counter (void)
+ {
+ --m_counter;
+ gcc_assert (m_counter >= 0);
+ }
+
+ int count (void) const { return m_counter; }
+
+private:
+ int& m_counter;
+};
+
+/* Given an rtx x, determine whether the expression can be used to create
+ an insn that calulates x and stores the result in the T bit.
+ This is used by the 'treg_set_expr' predicate to construct insns sequences
+ where T bit results are fed into other insns, such as addc, subc, negc
+ insns.
+
+ FIXME: The patterns that expand 'treg_set_expr' operands tend to
+ distinguish between 'positive' and 'negative' forms. For now this has to
+ be done in the preparation code. We could also introduce
+ 'pos_treg_set_expr' and 'neg_treg_set_expr' predicates for that and write
+ two different patterns for the 'postive' and 'negative' forms. However,
+ the total amount of lines of code seems to be about the same and the
+ '{pos|neg}_treg_set_expr' predicates would be more expensive, because the
+ recog function would need to look inside the expression by temporarily
+ splitting it. */
+static int sh_recog_treg_set_expr_reent_count = 0;
+
+bool
+sh_recog_treg_set_expr (rtx op, machine_mode mode)
+{
+ scope_counter recursion (sh_recog_treg_set_expr_reent_count);
+
+ /* Limit the recursion count to avoid nested expressions which we can't
+ resolve to a single treg set insn. */
+ if (recursion.count () > 1)
+ return false;
+
+ /* Early accept known possible operands before doing recog. */
+ if (op == const0_rtx || op == const1_rtx || t_reg_operand (op, mode))
+ return true;
+
+ /* Early reject impossible operands before doing recog.
+ There are some (set ((t) (subreg ...))) patterns, but we must be careful
+ not to allow any invalid reg-reg or mem-reg moves, or else other passes
+ such as lower-subreg will bail out. Some insns such as SH4A movua are
+ done with UNSPEC, so must reject those, too, or else it would result
+ in an invalid reg -> treg move. */
+ if (register_operand (op, mode) || memory_operand (op, mode)
+ || sh_unspec_insn_p (op))
+ return false;
+
+ if (!can_create_pseudo_p ())
+ return false;
+
+ /* We are going to invoke recog in a re-entrant way and thus
+ have to capture its current state and restore it afterwards. */
+ recog_data_d prev_recog_data = recog_data;
+
+ rtx_insn* i = make_insn_raw (gen_rtx_SET (VOIDmode, get_t_reg_rtx (), op));
+ SET_PREV_INSN (i) = NULL;
+ SET_NEXT_INSN (i) = NULL;
+
+ int result = recog (PATTERN (i), i, 0);
+
+ /* It seems there is no insn like that. Create a simple negated
+ version and try again. If we hit a negated form, we'll allow that
+ and append a nott sequence when splitting out the insns. Insns that
+ do the split can then remove the trailing nott if they know how to
+ deal with it. */
+ if (result < 0 && GET_CODE (op) == EQ)
+ {
+ PUT_CODE (op, NE);
+ result = recog (PATTERN (i), i, 0);
+ PUT_CODE (op, EQ);
+ }
+ if (result < 0 && GET_CODE (op) == NE)
+ {
+ PUT_CODE (op, EQ);
+ result = recog (PATTERN (i), i, 0);
+ PUT_CODE (op, NE);
+ }
+
+ recog_data = prev_recog_data;
+ return result >= 0;
+}
+
+/* Returns true when recog of a 'treg_set_expr' is currently in progress.
+ This can be used as a condition for insn/split patterns to allow certain
+ T bit setting patters only to be matched as sub expressions of other
+ patterns. */
+bool
+sh_in_recog_treg_set_expr (void)
+{
+ return sh_recog_treg_set_expr_reent_count > 0;
+}
+
+/* Given an rtx x, which is assumed to be some expression that has been
+ matched by the 'treg_set_expr' predicate before, split and emit the
+ insns that are necessary to calculate the expression and store the result
+ in the T bit.
+ The splitting is done recursively similar to 'try_split' in emit-rt.c.
+ Unfortunately we can't use 'try_split' here directly, as it tries to invoke
+ 'delete_insn' which then causes the DF parts to bail out, because we
+ currently are inside another gen_split* function and would invoke
+ 'try_split' in a reentrant way. */
+static std::pair<rtx_insn*, rtx_insn*>
+sh_try_split_insn_simple (rtx_insn* i, rtx_insn* curr_insn, int n = 0)
+{
+ if (dump_file)
+ {
+ fprintf (dump_file, "sh_try_split_insn_simple n = %d i = \n", n);
+ print_rtl_single (dump_file, i);
+ fprintf (dump_file, "\n");
+ }
+
+ rtx_insn* seq = safe_as_a<rtx_insn*> (split_insns (PATTERN (i), curr_insn));
+
+ if (seq == NULL)
+ return std::make_pair (i, i);
+
+ /* Avoid infinite splitter loops if any insn of the result matches
+ the original pattern. */
+ for (rtx_insn* s = seq; s != NULL; s = NEXT_INSN (s))
+ if (INSN_P (s) && rtx_equal_p (PATTERN (s), PATTERN (i)))
+ return std::make_pair (i, i);
+
+ unshare_all_rtl_in_chain (seq);
+
+ /* 'seq' is now a replacement for 'i'. Assuming that 'i' is an insn in
+ a linked list, replace the single insn with the new insns. */
+ rtx_insn* seqlast = seq;
+ while (NEXT_INSN (seqlast) != NULL)
+ seqlast = NEXT_INSN (seqlast);
+
+ if (rtx_insn* iprev = PREV_INSN (i))
+ SET_NEXT_INSN (iprev) = seq;
+ if (rtx_insn* inext = NEXT_INSN (i))
+ SET_PREV_INSN (inext) = seqlast;
+
+ SET_PREV_INSN (seq) = PREV_INSN (i);
+ SET_NEXT_INSN (seqlast) = NEXT_INSN (i);
+
+ SET_PREV_INSN (i) = NULL;
+ SET_NEXT_INSN (i) = NULL;
+
+ /* Recursively split all insns. */
+ for (i = seq; ; i = NEXT_INSN (i))
+ {
+ std::pair<rtx_insn*, rtx_insn*> ii =
+ sh_try_split_insn_simple (i, curr_insn, n + 1);
+ if (i == seq)
+ seq = ii.first;
+ if (i == seqlast)
+ {
+ seqlast = ii.second;
+ break;
+ }
+ i = ii.first;
+ }
+
+ return std::make_pair (seq, seqlast);
+}
+
+sh_treg_insns
+sh_split_treg_set_expr (rtx x, rtx_insn* curr_insn)
+{
+ if (t_reg_operand (x, VOIDmode))
+ return sh_treg_insns ();
+
+ scope_counter in_treg_set_expr (sh_recog_treg_set_expr_reent_count);
+
+ rtx_insn* i = make_insn_raw (gen_rtx_SET (VOIDmode, get_t_reg_rtx (), x));
+ SET_PREV_INSN (i) = NULL;
+ SET_NEXT_INSN (i) = NULL;
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "split_treg_set_expr insn:\n");
+ print_rtl (dump_file, i);
+ fprintf (dump_file, "\n");
+ }
+
+ /* We are going to invoke recog/split_insns in a re-entrant way and thus
+ have to capture its current state and restore it afterwards. */
+ recog_data_d prev_recog_data = recog_data;
+
+ int insn_code = recog (PATTERN (i), i, 0);
+
+ /* If the insn was not found, see if we matched the negated form before
+ and append a nott. */
+ bool append_nott = false;
+
+ if (insn_code < 0 && GET_CODE (x) == EQ)
+ {
+ PUT_CODE (x, NE);
+ insn_code = recog (PATTERN (i), i, 0);
+ if (insn_code >= 0)
+ append_nott = true;
+ else
+ PUT_CODE (x, EQ);
+ }
+ if (insn_code < 0 && GET_CODE (x) == NE)
+ {
+ PUT_CODE (x, EQ);
+ insn_code = recog (PATTERN (i), i, 0);
+ if (insn_code >= 0)
+ append_nott = true;
+ else
+ PUT_CODE (x, NE);
+ }
+
+ gcc_assert (insn_code >= 0);
+
+ /* Try to recursively split the insn. Some insns might refuse to split
+ any further while we are in the treg_set_expr splitting phase. They
+ will be emitted as part of the outer insn and then split again. */
+ std::pair<rtx_insn*, rtx_insn*> insnlist =
+ sh_try_split_insn_simple (i, curr_insn);
+
+ /* Restore recog state. */
+ recog_data = prev_recog_data;
+
+ rtx_insn* nott_insn = sh_is_nott_insn (insnlist.second)
+ ? insnlist.second
+ : NULL;
+ if (dump_file)
+ {
+ fprintf (dump_file, "split_treg_set_expr insnlist:\n");
+ print_rtl (dump_file, insnlist.first);
+ fprintf (dump_file, "\n");
+
+ if (nott_insn != NULL)
+ fprintf (dump_file, "trailing nott insn %d\n", INSN_UID (nott_insn));
+ }
+
+ if (nott_insn != NULL && append_nott)
+ {
+ if (dump_file)
+ fprintf (dump_file, "removing trailing nott\n");
+ remove_insn (nott_insn);
+ nott_insn = NULL;
+ append_nott = false;
+ }
+
+ emit_insn (insnlist.first);
+
+ if (append_nott)
+ nott_insn = emit_insn (gen_nott (get_t_reg_rtx ()));
+
+ rtx_insn* first_insn = get_insns ();
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "resulting insns:\n");
+ print_rtl (dump_file, first_insn);
+ fprintf (dump_file, "\n");
+ }
+
+ return sh_treg_insns (first_insn, nott_insn);
+}
+
/*------------------------------------------------------------------------------
Mode switching support code.
*/
;; SImode signed integer comparisons
;; -------------------------------------------------------------------------
-;; Various patterns to generate the TST #imm, R0 instruction.
-;; Although this adds some pressure on the R0 register, it can potentially
-;; result in faster code, even if the operand has to be moved to R0 first.
-;; This is because on SH4 TST #imm, R0 and MOV Rm, Rn are both MT group
-;; instructions and thus will be executed in parallel. On SH4A TST #imm, R0
-;; is an EX group instruction but still can be executed in parallel with the
-;; MT group MOV Rm, Rn instruction.
-
-;; Usual TST #imm, R0 patterns for SI, HI and QI
-;; This is usually used for bit patterns other than contiguous bits
-;; and single bits.
-(define_insn "tstsi_t"
+;; Patterns to generate the tst instruction which are usually formed by
+;; the combine pass.
+;; The canonical form here being used is (eq (and (op) (op)) 0).
+;; For some bit patterns, such as contiguous bits, we also must accept
+;; zero_extract forms. Single bit tests are also handled via zero_extract
+;; patterns in the 'bit field extract patterns' section. All variants
+;; are eventually converted to the 'tstsi_t' insn.
+;; As long as pseudos can be created (before RA), 'tstsi_t' will also accept
+;; constants that won't fit into 8 bits. After having captured the constant
+;; we can decide better whether/how to load it into a register and do other
+;; post-combine optimizations such as bypassing sign/zero extensions.
+(define_insn_and_split "tstsi_t"
[(set (reg:SI T_REG)
- (eq:SI (and:SI (match_operand:SI 0 "logical_operand" "%z,r")
- (match_operand:SI 1 "logical_operand" "K08,r"))
- (const_int 0)))]
- "TARGET_SH1"
- "tst %1,%0"
- [(set_attr "type" "mt_group")])
-
-(define_insn "tsthi_t"
- [(set (reg:SI T_REG)
- (eq:SI (subreg:SI (and:HI (match_operand:HI 0 "logical_operand" "%z")
- (match_operand 1 "const_int_operand")) 0)
+ (eq:SI (and:SI (match_operand:SI 0 "arith_reg_operand" "%z,r")
+ (match_operand:SI 1 "arith_or_int_operand" "K08,r"))
(const_int 0)))]
"TARGET_SH1
- && CONST_OK_FOR_K08 (INTVAL (operands[1]))"
+ && (can_create_pseudo_p () || arith_reg_operand (operands[1], SImode)
+ || satisfies_constraint_K08 (operands[1]))"
"tst %1,%0"
- [(set_attr "type" "mt_group")])
-
-(define_insn "tstqi_t"
- [(set (reg:SI T_REG)
- (eq:SI (subreg:SI (and:QI (match_operand:QI 0 "logical_operand" "%z")
- (match_operand 1 "const_int_operand")) 0)
- (const_int 0)))]
- "TARGET_SH1
- && (CONST_OK_FOR_K08 (INTVAL (operands[1]))
- || CONST_OK_FOR_I08 (INTVAL (operands[1])))"
+ "TARGET_SH1 && can_create_pseudo_p () && CONST_INT_P (operands[1])
+ && !sh_in_recog_treg_set_expr ()"
+ [(const_int 0)]
{
- operands[1] = GEN_INT (INTVAL (operands[1]) & 255);
- return "tst %1,%0";
+ gcc_assert (CONST_INT_P (operands[1]));
+
+ HOST_WIDE_INT op1val = INTVAL (operands[1]);
+ bool op0_dead_after_this =
+ sh_reg_dead_or_unused_after_insn (curr_insn, REGNO (operands[0]));
+
+ if (optimize)
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "tstsi_t: trying to optimize const_int 0x%08x\n",
+ (uint32_t)op1val);
+
+ /* See if we can convert a test with a reg and a constant into
+ something simpler, if the reg is known to be zero or sign
+ extended. */
+ sh_extending_set_of_reg eop0 = sh_find_extending_set_of_reg (operands[0],
+ curr_insn);
+ if (eop0.ext_code != UNKNOWN)
+ {
+ /* Adjust the constant, trying to eliminate bits that are not
+ contributing to the result. */
+ if (eop0.from_mode == QImode)
+ op1val = (op1val
+ | (eop0.ext_code == SIGN_EXTEND && (op1val & 0xFFFFFF80)
+ ? 0x80 : 0)) & 0xFF;
+ else if (eop0.from_mode == HImode)
+ op1val = (op1val
+ | (eop0.ext_code == SIGN_EXTEND && (op1val & 0xFFFF8000)
+ ? 0x8000 : 0)) & 0xFFFF;
+
+ if (dump_file)
+ fprintf (dump_file, "tstsi_t: using effective const_int: 0x%08x\n",
+ (uint32_t)op1val);
+
+ /* Try to bypass the sign/zero extension first if op0 dies after
+ this insn. */
+ if (op0_dead_after_this && eop0.can_use_as_unextended_reg ())
+ {
+ if (dump_file)
+ fprintf (dump_file, "tstsi_t: bypassing sign/zero extension\n");
+
+ operands[0] = eop0.use_as_unextended_reg (curr_insn);
+ }
+ else if ((eop0.from_mode == QImode && op1val == 0xFF)
+ || (eop0.from_mode == HImode && op1val == 0xFFFF))
+ {
+ if (dump_file)
+ fprintf (dump_file, "tstsi_t: converting to cmpeqsi_t\n");
+ emit_insn (gen_cmpeqsi_t (eop0.use_as_extended_reg (curr_insn),
+ const0_rtx));
+ DONE;
+ }
+ else if (eop0.ext_code == SIGN_EXTEND
+ && ((eop0.from_mode == QImode && op1val == 0x80)
+ || (eop0.from_mode == HImode && op1val == 0x8000)))
+ {
+ if (dump_file)
+ fprintf (dump_file, "tstsi_t: converting to cmpgesi_t\n");
+ emit_insn (gen_cmpgesi_t (eop0.use_as_extended_reg (curr_insn),
+ const0_rtx));
+ DONE;
+ }
+ else if (!CONST_OK_FOR_K08 (op1val))
+ {
+ if (dump_file)
+ fprintf (dump_file, "tstsi_t: converting const_int to signed "
+ "value\n");
+
+ /* If here we haven't done anything yet. Convert the constant
+ to a signed value to reduce the constant pool size. */
+ operands[0] = eop0.use_as_extended_reg (curr_insn);
+
+ if (eop0.from_mode == QImode)
+ op1val |= (op1val & 0x80) ? 0xFFFFFFFFFFFFFF00LL : 0;
+ else if (eop0.from_mode == HImode)
+ op1val |= (op1val & 0x8000) ? 0xFFFFFFFFFFFF0000LL : 0;
+ }
+ else
+ operands[0] = eop0.use_as_extended_reg (curr_insn);
+ }
+ }
+
+ if (dump_file)
+ fprintf (dump_file, "tstsi_t: using const_int 0x%08x\n",
+ (uint32_t)op1val);
+
+ /* Try to fit the constant into 8 bits by shuffling the value in the
+ register operand.
+ Doing that usually results in smaller code as the constants in the
+ pools are avoided (32 bit constant = load + constant = 6 bytes).
+ However, if the constant load (LS insn) can be hoisted insn dependencies
+ can be avoided and chances for parallel execution increase. The common
+ usage pattern is:
+ - load reg from mem
+ - test bits
+ - conditional branch
+
+ FIXME: For now we do that only when optimizing for size until there is
+ a better heuristic.
+
+ FIXME: If there are multiple tst insns in the block with the same
+ constant, avoid the #imm variant to avoid R0 loads. Use the 'tst Rn,Rm'
+ variant instead and load the constant into a reg. For that we'd need
+ to do some analysis. */
+
+ if ((op1val & 0xFFFF) == 0
+ && CONST_OK_FOR_K08 (op1val >> 16) && optimize_size)
+ {
+ /* Use a swap.w insn to do a shift + reg copy (to R0) in one insn. */
+ op1val = op1val >> 16;
+ rtx r = gen_reg_rtx (SImode);
+ emit_insn (gen_rotlsi3_16 (r, operands[0]));
+ operands[0] = r;
+ }
+ else if ((op1val & 0xFF) == 0
+ && CONST_OK_FOR_K08 (op1val >> 8) && optimize_size)
+ {
+ /* Use a swap.b insn to do a shift + reg copy (to R0) in one insn. */
+ op1val = op1val >> 8;
+ rtx r = gen_reg_rtx (SImode);
+ emit_insn (gen_swapbsi2 (r, operands[0]));
+ operands[0] = r;
+ }
+ else if ((op1val & 3) == 0
+ && CONST_OK_FOR_K08 (op1val >> 2) && optimize_size)
+ {
+ op1val = op1val >> 2;
+ rtx r = gen_reg_rtx (SImode);
+ emit_insn (gen_lshrsi3_k (r, operands[0], GEN_INT (2)));
+ operands[0] = r;
+ }
+ else if ((op1val & 1) == 0
+ && CONST_OK_FOR_K08 (op1val >> 1) && optimize_size)
+ {
+ op1val = op1val >> 1;
+ rtx r = gen_reg_rtx (SImode);
+ emit_insn (gen_shlr (r, operands[0]));
+ operands[0] = r;
+ }
+
+ operands[1] = GEN_INT (op1val);
+
+ if (!satisfies_constraint_K08 (operands[1]))
+ operands[1] = force_reg (SImode, operands[1]);
+
+ emit_insn (gen_tstsi_t (operands[0], operands[1]));
+ DONE;
}
[(set_attr "type" "mt_group")])
-;; Test low QI subreg against zero.
-;; This avoids unnecessary zero extension before the test.
-(define_insn "*tstqi_t_zero"
+;; This pattern is used by combine when testing QI/HImode subregs with a
+;; negative constant. Ignore high bits by masking them out in the constant.
+(define_insn_and_split "*tst<mode>_t"
[(set (reg:SI T_REG)
- (eq:SI (match_operand:QI 0 "logical_operand" "z") (const_int 0)))]
- "TARGET_SH1"
- "tst #255,%0"
- [(set_attr "type" "mt_group")])
+ (eq:SI (subreg:SI
+ (and:QIHI (match_operand:QIHI 0 "arith_reg_operand")
+ (match_operand 1 "const_int_operand")) 0)
+ (const_int 0)))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG)
+ (eq:SI (and:SI (match_dup 0) (match_dup 1)) (const_int 0)))]
+{
+ operands[0] = simplify_gen_subreg (SImode, operands[0], <MODE>mode, 0);
+ operands[1] = GEN_INT (INTVAL (operands[1])
+ & (<MODE>mode == HImode ? 0xFFFF : 0xFF));
+})
;; This pattern might be risky because it also tests the upper bits and not
;; only the subreg. We have to check whether the operands have been sign
(const_int 0)))]
"TARGET_SH1 && TARGET_LITTLE_ENDIAN && can_create_pseudo_p ()"
"#"
- "&& 1"
+ "&& !sh_in_recog_treg_set_expr ()"
[(const_int 0)]
{
sh_split_tst_subregs (curr_insn, <MODE>mode, <lowpart_le>, operands);
(const_int 0)))]
"TARGET_SH1 && TARGET_BIG_ENDIAN && can_create_pseudo_p ()"
"#"
- "&& 1"
+ "&& !sh_in_recog_treg_set_expr ()"
[(const_int 0)]
{
sh_split_tst_subregs (curr_insn, <MODE>mode, <lowpart_be>, operands);
DONE;
})
-;; Extract LSB, negate and store in T bit.
-(define_insn "tstsi_t_and_not"
- [(set (reg:SI T_REG)
- (and:SI (not:SI (match_operand:SI 0 "logical_operand" "z"))
- (const_int 1)))]
- "TARGET_SH1"
- "tst #1,%0"
- [(set_attr "type" "mt_group")])
-
;; Extract contiguous bits and compare them against zero.
-(define_insn "tst<mode>_t_zero_extract_eq"
+;; Notice that this will not be used for single bits. Special single bit
+;; extraction patterns are in the 'bit field extract patterns' section.
+(define_insn_and_split "*tst<mode>_t_zero_extract"
[(set (reg:SI T_REG)
- (eq:SI (zero_extract:SI (match_operand:QIHISIDI 0 "logical_operand" "z")
- (match_operand:SI 1 "const_int_operand")
- (match_operand:SI 2 "const_int_operand"))
+ (eq:SI (zero_extract:SI (match_operand:QIHISI 0 "arith_reg_operand")
+ (match_operand 1 "const_int_operand")
+ (match_operand 2 "const_int_operand"))
(const_int 0)))]
- "TARGET_SH1
- && CONST_OK_FOR_K08 (ZERO_EXTRACT_ANDMASK (operands[1], operands[2]))"
-{
- operands[1] = GEN_INT (ZERO_EXTRACT_ANDMASK (operands[1], operands[2]));
- return "tst %1,%0";
-}
- [(set_attr "type" "mt_group")])
-
-;; This split is required when testing bits in a QI subreg.
-(define_split
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
[(set (reg:SI T_REG)
- (eq:SI
- (if_then_else:SI
- (zero_extract:SI (match_operand 0 "logical_operand")
- (match_operand 1 "const_int_operand")
- (match_operand 2 "const_int_operand"))
- (match_operand 3 "const_int_operand")
- (const_int 0))
- (const_int 0)))]
- "TARGET_SH1
- && ZERO_EXTRACT_ANDMASK (operands[1], operands[2]) == INTVAL (operands[3])
- && CONST_OK_FOR_K08 (INTVAL (operands[3]))"
- [(set (reg:SI T_REG) (eq:SI (and:SI (match_dup 0) (match_dup 3))
- (const_int 0)))]
+ (eq:SI (and:SI (match_dup 0) (match_dup 1)) (const_int 0)))]
{
- if (GET_MODE (operands[0]) == QImode)
- operands[0] = simplify_gen_subreg (SImode, operands[0], QImode, 0);
+ operands[1] = GEN_INT (ZERO_EXTRACT_ANDMASK (operands[1], operands[2]));
+ if (GET_MODE (operands[0]) != SImode)
+ operands[0] = simplify_gen_subreg (SImode, operands[0], <MODE>mode, 0);
})
-;; Extract single bit, negate and store it in the T bit.
-;; Not used for SH4A.
-(define_insn "tstsi_t_zero_extract_xor"
+;; Convert '(reg << shift) & mask' into 'reg & (mask >> shift)'.
+;; The shifted-out bits in the mask will always be zero, since the
+;; shifted-in bits in the reg will also be always zero.
+(define_insn_and_split "*tstsi_t_shift_mask"
[(set (reg:SI T_REG)
- (zero_extract:SI (xor:SI (match_operand:SI 0 "logical_operand" "z")
- (match_operand:SI 3 "const_int_operand"))
- (match_operand:SI 1 "const_int_operand")
- (match_operand:SI 2 "const_int_operand")))]
- "TARGET_SH1
- && ZERO_EXTRACT_ANDMASK (operands[1], operands[2]) == INTVAL (operands[3])
- && CONST_OK_FOR_K08 (INTVAL (operands[3]))"
- "tst %3,%0"
- [(set_attr "type" "mt_group")])
-
-;; Extract single bit, negate and store it in the T bit.
-;; Used for SH4A little endian.
-(define_insn "tstsi_t_zero_extract_subreg_xor_little"
- [(set (reg:SI T_REG)
- (zero_extract:SI
- (subreg:QI (xor:SI (match_operand:SI 0 "logical_operand" "z")
- (match_operand:SI 3 "const_int_operand")) 0)
- (match_operand:SI 1 "const_int_operand")
- (match_operand:SI 2 "const_int_operand")))]
- "TARGET_SH1 && TARGET_LITTLE_ENDIAN
- && ZERO_EXTRACT_ANDMASK (operands[1], operands[2])
- == (INTVAL (operands[3]) & 255)
- && CONST_OK_FOR_K08 (INTVAL (operands[3]) & 255)"
-{
- operands[3] = GEN_INT (INTVAL (operands[3]) & 255);
- return "tst %3,%0";
-}
- [(set_attr "type" "mt_group")])
-
-;; Extract single bit, negate and store it in the T bit.
-;; Used for SH4A big endian.
-(define_insn "tstsi_t_zero_extract_subreg_xor_big"
+ (eq:SI (and:SI (ashift:SI (match_operand:SI 0 "arith_reg_operand")
+ (match_operand 1 "const_int_operand"))
+ (match_operand 2 "const_int_operand"))
+ (const_int 0)))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
[(set (reg:SI T_REG)
- (zero_extract:SI
- (subreg:QI (xor:SI (match_operand:SI 0 "logical_operand" "z")
- (match_operand:SI 3 "const_int_operand")) 3)
- (match_operand:SI 1 "const_int_operand")
- (match_operand:SI 2 "const_int_operand")))]
- "TARGET_SH1 && TARGET_BIG_ENDIAN
- && ZERO_EXTRACT_ANDMASK (operands[1], operands[2])
- == (INTVAL (operands[3]) & 255)
- && CONST_OK_FOR_K08 (INTVAL (operands[3]) & 255)"
-{
- operands[3] = GEN_INT (INTVAL (operands[3]) & 255);
- return "tst %3,%0";
-}
- [(set_attr "type" "mt_group")])
+ (eq:SI (and:SI (match_dup 0) (match_dup 2)) (const_int 0)))]
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) >> INTVAL (operands[1]));
+})
(define_insn "cmpeqsi_t"
[(set (reg:SI T_REG)
cmp/eq %1,%0"
[(set_attr "type" "mt_group")])
+;; Sometimes combine fails to form the (eq (and (op) (op)) 0) tst insn.
+;; Try to fix that in the split1 pass by looking for the previous set
+;; of the tested op. Also see if there is a preceeding sign/zero
+;; extension that can be avoided.
+(define_split
+ [(set (reg:SI T_REG)
+ (eq:SI (match_operand:SI 0 "arith_reg_operand") (const_int 0)))]
+ "TARGET_SH1 && can_create_pseudo_p () && optimize
+ && !sh_in_recog_treg_set_expr ()"
+ [(set (reg:SI T_REG) (eq:SI (match_dup 0) (const_int 0)))]
+{
+ if (dump_file)
+ fprintf (dump_file, "cmpeqsi_t: trying to optimize const_int 0\n");
+
+ /* If the tested reg is not dead after this insn, it's probably used by
+ something else after the comparison. It's probably better to leave
+ it as it is. */
+ if (find_regno_note (curr_insn, REG_DEAD, REGNO (operands[0])) == NULL_RTX)
+ FAIL;
+
+ /* FIXME: Maybe also search the predecessor basic blocks to catch
+ more cases. */
+ set_of_reg op = sh_find_set_of_reg (operands[0], curr_insn,
+ prev_nonnote_insn_bb);
+
+ if (op.set_src != NULL && GET_CODE (op.set_src) == AND
+ && !sh_insn_operands_modified_between_p (op.insn, op.insn, curr_insn))
+ {
+ if (dump_file)
+ fprintf (dump_file, "cmpeqsi_t: found preceeding and in insn %d\n",
+ INSN_UID (op.insn));
+
+ if (!(arith_reg_operand (XEXP (op.set_src, 0), SImode)
+ && (arith_reg_operand (XEXP (op.set_src, 1), SImode)
+ || CONST_INT_P (XEXP (op.set_src, 1)))))
+ FAIL;
+
+ /* Assume that the operands of the andsi insn are compatible with the
+ operands of the tstsi_t insn, which is generally the case. */
+ if (dump_file)
+ fprintf (dump_file, "cmpeqsi_t: replacing with tstsi_t\n");
+ emit_insn (gen_tstsi_t (XEXP (op.set_src, 0), XEXP (op.set_src, 1)));
+ DONE;
+ }
+
+ /* Converting HImode into tests against 0xFFFF tends to increase the code
+ size, as it will create constant pool entries. Disable it for now. */
+ const bool enable_himode = false;
+
+ /* FIXME: try to keep the (eq (reg) (const_int 0)). Even if the zero
+ extended reg is used after this insn, if we know that _before_ the zero
+ extension the value was loaded via sign extending mem load, we can just
+ use the value of the mem load directly. */
+ sh_extending_set_of_reg eop = sh_find_extending_set_of_reg (operands[0],
+ curr_insn);
+
+ if (eop.ext_code != UNKNOWN
+ && (eop.from_mode == QImode || (eop.from_mode == HImode && enable_himode))
+ && eop.can_use_as_unextended_reg ()
+ && !reg_used_between_p (operands[0], eop.insn, curr_insn))
+ {
+ /* Bypass the sign/zero extension and test against the bit mask, but
+ only if it's the only use of the sign/zero extracted value.
+ Otherwise we'd be introducing new constants in the pool. */
+ if (dump_file)
+ fprintf (dump_file, "cmpeqsi_t: bypassing sign/zero extension in "
+ "insn %d and using tstsi_t\n", INSN_UID (op.insn));
+
+ emit_insn (gen_tstsi_t (
+ eop.use_as_unextended_reg (curr_insn),
+ GEN_INT (eop.from_mode == QImode ? 0xFF : 0xFFFF)));
+ DONE;
+ }
+
+ if (dump_file)
+ fprintf (dump_file, "cmpeqsi_t: nothing optimized\n");
+ FAIL;
+})
+
;; FIXME: For some reason, on SH4A and SH2A combine fails to simplify this
;; pattern by itself. What this actually does is:
;; x == 0: (1 >> 0-0) & 1 = 1
cmp/ge %1,%0"
[(set_attr "type" "mt_group")])
+;; Recombine a cmp/pz followed by a nott into a shll.
+;; On non-SH2A recombine a cmp/pz followed by a movrt into shll-movt.
+;; On SH2A cmp/pz-movrt is slightly better, as it does not mutate the input.
+(define_split
+ [(set (reg:SI T_REG)
+ (ge:SI (match_operand:SI 0 "arith_reg_operand") (const_int 0)))]
+
+ "TARGET_SH1 && can_create_pseudo_p () && optimize
+ && !sh_in_recog_treg_set_expr ()"
+ [(const_int 0)]
+{
+ if (dump_file)
+ fprintf (dump_file, "cmpgesi_t: trying to optimize for const_int 0\n");
+
+ rtx_insn* i = next_nonnote_insn_bb (curr_insn);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "cmpgesi_t: following insn is \n");
+ print_rtl_single (dump_file, i);
+ fprintf (dump_file, "\n");
+ }
+
+ if (sh_is_nott_insn (i))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "cmpgesi_t: replacing (cmp/pz, nott) with (shll)\n");
+ emit_insn (gen_shll (gen_reg_rtx (SImode), operands[0]));
+ set_insn_deleted (i);
+ DONE;
+ }
+
+ /* On non-SH2A negc is used as movrt replacement, which sets T = 1.
+ Thus we can remove it only if T is marked as dead afterwards. */
+ if (rtx dest_reg = !TARGET_SH2A
+ && sh_reg_dead_or_unused_after_insn (i, T_REG)
+ ? sh_movrt_set_dest (i) : NULL)
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "cmpgesi_t: replacing (cmp/pz, movrt) with (shll, movt)\n");
+ emit_insn (gen_shll (gen_reg_rtx (SImode), operands[0]));
+ add_reg_note (emit_insn (gen_movt (dest_reg, get_t_reg_rtx ())),
+ REG_DEAD, get_t_reg_rtx ());
+ set_insn_deleted (i);
+ DONE;
+ }
+
+ if (dump_file)
+ fprintf (dump_file, "cmpgesi_t: nothing optimized\n");
+
+ FAIL;
+})
+
;; FIXME: This is actually wrong. There is no way to literally move a
;; general reg to t reg. Luckily, it seems that this pattern will be only
;; used when the general reg is known be either '0' or '1' during combine.
;; and invert the dependent logic.
(define_insn "*negtstsi"
[(set (reg:SI T_REG) (match_operand:SI 0 "arith_reg_operand" "r"))]
- "TARGET_SH1"
+ "TARGET_SH1 && !sh_in_recog_treg_set_expr ()"
"cmp/pl %0"
[(set_attr "type" "mt_group")])
;; Some integer sign comparison patterns can be realized with the div0s insn.
;; div0s Rm,Rn T = (Rm >> 31) ^ (Rn >> 31)
-(define_insn "cmp_div0s_0"
+;;
+;; The 'cmp_div0s' pattern is our canonical form, into which all the other
+;; variations are converted. The negative forms will split into a trailing
+;; nott sequence, which will be eliminated either by the
+;; 'any_treg_expr_to_reg' pattern, or by the 'sh_treg_combine' pass.
+(define_insn "cmp_div0s"
[(set (reg:SI T_REG)
(lshiftrt:SI (xor:SI (match_operand:SI 0 "arith_reg_operand" "%r")
(match_operand:SI 1 "arith_reg_operand" "r"))
"div0s %0,%1"
[(set_attr "type" "arith")])
-(define_insn "cmp_div0s_1"
+(define_insn_and_split "*cmp_div0s_1"
[(set (reg:SI T_REG)
- (lt:SI (xor:SI (match_operand:SI 0 "arith_reg_operand" "%r")
- (match_operand:SI 1 "arith_reg_operand" "r"))
- (const_int 0)))]
- "TARGET_SH1"
- "div0s %0,%1"
- [(set_attr "type" "arith")])
-
-(define_insn_and_split "*cmp_div0s_0"
- [(set (match_operand:SI 0 "arith_reg_dest" "")
- (lshiftrt:SI (xor:SI (match_operand:SI 1 "arith_reg_operand" "")
- (match_operand:SI 2 "arith_reg_operand" ""))
- (const_int 31)))
- (clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ (xor:SI (ge:SI (match_operand:SI 0 "arith_reg_operand")
+ (const_int 0))
+ (ge:SI (match_operand:SI 1 "arith_reg_operand")
+ (const_int 0))))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(set (reg:SI T_REG)
- (lshiftrt:SI (xor:SI (match_dup 1) (match_dup 2)) (const_int 31)))
- (set (match_dup 0) (reg:SI T_REG))])
+ (lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))])
-(define_insn "*cmp_div0s_0"
+(define_insn_and_split "*cmp_div0s_2"
[(set (reg:SI T_REG)
- (eq:SI (lshiftrt:SI (match_operand:SI 0 "arith_reg_operand" "%r")
+ (eq:SI (lshiftrt:SI (match_operand:SI 0 "arith_reg_operand")
(const_int 31))
- (ge:SI (match_operand:SI 1 "arith_reg_operand" "r")
+ (ge:SI (match_operand:SI 1 "arith_reg_operand")
(const_int 0))))]
- "TARGET_SH1"
- "div0s %0,%1"
- [(set_attr "type" "arith")])
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG)
+ (lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))])
-(define_insn_and_split "*cmp_div0s_1"
- [(set (match_operand:SI 0 "arith_reg_dest" "")
- (ge:SI (xor:SI (match_operand:SI 1 "arith_reg_operand" "")
- (match_operand:SI 2 "arith_reg_operand" ""))
- (const_int 0)))
- (clobber (reg:SI T_REG))]
- "TARGET_SH1"
+(define_insn_and_split "*cmp_div0s_3"
+ [(set (reg:SI T_REG)
+ (eq:SI (ge:SI (match_operand:SI 0 "arith_reg_operand")
+ (const_int 0))
+ (ge:SI (match_operand:SI 1 "arith_reg_operand")
+ (const_int 0))))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
- "&& can_create_pseudo_p ()"
- [(const_int 0)]
-;; We have to go through the movnegt expander here which will handle the
-;; SH2A vs non-SH2A cases.
-{
- emit_insn (gen_cmp_div0s_1 (operands[1], operands[2]));
- emit_insn (gen_movnegt (operands[0], get_t_reg_rtx ()));
- DONE;
-})
+ "&& 1"
+ [(set (reg:SI T_REG)
+ (lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))
+ (set (reg:SI T_REG) (xor:SI (reg:SI T_REG) (const_int 1)))])
-(define_insn_and_split "*cmp_div0s_1"
+(define_insn_and_split "*cmp_div0s_4"
[(set (reg:SI T_REG)
- (ge:SI (xor:SI (match_operand:SI 0 "arith_reg_operand" "")
- (match_operand:SI 1 "arith_reg_operand" ""))
+ (ge:SI (xor:SI (match_operand:SI 0 "arith_reg_operand")
+ (match_operand:SI 1 "arith_reg_operand"))
(const_int 0)))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
- "&& can_create_pseudo_p ()"
- [(set (reg:SI T_REG) (lt:SI (xor:SI (match_dup 0) (match_dup 1))
- (const_int 0)))
+ "&& 1"
+ [(set (reg:SI T_REG)
+ (lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))
(set (reg:SI T_REG) (xor:SI (reg:SI T_REG) (const_int 1)))])
-(define_insn_and_split "*cmp_div0s_1"
+(define_insn_and_split "*cmp_div0s_5"
+ [(set (reg:SI T_REG)
+ (xor:SI (lshiftrt:SI (match_operand:SI 0 "arith_reg_operand")
+ (const_int 31))
+ (ge:SI (match_operand:SI 1 "arith_reg_operand")
+ (const_int 0))))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG)
+ (lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))
+ (set (reg:SI T_REG) (xor:SI (reg:SI T_REG) (const_int 1)))])
+
+(define_insn_and_split "*cmp_div0s_6"
[(set (reg:SI T_REG)
(eq:SI (lshiftrt:SI (match_operand:SI 0 "arith_reg_operand")
(const_int 31))
(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand")
(const_int 31))))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
- "&& can_create_pseudo_p ()"
- [(set (reg:SI T_REG) (lt:SI (xor:SI (match_dup 0) (match_dup 1))
- (const_int 0)))
+ "&& 1"
+ [(set (reg:SI T_REG)
+ (lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))
(set (reg:SI T_REG) (xor:SI (reg:SI T_REG) (const_int 1)))])
;; -------------------------------------------------------------------------
(label_ref (match_dup 2))
(pc)))])
-;; Compare and branch combine patterns for div0s comparisons.
-(define_insn_and_split "*cbranch_div0s"
- [(set (pc)
- (if_then_else (lt (xor:SI (match_operand:SI 0 "arith_reg_operand" "")
- (match_operand:SI 1 "arith_reg_operand" ""))
- (const_int 0))
- (label_ref (match_operand 2))
- (pc)))
- (clobber (reg:SI T_REG))]
- "TARGET_SH1"
- "#"
- "&& 1"
- [(set (reg:SI T_REG)
- (lt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 0)))
- (set (pc)
- (if_then_else (ne (reg:SI T_REG) (const_int 0))
- (label_ref (match_dup 2))
- (pc)))])
-
-(define_insn_and_split "*cbranch_div0s"
- [(set (pc)
- (if_then_else (ge (xor:SI (match_operand:SI 0 "arith_reg_operand" "")
- (match_operand:SI 1 "arith_reg_operand" ""))
- (const_int 0))
- (label_ref (match_operand 2))
- (pc)))
- (clobber (reg:SI T_REG))]
- "TARGET_SH1"
- "#"
- "&& 1"
- [(set (reg:SI T_REG)
- (lt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 0)))
- (set (pc)
- (if_then_else (eq (reg:SI T_REG) (const_int 0))
- (label_ref (match_dup 2))
- (pc)))])
-
;; -------------------------------------------------------------------------
;; SImode unsigned integer comparisons
;; -------------------------------------------------------------------------
;; A simplified version of the addc insn, where the exact value of the
;; T bit doesn't matter. This is easier for combine to pick up.
;; We allow a reg or 0 for one of the operands in order to be able to
-;; do 'reg + T' sequences. Reload will load the constant 0 into the reg
-;; as needed.
-;; FIXME: The load of constant 0 should be split out before reload, or else
-;; it will be difficult to hoist or combine the constant load.
-(define_insn "*addc"
- [(set (match_operand:SI 0 "arith_reg_dest" "=r")
- (plus:SI (plus:SI (match_operand:SI 1 "arith_reg_operand" "%0")
- (match_operand:SI 2 "arith_reg_or_0_operand" "r"))
- (match_operand:SI 3 "t_reg_operand" "")))
+;; do 'reg + T' sequences.
+(define_insn_and_split "*addc"
+ [(set (match_operand:SI 0 "arith_reg_dest")
+ (plus:SI (plus:SI (match_operand:SI 1 "arith_reg_operand")
+ (match_operand:SI 2 "arith_reg_or_0_operand"))
+ (match_operand 3 "treg_set_expr")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
- "addc %2,%0"
- [(set_attr "type" "arith")])
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(const_int 0)]
+{
+ sh_treg_insns ti = sh_split_treg_set_expr (operands[3], curr_insn);
+ if (ti.has_trailing_nott ())
+ {
+ if (operands[2] == const0_rtx)
+ {
+ /* op1 + 0 + (1 - T) = op1 + 1 - T = op1 - (-1) - T */
+ remove_insn (ti.trailing_nott ());
+ emit_insn (gen_subc (operands[0], operands[1],
+ force_reg (SImode, GEN_INT (-1))));
+ DONE;
+ }
+ else if (!TARGET_SH2A)
+ {
+ /* op1 + op2 + (1 - T) = op1 - (0 - op2 - 1) - T = op1 - ~op2 - T
+ On SH2A keep the nott insn, because nott-addc sequence doesn't
+ mutate the inputs. */
+ remove_insn (ti.trailing_nott ());
+ rtx tmp = gen_reg_rtx (SImode);
+ emit_insn (gen_one_cmplsi2 (tmp, operands[2]));
+ emit_insn (gen_subc (operands[0], operands[1], tmp));
+ DONE;
+ }
+ }
-;; Split 'reg + reg + 1' into a sett addc sequence, as it can be scheduled
-;; better, if the sett insn can be done early.
-(define_insn_and_split "*addc_r_r_1"
- [(set (match_operand:SI 0 "arith_reg_dest" "")
- (plus:SI (plus:SI (match_operand:SI 1 "arith_reg_operand" "")
- (match_operand:SI 2 "arith_reg_operand" ""))
- (const_int 1)))
+ emit_insn (gen_addc (operands[0], operands[1],
+ force_reg (SImode, operands[2])));
+ DONE;
+})
+
+(define_insn_and_split "*addc"
+ [(set (match_operand:SI 0 "arith_reg_dest")
+ (plus:SI (plus:SI (match_operand 1 "treg_set_expr")
+ (match_operand:SI 2 "arith_reg_operand"))
+ (match_operand:SI 3 "arith_reg_operand")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
- [(set (reg:SI T_REG) (const_int 1))
- (parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 1) (match_dup 2))
- (reg:SI T_REG)))
+ [(parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 2) (match_dup 3))
+ (match_dup 1)))
(clobber (reg:SI T_REG))])])
-;; Left shifts by one are usually done with an add insn to avoid T_REG
-;; clobbers. Thus addc can also be used to do something like '(x << 1) + 1'.
-(define_insn_and_split "*addc_2r_1"
+(define_insn_and_split "*addc"
[(set (match_operand:SI 0 "arith_reg_dest")
- (plus:SI (mult:SI (match_operand:SI 1 "arith_reg_operand")
- (const_int 2))
- (const_int 1)))
+ (plus:SI (match_operand 1 "treg_set_expr")
+ (plus:SI (match_operand:SI 2 "arith_reg_operand")
+ (match_operand:SI 3 "arith_reg_operand"))))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
- [(set (reg:SI T_REG) (const_int 1))
- (parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 1) (match_dup 1))
- (reg:SI T_REG)))
+ [(parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 2) (match_dup 3))
+ (match_dup 1)))
(clobber (reg:SI T_REG))])])
;; Sometimes combine will try to do 'reg + (0-reg) + 1' if the *addc pattern
;; can be scheduled much better since the load of the constant can be
;; done earlier, before any comparison insns that store the result in
;; the T bit.
+;; However, avoid things like 'reg + 1', which would expand into a
+;; 3 insn sequence, instead of add #imm8.
(define_insn_and_split "*addc_t_r"
[(set (match_operand:SI 0 "arith_reg_dest")
- (plus:SI (match_operand:SI 1 "t_reg_operand")
+ (plus:SI (match_operand 1 "treg_set_expr_not_const01")
(match_operand:SI 2 "arith_reg_operand")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
- [(parallel [(set (match_dup 0)
- (plus:SI (plus:SI (match_dup 2) (const_int 0))
- (match_dup 1)))
+ [(parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 2) (const_int 0))
+ (match_dup 1)))
(clobber (reg:SI T_REG))])])
(define_insn_and_split "*addc_r_t"
[(set (match_operand:SI 0 "arith_reg_dest")
(plus:SI (match_operand:SI 1 "arith_reg_operand")
- (match_operand:SI 2 "t_reg_operand")))
+ (match_operand 2 "treg_set_expr_not_const01")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
- [(parallel [(set (match_dup 0)
- (plus:SI (plus:SI (match_dup 1) (const_int 0))
- (match_dup 2)))
+ [(parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 1) (const_int 0))
+ (match_dup 2)))
(clobber (reg:SI T_REG))])])
-;; Use shlr-addc to do 'reg + (reg & 1)'.
-(define_insn_and_split "*addc_r_lsb"
+;; Convert '2 * reg + T' into 'reg + reg + T'.
+(define_insn_and_split "*addc_2r_t"
[(set (match_operand:SI 0 "arith_reg_dest")
- (plus:SI (and:SI (match_operand:SI 1 "arith_reg_operand")
- (const_int 1))
- (match_operand:SI 2 "arith_reg_operand")))
- (clobber (reg:SI T_REG))]
- "TARGET_SH1"
- "#"
- "&& can_create_pseudo_p ()"
- [(parallel [(set (match_dup 0) (plus:SI (reg:SI T_REG) (match_dup 2)))
- (clobber (reg:SI T_REG))])]
-{
- emit_insn (gen_shlr (gen_reg_rtx (SImode), operands[1]));
-})
-
-;; Use shlr-addc to do 'reg + reg + (reg & 1)'.
-(define_insn_and_split "*addc_r_r_lsb"
- [(set (match_operand:SI 0 "arith_reg_dest")
- (plus:SI (plus:SI (and:SI (match_operand:SI 1 "arith_reg_operand")
- (const_int 1))
- (match_operand:SI 2 "arith_reg_operand"))
- (match_operand:SI 3 "arith_reg_operand")))
- (clobber (reg:SI T_REG))]
- "TARGET_SH1"
- "#"
- "&& can_create_pseudo_p ()"
- [(parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 2) (match_dup 3))
- (reg:SI T_REG)))
- (clobber (reg:SI T_REG))])]
-{
- emit_insn (gen_shlr (gen_reg_rtx (SImode), operands[1]));
-})
-
-;; Canonicalize 'reg + (reg & 1) + reg' into 'reg + reg + (reg & 1)'.
-(define_insn_and_split "*addc_r_lsb_r"
- [(set (match_operand:SI 0 "arith_reg_dest")
- (plus:SI (and:SI (match_operand:SI 1 "arith_reg_operand")
- (const_int 1))
- (plus:SI (match_operand:SI 2 "arith_reg_operand")
- (match_operand:SI 3 "arith_reg_operand"))))
- (clobber (reg:SI T_REG))]
- "TARGET_SH1"
- "#"
- "&& can_create_pseudo_p ()"
- [(parallel [(set (match_dup 0)
- (plus:SI (plus:SI (and:SI (match_dup 1) (const_int 1))
- (match_dup 2))
- (match_dup 3)))
- (clobber (reg:SI T_REG))])])
-
-;; Canonicalize '2 * reg + (reg & 1)' into 'reg + reg + (reg & 1)'.
-(define_insn_and_split "*addc_2r_lsb"
- [(set (match_operand:SI 0 "arith_reg_dest")
- (plus:SI (and:SI (match_operand:SI 1 "arith_reg_operand")
- (const_int 1))
+ (plus:SI (match_operand 1 "treg_set_expr")
(mult:SI (match_operand:SI 2 "arith_reg_operand")
(const_int 2))))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
- "&& can_create_pseudo_p ()"
- [(parallel [(set (match_dup 0)
- (plus:SI (plus:SI (and:SI (match_dup 1) (const_int 1))
- (match_dup 2))
- (match_dup 2)))
+ "&& 1"
+ [(parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 2) (match_dup 2))
+ (match_dup 1)))
(clobber (reg:SI T_REG))])])
-;; Use shll-addc to do 'reg + ((unsigned int)reg >> 31)'.
-(define_insn_and_split "*addc_r_msb"
+(define_insn_and_split "*addc_2r_t"
[(set (match_operand:SI 0 "arith_reg_dest")
- (plus:SI (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand")
- (const_int 31))
- (match_operand:SI 2 "arith_reg_operand")))
+ (plus:SI (mult:SI (match_operand:SI 1 "arith_reg_operand")
+ (const_int 2))
+ (match_operand 2 "treg_set_expr")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
- "&& can_create_pseudo_p ()"
- [(parallel [(set (match_dup 0) (plus:SI (reg:SI T_REG) (match_dup 2)))
- (clobber (reg:SI T_REG))])]
-{
- emit_insn (gen_shll (gen_reg_rtx (SImode), operands[1]));
-})
+ "&& 1"
+ [(parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 1) (match_dup 1))
+ (match_dup 2)))
+ (clobber (reg:SI T_REG))])])
-;; Use shll-addc to do 'reg + reg + ((unsigned int)reg >> 31)'.
-(define_insn_and_split "*addc_r_r_msb"
+;; Convert '(op2 + T) - op3' into 'op2 + (-op3) + T'
+(define_insn_and_split "*addc_negreg_t"
[(set (match_operand:SI 0 "arith_reg_dest")
- (plus:SI (plus:SI (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand")
- (const_int 31))
- (match_operand:SI 2 "arith_reg_operand"))
- (match_operand:SI 3 "arith_reg_operand")))
+ (minus:SI (plus:SI (match_operand 1 "treg_set_expr")
+ (match_operand:SI 2 "arith_reg_operand"))
+ (match_operand:SI 3 "arith_reg_operand")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
- "&& can_create_pseudo_p ()"
- [(parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 2) (match_dup 3))
- (reg:SI T_REG)))
+ "&& 1"
+ [(set (match_dup 4) (neg:SI (match_dup 3)))
+ (parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 2) (match_dup 4))
+ (match_dup 1)))
(clobber (reg:SI T_REG))])]
{
- emit_insn (gen_shll (gen_reg_rtx (SImode), operands[1]));
+ operands[4] = gen_reg_rtx (SImode);
})
-;; Canonicalize '2 * reg + ((unsigned int)reg >> 31)'
-;; into 'reg + reg + (reg & 1)'.
-(define_insn_and_split "*addc_2r_msb"
- [(set (match_operand:SI 0 "arith_reg_dest")
- (plus:SI (mult:SI (match_operand:SI 1 "arith_reg_operand")
- (const_int 2))
- (lshiftrt:SI (match_operand:SI 2 "arith_reg_operand")
- (const_int 31))))
- (clobber (reg:SI T_REG))]
- "TARGET_SH1"
- "#"
- "&& can_create_pseudo_p ()"
- [(parallel [(set (match_dup 0)
- (plus:SI (plus:SI (lshiftrt:SI (match_dup 2) (const_int 31))
- (match_dup 1))
- (match_dup 1)))
- (clobber (reg:SI T_REG))])])
-
(define_expand "addsi3"
[(set (match_operand:SI 0 "arith_reg_operand" "")
(plus:SI (match_operand:SI 1 "arith_operand" "")
;; We allow a reg or 0 for one of the operands in order to be able to
;; do 'reg - T' sequences. Reload will load the constant 0 into the reg
;; as needed.
-(define_insn "*subc"
- [(set (match_operand:SI 0 "arith_reg_dest" "=r")
- (minus:SI (minus:SI (match_operand:SI 1 "arith_reg_operand" "0")
- (match_operand:SI 2 "arith_reg_or_0_operand" "r"))
- (match_operand:SI 3 "t_reg_operand" "")))
+(define_insn_and_split "*subc"
+ [(set (match_operand:SI 0 "arith_reg_dest")
+ (minus:SI (minus:SI (match_operand:SI 1 "arith_reg_operand")
+ (match_operand:SI 2 "arith_reg_or_0_operand"))
+ (match_operand 3 "treg_set_expr")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
- "subc %2,%0"
- [(set_attr "type" "arith")])
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(const_int 0)]
+{
+ sh_treg_insns ti = sh_split_treg_set_expr (operands[3], curr_insn);
+ if (ti.has_trailing_nott ())
+ {
+ if (operands[2] == const0_rtx)
+ {
+ /* op1 - (1 - T) = op1 - 1 + T = op1 + (-1) + T */
+ remove_insn (ti.trailing_nott ());
+ emit_insn (gen_addc (operands[0], operands[1],
+ force_reg (SImode, GEN_INT (-1))));
+ DONE;
+ }
+ else if (!TARGET_SH2A)
+ {
+ /* op1 - op2 - (1 - T) = op1 + (0 - op2 - 1) + T = op1 + ~op2 + T
+ On SH2A keep the nott insn, because nott-subc sequence doesn't
+ mutate the inputs. */
+ remove_insn (ti.trailing_nott ());
+ rtx tmp = gen_reg_rtx (SImode);
+ emit_insn (gen_one_cmplsi2 (tmp, operands[2]));
+ emit_insn (gen_addc (operands[0], operands[1], tmp));
+ DONE;
+ }
+ }
+
+ emit_insn (gen_subc (operands[0], operands[1],
+ force_reg (SImode, operands[2])));
+ DONE;
+})
+
+;; Convert reg - T - reg = reg - reg - T
+(define_insn_and_split "*subc"
+ [(set (match_operand:SI 0 "arith_reg_dest")
+ (minus:SI (minus:SI (match_operand:SI 1 "arith_reg_operand")
+ (match_operand 2 "treg_set_expr"))
+ (match_operand:SI 3 "arith_reg_operand")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(parallel [(set (match_dup 0)
+ (minus:SI (minus:SI (match_dup 1) (match_dup 3))
+ (match_dup 2)))
+ (clobber (reg:SI T_REG))])])
;; Split reg - reg - 1 into a sett subc sequence, as it can be scheduled
;; better, if the sett insn can be done early.
(plus:SI (not:SI (match_operand:SI 1 "arith_reg_operand" ""))
(match_operand:SI 2 "arith_reg_operand" "")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
- [(set (reg:SI T_REG) (const_int 1))
- (parallel [(set (match_dup 0)
+ [(parallel [(set (match_dup 0)
(minus:SI (minus:SI (match_dup 2) (match_dup 1))
- (reg:SI T_REG)))
+ (const_int 1)))
(clobber (reg:SI T_REG))])])
;; Split 'reg - T' into 'reg - 0 - T' to utilize the subc insn.
;; can be scheduled much better since the load of the constant can be
;; done earlier, before any comparison insns that store the result in
;; the T bit.
+;; However, avoid things like 'reg - 1', which would expand into a
+;; 3 insn sequence, instead of add #imm8.
(define_insn_and_split "*subc"
[(set (match_operand:SI 0 "arith_reg_dest" "")
(minus:SI (match_operand:SI 1 "arith_reg_operand" "")
- (match_operand:SI 2 "t_reg_operand" "")))
+ (match_operand 2 "treg_set_expr_not_const01")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(parallel [(set (match_dup 0)
(match_dup 2)))
(clobber (reg:SI T_REG))])])
+;; Convert
+;; (1 - T) - op2 = 1 - op2 - T
+(define_insn_and_split "*subc_negt_reg"
+ [(set (match_operand:SI 0 "arith_reg_dest")
+ (minus:SI (match_operand 1 "treg_set_expr_not_const01")
+ (match_operand:SI 2 "arith_reg_operand")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(const_int 0)]
+{
+ sh_treg_insns ti = sh_split_treg_set_expr (operands[1], curr_insn);
+ if (ti.remove_trailing_nott ())
+ {
+ /* (1 - T) - op2 = 1 - op2 - T */
+ emit_insn (gen_subc (operands[0],
+ force_reg (SImode, GEN_INT (1)), operands[2]));
+ }
+ else
+ {
+ /* T - op2: use movt,sub sequence. */
+ rtx tmp = gen_reg_rtx (SImode);
+ emit_insn (gen_movt (tmp, get_t_reg_rtx ()));
+ emit_insn (gen_subsi3 (operands[0], tmp, operands[2]));
+ }
+ DONE;
+})
+
+;; Convert
+;; op1 - (1 - T) + op3 = op1 - 1 + T + op3
+;; (op1 - T) + op3 = op1 - (-op3) - T
+(define_insn_and_split "*subc_negreg_t"
+ [(set (match_operand:SI 0 "arith_reg_dest")
+ (plus:SI (minus:SI (match_operand:SI 1 "arith_reg_operand")
+ (match_operand 2 "treg_set_expr"))
+ (match_operand:SI 3 "arith_reg_operand")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(const_int 0)]
+{
+ sh_treg_insns ti = sh_split_treg_set_expr (operands[2], curr_insn);
+ if (ti.remove_trailing_nott ())
+ {
+ /* op1 - (1 - T) + op3 = (op1 - 1) + op3 + T */
+ rtx tmp = gen_reg_rtx (SImode);
+ emit_insn (gen_addsi3 (tmp, operands[1], GEN_INT (-1)));
+ emit_insn (gen_addc (operands[0], tmp, operands[3]));
+ }
+ else
+ {
+ /* (op1 - T) + op3' = 'op1 - (-op3) - T */
+ rtx tmp = gen_reg_rtx (SImode);
+ emit_insn (gen_negsi2 (tmp, operands[3]));
+ emit_insn (gen_subc (operands[0], operands[1], tmp));
+ }
+ DONE;
+})
+
(define_insn "*subsi3_internal"
[(set (match_operand:SI 0 "arith_reg_dest" "=r")
(minus:SI (match_operand:SI 1 "arith_reg_operand" "0")
offset += 8 - GET_MODE_SIZE (inmode);
operands[5] = gen_rtx_SUBREG (inmode, operands[0], offset);
})
-\f
-;; -------------------------------------------------------------------------
-;; Shifts and rotates
-;; -------------------------------------------------------------------------
+\f
+;; -------------------------------------------------------------------------
+;; Shifts and rotates
+;; -------------------------------------------------------------------------
+
+;; Let combine see that we can get the MSB and LSB into the T bit
+;; via shll and shlr. This allows it to plug it into insns that can have
+;; the T bit as an input (e.g. addc).
+;; FIXME: On SH2A use bld #0,Rn instead of shlr to avoid mutating the input.
+(define_insn_and_split "*reg_lsb_t"
+ [(set (reg:SI T_REG)
+ (and:SI (match_operand:SI 0 "arith_reg_operand")
+ (const_int 1)))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(const_int 0)]
+{
+ emit_insn (gen_shlr (gen_reg_rtx (SImode), operands[0]));
+})
+
+(define_insn_and_split "*reg_msb_t"
+ [(set (reg:SI T_REG)
+ (lshiftrt:SI (match_operand:SI 0 "arith_reg_operand")
+ (const_int 31)))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(const_int 0)]
+{
+ emit_insn (gen_shll (gen_reg_rtx (SImode), operands[0]));
+})
(define_expand "rotldi3"
[(set (match_operand:DI 0 "arith_reg_dest" "")
;; directly. Otherwise we have to insert a shift in between.
(define_insn_and_split "*rotcr"
[(set (match_operand:SI 0 "arith_reg_dest")
- (ior:SI (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand")
+ (ior:SI (lshiftrt:SI (match_operand:SI 1 "arith_reg_or_0_operand")
(match_operand:SI 2 "const_int_operand"))
- (ashift:SI (match_operand:SI 3 "arith_reg_or_t_reg_operand")
+ (ashift:SI (match_operand 3 "arith_reg_or_treg_set_expr")
(const_int 31))))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
- "&& can_create_pseudo_p ()"
+ "&& 1"
[(const_int 0)]
{
+ rtx prev_set_t_insn = NULL_RTX;
+
+ if (!arith_reg_operand (operands[3], SImode))
+ {
+ sh_treg_insns ti = sh_split_treg_set_expr (operands[3], curr_insn);
+ if (!ti.was_treg_operand ())
+ prev_set_t_insn = ti.first_insn ();
+
+ operands[3] = get_t_reg_rtx ();
+
+ if (TARGET_SH2A && ti.has_trailing_nott () && operands[1] == const0_rtx)
+ {
+ /* Convert to a movrt, rotr sequence. */
+ remove_insn (ti.trailing_nott ());
+ rtx tmp = gen_reg_rtx (SImode);
+ emit_insn (gen_movnegt (tmp, get_t_reg_rtx ()));
+ emit_insn (gen_rotrsi3_1 (operands[0], tmp));
+ DONE;
+ }
+ }
+
+ if (operands[1] == const0_rtx)
+ {
+ operands[1] = gen_reg_rtx (SImode);
+ emit_insn (gen_movt (operands[1], get_t_reg_rtx ()));
+ }
+
if (INTVAL (operands[2]) > 1)
{
const rtx shift_count = GEN_INT (INTVAL (operands[2]) - 1);
- rtx prev_set_t_insn = NULL_RTX;
rtx tmp_t_reg = NULL_RTX;
/* If we're going to emit a shift sequence that clobbers the T_REG,
if (sh_lshrsi_clobbers_t_reg_p (shift_count)
&& ! sh_dynamicalize_shift_p (shift_count))
{
- prev_set_t_insn = prev_nonnote_insn_bb (curr_insn);
+ if (prev_set_t_insn == NULL)
+ prev_set_t_insn = prev_nonnote_insn_bb (curr_insn);
/* Skip the nott insn, which was probably inserted by the splitter
of *rotcr_neg_t. Don't use one of the recog functions
;; it so that it will try the pattern above.
(define_split
[(set (match_operand:SI 0 "arith_reg_dest")
- (ior:SI (ashift:SI (match_operand:SI 1 "arith_reg_or_t_reg_operand")
+ (ior:SI (ashift:SI (match_operand 1 "arith_reg_or_treg_set_expr")
(const_int 31))
- (lshiftrt:SI (match_operand:SI 2 "arith_reg_operand")
+ (lshiftrt:SI (match_operand:SI 2 "arith_reg_or_0_operand")
(match_operand:SI 3 "const_int_operand"))))]
"TARGET_SH1 && can_create_pseudo_p ()"
[(parallel [(set (match_dup 0)
[(set (match_operand:SI 0 "arith_reg_dest")
(ior:SI (ashift:SI (match_operand:SI 1 "arith_reg_operand")
(match_operand:SI 2 "const_int_operand"))
- (match_operand:SI 3 "t_reg_operand")))
+ (match_operand 3 "treg_set_expr")))
(clobber (reg:SI T_REG))]
"TARGET_SH1"
"#"
[(parallel [(set (match_dup 0)
(ior:SI (ashift:SI (match_dup 1) (match_dup 2))
(and:SI (match_dup 3) (const_int 1))))
- (clobber (reg:SI T_REG))])])
+ (clobber (reg:SI T_REG))])]
+{
+ sh_split_treg_set_expr (operands[3], curr_insn);
+ operands[3] = get_t_reg_rtx ();
+})
(define_insn_and_split "*rotcl"
[(set (match_operand:SI 0 "arith_reg_dest")
emit_insn (gen_shll (gen_reg_rtx (SImode), operands[3]));
})
+(define_insn_and_split "*rotcl"
+ [(set (match_operand:SI 0 "arith_reg_dest")
+ (ior:SI (ashift:SI (match_operand:SI 1 "arith_reg_operand")
+ (match_operand 2 "const_int_operand"))
+ (zero_extract:SI (match_operand:SI 3 "arith_reg_operand")
+ (const_int 1)
+ (match_operand 4 "const_int_operand"))))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "#"
+ "&& can_create_pseudo_p ()"
+ [(parallel [(set (match_dup 0)
+ (ior:SI (ashift:SI (match_dup 1) (match_dup 2))
+ (and:SI (match_dup 5) (const_int 1))))
+ (clobber (reg:SI T_REG))])]
+{
+ if (TARGET_SH2A && satisfies_constraint_K03 (operands[4]))
+ {
+ /* On SH2A we can use the bld insn to zero extract a single bit
+ into the T bit. */
+ operands[5] = get_t_reg_rtx ();
+ emit_insn (gen_bldsi_reg (operands[3], operands[4]));
+ }
+ else
+ {
+ /* If we can't use the bld insn we have to emit a tst + nott sequence
+ to get the extracted bit into the T bit.
+ This will probably be worse than pre-shifting the operand. */
+ operands[5] = gen_reg_rtx (SImode);
+ emit_insn (gen_lshrsi3 (operands[5], operands[3], operands[4]));
+ }
+})
+
;; rotcr combine bridge pattern which will make combine try out more
;; complex patterns.
(define_insn_and_split "*rotcr"
[(set (match_operand:SI 0 "arith_reg_dest")
- (ashift:SI (match_operand:SI 1 "t_reg_operand") (const_int 31)))]
- "TARGET_SH1"
+ (ashift:SI (match_operand 1 "treg_set_expr") (const_int 31)))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
- [(set (match_dup 0) (match_dup 1))
- (parallel [(set (match_dup 0)
- (ior:SI (lshiftrt:SI (match_dup 0) (const_int 1))
+ [(parallel [(set (match_dup 0)
+ (ior:SI (lshiftrt:SI (const_int 0) (const_int 1))
(ashift:SI (match_dup 1) (const_int 31))))
- (set (reg:SI T_REG)
- (and:SI (match_dup 0) (const_int 1)))])])
+ (clobber (reg:SI T_REG))])])
(define_insn_and_split "*rotcr"
[(set (match_operand:SI 0 "arith_reg_dest")
DONE;
})
+;; If the shift amount is changed by combine it will try to plug the
+;; use on the symbol of the library function and the PR clobber.
+(define_insn_and_split "*ashrsi2_31"
+ [(set (match_operand:SI 0 "arith_reg_dest")
+ (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand")
+ (const_int 31)))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI PR_REG))
+ (use (match_operand:SI 2 "symbol_ref_operand"))]
+ "TARGET_SH1"
+ "#"
+ "&& 1"
+ [(parallel [(set (match_dup 0) (ashiftrt:SI (match_dup 1) (const_int 31)))
+ (clobber (reg:SI T_REG))])])
+
(define_insn "ashrsi3_d"
[(set (match_operand:SI 0 "arith_reg_dest" "=r")
(ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
;; T bit doesn't matter. This is easier for combine to pick up.
;; Notice that '0 - x - 1' is the same as '~x', thus we don't specify
;; extra patterns for this case.
-(define_insn "*negc"
+(define_insn_and_split "*negc"
[(set (match_operand:SI 0 "arith_reg_dest" "=r")
(minus:SI (neg:SI (match_operand:SI 1 "arith_reg_operand" "r"))
- (match_operand:SI 2 "t_reg_operand" "")))
+ (match_operand 2 "treg_set_expr")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
- "negc %1,%0"
- [(set_attr "type" "arith")])
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(const_int 0)]
+{
+ sh_split_treg_set_expr (operands[2], curr_insn);
+ emit_insn (gen_negc (operands[0], operands[1]));
+ DONE;
+});
(define_insn "*negdi_media"
[(set (match_operand:DI 0 "arith_reg_dest" "=r")
;; the displacement value to zero. However, doing so too early results in
;; missed opportunities for other optimizations such as post-inc or index
;; addressing loads.
-;; Although the 'zero_extend_movu_operand' predicate does not allow simple
-;; register addresses (an address without a displacement, index, post-inc),
-;; zero-displacement addresses might be generated during reload, wich are
-;; simplified to simple register addresses in turn. Thus, we have to
-;; provide the Sdd and Sra alternatives in the patterns.
+;; We don't allow the zero extending loads to match during RTL expansion
+;; (see zero_extend_operand predicate), as this would pessimize other
+;; optimization opportunities such as bit extractions of unsigned mems,
+;; where the zero extraction is irrelevant. If the zero extracting mem
+;; loads are emitted early it will be more difficult to change them back
+;; to sign extending loads (which are preferred).
+;; The combine pass will also try to combine mem loads and zero extends,
+;; which is prevented by 'sh_legitimate_combined_insn'.
(define_insn "*zero_extend<mode>si2_disp_mem"
[(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
(zero_extend:SI
;; On SH the thread pointer is kept in the GBR.
;; These patterns are usually expanded from the respective built-in functions.
(define_expand "get_thread_pointersi"
- [(set (match_operand:SI 0 "register_operand") (reg:SI GBR_REG))]
+ [(set (match_operand:SI 0 "arith_reg_dest") (reg:SI GBR_REG))]
"TARGET_SH1")
;; The store_gbr insn can also be used on !TARGET_SH1 for doing TLS accesses.
(define_insn "store_gbr"
- [(set (match_operand:SI 0 "register_operand" "=r") (reg:SI GBR_REG))]
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r") (reg:SI GBR_REG))]
""
"stc gbr,%0"
[(set_attr "type" "tls_load")])
(define_expand "set_thread_pointersi"
[(set (reg:SI GBR_REG)
- (unspec_volatile:SI [(match_operand:SI 0 "register_operand")]
+ (unspec_volatile:SI [(match_operand:SI 0 "arith_reg_operand")]
UNSPECV_GBR))]
"TARGET_SH1")
(define_insn "load_gbr"
[(set (reg:SI GBR_REG)
- (unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")]
+ (unspec_volatile:SI [(match_operand:SI 0 "arith_reg_operand" "r")]
UNSPECV_GBR))]
"TARGET_SH1"
"ldc %0,gbr"
;; zero displacement for some strange reason.
(define_insn "*mov<mode>_gbr_load"
- [(set (match_operand:QIHISI 0 "register_operand" "=z")
+ [(set (match_operand:QIHISI 0 "arith_reg_dest" "=z")
(mem:QIHISI (plus:SI (reg:SI GBR_REG)
(match_operand:QIHISI 1 "gbr_displacement"))))]
"TARGET_SH1"
[(set_attr "type" "load")])
(define_insn "*mov<mode>_gbr_load"
- [(set (match_operand:QIHISI 0 "register_operand" "=z")
+ [(set (match_operand:QIHISI 0 "arith_reg_dest" "=z")
(mem:QIHISI (reg:SI GBR_REG)))]
"TARGET_SH1"
"mov.<bwl> @(0,gbr),%0"
[(set_attr "type" "load")])
(define_insn "*mov<mode>_gbr_load"
- [(set (match_operand:SI 0 "register_operand" "=z")
+ [(set (match_operand:SI 0 "arith_reg_dest" "=z")
(sign_extend:SI
(mem:QIHI (plus:SI (reg:SI GBR_REG)
(match_operand:QIHI 1 "gbr_displacement")))))]
[(set_attr "type" "load")])
(define_insn "*mov<mode>_gbr_load"
- [(set (match_operand:SI 0 "register_operand" "=z")
+ [(set (match_operand:SI 0 "arith_reg_dest" "=z")
(sign_extend:SI (mem:QIHI (reg:SI GBR_REG))))]
"TARGET_SH1"
"mov.<bw> @(0,gbr),%0"
;; Do not match this insn during or after reload because it can't be split
;; afterwards.
(define_insn_and_split "*movdi_gbr_load"
- [(set (match_operand:DI 0 "register_operand")
+ [(set (match_operand:DI 0 "arith_reg_dest")
(match_operand:DI 1 "gbr_address_mem"))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
(use (match_operand:SI 2 "arith_reg_operand" "r"))]
"TARGET_SH1"
"negc %2,%0"
- "&& 1"
+ "&& !sh_in_recog_treg_set_expr ()"
[(const_int 0)]
{
if (sh_split_movrt_negc_to_movt_xor (curr_insn, operands))
(clobber (reg:SI T_REG))]
"TARGET_SH1 && ! TARGET_SH2A"
"#"
- "&& 1"
+ "&& !sh_in_recog_treg_set_expr ()"
[(const_int 0)]
{
if (sh_split_movrt_negc_to_movt_xor (curr_insn, operands))
;; T = 0: 0x80000000 -> reg
;; T = 1: 0x7FFFFFFF -> reg
;; This works because 0 - 0x80000000 = 0x80000000.
-;;
-;; This insn must not match again after it has been split into the constant
-;; load and negc. This is accomplished by the special negc insn that
-;; has a use on the operand.
(define_insn_and_split "*mov_t_msb_neg"
[(set (match_operand:SI 0 "arith_reg_dest")
(minus:SI (const_int -2147483648) ;; 0x80000000
- (match_operand 1 "t_reg_operand")))
+ (match_operand 1 "treg_set_expr")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()"
"#"
- "&& can_create_pseudo_p ()"
- [(set (match_dup 2) (const_int -2147483648))
- (parallel [(set (match_dup 0) (minus:SI (neg:SI (match_dup 2))
- (reg:SI T_REG)))
- (clobber (reg:SI T_REG))
- (use (match_dup 2))])]
+ "&& 1"
+ [(const_int 0)]
{
- operands[2] = gen_reg_rtx (SImode);
-})
+ if (negt_reg_operand (operands[1], VOIDmode))
+ {
+ emit_insn (gen_addc (operands[0],
+ force_reg (SImode, const0_rtx),
+ force_reg (SImode, GEN_INT (2147483647))));
+ DONE;
+ }
-(define_insn "*mov_t_msb_neg_negc"
- [(set (match_operand:SI 0 "arith_reg_dest" "=r")
- (minus:SI (neg:SI (match_operand:SI 1 "arith_reg_operand" "r"))
- (match_operand:SI 2 "t_reg_operand")))
- (clobber (reg:SI T_REG))
- (use (match_dup 1))]
- "TARGET_SH1"
- "negc %1,%0"
- [(set_attr "type" "arith")])
+ sh_treg_insns ti = sh_split_treg_set_expr (operands[1], curr_insn);
+ if (ti.remove_trailing_nott ())
+ emit_insn (gen_addc (operands[0],
+ force_reg (SImode, const0_rtx),
+ force_reg (SImode, GEN_INT (2147483647))));
+ else
+ emit_insn (gen_negc (operands[0],
+ force_reg (SImode, GEN_INT (-2147483648LL))));
+ DONE;
+})
+;; 0x7fffffff + T
+;; 0x7fffffff + (1-T) = 0 - 0x80000000 - T
(define_insn_and_split "*mov_t_msb_neg"
[(set (match_operand:SI 0 "arith_reg_dest")
- (plus:SI (match_operand 1 "negt_reg_operand")
+ (plus:SI (match_operand 1 "treg_set_expr")
(const_int 2147483647))) ;; 0x7fffffff
(clobber (reg:SI T_REG))]
"TARGET_SH1"
"#"
"&& can_create_pseudo_p ()"
- [(parallel [(set (match_dup 0)
- (minus:SI (const_int -2147483648) (reg:SI T_REG)))
- (clobber (reg:SI T_REG))])])
+ [(const_int 0)]
+{
+ if (negt_reg_operand (operands[1], VOIDmode))
+ {
+ emit_insn (gen_negc (operands[0],
+ force_reg (SImode, GEN_INT (-2147483648LL))));
+ DONE;
+ }
+
+ sh_treg_insns ti = sh_split_treg_set_expr (operands[1], curr_insn);
+ if (ti.remove_trailing_nott ())
+ emit_insn (gen_negc (operands[0],
+ force_reg (SImode, GEN_INT (-2147483648LL))));
+ else
+ emit_insn (gen_addc (operands[0],
+ force_reg (SImode, const0_rtx),
+ force_reg (SImode, GEN_INT (2147483647))));
+ DONE;
+})
(define_insn_and_split "*mov_t_msb_neg"
[(set (match_operand:SI 0 "arith_reg_dest")
- (if_then_else:SI (match_operand 1 "t_reg_operand")
- (const_int 2147483647) ;; 0x7fffffff
- (const_int -2147483648))) ;; 0x80000000
+ (if_then_else:SI (match_operand 1 "treg_set_expr")
+ (match_operand 2 "const_int_operand")
+ (match_operand 3 "const_int_operand")))
(clobber (reg:SI T_REG))]
- "TARGET_SH1"
+ "TARGET_SH1 && can_create_pseudo_p ()
+ && ((INTVAL (operands[2]) == -2147483648LL
+ && INTVAL (operands[3]) == 2147483647LL)
+ || (INTVAL (operands[2]) == 2147483647LL
+ && INTVAL (operands[3]) == -2147483648LL))"
"#"
- "&& can_create_pseudo_p ()"
- [(parallel [(set (match_dup 0)
- (minus:SI (const_int -2147483648) (reg:SI T_REG)))
- (clobber (reg:SI T_REG))])])
+ "&& 1"
+ [(const_int 0)]
+{
+ sh_treg_insns ti = sh_split_treg_set_expr (operands[1], curr_insn);
+
+ if (INTVAL (operands[2]) == -2147483648LL)
+ {
+ if (ti.remove_trailing_nott ())
+ emit_insn (gen_negc (operands[0],
+ force_reg (SImode, GEN_INT (-2147483648LL))));
+ else
+ emit_insn (gen_addc (operands[0],
+ force_reg (SImode, const0_rtx),
+ force_reg (SImode, operands[3])));
+ DONE;
+ }
+ else if (INTVAL (operands[2]) == 2147483647LL)
+ {
+ if (ti.remove_trailing_nott ())
+ emit_insn (gen_addc (operands[0],
+ force_reg (SImode, const0_rtx),
+ force_reg (SImode, GEN_INT (2147483647LL))));
+ else
+ emit_insn (gen_negc (operands[0],
+ force_reg (SImode, GEN_INT (-2147483648LL))));
+ DONE;
+ }
+ else
+ gcc_unreachable ();
+})
;; The *negnegt pattern helps the combine pass to figure out how to fold
;; an explicit double T bit negation.
""
[(const_int 0)])
-;; Store T bit as all zeros or ones in a reg.
-(define_insn "mov_neg_si_t"
- [(set (match_operand:SI 0 "arith_reg_dest" "=r")
- (neg:SI (match_operand 1 "t_reg_operand" "")))]
- "TARGET_SH1"
- "subc %0,%0"
- [(set_attr "type" "arith")])
-
-;; Store negated T bit as all zeros or ones in a reg.
-;; Use the following sequence:
+;; Store (negated) T bit as all zeros or ones in a reg.
;; subc Rn,Rn ! Rn = Rn - Rn - T; T = T
;; not Rn,Rn ! Rn = 0 - Rn
-(define_split
- [(set (match_operand:SI 0 "arith_reg_dest" "")
- (neg:SI (match_operand 1 "negt_reg_operand" "")))]
+(define_insn_and_split "mov_neg_si_t"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (neg:SI (match_operand 1 "treg_set_expr")))]
"TARGET_SH1"
- [(set (match_dup 0) (neg:SI (reg:SI T_REG)))
- (set (match_dup 0) (not:SI (match_dup 0)))])
+{
+ gcc_assert (t_reg_operand (operands[1], VOIDmode));
+ return "subc %0,%0";
+}
+ "&& can_create_pseudo_p () && !t_reg_operand (operands[1], VOIDmode)"
+ [(const_int 0)]
+{
+ sh_treg_insns ti = sh_split_treg_set_expr (operands[1], curr_insn);
+ emit_insn (gen_mov_neg_si_t (operands[0], get_t_reg_rtx ()));
+
+ if (ti.remove_trailing_nott ())
+ emit_insn (gen_one_cmplsi2 (operands[0], operands[0]));
+
+ DONE;
+}
+ [(set_attr "type" "arith")])
;; The *movtt pattern eliminates redundant T bit to T bit moves / tests.
(define_insn_and_split "*movtt"
;; This requires an additional pseudo. The SH specific sh_treg_combine RTL
;; pass will look for this insn. Disallow using it if pseudos can't be
;; created.
+;; Don't split the nott inside the splitting of a treg_set_expr, or else
+;; surrounding insns might not see and recombine it. Defer the splitting
+;; of the nott until after the whole insn containing the treg_set_expr
+;; has been split.
(define_insn_and_split "nott"
[(set (reg:SI T_REG)
(xor:SI (match_operand:SI 0 "t_reg_operand") (const_int 1)))]
gcc_assert (TARGET_SH2A);
return "nott";
}
- "! TARGET_SH2A && can_create_pseudo_p ()"
+ "!TARGET_SH2A && can_create_pseudo_p () && !sh_in_recog_treg_set_expr ()"
[(set (match_dup 0) (reg:SI T_REG))
(set (reg:SI T_REG) (eq:SI (match_dup 0) (const_int 0)))]
{
DONE;
})
+;; Sometimes the T bit result of insns is needed in normal registers.
+;; Instead of open coding all the pattern variations, use the treg_set_expr
+;; predicate to match any T bit output insn and split it out after.
+;; This pattern should be below all other related patterns so that it is
+;; considered as a last resort option during matching. This allows
+;; overriding it with special case patterns.
+(define_insn_and_split "any_treg_expr_to_reg"
+ [(set (match_operand:SI 0 "arith_reg_dest")
+ (match_operand 1 "treg_set_expr"))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& !sh_in_recog_treg_set_expr ()"
+ [(const_int 0)]
+{
+ if (dump_file)
+ fprintf (dump_file, "splitting any_treg_expr_to_reg\n");
+
+ if (t_reg_operand (operands[1], VOIDmode))
+ {
+ if (dump_file)
+ fprintf (dump_file, "t_reg_operand: emitting movt\n");
+ emit_insn (gen_movt (operands[0], get_t_reg_rtx ()));
+ DONE;
+ }
+ if (negt_reg_operand (operands[1], VOIDmode))
+ {
+ if (dump_file)
+ fprintf (dump_file, "negt_reg_operand: emitting movrt\n");
+ emit_insn (gen_movnegt (operands[0], get_t_reg_rtx ()));
+ DONE;
+ }
+
+ /* If the split out insns ended with a nott, emit a movrt sequence,
+ otherwise a normal movt. */
+ sh_treg_insns ti = sh_split_treg_set_expr (operands[1], curr_insn);
+ rtx_insn* i = NULL;
+ if (ti.remove_trailing_nott ())
+ {
+ /* Emit this same insn_and_split again. However, the next time it
+ is split, it will emit the actual negc/movrt insn. This gives
+ other surrounding insns the chance to see the trailing movrt. */
+ if (dump_file)
+ fprintf (dump_file,
+ "any_treg_expr_to_reg: replacing trailing nott with movrt\n");
+ i = emit_insn (gen_any_treg_expr_to_reg (
+ operands[0], gen_rtx_XOR (SImode, get_t_reg_rtx (),
+ const1_rtx)));
+ }
+ else
+ {
+ i = emit_insn (gen_movt (operands[0], get_t_reg_rtx ()));
+ if (dump_file)
+ fprintf (dump_file, "any_treg_expr_to_reg: appending movt\n");
+ }
+
+ add_reg_note (i, REG_UNUSED, get_t_reg_rtx ());
+ DONE;
+})
+
;; -------------------------------------------------------------------------
;; Instructions to cope with inline literal tables
;; -------------------------------------------------------------------------
else if (REG_P (operands[3])
&& satisfies_constraint_M (operands[1]))
{
- emit_insn (gen_bld_reg (operands[3], const0_rtx));
+ emit_insn (gen_bldsi_reg (operands[3], const0_rtx));
emit_insn (gen_bst_m2a (operands[0], operands[2]));
DONE;
}
FAIL;
})
+;; -------------------------------------------------------------------------
+;; Extract negated single bit and zero extend it.
+;; Generally we don't care about the exact xor const_int value, as long
+;; as it contains the extracted bit. For simplicity, the pattern variations
+;; that convert everything into the primary '*neg_zero_extract_0' pattern use
+;; a xor const_int -1 value.
+
+(define_insn_and_split "*neg_zero_extract_0"
+ [(set (reg:SI T_REG)
+ (zero_extract:SI (xor:QIHISI (match_operand:QIHISI 0 "arith_reg_operand")
+ (match_operand 1 "const_int_operand"))
+ (const_int 1)
+ (match_operand 2 "const_int_operand")))]
+ "TARGET_SH1 && can_create_pseudo_p ()
+ && INTVAL (operands[1]) & (1LL << INTVAL (operands[2]))"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG) (eq:SI (and:SI (match_dup 0) (match_dup 2))
+ (const_int 0)))]
+{
+ if (INTVAL (operands[2]) == 31 && <MODE>mode == SImode)
+ {
+ /* Use cmp/pz to extract bit 31 into the T bit. */
+ emit_insn (gen_cmpgesi_t (operands[0], const0_rtx));
+ DONE;
+ }
+
+ operands[2] = GEN_INT ((1 << INTVAL (operands[2])));
+ if (GET_MODE (operands[0]) != SImode)
+ operands[0] = simplify_gen_subreg (SImode, operands[0], <MODE>mode, 0);
+})
+
+(define_insn_and_split "*neg_zero_extract_1"
+ [(set (reg:SI T_REG)
+ (and:SI (not:SI (match_operand:SI 0 "arith_reg_operand"))
+ (const_int 1)))]
+ "TARGET_SH1"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG) (zero_extract:SI (xor:SI (match_dup 0) (const_int -1))
+ (const_int 1) (const_int 0)))])
+
+;; x & (1 << n) == 0: 0x00000000 + 1 = 1
+;; x & (1 << n) != 0: 0xFFFFFFFF + 1 = 0
+(define_insn_and_split "*neg_zero_extract_2"
+ [(set (reg:SI T_REG)
+ (plus:SI (sign_extract:SI (match_operand:QIHISI 0 "arith_reg_operand")
+ (const_int 1)
+ (match_operand 1 "const_int_operand"))
+ (const_int 1)))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG) (zero_extract:SI (xor:SI (match_dup 0) (const_int -1))
+ (const_int 1) (match_dup 1)))])
+
+;; (signed)x >> 31 + 1 = (x >= 0) ^ 1
+(define_insn_and_split "*neg_zero_extract_3"
+ [(set (reg:SI T_REG)
+ (plus:SI (ashiftrt:SI (match_operand:SI 0 "arith_reg_operand")
+ (const_int 31))
+ (const_int 1)))]
+ "TARGET_SH1 && can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG) (zero_extract:SI (xor:SI (match_dup 0) (const_int -1))
+ (const_int 1) (const_int 31)))])
+
+;; This is required for some bit patterns of DImode subregs.
+;; It looks like combine gets confused by the DImode right shift and fails
+;; to simplify things.
+(define_insn_and_split "*neg_zero_extract_4"
+ [(set (reg:SI T_REG)
+ (and:SI (and:SI
+ (lshiftrt:SI (xor:SI (match_operand:SI 0 "arith_reg_operand")
+ (match_operand 1 "const_int_operand"))
+ (match_operand 2 "const_int_operand"))
+ (not:SI (ashift:SI (match_operand:SI 3 "arith_reg_operand")
+ (match_operand 4 "const_int_operand"))))
+ (const_int 1)))]
+ "TARGET_SH1 && can_create_pseudo_p ()
+ && INTVAL (operands[4]) > 0
+ && INTVAL (operands[1]) & (1LL << INTVAL (operands[2]))"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG) (zero_extract:SI (xor:SI (match_dup 0) (match_dup 1))
+ (const_int 1) (match_dup 2)))])
+
+(define_insn_and_split "*neg_zero_extract_5"
+ [(set (reg:SI T_REG)
+ (and:SI (not:SI (subreg:SI
+ (lshiftrt:DI (match_operand:DI 0 "arith_reg_operand")
+ (match_operand 1 "const_int_operand"))
+ 0))
+ (const_int 1)))]
+ "TARGET_SH1 && TARGET_LITTLE_ENDIAN && can_create_pseudo_p ()
+ && INTVAL (operands[1]) < 32"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG) (zero_extract:SI (xor:SI (match_dup 0) (const_int -1))
+ (const_int 1) (match_dup 1)))]
+{
+ operands[0] = gen_lowpart (SImode, operands[0]);
+})
+
+(define_insn_and_split "*neg_zero_extract_6"
+ [(set (reg:SI T_REG)
+ (and:SI (not:SI (subreg:SI
+ (lshiftrt:DI (match_operand:DI 0 "arith_reg_operand")
+ (match_operand 1 "const_int_operand"))
+ 4))
+ (const_int 1)))]
+ "TARGET_SH1 && TARGET_BIG_ENDIAN && can_create_pseudo_p ()
+ && INTVAL (operands[1]) < 32"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG) (zero_extract:SI (xor:SI (match_dup 0) (const_int -1))
+ (const_int 1) (match_dup 1)))]
+{
+ operands[0] = gen_lowpart (SImode, operands[0]);
+})
+
+;; -------------------------------------------------------------------------
+;; Extract single bit and zero extend it.
+;; All patterns store the result bit in the T bit, although that is not
+;; always possible to do with a single insn and a nott must be appended.
+;; The trailing nott will be optimized away in most cases. E.g. if the
+;; extracted bit is fed into a branch condition, the condition can be
+;; inverted and the nott will be eliminated.
+;; FIXME: In cases where the trailing nott can't be eliminated, try to
+;; convert it into a (not, tst) sequence, which could be better on non-SH2A.
+
+;; On SH2A the 'bld<mode>_reg' insn will be used if the bit position fits.
+(define_insn_and_split "*zero_extract_0"
+ [(set (reg:SI T_REG)
+ (zero_extract:SI (match_operand:QIHISI 0 "arith_reg_operand")
+ (const_int 1)
+ (match_operand 1 "const_int_operand")))]
+ "TARGET_SH1 && can_create_pseudo_p ()
+ && !(TARGET_SH2A && satisfies_constraint_K03 (operands[1]))"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG) (eq:SI (and:SI (match_dup 0) (match_dup 1))
+ (const_int 0)))
+ (set (reg:SI T_REG) (xor:SI (reg:SI T_REG) (const_int 1)))]
+{
+ if (INTVAL (operands[1]) == 31 && <MODE>mode == SImode)
+ {
+ emit_insn (gen_shll (gen_reg_rtx (SImode), operands[0]));
+ DONE;
+ }
+
+ operands[1] = GEN_INT (1 << INTVAL (operands[1]));
+ if (GET_MODE (operands[0]) != SImode)
+ operands[0] = simplify_gen_subreg (SImode, operands[0], <MODE>mode, 0);
+})
+
+;; This is required for some bit patterns of DImode subregs.
+;; It looks like combine gets confused by the DImode right shift and fails
+;; to simplify things.
+(define_insn_and_split "*zero_extract_1"
+ [(set (reg:SI T_REG)
+ (subreg:SI (zero_extract:DI (match_operand:SI 0 "arith_reg_operand")
+ (const_int 1)
+ (match_operand 1 "const_int_operand"))
+ 0))]
+ "TARGET_SH1 && TARGET_LITTLE_ENDIAN && can_create_pseudo_p ()
+ && INTVAL (operands[1]) < 32"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG)
+ (zero_extract:SI (match_dup 0) (const_int 1) (match_dup 1)))])
+
+(define_insn_and_split "*zero_extract_2"
+ [(set (reg:SI T_REG)
+ (subreg:SI (zero_extract:DI (match_operand:SI 0 "arith_reg_operand")
+ (const_int 1)
+ (match_operand 1 "const_int_operand"))
+ 4))]
+ "TARGET_SH1 && TARGET_BIG_ENDIAN && can_create_pseudo_p ()
+ && INTVAL (operands[1]) < 32"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG)
+ (zero_extract:SI (match_dup 0) (const_int 1) (match_dup 1)))])
+
+;; -------------------------------------------------------------------------
;; SH2A instructions for bitwise operations.
;; FIXME: Convert multiple instruction insns to insn_and_split.
;; FIXME: Use iterators to fold at least and,xor,or insn variations.
[(set_attr "length" "4,4")])
;; Store a specified bit of the LSB 8 bits of a register in the T bit.
-(define_insn "bld_reg"
- [(set (reg:SI T_REG)
- (zero_extract:SI (match_operand:SI 0 "arith_reg_operand" "r")
- (const_int 1)
- (match_operand 1 "const_int_operand" "K03")))]
- "TARGET_SH2A && satisfies_constraint_K03 (operands[1])"
- "bld %1,%0")
-
-(define_insn "*bld_regqi"
+(define_insn "bld<mode>_reg"
[(set (reg:SI T_REG)
- (zero_extract:SI (match_operand:QI 0 "arith_reg_operand" "r")
+ (zero_extract:SI (match_operand:QIHISI 0 "arith_reg_operand" "r")
(const_int 1)
(match_operand 1 "const_int_operand" "K03")))]
"TARGET_SH2A && satisfies_constraint_K03 (operands[1])"