Index: include/llvm/Support/TargetOpcodes.def =================================================================== --- include/llvm/Support/TargetOpcodes.def +++ include/llvm/Support/TargetOpcodes.def @@ -493,6 +493,18 @@ /// Generic FP canonicalize value. HANDLE_TARGET_OPCODE(G_FCANONICALIZE) +/// FP min/max matching libm's fmin/fmax +HANDLE_TARGET_OPCODE(G_FMINNUM) +HANDLE_TARGET_OPCODE(G_FMAXNUM) + +/// FP min/max matching IEEE-754 2008's minnum/maxnum semantics. +HANDLE_TARGET_OPCODE(G_FMINNUM_IEEE) +HANDLE_TARGET_OPCODE(G_FMAXNUM_IEEE) + +/// FP min/max matching IEEE-754 2018 draft semantics. +HANDLE_TARGET_OPCODE(G_FMINIMUM) +HANDLE_TARGET_OPCODE(G_FMAXIMUM) + /// Generic pointer offset HANDLE_TARGET_OPCODE(G_GEP) Index: include/llvm/Target/GenericOpcodes.td =================================================================== --- include/llvm/Target/GenericOpcodes.td +++ include/llvm/Target/GenericOpcodes.td @@ -505,6 +505,62 @@ let hasSideEffects = 0; } +// FMINNUM/FMAXNUM - Perform floating-point minimum or maximum on two +// values. +// +// In the case where a single input is a NaN (either signaling or quiet), +// the non-NaN input is returned. +// +// The return value of (FMINNUM 0.0, -0.0) could be either 0.0 or -0.0. +def G_FMINNUM : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type0:$src1, type0:$src2); + let hasSideEffects = 0; + let isCommutable = 1; +} + +def G_FMAXNUM : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type0:$src1, type0:$src2); + let hasSideEffects = 0; + let isCommutable = 1; +} + +// FMINNUM_IEEE/FMAXNUM_IEEE - Perform floating-point minimum or maximum on +// two values, following the IEEE-754 2008 definition. This differs from +// FMINNUM/FMAXNUM in the handling of signaling NaNs. If one input is a +// signaling NaN, returns a quiet NaN. +def G_FMINNUM_IEEE : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type0:$src1, type0:$src2); + let hasSideEffects = 0; + let isCommutable = 1; +} + +def G_FMAXNUM_IEEE : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type0:$src1, type0:$src2); + let hasSideEffects = 0; + let isCommutable = 1; +} + +// FMINIMUM/FMAXIMUM - NaN-propagating minimum/maximum that also treat -0.0 +// as less than 0.0. While FMINNUM_IEEE/FMAXNUM_IEEE follow IEEE 754-2008 +// semantics, FMINIMUM/FMAXIMUM follow IEEE 754-2018 draft semantics. +def G_FMINIMUM : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type0:$src1, type0:$src2); + let hasSideEffects = 0; + let isCommutable = 1; +} + +def G_FMAXIMUM : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type0:$src1, type0:$src2); + let hasSideEffects = 0; + let isCommutable = 1; +} + //------------------------------------------------------------------------------ // Floating Point Binary ops. //------------------------------------------------------------------------------ Index: lib/CodeGen/GlobalISel/IRTranslator.cpp =================================================================== --- lib/CodeGen/GlobalISel/IRTranslator.cpp +++ lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -1222,6 +1222,14 @@ return TargetOpcode::G_FABS; case Intrinsic::copysign: return TargetOpcode::G_FCOPYSIGN; + case Intrinsic::minnum: + return TargetOpcode::G_FMINNUM; + case Intrinsic::maxnum: + return TargetOpcode::G_FMAXNUM; + case Intrinsic::minimum: + return TargetOpcode::G_FMINIMUM; + case Intrinsic::maximum: + return TargetOpcode::G_FMAXIMUM; case Intrinsic::canonicalize: return TargetOpcode::G_FCANONICALIZE; case Intrinsic::floor: Index: test/CodeGen/AArch64/GlobalISel/irtranslator-fp-min-max-intrinsics.ll =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/irtranslator-fp-min-max-intrinsics.ll @@ -0,0 +1,88 @@ +; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +; RUN: llc -mtriple=aarch64-- -verify-machineinstrs -global-isel -stop-after=irtranslator -o - %s | FileCheck %s + +define float @test_minnum(float %x, float %y) { + ; CHECK-LABEL: name: test_minnum + ; CHECK: bb.1 (%ir-block.0): + ; CHECK: liveins: $s0, $s1 + ; CHECK: [[COPY:%[0-9]+]]:_(s32) = COPY $s0 + ; CHECK: [[COPY1:%[0-9]+]]:_(s32) = COPY $s1 + ; CHECK: [[FMINNUM:%[0-9]+]]:_(s32) = G_FMINNUM [[COPY]], [[COPY1]] + ; CHECK: $s0 = COPY [[FMINNUM]](s32) + ; CHECK: RET_ReallyLR implicit $s0 + %val = call float @llvm.minnum.f32(float %x, float %y) + ret float %val +} + +define float @test_minnum_nnan(float %x, float %y) { + ; CHECK-LABEL: name: test_minnum_nnan + ; CHECK: bb.1 (%ir-block.0): + ; CHECK: liveins: $s0, $s1 + ; CHECK: [[COPY:%[0-9]+]]:_(s32) = COPY $s0 + ; CHECK: [[COPY1:%[0-9]+]]:_(s32) = COPY $s1 + ; CHECK: %2:_(s32) = nnan G_FMINNUM [[COPY]], [[COPY1]] + ; CHECK: $s0 = COPY %2(s32) + ; CHECK: RET_ReallyLR implicit $s0 + %val = call nnan float @llvm.minnum.f32(float %x, float %y) + ret float %val +} + +define float @test_maxnum(float %x, float %y) { + ; CHECK-LABEL: name: test_maxnum + ; CHECK: bb.1 (%ir-block.0): + ; CHECK: liveins: $s0, $s1 + ; CHECK: [[COPY:%[0-9]+]]:_(s32) = COPY $s0 + ; CHECK: [[COPY1:%[0-9]+]]:_(s32) = COPY $s1 + ; CHECK: [[FMAXNUM:%[0-9]+]]:_(s32) = G_FMAXNUM [[COPY]], [[COPY1]] + ; CHECK: $s0 = COPY [[FMAXNUM]](s32) + ; CHECK: RET_ReallyLR implicit $s0 + %val = call float @llvm.maxnum.f32(float %x, float %y) + ret float %val +} + +define float @test_minimum(float %x, float %y) { + ; CHECK-LABEL: name: test_minimum + ; CHECK: bb.1 (%ir-block.0): + ; CHECK: liveins: $s0, $s1 + ; CHECK: [[COPY:%[0-9]+]]:_(s32) = COPY $s0 + ; CHECK: [[COPY1:%[0-9]+]]:_(s32) = COPY $s1 + ; CHECK: [[FMINIMUM:%[0-9]+]]:_(s32) = G_FMINIMUM [[COPY]], [[COPY1]] + ; CHECK: $s0 = COPY [[FMINIMUM]](s32) + ; CHECK: RET_ReallyLR implicit $s0 + %val = call float @llvm.minimum.f32(float %x, float %y) + ret float %val +} + +define float @test_minimum_nnan(float %x, float %y) { + ; CHECK-LABEL: name: test_minimum_nnan + ; CHECK: bb.1 (%ir-block.0): + ; CHECK: liveins: $s0, $s1 + ; CHECK: [[COPY:%[0-9]+]]:_(s32) = COPY $s0 + ; CHECK: [[COPY1:%[0-9]+]]:_(s32) = COPY $s1 + ; CHECK: %2:_(s32) = nnan G_FMINIMUM [[COPY]], [[COPY1]] + ; CHECK: $s0 = COPY %2(s32) + ; CHECK: RET_ReallyLR implicit $s0 + %val = call nnan float @llvm.minimum.f32(float %x, float %y) + ret float %val +} + +define float @test_maximum(float %x, float %y) { + ; CHECK-LABEL: name: test_maximum + ; CHECK: bb.1 (%ir-block.0): + ; CHECK: liveins: $s0, $s1 + ; CHECK: [[COPY:%[0-9]+]]:_(s32) = COPY $s0 + ; CHECK: [[COPY1:%[0-9]+]]:_(s32) = COPY $s1 + ; CHECK: [[FMAXIMUM:%[0-9]+]]:_(s32) = G_FMAXIMUM [[COPY]], [[COPY1]] + ; CHECK: $s0 = COPY [[FMAXIMUM]](s32) + ; CHECK: RET_ReallyLR implicit $s0 + %val = call float @llvm.maximum.f32(float %x, float %y) + ret float %val +} + +declare float @llvm.minnum.f32(float, float) #0 +declare float @llvm.maxnum.f32(float, float) #0 + +declare float @llvm.minimum.f32(float, float) #0 +declare float @llvm.maximum.f32(float, float) #0 + +attributes #0 = { nounwind readnone speculatable } Index: test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir =================================================================== --- test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir +++ test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir @@ -303,6 +303,24 @@ # DEBUG-NEXT: G_FCANONICALIZE (opcode {{[0-9]+}}): 1 type index # DEBUG: .. type index coverage check SKIPPED: no rules defined # +# DEBUG-NEXT: G_FMINNUM (opcode 131): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_FMAXNUM (opcode 132): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_FMINNUM_IEEE (opcode 133): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_FMAXNUM_IEEE (opcode 134): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_FMINIMUM (opcode 135): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_FMAXIMUM (opcode 136): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# # DEBUG-NEXT: G_GEP (opcode {{[0-9]+}}): 2 type indices # DEBUG: .. the first uncovered type index: 2, OK #