Skip to content

Clang: Deprecate float support from __builtin_elementwise_max - #180885

Merged
wzssyqa merged 9 commits into
llvm:mainfrom
wzssyqa:elt_max_drop_float
Feb 28, 2026
Merged

wzssyqa merged 9 commits into
llvm:mainfrom
wzssyqa:elt_max_drop_float

Conversation

@wzssyqa

@wzssyqa wzssyqa commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Now we have
__builtin_elementwise_maxnum
__builtin_elementwise_maximum
__builtin_elementwise_maximumnum

Now we have
  __builtin_elementwise_maxnum
  __builtin_elementwise_maximum
  __builtin_elementwise_maximumnum
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. labels Feb 11, 2026
@llvmbot

llvmbot commented Feb 11, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-hlsl
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-codegen

Author: YunQiang Su (wzssyqa)

Changes

Now we have
__builtin_elementwise_maxnum
__builtin_elementwise_maximum
__builtin_elementwise_maximumnum


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

5 Files Affected:

  • (modified) clang/docs/LanguageExtensions.rst (+2-10)
  • (modified) clang/docs/ReleaseNotes.rst (+3)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+14-18)
  • (modified) clang/test/CodeGen/builtins-elementwise-math.c (+2-68)
  • (modified) clang/test/CodeGen/strictfp-elementwise-builtins.cpp (+12-12)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 29328355c3e6f..745000e79027c 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -839,16 +839,8 @@ of different sizes and signs is forbidden in binary and ternary builtins.
  T __builtin_elementwise_copysign(T x, T y)     return the magnitude of x with the sign of y.                          floating point types
  T __builtin_elementwise_fmod(T x, T y)         return the floating-point remainder of (x/y) whose sign                floating point types
                                                 matches the sign of x.
- T __builtin_elementwise_max(T x, T y)          return x or y, whichever is larger                                     integer and floating point types
-                                                For floating point types, follows semantics of maxNum
-                                                in IEEE 754-2008. See `LangRef
-                                                <http://llvm.org/docs/LangRef.html#i-fminmax-family>`_
-                                                for the comparison.
- T __builtin_elementwise_min(T x, T y)          return x or y, whichever is smaller                                    integer and floating point types
-                                                For floating point types, follows semantics of minNum
-                                                in IEEE 754-2008. See `LangRef
-                                                <http://llvm.org/docs/LangRef.html#i-fminmax-family>`_
-                                                for the comparison.
+ T __builtin_elementwise_max(T x, T y)          return x or y, whichever is larger                                     integer types
+ T __builtin_elementwise_min(T x, T y)          return x or y, whichever is smaller                                    integer types
  T __builtin_elementwise_maxnum(T x, T y)       return x or y, whichever is larger. Follows IEEE 754-2008              floating point types
                                                 semantics (maxNum) with +0.0>-0.0. See `LangRef
                                                 <http://llvm.org/docs/LangRef.html#i-fminmax-family>`_
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0dbea8efc2642..758982d6e6431 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -138,6 +138,9 @@ Non-comprehensive list of changes in this release
   Usable in constant expressions. Implicit conversion is supported for
   class/struct types with conversion operators.
 
+- Removed float types support from ``__builtin_elementwise_max`` and
+  ``__builtin_elementwise_min``.
+
 New Compiler Flags
 ------------------
 - New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index cf686581240a5..bb66677fb40c9 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4066,30 +4066,26 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     Value *Op0 = EmitScalarExpr(E->getArg(0));
     Value *Op1 = EmitScalarExpr(E->getArg(1));
     Value *Result;
-    if (Op0->getType()->isIntOrIntVectorTy()) {
-      QualType Ty = E->getArg(0)->getType();
-      if (auto *VecTy = Ty->getAs<VectorType>())
-        Ty = VecTy->getElementType();
-      Result = Builder.CreateBinaryIntrinsic(
-          Ty->isSignedIntegerType() ? Intrinsic::smax : Intrinsic::umax, Op0,
-          Op1, nullptr, "elt.max");
-    } else
-      Result = Builder.CreateMaxNum(Op0, Op1, /*FMFSource=*/nullptr, "elt.max");
+    assert(Op0->getType()->isIntOrIntVectorTy());
+    QualType Ty = E->getArg(0)->getType();
+    if (auto *VecTy = Ty->getAs<VectorType>())
+      Ty = VecTy->getElementType();
+    Result = Builder.CreateBinaryIntrinsic(
+        Ty->isSignedIntegerType() ? Intrinsic::smax : Intrinsic::umax, Op0,
+        Op1, nullptr, "elt.max");
     return RValue::get(Result);
   }
   case Builtin::BI__builtin_elementwise_min: {
     Value *Op0 = EmitScalarExpr(E->getArg(0));
     Value *Op1 = EmitScalarExpr(E->getArg(1));
     Value *Result;
-    if (Op0->getType()->isIntOrIntVectorTy()) {
-      QualType Ty = E->getArg(0)->getType();
-      if (auto *VecTy = Ty->getAs<VectorType>())
-        Ty = VecTy->getElementType();
-      Result = Builder.CreateBinaryIntrinsic(
-          Ty->isSignedIntegerType() ? Intrinsic::smin : Intrinsic::umin, Op0,
-          Op1, nullptr, "elt.min");
-    } else
-      Result = Builder.CreateMinNum(Op0, Op1, /*FMFSource=*/nullptr, "elt.min");
+    assert(Op0->getType()->isIntOrIntVectorTy());
+    QualType Ty = E->getArg(0)->getType();
+    if (auto *VecTy = Ty->getAs<VectorType>())
+      Ty = VecTy->getElementType();
+    Result = Builder.CreateBinaryIntrinsic(
+        Ty->isSignedIntegerType() ? Intrinsic::smin : Intrinsic::umin, Op0,
+        Op1, nullptr, "elt.min");
     return RValue::get(Result);
   }
 
diff --git a/clang/test/CodeGen/builtins-elementwise-math.c b/clang/test/CodeGen/builtins-elementwise-math.c
index 2df485f0155c3..a201403e8b6b1 100644
--- a/clang/test/CodeGen/builtins-elementwise-math.c
+++ b/clang/test/CodeGen/builtins-elementwise-math.c
@@ -339,32 +339,10 @@ void test_builtin_elementwise_minimum(float f1, float f2, double d1, double d2,
   vf1 = __builtin_elementwise_minimum(vf2, cvf1);
 }
 
