; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -passes=instsimplify -S | FileCheck %s ;############################################################### ;# NaN Tests # ;############################################################### ; minnum(X, qnan) -> X ; maxnum(X, qnan) -> X ; minnum(X, snan) -> qnan ; maxnum(X, snan) -> qnan ; minimum(X, nan) -> qnan ; maximum(X, nan) -> qnan ; minimumnum(X, nan) -> X ; maximumnum(X, nan) -> X define void @minmax_qnan_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_qnan_f32( ; CHECK-NEXT: store float [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MAXNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float +nan(0x380000), ptr [[MINIMUM_RES:%.*]], align 4 ; CHECK-NEXT: store float +nan(0x380000), ptr [[MAXIMUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: ret void ; %minnum = call float @llvm.minnum.f32(float %x, float 0x7FFF000000000000) store float %minnum, ptr %minnum_res %maxnum = call float @llvm.maxnum.f32(float %x, float 0x7FFF000000000000) store float %maxnum, ptr %maxnum_res %minimum = call float @llvm.minimum.f32(float %x, float 0x7FFF000000000000) store float %minimum, ptr %minimum_res %maximum = call float @llvm.maximum.f32(float %x, float 0x7FFF000000000000) store float %maximum, ptr %maximum_res %minimumnum = call float @llvm.minimumnum.f32(float %x, float 0x7FFF000000000000) store float %minimumnum, ptr %minimumnum_res %maximumnum = call float @llvm.maximumnum.f32(float %x, float 0x7FFF000000000000) store float %maximumnum, ptr %maximumnum_res ret void } ; Note that maxnum/minnum return qnan here for snan inputs, unlike maximumnum/minimumnum define void @minmax_snan_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_snan_f32( ; CHECK-NEXT: store float +nan(0x200000), ptr [[MINNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float +nan(0x200000), ptr [[MAXNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float +nan(0x200000), ptr [[MINIMUM_RES:%.*]], align 4 ; CHECK-NEXT: store float +nan(0x200000), ptr [[MAXIMUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X:%.*]], ptr [[MINIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: ret void ; %minnum = call float @llvm.minnum.f32(float %x, float 0x7FF4000000000000) store float %minnum, ptr %minnum_res %maxnum = call float @llvm.maxnum.f32(float %x, float 0x7FF4000000000000) store float %maxnum, ptr %maxnum_res %minimum = call float @llvm.minimum.f32(float %x, float 0x7FF4000000000000) store float %minimum, ptr %minimum_res %maximum = call float @llvm.maximum.f32(float %x, float 0x7FF4000000000000) store float %maximum, ptr %maximum_res %minimumnum = call float @llvm.minimumnum.f32(float %x, float 0x7FF4000000000000) store float %minimumnum, ptr %minimumnum_res %maximumnum = call float @llvm.maximumnum.f32(float %x, float 0x7FF4000000000000) store float %maximumnum, ptr %maximumnum_res ret void } define void @minmax_qnan_nxv2f64_op0( %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_qnan_nxv2f64_op0( ; CHECK-NEXT: store [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 16 ; CHECK-NEXT: store [[X]], ptr [[MAXNUM_RES:%.*]], align 16 ; CHECK-NEXT: store splat (double +nan(0xDEAD00000)), ptr [[MINIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store splat (double +nan(0xDEAD00000)), ptr [[MAXIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: store [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: ret void ; %minnum = call @llvm.minnum.nxv2f64( splat (double 0x7FF8000DEAD00000), %x) store %minnum, ptr %minnum_res %maxnum = call @llvm.maxnum.nxv2f64( splat (double 0x7FF8000DEAD00000), %x) store %maxnum, ptr %maxnum_res %minimum = call @llvm.minimum.nxv2f64( splat (double 0x7FF8000DEAD00000), %x) store %minimum, ptr %minimum_res %maximum = call @llvm.maximum.nxv2f64( splat (double 0x7FF8000DEAD00000), %x) store %maximum, ptr %maximum_res %minimumnum = call @llvm.minimumnum.nxv2f64( splat (double 0x7FF8000DEAD00000), %x) store %minimumnum, ptr %minimumnum_res %maximumnum = call @llvm.maximumnum.nxv2f64( splat (double 0x7FF8000DEAD00000), %x) store %maximumnum, ptr %maximumnum_res ret void } ; Note that maxnum/minnum return qnan here for snan inputs, unlike maximumnum/minimumnum define void @minmax_snan_nxv2f64_op1( %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_snan_nxv2f64_op1( ; CHECK-NEXT: store splat (double +nan(0x400DEAD00DEAD)), ptr [[MINNUM_RES:%.*]], align 16 ; CHECK-NEXT: store splat (double +nan(0x400DEAD00DEAD)), ptr [[MAXNUM_RES:%.*]], align 16 ; CHECK-NEXT: store splat (double +nan(0x400DEAD00DEAD)), ptr [[MINIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store splat (double +nan(0x400DEAD00DEAD)), ptr [[MAXIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store [[X:%.*]], ptr [[MINIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: store [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: ret void ; %minnum = call @llvm.minnum.nxv2f64( splat (double 0x7FF400DEAD00DEAD), %x) store %minnum, ptr %minnum_res %maxnum = call @llvm.maxnum.nxv2f64( splat (double 0x7FF400DEAD00DEAD), %x) store %maxnum, ptr %maxnum_res %minimum = call @llvm.minimum.nxv2f64( splat (double 0x7FF400DEAD00DEAD), %x) store %minimum, ptr %minimum_res %maximum = call @llvm.maximum.nxv2f64( splat (double 0x7FF400DEAD00DEAD), %x) store %maximum, ptr %maximum_res %minimumnum = call @llvm.minimumnum.nxv2f64( splat (double 0x7FF400DEAD00DEAD), %x) store %minimumnum, ptr %minimumnum_res %maximumnum = call @llvm.maximumnum.nxv2f64( splat (double 0x7FF400DEAD00DEAD), %x) store %maximumnum, ptr %maximumnum_res ret void } ; For maxnum and minnum, we cannot optimize this in InstSimplify, as the result should ; return <%x0, QNaN> and InstSimplify cannot create the extra instructions required to construct this. define void @minmax_mixed_snan_qnan_v2f64(<2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_mixed_snan_qnan_v2f64( ; CHECK-NEXT: [[MINNUM:%.*]] = call <2 x double> @llvm.minnum.v2f64(<2 x double> , <2 x double> [[X:%.*]]) ; CHECK-NEXT: store <2 x double> [[MINNUM]], ptr [[MINNUM_RES:%.*]], align 16 ; CHECK-NEXT: [[MAXNUM:%.*]] = call <2 x double> @llvm.maxnum.v2f64(<2 x double> , <2 x double> [[X]]) ; CHECK-NEXT: store <2 x double> [[MAXNUM]], ptr [[MAXNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> , ptr [[MINIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> , ptr [[MAXIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: ret void ; %minnum = call <2 x double> @llvm.minnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %minnum, ptr %minnum_res %maxnum = call <2 x double> @llvm.maxnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %maxnum, ptr %maxnum_res %minimum = call <2 x double> @llvm.minimum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %minimum, ptr %minimum_res %maximum = call <2 x double> @llvm.maximum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %maximum, ptr %maximum_res %minimumnum = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %minimumnum, ptr %minimumnum_res %maximumnum = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %maximumnum, ptr %maximumnum_res ret void } ; Test with vector variants (v2f64) with NaN and poison ; Use the poison element for flexibility to choose to return either the constant arg or the other arg X define void @minmax_mixed_qnan_poison_v2f64(<2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_mixed_qnan_poison_v2f64( ; CHECK-NEXT: store <2 x double> [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> [[X]], ptr [[MAXNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> , ptr [[MINIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> , ptr [[MAXIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: ret void ; %minnum = call <2 x double> @llvm.minnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %minnum, ptr %minnum_res %maxnum = call <2 x double> @llvm.maxnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %maxnum, ptr %maxnum_res %minimum = call <2 x double> @llvm.minimum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %minimum, ptr %minimum_res %maximum = call <2 x double> @llvm.maximum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %maximum, ptr %maximum_res %minimumnum = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %minimumnum, ptr %minimumnum_res %maximumnum = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %maximumnum, ptr %maximumnum_res ret void } ;############################################################### ;# Poison Tests # ;############################################################### define void @minmax_poison_op0_f16(half %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_poison_op0_f16( ; CHECK-NEXT: store half [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 2 ; CHECK-NEXT: store half [[X]], ptr [[MAXNUM_RES:%.*]], align 2 ; CHECK-NEXT: store half [[X]], ptr [[MINIMUM_RES:%.*]], align 2 ; CHECK-NEXT: store half [[X]], ptr [[MAXIMUM_RES:%.*]], align 2 ; CHECK-NEXT: store half [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 2 ; CHECK-NEXT: store half [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 2 ; CHECK-NEXT: ret void ; %minnum = call half @llvm.minnum.f16(half poison, half %x) store half %minnum, ptr %minnum_res %maxnum = call half @llvm.maxnum.f16(half poison, half %x) store half %maxnum, ptr %maxnum_res %minimum = call half @llvm.minimum.f16(half poison, half %x) store half %minimum, ptr %minimum_res %maximum = call half @llvm.maximum.f16(half poison, half %x) store half %maximum, ptr %maximum_res %minimumnum = call half @llvm.minimumnum.f16(half poison, half %x) store half %minimumnum, ptr %minimumnum_res %maximumnum = call half @llvm.maximumnum.f16(half poison, half %x) store half %maximumnum, ptr %maximumnum_res ret void } define void @minmax_poison_op1_nxv2f64( %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_poison_op1_nxv2f64( ; CHECK-NEXT: store [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 16 ; CHECK-NEXT: store [[X]], ptr [[MAXNUM_RES:%.*]], align 16 ; CHECK-NEXT: store [[X]], ptr [[MINIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store [[X]], ptr [[MAXIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: store [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: ret void ; %minnum = call nnan @llvm.minnum.nxv2f64( %x, poison) store %minnum, ptr %minnum_res %maxnum = call nnan @llvm.maxnum.nxv2f64( %x, poison) store %maxnum, ptr %maxnum_res %minimum = call nnan @llvm.minimum.nxv2f64( %x, poison) store %minimum, ptr %minimum_res %maximum = call nnan @llvm.maximum.nxv2f64( %x, poison) store %maximum, ptr %maximum_res %minimumnum = call nnan @llvm.minimumnum.nxv2f64( %x, poison) store %minimumnum, ptr %minimumnum_res %maximumnum = call nnan @llvm.maximumnum.nxv2f64( %x, poison) store %maximumnum, ptr %maximumnum_res ret void } ;############################################################### ;# Positive Infinity Tests # ;############################################################### ; maxnum(X, +inf) -> +inf (ignoring SNaN -> QNaN propagation) ; minnum(X, +inf) -> X if nnan (ignoring NaN quieting) ; maximum(X, +inf) -> +inf if nnan ; minimum(X, +inf) -> X (ignoring NaN quieting) ; maximumnum(X, +inf) -> +inf ; minimumnum(X, +inf) -> X if nnan (ignoring NaN quieting) ; Can only optimize maxnum, minimum, and maximumnum without the nnan flag define void @minmax_pos_inf_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_pos_inf_f32( ; CHECK-NEXT: [[MINNUM:%.*]] = call float @llvm.minnum.f32(float [[X:%.*]], float +inf) ; CHECK-NEXT: store float [[MINNUM]], ptr [[MINNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float +inf, ptr [[MAXNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MINIMUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXIMUM:%.*]] = call float @llvm.maximum.f32(float [[X]], float +inf) ; CHECK-NEXT: store float [[MAXIMUM]], ptr [[MAXIMUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MINIMUMNUM:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float +inf) ; CHECK-NEXT: store float [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float +inf, ptr [[MAXIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: ret void ; %minnum = call float @llvm.minnum.f32(float %x, float 0x7FF0000000000000) store float %minnum, ptr %minnum_res %maxnum = call float @llvm.maxnum.f32(float %x, float 0x7FF0000000000000) store float %maxnum, ptr %maxnum_res %minimum = call float @llvm.minimum.f32(float %x, float 0x7FF0000000000000) store float %minimum, ptr %minimum_res %maximum = call float @llvm.maximum.f32(float %x, float 0x7FF0000000000000) store float %maximum, ptr %maximum_res %minimumnum = call float @llvm.minimumnum.f32(float %x, float 0x7FF0000000000000) store float %minimumnum, ptr %minimumnum_res %maximumnum = call float @llvm.maximumnum.f32(float %x, float 0x7FF0000000000000) store float %maximumnum, ptr %maximumnum_res ret void } ; Can optimize all minmax variants if the nnan flag is set define void @minmax_pos_inf_nnan_v2f32(<2 x float> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_pos_inf_nnan_v2f32( ; CHECK-NEXT: store <2 x float> [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 8 ; CHECK-NEXT: store <2 x float> splat (float +inf), ptr [[MAXNUM_RES:%.*]], align 8 ; CHECK-NEXT: store <2 x float> [[X]], ptr [[MINIMUM_RES:%.*]], align 8 ; CHECK-NEXT: store <2 x float> splat (float +inf), ptr [[MAXIMUM_RES:%.*]], align 8 ; CHECK-NEXT: store <2 x float> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 8 ; CHECK-NEXT: store <2 x float> splat (float +inf), ptr [[MAXIMUMNUM_RES:%.*]], align 8 ; CHECK-NEXT: ret void ; %minnum = call nnan <2 x float> @llvm.minnum.v2f32(<2 x float> splat (float 0x7FF0000000000000), <2 x float> %x) store <2 x float> %minnum, ptr %minnum_res %maxnum = call nnan <2 x float> @llvm.maxnum.v2f32(<2 x float> splat (float 0x7FF0000000000000), <2 x float> %x) store <2 x float> %maxnum, ptr %maxnum_res %minimum = call nnan <2 x float> @llvm.minimum.v2f32(<2 x float> splat (float 0x7FF0000000000000), <2 x float> %x) store <2 x float> %minimum, ptr %minimum_res %maximum = call nnan <2 x float> @llvm.maximum.v2f32(<2 x float> splat (float 0x7FF0000000000000), <2 x float> %x) store <2 x float> %maximum, ptr %maximum_res %minimumnum = call nnan <2 x float> @llvm.minimumnum.v2f32(<2 x float> splat (float 0x7FF0000000000000), <2 x float> %x) store <2 x float> %minimumnum, ptr %minimumnum_res %maximumnum = call nnan <2 x float> @llvm.maximumnum.v2f32(<2 x float> splat (float 0x7FF0000000000000), <2 x float> %x) store <2 x float> %maximumnum, ptr %maximumnum_res ret void } ;############################################################### ;# Negative Infinity Tests # ;############################################################### ; minnum(X, -inf) -> -inf (Ignoring SNaN -> QNaN propagation) ; maxnum(X, -inf) -> X if nnan ; minimum(X, -inf) -> -inf if nnan ; maximum(X, -inf) -> X (Ignoring NaN quieting) ; minimumnum(X, -inf) -> -inf ; maximumnum(X, -inf) -> X if nnan ; Can only optimize minnum, maximum, and minimumnum without the nnan flag define void @minmax_neg_inf_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_neg_inf_f32( ; CHECK-NEXT: store float -inf, ptr [[MINNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXNUM:%.*]] = call float @llvm.maxnum.f32(float [[X:%.*]], float -inf) ; CHECK-NEXT: store float [[MAXNUM]], ptr [[MAXNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MINIMUM:%.*]] = call float @llvm.minimum.f32(float [[X]], float -inf) ; CHECK-NEXT: store float [[MINIMUM]], ptr [[MINIMUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MAXIMUM_RES:%.*]], align 4 ; CHECK-NEXT: store float -inf, ptr [[MINIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXIMUMNUM:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float -inf) ; CHECK-NEXT: store float [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: ret void ; %minnum = call float @llvm.minnum.f32(float %x, float 0xFFF0000000000000) store float %minnum, ptr %minnum_res %maxnum = call float @llvm.maxnum.f32(float %x, float 0xFFF0000000000000) store float %maxnum, ptr %maxnum_res %minimum = call float @llvm.minimum.f32(float %x, float 0xFFF0000000000000) store float %minimum, ptr %minimum_res %maximum = call float @llvm.maximum.f32(float %x, float 0xFFF0000000000000) store float %maximum, ptr %maximum_res %minimumnum = call float @llvm.minimumnum.f32(float %x, float 0xFFF0000000000000) store float %minimumnum, ptr %minimumnum_res %maximumnum = call float @llvm.maximumnum.f32(float %x, float 0xFFF0000000000000) store float %maximumnum, ptr %maximumnum_res ret void } ; Can optimize all minmax variants if the nnan flag is set define void @minmax_neg_inf_nnan_v2f64(<2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_neg_inf_nnan_v2f64( ; CHECK-NEXT: store <2 x double> splat (double -inf), ptr [[MINNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> [[X:%.*]], ptr [[MAXNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> splat (double -inf), ptr [[MINIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> [[X]], ptr [[MAXIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> splat (double -inf), ptr [[MINIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: ret void ; %minnum = call nnan <2 x double> @llvm.minnum.v2f64(<2 x double> %x, <2 x double> splat (double 0xFFF0000000000000)) store <2 x double> %minnum, ptr %minnum_res %maxnum = call nnan <2 x double> @llvm.maxnum.v2f64(<2 x double> %x, <2 x double> splat (double 0xFFF0000000000000)) store <2 x double> %maxnum, ptr %maxnum_res %minimum = call nnan <2 x double> @llvm.minimum.v2f64(<2 x double> %x, <2 x double> splat (double 0xFFF0000000000000)) store <2 x double> %minimum, ptr %minimum_res %maximum = call nnan <2 x double> @llvm.maximum.v2f64(<2 x double> %x, <2 x double> splat (double 0xFFF0000000000000)) store <2 x double> %maximum, ptr %maximum_res %minimumnum = call nnan <2 x double> @llvm.minimumnum.v2f64(<2 x double> %x, <2 x double> splat (double 0xFFF0000000000000)) store <2 x double> %minimumnum, ptr %minimumnum_res %maximumnum = call nnan <2 x double> @llvm.maximumnum.v2f64(<2 x double> %x, <2 x double> splat (double 0xFFF0000000000000)) store <2 x double> %maximumnum, ptr %maximumnum_res ret void } ;############################################################### ;# Largest Positive Float Constant Tests # ;############################################################### ; maxnum(X, +largest) -> +largest if ninf (ignoring SNaN -> QNaN propagation) ; minnum(X, +largest) -> X if ninf && nnan ; maximum(X, +largest) -> +largest if ninf && nnan ; minimum(X, +largest) -> X if ninf (ignoring quieting of sNaNs) ; maximumnum(X, +largest) -> +largest if ninf ; minimumnum(X, +largest) -> X if ninf && nnan ; None of these should be optimized away without the nnan/ninf flags define void @minmax_largest_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_largest_f32( ; CHECK-NEXT: [[MINNUM:%.*]] = call float @llvm.minnum.f32(float [[X:%.*]], float f0x7F7FFFFF) ; CHECK-NEXT: store float [[MINNUM]], ptr [[MINNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXNUM:%.*]] = call float @llvm.maxnum.f32(float [[X]], float f0x7F7FFFFF) ; CHECK-NEXT: store float [[MAXNUM]], ptr [[MAXNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MINIMUM:%.*]] = call float @llvm.minimum.f32(float [[X]], float f0x7F7FFFFF) ; CHECK-NEXT: store float [[MINIMUM]], ptr [[MINIMUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXIMUM:%.*]] = call float @llvm.maximum.f32(float [[X]], float f0x7F7FFFFF) ; CHECK-NEXT: store float [[MAXIMUM]], ptr [[MAXIMUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MINIMUMNUM:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float f0x7F7FFFFF) ; CHECK-NEXT: store float [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXIMUMNUM:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float f0x7F7FFFFF) ; CHECK-NEXT: store float [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: ret void ; %minnum = call float @llvm.minnum.f32(float %x, float 0x47EFFFFFE0000000) store float %minnum, ptr %minnum_res %maxnum = call float @llvm.maxnum.f32(float %x, float 0x47EFFFFFE0000000) store float %maxnum, ptr %maxnum_res %minimum = call float @llvm.minimum.f32(float %x, float 0x47EFFFFFE0000000) store float %minimum, ptr %minimum_res %maximum = call float @llvm.maximum.f32(float %x, float 0x47EFFFFFE0000000) store float %maximum, ptr %maximum_res %minimumnum = call float @llvm.minimumnum.f32(float %x, float 0x47EFFFFFE0000000) store float %minimumnum, ptr %minimumnum_res %maximumnum = call float @llvm.maximumnum.f32(float %x, float 0x47EFFFFFE0000000) store float %maximumnum, ptr %maximumnum_res ret void } ; We can optimize maxnum, minimum, and maximumnum if we know ninf is set define void @minmax_largest_f32_ninf(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_largest_f32_ninf( ; CHECK-NEXT: [[MINNUM:%.*]] = call ninf float @llvm.minnum.f32(float [[X:%.*]], float f0x7F7FFFFF) ; CHECK-NEXT: store float [[MINNUM]], ptr [[MINNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float f0x7F7FFFFF, ptr [[MAXNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MINIMUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXIMUM:%.*]] = call ninf float @llvm.maximum.f32(float [[X]], float f0x7F7FFFFF) ; CHECK-NEXT: store float [[MAXIMUM]], ptr [[MAXIMUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MINIMUMNUM:%.*]] = call ninf float @llvm.minimumnum.f32(float [[X]], float f0x7F7FFFFF) ; CHECK-NEXT: store float [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float f0x7F7FFFFF, ptr [[MAXIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: ret void ; %minnum = call ninf float @llvm.minnum.f32(float %x, float 0x47EFFFFFE0000000) store float %minnum, ptr %minnum_res %maxnum = call ninf float @llvm.maxnum.f32(float %x, float 0x47EFFFFFE0000000) store float %maxnum, ptr %maxnum_res %minimum = call ninf float @llvm.minimum.f32(float %x, float 0x47EFFFFFE0000000) store float %minimum, ptr %minimum_res %maximum = call ninf float @llvm.maximum.f32(float %x, float 0x47EFFFFFE0000000) store float %maximum, ptr %maximum_res %minimumnum = call ninf float @llvm.minimumnum.f32(float %x, float 0x47EFFFFFE0000000) store float %minimumnum, ptr %minimumnum_res %maximumnum = call ninf float @llvm.maximumnum.f32(float %x, float 0x47EFFFFFE0000000) store float %maximumnum, ptr %maximumnum_res ret void } ; All can be optimized if both the ninf and nnan flags are set (ignoring SNaN propagation in minnum/maxnum) define void @minmax_largest_v2f32_ninf_nnan(<2 x float> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_largest_v2f32_ninf_nnan( ; CHECK-NEXT: store <2 x float> [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 8 ; CHECK-NEXT: store <2 x float> splat (float f0x7F7FFFFF), ptr [[MAXNUM_RES:%.*]], align 8 ; CHECK-NEXT: store <2 x float> [[X]], ptr [[MINIMUM_RES:%.*]], align 8 ; CHECK-NEXT: store <2 x float> splat (float f0x7F7FFFFF), ptr [[MAXIMUM_RES:%.*]], align 8 ; CHECK-NEXT: store <2 x float> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 8 ; CHECK-NEXT: store <2 x float> splat (float f0x7F7FFFFF), ptr [[MAXIMUMNUM_RES:%.*]], align 8 ; CHECK-NEXT: ret void ; %minnum = call ninf nnan <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> splat (float 0x47EFFFFFE0000000)) store <2 x float> %minnum, ptr %minnum_res %maxnum = call ninf nnan <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> splat (float 0x47EFFFFFE0000000)) store <2 x float> %maxnum, ptr %maxnum_res %minimum = call ninf nnan <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> splat (float 0x47EFFFFFE0000000)) store <2 x float> %minimum, ptr %minimum_res %maximum = call ninf nnan <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> splat (float 0x47EFFFFFE0000000)) store <2 x float> %maximum, ptr %maximum_res %minimumnum = call ninf nnan <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> splat (float 0x47EFFFFFE0000000)) store <2 x float> %minimumnum, ptr %minimumnum_res %maximumnum = call ninf nnan <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> splat (float 0x47EFFFFFE0000000)) store <2 x float> %maximumnum, ptr %maximumnum_res ret void } ;############################################################### ;# Largest Negative Float Constant Tests # ;############################################################### ; maxnum(X, -largest) -> X if ninf && nnan ; minnum(X, -largest) -> -largest if ninf (ignoring SNaN -> QNaN propagation) ; maximum(X, -largest) -> X if ninf (ignoring quieting of sNaNs) ; minimum(X, -largest) -> -largest if ninf && nnan ; maximumnum(X, -largest) -> X if ninf && nnan ; minimumnum(X, -largest) -> -largest if ninf ; None of these should be optimized away without the nnan/ninf flags define void @minmax_neg_largest_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_neg_largest_f32( ; CHECK-NEXT: [[MINNUM:%.*]] = call float @llvm.minnum.f32(float [[X:%.*]], float f0xFF7FFFFF) ; CHECK-NEXT: store float [[MINNUM]], ptr [[MINNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXNUM:%.*]] = call float @llvm.maxnum.f32(float [[X]], float f0xFF7FFFFF) ; CHECK-NEXT: store float [[MAXNUM]], ptr [[MAXNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MINIMUM:%.*]] = call float @llvm.minimum.f32(float [[X]], float f0xFF7FFFFF) ; CHECK-NEXT: store float [[MINIMUM]], ptr [[MINIMUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXIMUM:%.*]] = call float @llvm.maximum.f32(float [[X]], float f0xFF7FFFFF) ; CHECK-NEXT: store float [[MAXIMUM]], ptr [[MAXIMUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MINIMUMNUM:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float f0xFF7FFFFF) ; CHECK-NEXT: store float [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXIMUMNUM:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float f0xFF7FFFFF) ; CHECK-NEXT: store float [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: ret void ; %minnum = call float @llvm.minnum.f32(float %x, float 0xC7EFFFFFE0000000) store float %minnum, ptr %minnum_res %maxnum = call float @llvm.maxnum.f32(float %x, float 0xC7EFFFFFE0000000) store float %maxnum, ptr %maxnum_res %minimum = call float @llvm.minimum.f32(float %x, float 0xC7EFFFFFE0000000) store float %minimum, ptr %minimum_res %maximum = call float @llvm.maximum.f32(float %x, float 0xC7EFFFFFE0000000) store float %maximum, ptr %maximum_res %minimumnum = call float @llvm.minimumnum.f32(float %x, float 0xC7EFFFFFE0000000) store float %minimumnum, ptr %minimumnum_res %maximumnum = call float @llvm.maximumnum.f32(float %x, float 0xC7EFFFFFE0000000) store float %maximumnum, ptr %maximumnum_res ret void } ; We can optimize minnum, maximum, and minimumnum if we know ninf is set define void @minmax_neg_largest_f32_ninf(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_neg_largest_f32_ninf( ; CHECK-NEXT: store float f0xFF7FFFFF, ptr [[MINNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXNUM:%.*]] = call ninf float @llvm.maxnum.f32(float [[X:%.*]], float f0xFF7FFFFF) ; CHECK-NEXT: store float [[MAXNUM]], ptr [[MAXNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MINIMUM:%.*]] = call ninf float @llvm.minimum.f32(float [[X]], float f0xFF7FFFFF) ; CHECK-NEXT: store float [[MINIMUM]], ptr [[MINIMUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MAXIMUM_RES:%.*]], align 4 ; CHECK-NEXT: store float f0xFF7FFFFF, ptr [[MINIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXIMUMNUM:%.*]] = call ninf float @llvm.maximumnum.f32(float [[X]], float f0xFF7FFFFF) ; CHECK-NEXT: store float [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: ret void ; %minnum = call ninf float @llvm.minnum.f32(float %x, float 0xC7EFFFFFE0000000) store float %minnum, ptr %minnum_res %maxnum = call ninf float @llvm.maxnum.f32(float %x, float 0xC7EFFFFFE0000000) store float %maxnum, ptr %maxnum_res %minimum = call ninf float @llvm.minimum.f32(float %x, float 0xC7EFFFFFE0000000) store float %minimum, ptr %minimum_res %maximum = call ninf float @llvm.maximum.f32(float %x, float 0xC7EFFFFFE0000000) store float %maximum, ptr %maximum_res %minimumnum = call ninf float @llvm.minimumnum.f32(float %x, float 0xC7EFFFFFE0000000) store float %minimumnum, ptr %minimumnum_res %maximumnum = call ninf float @llvm.maximumnum.f32(float %x, float 0xC7EFFFFFE0000000) store float %maximumnum, ptr %maximumnum_res ret void } ; All can be optimized if both the ninf and nnan flags are set (ignoring SNaN propagation in minnum/maxnum) define void @minmax_neg_largest_nxv2f32_nnan_ninf( %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_neg_largest_nxv2f32_nnan_ninf( ; CHECK-NEXT: store splat (float f0xFF7FFFFF), ptr [[MINNUM_RES:%.*]], align 8 ; CHECK-NEXT: store [[X:%.*]], ptr [[MAXNUM_RES:%.*]], align 8 ; CHECK-NEXT: store splat (float f0xFF7FFFFF), ptr [[MINIMUM_RES:%.*]], align 8 ; CHECK-NEXT: store [[X]], ptr [[MAXIMUM_RES:%.*]], align 8 ; CHECK-NEXT: store splat (float f0xFF7FFFFF), ptr [[MINIMUMNUM_RES:%.*]], align 8 ; CHECK-NEXT: store [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 8 ; CHECK-NEXT: ret void ; %minnum = call nnan ninf @llvm.minnum.nxv2f32( %x, splat (float 0xC7EFFFFFE0000000)) store %minnum, ptr %minnum_res %maxnum = call nnan ninf @llvm.maxnum.nxv2f32( %x, splat (float 0xC7EFFFFFE0000000)) store %maxnum, ptr %maxnum_res %minimum = call nnan ninf @llvm.minimum.nxv2f32( %x, splat (float 0xC7EFFFFFE0000000)) store %minimum, ptr %minimum_res %maximum = call nnan ninf @llvm.maximum.nxv2f32( %x, splat (float 0xC7EFFFFFE0000000)) store %maximum, ptr %maximum_res %minimumnum = call nnan ninf @llvm.minimumnum.nxv2f32( %x, splat (float 0xC7EFFFFFE0000000)) store %minimumnum, ptr %minimumnum_res %maximumnum = call nnan ninf @llvm.maximumnum.nxv2f32( %x, splat (float 0xC7EFFFFFE0000000)) store %maximumnum, ptr %maximumnum_res ret void } ;############################################################### ;# Mixed Constant Vector Elements # ;############################################################### ; Tests elementwise handling of different combinations of the above optimizable constants ; Test with vector variants (v2f64) with +Inf and poison ; Poison element allows for flexibility to choose either X or where applicable define void @minmax_mixed_pos_inf_poison_v2f64_nnan(<2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_mixed_pos_inf_poison_v2f64_nnan( ; CHECK-NEXT: store <2 x double> [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> , ptr [[MAXNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> [[X]], ptr [[MINIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> , ptr [[MAXIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <2 x double> , ptr [[MAXIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: ret void ; %minnum = call nnan <2 x double> @llvm.minnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %minnum, ptr %minnum_res %maxnum = call nnan <2 x double> @llvm.maxnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %maxnum, ptr %maxnum_res %minimum = call nnan <2 x double> @llvm.minimum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %minimum, ptr %minimum_res %maximum = call nnan <2 x double> @llvm.maximum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %maximum, ptr %maximum_res %minimumnum = call nnan <2 x double> @llvm.minimumnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %minimumnum, ptr %minimumnum_res %maximumnum = call nnan <2 x double> @llvm.maximumnum.v2f64(<2 x double> , <2 x double> %x) store <2 x double> %maximumnum, ptr %maximumnum_res ret void } ; Tests to show that we can optimize different classes of constatn (inf/nan/poison) in different vector elements. ; We can only optimize if the result would be choosing all elements of the input X, or all constant elements though ; (where poison allows us to choose either). ; ; nnan minnum(, X) = (Cannot mix elements from X and constant vector) ; nnan maxnum(, X) = ; nnan minimum(, X) = (Cannot mix elements from X and constant vector) ; nnan maximum(, X) = ; nnan minimumnum(, X) = (Poison can be either X or constant value) ; nnan maximumnum(, X) = define void @minmax_mixed_pos_inf_poison_snan_v3f32(<3 x float> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_mixed_pos_inf_poison_snan_v3f32( ; CHECK-NEXT: [[MINNUM:%.*]] = call nnan <3 x float> @llvm.minnum.v3f32(<3 x float> , <3 x float> [[X:%.*]]) ; CHECK-NEXT: store <3 x float> [[MINNUM]], ptr [[MINNUM_RES:%.*]], align 16 ; CHECK-NEXT: store <3 x float> , ptr [[MAXNUM_RES:%.*]], align 16 ; CHECK-NEXT: [[MINIMUM:%.*]] = call nnan <3 x float> @llvm.minimum.v3f32(<3 x float> , <3 x float> [[X]]) ; CHECK-NEXT: store <3 x float> [[MINIMUM]], ptr [[MINIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store <3 x float> , ptr [[MAXIMUM_RES:%.*]], align 16 ; CHECK-NEXT: store <3 x float> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: [[MAXIMUMNUM:%.*]] = call nnan <3 x float> @llvm.maximumnum.v3f32(<3 x float> , <3 x float> [[X]]) ; CHECK-NEXT: store <3 x float> [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16 ; CHECK-NEXT: ret void ; %minnum = call nnan <3 x float> @llvm.minnum.v3f32(<3 x float> , <3 x float> %x) store <3 x float> %minnum, ptr %minnum_res %maxnum = call nnan <3 x float> @llvm.maxnum.v3f32(<3 x float> , <3 x float> %x) store <3 x float> %maxnum, ptr %maxnum_res %minimum = call nnan <3 x float> @llvm.minimum.v3f32(<3 x float> , <3 x float> %x) store <3 x float> %minimum, ptr %minimum_res %maximum = call nnan <3 x float> @llvm.maximum.v3f32(<3 x float> , <3 x float> %x) store <3 x float> %maximum, ptr %maximum_res %minimumnum = call nnan <3 x float> @llvm.minimumnum.v3f32(<3 x float> , <3 x float> %x) store <3 x float> %minimumnum, ptr %minimumnum_res %maximumnum = call nnan <3 x float> @llvm.maximumnum.v3f32(<3 x float> , <3 x float> %x) store <3 x float> %maximumnum, ptr %maximumnum_res ret void } ;############################################################### ;# Min(x, x) / Max(x, x) # ;############################################################### ; min(x, x) -> x and max(x, x) -> x for all variants (ignoring SNaN quieting) define void @minmax_same_args(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_same_args( ; CHECK-NEXT: store float [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MAXNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MINIMUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MAXIMUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: store float [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: ret void ; %minnum = call float @llvm.minnum.f32(float %x, float %x) store float %minnum, ptr %minnum_res %maxnum = call float @llvm.maxnum.f32(float %x, float %x) store float %maxnum, ptr %maxnum_res %minimum = call float @llvm.minimum.f32(float %x, float %x) store float %minimum, ptr %minimum_res %maximum = call float @llvm.maximum.f32(float %x, float %x) store float %maximum, ptr %maximum_res %minimumnum = call float @llvm.minimumnum.f32(float %x, float %x) store float %minimumnum, ptr %minimumnum_res %maximumnum = call float @llvm.maximumnum.f32(float %x, float %x) store float %maximumnum, ptr %maximumnum_res ret void } ;############################################################### ;# Nested calls: M(x, M(x, y)) -> M(x, y) # ;############################################################### define void @minmax_x_minmax_xy(<2 x float> %x, <2 x float> %y, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_x_minmax_xy( ; CHECK-NEXT: [[MINNUM_XY:%.*]] = call <2 x float> @llvm.minnum.v2f32(<2 x float> [[X:%.*]], <2 x float> [[Y:%.*]]) ; CHECK-NEXT: store <2 x float> [[MINNUM_XY]], ptr [[MINNUM_RES:%.*]], align 8 ; CHECK-NEXT: [[MAXNUM_XY:%.*]] = call <2 x float> @llvm.maxnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]]) ; CHECK-NEXT: store <2 x float> [[MAXNUM_XY]], ptr [[MAXNUM_RES:%.*]], align 8 ; CHECK-NEXT: [[MINIMUM_XY:%.*]] = call <2 x float> @llvm.minimum.v2f32(<2 x float> [[X]], <2 x float> [[Y]]) ; CHECK-NEXT: store <2 x float> [[MINIMUM_XY]], ptr [[MINIMUM_RES:%.*]], align 8 ; CHECK-NEXT: [[MAXIMUM_XY:%.*]] = call <2 x float> @llvm.maximum.v2f32(<2 x float> [[X]], <2 x float> [[Y]]) ; CHECK-NEXT: store <2 x float> [[MAXIMUM_XY]], ptr [[MAXIMUM_RES:%.*]], align 8 ; CHECK-NEXT: [[MINIMUMNUM_XY:%.*]] = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]]) ; CHECK-NEXT: store <2 x float> [[MINIMUMNUM_XY]], ptr [[MINIMUMNUM_RES:%.*]], align 8 ; CHECK-NEXT: [[MAXIMUMNUM_XY:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]]) ; CHECK-NEXT: store <2 x float> [[MAXIMUMNUM_XY]], ptr [[MAXIMUMNUM_RES:%.*]], align 8 ; CHECK-NEXT: ret void ; %minnum_xy = call <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> %y) %minnum_nested = call <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> %minnum_xy) store <2 x float> %minnum_nested, ptr %minnum_res %maxnum_xy = call <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> %y) %maxnum_nested = call <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> %maxnum_xy) store <2 x float> %maxnum_nested, ptr %maxnum_res %minimum_xy = call <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> %y) %minimum_nested = call <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> %minimum_xy) store <2 x float> %minimum_nested, ptr %minimum_res %maximum_xy = call <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> %y) %maximum_nested = call <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> %maximum_xy) store <2 x float> %maximum_nested, ptr %maximum_res %minimumnum_xy = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> %y) %minimumnum_nested = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> %minimumnum_xy) store <2 x float> %minimumnum_nested, ptr %minimumnum_res %maximumnum_xy = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> %y) %maximumnum_nested = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> %maximumnum_xy) store <2 x float> %maximumnum_nested, ptr %maximumnum_res ret void } ; Negative test: m(Z, m(X,Y)) cannot be optimized to m(x, y) define void @minmax_z_minmax_xy(float %x, float %y, float %z, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_z_minmax_xy( ; CHECK-NEXT: [[MINNUM_XY:%.*]] = call float @llvm.minnum.f32(float [[X:%.*]], float [[Y:%.*]]) ; CHECK-NEXT: [[MINNUM_NESTED:%.*]] = call float @llvm.minnum.f32(float [[Z:%.*]], float [[MINNUM_XY]]) ; CHECK-NEXT: store float [[MINNUM_NESTED]], ptr [[MINNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXNUM_XY:%.*]] = call float @llvm.maxnum.f32(float [[X]], float [[Y]]) ; CHECK-NEXT: [[MAXNUM_NESTED:%.*]] = call float @llvm.maxnum.f32(float [[Z]], float [[MAXNUM_XY]]) ; CHECK-NEXT: store float [[MAXNUM_NESTED]], ptr [[MAXNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MINIMUM_XY:%.*]] = call float @llvm.minimum.f32(float [[X]], float [[Y]]) ; CHECK-NEXT: [[MINIMUM_NESTED:%.*]] = call float @llvm.minimum.f32(float [[Z]], float [[MINIMUM_XY]]) ; CHECK-NEXT: store float [[MINIMUM_NESTED]], ptr [[MINIMUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXIMUM_XY:%.*]] = call float @llvm.maximum.f32(float [[X]], float [[Y]]) ; CHECK-NEXT: [[MAXIMUM_NESTED:%.*]] = call float @llvm.maximum.f32(float [[Z]], float [[MAXIMUM_XY]]) ; CHECK-NEXT: store float [[MAXIMUM_NESTED]], ptr [[MAXIMUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MINIMUMNUM_XY:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float [[Y]]) ; CHECK-NEXT: [[MINIMUMNUM_NESTED:%.*]] = call float @llvm.minimumnum.f32(float [[Z]], float [[MINIMUMNUM_XY]]) ; CHECK-NEXT: store float [[MINIMUMNUM_NESTED]], ptr [[MINIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: [[MAXIMUMNUM_XY:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float [[Y]]) ; CHECK-NEXT: [[MAXIMUMNUM_NESTED:%.*]] = call float @llvm.maximumnum.f32(float [[Z]], float [[MAXIMUMNUM_XY]]) ; CHECK-NEXT: store float [[MAXIMUMNUM_NESTED]], ptr [[MAXIMUMNUM_RES:%.*]], align 4 ; CHECK-NEXT: ret void ; %minnum_xy = call float @llvm.minnum.f32(float %x, float %y) %minnum_nested = call float @llvm.minnum.f32(float %z, float %minnum_xy) store float %minnum_nested, ptr %minnum_res %maxnum_xy = call float @llvm.maxnum.f32(float %x, float %y) %maxnum_nested = call float @llvm.maxnum.f32(float %z, float %maxnum_xy) store float %maxnum_nested, ptr %maxnum_res %minimum_xy = call float @llvm.minimum.f32(float %x, float %y) %minimum_nested = call float @llvm.minimum.f32(float %z, float %minimum_xy) store float %minimum_nested, ptr %minimum_res %maximum_xy = call float @llvm.maximum.f32(float %x, float %y) %maximum_nested = call float @llvm.maximum.f32(float %z, float %maximum_xy) store float %maximum_nested, ptr %maximum_res %minimumnum_xy = call float @llvm.minimumnum.f32(float %x, float %y) %minimumnum_nested = call float @llvm.minimumnum.f32(float %z, float %minimumnum_xy) store float %minimumnum_nested, ptr %minimumnum_res %maximumnum_xy = call float @llvm.maximumnum.f32(float %x, float %y) %maximumnum_nested = call float @llvm.maximumnum.f32(float %z, float %maximumnum_xy) store float %maximumnum_nested, ptr %maximumnum_res ret void } ;############################################################### ;# Nested calls: M(M(x, y), M'(x, y)) -> M(x, y) # ;############################################################### ; m(m(X,Y), m'(Y,X)) -> m(X, Y) ; Test where m' is the same op as m define void @minmax_minmax_xy_minmax_yx(half %x, half %y, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_minmax_xy_minmax_yx( ; CHECK-NEXT: [[MINNUM_XY:%.*]] = call half @llvm.minnum.f16(half [[X:%.*]], half [[Y:%.*]]) ; CHECK-NEXT: store half [[MINNUM_XY]], ptr [[MINNUM_RES:%.*]], align 2 ; CHECK-NEXT: [[MAXNUM_XY:%.*]] = call half @llvm.maxnum.f16(half [[X]], half [[Y]]) ; CHECK-NEXT: store half [[MAXNUM_XY]], ptr [[MAXNUM_RES:%.*]], align 2 ; CHECK-NEXT: [[MINIMUM_XY:%.*]] = call half @llvm.minimum.f16(half [[X]], half [[Y]]) ; CHECK-NEXT: store half [[MINIMUM_XY]], ptr [[MINIMUM_RES:%.*]], align 2 ; CHECK-NEXT: [[MAXIMUM_XY:%.*]] = call half @llvm.maximum.f16(half [[X]], half [[Y]]) ; CHECK-NEXT: store half [[MAXIMUM_XY]], ptr [[MAXIMUM_RES:%.*]], align 2 ; CHECK-NEXT: [[MINIMUMNUM_XY:%.*]] = call half @llvm.minimumnum.f16(half [[X]], half [[Y]]) ; CHECK-NEXT: store half [[MINIMUMNUM_XY]], ptr [[MINIMUMNUM_RES:%.*]], align 2 ; CHECK-NEXT: [[MAXIMUMNUM_XY:%.*]] = call half @llvm.maximumnum.f16(half [[X]], half [[Y]]) ; CHECK-NEXT: store half [[MAXIMUMNUM_XY]], ptr [[MAXIMUMNUM_RES:%.*]], align 2 ; CHECK-NEXT: ret void ; %minnum_xy = call half @llvm.minnum.f16(half %x, half %y) %minnum_yx = call half @llvm.minnum.f16(half %y, half %x) %final_minnum = call half @llvm.minnum.f16(half %minnum_xy, half %minnum_yx) store half %final_minnum, ptr %minnum_res %maxnum_xy = call half @llvm.maxnum.f16(half %x, half %y) %maxnum_yx = call half @llvm.maxnum.f16(half %y, half %x) %final_maxnum = call half @llvm.maxnum.f16(half %maxnum_xy, half %maxnum_yx) store half %final_maxnum, ptr %maxnum_res %minimum_xy = call half @llvm.minimum.f16(half %x, half %y) %minimum_yx = call half @llvm.minimum.f16(half %y, half %x) %final_minimum = call half @llvm.minimum.f16(half %minimum_xy, half %minimum_yx) store half %final_minimum, ptr %minimum_res %maximum_xy = call half @llvm.maximum.f16(half %x, half %y) %maximum_yx = call half @llvm.maximum.f16(half %y, half %x) %final_maximum = call half @llvm.maximum.f16(half %maximum_xy, half %maximum_yx) store half %final_maximum, ptr %maximum_res %minimumnum_xy = call half @llvm.minimumnum.f16(half %x, half %y) %minimumnum_yx = call half @llvm.minimumnum.f16(half %y, half %x) %final_minimumnum = call half @llvm.minimumnum.f16(half %minimumnum_xy, half %minimumnum_yx) store half %final_minimumnum, ptr %minimumnum_res %maximumnum_xy = call half @llvm.maximumnum.f16(half %x, half %y) %maximumnum_yx = call half @llvm.maximumnum.f16(half %y, half %x) %final_maximumnum = call half @llvm.maximumnum.f16(half %maximumnum_xy, half %maximumnum_yx) store half %final_maximumnum, ptr %maximumnum_res ret void } ; m(m(X,Y), m'(Y,X)) -> m(X, Y) ; Test where m' is the opposite op from m define void @minmax_minmax_xy_maxmin_yx(double %x, double %y, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) { ; CHECK-LABEL: @minmax_minmax_xy_maxmin_yx( ; CHECK-NEXT: [[MINNUM_XY:%.*]] = call double @llvm.minnum.f64(double [[Y:%.*]], double [[X:%.*]]) ; CHECK-NEXT: store double [[MINNUM_XY]], ptr [[MINNUM_RES:%.*]], align 8 ; CHECK-NEXT: [[MAXNUM_XY:%.*]] = call double @llvm.maxnum.f64(double [[Y]], double [[X]]) ; CHECK-NEXT: store double [[MAXNUM_XY]], ptr [[MAXNUM_RES:%.*]], align 8 ; CHECK-NEXT: [[MINIMUM_XY:%.*]] = call double @llvm.minimum.f64(double [[Y]], double [[X]]) ; CHECK-NEXT: store double [[MINIMUM_XY]], ptr [[MINIMUM_RES:%.*]], align 8 ; CHECK-NEXT: [[MAXIMUM_XY:%.*]] = call double @llvm.maximum.f64(double [[Y]], double [[X]]) ; CHECK-NEXT: store double [[MAXIMUM_XY]], ptr [[MAXIMUM_RES:%.*]], align 8 ; CHECK-NEXT: [[MINIMUMNUM_XY:%.*]] = call double @llvm.minimumnum.f64(double [[Y]], double [[X]]) ; CHECK-NEXT: store double [[MINIMUMNUM_XY]], ptr [[MINIMUMNUM_RES:%.*]], align 8 ; CHECK-NEXT: [[MAXIMUMNUM_XY:%.*]] = call double @llvm.maximumnum.f64(double [[Y]], double [[X]]) ; CHECK-NEXT: store double [[MAXIMUMNUM_XY]], ptr [[MAXIMUMNUM_RES:%.*]], align 8 ; CHECK-NEXT: ret void ; %minnum_xy = call double @llvm.minnum.f64(double %x, double %y) %maxnum_yx = call double @llvm.maxnum.f64(double %y, double %x) %final_minnum = call double @llvm.minnum.f64(double %minnum_xy, double %maxnum_yx) store double %final_minnum, ptr %minnum_res %maxnum_xy = call double @llvm.maxnum.f64(double %x, double %y) %minnum_yx = call double @llvm.minnum.f64(double %y, double %x) %final_maxnum = call double @llvm.maxnum.f64(double %maxnum_xy, double %minnum_yx) store double %final_maxnum, ptr %maxnum_res %minimum_xy = call double @llvm.minimum.f64(double %x, double %y) %maximum_yx = call double @llvm.maximum.f64(double %y, double %x) %final_minimum = call double @llvm.minimum.f64(double %minimum_xy, double %maximum_yx) store double %final_minimum, ptr %minimum_res %maximum_xy = call double @llvm.maximum.f64(double %x, double %y) %minimum_yx = call double @llvm.minimum.f64(double %y, double %x) %final_maximum = call double @llvm.maximum.f64(double %maximum_xy, double %minimum_yx) store double %final_maximum, ptr %maximum_res %minimumnum_xy = call double @llvm.minimumnum.f64(double %x, double %y) %maximumnum_yx = call double @llvm.maximumnum.f64(double %y, double %x) %final_minimumnum = call double @llvm.minimumnum.f64(double %minimumnum_xy, double %maximumnum_yx) store double %final_minimumnum, ptr %minimumnum_res %maximumnum_xy = call double @llvm.maximumnum.f64(double %x, double %y) %minimumnum_yx = call double @llvm.minimumnum.f64(double %y, double %x) %final_maximumnum = call double @llvm.maximumnum.f64(double %maximumnum_xy, double %minimumnum_yx) store double %final_maximumnum, ptr %maximumnum_res ret void } ;############################################################### ;# Constant Expression Vector Tests # ;############################################################### ; Test that minmax intrinsics with constant expression vectors don't crash ; when getAggregateElement returns null for certain constant expressions. ; These tests cover various scenarios where getAggregateElement() fails: ; - Bitcast from mismatched vector element counts ; - Bitcast from integer to float vectors ; - Bitcast from i64 with different element boundaries ; Test with bitcast from <1 x i32> to <2 x half> (element count mismatch) define <2 x half> @minmax_bitcast_v2f16_minnum(<2 x half> %x) { ; CHECK-LABEL: @minmax_bitcast_v2f16_minnum( ; CHECK-NEXT: [[RESULT:%.*]] = call <2 x half> @llvm.minnum.v2f16(<2 x half> [[X:%.*]], <2 x half> bitcast (<1 x i32> splat (i32 1078530011) to <2 x half>)) ; CHECK-NEXT: ret <2 x half> [[RESULT]] ; %result = call <2 x half> @llvm.minnum.v2f16(<2 x half> %x, <2 x half> bitcast (<1 x i32> to <2 x half>)) ret <2 x half> %result } ; Test with bitcast from <2 x i32> to <4 x half> (different element boundaries) define <4 x half> @minmax_bitcast_v4f16_maxnum(<4 x half> %x) { ; CHECK-LABEL: @minmax_bitcast_v4f16_maxnum( ; CHECK-NEXT: [[RESULT:%.*]] = call <4 x half> @llvm.maxnum.v4f16(<4 x half> [[X:%.*]], <4 x half> bitcast (<2 x i32> to <4 x half>)) ; CHECK-NEXT: ret <4 x half> [[RESULT]] ; %result = call <4 x half> @llvm.maxnum.v4f16(<4 x half> %x, <4 x half> bitcast (<2 x i32> to <4 x half>)) ret <4 x half> %result } ; Test with bitcast from <1 x i64> to <2 x float> (scalar to vector bitcast) define <2 x float> @minmax_bitcast_v2f32_minimum(<2 x float> %x) { ; CHECK-LABEL: @minmax_bitcast_v2f32_minimum( ; CHECK-NEXT: [[RESULT:%.*]] = call <2 x float> @llvm.minimum.v2f32(<2 x float> [[X:%.*]], <2 x float> bitcast (<1 x i64> splat (i64 4638564619268087808) to <2 x float>)) ; CHECK-NEXT: ret <2 x float> [[RESULT]] ; %result = call <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> bitcast (<1 x i64> to <2 x float>)) ret <2 x float> %result } ; Test with bitcast from <1 x double> to <4 x half> (type size mismatch) define <4 x half> @minmax_bitcast_v4f16_maximum(<4 x half> %x) { ; CHECK-LABEL: @minmax_bitcast_v4f16_maximum( ; CHECK-NEXT: [[RESULT:%.*]] = call <4 x half> @llvm.maximum.v4f16(<4 x half> [[X:%.*]], <4 x half> bitcast (<1 x double> splat (double f0x400921FB54442D18) to <4 x half>)) ; CHECK-NEXT: ret <4 x half> [[RESULT]] ; %result = call <4 x half> @llvm.maximum.v4f16(<4 x half> %x, <4 x half> bitcast (<1 x double> to <4 x half>)) ret <4 x half> %result } ; Test with bitcast from <2 x i16> to <2 x half> (integer to float) define <2 x half> @minmax_bitcast_v2f16_minimumnum(<2 x half> %x) { ; CHECK-LABEL: @minmax_bitcast_v2f16_minimumnum( ; CHECK-NEXT: [[RESULT:%.*]] = call <2 x half> @llvm.minimumnum.v2f16(<2 x half> [[X:%.*]], <2 x half> ) ; CHECK-NEXT: ret <2 x half> [[RESULT]] ; %result = call <2 x half> @llvm.minimumnum.v2f16(<2 x half> %x, <2 x half> bitcast (<2 x i16> to <2 x half>)) ret <2 x half> %result } ; Test with bitcast from <4 x i16> to <4 x half> (matching element count but getAggregateElement may fail) define <4 x half> @minmax_bitcast_v4f16_maximumnum(<4 x half> %x) { ; CHECK-LABEL: @minmax_bitcast_v4f16_maximumnum( ; CHECK-NEXT: [[RESULT:%.*]] = call <4 x half> @llvm.maximumnum.v4f16(<4 x half> [[X:%.*]], <4 x half> ) ; CHECK-NEXT: ret <4 x half> [[RESULT]] ; %result = call <4 x half> @llvm.maximumnum.v4f16(<4 x half> %x, <4 x half> bitcast (<4 x i16> to <4 x half>)) ret <4 x half> %result }