re PR target/51244 ([SH] Inefficient conditional branch and code around T bit)
PR target/51244 * config/sh/sh.md (negsi_cond, negdi_cond, stack_protect_test): Remove get_t_reg_rtx when invoking gen_branch_true or gen_branch_false. (*zero_extend<mode>si2_compact): Convert to insn_and_split. Convert zero extensions of T bit stores to reg moves in splitter. Remove obsolete unnamed peephole2 that caught zero extensions after negc T bit stores. (*branch_true_eq, *branch_false_ne): Delete. (branch_true, branch_false): Convert insn to expander. Move actual insn logic to... (*cbranch_t): ...this new insn_and_split. Try to find preceding redundant T bit stores and tests and combine them with the conditional branch if possible in the splitter. (movrt_xor, *movt_movrt): New insn_and_split. * config/sh/predicates.md (cbranch_treg_value): New predicate. * config/sh/sh-protos.h (sh_eval_treg_value): Forward declare... * config/sh/sh.c (sh_eval_treg_value): ...this new function. (expand_cbranchsi4, expand_cbranchdi4): Remove get_t_reg_rtx when invoking gen_branch_true or gen_branch_false. PR target/51244 * gcc.target/sh/pr51244-13.c: New. * gcc.target/sh/pr51244-14.c: New. * gcc.target/sh/pr51244-15.c: New. * gcc.target/sh/pr51244-16.c: New. From-SVN: r192387
This commit is contained in:
parent
76a2a3f738
commit
b4eca9c8df
@ -1,3 +1,25 @@
|
||||
2012-10-12 Oleg Endo <olegendo@gcc.gnu.org>
|
||||
|
||||
PR target/51244
|
||||
* config/sh/sh.md (negsi_cond, negdi_cond, stack_protect_test): Remove
|
||||
get_t_reg_rtx when invoking gen_branch_true or gen_branch_false.
|
||||
(*zero_extend<mode>si2_compact): Convert to insn_and_split. Convert
|
||||
zero extensions of T bit stores to reg moves in splitter. Remove
|
||||
obsolete unnamed peephole2 that caught zero extensions after negc T bit
|
||||
stores.
|
||||
(*branch_true_eq, *branch_false_ne): Delete.
|
||||
(branch_true, branch_false): Convert insn to expander. Move actual
|
||||
insn logic to...
|
||||
(*cbranch_t): ...this new insn_and_split. Try to find preceding
|
||||
redundant T bit stores and tests and combine them with the conditional
|
||||
branch if possible in the splitter.
|
||||
(movrt_xor, *movt_movrt): New insn_and_split.
|
||||
* config/sh/predicates.md (cbranch_treg_value): New predicate.
|
||||
* config/sh/sh-protos.h (sh_eval_treg_value): Forward declare...
|
||||
* config/sh/sh.c (sh_eval_treg_value): ...this new function.
|
||||
(expand_cbranchsi4, expand_cbranchdi4): Remove get_t_reg_rtx
|
||||
when invoking gen_branch_true or gen_branch_false.
|
||||
|
||||
2012-10-11 Uros Bizjak <ubizjak@gmail.com>
|
||||
|
||||
* config/alpha/alpha.md (IMODE): New mode iterator.
|
||||
|
@ -1048,6 +1048,14 @@
|
||||
}
|
||||
})
|
||||
|
||||
;; A predicate that returns true if OP is a valid construct around the T bit
|
||||
;; that can be used as an operand for conditional branches.
|
||||
(define_predicate "cbranch_treg_value"
|
||||
(match_code "eq,ne,reg,subreg,xor,sign_extend,zero_extend")
|
||||
{
|
||||
return sh_eval_treg_value (op) >= 0;
|
||||
})
|
||||
|
||||
;; Returns true of OP is arith_reg_operand or t_reg_operand.
|
||||
(define_predicate "arith_reg_or_t_reg_operand"
|
||||
(ior (match_operand 0 "arith_reg_operand")
|
||||
|
@ -162,6 +162,7 @@ extern bool sh_cfun_trap_exit_p (void);
|
||||
extern void sh_canonicalize_comparison (enum rtx_code&, rtx&, rtx&,
|
||||
enum machine_mode mode = VOIDmode);
|
||||
extern rtx sh_find_equiv_gbr_addr (rtx cur_insn, rtx mem);
|
||||
extern int sh_eval_treg_value (rtx op);
|
||||
#endif /* RTX_CODE */
|
||||
|
||||
extern void sh_cpu_cpp_builtins (cpp_reader* pfile);
|
||||
|
@ -2059,7 +2059,7 @@ prepare_cbranch_operands (rtx *operands, enum machine_mode mode,
|
||||
void
|
||||
expand_cbranchsi4 (rtx *operands, enum rtx_code comparison, int probability)
|
||||
{
|
||||
rtx (*branch_expander) (rtx, rtx) = gen_branch_true;
|
||||
rtx (*branch_expander) (rtx) = gen_branch_true;
|
||||
comparison = prepare_cbranch_operands (operands, SImode, comparison);
|
||||
switch (comparison)
|
||||
{
|
||||
@ -2071,7 +2071,7 @@ expand_cbranchsi4 (rtx *operands, enum rtx_code comparison, int probability)
|
||||
emit_insn (gen_rtx_SET (VOIDmode, get_t_reg_rtx (),
|
||||
gen_rtx_fmt_ee (comparison, SImode,
|
||||
operands[1], operands[2])));
|
||||
rtx jump = emit_jump_insn (branch_expander (operands[3], get_t_reg_rtx ()));
|
||||
rtx jump = emit_jump_insn (branch_expander (operands[3]));
|
||||
if (probability >= 0)
|
||||
add_reg_note (jump, REG_BR_PROB, GEN_INT (probability));
|
||||
}
|
||||
@ -2123,7 +2123,7 @@ expand_cbranchdi4 (rtx *operands, enum rtx_code comparison)
|
||||
if (TARGET_CMPEQDI_T)
|
||||
{
|
||||
emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
|
||||
emit_jump_insn (gen_branch_true (operands[3], get_t_reg_rtx ()));
|
||||
emit_jump_insn (gen_branch_true (operands[3]));
|
||||
return true;
|
||||
}
|
||||
msw_skip = NE;
|
||||
@ -2150,7 +2150,7 @@ expand_cbranchdi4 (rtx *operands, enum rtx_code comparison)
|
||||
if (TARGET_CMPEQDI_T)
|
||||
{
|
||||
emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
|
||||
emit_jump_insn (gen_branch_false (operands[3], get_t_reg_rtx ()));
|
||||
emit_jump_insn (gen_branch_false (operands[3]));
|
||||
return true;
|
||||
}
|
||||
msw_taken = NE;
|
||||
@ -2281,6 +2281,43 @@ expand_cbranchdi4 (rtx *operands, enum rtx_code comparison)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Given an operand, return 1 if the evaluated operand plugged into an
|
||||
if_then_else will result in a branch_true, 0 if branch_false, or
|
||||
-1 if neither nor applies. The truth table goes like this:
|
||||
|
||||
op | cmpval | code | result
|
||||
---------+--------+---------+--------------------
|
||||
T (0) | 0 | EQ (1) | 0 = 0 ^ (0 == 1)
|
||||
T (0) | 1 | EQ (1) | 1 = 0 ^ (1 == 1)
|
||||
T (0) | 0 | NE (0) | 1 = 0 ^ (0 == 0)
|
||||
T (0) | 1 | NE (0) | 0 = 0 ^ (1 == 0)
|
||||
!T (1) | 0 | EQ (1) | 1 = 1 ^ (0 == 1)
|
||||
!T (1) | 1 | EQ (1) | 0 = 1 ^ (1 == 1)
|
||||
!T (1) | 0 | NE (0) | 0 = 1 ^ (0 == 0)
|
||||
!T (1) | 1 | NE (0) | 1 = 1 ^ (1 == 0) */
|
||||
int
|
||||
sh_eval_treg_value (rtx op)
|
||||
{
|
||||
enum rtx_code code = GET_CODE (op);
|
||||
if ((code != EQ && code != NE) || !CONST_INT_P (XEXP (op, 1)))
|
||||
return -1;
|
||||
|
||||
int cmpop = code == EQ ? 1 : 0;
|
||||
int cmpval = INTVAL (XEXP (op, 1));
|
||||
if (cmpval != 0 && cmpval != 1)
|
||||
return -1;
|
||||
|
||||
int t;
|
||||
if (t_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0))))
|
||||
t = 0;
|
||||
else if (negt_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0))))
|
||||
t = 1;
|
||||
else
|
||||
return -1;
|
||||
|
||||
return t ^ (cmpval == cmpop);
|
||||
}
|
||||
|
||||
/* Emit INSN, possibly in a PARALLEL with an USE of fpscr for SH4. */
|
||||
|
||||
static void
|
||||
@ -2485,9 +2522,9 @@ sh_emit_compare_and_branch (rtx *operands, enum machine_mode mode)
|
||||
sh_emit_set_t_insn (gen_ieee_ccmpeqsf_t (op0, op1), mode);
|
||||
|
||||
if (branch_code == code)
|
||||
emit_jump_insn (gen_branch_true (operands[3], get_t_reg_rtx ()));
|
||||
emit_jump_insn (gen_branch_true (operands[3]));
|
||||
else
|
||||
emit_jump_insn (gen_branch_false (operands[3], get_t_reg_rtx ()));
|
||||
emit_jump_insn (gen_branch_false (operands[3]));
|
||||
}
|
||||
|
||||
void
|
||||
@ -2521,7 +2558,7 @@ sh_emit_compare_and_set (rtx *operands, enum machine_mode mode)
|
||||
{
|
||||
lab = gen_label_rtx ();
|
||||
sh_emit_scc_to_t (EQ, op0, op1);
|
||||
emit_jump_insn (gen_branch_true (lab, get_t_reg_rtx ()));
|
||||
emit_jump_insn (gen_branch_true (lab));
|
||||
code = GT;
|
||||
}
|
||||
else
|
||||
|
@ -5322,8 +5322,8 @@ label:
|
||||
emit_insn (gen_movsi (operands[0], operands[1]));
|
||||
|
||||
emit_jump_insn (INTVAL (operands[3])
|
||||
? gen_branch_true (skip_neg_label, get_t_reg_rtx ())
|
||||
: gen_branch_false (skip_neg_label, get_t_reg_rtx ()));
|
||||
? gen_branch_true (skip_neg_label)
|
||||
: gen_branch_false (skip_neg_label));
|
||||
|
||||
emit_label_after (skip_neg_label,
|
||||
emit_insn (gen_negsi2 (operands[0], operands[1])));
|
||||
@ -5391,8 +5391,8 @@ label:
|
||||
emit_insn (gen_movsi (high_dst, high_src));
|
||||
|
||||
emit_jump_insn (INTVAL (operands[3])
|
||||
? gen_branch_true (skip_neg_label, get_t_reg_rtx ())
|
||||
: gen_branch_false (skip_neg_label, get_t_reg_rtx ()));
|
||||
? gen_branch_true (skip_neg_label)
|
||||
: gen_branch_false (skip_neg_label));
|
||||
|
||||
if (!INTVAL (operands[3]))
|
||||
emit_insn (gen_clrt ());
|
||||
@ -5575,11 +5575,57 @@ label:
|
||||
[(set (match_operand:SI 0 "arith_reg_dest")
|
||||
(zero_extend:SI (match_operand:QIHI 1 "zero_extend_operand")))])
|
||||
|
||||
(define_insn "*zero_extend<mode>si2_compact"
|
||||
(define_insn_and_split "*zero_extend<mode>si2_compact"
|
||||
[(set (match_operand:SI 0 "arith_reg_dest" "=r")
|
||||
(zero_extend:SI (match_operand:QIHI 1 "arith_reg_operand" "r")))]
|
||||
"TARGET_SH1"
|
||||
"extu.<bw> %1,%0"
|
||||
"&& can_create_pseudo_p ()"
|
||||
[(set (match_dup 0) (match_dup 2))]
|
||||
{
|
||||
/* Sometimes combine fails to combine a T bit or negated T bit store to a
|
||||
reg with a following zero extension. In the split pass after combine,
|
||||
try to figure the extended reg was set. If it originated from the T
|
||||
bit we can replace the zero extension with a reg move, which will be
|
||||
eliminated. Notice that this also helps the *cbranch_t splitter when
|
||||
it tries to post-combine tests and conditional branches, as it does not
|
||||
check for zero extensions. */
|
||||
rtx ext_reg;
|
||||
if (REG_P (operands[1]))
|
||||
ext_reg = operands[1];
|
||||
else if (GET_CODE (operands[1]) == SUBREG && REG_P (SUBREG_REG (operands[1])))
|
||||
ext_reg = SUBREG_REG (operands[1]);
|
||||
else
|
||||
FAIL;
|
||||
|
||||
/* Reg moves must be of the same mode. */
|
||||
if (GET_MODE (ext_reg) != SImode)
|
||||
FAIL;
|
||||
|
||||
operands[2] = NULL_RTX;
|
||||
for (rtx i = prev_nonnote_insn_bb (curr_insn); i != NULL_RTX;
|
||||
i = prev_nonnote_insn_bb (i))
|
||||
{
|
||||
if (LABEL_P (i) || BARRIER_P (i))
|
||||
break;
|
||||
if (!NONJUMP_INSN_P (i))
|
||||
continue;
|
||||
|
||||
if (reg_set_p (ext_reg, i))
|
||||
{
|
||||
rtx set_op = XEXP (set_of (ext_reg, i), 1);
|
||||
if (set_op == NULL_RTX)
|
||||
break;
|
||||
if (t_reg_operand (set_op, VOIDmode)
|
||||
|| negt_reg_operand (set_op, VOIDmode))
|
||||
operands[2] = ext_reg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (operands[2] == NULL_RTX)
|
||||
FAIL;
|
||||
}
|
||||
[(set_attr "type" "arith")])
|
||||
|
||||
(define_insn "*zero_extendhisi2_media"
|
||||
@ -8108,47 +8154,126 @@ label:
|
||||
;; Define the real conditional branch instructions.
|
||||
;; ------------------------------------------------------------------------
|
||||
|
||||
(define_insn "branch_true"
|
||||
[(set (pc) (if_then_else (ne (match_operand 1 "t_reg_operand" "")
|
||||
(const_int 0))
|
||||
(label_ref (match_operand 0 "" ""))
|
||||
(define_expand "branch_true"
|
||||
[(set (pc) (if_then_else (ne (reg:SI T_REG) (const_int 0))
|
||||
(label_ref (match_operand 0))
|
||||
(pc)))]
|
||||
"TARGET_SH1"
|
||||
{
|
||||
return output_branch (1, insn, operands);
|
||||
}
|
||||
[(set_attr "type" "cbranch")])
|
||||
"TARGET_SH1")
|
||||
|
||||
(define_insn "*branch_true_eq"
|
||||
[(set (pc) (if_then_else (eq (match_operand 1 "t_reg_operand" "")
|
||||
(const_int 1))
|
||||
(label_ref (match_operand 0 "" ""))
|
||||
(define_expand "branch_false"
|
||||
[(set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0))
|
||||
(label_ref (match_operand 0))
|
||||
(pc)))]
|
||||
"TARGET_SH1"
|
||||
{
|
||||
return output_branch (1, insn, operands);
|
||||
}
|
||||
[(set_attr "type" "cbranch")])
|
||||
"TARGET_SH1")
|
||||
|
||||
(define_insn "branch_false"
|
||||
[(set (pc) (if_then_else (eq (match_operand 1 "t_reg_operand" "")
|
||||
(const_int 0))
|
||||
(label_ref (match_operand 0 "" ""))
|
||||
(define_insn_and_split "*cbranch_t"
|
||||
[(set (pc) (if_then_else (match_operand 1 "cbranch_treg_value")
|
||||
(label_ref (match_operand 0))
|
||||
(pc)))]
|
||||
"TARGET_SH1"
|
||||
{
|
||||
return output_branch (0, insn, operands);
|
||||
return output_branch (sh_eval_treg_value (operands[1]), insn, operands);
|
||||
}
|
||||
[(set_attr "type" "cbranch")])
|
||||
|
||||
(define_insn "*branch_false_ne"
|
||||
[(set (pc) (if_then_else (ne (match_operand 1 "t_reg_operand" "")
|
||||
(const_int 1))
|
||||
(label_ref (match_operand 0 "" ""))
|
||||
"&& can_create_pseudo_p ()"
|
||||
[(set (pc) (if_then_else (eq (reg:SI T_REG) (match_dup 2))
|
||||
(label_ref (match_dup 0))
|
||||
(pc)))]
|
||||
"TARGET_SH1"
|
||||
{
|
||||
return output_branch (0, insn, operands);
|
||||
/* Try to find missed test and branch combine opportunities which result
|
||||
in redundant T bit tests before conditional branches.
|
||||
FIXME: Probably this would not be needed if CCmode was used
|
||||
together with TARGET_FIXED_CONDITION_CODE_REGS. */
|
||||
|
||||
const int treg_value = sh_eval_treg_value (operands[1]);
|
||||
operands[2] = NULL_RTX;
|
||||
|
||||
/* Scan the insns backwards for an insn that sets the T bit by testing a
|
||||
reg against zero like:
|
||||
(set (reg T_REG) (eq (reg) (const_int 0))) */
|
||||
rtx testing_insn = NULL_RTX;
|
||||
rtx tested_reg = NULL_RTX;
|
||||
|
||||
for (rtx i = prev_nonnote_insn_bb (curr_insn); i != NULL_RTX;
|
||||
i = prev_nonnote_insn_bb (i))
|
||||
{
|
||||
if (LABEL_P (i) || BARRIER_P (i))
|
||||
break;
|
||||
if (!NONJUMP_INSN_P (i))
|
||||
continue;
|
||||
|
||||
rtx p = PATTERN (i);
|
||||
if (p != NULL_RTX
|
||||
&& GET_CODE (p) == SET && t_reg_operand (XEXP (p, 0), VOIDmode)
|
||||
&& GET_CODE (XEXP (p, 1)) == EQ
|
||||
&& REG_P (XEXP (XEXP (p, 1), 0))
|
||||
&& satisfies_constraint_Z (XEXP (XEXP (p, 1), 1)))
|
||||
{
|
||||
testing_insn = i;
|
||||
tested_reg = XEXP (XEXP (p, 1), 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (testing_insn == NULL_RTX)
|
||||
FAIL;
|
||||
|
||||
/* Continue scanning the insns backwards and try to find the insn that
|
||||
sets the tested reg which we found above. If the reg is set by storing
|
||||
the T bit or the negated T bit we can eliminate the test insn before
|
||||
the branch. Notice that the branch condition has to be inverted if the
|
||||
test is eliminated. */
|
||||
|
||||
/* If the T bit is used between the testing insn and the brach insn
|
||||
leave it alone. */
|
||||
if (reg_used_between_p (get_t_reg_rtx (), testing_insn, curr_insn))
|
||||
FAIL;
|
||||
|
||||
for (rtx i = prev_nonnote_insn_bb (testing_insn); i != NULL_RTX;
|
||||
i = prev_nonnote_insn_bb (i))
|
||||
{
|
||||
if (LABEL_P (i) || BARRIER_P (i))
|
||||
break;
|
||||
if (!NONJUMP_INSN_P (i))
|
||||
continue;
|
||||
|
||||
if (reg_set_p (tested_reg, i))
|
||||
{
|
||||
const_rtx tested_reg_set = set_of (tested_reg, i);
|
||||
|
||||
/* It could also be a clobber... */
|
||||
if (tested_reg_set == NULL_RTX || GET_CODE (tested_reg_set) != SET)
|
||||
break;
|
||||
|
||||
rtx set_op1 = XEXP (tested_reg_set, 1);
|
||||
if (t_reg_operand (set_op1, VOIDmode))
|
||||
operands[2] = GEN_INT (treg_value ^ 1);
|
||||
else if (negt_reg_operand (set_op1, VOIDmode))
|
||||
operands[2] = GEN_INT (treg_value);
|
||||
else if (REG_P (set_op1))
|
||||
{
|
||||
/* If it's a reg-reg copy follow the copied reg. This can
|
||||
happen e.g. when T bit store zero-extensions are
|
||||
eliminated. */
|
||||
tested_reg = set_op1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* It's only safe to remove the testing insn if the T bit is not
|
||||
modified between the testing insn and the insn that stores the
|
||||
T bit. Notice that some T bit stores such as negc also modify
|
||||
the T bit. */
|
||||
if (modified_between_p (get_t_reg_rtx (), i, testing_insn)
|
||||
|| modified_in_p (get_t_reg_rtx (), i))
|
||||
operands[2] = NULL_RTX;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (operands[2] == NULL_RTX)
|
||||
FAIL;
|
||||
|
||||
set_insn_deleted (testing_insn);
|
||||
}
|
||||
[(set_attr "type" "cbranch")])
|
||||
|
||||
@ -10960,25 +11085,53 @@ label:
|
||||
(set (reg:SI T_REG) (const_int 1))
|
||||
(use (match_dup 2))])])
|
||||
|
||||
;; In some cases the zero extension does not get combined away and a
|
||||
;; sequence like the following might remain:
|
||||
;; mov #-1,r2
|
||||
;; tst r1,r1
|
||||
;; negc r2,r1
|
||||
;; extu.b r1,r1
|
||||
(define_peephole2
|
||||
[(parallel
|
||||
[(set (match_operand:SI 0 "arith_reg_dest" "")
|
||||
(xor:SI (match_operand:SI 1 "t_reg_operand" "") (const_int 1)))
|
||||
(set (reg:SI T_REG) (const_int 1))
|
||||
(use (match_operand:SI 2 "arith_reg_operand" ""))])
|
||||
(set (match_dup 0)
|
||||
(zero_extend:SI (match_operand 3 "arith_reg_operand" "")))]
|
||||
"TARGET_SH1 && REGNO (operands[0]) == REGNO (operands[3])"
|
||||
[(parallel
|
||||
[(set (match_dup 0) (xor:SI (match_dup 1) (const_int 1)))
|
||||
(set (reg:SI T_REG) (const_int 1))
|
||||
(use (match_dup 2))])])
|
||||
;; Store the negated T bit in a reg using r0 and xor. This one doesn't
|
||||
;; clobber the T bit, which is useful when storing the T bit and the
|
||||
;; negated T bit in parallel. On SH2A the movrt insn can be used for that.
|
||||
;; Usually we don't want this insn to be matched, except for cases where the
|
||||
;; T bit clobber is really not appreciated. Hence the extra use on T_REG.
|
||||
(define_insn_and_split "movrt_xor"
|
||||
[(set (match_operand:SI 0 "arith_reg_dest" "=z")
|
||||
(xor:SI (match_operand:SI 1 "t_reg_operand") (const_int 1)))
|
||||
(use (reg:SI T_REG))]
|
||||
"TARGET_SH1 && !TARGET_SH2A"
|
||||
"#"
|
||||
"&& reload_completed"
|
||||
[(set (match_dup 0) (reg:SI T_REG))
|
||||
(set (match_dup 0) (xor:SI (match_dup 0) (const_int 1)))])
|
||||
|
||||
;; Store the T bit and the negated T bit in two regs in parallel. There is
|
||||
;; no real insn to do that, but specifying this pattern will give combine
|
||||
;; some opportunities.
|
||||
(define_insn_and_split "*movt_movrt"
|
||||
[(parallel [(set (match_operand:SI 0 "arith_reg_dest")
|
||||
(match_operand:SI 1 "negt_reg_operand"))
|
||||
(set (match_operand:SI 2 "arith_reg_dest")
|
||||
(match_operand:SI 3 "t_reg_operand"))])]
|
||||
"TARGET_SH1"
|
||||
"#"
|
||||
"&& 1"
|
||||
[(const_int 0)]
|
||||
{
|
||||
rtx i = TARGET_SH2A
|
||||
? gen_movrt (operands[0], get_t_reg_rtx ())
|
||||
: gen_movrt_xor (operands[0], get_t_reg_rtx ());
|
||||
|
||||
emit_insn (i);
|
||||
emit_insn (gen_movt (operands[2], get_t_reg_rtx ()));
|
||||
DONE;
|
||||
})
|
||||
|
||||
(define_insn_and_split "*movt_movrt"
|
||||
[(parallel [(set (match_operand:SI 0 "arith_reg_dest")
|
||||
(match_operand:SI 1 "t_reg_operand"))
|
||||
(set (match_operand:SI 2 "arith_reg_dest")
|
||||
(match_operand:SI 3 "negt_reg_operand"))])]
|
||||
"TARGET_SH1"
|
||||
"#"
|
||||
"&& 1"
|
||||
[(parallel [(set (match_dup 2) (match_dup 3))
|
||||
(set (match_dup 0) (match_dup 1))])])
|
||||
|
||||
;; Use negc to store the T bit in a MSB of a reg in the following way:
|
||||
;; T = 1: 0x80000000 -> reg
|
||||
@ -15165,7 +15318,7 @@ label:
|
||||
else
|
||||
{
|
||||
emit_insn (gen_stack_protect_test_si (operands[0], operands[1]));
|
||||
emit_jump_insn (gen_branch_true (operands[2], get_t_reg_rtx ()));
|
||||
emit_jump_insn (gen_branch_true (operands[2]));
|
||||
}
|
||||
|
||||
DONE;
|
||||
|
@ -1,3 +1,11 @@
|
||||
2012-10-12 Oleg Endo <olegendo@gcc.gnu.org>
|
||||
|
||||
PR target/51244
|
||||
* gcc.target/sh/pr51244-13.c: New.
|
||||
* gcc.target/sh/pr51244-14.c: New.
|
||||
* gcc.target/sh/pr51244-15.c: New.
|
||||
* gcc.target/sh/pr51244-16.c: New.
|
||||
|
||||
2012-10-11 Paolo Carlini <paolo.carlini@oracle.com>
|
||||
|
||||
PR c++/51878
|
||||
|
85
gcc/testsuite/gcc.target/sh/pr51244-13.c
Normal file
85
gcc/testsuite/gcc.target/sh/pr51244-13.c
Normal file
@ -0,0 +1,85 @@
|
||||
/* This is a case extracted from CSiBE which contained the following
|
||||
sequence:
|
||||
shll r0
|
||||
movt r0
|
||||
tst r0,r0
|
||||
bf .L11
|
||||
where the 'tst r0,r0' before the branch can be omitted by inverting the
|
||||
branch condition. The tested function contains two other tst insns. If
|
||||
everything goes as expected we will be seeing only those other two tst
|
||||
insns. */
|
||||
/* { dg-do compile { target "sh*-*-*" } } */
|
||||
/* { dg-options "-O2" } */
|
||||
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */
|
||||
/* { dg-final { scan-assembler-times "tst" 2 } } */
|
||||
|
||||
static __inline__ int
|
||||
__test_bit (unsigned long nr, volatile void * addr)
|
||||
{
|
||||
/* This is on purpose. */
|
||||
int oldbit;
|
||||
return oldbit & 1;
|
||||
}
|
||||
|
||||
static __inline__ int
|
||||
__constant_test_bit (unsigned long nr, volatile void * addr)
|
||||
{
|
||||
return (((volatile char *) addr)[(nr>>3)^7] & (1<<(nr&7))) != 0;
|
||||
}
|
||||
|
||||
struct list_head
|
||||
{
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
static inline void
|
||||
__list_del (struct list_head *prev, struct list_head *next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
static inline void
|
||||
list_del (struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
entry->next = 0;
|
||||
entry->prev = 0;
|
||||
}
|
||||
|
||||
extern int nr_active_pages;
|
||||
extern int nr_inactive_pages;
|
||||
extern struct list_head active_list;
|
||||
|
||||
typedef struct page
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head lru;
|
||||
} mem_map_t;
|
||||
|
||||
void
|
||||
activate_page_nolock (struct page * page)
|
||||
{
|
||||
if ((__builtin_constant_p((6))
|
||||
? __constant_test_bit((6),(&(page)->flags))
|
||||
: __test_bit((6),(&(page)->flags)) )
|
||||
&& !(__builtin_constant_p((7))
|
||||
? __constant_test_bit((7),(&(page)->flags))
|
||||
: __test_bit((7),(&(page)->flags)) ))
|
||||
{
|
||||
list_del(&(page)->lru);
|
||||
nr_inactive_pages--;
|
||||
if (!(__builtin_constant_p(6) ? __constant_test_bit((6),(&(page)->flags))
|
||||
: __test_bit((6),(&(page)->flags))))
|
||||
printk("", "", 43);
|
||||
|
||||
if ((__builtin_constant_p(7) ? __constant_test_bit((7),(&(page)->flags))
|
||||
: __test_bit((7),(&(page)->flags))))
|
||||
printk("", "", 43);
|
||||
|
||||
(__builtin_constant_p(7) ? __constant_set_bit((7),(&(page)->flags))
|
||||
: __set_bit((7),(&(page)->flags)) );
|
||||
list_add(&(page)->lru, &active_list);
|
||||
nr_active_pages++;
|
||||
}
|
||||
}
|
107
gcc/testsuite/gcc.target/sh/pr51244-14.c
Normal file
107
gcc/testsuite/gcc.target/sh/pr51244-14.c
Normal file
@ -0,0 +1,107 @@
|
||||
/* This is a case extracted from CSiBE which would sometimes contain the
|
||||
following sequence:
|
||||
cmp/eq r12,r13
|
||||
movt r0
|
||||
xor #1,r0
|
||||
extu.b r0,r0
|
||||
movt r3
|
||||
tst r0,r0
|
||||
bf/s .L35
|
||||
where the negated T bit store did not combine properly. Since there are
|
||||
other movt insns we only check for the xor and the extu. */
|
||||
/* { dg-do compile { target "sh*-*-*" } } */
|
||||
/* { dg-options "-O2" } */
|
||||
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */
|
||||
/* { dg-final { scan-assembler-not "xor|extu" } } */
|
||||
|
||||
typedef struct transaction_s transaction_t;
|
||||
|
||||
struct journal_head
|
||||
{
|
||||
transaction_t * b_transaction;
|
||||
struct journal_head *b_cpnext, *b_cpprev;
|
||||
};
|
||||
|
||||
struct transaction_s
|
||||
{
|
||||
struct journal_head * t_checkpoint_list;
|
||||
transaction_t *t_cpnext, *t_cpprev;
|
||||
};
|
||||
|
||||
struct journal_s
|
||||
{
|
||||
transaction_t * j_checkpoint_transactions;
|
||||
unsigned long j_first, j_last;
|
||||
};
|
||||
|
||||
typedef struct journal_s journal_t;
|
||||
|
||||
extern int __try_to_free_cp_buf (struct journal_head *jh);
|
||||
extern int __cleanup_transaction (journal_t *journal, transaction_t *transaction);
|
||||
extern void __flush_batch (void **bhs, int *batch_count);
|
||||
extern void* jh2bh (void*);
|
||||
|
||||
static int
|
||||
__flush_buffer (journal_t *journal, struct journal_head *jh,
|
||||
void **bhs, int *batch_count, int *drop_count)
|
||||
{
|
||||
void *bh = jh2bh (jh);
|
||||
int ret = 0;
|
||||
if (bh)
|
||||
{
|
||||
bhs[*batch_count] = bh;
|
||||
(*batch_count)++;
|
||||
if (*batch_count == 64)
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int last_buffer = 0;
|
||||
if (jh->b_cpnext == jh)
|
||||
last_buffer = 1;
|
||||
if (__try_to_free_cp_buf (jh))
|
||||
{
|
||||
(*drop_count)++;
|
||||
ret = last_buffer;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
log_do_checkpoint (journal_t *journal, int nblocks)
|
||||
{
|
||||
transaction_t *transaction, *last_transaction, *next_transaction;
|
||||
int batch_count = 0;
|
||||
void *bhs[64];
|
||||
|
||||
repeat:
|
||||
transaction = journal->j_checkpoint_transactions;
|
||||
if (transaction == ((void *)0))
|
||||
return 0;
|
||||
last_transaction = transaction->t_cpprev;
|
||||
next_transaction = transaction;
|
||||
do
|
||||
{
|
||||
struct journal_head *jh, *last_jh, *next_jh;
|
||||
int drop_count = 0;
|
||||
int cleanup_ret, retry = 0;
|
||||
transaction = next_transaction;
|
||||
next_transaction = transaction->t_cpnext;
|
||||
jh = transaction->t_checkpoint_list;
|
||||
last_jh = jh->b_cpprev;
|
||||
next_jh = jh;
|
||||
do
|
||||
{
|
||||
jh = next_jh;
|
||||
next_jh = jh->b_cpnext;
|
||||
retry = __flush_buffer(journal, jh, bhs, &batch_count, &drop_count);
|
||||
} while (jh != last_jh && !retry);
|
||||
|
||||
if (retry)
|
||||
goto repeat;
|
||||
|
||||
cleanup_ret = __cleanup_transaction(journal, transaction);
|
||||
goto repeat;
|
||||
} while (transaction != last_transaction);
|
||||
}
|
71
gcc/testsuite/gcc.target/sh/pr51244-15.c
Normal file
71
gcc/testsuite/gcc.target/sh/pr51244-15.c
Normal file
@ -0,0 +1,71 @@
|
||||
/* Check that the redundant test removal code in the *cbranch_t split works
|
||||
as expected on non-SH2A targets. Because on SH2A the movrt instruction
|
||||
is used, this test is re-used and checked differently in pr51244-16.c. */
|
||||
/* { dg-do compile { target "sh*-*-*" } } */
|
||||
/* { dg-options "-O2" } */
|
||||
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" "-m2a*" } { "" } } */
|
||||
/* { dg-final { scan-assembler-times "tst" 6 } } */
|
||||
/* { dg-final { scan-assembler-times "movt" 6 } } */
|
||||
/* { dg-final { scan-assembler-times "xor" 3 } } */
|
||||
/* { dg-final { scan-assembler-not "extu|exts|negc" } } */
|
||||
|
||||
typedef char bool;
|
||||
|
||||
int
|
||||
test_0 (int a, int b, int c, int* d)
|
||||
{
|
||||
/* non SH2A: 1x tst, 1x movt, 1x xor
|
||||
SH2A: 1x tst, 1x movrt */
|
||||
bool x = a == 0;
|
||||
d[2] = !x;
|
||||
return x ? b : c;
|
||||
}
|
||||
|
||||
int
|
||||
test_1 (int a, int b, int c, int* d)
|
||||
{
|
||||
/* 1x tst, 1x movt */
|
||||
bool x = a != 0;
|
||||
d[2] = !x;
|
||||
return x ? b : c;
|
||||
}
|
||||
|
||||
int
|
||||
test_2 (int a, int b, int c, char* d)
|
||||
{
|
||||
/* Check that there is no sign/zero-extension before the store.
|
||||
non SH2A: 1x tst, 1x movt, 1x xor
|
||||
SH2A: 1x tst, 1x movrt */
|
||||
bool x = a == 0;
|
||||
d[2] = !x;
|
||||
return x ? b : c;
|
||||
}
|
||||
|
||||
int
|
||||
test_3 (int a, int b, int c, char* d)
|
||||
{
|
||||
/* Check that there is no sign/zero-extension before the store.
|
||||
1x tst, 1x movt */
|
||||
bool x = a != 0;
|
||||
d[2] = !x;
|
||||
return x ? b : c;
|
||||
}
|
||||
|
||||
int
|
||||
test_4 (int a, int b, int c, char* d)
|
||||
{
|
||||
/* 1x tst, 1x movt */
|
||||
bool x = a != 0;
|
||||
d[2] = !x;
|
||||
return !x ? b : c;
|
||||
}
|
||||
|
||||
int
|
||||
test_5 (int a, int b, int c, char* d)
|
||||
{
|
||||
/* non SH2A: 1x tst, 1x movt, 1x xor
|
||||
SH2A: 1x tst, 1x movrt */
|
||||
bool x = a == 0;
|
||||
d[2] = !x;
|
||||
return !x ? b : c;
|
||||
}
|
11
gcc/testsuite/gcc.target/sh/pr51244-16.c
Normal file
11
gcc/testsuite/gcc.target/sh/pr51244-16.c
Normal file
@ -0,0 +1,11 @@
|
||||
/* Check that the redundant test removal code in the *cbranch_t split works
|
||||
as expected on SH2A targets. */
|
||||
/* { dg-do compile { target "sh*-*-*" } } */
|
||||
/* { dg-options "-O2" } */
|
||||
/* { dg-skip-if "" { "sh*-*-*" } { "*" } { "-m2a*" } } */
|
||||
/* { dg-final { scan-assembler-times "tst" 6 } } */
|
||||
/* { dg-final { scan-assembler-times "movt" 3 } } */
|
||||
/* { dg-final { scan-assembler-times "movrt" 3 } } */
|
||||
/* { dg-final { scan-assembler-not "extu|exts|negc" } } */
|
||||
|
||||
#include "pr51244-15.c"
|
Loading…
Reference in New Issue
Block a user