Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -10881,10 +10881,11 @@ 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. +If either operand is a NaN, returns the canonicalized 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -10922,10 +10923,11 @@ 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. +If either operand is a NaN, returns the canonicalized 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Index: include/llvm/IR/IRBuilder.h =================================================================== --- include/llvm/IR/IRBuilder.h +++ include/llvm/IR/IRBuilder.h @@ -620,6 +620,15 @@ Type *ResultType, const Twine &Name = ""); + /// Create a call to intrinsic \p ID with 1 operand which is mangled on the + /// first type. + CallInst *CreateUnaryIntrinsic(Intrinsic::ID ID, Value *Op, + const Twine &Name = ""); + + CallInst *CreateCanonicalize(Value *Op, const Twine &Name = "") { + return CreateUnaryIntrinsic(Intrinsic::canonicalize, Op, Name); + } + /// Create a call to intrinsic \p ID with 2 operands which is mangled on the /// first type. CallInst *CreateBinaryIntrinsic(Intrinsic::ID ID, Index: lib/IR/IRBuilder.cpp =================================================================== --- lib/IR/IRBuilder.cpp +++ lib/IR/IRBuilder.cpp @@ -613,10 +613,17 @@ return createCallHelper(FnGCRelocate, Args, this, Name); } +CallInst *IRBuilderBase::CreateUnaryIntrinsic(Intrinsic::ID ID, Value *Op, + const Twine &Name) { + Module *M = BB->getParent()->getParent(); + Function *Fn = Intrinsic::getDeclaration(M, ID, { Op->getType() }); + return createCallHelper(Fn, { Op }, this, Name); +} + CallInst *IRBuilderBase::CreateBinaryIntrinsic(Intrinsic::ID ID, Value *LHS, Value *RHS, const Twine &Name) { Module *M = BB->getParent()->getParent(); - Function *Fn = Intrinsic::getDeclaration(M, ID, { LHS->getType() }); + Function *Fn = Intrinsic::getDeclaration(M, ID, { LHS->getType() }); return createCallHelper(Fn, { LHS, RHS }, this, Name); } Index: lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineCalls.cpp +++ lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -1163,30 +1163,31 @@ return Builder.CreateSelect(Mask, Op0, Op1); } -static Value *simplifyMinnumMaxnum(const IntrinsicInst &II) { +static Value *simplifyMinnumMaxnum(const IntrinsicInst &II, + InstCombiner::BuilderTy &Builder) { Value *Arg0 = II.getArgOperand(0); Value *Arg1 = II.getArgOperand(1); // fmin(x, x) -> x if (Arg0 == Arg1) - return Arg0; + return Builder.CreateCanonicalize(Arg0); const auto *C1 = dyn_cast(Arg1); // fmin(x, nan) -> x if (C1 && C1->isNaN()) - return Arg0; + return Builder.CreateCanonicalize(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(Arg0)) - return Arg1; + return Builder.CreateCanonicalize(Arg1); // fmin(x, undef) -> x if (isa(Arg1)) - return Arg0; + return Builder.CreateCanonicalize(Arg0); Value *X = nullptr; Value *Y = nullptr; @@ -1194,8 +1195,9 @@ // 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) + if (Arg0 == X || Arg0 == Y) { return Arg1; + } } // fmin(fmin(x, y), x) -> fmin(x, y) @@ -1210,7 +1212,7 @@ if (C1 && C1->isInfinity()) { // fmin(x, -inf) -> -inf if (C1->isNegative()) - return Arg1; + return Builder.CreateCanonicalize(Arg1); } } else { assert(II.getIntrinsicID() == Intrinsic::maxnum); @@ -1233,7 +1235,7 @@ if (C1 && C1->isInfinity()) { // fmax(x, inf) -> inf if (!C1->isNegative()) - return Arg1; + return Builder.CreateCanonicalize(Arg1); } } return nullptr; @@ -2000,7 +2002,7 @@ II->setArgOperand(1, Arg0); return II; } - if (Value *V = simplifyMinnumMaxnum(*II)) + if (Value *V = simplifyMinnumMaxnum(*II, Builder)) return replaceInstUsesWith(*II, V); break; } Index: test/Transforms/InstCombine/AMDGPU/amdgcn-intrinsics.ll =================================================================== --- test/Transforms/InstCombine/AMDGPU/amdgcn-intrinsics.ll +++ test/Transforms/InstCombine/AMDGPU/amdgcn-intrinsics.ll @@ -1206,21 +1206,24 @@ } ; CHECK-LABEL: @fmed3_x_qnan0_qnan1_f32( -; CHECK: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @fmed3_x_qnan0_qnan1_f32(float %x) { %med3 = call float @llvm.amdgcn.fmed3.f32(float %x, float 0x7FF8001000000000, float 0x7FF8002000000000) ret float %med3 } ; CHECK-LABEL: @fmed3_qnan0_x_qnan1_f32( -; CHECK: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @fmed3_qnan0_x_qnan1_f32(float %x) { %med3 = call float @llvm.amdgcn.fmed3.f32(float 0x7FF8001000000000, float %x, float 0x7FF8002000000000) ret float %med3 } ; CHECK-LABEL: @fmed3_qnan0_qnan1_x_f32( -; CHECK: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @fmed3_qnan0_qnan1_x_f32(float %x) { %med3 = call float @llvm.amdgcn.fmed3.f32(float 0x7FF8001000000000, float 0x7FF8002000000000, float %x) ret float %med3 Index: test/Transforms/InstCombine/maxnum.ll =================================================================== --- test/Transforms/InstCombine/maxnum.ll +++ test/Transforms/InstCombine/maxnum.ll @@ -113,42 +113,48 @@ } ; CHECK-LABEL: @noop_maxnum_f32 -; CHECK-NEXT: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @noop_maxnum_f32(float %x) #0 { %y = call float @llvm.maxnum.f32(float %x, float %x) #0 ret float %y } ; CHECK-LABEL: @maxnum_f32_nan_val -; CHECK-NEXT: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @maxnum_f32_nan_val(float %x) #0 { %y = call float @llvm.maxnum.f32(float 0x7FF8000000000000, float %x) #0 ret float %y } ; CHECK-LABEL: @maxnum_f32_val_nan -; CHECK-NEXT: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @maxnum_f32_val_nan(float %x) #0 { %y = call float @llvm.maxnum.f32(float %x, float 0x7FF8000000000000) #0 ret float %y } ; CHECK-LABEL: @fold_maxnum_f32_undef_undef -; CHECK-NEXT: ret float undef -define float @fold_maxnum_f32_undef_undef(float %x) nounwind { +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float undef) +; CHECK-NEXT: ret float %1 +define float @fold_maxnum_f32_undef_undef() nounwind { %val = call float @llvm.maxnum.f32(float undef, float undef) #0 ret float %val } ; CHECK-LABEL: @fold_maxnum_f32_val_undef -; CHECK-NEXT: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @fold_maxnum_f32_val_undef(float %x) nounwind { %val = call float @llvm.maxnum.f32(float %x, float undef) #0 ret float %val } ; CHECK-LABEL: @fold_maxnum_f32_undef_val -; CHECK-NEXT: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @fold_maxnum_f32_undef_val(float %x) nounwind { %val = call float @llvm.maxnum.f32(float undef, float %x) #0 ret float %val @@ -205,7 +211,8 @@ } ; CHECK-LABEL: @fold_maxnum_f32_inf_val -; CHECK-NEXT: ret float 0x7FF0000000000000 +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float 0x7FF0000000000000) +; CHECK-NEXT: ret float %1 define float @fold_maxnum_f32_inf_val(float %x) nounwind { %val = call float @llvm.maxnum.f32(float 0x7FF0000000000000, float %x) #0 ret float %val Index: test/Transforms/InstCombine/minnum.ll =================================================================== --- test/Transforms/InstCombine/minnum.ll +++ test/Transforms/InstCombine/minnum.ll @@ -115,42 +115,48 @@ } ; CHECK-LABEL: @noop_minnum_f32 -; CHECK-NEXT: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @noop_minnum_f32(float %x) #0 { %y = call float @llvm.minnum.f32(float %x, float %x) #0 ret float %y } ; CHECK-LABEL: @minnum_f32_nan_val -; CHECK-NEXT: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @minnum_f32_nan_val(float %x) #0 { %y = call float @llvm.minnum.f32(float 0x7FF8000000000000, float %x) #0 ret float %y } ; CHECK-LABEL: @minnum_f32_val_nan -; CHECK-NEXT: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @minnum_f32_val_nan(float %x) #0 { %y = call float @llvm.minnum.f32(float %x, float 0x7FF8000000000000) #0 ret float %y } ; CHECK-LABEL: @fold_minnum_f32_undef_undef -; CHECK-NEXT: ret float undef -define float @fold_minnum_f32_undef_undef(float %x) nounwind { +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float undef) +; CHECK-NEXT: ret float %1 +define float @fold_minnum_f32_undef_undef() nounwind { %val = call float @llvm.minnum.f32(float undef, float undef) #0 ret float %val } ; CHECK-LABEL: @fold_minnum_f32_val_undef -; CHECK-NEXT: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @fold_minnum_f32_val_undef(float %x) nounwind { %val = call float @llvm.minnum.f32(float %x, float undef) #0 ret float %val } ; CHECK-LABEL: @fold_minnum_f32_undef_val -; CHECK-NEXT: ret float %x +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float %x) +; CHECK-NEXT: ret float %1 define float @fold_minnum_f32_undef_val(float %x) nounwind { %val = call float @llvm.minnum.f32(float undef, float %x) #0 ret float %val @@ -235,7 +241,8 @@ } ; CHECK-LABEL: @fold_minnum_f32_minf_val -; CHECK-NEXT: ret float 0xFFF0000000000000 +; CHECK-NEXT: %1 = call float @llvm.canonicalize.f32(float 0xFFF0000000000000) +; CHECK-NEXT: ret float %1 define float @fold_minnum_f32_minf_val(float %x) nounwind { %val = call float @llvm.minnum.f32(float 0xFFF0000000000000, float %x) #0 ret float %val