-void test_builtin_elementwise_max(float f1, float f2, double d1, double d2,
-                                  float4 vf1, float4 vf2, long long int i1,
-                                  long long int i2, si8 vi1, si8 vi2,
+void test_builtin_elementwise_max(long long int i2, si8 vi1, si8 vi2, long long int i1,
                                   unsigned u1, unsigned u2, u4 vu1, u4 vu2,
                                   _BitInt(31) bi1, _BitInt(31) bi2,
                                   unsigned _BitInt(55) bu1, unsigned _BitInt(55) bu2) {
-  // CHECK-LABEL: define void @test_builtin_elementwise_max(
-  // CHECK:      [[F1:%.+]] = load float, ptr %f1.addr, align 4
-  // CHECK-NEXT: [[F2:%.+]] = load float, ptr %f2.addr, align 4
-  // CHECK-NEXT:  call float @llvm.maxnum.f32(float [[F1]], float [[F2]])
-  f1 = __builtin_elementwise_max(f1, f2);
-
-  // CHECK:      [[D1:%.+]] = load double, ptr %d1.addr, align 8
-  // CHECK-NEXT: [[D2:%.+]] = load double, ptr %d2.addr, align 8
-  // CHECK-NEXT: call double @llvm.maxnum.f64(double [[D1]], double [[D2]])
-  d1 = __builtin_elementwise_max(d1, d2);
-
-  // CHECK:      [[D2:%.+]] = load double, ptr %d2.addr, align 8
-  // CHECK-NEXT: call double @llvm.maxnum.f64(double 2.000000e+01, double [[D2]])
-  d1 = __builtin_elementwise_max(20.0, d2);
-
-  // CHECK:      [[VF1:%.+]] = load <4 x float>, ptr %vf1.addr, align 16
-  // CHECK-NEXT: [[VF2:%.+]] = load <4 x float>, ptr %vf2.addr, align 16
-  // CHECK-NEXT: call <4 x float> @llvm.maxnum.v4f32(<4 x float> [[VF1]], <4 x float> [[VF2]])
-  vf1 = __builtin_elementwise_max(vf1, vf2);
-
   // CHECK:      [[I1:%.+]] = load i64, ptr %i1.addr, align 8
   // CHECK-NEXT: [[I2:%.+]] = load i64, ptr %i2.addr, align 8
   // CHECK-NEXT: call i64 @llvm.smax.i64(i64 [[I1]], i64 [[I2]])
@@ -403,17 +381,6 @@ void test_builtin_elementwise_max(float f1, float f2, double d1, double d2,
   // CHECK-NEXT: call i55 @llvm.umax.i55(i55 [[LOADEDV2]], i55 [[LOADEDV3]])
   bu1 = __builtin_elementwise_max(bu1, bu2);
 
-  // CHECK:      [[CVF1:%.+]] = load <4 x float>, ptr %cvf1, align 16
-  // CHECK-NEXT: [[VF2:%.+]] = load <4 x float>, ptr %vf2.addr, align 16
-  // CHECK-NEXT: call <4 x float> @llvm.maxnum.v4f32(<4 x float> [[CVF1]], <4 x float> [[VF2]])
-  const float4 cvf1 = vf1;
-  vf1 = __builtin_elementwise_max(cvf1, vf2);
-
-  // CHECK:      [[VF2:%.+]] = load <4 x float>, ptr %vf2.addr, align 16
-  // CHECK-NEXT: [[CVF1:%.+]] = load <4 x float>, ptr %cvf1, align 16
-  // CHECK-NEXT: call <4 x float> @llvm.maxnum.v4f32(<4 x float> [[VF2]], <4 x float> [[CVF1]])
-  vf1 = __builtin_elementwise_max(vf2, cvf1);
-
   // CHECK:      [[IAS1:%.+]] = load i32, ptr addrspace(1) @int_as_one, align 4
   // CHECK-NEXT: [[B:%.+]] = load i32, ptr @b, align 4
   // CHECK-NEXT: call i32 @llvm.smax.i32(i32 [[IAS1]], i32 [[B]])
@@ -423,32 +390,10 @@ void test_builtin_elementwise_max(float f1, float f2, double d1, double d2,
   i1 = __builtin_elementwise_max(1, 'a');
 }
 
-void test_builtin_elementwise_min(float f1, float f2, double d1, double d2,
-                                  float4 vf1, float4 vf2, long long int i1,
-                                  long long int i2, si8 vi1, si8 vi2,
+void test_builtin_elementwise_min(long long int i2, si8 vi1, si8 vi2, long long int i1,
                                   unsigned u1, unsigned u2, u4 vu1, u4 vu2,
                                   _BitInt(31) bi1, _BitInt(31) bi2,
                                   unsigned _BitInt(55) bu1, unsigned _BitInt(55) bu2) {
-  // CHECK-LABEL: define void @test_builtin_elementwise_min(
-  // CHECK:      [[F1:%.+]] = load float, ptr %f1.addr, align 4
-  // CHECK-NEXT: [[F2:%.+]] = load float, ptr %f2.addr, align 4
-  // CHECK-NEXT:  call float @llvm.minnum.f32(float [[F1]], float [[F2]])
-  f1 = __builtin_elementwise_min(f1, f2);
-
-  // CHECK:      [[D1:%.+]] = load double, ptr %d1.addr, align 8
-  // CHECK-NEXT: [[D2:%.+]] = load double, ptr %d2.addr, align 8
-  // CHECK-NEXT: call double @llvm.minnum.f64(double [[D1]], double [[D2]])
-  d1 = __builtin_elementwise_min(d1, d2);
-
-  // CHECK:      [[D1:%.+]] = load double, ptr %d1.addr, align 8
-  // CHECK-NEXT: call double @llvm.minnum.f64(double [[D1]], double 2.000000e+00)
-  d1 = __builtin_elementwise_min(d1, 2.0);
-
-  // CHECK:      [[VF1:%.+]] = load <4 x float>, ptr %vf1.addr, align 16
-  // CHECK-NEXT: [[VF2:%.+]] = load <4 x float>, ptr %vf2.addr, align 16
-  // CHECK-NEXT: call <4 x float> @llvm.minnum.v4f32(<4 x float> [[VF1]], <4 x float> [[VF2]])
-  vf1 = __builtin_elementwise_min(vf1, vf2);
-
   // CHECK:      [[I1:%.+]] = load i64, ptr %i1.addr, align 8
   // CHECK-NEXT: [[I2:%.+]] = load i64, ptr %i2.addr, align 8
   // CHECK-NEXT: call i64 @llvm.smin.i64(i64 [[I1]], i64 [[I2]])
@@ -494,17 +439,6 @@ void test_builtin_elementwise_min(float f1, float f2, double d1, double d2,
   // CHECK-NEXT: call i55 @llvm.umin.i55(i55 [[LOADEDV2]], i55 [[LOADEDV3]])
   bu1 = __builtin_elementwise_min(bu1, bu2);
 
-  // CHECK:      [[CVF1:%.+]] = load <4 x float>, ptr %cvf1, align 16
-  // CHECK-NEXT: [[VF2:%.+]] = load <4 x float>, ptr %vf2.addr, align 16
-  // CHECK-NEXT: call <4 x float> @llvm.minnum.v4f32(<4 x float> [[CVF1]], <4 x float> [[VF2]])
-  const float4 cvf1 = vf1;
-  vf1 = __builtin_elementwise_min(cvf1, vf2);
-
-  // CHECK:      [[VF2:%.+]] = load <4 x float>, ptr %vf2.addr, align 16
-  // CHECK-NEXT: [[CVF1:%.+]] = load <4 x float>, ptr %cvf1, align 16
-  // CHECK-NEXT: call <4 x float> @llvm.minnum.v4f32(<4 x float> [[VF2]], <4 x float> [[CVF1]])
-  vf1 = __builtin_elementwise_min(vf2, cvf1);
-
   // CHECK:      [[IAS1:%.+]] = load i32, ptr addrspace(1) @int_as_one, align 4
   // CHECK-NEXT: [[B:%.+]] = load i32, ptr @b, align 4
   // CHECK-NEXT: call i32 @llvm.smin.i32(i32 [[IAS1]], i32 [[B]])
diff --git a/clang/test/CodeGen/strictfp-elementwise-builtins.cpp b/clang/test/CodeGen/strictfp-elementwise-builtins.cpp
index 6453d50f044aa..7de0a396e08f9 100644
--- a/clang/test/CodeGen/strictfp-elementwise-builtins.cpp
+++ b/clang/test/CodeGen/strictfp-elementwise-builtins.cpp
@@ -27,24 +27,24 @@ float4 strict_elementwise_abs(float4 a) {
   return __builtin_elementwise_abs(a);
 }
 
-// CHECK-LABEL: define dso_local noundef <4 x float> @_Z22strict_elementwise_maxDv4_fS_
-// CHECK-SAME: (<4 x float> noundef [[A:%.*]], <4 x float> noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-LABEL: define dso_local noundef <4 x float> @_Z25strict_elementwise_maxnumDv4_fS_
+// CHECK-SAME: (<4 x float> noundef [[A:%.*]], <4 x float> noundef [[B:%.*]]) local_unnamed_addr #[[ATTR2]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[ELT_MAX:%.*]] = tail call <4 x float> @llvm.experimental.constrained.maxnum.v4f32(<4 x float> [[A]], <4 x float> [[B]], metadata !"fpexcept.strict") #[[ATTR4]]
-// CHECK-NEXT:    ret <4 x float> [[ELT_MAX]]
+// CHECK-NEXT:    [[ELT_MAXNUM:%.*]] = tail call <4 x float> @llvm.maxnum.v4f32(<4 x float> [[A]], <4 x float> [[B]]) #[[ATTR4]]
+// CHECK-NEXT:    ret <4 x float> [[ELT_MAXNUM]]
 //
-float4 strict_elementwise_max(float4 a, float4 b) {
-  return __builtin_elementwise_max(a, b);
+float4 strict_elementwise_maxnum(float4 a, float4 b) {
+  return __builtin_elementwise_maxnum(a, b);
 }
 
-// CHECK-LABEL: define dso_local noundef <4 x float> @_Z22strict_elementwise_minDv4_fS_
-// CHECK-SAME: (<4 x float> noundef [[A:%.*]], <4 x float> noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-LABEL: define dso_local noundef <4 x float> @_Z25strict_elementwise_minnumDv4_fS_
+// CHECK-SAME: (<4 x float> noundef [[A:%.*]], <4 x float> noundef [[B:%.*]]) local_unnamed_addr #[[ATTR2]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[ELT_MIN:%.*]] = tail call <4 x float> @llvm.experimental.constrained.minnum.v4f32(<4 x float> [[A]], <4 x float> [[B]], metadata !"fpexcept.strict") #[[ATTR4]]
-// CHECK-NEXT:    ret <4 x float> [[ELT_MIN]]
+// CHECK-NEXT:    [[ELT_MINNUM:%.*]] = tail call <4 x float> @llvm.minnum.v4f32(<4 x float> [[A]], <4 x float> [[B]]) #[[ATTR4]]
+// CHECK-NEXT:    ret <4 x float> [[ELT_MINNUM]]
 //
-float4 strict_elementwise_min(float4 a, float4 b) {
-  return __builtin_elementwise_min(a, b);
+float4 strict_elementwise_minnum(float4 a, float4 b) {
+  return __builtin_elementwise_minnum(a, b);
 }
 
 // CHECK-LABEL: define dso_local noundef <4 x float> @_Z26strict_elementwise_maximumDv4_fS_

@wzssyqa
wzssyqa requested review from arsenm and nikic February 11, 2026 05:22
@github-actions

github-actions Bot commented Feb 11, 2026

Copy link
Copy Markdown

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

@github-actions

github-actions Bot commented Feb 11, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 114327 tests passed
  • 4581 tests skipped

✅ The build succeeded and all tests passed.

@github-actions

github-actions Bot commented Feb 11, 2026

Copy link
Copy Markdown

🪟 Windows x64 Test Results

  • 54406 tests passed
  • 2315 tests skipped

✅ The build succeeded and all tests passed.

@nikic
nikic requested a review from fhahn February 11, 2026 08:50
@nikic

nikic commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

@AaronBallman Checking on what the policy here is, is this something we can do?

Comment thread clang/lib/CodeGen/CGBuiltin.cpp Outdated
@AaronBallman

Copy link
Copy Markdown
Contributor

@AaronBallman Checking on what the policy here is, is this something we can do?

The PR summary lacks any justification for the change beyond the existence of other builtins, but I don't believe this is a change we'd want to make unless I'm missing something. This will break working code, won't it?

@AaronBallman
AaronBallman self-requested a review February 11, 2026 13:30
@llvmbot llvmbot added the HLSL HLSL Language Support label Feb 11, 2026

@AaronBallman AaronBallman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Marking as requested changes because it's not clear why these changes are being proposed.

@wzssyqa

wzssyqa commented Feb 12, 2026

Copy link
Copy Markdown
Contributor Author

Marking as requested changes because it's not clear why these changes are being proposed.

This topic was talked in: #113133: Should we add nsz top __builtin_elementwise_min.

@nikic suggested that we should remove float support from __builtin_elementwise_min.

My suggestion is that we can set __builtin_elementwise_min as the the most relaxed flavor: backend can do anything
that they want.

@AaronBallman

Copy link
Copy Markdown
Contributor

Marking as requested changes because it's not clear why these changes are being proposed.

This topic was talked in: #113133: Should we add nsz top __builtin_elementwise_min.

@nikic suggested that we should remove float support from __builtin_elementwise_min.

My suggestion is that we can set __builtin_elementwise_min as the the most relaxed flavor: backend can do anything that they want.

This builtin was released over five years ago in Clang, so changing the semantics with no notice is pretty user hostile. It doesn't sound like the float support we have is wrong, just that someone wanted to change the semantics around negative zero handling for better optimization opportunities. Is that correct?

@nikic

nikic commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

The context here is that there are at least 4 notions of what "max" means for floating-point numbers that are in common use, and the used semantics should not hidden behind such an innocuous name.

It would probably make more sense to warn if this intrinsic is used with floats rather than not supporting them entirely.

@AaronBallman

Copy link
Copy Markdown
Contributor

The context here is that there are at least 4 notions of what "max" means for floating-point numbers that are in common use, and the used semantics should not hidden behind such an innocuous name.

Because this is a builtin, I agree.

It would probably make more sense to warn if this intrinsic is used with floats rather than not supporting them entirely.

That's how I lean but I don't have a good idea for what the warning conditions would be. I don't think "warn on any float" is viable -- outside of a handful of edge cases, the function does what it says on the tin, right? And the edge cases aren't something we can catch statically in most cases, I believe.

@arsenm

arsenm commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

That's how I lean but I don't have a good idea for what the warning conditions would be. I don't think "warn on any float" is viable -- outside of a handful of edge cases, the function does what it says on the tin, right? And the edge cases aren't something we can catch statically in most cases, I believe.

Deprecation warning

@AaronBallman

Copy link
Copy Markdown
Contributor

That's how I lean but I don't have a good idea for what the warning conditions would be. I don't think "warn on any float" is viable -- outside of a handful of edge cases, the function does what it says on the tin, right? And the edge cases aren't something we can catch statically in most cases, I believe.

Deprecation warning

That seems like a reasonable approach to me; GCC doesn't implement this builtin nor does MSVC, so I don't think we need it for compatibility with other implementations.

@wzssyqa
wzssyqa marked this pull request as draft February 25, 2026 07:31
@wzssyqa wzssyqa changed the title Clang: Drop float support from __builtin_elementwise_max Clang: Deprecate float support from __builtin_elementwise_max Feb 25, 2026
@wzssyqa
wzssyqa marked this pull request as ready for review February 25, 2026 09:59
@wzssyqa
wzssyqa requested a review from AaronBallman February 25, 2026 09:59
@wzssyqa
wzssyqa requested a review from nikic February 25, 2026 09:59
@llvmbot llvmbot added clang:frontend Language frontend issues, e.g. anything involving "Sema" libclc libclc OpenCL library labels Feb 25, 2026
Comment thread clang/include/clang/Basic/DiagnosticFrontendKinds.td Outdated
Comment thread clang/lib/CodeGen/CGBuiltin.cpp Outdated
@wzssyqa
wzssyqa requested a review from AaronBallman February 26, 2026 08:25
Comment thread clang/lib/Sema/SemaChecking.cpp Outdated
@wzssyqa
wzssyqa requested a review from AaronBallman February 27, 2026 00:39

@AaronBallman AaronBallman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

@wzssyqa
wzssyqa merged commit 5f22dec into llvm:main Feb 28, 2026
11 checks passed
@llvm-ci

llvm-ci commented Feb 28, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder openmp-offload-sles-build-only running on rocm-worker-hw-04-sles while building clang,libclc at step 2 "checkout".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/140/builds/39549

Here is the relevant piece of the build log for the reference
Step 2 (checkout) failure: update (failure)

sahas3 pushed a commit to sahas3/llvm-project that referenced this pull request Mar 4, 2026
…80885)

Now we have
  __builtin_elementwise_maxnum
  __builtin_elementwise_maximum
  __builtin_elementwise_maximumnum
sujianIBM pushed a commit to sujianIBM/llvm-project that referenced this pull request Mar 5, 2026
…80885)

Now we have
  __builtin_elementwise_maxnum
  __builtin_elementwise_maximum
  __builtin_elementwise_maximumnum
@andykaylor

Copy link
Copy Markdown
Contributor

@AaronBallman @jcranmer-intel @phoebewang

I didn't see this PR when it went through, and I only just noticed that __builtin_elementwise_max and _builtin_elementwise_min are listed as deprecated. This is somewhat unfortunate, as now (if I'm reading things correctly) all of the available elementwise fmax builtins require strict signed zero behavior. This means that they can't be implemented using the x86 maxps instruction, which returns the second operand if both operands are zero of either sign.

This might not have been noticed because, unlike the __builtin_fmax handling, Clang's __builtin_elementwise_max doesn't set the nsz flag so the backend never gets the message that it can ignore the sign of zero on operands, but I'm pretty sure that Clang's documentation for __builtin_elementwise_max is saying that the sign of zero may be ignored.

Note the difference in generated code when signed zeros are ignored (I had to use -ffast-math because Clang's __builtin_elementwise_max emitter also doesn't check FP semantics):

https://godbolt.org/z/rdvh9797E

@arsenm

arsenm commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

This might not have been noticed because, unlike the __builtin_fmax handling, Clang's __builtin_elementwise_max doesn't set the nsz flag so the backend never gets the message that it can ignore the sign of zero on operands, but I'm pretty sure that Clang's documentation for __builtin_elementwise_max is saying that the sign of zero may be ignored.

If you really really want that, that should be a distinct elementwise fmin/fmax.

Alternatively we should round out the math pragmas to have nsz

@phoebewang

Copy link
Copy Markdown
Contributor

I seldom pay attention to generic intrinsic changes, but I'm not surprise to the change. After #172012, the community consensus is to decouple nsz flag from generic intrinsics. The assumption is Clang front end should always emit nsz to match the C semantics, so that x86 is exempted suffering performance lose. But I'm not front end expert and not pay attention to the progress.

@andykaylor

Copy link
Copy Markdown
Contributor

This might not have been noticed because, unlike the __builtin_fmax handling, Clang's __builtin_elementwise_max doesn't set the nsz flag so the backend never gets the message that it can ignore the sign of zero on operands, but I'm pretty sure that Clang's documentation for __builtin_elementwise_max is saying that the sign of zero may be ignored.

If you really really want that, that should be a distinct elementwise fmin/fmax.

Alternatively we should round out the math pragmas to have nsz

The Clang documentation for __builtin_elementwise_max already indicates that it doesn't respect signed zero (that is, it maps to the IEEE-754 2008 maxNum). We could add __builtin_elementwise_fmax and leave __builtin_elementwise_max deprecated. I suppose that adds a bit of clarity, but my understanding is that it was depcrecated because we have too many variations of this already.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category HLSL HLSL Language Support libclc libclc OpenCL library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants