https://gcc.gnu.org/g:31c8e6c019106d3e816e1bae77c4c9a70adf6957
commit r17-1950-g31c8e6c019106d3e816e1bae77c4c9a70adf6957 Author: liuhongt <[email protected]> Date: Sun Jun 21 23:29:49 2026 -0700 i386: Allow immediate cmov arm in *cmov_bt<mode> [PR125907] The "bt followed by cmov" splitter only matched when both cmov arms were nonimmediate_operand, so "(a & (1 << b)) ? c : imm" could not be folded into bt + cmov and instead emitted a sal/shr + and + cmov sequence. Keep the existing define_split for the two-register case unchanged, and add a separate define_insn_and_split for the immediate false-arm case. The immediate arm must be materialized into a register, and that extra move cannot be expressed in a combine define_split (which is limited to two insns), so the new pattern is a define_insn_and_split that combine recognizes as a single insn. Because it is a real insn whose split sets the flags register, it carries a (clobber (reg:CC FLAGS_REG)), matching the sibling bt helpers. gcc/ChangeLog: PR target/125907 * config/i386/i386.md (*cmov_bt<SWI248:mode>): New define_insn_and_split handling a const_int false arm, forced into a register by the splitter. Leave the existing define_split for the nonimmediate case unchanged. gcc/testsuite/ChangeLog: PR target/125907 * gcc.target/i386/bt-cmov-1.c: New test. Diff: --- gcc/config/i386/i386.md | 35 +++++++++++++++++++++++++++++++ gcc/testsuite/gcc.target/i386/bt-cmov-1.c | 33 +++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 00bc37b1cb03..8fb70eccd249 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -20248,6 +20248,41 @@ std::swap (operands[3], operands[4]); }) +;; The same as above, but with operands[4] as const_int_operand. A combine +;; define_split cannot express the extra move needed to materialize the +;; immediate into a register (it is limited to two insns), so use a +;; define_insn_and_split that combine recognizes as a single insn. +(define_insn_and_split "*cmov_bt<SWI248:mode>" + [(set (match_operand:SWI248 0 "register_operand") + (if_then_else:SWI248 + (match_operator 5 "bt_comparison_operator" + [(zero_extract:SWI48 + (match_operand:SWI48 1 "nonimmediate_operand") + (const_int 1) + (match_operand:QI 2 "register_operand")) + (const_int 0)]) + (match_operand:SWI248 3 "nonimmediate_operand") + (match_operand:SWI248 4 "const_int_operand"))) + (clobber (reg:CC FLAGS_REG))] + "TARGET_USE_BT && TARGET_CMOVE + && ix86_pre_reload_split ()" + "#" + "&& 1" + [(set (reg:CCC FLAGS_REG) + (compare:CCC + (const_int 0) + (zero_extract:SWI48 (match_dup 1) (const_int 1) (match_dup 2)))) + (set (match_dup 0) + (if_then_else:SWI248 (ltu (reg:CCC FLAGS_REG) (const_int 0)) + (match_dup 3) + (match_dup 4)))] +{ + operands[4] = force_reg (<SWI248:MODE>mode, operands[4]); + + if (GET_CODE (operands[5]) == EQ) + std::swap (operands[3], operands[4]); +}) + ;; Help combine recognize bt followed by setc (define_insn_and_split "*bt<mode>_setcqi" [(set (subreg:SWI48 (match_operand:QI 0 "register_operand") 0) diff --git a/gcc/testsuite/gcc.target/i386/bt-cmov-1.c b/gcc/testsuite/gcc.target/i386/bt-cmov-1.c new file mode 100644 index 000000000000..07234c2a9a02 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/bt-cmov-1.c @@ -0,0 +1,33 @@ +/* PR target/125907 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -march=x86-64 -mtune=generic" } */ + +/* Verify that "(a & (1 << b)) ? c : imm" and the equivalent + "((a >> b) & 1) ? c : imm" forms use bt + cmov even when one of the + cmov arms is an immediate. The immediate arm is materialized into a + register by the *cmov_bt<mode> splitter. */ + +unsigned f0 (unsigned a, unsigned b, unsigned c) +{ + return (a & (1 << b)) ? c : 2; +} + +unsigned f1 (unsigned a, unsigned b, unsigned c) +{ + return ((a >> b) & 1) ? c : 2; +} + +unsigned f2 (unsigned a, unsigned b, unsigned c) +{ + return (a & (1 << b)) ? 2 : c; +} + +unsigned f3 (unsigned a, unsigned b, unsigned c) +{ + return ((a >> b) & 1) ? 2 : c; +} + +/* { dg-final { scan-assembler-times "bt\[l\]?\[ \\t\]+" 4 } } */ +/* { dg-final { scan-assembler-times "cmov\[n\]*c" 4 } } */ +/* { dg-final { scan-assembler-not "sal" } } */ +/* { dg-final { scan-assembler-not "shr" } } */
