On Thu, Oct 8, 2026 at 6:33 AM Naveen
<[email protected]> wrote:
>
> The x86 backend misses an optimization for a zero-extended HImode
> unsigned minimum with a constant. Add an HImode conditional-move
> pattern that preserves the flags dependency and use it in a new
> zero-extension splitter.
>
> This allows x86-64 to use a HImode conditional move and avoids the
> redundant zero-extension instruction.
>
> Will be working on the tree-level fold that makes the zero-extend
> redundancy visible to all optimizers.
>
> Regression tested on x86_64-linux with no new failures.
>
> gcc/ChangeLog:
>
> PR target/94543
> * config/i386/i386.md (*movhicc_noc_flags_use): New insn
> pattern.
> (*zero_extend_uminhisi2): New insn-and-split pattern.
>
> gcc/testsuite/ChangeLog:
>
> PR target/94543
> * gcc.target/i386/pr94543.c: New test.
>
> Signed-off-by: Naveen <[email protected]>
> ---
> gcc/config/i386/i386.md | 39 +++++++++++++++++++++++++
> gcc/testsuite/gcc.target/i386/pr94543.c | 11 +++++++
> 2 files changed, 50 insertions(+)
> create mode 100644 gcc/testsuite/gcc.target/i386/pr94543.c
>
> diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
> index d5b90291fc9..a800dafda29 100644
> --- a/gcc/config/i386/i386.md
> +++ b/gcc/config/i386/i386.md
> @@ -27513,6 +27513,20 @@
> (set (match_dup 0)
> (neg:SWI (ltu:SWI (reg:CCC FLAGS_REG) (const_int 0))))])
>
> +(define_insn "*movhicc_noc_flags_use"
> + [(set (match_operand:HI 0 "register_operand" "=r,r")
> + (if_then_else:HI (match_operator 1 "ix86_comparison_operator"
> + [(reg FLAGS_REG) (const_int 0)])
> + (match_operand:HI 2 "nonimmediate_operand" "rm,0")
> + (match_operand:HI 3 "nonimmediate_operand" "0,rm")))
> + (use (reg:CC FLAGS_REG))]
> + "TARGET_CMOVE && !(MEM_P (operands[2]) && MEM_P (operands[3]))"
> + "@
> + cmov%O2%C1\t{%2, %0|%0, %2}
> + cmov%O2%c1\t{%3, %0|%0, %3}"
> + [(set_attr "type" "icmov")
> + (set_attr "mode" "HI")])
If you want to go this way, you will have to define something like
[(set (strict_low_part (match_operand:HI 0 "register_operand" "+r,r"))
(if_then_else:HI (match_operator 1 "ix86_comparison_operator"
[(reg FLAGS_REG) (const_int 0)])
(match_operand:HI 2 "nonimmediate_operand" "rm,0")
(match_operand:HI 3 "nonimmediate_operand" "0,rm")))
... but you will probably hit PR82524 here. See e.g.
"*add<mode>_1_slp" for a workaround.
Uros.
> +
> (define_insn "*mov<mode>cc_noc"
> [(set (match_operand:SWI248 0 "register_operand" "=r,r,r,r,r")
> (if_then_else:SWI248 (match_operator 1 "ix86_comparison_operator"
> @@ -28098,6 +28112,31 @@
>
> ;; min/max patterns
>
> +(define_insn_and_split "*zero_extend_uminhisi2"
> + [(set (match_operand:SI 0 "register_operand" "=&r")
> + (zero_extend:SI
> + (umin:HI (match_operand:HI 1 "register_operand")
> + (match_operand:HI 2 "const_int_operand"))))]
> + "TARGET_64BIT && TARGET_CMOVE && !TARGET_PARTIAL_REG_STALL
> + && INTVAL (operands[2]) >= 0
> + && (unsigned HOST_WIDE_INT) INTVAL (operands[2])
> + <= GET_MODE_MASK (HImode)"
> + "#"
> + "&& 1"
> + [(set (match_dup 0) (match_dup 2))
> + (set (reg:CC FLAGS_REG)
> + (compare:CC (match_dup 1) (match_dup 3)))
> + (parallel
> + [(set (match_dup 4)
> + (if_then_else:HI (leu (reg:CC FLAGS_REG) (const_int 0))
> + (match_dup 1)
> + (match_dup 3)))
> + (use (reg:CC FLAGS_REG))])]
> +{
> + operands[3] = lowpart_subreg (HImode, operands[0], SImode);
> + operands[4] = operands[3];
> +})
> +
> (define_code_attr maxmin_rel
> [(smax "GE") (smin "LE") (umax "GEU") (umin "LEU")])
>
> diff --git a/gcc/testsuite/gcc.target/i386/pr94543.c
> b/gcc/testsuite/gcc.target/i386/pr94543.c
> new file mode 100644
> index 00000000000..d8458171e7b
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr94543.c
> @@ -0,0 +1,11 @@
> +/* PR target/94543 */
> +/* { dg-do compile { target { ! ia32 } } } */
> +/* { dg-options "-O2 -march=x86-64" } */
> +/* { dg-final { scan-assembler "cmovbe\[ \t\]+%di, %ax" } } */
> +/* { dg-final { scan-assembler-not "movzwl" } } */
> +
> +unsigned
> +f (unsigned short x)
> +{
> + return (x > 0xff ? 0xff : x) & 0xff;
> +}
> --
> 2.34.1
>