On Thu Oct 8, 2026 at 8:05 AM PDT, Roger Sayle wrote:
> diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
> index b09a5077820..1c7ae7c3e70 100644
> --- a/gcc/config/i386/i386.cc
> +++ b/gcc/config/i386/i386.cc
> @@ -28130,6 +28130,8 @@ ix86_optab_supported_p (int op, machine_mode mode1, 
> machine_mode,
>      case exp10_optab:
>      case exp2_optab:
>      case expm1_optab:
> +    case fmax_optab:
> +    case fmin_optab:
>      case ldexp_optab:
>      case scalb_optab:
>      case lround_optab:
> diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
> index 0d4749e6e09..0a018ed5d3b 100644
> --- a/gcc/config/i386/i386.md
> +++ b/gcc/config/i386/i386.md
> @@ -1080,6 +1080,8 @@
>  (define_code_attr maxmin_int [(smax "maxs") (smin "mins")
>                             (umax "maxu") (umin "minu")])
>  (define_code_attr maxmin_float [(smax "max") (smin "min")])
> +(define_code_attr maxmin_unspec_ieee [(smax "UNSPEC_IEEE_MAX")
> +                                   (smin "UNSPEC_IEEE_MIN")])
>  
>  (define_int_iterator IEEE_MAXMIN
>       [UNSPEC_IEEE_MAX
> diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
> index 0ce82789dc6..566b6c1e893 100644
> --- a/gcc/config/i386/sse.md
> +++ b/gcc/config/i386/sse.md
> @@ -3635,6 +3635,36 @@
>     (set_attr "prefix" "<round_saeonly_scalar_prefix>")
>     (set_attr "mode" "<ssescalarmode>")])
>  
> +;; Expander for __builtin_fmax and __builtin_fmin
> +(define_expand "f<maxmin_float><mode>3"
> +  [(set (match_operand:MODEF 0 "register_operand")
> +        (smaxmin:MODEF
> +          (match_operand:MODEF 1 "register_operand")
> +          (match_operand:MODEF 2 "nonimmediate_operand")))]
> +  "TARGET_SSE4_1
> +   && TARGET_SSE_MATH
> +   && SSE_FLOAT_MODE_P (<MODE>mode)"
When both operands is sNAN, maxss won't quiet NAN, but fmax doesn,
 so should be restrict the condition with && !HONOR_SNANS (<MODE>mode)?
> +{
> +  /* Expand *ieee_<code><mode>3 to preserve operand order.  */
> +  rtx result = gen_reg_rtx (<MODE>mode);
> +  rtx unspec = gen_rtx_UNSPEC (<MODE>mode,
> +                            gen_rtvec (2, operands[1], operands[2]),
> +                            <maxmin_unspec_ieee>);
> +  emit_insn (gen_rtx_SET (result, unspec));
> +
> +  /* Expand gen_<sse>_vmmaskcmp<ssevecmodelower>.  */
> +  rtx vmask = gen_reg_rtx (<ssevecmode>mode);
> +  rtx vop1 = lowpart_subreg (<ssevecmode>mode, operands[1], <MODE>mode);
> +  rtx cmp = gen_rtx_UNORDERED (<ssevecmode>mode, vop1, vop1);
op2(NOT op1) of max/min{ss,sd} need to be checked if it's NAN.

For MAXSD:
MAX(SRC1, SRC2)
{
    IF ((SRC1 = 0.0) and (SRC2 = 0.0)) THEN DEST := SRC2;
        ELSE IF (SRC1 = NaN) THEN DEST := SRC2; FI;
        ELSE IF (SRC2 = NaN) THEN DEST := SRC2; FI;
        ELSE IF (SRC1 > SRC2) THEN DEST := SRC1;
        ELSE DEST := SRC2;
    FI;
}
maxsd return op2 when op1 is not NAN, but op2 is NAN, but according to ieee,
the valid value(op1) should be returned. 

So for fmax
if op2 is NAN,  return op1(it's ok if op1 is also NAN)
else return result of MAXSD

> +  emit_insn (gen_rtx_SET (vmask, merge));
> +
> +  rtx smask = lowpart_subreg (<MODE>mode, vmask, <ssevecmode>mode);
Also you can simply use
 rtx op2 = force_reg (<MODE>mode, operands[2]);
 emit_insn (gen_rtx_SET (smask, gen_rtx_UNORDERED (<MODE>mode, op2,
                                                   op2)));
which is setcc_<mode>_sse, no need to lowpart_subreg it to vector mode,
do comparison and lowpart_subreg it back to scalar mode.

> +  rtx merge = gen_rtx_VEC_MERGE (<ssevecmode>mode, cmp, vop1, const1_rtx);
> +  emit_insn (gen_sse4_1_blendv<ssemodesuffix> (operands[0], result,
> +                                            operands[1], smask));
> +  DONE;
> +})
> +
>  (define_mode_attr addsub_cst [(V4DF "5") (V2DF "1")
>                                (V4SF "5") (V8SF "85")])
>  
> diff --git a/gcc/testsuite/gcc.target/i386/builtin-fmax-1.c 
> b/gcc/testsuite/gcc.target/i386/builtin-fmax-1.c
> new file mode 100644
> index 00000000000..b7b92dc4fe6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/builtin-fmax-1.c
> @@ -0,0 +1,19 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -mavx -mfpmath=sse" } */
> +
> +double foo(double x, double y)
> +{
> +  return __builtin_fmax (x,y);
> +}
> +
> +float bar(float x, float y)
> +{
> +  return __builtin_fmaxf (x,y);
> +}
> +
> +/* { dg-final { scan-assembler-times "vblendvpd" 1 } } */
> +/* { dg-final { scan-assembler-times "vblendvps" 1 } } */
> +/* { dg-final { scan-assembler-times "vcmpunordsd" 1 } } */
> +/* { dg-final { scan-assembler-times "vcmpunordss" 1 } } */
> +/* { dg-final { scan-assembler-times "vmaxsd" 1 } } */
> +/* { dg-final { scan-assembler-times "vmaxss" 1 } } */
> diff --git a/gcc/testsuite/gcc.target/i386/builtin-fmin-1.c 
> b/gcc/testsuite/gcc.target/i386/builtin-fmin-1.c
> new file mode 100644
> index 00000000000..fcc30de04a6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/builtin-fmin-1.c
> @@ -0,0 +1,19 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -mavx -mfpmath=sse" } */
> +
> +double foo(double x, double y)
> +{
> +  return __builtin_fmin (x,y);
> +}
> +
> +float bar(float x, float y)
> +{
> +  return __builtin_fminf (x,y);
> +}
> +
> +/* { dg-final { scan-assembler-times "vblendvpd" 1 } } */
> +/* { dg-final { scan-assembler-times "vblendvps" 1 } } */
> +/* { dg-final { scan-assembler-times "vcmpunordsd" 1 } } */
> +/* { dg-final { scan-assembler-times "vcmpunordss" 1 } } */
> +/* { dg-final { scan-assembler-times "vminsd" 1 } } */
> +/* { dg-final { scan-assembler-times "vminss" 1 } } */

Reply via email to