diff options
author | Jerry DeLisle <jvdelisle@gcc.gnu.org> | 2025-09-02 15:58:26 -0700 |
---|---|---|
committer | Jerry DeLisle <jvdelisle@gcc.gnu.org> | 2025-09-02 15:58:26 -0700 |
commit | 071b4126c613881f4cb25b4e5c39032964827f88 (patch) | |
tree | 7ed805786566918630d1d617b1ed8f7310f5fd8e /gcc/ifcvt.cc | |
parent | 845d23f3ea08ba873197c275a8857eee7edad996 (diff) | |
parent | caa1c2f42691d68af4d894a5c3e700ecd2dba080 (diff) | |
download | gcc-devel/gfortran-test.zip gcc-devel/gfortran-test.tar.gz gcc-devel/gfortran-test.tar.bz2 |
Merge branch 'master' into gfortran-testdevel/gfortran-test
Diffstat (limited to 'gcc/ifcvt.cc')
-rw-r--r-- | gcc/ifcvt.cc | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc index a0c6575..4579148 100644 --- a/gcc/ifcvt.cc +++ b/gcc/ifcvt.cc @@ -1179,6 +1179,211 @@ noce_try_move (struct noce_if_info *if_info) return false; } +/* If a sign bit test is selecting across constants, we may be able + to generate efficient code utilizing the -1/0 result of a sign + bit splat idiom. + + Do this before trying the generalized conditional move as these + (when applicable) are hopefully faster than a conditional move. */ + +static bool +noce_try_sign_bit_splat (struct noce_if_info *if_info) +{ + rtx cond = if_info->cond; + enum rtx_code code = GET_CODE (cond); + + /* We're looking for sign bit tests, so only a few cases are + interesting. LT/GE 0 LE/GT -1. */ + if (((code == LT || code == GE) + && XEXP (cond, 1) == CONST0_RTX (GET_MODE (cond))) + || ((code == LE || code == GT) + && XEXP (cond, 1) == CONSTM1_RTX (GET_MODE (cond)))) + ; + else + return false; + + /* It would be good if this could be extended since constant synthesis + on some platforms will result in blocks which fail this test. */ + if (!noce_simple_bbs (if_info)) + return false; + + /* Only try this for constants in the true/false arms and a REG + destination. We could select between 0 and a REG pretty + easily with a logical AND. */ + if (!CONST_INT_P (if_info->a) + || !CONST_INT_P (if_info->b) + || !REG_P (if_info->x)) + return false; + + machine_mode mode = GET_MODE (if_info->x); + + /* If the mode of the destination does not match the mode of + the value we're testing, then this optimization is not valid. */ + if (mode != GET_MODE (XEXP (cond, 0))) + return false; + + HOST_WIDE_INT val_a = INTVAL (if_info->a); + HOST_WIDE_INT val_b = INTVAL (if_info->b); + + rtx_insn *seq; + start_sequence (); + + /* We're testing the sign bit of this operand. */ + rtx condop = XEXP (cond, 0); + + /* To splat the sign bit we arithmetically shift the + input value right by the size of the object - 1 bits. + + Note some targets do not have strong shifters, but do have + alternative ways to generate the sign bit splat. The + profitability test when we end the sequence should reject + cases when the branchy sequence is better. */ + int splat_count = GET_MODE_BITSIZE (GET_MODE (condop)).to_constant () - 1; + rtx splat = GEN_INT (splat_count); + + rtx temp; + temp = expand_simple_binop (GET_MODE (XEXP (cond, 0)), ASHIFTRT, + XEXP (cond, 0), splat, NULL_RTX, + false, OPTAB_WIDEN); + if (!temp) + goto fail; + + /* IOR of anything with -1 still results in -1. So we can + IOR the other operand to generate a select between -1 and + an arbitrary constant. */ + if (val_a == -1) + { + if (code == LT || code == LE) + { + temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true); + if (!temp) + goto fail; + } + + temp = expand_simple_binop (mode, IOR, temp, GEN_INT (val_b), + if_info->x, false, OPTAB_WIDEN); + } + /* AND of anything with 0 is still zero. So we can AND + with the -1 operand with the a constant to select + between the constant and zero. */ + else if (val_b == 0) + { + if (code == LT || code == LE) + { + temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true); + if (!temp) + goto fail; + } + + /* Since we know the value is currenly -1 or 0, some constants may + be more easily handled by shifting the value again. A right + logical shift constructs 2^n-1 constants a left shift constructs + ~(2^n-1) constants. Given some targets don't have efficient + shifts, generate the obvious RTL for both forms and select the + one with smaller cost. */ + rtx and_form = gen_rtx_AND (mode, temp, GEN_INT (val_a)); + rtx shift_left = gen_rtx_ASHIFT (mode, temp, GEN_INT (ctz_hwi (val_a))); + HOST_WIDE_INT rshift_count + = (clz_hwi (val_a) & (GET_MODE_PRECISION (mode).to_constant() - 1)); + rtx shift_right = gen_rtx_LSHIFTRT (mode, temp, GEN_INT (rshift_count)); + bool speed_p = optimize_insn_for_speed_p (); + if (exact_log2 (val_a + 1) >= 0 + && (rtx_cost (shift_right, mode, SET, 1, speed_p) + <= rtx_cost (and_form, mode, SET, 1, speed_p))) + temp = expand_simple_binop (mode, LSHIFTRT, temp, + GEN_INT (rshift_count), + if_info->x, false, OPTAB_WIDEN); + else if (exact_log2 (~val_a + 1) >= 0 + && (rtx_cost (shift_left, mode, SET, 1, speed_p) + <= rtx_cost (and_form, mode, SET, 1, speed_p))) + temp = expand_simple_binop (mode, ASHIFT, temp, + GEN_INT (ctz_hwi (val_a)), + if_info->x, false, OPTAB_WIDEN); + else + temp = expand_simple_binop (mode, AND, temp, GEN_INT (val_a), + if_info->x, false, OPTAB_WIDEN); + } + /* Same cases, but with the test or arms swapped. These + can be realized as well, though it typically costs + an extra instruction. */ + else if (val_b == -1) + { + if (code != LT && code != LE) + { + temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true); + if (!temp) + goto fail; + } + + temp = expand_simple_binop (mode, IOR, temp, GEN_INT (val_a), + if_info->x, false, OPTAB_WIDEN); + } + else if (val_a == 0) + { + if (code != LT && code != LE) + { + temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true); + if (!temp) + goto fail; + } + + /* Since we know the value is currenly -1 or 0, some constants may + be more easily handled by shifting the value again. A right + logical shift constructs 2^n-1 constants a left shift constructs + ~(2^n-1) constants. Given some targets don't have efficient + shifts, generate the obvious RTL for both forms and select the + one with smaller cost. */ + rtx and_form = gen_rtx_AND (mode, temp, GEN_INT (val_b)); + rtx shift_left = gen_rtx_ASHIFT (mode, temp, GEN_INT (ctz_hwi (val_b))); + HOST_WIDE_INT rshift_count + = (clz_hwi (val_b) & (GET_MODE_PRECISION (mode).to_constant() - 1)); + rtx shift_right = gen_rtx_LSHIFTRT (mode, temp, GEN_INT (rshift_count)); + bool speed_p = optimize_insn_for_speed_p (); + if (exact_log2 (val_b + 1) >= 0 + && (rtx_cost (shift_right, mode, SET, 1, speed_p) + <= rtx_cost (and_form, mode, SET, 1, speed_p))) + temp = expand_simple_binop (mode, LSHIFTRT, temp, + GEN_INT (rshift_count), + if_info->x, false, OPTAB_WIDEN); + else if (exact_log2 (~val_b + 1) >= 0 + && (rtx_cost (shift_left, mode, SET, 1, speed_p) + <= rtx_cost (and_form, mode, SET, 1, speed_p))) + temp = expand_simple_binop (mode, ASHIFT, temp, + GEN_INT (ctz_hwi (val_b)), + if_info->x, false, OPTAB_WIDEN); + else + temp = expand_simple_binop (mode, AND, temp, GEN_INT (val_b), + if_info->x, false, OPTAB_WIDEN); + } + /* Nothing worked. */ + else + temp = NULL_RTX; + + if (!temp) + goto fail; + + /* Move into the final destination if the value wasn't + constructed there. */ + if (if_info->x != temp) + emit_move_insn (if_info->x, temp); + + /* This ends the sequence and tests the cost model. */ + seq = end_ifcvt_sequence (if_info); + if (!seq || !targetm.noce_conversion_profitable_p (seq, if_info)) + return false; + + /* Everything looks good. Install the if-converted sequence. */ + emit_insn_before_setloc (seq, if_info->jump, + INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "splat_sign_bit_trivial"; + return true; + + fail: + end_ifcvt_sequence (if_info); + return false; +} + + /* Try forming an IF_THEN_ELSE (cond, b, a) and collapsing that through simplify_rtx. Sometimes that can eliminate the IF_THEN_ELSE. If that is the case, emit the result into x. */ @@ -4238,6 +4443,8 @@ noce_process_if_block (struct noce_if_info *if_info) if (!targetm.have_conditional_execution () && noce_try_store_flag_constants (if_info)) goto success; + if (noce_try_sign_bit_splat (if_info)) + goto success; if (HAVE_conditional_move && noce_try_cmove (if_info)) goto success; |