> On 13 Nov 2026, at 15:50, Karl Meakin <[email protected]> wrote:
>
> The floating point variants of `vminv` and `vmaxv` were not ported to
> the pragma-based framework when the integer variants were, because the
> semantics AArch64's `fminv`/`fmaxv` instructions don't match the
> semantics of GCC's `IFN_REDUC_MIN`/`IFN_REDUC_MAX`. This commit ports
> them to the pragma framework, by expanding them to `UNSPEC_FMINV` and
> `UNSPEC_FMAXV`.
>
> There are no test changes, as tests for FP variants of vminv/vmaxv were
> already included in vminv.c and vmaxv.c. The absence of any changes to
> the tests does show that there were no changes in codegen (ie no
> regressions).
>
> gcc/ChangeLog:
>
> * config/aarch64/aarch64-builtins.cc
> (aarch64_general_gimple_fold_builtin): Delete cases for handling
> `reduc_{u,s}{min,max}_scal_`.
> * config/aarch64/aarch64-neon-builtins-base.cc (build_ifn_call):
> New function.
> (struct gimple_reduce_minmax): New struct.
> * config/aarch64/aarch64-neon-builtins-base.def (vminv, vminvq,
> vmaxv, vmaxvq): New intrinsic function declarations.
> * config/aarch64/aarch64-simd-builtins.def: Delete
> `reduc_{us}{min,max}{,_nan}_scal_` builtin functions.
> * config/aarch64/aarch64-simd.md (reduc_<optab>_scal_<mode>):
> Rename to ...
> (@reduc_<optab>_scal_<mode>): ...this, so that it is callable
> from `gimple_reduce_minmax::expand ()`.
> * config/aarch64/arm_neon.h
> (vpmaxqd_f64, vpmaxs_f32, vpmaxnmqd_f64, vpmaxnms_f32,
> vpminqd_f64, vpmins_f32, vpminnmqd_f64): Call the intrinsic
> (v{min,max}{,nm}v) functions rather than the now-deleted
> `__builtin_aarch64_reduc_s{min,max}_scal` functions.
> (vmaxv_f32, vmaxvq_f32, vmaxvq_f64, vminv_f32, vminvq_f32,
> vminvq_f64, vmaxv_f16, vmaxvq_f16, vminv_f16, vminvq_f16):
> Delete intrinsic function definitions.
> ---
> gcc/config/aarch64/aarch64-builtins.cc | 12 ---
> .../aarch64/aarch64-neon-builtins-base.cc | 60 +++++++++++-
> .../aarch64/aarch64-neon-builtins-base.def | 12 ++-
> gcc/config/aarch64/aarch64-simd-builtins.def | 8 --
> gcc/config/aarch64/aarch64-simd.md | 6 +-
> gcc/config/aarch64/arm_neon.h | 92 ++-----------------
> 6 files changed, 79 insertions(+), 111 deletions(-)
>
> diff --git a/gcc/config/aarch64/aarch64-builtins.cc
> b/gcc/config/aarch64/aarch64-builtins.cc
> index 1d8baaef5c4..337ecb1088a 100644
> --- a/gcc/config/aarch64/aarch64-builtins.cc
> +++ b/gcc/config/aarch64/aarch64-builtins.cc
> @@ -5268,18 +5268,6 @@ aarch64_general_gimple_fold_builtin (unsigned int
> fcode, gcall *stmt,
> break;
> }
>
> - BUILTIN_VDQIF (UNOP, reduc_smax_scal_, 10, ALL)
> - BUILTIN_VDQ_BHSI (UNOPU, reduc_umax_scal_, 10, ALL)
> - new_stmt = gimple_build_call_internal (IFN_REDUC_MAX,
> - 1, args[0]);
> - gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt));
> - break;
> - BUILTIN_VDQIF (UNOP, reduc_smin_scal_, 10, ALL)
> - BUILTIN_VDQ_BHSI (UNOPU, reduc_umin_scal_, 10, ALL)
> - new_stmt = gimple_build_call_internal (IFN_REDUC_MIN,
> - 1, args[0]);
> - gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt));
> - break;
> BUILTIN_VSDQ_I_DI (BINOP, ashl, 3, DEFAULT)
> if (TREE_CODE (args[1]) == INTEGER_CST
> && wi::ltu_p (wi::to_wide (args[1]), element_precision (args[0])))
> diff --git a/gcc/config/aarch64/aarch64-neon-builtins-base.cc
> b/gcc/config/aarch64/aarch64-neon-builtins-base.cc
> index 8c3bcc6eb74..1f5b46f216d 100644
> --- a/gcc/config/aarch64/aarch64-neon-builtins-base.cc
> +++ b/gcc/config/aarch64/aarch64-neon-builtins-base.cc
> @@ -1176,6 +1176,62 @@ struct gimple_minmax : public gimple_function_base
> }
> };
>
> +/* Build a call to the internal function IFN.
> + Handles singleton vector arguments specially: they are cast to their
> + inner element type before the function is called, and then the return
> value
> + is cast back to the expected singleton vector type. */
> +tree
> +build_ifn_call (gimple_folder &f, internal_fn ifn, tree ret_type,
> + vec<tree> &args)
I think this function can be static.
Ok with that change.
Thanks,
Kyrill
> +{
> + for (auto &arg : args)
> + if (one_element_vector_type_p (TREE_TYPE (arg)))
> + arg = f.force_val (build_cast (TREE_TYPE (TREE_TYPE (arg)), arg));
> +
> + auto call = gimple_build_call_internal_vec (ifn, args);
> + gimple_seq_add_stmt_without_update (&f.stmts, call);
> +
> + if (!one_element_vector_type_p (ret_type))
> + {
> + auto ret = is_gimple_reg_type (ret_type) ? make_ssa_name (ret_type)
> + : create_tmp_var (ret_type);
> + gimple_call_set_lhs (call, ret);
> + return ret;
> + }
> + else
> + {
> + auto ret = create_tmp_var (TREE_TYPE (ret_type));
> + gimple_call_set_lhs (call, ret);
> + return build_vec_dup (ret_type, ret);
> + }
> +}
> +
> +template <bool min_p>
> +struct gimple_reduce_minmax : public gimple_function_base
> +{
> + rtx expand (function_expander &e) const override
> + {
> + auto arg_mode = e.args[0]->mode;
> + auto unspec = min_p ? UNSPEC_FMINV : UNSPEC_FMAXV;
> + auto icode = code_for_reduc_scal (unspec, arg_mode);
> + gcc_assert (icode != CODE_FOR_nothing);
> + return e.use_exact_insn (icode);
> + }
> +
> + gimple *fold (gimple_folder &f) const override
> + {
> + auto arg = gimple_call_arg (f.call, 0);
> + auto ret_type = TREE_TYPE (f.lhs);
> + if (FLOAT_TYPE_P (ret_type))
> + return nullptr;
> +
> + auto_vec<tree> args{};
> + args.safe_push (arg);
> + auto ifn = min_p ? IFN_REDUC_MIN : IFN_REDUC_MAX;
> + return gimple_build_assign (f.lhs, build_ifn_call (f, ifn, ret_type,
> args));
> + }
> +};
> +
> // Reinterpret
> NEON_FUNCTION (vreinterpret, vreinterpretq, gimple_reinterpret,)
>
> @@ -1218,8 +1274,8 @@ NEON_FUNCTION (vminnm, vminnmq, gimple_ifn, (IFN_FMIN))
>
> // Reductions
> NEON_FUNCTION (vaddv, vaddvq, gimple_ifn, (IFN_REDUC_PLUS))
> -NEON_FUNCTION (vmaxv, vmaxvq, gimple_ifn, (IFN_REDUC_MAX))
> -NEON_FUNCTION (vminv, vminvq, gimple_ifn, (IFN_REDUC_MIN))
> +NEON_FUNCTION (vmaxv, vmaxvq, gimple_reduce_minmax<false>,)
> +NEON_FUNCTION (vminv, vminvq, gimple_reduce_minmax<true>,)
> NEON_FUNCTION (vmaxnmv, vmaxnmvq, gimple_ifn, (IFN_REDUC_FMAX))
> NEON_FUNCTION (vminnmv, vminnmvq, gimple_ifn, (IFN_REDUC_FMIN))
>
> diff --git a/gcc/config/aarch64/aarch64-neon-builtins-base.def
> b/gcc/config/aarch64/aarch64-neon-builtins-base.def
> index e8edb2679b9..68a8e87be3c 100644
> --- a/gcc/config/aarch64/aarch64-neon-builtins-base.def
> +++ b/gcc/config/aarch64/aarch64-neon-builtins-base.def
> @@ -211,8 +211,12 @@ DEF_NEON_FUNCTION (vaddv, s_float_bhs_integer,
> ("s0,D0"))
> DEF_NEON_FUNCTION (vaddvq, sd_float_all_integer, ("s0,Q0"))
> DEF_NEON_FUNCTION (vmaxv, bhs_integer, ("s0,D0"))
> DEF_NEON_FUNCTION (vmaxvq, bhs_integer, ("s0,Q0"))
> -DEF_NEON_FUNCTION (vminv, bhs_integer, ("s0,D0"))
> -DEF_NEON_FUNCTION (vminvq, bhs_integer, ("s0,Q0"))
> +DEF_NEON_FUNCTION (vmaxv, sd_float, ("s0,D0"))
> +DEF_NEON_FUNCTION (vmaxvq, sd_float, ("s0,Q0"))
> +DEF_NEON_FUNCTION (vminv, bhs_integer, ("s0,D0"))
> +DEF_NEON_FUNCTION (vminvq, bhs_integer, ("s0,Q0"))
> +DEF_NEON_FUNCTION (vminv, sd_float, ("s0,D0"))
> +DEF_NEON_FUNCTION (vminvq, sd_float, ("s0,Q0"))
> DEF_NEON_FUNCTION (vmaxnmv, s_float, ("s0,D0"))
> DEF_NEON_FUNCTION (vmaxnmvq, sd_float, ("s0,Q0"))
> DEF_NEON_FUNCTION (vminnmv, s_float, ("s0,D0"))
> @@ -221,6 +225,10 @@ DEF_NEON_FUNCTION (vminnmvq, sd_float,
> ("s0,Q0"))
>
> // Reductions (FP16)
> #define REQUIRED_EXTENSIONS nonstreaming_only (AARCH64_FL_SIMD |
> AARCH64_FL_F16)
> +DEF_NEON_FUNCTION (vmaxv, h_float, ("s0,D0"))
> +DEF_NEON_FUNCTION (vmaxvq, h_float, ("s0,Q0"))
> +DEF_NEON_FUNCTION (vminv, h_float, ("s0,D0"))
> +DEF_NEON_FUNCTION (vminvq, h_float, ("s0,Q0"))
> DEF_NEON_FUNCTION (vmaxnmv, h_float, ("s0,D0"))
> DEF_NEON_FUNCTION (vmaxnmvq, h_float, ("s0,Q0"))
> DEF_NEON_FUNCTION (vminnmv, h_float, ("s0,D0"))
> diff --git a/gcc/config/aarch64/aarch64-simd-builtins.def
> b/gcc/config/aarch64/aarch64-simd-builtins.def
> index d1a19cebe97..03d97e2265e 100644
> --- a/gcc/config/aarch64/aarch64-simd-builtins.def
> +++ b/gcc/config/aarch64/aarch64-simd-builtins.def
> @@ -495,14 +495,6 @@
> BUILTIN_VALL (UNOP, reduc_plus_scal_, 10, DEFAULT)
> BUILTIN_VDQ_I (UNOPU, reduc_plus_scal_, 10, DEFAULT)
>
> - /* Implemented by reduc_<maxmin_uns>_scal_<mode> (producing scalar). */
> - BUILTIN_VDQIF_F16 (UNOP, reduc_smax_scal_, 10, DEFAULT)
> - BUILTIN_VDQIF_F16 (UNOP, reduc_smin_scal_, 10, DEFAULT)
> - BUILTIN_VDQ_BHSI (UNOPU, reduc_umax_scal_, 10, DEFAULT)
> - BUILTIN_VDQ_BHSI (UNOPU, reduc_umin_scal_, 10, DEFAULT)
> - BUILTIN_VHSDF (UNOP, reduc_smax_nan_scal_, 10, DEFAULT)
> - BUILTIN_VHSDF (UNOP, reduc_smin_nan_scal_, 10, DEFAULT)
> -
> /* Implemented by <fmaxmin><mode>3. */
> VAR1 (BINOP, fmax, 3, FP, hf)
> VAR1 (BINOP, fmin, 3, FP, hf)
> diff --git a/gcc/config/aarch64/aarch64-simd.md
> b/gcc/config/aarch64/aarch64-simd.md
> index 7691fe2fdcd..7c8bc6985a1 100644
> --- a/gcc/config/aarch64/aarch64-simd.md
> +++ b/gcc/config/aarch64/aarch64-simd.md
> @@ -2962,9 +2962,9 @@ (define_expand "aarch64_<su>mlsl_hi_laneq<mode>"
> )
>
> ;; FP vector operations.
> -;; AArch64 AdvSIMD supports single-precision (32-bit) and
> +;; AArch64 AdvSIMD supports single-precision (32-bit) and
> ;; double-precision (64-bit) floating-point data types and arithmetic as
> -;; defined by the IEEE 754-2008 standard. This makes them vectorizable
> +;; defined by the IEEE 754-2008 standard. This makes them vectorizable
> ;; without the need for -ffast-math or -funsafe-math-optimizations.
> ;;
> ;; Floating-point operations can raise an exception. Vectorizing such
> @@ -4231,7 +4231,7 @@ (define_expand "popcount<mode>2"
>
> ;; Template for outputting a scalar, so we can create __builtins which can be
> ;; gimple_fold'd to the IFN_REDUC_(MAX|MIN) function. (This is FP smax/smin).
> -(define_expand "reduc_<optab>_scal_<mode>"
> +(define_expand "@reduc_<optab>_scal_<mode>"
> [(match_operand:<VEL> 0 "register_operand")
> (unspec:<VEL> [(match_operand:VHSDF 1 "register_operand")]
> FMAXMINV)]
> diff --git a/gcc/config/aarch64/arm_neon.h b/gcc/config/aarch64/arm_neon.h
> index a8d1c4f61da..204e2ac47db 100644
> --- a/gcc/config/aarch64/arm_neon.h
> +++ b/gcc/config/aarch64/arm_neon.h
> @@ -10858,14 +10858,14 @@ __extension__ extern __inline float64_t
> __attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> vpmaxqd_f64 (float64x2_t __a)
> {
> - return __builtin_aarch64_reduc_smax_nan_scal_v2df (__a);
> + return vmaxvq_f64 (__a);
> }
>
> __extension__ extern __inline float32_t
> __attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> vpmaxs_f32 (float32x2_t __a)
> {
> - return __builtin_aarch64_reduc_smax_nan_scal_v2sf (__a);
> + return vmaxv_f32 (__a);
> }
>
> /* vpmaxnm */
> @@ -10895,14 +10895,14 @@ __extension__ extern __inline float64_t
> __attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> vpmaxnmqd_f64 (float64x2_t __a)
> {
> - return __builtin_aarch64_reduc_smax_scal_v2df (__a);
> + return vmaxnmvq_f64 (__a);
> }
>
> __extension__ extern __inline float32_t
> __attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> vpmaxnms_f32 (float32x2_t __a)
> {
> - return __builtin_aarch64_reduc_smax_scal_v2sf (__a);
> + return vmaxnmv_f32 (__a);
> }
>
> /* vpmin */
> @@ -11022,14 +11022,14 @@ __extension__ extern __inline float64_t
> __attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> vpminqd_f64 (float64x2_t __a)
> {
> - return __builtin_aarch64_reduc_smin_nan_scal_v2df (__a);
> + return vminvq_f64 (__a);
> }
>
> __extension__ extern __inline float32_t
> __attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> vpmins_f32 (float32x2_t __a)
> {
> - return __builtin_aarch64_reduc_smin_nan_scal_v2sf (__a);
> + return vminv_f32 (__a);
> }
>
> /* vpminnm */
> @@ -11059,60 +11059,14 @@ __extension__ extern __inline float64_t
> __attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> vpminnmqd_f64 (float64x2_t __a)
> {
> - return __builtin_aarch64_reduc_smin_scal_v2df (__a);
> + return vminnmvq_f64 (__a);
> }
>
> __extension__ extern __inline float32_t
> __attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> vpminnms_f32 (float32x2_t __a)
> {
> - return __builtin_aarch64_reduc_smin_scal_v2sf (__a);
> -}
> -
> -/* vmaxv */
> -
> -__extension__ extern __inline float32_t
> -__attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> -vmaxv_f32 (float32x2_t __a)
> -{
> - return __builtin_aarch64_reduc_smax_nan_scal_v2sf (__a);
> -}
> -
> -__extension__ extern __inline float32_t
> -__attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> -vmaxvq_f32 (float32x4_t __a)
> -{
> - return __builtin_aarch64_reduc_smax_nan_scal_v4sf (__a);
> -}
> -
> -__extension__ extern __inline float64_t
> -__attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> -vmaxvq_f64 (float64x2_t __a)
> -{
> - return __builtin_aarch64_reduc_smax_nan_scal_v2df (__a);
> -}
> -
> -/* vminv */
> -
> -__extension__ extern __inline float32_t
> -__attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> -vminv_f32 (float32x2_t __a)
> -{
> - return __builtin_aarch64_reduc_smin_nan_scal_v2sf (__a);
> -}
> -
> -__extension__ extern __inline float32_t
> -__attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> -vminvq_f32 (float32x4_t __a)
> -{
> - return __builtin_aarch64_reduc_smin_nan_scal_v4sf (__a);
> -}
> -
> -__extension__ extern __inline float64_t
> -__attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> -vminvq_f64 (float64x2_t __a)
> -{
> - return __builtin_aarch64_reduc_smin_nan_scal_v2df (__a);
> + return vminnmv_f32 (__a);
> }
>
> /* vmla */
> @@ -17430,36 +17384,6 @@ vmulxq_n_f16 (float16x8_t __a, float16_t __b)
> return vmulxq_f16 (__a, vdupq_n_f16 (__b));
> }
>
> -/* ARMv8.2-A FP16 reduction vector intrinsics. */
> -
> -__extension__ extern __inline float16_t
> -__attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> -vmaxv_f16 (float16x4_t __a)
> -{
> - return __builtin_aarch64_reduc_smax_nan_scal_v4hf (__a);
> -}
> -
> -__extension__ extern __inline float16_t
> -__attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> -vmaxvq_f16 (float16x8_t __a)
> -{
> - return __builtin_aarch64_reduc_smax_nan_scal_v8hf (__a);
> -}
> -
> -__extension__ extern __inline float16_t
> -__attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> -vminv_f16 (float16x4_t __a)
> -{
> - return __builtin_aarch64_reduc_smin_nan_scal_v4hf (__a);
> -}
> -
> -__extension__ extern __inline float16_t
> -__attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
> -vminvq_f16 (float16x8_t __a)
> -{
> - return __builtin_aarch64_reduc_smin_nan_scal_v8hf (__a);
> -}
> -
> #pragma GCC pop_options
>
> /* AdvSIMD Dot Product intrinsics. */
> --
> 2.56.0
>