Skip to content

Commit

Permalink
Add minnum / maxnum intrinsics
Browse files Browse the repository at this point in the history
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
  • Loading branch information
arsenm committed Oct 21, 2014
1 parent 44e5b4e commit d6511b4
Show file tree
Hide file tree
Showing 14 changed files with 773 additions and 2 deletions.
87 changes: 85 additions & 2 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8028,9 +8028,9 @@ all types however.

declare float @llvm.fabs.f32(float %Val)
declare double @llvm.fabs.f64(double %Val)
declare x86_fp80 @llvm.fabs.f80(x86_fp80 %Val)
declare x86_fp80 @llvm.fabs.f80(x86_fp80 %Val)
declare fp128 @llvm.fabs.f128(fp128 %Val)
declare ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128 %Val)
declare ppc_fp128 @llvm.fabs.ppcf128(ppc_fp128 %Val)

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

'``llvm.minnum.*``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

This is an overloaded intrinsic. You can use ``llvm.minnum`` on any
floating point or vector of floating point type. Not all targets support
all types however.

::

declare float @llvm.minnum.f32(float %Val)
declare double @llvm.minnum.f64(double %Val)
declare x86_fp80 @llvm.minnum.f80(x86_fp80 %Val)
declare fp128 @llvm.minnum.f128(fp128 %Val)
declare ppc_fp128 @llvm.minnum.ppcf128(ppc_fp128 %Val)

Overview:
"""""""""

The '``llvm.minnum.*``' intrinsics return the minimum of the two
arguments.


Arguments:
""""""""""

The arguments and return value are floating point numbers of the same
type.

Semantics:
""""""""""

Follows the IEEE-754 semantics for minNum, which also match for libm's
fmin.

If either operand is a NaN, returns the other non-NaN operand. Returns
NaN only if both operands are NaN. If the operands compare equal,
returns a value that compares equal to both operands. This means that
fmin(+/-0.0, +/-0.0) could return either -0.0 or 0.0.

'``llvm.maxnum.*``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

This is an overloaded intrinsic. You can use ``llvm.maxnum`` on any
floating point or vector of floating point type. Not all targets support
all types however.

::

declare float @llvm.maxnum.f32(float %Val)
declare double @llvm.maxnum.f64(double %Val)
declare x86_fp80 @llvm.maxnum.f80(x86_fp80 %Val)
declare fp128 @llvm.maxnum.f128(fp128 %Val)
declare ppc_fp128 @llvm.maxnum.ppcf128(ppc_fp128 %Val)

Overview:
"""""""""

The '``llvm.maxnum.*``' intrinsics return the maximum of the two
arguments.


Arguments:
""""""""""

The arguments and return value are floating point numbers of the same
type.

Semantics:
""""""""""
Follows the IEEE-754 semantics for maxNum, which also match for libm's
fmax.

If either operand is a NaN, returns the other non-NaN operand. Returns
NaN only if both operands are NaN. If the operands compare equal,
returns a value that compares equal to both operands. This means that
fmax(+/-0.0, +/-0.0) could return either -0.0 or 0.0.

'``llvm.copysign.*``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ let Properties = [IntrNoMem] in {
def int_exp : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
def int_exp2 : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
def int_fabs : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
def int_minnum : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>]>;
def int_maxnum : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>]>;
def int_copysign : Intrinsic<[llvm_anyfloat_ty],
[LLVMMatchType<0>, LLVMMatchType<0>]>;
def int_floor : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
Expand Down
12 changes: 12 additions & 0 deletions llvm/include/llvm/IR/PatternMatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,18 @@ m_BSwap(const Opnd0 &Op0) {
return m_Intrinsic<Intrinsic::bswap>(Op0);
}

template<typename Opnd0, typename Opnd1>
inline typename m_Intrinsic_Ty<Opnd0, Opnd1>::Ty
m_FMin(const Opnd0 &Op0, const Opnd1 &Op1) {
return m_Intrinsic<Intrinsic::minnum>(Op0, Op1);
}

