target/109650: Fix wrong code after cc0 -> CCmode transition.
This patch fixes a wrong-code bug in the wake of PR92729, the transition that turned the AVR backend from cc0 to CCmode. In cc0, the insn that uses cc0 like a conditional branch always follows the cc0 setter, which is no more the case with CCmode where set and use of REG_CC might be in different basic blocks. This patch removes the machine-dependent reorg pass in avr_reorg entirely. It is replaced by a new, AVR specific mini-pass that runs prior to split2. Canonicalization of comparisons away from the "difficult" codes GT[U] and LE[U] is now mostly performed by implementing TARGET_CANONICALIZE_COMPARISON. Moreover: * Text peephole conditions get "dead_or_set_regno_p (*, REG_CC)" as needed. * RTL peephole conditions get "peep2_regno_dead_p (*, REG_CC)" as needed. * Conditional branches no more clobber REG_CC. * insn output for compares looks ahead to determine the branch mode in use. This needs also "dead_or_set_regno_p (*, REG_CC)". * Add RTL peepholes for decrement-and-branch detection. * Some of the patterns like "*cmphi.zero-extend.0" lost their combine-ational part wit PR92729. Restore them. Finally, it fixes some of the many indentation glitches left over from PR92729. gcc/ PR target/109650 PR target/92729 * config/avr/avr-passes.def (avr_pass_ifelse): Insert new pass. * config/avr/avr.cc (avr_pass_ifelse): New RTL pass. (avr_pass_data_ifelse): New pass_data for it. (make_avr_pass_ifelse, avr_redundant_compare, avr_cbranch_cost) (avr_canonicalize_comparison, avr_out_plus_set_ZN) (avr_out_cmp_ext): New functions. (compare_condtition): Make sure REG_CC dies in the branch insn. (avr_rtx_costs_1): Add computation of cbranch costs. (avr_adjust_insn_length) [ADJUST_LEN_ADD_SET_ZN, ADJUST_LEN_CMP_ZEXT]: [ADJUST_LEN_CMP_SEXT]Handle them. (TARGET_CANONICALIZE_COMPARISON): New define. (avr_simplify_comparison_p, compare_diff_p, avr_compare_pattern) (avr_reorg_remove_redundant_compare, avr_reorg): Remove functions. (TARGET_MACHINE_DEPENDENT_REORG): Remove define. * config/avr/avr-protos.h (avr_simplify_comparison_p): Remove proto. (make_avr_pass_ifelse, avr_out_plus_set_ZN, cc_reg_rtx) (avr_out_cmp_zext): New Protos * config/avr/avr.md (branch, difficult_branch): Don't split insns. (*cbranchhi.zero-extend.0", *cbranchhi.zero-extend.1") (*swapped_tst<mode>, *add.for.eqne.<mode>): New insns. (*cbranch<mode>4): Rename to cbranch<mode>4_insn. (define_peephole): Add dead_or_set_regno_p(insn,REG_CC) as needed. (define_deephole2): Add peep2_regno_dead_p(*,REG_CC) as needed. Add new RTL peepholes for decrement-and-branch and *swapped_tst<mode>. Rework signtest-and-branch peepholes for *sbrx_branch<mode>. (adjust_len) [add_set_ZN, cmp_zext]: New. (QIPSI): New mode iterator. (ALLs1, ALLs2, ALLs4, ALLs234): New mode iterators. (gelt): New code iterator. (gelt_eqne): New code attribute. (rvbranch, *rvbranch, difficult_rvbranch, *difficult_rvbranch) (branch_unspec, *negated_tst<mode>, *reversed_tst<mode>) (*cmpqi_sign_extend): Remove insns. (define_c_enum "unspec") [UNSPEC_IDENTITY]: Remove. * config/avr/avr-dimode.md (cbranch<mode>4): Canonicalize comparisons. * config/avr/predicates.md (scratch_or_d_register_operand): New. * config/avr/constraints.md (Yxx): New constraint. gcc/testsuite/ PR target/109650 * gcc.target/avr/torture/pr109650-1.c: New test. * gcc.target/avr/torture/pr109650-2.c: New test.
This commit is contained in:
parent
17bccd1d2c
commit
30a8771c0f
9 changed files with 1460 additions and 777 deletions
|
@ -455,12 +455,18 @@
|
|||
(define_expand "cbranch<mode>4"
|
||||
[(set (pc)
|
||||
(if_then_else (match_operator 0 "ordered_comparison_operator"
|
||||
[(match_operand:ALL8 1 "register_operand" "")
|
||||
(match_operand:ALL8 2 "nonmemory_operand" "")])
|
||||
(label_ref (match_operand 3 "" ""))
|
||||
(pc)))]
|
||||
[(match_operand:ALL8 1 "register_operand")
|
||||
(match_operand:ALL8 2 "nonmemory_operand")])
|
||||
(label_ref (match_operand 3))
|
||||
(pc)))]
|
||||
"avr_have_dimode"
|
||||
{
|
||||
int icode = (int) GET_CODE (operands[0]);
|
||||
|
||||
targetm.canonicalize_comparison (&icode, &operands[1], &operands[2], false);
|
||||
operands[0] = gen_rtx_fmt_ee ((enum rtx_code) icode,
|
||||
VOIDmode, operands[1], operands[2]);
|
||||
|
||||
rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
|
||||
|
||||
avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A));
|
||||
|
@ -490,8 +496,8 @@
|
|||
(if_then_else (match_operator 0 "ordered_comparison_operator"
|
||||
[(reg:ALL8 ACC_A)
|
||||
(reg:ALL8 ACC_B)])
|
||||
(label_ref (match_operand 1 "" ""))
|
||||
(pc)))]
|
||||
(label_ref (match_operand 1))
|
||||
(pc)))]
|
||||
"avr_have_dimode"
|
||||
"#"
|
||||
"&& reload_completed"
|
||||
|
@ -544,8 +550,8 @@
|
|||
(if_then_else (match_operator 0 "ordered_comparison_operator"
|
||||
[(reg:ALL8 ACC_A)
|
||||
(match_operand:ALL8 1 "const_operand" "n Ynn")])
|
||||
(label_ref (match_operand 2 "" ""))
|
||||
(pc)))
|
||||
(label_ref (match_operand 2 "" ""))
|
||||
(pc)))
|
||||
(clobber (match_scratch:QI 3 "=&d"))]
|
||||
"avr_have_dimode
|
||||
&& !s8_operand (operands[1], VOIDmode)"
|
||||
|
|
|
@ -43,3 +43,23 @@ INSERT_PASS_BEFORE (pass_free_cfg, 1, avr_pass_recompute_notes);
|
|||
insns withaout any insns in between. */
|
||||
|
||||
INSERT_PASS_AFTER (pass_expand, 1, avr_pass_casesi);
|
||||
|
||||
/* If-else decision trees generated for switch / case may produce sequences
|
||||
like
|
||||
|
||||
SREG = compare (reg, val);
|
||||
if (SREG == 0) goto label1;
|
||||
SREG = compare (reg, 1 + val);
|
||||
if (SREG >= 0) goto label2;
|
||||
|
||||
which can be optimized to
|
||||
|
||||
SREG = compare (reg, val);
|
||||
if (SREG == 0) goto label1;
|
||||
if (SREG >= 0) goto label2;
|
||||
|
||||
The optimal place for such a pass would be directly after expand, but
|
||||
it's not possible for a jump insn to target more than one code label.
|
||||
Hence, run a mini pass right before split2 which introduces REG_CC. */
|
||||
|
||||
INSERT_PASS_BEFORE (pass_split_after_reload, 1, avr_pass_ifelse);
|
||||
|
|
|
@ -58,6 +58,8 @@ extern const char *ret_cond_branch (rtx x, int len, int reverse);
|
|||
extern const char *avr_out_movpsi (rtx_insn *, rtx*, int*);
|
||||
extern const char *avr_out_sign_extend (rtx_insn *, rtx*, int*);
|
||||
extern const char *avr_out_insert_notbit (rtx_insn *, rtx*, rtx, int*);
|
||||
extern const char *avr_out_plus_set_ZN (rtx*, int*);
|
||||
extern const char *avr_out_cmp_ext (rtx*, enum rtx_code, int*);
|
||||
|
||||
extern const char *ashlqi3_out (rtx_insn *insn, rtx operands[], int *len);
|
||||
extern const char *ashlhi3_out (rtx_insn *insn, rtx operands[], int *len);
|
||||
|
@ -112,8 +114,6 @@ extern int jump_over_one_insn_p (rtx_insn *insn, rtx dest);
|
|||
|
||||
extern void avr_final_prescan_insn (rtx_insn *insn, rtx *operand,
|
||||
int num_operands);
|
||||
extern int avr_simplify_comparison_p (machine_mode mode,
|
||||
RTX_CODE op, rtx x);
|
||||
extern RTX_CODE avr_normalize_condition (RTX_CODE condition);
|
||||
extern void out_shift_with_cnt (const char *templ, rtx_insn *insn,
|
||||
rtx operands[], int *len, int t_len);
|
||||
|
@ -145,6 +145,7 @@ extern rtx tmp_reg_rtx;
|
|||
extern rtx zero_reg_rtx;
|
||||
extern rtx all_regs_rtx[32];
|
||||
extern rtx rampz_rtx;
|
||||
extern rtx cc_reg_rtx;
|
||||
|
||||
#endif /* RTX_CODE */
|
||||
|
||||
|
@ -160,6 +161,7 @@ class rtl_opt_pass;
|
|||
extern rtl_opt_pass *make_avr_pass_pre_proep (gcc::context *);
|
||||
extern rtl_opt_pass *make_avr_pass_recompute_notes (gcc::context *);
|
||||
extern rtl_opt_pass *make_avr_pass_casesi (gcc::context *);
|
||||
extern rtl_opt_pass *make_avr_pass_ifelse (gcc::context *);
|
||||
|
||||
/* From avr-log.cc */
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -245,6 +245,11 @@
|
|||
(match_test "INTVAL (avr_to_int_mode (op)) == -2"))
|
||||
(match_test "satisfies_constraint_Cm2 (op)")))
|
||||
|
||||
;; Constraint that's the empty set. Useful with mode and code iterators.
|
||||
(define_constraint "Yxx"
|
||||
"A constraints that is always false"
|
||||
(match_test "false"))
|
||||
|
||||
(define_constraint "Yx2"
|
||||
"Fixed-point or integer constant not in the range @minus{}2 @dots{} 2"
|
||||
(and (ior (match_code "const_int")
|
||||
|
|
|
@ -27,6 +27,11 @@
|
|||
(and (match_code "reg")
|
||||
(match_test "REGNO (op) >= 16 && REGNO (op) <= 31")))
|
||||
|
||||
(define_predicate "scratch_or_d_register_operand"
|
||||
(ior (match_operand 0 "d_register_operand")
|
||||
(and (match_code ("scratch"))
|
||||
(match_operand 0 "scratch_operand"))))
|
||||
|
||||
(define_predicate "even_register_operand"
|
||||
(and (match_code "reg")
|
||||
(and (match_test "REGNO (op) <= 31")
|
||||
|
|
63
gcc/testsuite/gcc.target/avr/torture/pr109650-1.c
Normal file
63
gcc/testsuite/gcc.target/avr/torture/pr109650-1.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* { dg-do run } */
|
||||
/* { dg-options { -std=c99 } } */
|
||||
|
||||
typedef _Bool bool;
|
||||
typedef __UINT8_TYPE__ uint8_t;
|
||||
|
||||
static inline __attribute__((__always_inline__))
|
||||
bool func1a (bool p1, uint8_t p2)
|
||||
{
|
||||
if (p1)
|
||||
return p2 <= 8;
|
||||
return p2 <= 2;
|
||||
}
|
||||
|
||||
__attribute__((__noinline__, __noclone__))
|
||||
bool func1b (bool p1, uint8_t p2)
|
||||
{
|
||||
return func1a (p1, p2);
|
||||
}
|
||||
|
||||
static inline __attribute__((__always_inline__))
|
||||
bool func2a (bool p1, unsigned p2)
|
||||
{
|
||||
if (p1)
|
||||
return p2 <= 8;
|
||||
return p2 <= 2;
|
||||
}
|
||||
|
||||
__attribute__((__noinline__, __noclone__))
|
||||
bool func2b (bool p1, unsigned p2)
|
||||
{
|
||||
return func2a (p1, p2);
|
||||
}
|
||||
|
||||
void test1 (void)
|
||||
{
|
||||
if (func1a (0, 1) != func1b (0, 1)) __builtin_abort();
|
||||
if (func1a (0, 2) != func1b (0, 2)) __builtin_abort();
|
||||
if (func1a (0, 3) != func1b (0, 3)) __builtin_abort();
|
||||
|
||||
if (func1a (1, 7) != func1b (1, 7)) __builtin_abort();
|
||||
if (func1a (1, 8) != func1b (1, 8)) __builtin_abort();
|
||||
if (func1a (1, 9) != func1b (1, 9)) __builtin_abort();
|
||||
}
|
||||
|
||||
void test2 (void)
|
||||
{
|
||||
if (func2a (0, 1) != func2b (0, 1)) __builtin_abort();
|
||||
if (func2a (0, 2) != func2b (0, 2)) __builtin_abort();
|
||||
if (func2a (0, 3) != func2b (0, 3)) __builtin_abort();
|
||||
|
||||
if (func2a (1, 7) != func2b (1, 7)) __builtin_abort();
|
||||
if (func2a (1, 8) != func2b (1, 8)) __builtin_abort();
|
||||
if (func2a (1, 9) != func2b (1, 9)) __builtin_abort();
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
|
||||
__builtin_exit (0);
|
||||
}
|
79
gcc/testsuite/gcc.target/avr/torture/pr109650-2.c
Normal file
79
gcc/testsuite/gcc.target/avr/torture/pr109650-2.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
/* { dg-do run } */
|
||||
|
||||
typedef __UINT8_TYPE__ uint8_t;
|
||||
|
||||
#define AI static __inline__ __attribute__((__always_inline__))
|
||||
#define NI __attribute__((__noinline__,__noclone__))
|
||||
|
||||
AI uint8_t func1_eq (uint8_t c, unsigned x)
|
||||
{
|
||||
if (x == c)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
AI uint8_t func1_ne (uint8_t c, unsigned x)
|
||||
{
|
||||
if (x != c)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
AI uint8_t func1_ltu (uint8_t c, unsigned x)
|
||||
{
|
||||
if (x < c)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
AI uint8_t func1_leu (uint8_t c, unsigned x)
|
||||
{
|
||||
if (x <= c)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
AI uint8_t func1_gtu (uint8_t c, unsigned x)
|
||||
{
|
||||
if (x > c)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
AI uint8_t func1_geu (uint8_t c, unsigned x)
|
||||
{
|
||||
if (x >= c)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
NI uint8_t func2_eq (uint8_t c, unsigned x) { return func1_eq (c, x); }
|
||||
NI uint8_t func2_ne (uint8_t c, unsigned x) { return func1_ne (c, x); }
|
||||
NI uint8_t func2_ltu (uint8_t c, unsigned x) { return func1_ltu (c, x); }
|
||||
NI uint8_t func2_leu (uint8_t c, unsigned x) { return func1_leu (c, x); }
|
||||
NI uint8_t func2_gtu (uint8_t c, unsigned x) { return func1_gtu (c, x); }
|
||||
NI uint8_t func2_geu (uint8_t c, unsigned x) { return func1_geu (c, x); }
|
||||
|
||||
AI void test4 (uint8_t c, unsigned x)
|
||||
{
|
||||
if (func2_eq (c, x) != func1_eq (c, x)) __builtin_abort();
|
||||
if (func2_ne (c, x) != func1_ne (c, x)) __builtin_abort();
|
||||
if (func2_ltu (c, x) != func1_ltu (c, x)) __builtin_abort();
|
||||
if (func2_leu (c, x) != func1_leu (c, x)) __builtin_abort();
|
||||
if (func2_gtu (c, x) != func1_gtu (c, x)) __builtin_abort();
|
||||
if (func2_geu (c, x) != func1_geu (c, x)) __builtin_abort();
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
test4 (127, 127);
|
||||
test4 (127, 128);
|
||||
test4 (128, 127);
|
||||
|
||||
test4 (0x42, 0x142);
|
||||
test4 (0x0, 0x100);
|
||||
test4 (0x0, 0x0);
|
||||
test4 (0x0, 0x1);
|
||||
|
||||
__builtin_exit (0);
|
||||
}
|
Loading…
Add table
Reference in a new issue