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:
Georg-Johann Lay 2023-06-10 21:47:53 +02:00
parent 17bccd1d2c
commit 30a8771c0f
9 changed files with 1460 additions and 777 deletions

View file

@ -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)"

View file

@ -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);

View file

@ -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

View file

@ -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")

View file

@ -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")

View 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);
}

View 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);
}