template<typename Opnd0, typename Opnd1>
inline typename m_Intrinsic_Ty<Opnd0, Opnd1>::Ty
m_FMax(const Opnd0 &Op0, const Opnd1 &Op1) {
return m_Intrinsic<Intrinsic::maxnum>(Op0, Op1);
}

} // end namespace PatternMatch
} // end namespace llvm

Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/Target/TargetSelectionDAG.td
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ def fdiv : SDNode<"ISD::FDIV" , SDTFPBinOp>;
def frem : SDNode<"ISD::FREM" , SDTFPBinOp>;
def fma : SDNode<"ISD::FMA" , SDTFPTernaryOp>;
def fabs : SDNode<"ISD::FABS" , SDTFPUnaryOp>;
def fminnum : SDNode<"ISD::FMINNUM" , SDTFPBinOp>;
def fmaxnum : SDNode<"ISD::FMAXNUM" , SDTFPBinOp>;
def fgetsign : SDNode<"ISD::FGETSIGN" , SDTFPToIntOp>;
def fneg : SDNode<"ISD::FNEG" , SDTFPUnaryOp>;
def fsqrt : SDNode<"ISD::FSQRT" , SDTFPUnaryOp>;
Expand Down
10 changes: 10 additions & 0 deletions llvm/include/llvm/Transforms/Utils/VectorUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ static inline bool isTriviallyVectorizable(Intrinsic::ID ID) {
case Intrinsic::log10:
case Intrinsic::log2:
case Intrinsic::fabs:
case Intrinsic::minnum:
case Intrinsic::maxnum:
case Intrinsic::copysign:
case Intrinsic::floor:
case Intrinsic::ceil:
Expand Down Expand Up @@ -153,6 +155,14 @@ getIntrinsicIDForCall(CallInst *CI, const TargetLibraryInfo *TLI) {
case LibFunc::fabsf:
case LibFunc::fabsl:
return checkUnaryFloatSignature(*CI, Intrinsic::fabs);
case LibFunc::fmin:
case LibFunc::fminf:
case LibFunc::fminl:
return checkBinaryFloatSignature(*CI, Intrinsic::minnum);
case LibFunc::fmax:
case LibFunc::fmaxf:
case LibFunc::fmaxl:
return checkBinaryFloatSignature(*CI, Intrinsic::maxnum);
case LibFunc::copysign:
case LibFunc::copysignf:
case LibFunc::copysignl:
Expand Down
15 changes: 15 additions & 0 deletions llvm/lib/Analysis/ConstantFolding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,8 @@ Constant *llvm::ConstantFoldLoadThroughGEPIndices(Constant *C,
bool llvm::canConstantFoldCallTo(const Function *F) {
switch (F->getIntrinsicID()) {
case Intrinsic::fabs:
case Intrinsic::minnum:
case Intrinsic::maxnum:
case Intrinsic::log:
case Intrinsic::log2:
case Intrinsic::log10:
Expand Down Expand Up @@ -1625,6 +1627,19 @@ static Constant *ConstantFoldScalarCall(StringRef Name, unsigned IntrinsicID,
V1.copySign(V2);
return ConstantFP::get(Ty->getContext(), V1);
}

if (IntrinsicID == Intrinsic::minnum) {
const APFloat &C1 = Op1->getValueAPF();
const APFloat &C2 = Op2->getValueAPF();
return ConstantFP::get(Ty->getContext(), minnum(C1, C2));
}

if (IntrinsicID == Intrinsic::maxnum) {
const APFloat &C1 = Op1->getValueAPF();
const APFloat &C2 = Op2->getValueAPF();
return ConstantFP::get(Ty->getContext(), maxnum(C1, C2));
}

if (!TLI)
return nullptr;
if (Name == "pow" && TLI->has(LibFunc::pow))
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Analysis/TargetTransformInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,8 @@ struct NoTTI final : ImmutablePass, TargetTransformInfo {
// These will all likely lower to a single selection DAG node.
if (Name == "copysign" || Name == "copysignf" || Name == "copysignl" ||
Name == "fabs" || Name == "fabsf" || Name == "fabsl" || Name == "sin" ||
Name == "fmin" || Name == "fminf" || Name == "fminl" ||
Name == "fmax" || Name == "fmaxf" || Name == "fmaxl" ||
Name == "sinf" || Name == "sinl" || Name == "cos" || Name == "cosf" ||
Name == "cosl" || Name == "sqrt" || Name == "sqrtf" || Name == "sqrtl")
return false;
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2578,6 +2578,8 @@ bool llvm::isSafeToSpeculativelyExecute(const Value *V,
case Intrinsic::fma:
case Intrinsic::fmuladd:
case Intrinsic::fabs:
case Intrinsic::minnum:
case Intrinsic::maxnum:
return true;
// TODO: some fp intrinsics are marked as having the same error handling
// as libm. They're safe to speculate when they won't error.
Expand Down
84 changes: 84 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,90 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) {
}
}
break;
case Intrinsic::minnum:
case Intrinsic::maxnum: {
Value *Arg0 = II->getArgOperand(0);
Value *Arg1 = II->getArgOperand(1);

// fmin(x, x) -> x
if (Arg0 == Arg1)
return ReplaceInstUsesWith(CI, Arg0);

const ConstantFP *C0 = dyn_cast<ConstantFP>(Arg0);
const ConstantFP *C1 = dyn_cast<ConstantFP>(Arg1);

// Canonicalize constants into the RHS.
if (C0 && !C1) {
II->setArgOperand(0, Arg1);
II->setArgOperand(1, Arg0);
return II;
}

// fmin(x, nan) -> x
if (C1 && C1->isNaN())
return ReplaceInstUsesWith(CI, Arg0);

// This is the value because if undef were NaN, we would return the other
// value and cannot return a NaN unless both operands are.
//
// fmin(undef, x) -> x
if (isa<UndefValue>(Arg0))
return ReplaceInstUsesWith(CI, Arg1);

// fmin(x, undef) -> x
if (isa<UndefValue>(Arg1))
return ReplaceInstUsesWith(CI, Arg0);

Value *X = nullptr;
Value *Y = nullptr;
if (II->getIntrinsicID() == Intrinsic::minnum) {
// fmin(x, fmin(x, y)) -> fmin(x, y)
// fmin(y, fmin(x, y)) -> fmin(x, y)
if (match(Arg1, m_FMin(m_Value(X), m_Value(Y)))) {
if (Arg0 == X || Arg0 == Y)
return ReplaceInstUsesWith(CI, Arg1);
}

// fmin(fmin(x, y), x) -> fmin(x, y)
// fmin(fmin(x, y), y) -> fmin(x, y)
if (match(Arg0, m_FMin(m_Value(X), m_Value(Y)))) {
if (Arg1 == X || Arg1 == Y)
return ReplaceInstUsesWith(CI, Arg0);
}

// TODO: fmin(nnan x, inf) -> x
// TODO: fmin(nnan ninf x, flt_max) -> x
if (C1 && C1->isInfinity()) {
// fmin(x, -inf) -> -inf
if (C1->isNegative())
return ReplaceInstUsesWith(CI, Arg1);
}
} else {
assert(II->getIntrinsicID() == Intrinsic::maxnum);
// fmax(x, fmax(x, y)) -> fmax(x, y)
// fmax(y, fmax(x, y)) -> fmax(x, y)
if (match(Arg1, m_FMax(m_Value(X), m_Value(Y)))) {
if (Arg0 == X || Arg0 == Y)
return ReplaceInstUsesWith(CI, Arg1);
}

// fmax(fmax(x, y), x) -> fmax(x, y)
// fmax(fmax(x, y), y) -> fmax(x, y)
if (match(Arg0, m_FMax(m_Value(X), m_Value(Y)))) {
if (Arg1 == X || Arg1 == Y)
return ReplaceInstUsesWith(CI, Arg0);
}

// TODO: fmax(nnan x, -inf) -> x
// TODO: fmax(nnan ninf x, -flt_max) -> x
if (C1 && C1->isInfinity()) {
// fmax(x, inf) -> inf
if (!C1->isNegative())
return ReplaceInstUsesWith(CI, Arg1);
}
}
break;
}
case Intrinsic::ppc_altivec_lvx:
case Intrinsic::ppc_altivec_lvxl:
// Turn PPC lvx -> load if the pointer is known aligned.
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Transforms/Vectorize/BBVectorize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,8 @@ namespace {
case Intrinsic::trunc:
case Intrinsic::floor:
case Intrinsic::fabs:
case Intrinsic::minnum:
case Intrinsic::maxnum:
return Config.VectorizeMath;
case Intrinsic::bswap:
case Intrinsic::ctpop:
Expand Down
Loading

0 comments on commit d6511b4

Please sign in to comment.