Skip to content

Commit d6511b4

Browse files
committed
Add minnum / maxnum intrinsics
These are named following the IEEE-754 names for these functions, rather than the libm fmin / fmax to avoid possible ambiguities. Some languages may implement something resembling fmin / fmax which return NaN if either operand is to propagate errors. These implement the IEEE-754 semantics of returning the other operand if either is a NaN representing missing data. llvm-svn: 220341
1 parent 44e5b4e commit d6511b4

File tree

14 files changed

+773
-2
lines changed

14 files changed

+773
-2
lines changed

llvm/docs/LangRef.rst

+85-2
Original file line numberDiff line numberDiff line change
@@ -8028,9 +8028,9 @@ all types however.
80288028

80298029
declare float @llvm.fabs.f32(float %Val)
80308030
declare double @llvm.fabs.f64(double %Val)
8031-
declare x86_fp80 @llvm.fabs.f80(x86_fp80 %Val)
8031+
declare x86_fp80 @llvm.fabs.f80(x86_fp80 %Val)
80328032
declare fp128 @llvm.fabs.f128(fp128 %Val)
8033-
declare ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128 %Val)
8033+
declare ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128 %Val)
80348034

80358035
Overview:
80368036
"""""""""
@@ -8050,6 +8050,89 @@ Semantics:
80508050
This function returns the same values as the libm ``fabs`` functions
80518051
would, and handles error conditions in the same way.
80528052

8053+
'``llvm.minnum.*``' Intrinsic
8054+
^^^^^^^^^^^^^^^^^^^^^^^^^^^
8055+
8056+
Syntax:
8057+
"""""""
8058+
8059+
This is an overloaded intrinsic. You can use ``llvm.minnum`` on any
8060+
floating point or vector of floating point type. Not all targets support
8061+
all types however.
8062+
8063+
::
8064+
8065+
declare float @llvm.minnum.f32(float %Val)
8066+
declare double @llvm.minnum.f64(double %Val)
8067+
declare x86_fp80 @llvm.minnum.f80(x86_fp80 %Val)
8068+
declare fp128 @llvm.minnum.f128(fp128 %Val)
8069+
declare ppc_fp128 @llvm.minnum.ppcf128(ppc_fp128 %Val)
8070+
8071+
Overview:
8072+
"""""""""
8073+
8074+
The '``llvm.minnum.*``' intrinsics return the minimum of the two
8075+
arguments.
8076+
8077+
8078+
Arguments:
8079+
""""""""""
8080+
8081+
The arguments and return value are floating point numbers of the same
8082+
type.
8083+
8084+
Semantics:
8085+
""""""""""
8086+
8087+
Follows the IEEE-754 semantics for minNum, which also match for libm's
8088+
fmin.
8089+
8090+
If either operand is a NaN, returns the other non-NaN operand. Returns
8091+
NaN only if both operands are NaN. If the operands compare equal,
8092+
returns a value that compares equal to both operands. This means that
8093+
fmin(+/-0.0, +/-0.0) could return either -0.0 or 0.0.
8094+
8095+
'``llvm.maxnum.*``' Intrinsic
8096+
^^^^^^^^^^^^^^^^^^^^^^^^^^^
8097+
8098+
Syntax:
8099+
"""""""
8100+
8101+
This is an overloaded intrinsic. You can use ``llvm.maxnum`` on any
8102+
floating point or vector of floating point type. Not all targets support
8103+
all types however.
8104+
8105+
::
8106+
8107+
declare float @llvm.maxnum.f32(float %Val)
8108+
declare double @llvm.maxnum.f64(double %Val)
8109+
declare x86_fp80 @llvm.maxnum.f80(x86_fp80 %Val)
8110+
declare fp128 @llvm.maxnum.f128(fp128 %Val)
8111+
declare ppc_fp128 @llvm.maxnum.ppcf128(ppc_fp128 %Val)
8112+
8113+
Overview:
8114+
"""""""""
8115+
8116+
The '``llvm.maxnum.*``' intrinsics return the maximum of the two
8117+
arguments.
8118+
8119+
8120+
Arguments:
8121+
""""""""""
8122+
8123+
The arguments and return value are floating point numbers of the same
8124+
type.
8125+
8126+
Semantics:
8127+
""""""""""
8128+
Follows the IEEE-754 semantics for maxNum, which also match for libm's
8129+
fmax.
8130+
8131+
If either operand is a NaN, returns the other non-NaN operand. Returns
8132+
NaN only if both operands are NaN. If the operands compare equal,
8133+
returns a value that compares equal to both operands. This means that
8134+
fmax(+/-0.0, +/-0.0) could return either -0.0 or 0.0.
8135+
80538136
'``llvm.copysign.*``' Intrinsic
80548137
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
80558138

llvm/include/llvm/IR/Intrinsics.td

+2
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,8 @@ let Properties = [IntrNoMem] in {
328328
def int_exp : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
329329
def int_exp2 : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
330330
def int_fabs : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
331+
def int_minnum : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>]>;
332+
def int_maxnum : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>]>;
331333
def int_copysign : Intrinsic<[llvm_anyfloat_ty],
332334
[LLVMMatchType<0>, LLVMMatchType<0>]>;
333335
def int_floor : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;

llvm/include/llvm/IR/PatternMatch.h

+12
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,18 @@ m_BSwap(const Opnd0 &Op0) {
12281228
return m_Intrinsic<Intrinsic::bswap>(Op0);
12291229
}
12301230

1231+
template<typename Opnd0, typename Opnd1>
1232+
inline typename m_Intrinsic_Ty<Opnd0, Opnd1>::Ty
1233+
m_FMin(const Opnd0 &Op0, const Opnd1 &Op1) {
1234+
return m_Intrinsic<Intrinsic::minnum>(Op0, Op1);
1235+
}
1236+
1237+
template<typename Opnd0, typename Opnd1>
1238+
inline typename m_Intrinsic_Ty<Opnd0, Opnd1>::Ty
1239+
m_FMax(const Opnd0 &Op0, const Opnd1 &Op1) {
1240+
return m_Intrinsic<Intrinsic::maxnum>(Op0, Op1);
1241+
}
1242+
12311243
} // end namespace PatternMatch
12321244
} // end namespace llvm
12331245

llvm/include/llvm/Target/TargetSelectionDAG.td

+2
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,8 @@ def fdiv : SDNode<"ISD::FDIV" , SDTFPBinOp>;
373373
def frem : SDNode<"ISD::FREM" , SDTFPBinOp>;
374374
def fma : SDNode<"ISD::FMA" , SDTFPTernaryOp>;
375375
def fabs : SDNode<"ISD::FABS" , SDTFPUnaryOp>;
376+
def fminnum : SDNode<"ISD::FMINNUM" , SDTFPBinOp>;
377+
def fmaxnum : SDNode<"ISD::FMAXNUM" , SDTFPBinOp>;
376378
def fgetsign : SDNode<"ISD::FGETSIGN" , SDTFPToIntOp>;
377379
def fneg : SDNode<"ISD::FNEG" , SDTFPUnaryOp>;
378380
def fsqrt : SDNode<"ISD::FSQRT" , SDTFPUnaryOp>;

llvm/include/llvm/Transforms/Utils/VectorUtils.h

+10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ static inline bool isTriviallyVectorizable(Intrinsic::ID ID) {
3636
case Intrinsic::log10:
3737
case Intrinsic::log2:
3838
case Intrinsic::fabs:
39+
case Intrinsic::minnum:
40+
case Intrinsic::maxnum:
3941
case Intrinsic::copysign:
4042
case Intrinsic::floor:
4143
case Intrinsic::ceil:
@@ -153,6 +155,14 @@ getIntrinsicIDForCall(CallInst *CI, const TargetLibraryInfo *TLI) {
153155
case LibFunc::fabsf:
154156
case LibFunc::fabsl:
155157
return checkUnaryFloatSignature(*CI, Intrinsic::fabs);
158+
case LibFunc::fmin:
159+
case LibFunc::fminf:
160+
case LibFunc::fminl:
161+
return checkBinaryFloatSignature(*CI, Intrinsic::minnum);
162+
case LibFunc::fmax:
163+
case LibFunc::fmaxf:
164+
case LibFunc::fmaxl:
165+
return checkBinaryFloatSignature(*CI, Intrinsic::maxnum);
156166
case LibFunc::copysign:
157167
case LibFunc::copysignf:
158168
case LibFunc::copysignl:

llvm/lib/Analysis/ConstantFolding.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -1229,6 +1229,8 @@ Constant *llvm::ConstantFoldLoadThroughGEPIndices(Constant *C,
12291229
bool llvm::canConstantFoldCallTo(const Function *F) {
12301230
switch (F->getIntrinsicID()) {
12311231
case Intrinsic::fabs:
1232+
case Intrinsic::minnum:
1233+
case Intrinsic::maxnum:
12321234
case Intrinsic::log:
12331235
case Intrinsic::log2:
12341236
case Intrinsic::log10:
@@ -1625,6 +1627,19 @@ static Constant *ConstantFoldScalarCall(StringRef Name, unsigned IntrinsicID,
16251627
V1.copySign(V2);
16261628
return ConstantFP::get(Ty->getContext(), V1);
16271629
}
1630+
1631+
if (IntrinsicID == Intrinsic::minnum) {
1632+
const APFloat &C1 = Op1->getValueAPF();
1633+
const APFloat &C2 = Op2->getValueAPF();
1634+
return ConstantFP::get(Ty->getContext(), minnum(C1, C2));
1635+
}
1636+
1637+
if (IntrinsicID == Intrinsic::maxnum) {
1638+
const APFloat &C1 = Op1->getValueAPF();
1639+
const APFloat &C2 = Op2->getValueAPF();
1640+
return ConstantFP::get(Ty->getContext(), maxnum(C1, C2));
1641+
}
1642+
16281643
if (!TLI)
16291644
return nullptr;
16301645
if (Name == "pow" && TLI->has(LibFunc::pow))

llvm/lib/Analysis/TargetTransformInfo.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,8 @@ struct NoTTI final : ImmutablePass, TargetTransformInfo {
475475
// These will all likely lower to a single selection DAG node.
476476
if (Name == "copysign" || Name == "copysignf" || Name == "copysignl" ||
477477
Name == "fabs" || Name == "fabsf" || Name == "fabsl" || Name == "sin" ||
478+
Name == "fmin" || Name == "fminf" || Name == "fminl" ||
479+
Name == "fmax" || Name == "fmaxf" || Name == "fmaxl" ||
478480
Name == "sinf" || Name == "sinl" || Name == "cos" || Name == "cosf" ||
479481
Name == "cosl" || Name == "sqrt" || Name == "sqrtf" || Name == "sqrtl")
480482
return false;

llvm/lib/Analysis/ValueTracking.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -2578,6 +2578,8 @@ bool llvm::isSafeToSpeculativelyExecute(const Value *V,
25782578
case Intrinsic::fma:
25792579
case Intrinsic::fmuladd:
25802580
case Intrinsic::fabs:
2581+
case Intrinsic::minnum:
2582+
case Intrinsic::maxnum:
25812583
return true;
25822584
// TODO: some fp intrinsics are marked as having the same error handling
25832585
// as libm. They're safe to speculate when they won't error.

llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp

+84
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,90 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) {
519519
}
520520
}
521521
break;
522+
case Intrinsic::minnum:
523+
case Intrinsic::maxnum: {
524+
Value *Arg0 = II->getArgOperand(0);
525+
Value *Arg1 = II->getArgOperand(1);
526+
527+
// fmin(x, x) -> x
528+
if (Arg0 == Arg1)
529+
return ReplaceInstUsesWith(CI, Arg0);
530+
531+
const ConstantFP *C0 = dyn_cast<ConstantFP>(Arg0);
532+
const ConstantFP *C1 = dyn_cast<ConstantFP>(Arg1);
533+
534+
// Canonicalize constants into the RHS.
535+
if (C0 && !C1) {
536+
II->setArgOperand(0, Arg1);
537+
II->setArgOperand(1, Arg0);
538+
return II;
539+
}
540+
541+
// fmin(x, nan) -> x
542+
if (C1 && C1->isNaN())
543+
return ReplaceInstUsesWith(CI, Arg0);
544+
545+
// This is the value because if undef were NaN, we would return the other
546+
// value and cannot return a NaN unless both operands are.
547+
//
548+
// fmin(undef, x) -> x
549+
if (isa<UndefValue>(Arg0))
550+
return ReplaceInstUsesWith(CI, Arg1);
551+
552+
// fmin(x, undef) -> x
553+
if (isa<UndefValue>(Arg1))
554+
return ReplaceInstUsesWith(CI, Arg0);
555+
556+
Value *X = nullptr;
557+
Value *Y = nullptr;
558+
if (II->getIntrinsicID() == Intrinsic::minnum) {
559+
// fmin(x, fmin(x, y)) -> fmin(x, y)
560+
// fmin(y, fmin(x, y)) -> fmin(x, y)
561+
if (match(Arg1, m_FMin(m_Value(X), m_Value(Y)))) {
562+
if (Arg0 == X || Arg0 == Y)
563+
return ReplaceInstUsesWith(CI, Arg1);
564+
}
565+
566+
// fmin(fmin(x, y), x) -> fmin(x, y)
567+
// fmin(fmin(x, y), y) -> fmin(x, y)
568+
if (match(Arg0, m_FMin(m_Value(X), m_Value(Y)))) {
569+
if (Arg1 == X || Arg1 == Y)
570+
return ReplaceInstUsesWith(CI, Arg0);
571+
}
572+
573+
// TODO: fmin(nnan x, inf) -> x
574+
// TODO: fmin(nnan ninf x, flt_max) -> x
575+
if (C1 && C1->isInfinity()) {
576+
// fmin(x, -inf) -> -inf
577+
if (C1->isNegative())
578+
return ReplaceInstUsesWith(CI, Arg1);
579+
}
580+
} else {
581+
assert(II->getIntrinsicID() == Intrinsic::maxnum);
582+
// fmax(x, fmax(x, y)) -> fmax(x, y)
583+
// fmax(y, fmax(x, y)) -> fmax(x, y)
584+
if (match(Arg1, m_FMax(m_Value(X), m_Value(Y)))) {
585+
if (Arg0 == X || Arg0 == Y)
586+
return ReplaceInstUsesWith(CI, Arg1);
587+
}
588+
589+
// fmax(fmax(x, y), x) -> fmax(x, y)
590+
// fmax(fmax(x, y), y) -> fmax(x, y)
591+
if (match(Arg0, m_FMax(m_Value(X), m_Value(Y)))) {
592+
if (Arg1 == X || Arg1 == Y)
593+
return ReplaceInstUsesWith(CI, Arg0);
594+
}
595+
596+
// TODO: fmax(nnan x, -inf) -> x
597+
// TODO: fmax(nnan ninf x, -flt_max) -> x
598+
if (C1 && C1->isInfinity()) {
599+
// fmax(x, inf) -> inf
600+
if (!C1->isNegative())
601+
return ReplaceInstUsesWith(CI, Arg1);
602+
}
603+
}
604+
break;
605+
}
522606
case Intrinsic::ppc_altivec_lvx:
523607
case Intrinsic::ppc_altivec_lvxl:
524608
// Turn PPC lvx -> load if the pointer is known aligned.

llvm/lib/Transforms/Vectorize/BBVectorize.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,8 @@ namespace {
685685
case Intrinsic::trunc:
686686
case Intrinsic::floor:
687687
case Intrinsic::fabs:
688+
case Intrinsic::minnum:
689+
case Intrinsic::maxnum:
688690
return Config.VectorizeMath;
689691
case Intrinsic::bswap:
690692
case Intrinsic::ctpop:

0 commit comments

Comments
 (0)