Skip to content

Conversation

@wzssyqa
Copy link
Contributor

@wzssyqa wzssyqa commented Oct 21, 2024

See: #112852

We will define llvm.minnum and llvm.maxnum with +0.0>-0.0, by default, while libc doesn't require it.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. llvm:ir labels Oct 21, 2024
@llvmbot
Copy link
Member

llvmbot commented Oct 21, 2024

@llvm/pr-subscribers-backend-risc-v
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-clang-codegen

Author: YunQiang Su (wzssyqa)

Changes

See: #112852

We will define llvm.minnum and llvm.maxnum with +0.0>-0.0, by default, while libc doesn't require it.


Full diff: https://github.com/llvm/llvm-project/pull/113133.diff

4 Files Affected:

  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+20-13)
  • (added) clang/test/CodeGen/fmaxnum_fminnum_use_nsz.c (+33)
  • (modified) llvm/include/llvm/IR/IRBuilder.h (+10-6)
  • (modified) llvm/lib/IR/IRBuilder.cpp (+2-2)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 28f28c70b5ae52..f2d6049908720b 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -510,19 +510,20 @@ static Value *emitUnaryMaybeConstrainedFPBuiltin(CodeGenFunction &CGF,
 
 // Emit an intrinsic that has 2 operands of the same type as its result.
 // Depending on mode, this may be a constrained floating-point intrinsic.
-static Value *emitBinaryMaybeConstrainedFPBuiltin(CodeGenFunction &CGF,
-                                const CallExpr *E, unsigned IntrinsicID,
-                                unsigned ConstrainedIntrinsicID) {
+static Value *emitBinaryMaybeConstrainedFPBuiltin(
+    CodeGenFunction &CGF, const CallExpr *E, unsigned IntrinsicID,
+    unsigned ConstrainedIntrinsicID, llvm::FastMathFlags *FMF = nullptr) {
   llvm::Value *Src0 = CGF.EmitScalarExpr(E->getArg(0));
   llvm::Value *Src1 = CGF.EmitScalarExpr(E->getArg(1));
 
   CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, E);
   if (CGF.Builder.getIsFPConstrained()) {
     Function *F = CGF.CGM.getIntrinsic(ConstrainedIntrinsicID, Src0->getType());
-    return CGF.Builder.CreateConstrainedFPCall(F, { Src0, Src1 });
+    return CGF.Builder.CreateConstrainedFPCall(F, {Src0, Src1}, "",
+                                               std::nullopt, std::nullopt, FMF);
   } else {
     Function *F = CGF.CGM.getIntrinsic(IntrinsicID, Src0->getType());
-    return CGF.Builder.CreateCall(F, { Src0, Src1 });
+    return CGF.Builder.CreateCall(F, {Src0, Src1}, "", nullptr, FMF);
   }
 }
 
@@ -2846,10 +2847,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_fmaxf:
     case Builtin::BI__builtin_fmaxf16:
     case Builtin::BI__builtin_fmaxl:
-    case Builtin::BI__builtin_fmaxf128:
-      return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::maxnum,
-                                   Intrinsic::experimental_constrained_maxnum));
+    case Builtin::BI__builtin_fmaxf128: {
+      llvm::FastMathFlags FMF;
+      FMF.setNoSignedZeros();
+      return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::maxnum,
+          Intrinsic::experimental_constrained_maxnum, &FMF));
+    }
 
     case Builtin::BIfmin:
     case Builtin::BIfminf:
@@ -2858,10 +2862,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_fminf:
     case Builtin::BI__builtin_fminf16:
     case Builtin::BI__builtin_fminl:
-    case Builtin::BI__builtin_fminf128:
-      return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::minnum,
-                                   Intrinsic::experimental_constrained_minnum));
+    case Builtin::BI__builtin_fminf128: {
+      llvm::FastMathFlags FMF;
+      FMF.setNoSignedZeros();
+      return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::minnum,
+          Intrinsic::experimental_constrained_minnum, &FMF));
+    }
 
     case Builtin::BIfmaximum_num:
     case Builtin::BIfmaximum_numf:
diff --git a/clang/test/CodeGen/fmaxnum_fminnum_use_nsz.c b/clang/test/CodeGen/fmaxnum_fminnum_use_nsz.c
new file mode 100644
index 00000000000000..9798baf0432fea
--- /dev/null
+++ b/clang/test/CodeGen/fmaxnum_fminnum_use_nsz.c
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -triple x86_64 %s -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK
+
+float fminf (float, float);
+double fmin (double, double);
+long double fminl (long double, long double);
+float fmaxf (float, float);
+double fmax (double, double);
+long double fmaxl (long double, long double);
+
+// CHECK: call nsz float @llvm.minnum.f32
+float fmin1(float a, float b) {
+        return fminf(a, b);
+}
+// CHECK: call nsz double @llvm.minnum.f64
+float fmin2(double a, double b) {
+        return fmin(a, b);
+}
+// CHECK: call nsz x86_fp80 @llvm.minnum.f80
+float fmin3(long double a, long double b) {
+        return fminl(a, b);
+}
+// CHECK: call nsz float @llvm.maxnum.f32
+float fmax1(float a, float b) {
+        return fmaxf(a, b);
+}
+// CHECK: call nsz double @llvm.maxnum.f64
+float fmax2(double a, double b) {
+        return fmax(a, b);
+}
+// CHECK: call nsz x86_fp80 @llvm.maxnum.f80
+float fmax3(long double a, long double b) {
+        return fmaxl(a, b);
+}
diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h
index 23fd8350a29b3d..1baca4f003cad6 100644
--- a/llvm/include/llvm/IR/IRBuilder.h
+++ b/llvm/include/llvm/IR/IRBuilder.h
@@ -2438,12 +2438,14 @@ class IRBuilderBase {
 public:
   CallInst *CreateCall(FunctionType *FTy, Value *Callee,
                        ArrayRef<Value *> Args = {}, const Twine &Name = "",
-                       MDNode *FPMathTag = nullptr) {
+                       MDNode *FPMathTag = nullptr,
+                       FastMathFlags *uFMF = nullptr) {
     CallInst *CI = CallInst::Create(FTy, Callee, Args, DefaultOperandBundles);
     if (IsFPConstrained)
       setConstrainedFPCallAttr(CI);
-    if (isa<FPMathOperator>(CI))
-      setFPAttrs(CI, FPMathTag, FMF);
+    if (isa<FPMathOperator>(CI)) {
+      setFPAttrs(CI, FPMathTag, uFMF ? (FMF | *uFMF) : FMF);
+    }
     return Insert(CI, Name);
   }
 
@@ -2459,9 +2461,10 @@ class IRBuilderBase {
   }
 
   CallInst *CreateCall(FunctionCallee Callee, ArrayRef<Value *> Args = {},
-                       const Twine &Name = "", MDNode *FPMathTag = nullptr) {
+                       const Twine &Name = "", MDNode *FPMathTag = nullptr,
+                       FastMathFlags *uFMF = nullptr) {
     return CreateCall(Callee.getFunctionType(), Callee.getCallee(), Args, Name,
-                      FPMathTag);
+                      FPMathTag, uFMF);
   }
 
   CallInst *CreateCall(FunctionCallee Callee, ArrayRef<Value *> Args,
@@ -2474,7 +2477,8 @@ class IRBuilderBase {
   CallInst *CreateConstrainedFPCall(
       Function *Callee, ArrayRef<Value *> Args, const Twine &Name = "",
       std::optional<RoundingMode> Rounding = std::nullopt,
-      std::optional<fp::ExceptionBehavior> Except = std::nullopt);
+      std::optional<fp::ExceptionBehavior> Except = std::nullopt,
+      FastMathFlags *FMF = nullptr);
 
   Value *CreateSelect(Value *C, Value *True, Value *False,
                       const Twine &Name = "", Instruction *MDFrom = nullptr);
diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp
index f340f7aafdc76f..5feaf956b45a97 100644
--- a/llvm/lib/IR/IRBuilder.cpp
+++ b/llvm/lib/IR/IRBuilder.cpp
@@ -1031,7 +1031,7 @@ CallInst *IRBuilderBase::CreateConstrainedFPCmp(
 CallInst *IRBuilderBase::CreateConstrainedFPCall(
     Function *Callee, ArrayRef<Value *> Args, const Twine &Name,
     std::optional<RoundingMode> Rounding,
-    std::optional<fp::ExceptionBehavior> Except) {
+    std::optional<fp::ExceptionBehavior> Except, FastMathFlags *FMF) {
   llvm::SmallVector<Value *, 6> UseArgs;
 
   append_range(UseArgs, Args);
@@ -1040,7 +1040,7 @@ CallInst *IRBuilderBase::CreateConstrainedFPCall(
     UseArgs.push_back(getConstrainedFPRounding(Rounding));
   UseArgs.push_back(getConstrainedFPExcept(Except));
 
-  CallInst *C = CreateCall(Callee, UseArgs, Name);
+  CallInst *C = CreateCall(Callee, UseArgs, Name, nullptr, FMF);
   setConstrainedFPCallAttr(C);
   return C;
 }

@llvmbot
Copy link
Member

llvmbot commented Oct 21, 2024

@llvm/pr-subscribers-clang

Author: YunQiang Su (wzssyqa)

Changes

See: #112852

We will define llvm.minnum and llvm.maxnum with +0.0>-0.0, by default, while libc doesn't require it.


Full diff: https://github.com/llvm/llvm-project/pull/113133.diff

4 Files Affected:

  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+20-13)
  • (added) clang/test/CodeGen/fmaxnum_fminnum_use_nsz.c (+33)
  • (modified) llvm/include/llvm/IR/IRBuilder.h (+10-6)
  • (modified) llvm/lib/IR/IRBuilder.cpp (+2-2)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 28f28c70b5ae52..f2d6049908720b 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -510,19 +510,20 @@ static Value *emitUnaryMaybeConstrainedFPBuiltin(CodeGenFunction &CGF,
 
 // Emit an intrinsic that has 2 operands of the same type as its result.
 // Depending on mode, this may be a constrained floating-point intrinsic.
-static Value *emitBinaryMaybeConstrainedFPBuiltin(CodeGenFunction &CGF,
-                                const CallExpr *E, unsigned IntrinsicID,
-                                unsigned ConstrainedIntrinsicID) {
+static Value *emitBinaryMaybeConstrainedFPBuiltin(
+    CodeGenFunction &CGF, const CallExpr *E, unsigned IntrinsicID,
+    unsigned ConstrainedIntrinsicID, llvm::FastMathFlags *FMF = nullptr) {
   llvm::Value *Src0 = CGF.EmitScalarExpr(E->getArg(0));
   llvm::Value *Src1 = CGF.EmitScalarExpr(E->getArg(1));
 
   CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, E);
   if (CGF.Builder.getIsFPConstrained()) {
     Function *F = CGF.CGM.getIntrinsic(ConstrainedIntrinsicID, Src0->getType());
-    return CGF.Builder.CreateConstrainedFPCall(F, { Src0, Src1 });
+    return CGF.Builder.CreateConstrainedFPCall(F, {Src0, Src1}, "",
+                                               std::nullopt, std::nullopt, FMF);
   } else {
     Function *F = CGF.CGM.getIntrinsic(IntrinsicID, Src0->getType());
-    return CGF.Builder.CreateCall(F, { Src0, Src1 });
+    return CGF.Builder.CreateCall(F, {Src0, Src1}, "", nullptr, FMF);
   }
 }
 
@@ -2846,10 +2847,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_fmaxf:
     case Builtin::BI__builtin_fmaxf16:
     case Builtin::BI__builtin_fmaxl:
-    case Builtin::BI__builtin_fmaxf128:
-      return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::maxnum,
-                                   Intrinsic::experimental_constrained_maxnum));
+    case Builtin::BI__builtin_fmaxf128: {
+      llvm::FastMathFlags FMF;
+      FMF.setNoSignedZeros();
+      return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::maxnum,
+          Intrinsic::experimental_constrained_maxnum, &FMF));
+    }
 
     case Builtin::BIfmin:
     case Builtin::BIfminf:
@@ -2858,10 +2862,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Builtin::BI__builtin_fminf:
     case Builtin::BI__builtin_fminf16:
     case Builtin::BI__builtin_fminl:
-    case Builtin::BI__builtin_fminf128:
-      return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(*this, E,
-                                   Intrinsic::minnum,
-                                   Intrinsic::experimental_constrained_minnum));
+    case Builtin::BI__builtin_fminf128: {
+      llvm::FastMathFlags FMF;
+      FMF.setNoSignedZeros();
+      return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(
+          *this, E, Intrinsic::minnum,
+          Intrinsic::experimental_constrained_minnum, &FMF));
+    }
 
     case Builtin::BIfmaximum_num:
     case Builtin::BIfmaximum_numf:
diff --git a/clang/test/CodeGen/fmaxnum_fminnum_use_nsz.c b/clang/test/CodeGen/fmaxnum_fminnum_use_nsz.c
new file mode 100644
index 00000000000000..9798baf0432fea
--- /dev/null
+++ b/clang/test/CodeGen/fmaxnum_fminnum_use_nsz.c
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -triple x86_64 %s -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK
+
+float fminf (float, float);
+double fmin (double, double);
+long double fminl (long double, long double);
+float fmaxf (float, float);
+double fmax (double, double);
+long double fmaxl (long double, long double);
+
+// CHECK: call nsz float @llvm.minnum.f32
+float fmin1(float a, float b) {
+        return fminf(a, b);
+}
+// CHECK: call nsz double @llvm.minnum.f64
+float fmin2(double a, double b) {
+        return fmin(a, b);
+}
+// CHECK: call nsz x86_fp80 @llvm.minnum.f80
+float fmin3(long double a, long double b) {
+        return fminl(a, b);
+}
+// CHECK: call nsz float @llvm.maxnum.f32
+float fmax1(float a, float b) {
+        return fmaxf(a, b);
+}
+// CHECK: call nsz double @llvm.maxnum.f64
+float fmax2(double a, double b) {
+        return fmax(a, b);
+}
+// CHECK: call nsz x86_fp80 @llvm.maxnum.f80
+float fmax3(long double a, long double b) {
+        return fmaxl(a, b);
+}
diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h
index 23fd8350a29b3d..1baca4f003cad6 100644
--- a/llvm/include/llvm/IR/IRBuilder.h
+++ b/llvm/include/llvm/IR/IRBuilder.h
@@ -2438,12 +2438,14 @@ class IRBuilderBase {
 public:
   CallInst *CreateCall(FunctionType *FTy, Value *Callee,
                        ArrayRef<Value *> Args = {}, const Twine &Name = "",
-                       MDNode *FPMathTag = nullptr) {
+                       MDNode *FPMathTag = nullptr,
+                       FastMathFlags *uFMF = nullptr) {
     CallInst *CI = CallInst::Create(FTy, Callee, Args, DefaultOperandBundles);
     if (IsFPConstrained)
       setConstrainedFPCallAttr(CI);
-    if (isa<FPMathOperator>(CI))
-      setFPAttrs(CI, FPMathTag, FMF);
+    if (isa<FPMathOperator>(CI)) {
+      setFPAttrs(CI, FPMathTag, uFMF ? (FMF | *uFMF) : FMF);
+    }
     return Insert(CI, Name);
   }
 
@@ -2459,9 +2461,10 @@ class IRBuilderBase {
   }
 
   CallInst *CreateCall(FunctionCallee Callee, ArrayRef<Value *> Args = {},
-                       const Twine &Name = "", MDNode *FPMathTag = nullptr) {
+                       const Twine &Name = "", MDNode *FPMathTag = nullptr,
+                       FastMathFlags *uFMF = nullptr) {
     return CreateCall(Callee.getFunctionType(), Callee.getCallee(), Args, Name,
-                      FPMathTag);
+                      FPMathTag, uFMF);
   }
 
   CallInst *CreateCall(FunctionCallee Callee, ArrayRef<Value *> Args,
@@ -2474,7 +2477,8 @@ class IRBuilderBase {
   CallInst *CreateConstrainedFPCall(
       Function *Callee, ArrayRef<Value *> Args, const Twine &Name = "",
       std::optional<RoundingMode> Rounding = std::nullopt,
-      std::optional<fp::ExceptionBehavior> Except = std::nullopt);
+      std::optional<fp::ExceptionBehavior> Except = std::nullopt,
+      FastMathFlags *FMF = nullptr);
 
   Value *CreateSelect(Value *C, Value *True, Value *False,
                       const Twine &Name = "", Instruction *MDFrom = nullptr);
diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp
index f340f7aafdc76f..5feaf956b45a97 100644
--- a/llvm/lib/IR/IRBuilder.cpp
+++ b/llvm/lib/IR/IRBuilder.cpp
@@ -1031,7 +1031,7 @@ CallInst *IRBuilderBase::CreateConstrainedFPCmp(
 CallInst *IRBuilderBase::CreateConstrainedFPCall(
     Function *Callee, ArrayRef<Value *> Args, const Twine &Name,
     std::optional<RoundingMode> Rounding,
-    std::optional<fp::ExceptionBehavior> Except) {
+    std::optional<fp::ExceptionBehavior> Except, FastMathFlags *FMF) {
   llvm::SmallVector<Value *, 6> UseArgs;
 
   append_range(UseArgs, Args);
@@ -1040,7 +1040,7 @@ CallInst *IRBuilderBase::CreateConstrainedFPCall(
     UseArgs.push_back(getConstrainedFPRounding(Rounding));
   UseArgs.push_back(getConstrainedFPExcept(Except));
 
-  CallInst *C = CreateCall(Callee, UseArgs, Name);
+  CallInst *C = CreateCall(Callee, UseArgs, Name, nullptr, FMF);
   setConstrainedFPCallAttr(C);
   return C;
 }

@wzssyqa wzssyqa requested a review from arsenm October 21, 2024 07:25
@arsenm arsenm added the floating-point Floating-point math label Oct 21, 2024
Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also add new builtins (with the minnum/maxnum names) to get the stronger 2019 behavior

@wzssyqa wzssyqa force-pushed the clang-fmin-with-nsz branch from e3bff67 to 8655cb0 Compare October 23, 2024 03:11
@wzssyqa wzssyqa requested a review from arsenm October 23, 2024 04:48
@wzssyqa
Copy link
Contributor Author

wzssyqa commented Nov 25, 2024

@arsenm ping

Copy link
Contributor

@jcranmer-intel jcranmer-intel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll want to merge the fast-math flags, so that compiling with -ffinite-math-only gets you nnan ninf nsz on the maxnum/minnum, rather than just nsz.

@wzssyqa wzssyqa force-pushed the clang-fmin-with-nsz branch from 105c9a7 to a3f3567 Compare February 27, 2025 10:06
@wzssyqa wzssyqa marked this pull request as draft February 27, 2025 10:13
@wzssyqa
Copy link
Contributor Author

wzssyqa commented Feb 27, 2025

nsz is missing for __builtin_elementwise_min.
I am working on it.

@arsenm
Copy link
Contributor

arsenm commented Feb 27, 2025

We should also have new builtins for the raw minnum / maxnum intrinsics (plus elementwise) to not get the nsz

@arsenm
Copy link
Contributor

arsenm commented Feb 27, 2025

nsz is missing for __builtin_elementwise_min. I am working on it.

I'm not sure that's a good idea. For fmin/fmax it seems OK, but this doesn't take the libm name

@wzssyqa wzssyqa force-pushed the clang-fmin-with-nsz branch from 8b63af7 to ce675ee Compare March 4, 2025 06:15
@wzssyqa wzssyqa marked this pull request as ready for review March 4, 2025 06:16
@wzssyqa wzssyqa requested review from arsenm and jcranmer-intel March 4, 2025 06:16
@wzssyqa
Copy link
Contributor Author

wzssyqa commented Mar 4, 2025

You'll want to merge the fast-math flags, so that compiling with -ffinite-math-only gets you nnan ninf nsz on the maxnum/minnum, rather than just nsz.

Done, for cc1, we use -menable-no-nans and -menable-no-infs.

@arsenm arsenm changed the title Clang: emit llvm.minnum and llvm.maxnum with nsz always Clang: Add nsz to llvm.minnum and llvm.maxnum emitted from fmin and fmax Mar 4, 2025
@wzssyqa wzssyqa requested a review from arsenm March 4, 2025 07:49
@efriedma-quic
Copy link
Collaborator

Please propose a fix for the definition of nsz itself itself in LangRef; burying an exception to the general nsz rules in the middle of the definition of min/max is, at best, confusing.

@wzssyqa
Copy link
Contributor Author

wzssyqa commented Sep 14, 2025

It takes too long to take this decision.
Since we cannot agree on the wording of nsz now, and in fact we had some words in maxnum/minnum about nsz.
I prefer we can continue with this PR.

@wzssyqa
Copy link
Contributor Author

wzssyqa commented Sep 14, 2025

It takes too long to take this decision. Since we cannot agree on the wording of nsz now, and in fact we had some words in maxnum/minnum about nsz. I prefer we can continue with this PR.

We have got some complains: #138303

See: llvm#112852

We will define llvm.minnum and llvm.maxnum with +0.0>-0.0, by default,
while libc doesn't require it.

fix testcases

-ffp-exception-behavior=strict

add missing builtin test

test auto vectorize

fix test cases

update testcase

disable-llvm-passes

fix elementswise

fix some tests
@wzssyqa
Copy link
Contributor Author

wzssyqa commented Dec 2, 2025

#168838

OK, now we decides to define llvm.minnum and llvm.maxnum as same as libm.
So we don't need it now. Let's close it.

@wzssyqa wzssyqa closed this Dec 2, 2025
@arsenm
Copy link
Contributor

arsenm commented Dec 2, 2025

#168838

OK, now we decides to define llvm.minnum and llvm.maxnum as same as libm. So we don't need it now. Let's close it.

No, this should still be done

@arsenm arsenm reopened this Dec 2, 2025
@github-actions
Copy link

github-actions bot commented Dec 2, 2025

🐧 Linux x64 Test Results

  • 193631 tests passed
  • 6242 tests skipped

✅ The build succeeded and all tests passed.

@wzssyqa
Copy link
Contributor Author

wzssyqa commented Dec 4, 2025

@arsenm ping

Copy link
Contributor

@fhahn fhahn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any data on the impact of the change?

@wzssyqa
Copy link
Contributor Author

wzssyqa commented Dec 4, 2025

Do you have any data on the impact of the change?

It is used in TargetLowering::expandFMINNUM_FMAXNUM:

  if ((Node->getFlags().hasNoNaNs() ||
       (DAG.isKnownNeverNaN(Node->getOperand(0)) &&
        DAG.isKnownNeverNaN(Node->getOperand(1)))) &&
      (Node->getFlags().hasNoSignedZeros() ||
       DAG.isKnownNeverZeroFloat(Node->getOperand(0)) ||
       DAG.isKnownNeverZeroFloat(Node->getOperand(1)))) {
    unsigned IEEE2018Op =
        Node->getOpcode() == ISD::FMINNUM ? ISD::FMINIMUM : ISD::FMAXIMUM;
    if (isOperationLegalOrCustom(IEEE2018Op, VT))
      return DAG.getNode(IEEE2018Op, dl, VT, Node->getOperand(0),
                         Node->getOperand(1), Node->getFlags());
  }

We will add more expand rules there.

@arsenm
Copy link
Contributor

arsenm commented Dec 4, 2025

Do you have any data on the impact of the change?

This change in isolation shouldn't change anything. In the absence of changing the minnum definition to have strict zero handling, adding nsz is a no-op. With strict signed zero handling, this would help avoid regressions in the lowering

@wzssyqa wzssyqa requested review from arsenm and fhahn December 5, 2025 01:56
@github-actions
Copy link

github-actions bot commented Dec 5, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Result = Builder.CreateMinNum(Op0, Op1, /*FMFSource=*/nullptr, "elt.min");
} else {
FastMathFlags FMF;
FMF.setNoSignedZeros(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should apply in the elementwise case.

I also think it was a mistake to allow floating point in elementwise min/max

Copy link
Contributor Author

@wzssyqa wzssyqa Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See: #129207

In that PR, we planned to use the same naming scheme
__builtin_elementwise_max -> max (fmax)
__builtin_elementwise_maxnum -> maxnum

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are planning to drop float support of __builtin_elementwise, it should be in another patchset.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this should be discussed separately. Feedback from library authors has been that different builtins for floats/ints are a bit of a pain

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But they are in fact, different operations. And you have many choices for which FP min/max.

Given this name doesn't have the historic fmin/fmax in it, I don't think this should take the fuzzy signed zero handling

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact I am planning to add __builtin_elementwise_maximumnum after this PR is merged.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree this should not continue to use minnum/maxnum, though that change should not be part of this PR.

minnum/maxnum behavior has never been consistent across targets (or across scenarios). But we could also add __builtin_elementwise_maximumnum/__builtin_elementwise_minimnumnum

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree this should not continue to use minnum/maxnum, though that change should not be part of this PR.

So can we merge this PR?

minnum/maxnum behavior has never been consistent across targets (or across scenarios).

In fact, all of the architectures that claims implement IEEE754-2008, has the same behavior:
AArch64, MIPSr6, LoongArch, PowerPC/VSX
That's why I'd plan to define minnum/maxnum as the same as these architectures.

But we could also add __builtin_elementwise_maximumnum/__builtin_elementwise_minimnumnum

I will do it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So can we merge this PR?

The discussion in #137567 is still unresolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#137567 is about all opcodes, and in fact for min/max we have done with
#112852

@wzssyqa wzssyqa requested review from arsenm and nikic December 9, 2025 08:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:RISC-V clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category floating-point Floating-point math llvm:ir

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants