Hi, There is a new insn on my target, which has a nested if_then_else and set -1, 0 and 1 according to a comparison.
[(set (match_operand:SI 0 "gpc_reg_operand" "=r") (if_then_else:SI (lt (match_operand:CC 1 "cc_reg_operand" "y") (const_int 0)) (const_int -1) (if_then_else (gt (match_dup 1) (const_int 0)) (const_int 1) (const_int 0))))] In ifcvt pass, it probably contains a comparison, a branch, a setcc and a constant set. 8: r122:CC=cmp(r120:DI#0,r121:DI#0) 9: pc={(r122:CC<0)?L29:pc} 14: r118:SI=r122:CC>0 29: L29: 5: r118:SI=0xffffffffffffffff This patch adds the new conversion into ifcvt and convert this kind of branch into a nested if-then-else insn if the target supports such pattern. HAVE_ternary_conditional_set indicates if the target has such nested if-then-else insn. It's set in genconfig. noce_try_ternary_cset will be executed to detect suitable pattern and convert it to the nested if-then-else insn if HAVE_ternary_conditional_set is set. The hook TARGET_NOCE_TERNARY_CSET_P detects target specific pattern and output conditions and setting integers for the nested if-then-else. Bootstrapped and tested on powerpc64-linux BE/LE and x86 with no regressions. Is this okay for trunk? Any recommendations? Thanks a lot. ChangeLog 2022-11-23 Haochen Gui <guih...@linux.ibm.com> gcc/ * doc/tm.texi: Regenerate. * doc/tm.texi.in (TARGET_NOCE_TERNARY_CSET_P): Document new hook. * genconfig.cc (have_ternary_cset_flag): New. (walk_insn_part): Detect nested if-then-else with const_int setting and set have_ternary_cset_flag. (HAVE_ternary_conditional_set): Define. * ifcvt.cc (noce_emit_ternary_cset): New function to emit nested if-then-else insns. (noce_try_ternary_cset): Detect ternary conditional set and emit the insn. (noce_process_if_block): Try to do ternary condition set convertion when a target supports ternary conditional set insn. * target.def (noce_ternary_cset_p): New hook. * targhooks.cc (default_noce_ternary_cset_p): New function. * targhooks.h (default_noce_ternary_cset_p): New declare. patch.diff diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 92bda1a7e14..9823eccbe68 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -7094,6 +7094,15 @@ the @code{POLY_VALUE_MIN}, @code{POLY_VALUE_MAX} and implementation returns the lowest possible value of @var{val}. @end deftypefn +@deftypefn {Target Hook} bool TARGET_NOCE_TERNARY_CSET_P (struct noce_if_info *@var{if_info}, rtx *@var{outer_cond}, rtx *@var{inner_cond}, int *@var{int1}, int *@var{int2}, int *@var{int3}) +This hook returns true if the if-then-else-join blocks describled in +@code{if_info} can be converted to a ternary conditional set implemented by +a nested if-then-else insn. The @code{int1}, @code{int2} and @code{int3} +are three possible results of the nested if-then-else insn. +@code{outer_cond} and @code{inner_cond} are the conditions for outer and +if-then-else. +@end deftypefn + @node Scheduling @section Adjusting the Instruction Scheduler diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 112462310b1..1d6f28cc50a 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -4631,6 +4631,8 @@ Define this macro if a non-short-circuit operation produced by @hook TARGET_ESTIMATED_POLY_VALUE +@hook TARGET_NOCE_TERNARY_CSET_P + @node Scheduling @section Adjusting the Instruction Scheduler diff --git a/gcc/genconfig.cc b/gcc/genconfig.cc index b7c6b48eec6..902c832cf5a 100644 --- a/gcc/genconfig.cc +++ b/gcc/genconfig.cc @@ -33,6 +33,7 @@ static int max_recog_operands; /* Largest operand number seen. */ static int max_dup_operands; /* Largest number of match_dup in any insn. */ static int max_clobbers_per_insn; static int have_cmove_flag; +static int have_ternary_cset_flag; static int have_cond_exec_flag; static int have_lo_sum_flag; static int have_rotate_flag; @@ -136,6 +137,12 @@ walk_insn_part (rtx part, int recog_p, int non_pc_set_src) && GET_CODE (XEXP (part, 1)) == MATCH_OPERAND && GET_CODE (XEXP (part, 2)) == MATCH_OPERAND) have_cmove_flag = 1; + else if (recog_p && non_pc_set_src + && GET_CODE (XEXP (part, 1)) == CONST_INT + && GET_CODE (XEXP (part, 2)) == IF_THEN_ELSE + && GET_CODE (XEXP (XEXP (part, 2), 1)) == CONST_INT + && GET_CODE (XEXP (XEXP (part, 2), 2)) == CONST_INT) + have_ternary_cset_flag = 1; break; case COND_EXEC: @@ -328,6 +335,11 @@ main (int argc, const char **argv) else printf ("#define HAVE_conditional_move 0\n"); + if (have_ternary_cset_flag) + printf ("#define HAVE_ternary_conditional_set 1\n"); + else + printf ("#define HAVE_ternary_conditional_set 0\n"); + if (have_cond_exec_flag) printf ("#define HAVE_conditional_execution 1\n"); else diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc index eb8efb89a89..774dff21719 100644 --- a/gcc/ifcvt.cc +++ b/gcc/ifcvt.cc @@ -1830,6 +1830,42 @@ noce_emit_cmove (struct noce_if_info *if_info, rtx x, enum rtx_code code, return NULL_RTX; } +/* Emit a nested if-then-else insn. */ + +static rtx +noce_emit_ternary_cset (rtx x, rtx outer_cond, rtx inner_cond, + int a, int b, int c) +{ + rtx target; + machine_mode mode; + machine_mode orig_mode = GET_MODE (x); + + FOR_EACH_MODE_FROM (mode, orig_mode) + { + rtx inner_if_then_else = gen_rtx_IF_THEN_ELSE (mode, inner_cond, + GEN_INT (b), GEN_INT (c)); + rtx outer_if_then_else = gen_rtx_IF_THEN_ELSE (mode, outer_cond, + GEN_INT (a), + inner_if_then_else); + target = (mode == orig_mode) ? x : gen_reg_rtx (mode); + rtx set = gen_rtx_SET (target, outer_if_then_else); + start_sequence (); + rtx_insn *insn = emit_insn (set); + + if (recog_memoized (insn) >= 0) + { + rtx_insn *seq = get_insns (); + end_sequence (); + emit_insn (seq); + + return target; + } + } + + end_sequence (); + return NULL_RTX; +} + /* Try only simple constants and registers here. More complex cases are handled in noce_try_cmove_arith after noce_try_store_flag_arith has had a go at it. */ @@ -2987,6 +3023,43 @@ noce_try_bitop (struct noce_if_info *if_info) return TRUE; } +/* Try to find pattern "a < b ? -1 : (a > b ? 1 : 0);" and convert it to + a nested if-then-else insn. */ + +static int +noce_try_ternary_cset (struct noce_if_info *if_info) +{ + rtx outer_cond = NULL_RTX, inner_cond = NULL_RTX; + int int1 = 0, int2 = 0, int3 = 0; + + if (targetm.noce_ternary_cset_p (if_info, &outer_cond, &inner_cond, + &int1, &int2, &int3)) + { + start_sequence (); + rtx target = noce_emit_ternary_cset (if_info->x, outer_cond, inner_cond, + int1, int2, int3); + if (target) + { + rtx_insn *ifcvt_seq; + + if (target != if_info->x) + noce_emit_move_insn (if_info->x, target); + + ifcvt_seq = end_ifcvt_sequence (if_info); + if (!ifcvt_seq) + return false; + + emit_insn_before_setloc (ifcvt_seq, if_info->jump, + INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "noce_try_ternary_cset"; + return true; + } + + end_sequence (); + } + + return false; +} /* Similar to get_condition, only the resulting condition must be valid at JUMP, instead of at EARLIEST. @@ -3963,6 +4036,9 @@ noce_process_if_block (struct noce_if_info *if_info) if (HAVE_conditional_move && noce_try_cmove (if_info)) goto success; + if (HAVE_ternary_conditional_set + && noce_try_ternary_cset (if_info)) + goto success; if (! targetm.have_conditional_execution ()) { if (noce_try_addcc (if_info)) diff --git a/gcc/target.def b/gcc/target.def index 2a7fa68f83d..1b983bf852e 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -3907,6 +3907,20 @@ candidate as a replacement for the if-convertible sequence described in\n\ bool, (rtx_insn *seq, struct noce_if_info *if_info), default_noce_conversion_profitable_p) +/* Return true if the if-then-else-join blocks can be converted to a nested\n\ +if-then-else insn. */ +DEFHOOK +(noce_ternary_cset_p, + "This hook returns true if the if-then-else-join blocks describled in\n\ +@code{if_info} can be converted to a ternary conditional set implemented by\n\ +a nested if-then-else insn. The @code{int1}, @code{int2} and @code{int3}\n\ +are three possible results of the nested if-then-else insn.\n\ +@code{outer_cond} and @code{inner_cond} are the conditions for outer and\n\ +if-then-else.", +bool, (struct noce_if_info *if_info, rtx *outer_cond, rtx *inner_cond, + int *int1, int *int2, int *int3), +default_noce_ternary_cset_p) + /* Return true if new_addr should be preferred over the existing address used by memref in insn. */ DEFHOOK diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc index b15ae19bcb6..179324e3f0b 100644 --- a/gcc/targhooks.cc +++ b/gcc/targhooks.cc @@ -2673,4 +2673,17 @@ default_gcov_type_size (void) return TYPE_PRECISION (long_long_integer_type_node) > 32 ? 64 : 32; } +/* The default implementation of TARGET_NOCE_TERNARY_CSET_P. */ + +bool +default_noce_ternary_cset_p (struct noce_if_info *if_info ATTRIBUTE_UNUSED, + rtx *outer_cond ATTRIBUTE_UNUSED, + rtx *inner_cond ATTRIBUTE_UNUSED, + int *int1 ATTRIBUTE_UNUSED, + int *int2 ATTRIBUTE_UNUSED, + int *int3 ATTRIBUTE_UNUSED) +{ + return false; +} + #include "gt-targhooks.h" diff --git a/gcc/targhooks.h b/gcc/targhooks.h index ecce55ebe79..a58bc8ef6a6 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -296,5 +296,7 @@ extern rtx default_memtag_extract_tag (rtx, rtx); extern rtx default_memtag_untagged_pointer (rtx, rtx); extern HOST_WIDE_INT default_gcov_type_size (void); +extern bool default_noce_ternary_cset_p (struct noce_if_info *, rtx *, rtx *, + int *, int *, int *); #endif /* GCC_TARGHOOKS_H */