re PR target/49263 (SH Target: underutilized "TST #imm, R0" instruction)

gcc/
	PR target/49263
	PR target/53987
	PR target/64345
	PR target/59533
	PR target/52933
	PR target/54236
	PR target/51244
	* config/sh/sh-protos.h
	(sh_extending_set_of_reg::can_use_as_unextended_reg,
	sh_extending_set_of_reg::use_as_unextended_reg,
	sh_is_nott_insn, sh_movt_set_dest, sh_movrt_set_dest, sh_is_movt_insn,
	sh_is_movrt_insn, sh_insn_operands_modified_between_p,
	sh_reg_dead_or_unused_after_insn, sh_in_recog_treg_set_expr,
	sh_recog_treg_set_expr, sh_split_treg_set_expr): New functions.
	(sh_treg_insns): New class.
	* config/sh/sh.c (TARGET_LEGITIMATE_COMBINED_INSN): Define target hook.
	(scope_counter): New class.
	(sh_legitimate_combined_insn, sh_is_nott_insn, sh_movt_set_dest,
	sh_movrt_set_dest, sh_reg_dead_or_unused_after_insn,
	sh_extending_set_of_reg::can_use_as_unextended_reg,
	sh_extending_set_of_reg::use_as_unextended_reg, sh_recog_treg_set_expr,
	sh_in_recog_treg_set_expr, sh_try_split_insn_simple,
	sh_split_treg_set_expr): New functions.
	(addsubcosts): Handle treg_set_expr.
	(sh_rtx_costs): Handle IF_THEN_ELSE and ZERO_EXTRACT.
	(sh_rtx_costs): Use arith_reg_operand in SIGN_EXTEND and ZERO_EXTEND.
	(sh_rtx_costs): Handle additional bit test patterns in EQ and AND cases.
	(sh_insn_operands_modified_between_p): Make non-static.
	* config/sh/predicates.md (zero_extend_movu_operand): Allow
	simple_mem_operand in addition to displacement_mem_operand.
	(zero_extend_operand): Don't allow zero_extend_movu_operand.
	(treg_set_expr, treg_set_expr_not_const01,
	arith_reg_or_treg_set_expr): New predicates.
	* config/sh/sh.md (tstsi_t): Use arith_reg_operand and
	arith_or_int_operand instead of logical_operand.  Convert to
	insn_and_split.  Try to optimize constant operand in splitter.
	(tsthi_t, tstqi_t): Fold into *tst<mode>_t.  Convert to insn_and_split.
	(*tstqi_t_zero): Delete.
	(*tst<mode>_t_subregs): Add !sh_in_recog_treg_set_expr split condition.
	(tstsi_t_and_not): Delete.
	(tst<mode>_t_zero_extract_eq): Rename to *tst<mode>_t_zero_extract.
	Convert to insn_and_split.
	(unnamed split, tstsi_t_zero_extract_xor,
	tstsi_t_zero_extract_subreg_xor_little,
	tstsi_t_zero_extract_subreg_xor_big): Delete.
	(*tstsi_t_shift_mask): New insn_and_split.
	(cmpeqsi_t, cmpgesi_t): Add new split for const_int 0 operands and try
	to recombine with surrounding insns when splitting.
	(*negtstsi): Add !sh_in_recog_treg_set_expr condition.
	(cmp_div0s_0, cmp_div0s_1, *cmp_div0s_0, *cmp_div0s_1): Rewrite as ...
	(cmp_div0s, *cmp_div0s_1, *cmp_div0s_2, *cmp_div0s_3, *cmp_div0s_4,
	*cmp_div0s_5, *cmp_div0s_6): ... these new insn_and_split patterns.
	(*cbranch_div0s: Delete.
	(*addc): Convert to insn_and_split.  Use treg_set_expr as 3rd operand.
	Try to recombine with surrounding insns when splitting.  Add operand
	order variants.
	(*addc_t_r, *addc_r_t): Use treg_set_expr_not_const01.
	(*addc_r_r_1, *addc_r_lsb, *addc_r_r_lsb, *addc_r_lsb_r, *addc_r_msb,
	*addc_r_r_msb, *addc_2r_msb): Delete.
	(*addc_2r_lsb): Rename to *addc_2r_t.  Use treg_set_expr.  Add operand
	order variant.
	(*addc_negreg_t): New insn_and_split.
	(*subc): Convert to insn_and_split.  Use treg_set_expr as 3rd operand.
	Try to recombine with surrounding insns when splitting.
	Add operand order variants.  
	(*subc_negt_reg, *subc_negreg_t, *reg_lsb_t, *reg_msb_t): New
	insn_and_split patterns.
	(*rotcr): Use arith_reg_or_treg_set_expr.  Try to recombine with
	surrounding insns when splitting.
	(unnamed rotcr split): Use arith_reg_or_treg_set_expr.
	(*rotcl): Likewise.  Add zero_extract variant.
	(*ashrsi2_31): New insn_and_split.
	(*negc): Convert to insn_and_split.  Use treg_set_expr.
	(*zero_extend<mode>si2_disp_mem): Update comment.
	(movrt_negc, *movrt_negc, nott): Add !sh_in_recog_treg_set_expr split
	condition.
	(*mov_t_msb_neg, mov_neg_si_t): Use treg_set_expr.  Try to recombine
	with surrounding insns when splitting.
	(any_treg_expr_to_reg): New insn_and_split.
	(*neg_zero_extract_0, *neg_zero_extract_1, *neg_zero_extract_2,
	*neg_zero_extract_3, *neg_zero_extract_4, *neg_zero_extract_5,
	*neg_zero_extract_6, *zero_extract_0, *zero_extract_1,
	*zero_extract_2): New single bit zero extract patterns.
	(bld_reg, *bld_regqi): Fold into bld<mode>_reg.
	(*get_thread_pointersi, store_gbr, *mov<mode>_gbr_load,
	*mov<mode>_gbr_load, *mov<mode>_gbr_load, *mov<mode>_gbr_load,
	*movdi_gbr_load): Use arith_reg_dest instead of register_operand for
	set destination.
	(set_thread_pointersi, load_gbr): Use arith_reg_operand instead of
	register_operand for set source.

gcc/testsuite/
	PR target/49263
	PR target/53987
	PR target/64345
	PR target/59533
	PR target/52933
	PR target/54236
	PR target/51244
	* gcc.target/sh/pr64345-1.c: New.
	* gcc.target/sh/pr64345-2.c: New.
	* gcc.target/sh/pr59533-1.c: New.
	* gcc.target/sh/pr49263.c: Adjust matching of expected insns.
	* gcc.target/sh/pr52933-2.c: Likewise.
	* gcc.target/sh/pr54089-1.c: Likewise.
	* gcc.target/sh/pr54236-1.c: Likewise.
	* gcc.target/sh/pr51244-20-sh2a.c: Likewise.
	* gcc.target/sh/pr49263-1.c: Remove xfails.
	* gcc.target/sh/pr49263-2.c: Likewise.
	* gcc.target/sh/pr49263-3.c: Likewise.
	* gcc.target/sh/pr53987-1.c: Likewise.
	* gcc.target/sh/pr52933-1.c: Adjust matching of expected insns.
	(test_24, test_25, test_26, test_27, test_28, test_29, test_30): New.
	* gcc.target/sh/pr51244-12.c: Adjust matching of expected insns.
	(test05, test06, test07, test08, test09, test10, test11, test12): New.
	* gcc.target/sh/pr54236-3.c: Adjust matching of expected insns.
	(test_002, test_003, test_004, test_005, test_006, test_007, test_008,
	test_009): New.
	* gcc.target/sh/pr51244-4.c: Adjust matching of expected insns.
	(test_02): New.

From-SVN: r220081
This commit is contained in:
Oleg Endo 2015-01-24 13:04:53 +00:00
parent 19bd4ebce2
commit 841dbf801d
22 changed files with 2411 additions and 537 deletions

View file

@ -1,3 +1,96 @@
2015-01-24 Oleg Endo <olegendo@gcc.gnu.org>
PR target/49263
PR target/53987
PR target/64345
PR target/59533
PR target/52933
PR target/54236
PR target/51244
* config/sh/sh-protos.h
(sh_extending_set_of_reg::can_use_as_unextended_reg,
sh_extending_set_of_reg::use_as_unextended_reg,
sh_is_nott_insn, sh_movt_set_dest, sh_movrt_set_dest, sh_is_movt_insn,
sh_is_movrt_insn, sh_insn_operands_modified_between_p,
sh_reg_dead_or_unused_after_insn, sh_in_recog_treg_set_expr,
sh_recog_treg_set_expr, sh_split_treg_set_expr): New functions.
(sh_treg_insns): New class.
* config/sh/sh.c (TARGET_LEGITIMATE_COMBINED_INSN): Define target hook.
(scope_counter): New class.
(sh_legitimate_combined_insn, sh_is_nott_insn, sh_movt_set_dest,
sh_movrt_set_dest, sh_reg_dead_or_unused_after_insn,
sh_extending_set_of_reg::can_use_as_unextended_reg,
sh_extending_set_of_reg::use_as_unextended_reg, sh_recog_treg_set_expr,
sh_in_recog_treg_set_expr, sh_try_split_insn_simple,
sh_split_treg_set_expr): New functions.
(addsubcosts): Handle treg_set_expr.
(sh_rtx_costs): Handle IF_THEN_ELSE and ZERO_EXTRACT.
(sh_rtx_costs): Use arith_reg_operand in SIGN_EXTEND and ZERO_EXTEND.
(sh_rtx_costs): Handle additional bit test patterns in EQ and AND cases.
(sh_insn_operands_modified_between_p): Make non-static.
* config/sh/predicates.md (zero_extend_movu_operand): Allow
simple_mem_operand in addition to displacement_mem_operand.
(zero_extend_operand): Don't allow zero_extend_movu_operand.
(treg_set_expr, treg_set_expr_not_const01,
arith_reg_or_treg_set_expr): New predicates.
* config/sh/sh.md (tstsi_t): Use arith_reg_operand and
arith_or_int_operand instead of logical_operand. Convert to
insn_and_split. Try to optimize constant operand in splitter.
(tsthi_t, tstqi_t): Fold into *tst<mode>_t. Convert to insn_and_split.
(*tstqi_t_zero): Delete.
(*tst<mode>_t_subregs): Add !sh_in_recog_treg_set_expr split condition.
(tstsi_t_and_not): Delete.
(tst<mode>_t_zero_extract_eq): Rename to *tst<mode>_t_zero_extract.
Convert to insn_and_split.
(unnamed split, tstsi_t_zero_extract_xor,
tstsi_t_zero_extract_subreg_xor_little,
tstsi_t_zero_extract_subreg_xor_big): Delete.
(*tstsi_t_shift_mask): New insn_and_split.
(cmpeqsi_t, cmpgesi_t): Add new split for const_int 0 operands and try
to recombine with surrounding insns when splitting.
(*negtstsi): Add !sh_in_recog_treg_set_expr condition.
(cmp_div0s_0, cmp_div0s_1, *cmp_div0s_0, *cmp_div0s_1): Rewrite as ...
(cmp_div0s, *cmp_div0s_1, *cmp_div0s_2, *cmp_div0s_3, *cmp_div0s_4,
*cmp_div0s_5, *cmp_div0s_6): ... these new insn_and_split patterns.
(*cbranch_div0s: Delete.
(*addc): Convert to insn_and_split. Use treg_set_expr as 3rd operand.
Try to recombine with surrounding insns when splitting. Add operand
order variants.
(*addc_t_r, *addc_r_t): Use treg_set_expr_not_const01.
(*addc_r_r_1, *addc_r_lsb, *addc_r_r_lsb, *addc_r_lsb_r, *addc_r_msb,
*addc_r_r_msb, *addc_2r_msb): Delete.
(*addc_2r_lsb): Rename to *addc_2r_t. Use treg_set_expr. Add operand
order variant.
(*addc_negreg_t): New insn_and_split.
(*subc): Convert to insn_and_split. Use treg_set_expr as 3rd operand.
Try to recombine with surrounding insns when splitting.
Add operand order variants.
(*subc_negt_reg, *subc_negreg_t, *reg_lsb_t, *reg_msb_t): New
insn_and_split patterns.
(*rotcr): Use arith_reg_or_treg_set_expr. Try to recombine with
surrounding insns when splitting.
(unnamed rotcr split): Use arith_reg_or_treg_set_expr.
(*rotcl): Likewise. Add zero_extract variant.
(*ashrsi2_31): New insn_and_split.
(*negc): Convert to insn_and_split. Use treg_set_expr.
(*zero_extend<mode>si2_disp_mem): Update comment.
(movrt_negc, *movrt_negc, nott): Add !sh_in_recog_treg_set_expr split
condition.
(*mov_t_msb_neg, mov_neg_si_t): Use treg_set_expr. Try to recombine
with surrounding insns when splitting.
(any_treg_expr_to_reg): New insn_and_split.
(*neg_zero_extract_0, *neg_zero_extract_1, *neg_zero_extract_2,
*neg_zero_extract_3, *neg_zero_extract_4, *neg_zero_extract_5,
*neg_zero_extract_6, *zero_extract_0, *zero_extract_1,
*zero_extract_2): New single bit zero extract patterns.
(bld_reg, *bld_regqi): Fold into bld<mode>_reg.
(*get_thread_pointersi, store_gbr, *mov<mode>_gbr_load,
*mov<mode>_gbr_load, *mov<mode>_gbr_load, *mov<mode>_gbr_load,
*movdi_gbr_load): Use arith_reg_dest instead of register_operand for
set destination.
(set_thread_pointersi, load_gbr): Use arith_reg_operand instead of
register_operand for set source.
2015-01-23 Jan Hubicka <hubicka@ucw.cz>
* i386.opt (prefetch_sse): New targetsave.

View file

@ -456,17 +456,17 @@
;; Returns 1 if the operand can be used in an SH2A movu.{b|w} insn.
(define_predicate "zero_extend_movu_operand"
(and (match_operand 0 "displacement_mem_operand")
(match_test "GET_MODE (op) == QImode || GET_MODE (op) == HImode")))
(and (ior (match_operand 0 "displacement_mem_operand")
(match_operand 0 "simple_mem_operand"))
(ior (match_test "GET_MODE (op) == QImode")
(match_test "GET_MODE (op) == HImode"))))
;; Returns 1 if the operand can be used in a zero_extend.
(define_predicate "zero_extend_operand"
(ior (and (match_test "TARGET_SHMEDIA")
(match_operand 0 "general_extend_operand"))
(and (match_test "! TARGET_SHMEDIA")
(match_operand 0 "arith_reg_operand"))
(and (match_test "TARGET_SH2A")
(match_operand 0 "zero_extend_movu_operand"))))
(match_operand 0 "arith_reg_operand"))))
;; Returns 1 if OP can be source of a simple move operation. Same as
;; general_operand, but a LABEL_REF is valid, PRE_DEC is invalid as
@ -1152,6 +1152,18 @@
(match_test "mode != HImode")
(match_test "TARGET_SH4A"))))
;; A predicate that matches any expression for which there is an
;; insn pattern that sets the T bit.
(define_predicate "treg_set_expr"
(match_test "sh_recog_treg_set_expr (op, mode)"))
;; Same as treg_set_expr but disallow constants 0 and 1 which can be loaded
;; into the T bit.
(define_predicate "treg_set_expr_not_const01"
(and (match_test "op != const0_rtx")
(match_test "op != const1_rtx")
(match_operand 0 "treg_set_expr")))
;; A predicate describing the T bit register in any form.
(define_predicate "t_reg_operand"
(match_code "reg,subreg,sign_extend,zero_extend")
@ -1206,6 +1218,10 @@
(ior (match_operand 0 "arith_reg_operand")
(match_operand 0 "t_reg_operand")))
(define_predicate "arith_reg_or_treg_set_expr"
(ior (match_operand 0 "arith_reg_operand")
(match_operand 0 "treg_set_expr")))
;; A predicate describing the negated value of the T bit register shifted
;; left by 31.
(define_predicate "negt_reg_shl31_operand"

View file

@ -264,6 +264,14 @@ struct sh_extending_set_of_reg : public set_of_reg
ext_code = UNKNOWN;
}
/* Returns true if it's possible to use the source reg of the sign
or zero extending set directly, bypassing the extension. */
bool can_use_as_unextended_reg (void) const;
/* Returns the reg rtx of the sign or zero extending set source, that can
be safely used at the specified insn in SImode. */
rtx use_as_unextended_reg (rtx_insn* use_at_insn) const;
/* Returns the reg rtx of the sign or zero extending result, that can be
safely used at the specified insn in SImode. If the set source is an
implicitly sign extending mem load, the mem load is converted into an
@ -281,7 +289,66 @@ extern bool sh_split_movrt_negc_to_movt_xor (rtx_insn* curr_insn,
extern void sh_split_tst_subregs (rtx_insn* curr_insn,
machine_mode subreg_mode, int subreg_offset,
rtx operands[]);
extern bool sh_is_nott_insn (const rtx_insn* i);
extern rtx sh_movt_set_dest (const rtx_insn* i);
extern rtx sh_movrt_set_dest (const rtx_insn* i);
inline bool sh_is_movt_insn (const rtx_insn* i)
{
return sh_movt_set_dest (i) != NULL;
}
inline bool sh_is_movrt_insn (const rtx_insn* i)
{
return sh_movrt_set_dest (i) != NULL;
}
extern bool sh_insn_operands_modified_between_p (rtx_insn* operands_insn,
const rtx_insn* from,
const rtx_insn* to);
extern bool sh_reg_dead_or_unused_after_insn (const rtx_insn* i, int regno);
extern void sh_remove_reg_dead_or_unused_notes (rtx_insn* i, int regno);
extern bool sh_in_recog_treg_set_expr (void);
extern bool sh_recog_treg_set_expr (rtx op, machine_mode mode);
/* Result value of sh_split_treg_set_expr. Contains the first insn emitted
and the optional trailing nott insn. */
class sh_treg_insns
{
public:
sh_treg_insns (void) : m_first_insn (NULL), m_trailing_nott_insn (NULL) { }
sh_treg_insns (rtx_insn* first_insn, rtx_insn* nott_insn)
: m_first_insn (first_insn),
m_trailing_nott_insn (nott_insn)
{ }
bool was_treg_operand (void) const { return m_first_insn == NULL; }
bool has_trailing_nott (void) const { return m_trailing_nott_insn != NULL; }
rtx_insn* trailing_nott (void) const { return m_trailing_nott_insn; }
rtx_insn* first_insn (void) const { return m_first_insn; }
/* If there is a trailing nott, remove it from the emitted insns and
return true. Return false otherwise. */
bool
remove_trailing_nott (void)
{
if (!has_trailing_nott ())
return false;
remove_insn (trailing_nott ());
return true;
}
private:
rtx_insn* m_first_insn;
rtx_insn* m_trailing_nott_insn;
};
extern sh_treg_insns sh_split_treg_set_expr (rtx x, rtx_insn* curr_insn);
#endif /* RTX_CODE */
extern void sh_cpu_cpp_builtins (cpp_reader* pfile);

