On 2/10/23 15:41, Philipp Tomsich wrote:
Some architectures, as it the case on RISC-V with the proposed
ZiCondOps and the vendor-defined XVentanaCondOps, define a
conditional-zero instruction that is equivalent to:
- the positive form: rd = (rc != 0) ? rs : 0
- the negated form: rd = (rc == 0) ? rs : 0
While noce_try_store_flag_mask will somewhat work for this case, it
will generate a number of atomic RTX that will misdirect the cost
calculation and may be too long (i.e., 4 RTX and more) to successfully
merge at combine-time.
Instead, we add two new transforms that attempt to build up what we
define as the canonical form of a conditional-zero expression:
(set (match_operand 0 "register_operand" "=r")
(and (neg (eq_or_ne (match_operand 1 "register_operand" "r")
(const_int 0)))
(match_operand 2 "register_operand" "r")))
Architectures that provide a conditional-zero are thus expected to
define an instruction matching this pattern in their backend.
Based on this, we support the following cases:
- noce_try_condzero:
a ? a : b
a ? b : 0 (and then/else swapped)
!a ? b : 0 (and then/else swapped)
- noce_try_condzero_arith:
conditional-plus, conditional-minus, conditional-and,
conditional-or, conditional-xor, conditional-shift,
conditional-and
Given that this is hooked into the CE passes, it is less powerful than
a tree-pass (e.g., it can not transform cases where an extension, such
as for uint16_t operations is in either the then or else-branch
together with the arithmetic) but already covers a good array of cases
and triggers across SPEC CPU 2017.
Adding transformations in a tree pass should come in a future
improvement.
gcc/ChangeLog:
* ifcvt.cc (noce_emit_insn): Add prototype.
(noce_emit_condzero): Helper for noce_try_condzero and
noce_try_condzero_arith transforms.
(noce_try_condzero): New transform.
(noce_try_condzero_arith): New transform for conditional
arithmetic that can be built up by exploiting that the
conditional-zero instruction will inject 0, which acts
as the neutral element for operations.
(noce_process_if_block): Call noce_try_condzero and
noce_try_condzero_arith.
A few things we've noted while working with this patch internally.
1. The canonical conditional-zero-or-value requires operand 2 to be a
register. That will tend to inhibit if-conversion of something like a
shift-by-constant. This showed up somewhere in leela.
2. Internally we fixed #1 by forcing that argument into a register when
it was a constant and reload hasn't completed. This (naturally)
resulted in more if-conversions happening. That in turn caused
perlbench to fail. This was tracked down to a conditional-and sequence.
I think the call to noce_emit_condzero needs adjustment to pass arg0
instead of arg1 when OP == AND.
3. The canaonical conditional-zero-or-value assumes the target can do a
generic SEQ/SNE of two register values. As you know, on RISC-V we have
SEQZ/SNEZ. So we've added another fallback path to handle that case in
noce_emit_condzero. You subtract the two values, then you can do an
SEQZ/SNEZ on the result.
Formatting nits noted below.
+ if (!noce_emit_insn (gen_rtx_SET (tmp, cond))) {
+ end_sequence ();
+ return NULL_RTX;
+ }
Open-curly on new line, indented two places from the IF, similarly for
the close curly. That will probably require adjusting the indentation
of the statements.
+ if (code == NE && cond_arg1 == const0_rtx &&
+ REG_P (b) && rtx_equal_p (b, cond_arg0))
Don't end with &&, instead bring it down indented one position from the
open parenthesis.
+ {
+ orig_b = b;
+ b = const0_rtx;
+ }
+
+ /* We may encounter the form "(b == 0) ? b : a", which can be
+ simplied to "(b == 0) ? 0 : a". */
+ if (code == EQ && cond_arg1 == const0_rtx &&
+ REG_P (b) && rtx_equal_p (b, cond_arg0))
Likewise.
+ {
+ target = noce_emit_condzero(if_info, reversep ? a : b, reversep);
Missing whitespace before open-paren of function call.
+
+ if (op != PLUS && op != MINUS && op != IOR && op != XOR &&
+ op != ASHIFT && op != ASHIFTRT && op != LSHIFTRT && op != AND)
Bring the "&&" down to the next line here too.
+
+ if (!rtx_equal_p (if_info->x, arg0))
+ return FALSE;
?!? What's this for?
+
+ target = noce_emit_condzero(if_info, arg1, op != AND ? true : false);
Missing space before open-paren.
+
+ emit_insn_before_setloc(seq, if_info->jump,
+ INSN_LOCATION(if_info->insn_a));
Here too.
I think the ChangeLog entry for the testsuite needs its filenames
updated :-)
While this was submitted before stage1 closed, I think we're probably
too late for it to go forward until we re-open stage1.
jeff