View file

@ -364,6 +364,8 @@ static bool sequence_insn_p (rtx_insn *);
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;
@ -672,6 +674,9 @@ static const struct attribute_spec sh_attribute_table[] =
#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
@ -2044,6 +2049,26 @@ sh_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
*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)
{
@ -3330,6 +3355,12 @@ addsubcosts (rtx x)
&& 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.
@ -3445,10 +3476,36 @@ sh_rtx_costs (rtx x, int code, int outer_code, int opno ATTRIBUTE_UNUSED,
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;
@ -3463,7 +3520,7 @@ sh_rtx_costs (rtx x, int code, int outer_code, int opno ATTRIBUTE_UNUSED,
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;
@ -3555,8 +3612,21 @@ sh_rtx_costs (rtx x, int code, int outer_code, int opno ATTRIBUTE_UNUSED,
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;
@ -3622,6 +3692,14 @@ sh_rtx_costs (rtx x, int code, int outer_code, int opno ATTRIBUTE_UNUSED,
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));
@ -13751,7 +13829,7 @@ sh_unspec_insn_p (rtx x)
/* 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)
@ -13770,6 +13848,57 @@ sh_insn_operands_modified_between_p (rtx_insn* operands_insn,
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
@ -14006,6 +14135,40 @@ sh_extending_set_of_reg::use_as_extended_reg (rtx_insn* use_at_insn) const
}
}
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
@ -14059,6 +14222,276 @@ sh_split_tst_subregs (rtx_insn* curr_insn, machine_mode subreg_mode,
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.
*/

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,34 @@
2015-01-24 Oleg Endo <olegendo@gcc.gnu.org>
PR target/49263
PR target/53987
PR target/64345
PR target/59533
PR target/52933
PR target/54236
PR target/51244
* gcc.target/sh/pr64345-1.c: New.
* gcc.target/sh/pr64345-2.c: New.
* gcc.target/sh/pr59533-1.c: New.
* gcc.target/sh/pr49263.c: Adjust matching of expected insns.
* gcc.target/sh/pr52933-2.c: Likewise.
* gcc.target/sh/pr54089-1.c: Likewise.
* gcc.target/sh/pr54236-1.c: Likewise.
* gcc.target/sh/pr51244-20-sh2a.c: Likewise.
* gcc.target/sh/pr49263-1.c: Remove xfails.
* gcc.target/sh/pr49263-2.c: Likewise.
* gcc.target/sh/pr49263-3.c: Likewise.
* gcc.target/sh/pr53987-1.c: Likewise.
* gcc.target/sh/pr52933-1.c: Adjust matching of expected insns.
(test_24, test_25, test_26, test_27, test_28, test_29, test_30): New.
* gcc.target/sh/pr51244-12.c: Adjust matching of expected insns.
(test05, test06, test07, test08, test09, test10, test11, test12): New.
* gcc.target/sh/pr54236-3.c: Adjust matching of expected insns.
(test_002, test_003, test_004, test_005, test_006, test_007, test_008,
test_009): New.
* gcc.target/sh/pr51244-4.c: Adjust matching of expected insns.
(test_02): New.
2015-01-24 Tom de Vries <tom@codesourcery.com>
* gcc.target/i386/funcspec-5.c: Replace avx512 with avx512f.

View file

@ -3,9 +3,9 @@
/* { dg-do compile } */
/* { dg-options "-O2" } */
/* { dg-final { scan-assembler-not "and" } } */
/* { dg-final { scan-assembler-not "bclr" { xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "extu" 1 { xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "exts" 1 { xfail *-*-* } } } */
/* { dg-final { scan-assembler-not "bclr" } } */
/* { dg-final { scan-assembler-times "extu" 1 } } */
/* { dg-final { scan-assembler-times "exts" 1 } } */
#define make_func(__valtype__, __valget__, __tstval__, __suff__)\
int test_imm_##__tstval__##__suff__ (__valtype__ val) \

View file

@ -3,13 +3,12 @@
/* { dg-do compile } */
/* { dg-options "-O2" } */
/* { dg-final { scan-assembler-not "and" } } */
/* { dg-final { scan-assembler-not "exts" { xfail *-*-* } } } */
/* { dg-final { scan-assembler-not "exts" } } */
/* { dg-final { scan-assembler-times "tst\t#127,r0" 2 } } */
/* { dg-final { scan-assembler-times "tst\t#255,r0" 1 { xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "65407" 1 { xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "-129" 2 { xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "extu" 1 { xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "tst\t#255,r0" 1 } } */
/* { dg-final { scan-assembler-times "65407" 1 } } */
/* { dg-final { scan-assembler-times "-129" 2 } } */
/* { dg-final { scan-assembler-times "extu" 1 } } */
int
test_00 (unsigned char x)

View file

@ -2,18 +2,18 @@
is shifted by a constant amount. */
/* { dg-do compile } */
/* { dg-options "-O2" } */
/* { dg-final { scan-assembler-not "and|shl|sha|exts" { xfail *-*-* } } } */
/* { dg-final { scan-assembler-not "and|shl|sha|exts" } } */
/* { dg-final { scan-assembler-times "tst\t#7,r0" 3 { xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "tst\t#12,r0" 1 { xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "tst\t#24,r0" 6 { xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "tst\t#13,r0" 3 { xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "tst\t#242,r0" 3 { xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "tst\t#7,r0" 3 } } */
/* { dg-final { scan-assembler-times "tst\t#12,r0" 1 } } */
/* { dg-final { scan-assembler-times "tst\t#24,r0" 6 } } */
/* { dg-final { scan-assembler-times "tst\t#13,r0" 3 } } */
/* { dg-final { scan-assembler-times "tst\t#242,r0" 3 } } */
/* { dg-final { scan-assembler-times "tst\t#252,r0" 1 } } */
/* { dg-final { scan-assembler-times "tst\t#64,r0" 6 { target { ! sh2a } xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "tst\t#64,r0" 4 { target { sh2a } xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "bld\t#6" 2 { target { sh2a } xfail *-*-* } } } */
/* { dg-final { scan-assembler-times "tst\t#64,r0" 6 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#64,r0" 4 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "bld\t#6" 2 { target { sh2a } } } } */
int
test_00 (unsigned char* x, int y, int z)

View file

@ -5,6 +5,8 @@
/* { dg-do compile } */
/* { dg-options "-O2" } */
/* { dg-final { scan-assembler-not "and" } } */
/* { dg-final { scan-assembler-not "extu" } } */
/* { dg-final { scan-assembler-not "exts" } } */
#define make_func(__valtype__, __valget__, __tstval__, __suff__)\
int test_imm_##__tstval__##__suff__ (__valtype__ val) \

View file

@ -4,8 +4,9 @@
/* { dg-do compile } */
/* { dg-options "-O1" } */
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */
/* { dg-final { scan-assembler-times "negc" 10 } } */
/* { dg-final { scan-assembler-not "movrt|#-1|add|sub" } } */
/* { dg-final { scan-assembler-times "negc" 15 } } */
/* { dg-final { scan-assembler-times "addc" 3 } } */
/* { dg-final { scan-assembler-not "movrt|#-1|add\t|sub\t|movt" } } */
int
test00 (int a, int b, int* x)
@ -66,3 +67,56 @@ test04_inv (int a)
{
return ((a & 0x55) != 0) ? 0x80000000 : 0x7FFFFFFF;
}
int
test05 (int a, int b)
{
/* 1x addc */
return a != b ? 0x7FFFFFFF : 0x80000000;
}
int
test06 (char a)
{
return ((a & 0x03) == 0) ? 0x7FFFFFFF : 0x80000000;
}
int
test07 (char a)
{
return ((a & 0x80) == 0) ? 0x7FFFFFFF : 0x80000000;
}
int
test08 (char a)
{
return ((a & 1) == 0) ? 0x7FFFFFFF : 0x80000000;
}
int
test09 (int a)
{
/* 1x cmp/pz, 1x addc */
return a < 0 ? 0x7FFFFFFF : 0x80000000;
}
int
test10 (int a)
{
/* 1x cmp/pz, 1x negc */
return a >= 0 ? 0x7FFFFFFF : 0x80000000;
}
int
test11 (int a)
{
/* 1x cmp/pl, 1x negc */
return a > 0 ? 0x7FFFFFFF : 0x80000000;
}
int
test12 (int a)
{
/* 1x cmp/pl, 1x addc */
return a <= 0 ? 0x7FFFFFFF : 0x80000000;
}

View file

@ -4,8 +4,8 @@
/* { dg-options "-O2" } */
/* { dg-skip-if "" { "sh*-*-*" } { "*" } { "-m2a*" } } */
/* { dg-final { scan-assembler-times "tst" 6 } } */
/* { dg-final { scan-assembler-times "movt" 1 } } */
/* { dg-final { scan-assembler-times "nott" 1 } } */
/* { dg-final { scan-assembler-not "movt" } } */
/* { dg-final { scan-assembler-times "nott" 2 } } */
/* { dg-final { scan-assembler-times "cmp/eq" 2 } } */
/* { dg-final { scan-assembler-times "cmp/hi" 4 } } */
/* { dg-final { scan-assembler-times "cmp/gt" 3 } } */

View file

@ -1,19 +1,31 @@
/* Check that storing the (negated) T bit as all ones or zeros in a reg
uses the subc instruction. On SH2A a sequence with the movrt instruction
is also OK instead of subc. */
uses the subc instruction. */
/* { dg-do compile } */
/* { dg-options "-O1 -mbranch-cost=2" } */
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */
/* { dg-final { scan-assembler-not "movt|tst|negc" } } */
/* { dg-final { scan-assembler "subc|movrt|neg|not" } } */
/* { dg-final { scan-assembler-not "movt|tst|negc|movrt" } } */
/* { dg-final { scan-assembler-times "subc" 3 } } */
/* { dg-final { scan-assembler-times "not\t" 1 } } */
/* { dg-final { scan-assembler-times "shll" 1 } } */
/* { dg-final { scan-assembler-not "cmp/gt" } } */
int test_00 (int x, int y)
int
test_00 (int x, int y)
{
/* 1x subc, 1x not */
return x != y ? -1 : 0;
}
int test_01 (int x, int y)
int
test_01 (int x, int y)
{
/* 1x subc */
return x == y ? -1 : 0;
}
int
test_02 (int x)
{
/* 1x shll, 1x subc */
return 0 <= x ? 0 : -1;
}

View file

@ -5,8 +5,13 @@
/* { dg-do compile } */
/* { dg-options "-O2" } */
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */
/* { dg-final { scan-assembler-times "div0s" 25 } } */
/* { dg-final { scan-assembler-times "div0s" 32 } } */
/* { dg-final { scan-assembler-not "tst" } } */
/* { dg-final { scan-assembler-not "not\t" } } */
/* { dg-final { scan-assembler-not "nott" } } */
/* { dg-final { scan-assembler-times "negc" 9 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "movrt" 9 { target { sh2a } } } } */
typedef unsigned char bool;
@ -166,3 +171,45 @@ test_23 (int a, int b, int c, int d)
/* Should emit 2x div0s. */
return ((a < 0) == (b < 0)) | ((c < 0) == (d < 0));
}
bool
test_24 (int a, int b)
{
return a >= 0 != b >= 0;
}
bool
test_25 (int a, int b)
{
return !(a < 0 != b < 0);
}
int
test_26 (int a, int b, int c, int d)
{
return a >= 0 != b >= 0 ? c : d;
}
int
test_27 (int a, int b)
{
return a >= 0 == b >= 0;
}
int
test_28 (int a, int b, int c, int d)
{
return a >= 0 == b >= 0 ? c : d;
}
int
test_29 (int a, int b)
{
return ((a >> 31) ^ (b >= 0)) & 1;
}
int
test_30 (int a, int b)
{
return ((a >> 31) ^ (b >> 31)) & 1;
}

View file

@ -6,7 +6,12 @@
/* { dg-do compile } */
/* { dg-options "-O2 -mpretend-cmove" } */
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */
/* { dg-final { scan-assembler-times "div0s" 25 } } */
/* { dg-final { scan-assembler-times "div0s" 32 } } */
/* { dg-final { scan-assembler-not "tst" } } */
/* { dg-final { scan-assembler-not "not\t" } } */
/* { dg-final { scan-assembler-not "nott" } } */
/* { dg-final { scan-assembler-times "negc" 9 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "movrt" 9 { target { sh2a } } } } */
#include "pr52933-1.c"

View file

@ -6,7 +6,7 @@
/* { dg-final { scan-assembler-not "exts.b" } } */
/* { dg-final { scan-assembler-not "exts.w" } } */
/* { dg-final { scan-assembler-not "movu" } } */
/* { dg-final { scan-assembler-not "tst\t#255" { xfail *-*-*} } } */
/* { dg-final { scan-assembler-not "tst\t#255" } } */
int
test_00 (unsigned char* x, char* xx, int y, int z)

View file

@ -4,6 +4,8 @@
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */
/* { dg-final { scan-assembler-times "rotcr" 24 } } */
/* { dg-final { scan-assembler-times "shll\t" 1 } } */
/* { dg-final { scan-assembler-not "and\t#1" } } */
/* { dg-final { scan-assembler-not "cmp/pl" } } */
typedef char bool;

View file

@ -7,8 +7,12 @@
/* { dg-final { scan-assembler-times "addc" 4 } } */
/* { dg-final { scan-assembler-times "subc" 3 } } */
/* { dg-final { scan-assembler-times "sett" 5 } } */
/* { dg-final { scan-assembler-times "negc" 1 } } */
/* { dg-final { scan-assembler-not "movt" } } */
/* { dg-final { scan-assembler-times "negc" 2 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-not "movt" { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "bld" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "movt" 1 { target { sh2a } } } } */
int
test_00 (int a, int b, int c, int d)
@ -64,7 +68,8 @@ test_07 (int *vec)
{
/* Must not see a 'sett' or 'addc' here.
This is a case where combine tries to produce
'a + (0 - b) + 1' out of 'a - b + 1'. */
'a + (0 - b) + 1' out of 'a - b + 1'.
On non-SH2A there is a 'tst + negc', on SH2A a 'bld + movt'. */
int z = vec[0];
int vi = vec[1];
int zi = vec[2];

View file

@ -2,11 +2,13 @@
If everything works as expected we won't see any movt instructions in
these cases. */
/* { dg-do compile } */
/* { dg-options "-O1" } */
/* { dg-options "-O2" } */
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */
/* { dg-final { scan-assembler-times "addc" 1 } } */
/* { dg-final { scan-assembler-times "subc" 1 } } */
/* { dg-final { scan-assembler-not "movt" } } */
/* { dg-final { scan-assembler-times "addc" 4 } } */
/* { dg-final { scan-assembler-times "subc" 5 } } */
/* { dg-final { scan-assembler-times "movt" 1 } } */
/* { dg-final { scan-assembler-times "sub\t" 1 } } */
/* { dg-final { scan-assembler-times "neg\t" 2 } } */
int
test_000 (int* x, unsigned int c)
@ -29,3 +31,66 @@ test_001 (int* x, unsigned int c)
s -= ! (x[i] & 0x3000);
return s;
}
int
test_002 (int a, int b, int c)
{
/* 1x tst, 1x subc */
return ((a & b) != 0) - c;
}
int
test_003 (int a, int b, int c)
{
/* 1x tst, 1x movt, 1x sub */
return ((a & b) == 0) - c;
}
int
test_004 (int a, int b, int c)
{
/* 1x tst, 1x addc */
return c - ((a & b) != 0);
}
int
test_005 (int a, int b, int c)
{
/* 1x shll, 1x subc */
int x = a < 0;
return c - (b + x);
}
int
test_006 (int a, int b, int c)
{
/* 1x neg, 1x cmp/pl, 1x addc */
int x = a > 0;
int y = b + x;
return y - c;
}
int
test_007 (int a, int b, int c)
{
/* 1x add #-1, 1x cmp/eq, 1x addc */
int x = a != 1;
int y = b - x;
return c + y;
}
int
test_008 (int a, int b, int c)
{
/* 1x neg, 1x cmp/gt, 1x subc */
int x = a > 1;
int y = b - x;
return c + y;
}
int
test_009 (int a, int b, int c, int d)
{
/* 1x div0s, 1x subc */
return c - d - (a < 0 != b < 0);
}

View file

@ -0,0 +1,185 @@
/* Check that the cmp/pz instruction is generated as expected. */
/* { dg-do compile } */
/* { dg-options "-O1" } */
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */
/* { dg-final { scan-assembler-times "shll" 1 } } */
/* { dg-final { scan-assembler-times "movt" 5 } } */
/* { dg-final { scan-assembler-times "rotcl" 1 } } */
/* { dg-final { scan-assembler-times "and" 3 } } */
/* { dg-final { scan-assembler-times "extu.b" 5 } } */
/* { dg-final { scan-assembler-times "cmp/pz" 22 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "addc" 3 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "subc" 12 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "cmp/pz" 20 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "addc" 5 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "subc" 10 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "bld" 2 { target { sh2a } } } } */
int
test_00 (unsigned char* a)
{
/* 1x cmp/pz, 1x movt */
return a[0] < 128;
}
int
test_01 (unsigned char* a)
{
/* 1x cmp/pz, 1x addc */
return a[0] + (a[0] < 128);
}
int
test_02 (unsigned char* a)
{
/* 1x cmp/pz, 1x addc */
return a[0] + ((a[0] & 0x80) == 0);
}
int
test_03 (unsigned char* a)
{
/* 1x cmp/pz, 1x subc
SH2A: 1x bld, 1x addc */
return a[0] + (a[0] > 127);
}
int
test_04 (unsigned char* a)
{
/* 1x cmp/pz, 1x subc
SH2A: 1x bld, 1x addc */
return a[0] + ((a[0] & 0x80) != 0);
}
int
test_05 (unsigned char* a, int b, int c)
{
/* 1x cmp/pz */
if (a[0] < 128)
return c;
else
return b + 50;
}
unsigned int
test_06 (unsigned int a)
{
/* 1x cmp/pz, 1x movt */
return ~a >> 31;
}
int
test_07 (unsigned short* a)
{
/* 1x cmp/pz */
return a[0] < 32768;
}
int
test_08 (unsigned short* a)
{
/* 1x cmp/pz, 1x addc */
return a[0] + (a[0] < 32768);
}
unsigned int
test_09 (unsigned int a)
{
/* 1x cmp/pz, 1x movt */
return (a >> 31) ^ 1;
}
unsigned int
test_10 (unsigned int a, unsigned int b)
{
/* 1x cmp/pz, 1x rotcl */
return (a << 1) | ((a >> 31) ^ 1);
}
unsigned int
test_11 (int x)
{
/* 1x cmp/pz, 1x subc */
return ~(x >> 31);
}
unsigned int
test_12 (int x)
{
/* 1x cmp/pz, 1x subc */
return 0xFFFFFFFF - (x >> 31);
}
unsigned int
test_13 (int x)
{
/* 1x cmp/pz, 1x subc, 1x add */
return ~(x >> 31) << 1;
}
unsigned int
test_14 (int x)
{
/* 1x cmp/pz, 1x subc */
return ~(x >> 31) >> 1;
}
unsigned int
test_15 (int x)
{
/* 1x cmp/pz, 1x subc */
return ~(x >> 31) >> 31;
}
unsigned int
test_16 (int x)
{
/* 1x cmp/pz, 1x subc, 1x and */
return ~(x >> 31) & 0xFF000000;
}
unsigned int
test_17 (int x)
{
/* 1x cmp/pz, 1x subc, 1x and */
return ~(x >> 31) & 0x00FF0000;
}
unsigned int
test_18 (int x)
{
/* 1x cmp/pz, 1x subc, 1x and */
return ~(x >> 31) & 0x0000FF00;
}
unsigned int
test_19 (int x)
{
/* 1x cmp/pz, 1x subc, 1x extu.b */
return ~(x >> 31) & 0x000000FF;
}
unsigned int
test_20 (int x, unsigned int y, unsigned int z)
{
/* 1x shll */
return ~(x >> 31) ? y : z;
}
int
test_21 (int x)
{
/* 1x cmp/pz, 1x subc */
return x >= 0 ? 0xFFFFFFFF : 0;
}
int
test_22 (int x)
{
/* 1x cmp/pz, 1x movt */
return (x >> 31) + 1;
}

View file

@ -0,0 +1,97 @@
/* Verify that single bit zero extractions emit the expected
insns sequences. */
/* { dg-do compile } */
/* { dg-options "-O2" } */
/* { dg-final { scan-assembler-not "exts|extu|sha|shld|subc|xor" } } */
/* { dg-final { scan-assembler-times "tst" 716 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "cmp/pz" 6 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "shll\t" 6 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "shlr\t" 8 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "tst" 442 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "bld" 276 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "cmp/pz" 6 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "shll\t" 4 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "shlr\t" 8 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "and\t#1" 32 } } */
#define make_func(type,shift)\
int test_##type##_##_shift_##shift##_0 (type x)\
{\
return ((x >> shift) ^ 1) & 1 ? -40 : -10;\
}\
int test_##type##_##_shift_##shift##_1 (type x)\
{\
return ((x >> shift) ^ 0) & 1 ? -40 : -10;\
}\
int test_##type##_##_shift_##shift##_2 (type x)\
{\
return ((x >> shift) ^ 1) & 1;\
}\
int test_##type##_##_shift_##shift##_3 (type x)\
{\
return ((x >> shift) ^ 0) & 1;\
}\
int test_##type##_##_shift_##shift##_4 (type x)\
{\
return (x & (1 << shift)) == 0;\
}\
int test_##type##_##_shift_##shift##_5 (type x)\
{\
return (x & (1 << shift)) != 0;\
}\
\
int test_##type##_##_shift_##shift##_6 (type* x)\
{\
return ((*x >> shift) ^ 1) & 1 ? -40 : -10;\
}\
int test_##type##_##_shift_##shift##_7 (type* x)\
{\
return ((*x >> shift) ^ 0) & 1 ? -40 : -10;\
}\
int test_##type##_##_shift_##shift##_8 (type* x)\
{\
return ((*x >> shift) ^ 1) & 1;\
}\
int test_##type##_##_shift_##shift##_9 (type* x)\
{\
return ((*x >> shift) ^ 0) & 1;\
}\
int test_##type##_##_shift_##shift##_10 (type* x)\
{\
return (*x & (1 << shift)) == 0;\
}\
int test_##type##_##_shift_##shift##_11 (type* x)\
{\
return (*x & (1 << shift)) != 0;\
}
#define make_funcs(type)\
make_func (type, 0)\
make_func (type, 1)\
make_func (type, 2)\
make_func (type, 3)\
make_func (type, 4)\
make_func (type, 5)\
make_func (type, 6)\
make_func (type, 7)
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef signed short int16_t;
typedef unsigned short uint16_t;
typedef signed int int32_t;
typedef unsigned int uint32_t;
typedef signed long long int64_t;
typedef unsigned long long uint64_t;
make_funcs (int8_t)
make_funcs (uint8_t)
make_funcs (int16_t)
make_funcs (uint16_t)
make_funcs (int32_t)
make_funcs (uint32_t)
make_funcs (int64_t)
make_funcs (uint64_t)

View file

@ -0,0 +1,116 @@
/* Verify that the TST insn is used to extract a zero extended
single bit into the T bit (for a following conditional branch) and into
a GP register. */
/* { dg-do compile } */
/* { dg-options "-O2" } */
/* { dg-final { scan-assembler-not "exts|extu|sha|shld|subc|xor" } } */
/* { dg-final { scan-assembler-times "tst" 61 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#1," 1 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#2" 2 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#4" 2 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#8" 2 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#16" 2 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#32" 2 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#64" 2 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#128" 2 { target { ! sh2a } } } } */
/* { dg-final { scan-assembler-times "tst" 54 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#1," 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#2" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#4" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#8" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#16" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#32" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#64" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "tst\t#128" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "bld\t#1," 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "bld\t#2" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "bld\t#3" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "bld\t#4" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "bld\t#5" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "bld\t#6" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "bld\t#7" 1 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "and\t#1" 1 } } */
/* { dg-final { scan-assembler-times "cmp/pz" 1 } } */
/* { dg-final { scan-assembler-times "shll" 1 } } */
typedef struct
{
unsigned int b31 : 1;
unsigned int b30 : 1;
unsigned int b29 : 1;
unsigned int b28 : 1;
unsigned int b27 : 1;
unsigned int b26 : 1;
unsigned int b25 : 1;
unsigned int b24 : 1;
unsigned int b23 : 1;
unsigned int b22 : 1;
unsigned int b21 : 1;
unsigned int b20 : 1;
unsigned int b19 : 1;
unsigned int b18 : 1;
unsigned int b17 : 1;
unsigned int b16 : 1;
unsigned int b15 : 1;
unsigned int b14 : 1;
unsigned int b13 : 1;
unsigned int b12 : 1;
unsigned int b11 : 1;
unsigned int b10 : 1;
unsigned int b9 : 1;
unsigned int b8 : 1;
unsigned int b7 : 1;
unsigned int b6 : 1;
unsigned int b5 : 1;
unsigned int b4 : 1;
unsigned int b3 : 1;
unsigned int b2 : 1;
unsigned int b1 : 1;
unsigned int b0 : 1;
} S;
#define make_funcs(bitpos)\
unsigned int test_b##bitpos##_0 (S s)\
{\
return s.b##bitpos;\
}\
unsigned int test_b##bitpos##_1 (S s)\
{\
return !s.b##bitpos;\
}\
make_funcs (0)
make_funcs (1)
make_funcs (2)
make_funcs (3)
make_funcs (4)
make_funcs (5)
make_funcs (6)
make_funcs (7)
make_funcs (8)
make_funcs (9)
make_funcs (10)
make_funcs (11)
make_funcs (12)
make_funcs (13)
make_funcs (14)
make_funcs (15)
make_funcs (16)
make_funcs (17)
make_funcs (18)
make_funcs (19)
make_funcs (20)
make_funcs (21)
make_funcs (22)
make_funcs (23)
make_funcs (24)
make_funcs (25)
make_funcs (26)
make_funcs (27)
make_funcs (28)
make_funcs (29)
make_funcs (30)
make_funcs (31)