[clang] [clang] constexpr built-in fma function. (PR #113020)

via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 22 09:12:12 PDT 2024


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/113020

>From 93c625ad60fc834e72df667addc6eec83247fc8c Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 19 Oct 2024 03:45:17 +0000
Subject: [PATCH 1/3] constexpr fma

---
 clang/docs/ReleaseNotes.rst                   |  1 +
 clang/include/clang/Basic/Builtins.td         |  1 +
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      | 37 +++++++++++++++++++
 clang/lib/AST/ExprConstant.cpp                | 16 ++++++++
 clang/test/AST/ByteCode/builtin-functions.cpp |  9 +++++
 clang/test/Sema/constant-builtins-2.c         |  7 ++++
 6 files changed, 71 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b7a6ace8bb895d..605d55a9e51f37 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -273,6 +273,7 @@ Non-comprehensive list of changes in this release
 - Plugins can now define custom attributes that apply to statements
   as well as declarations.
 - ``__builtin_abs`` function can now be used in constant expressions.
+- ``__builtin_fma`` function can now be used in constant expressions.
 
 New Compiler Flags
 ------------------
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 90475a361bb8f8..55f470a9f715b9 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -3723,6 +3723,7 @@ def Fma : FPMathTemplate, LibBuiltin<"math.h"> {
   let Attributes = [NoThrow, ConstIgnoringErrnoAndExceptions];
   let Prototype = "T(T, T, T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Fmax : FPMathTemplate, LibBuiltin<"math.h"> {
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index d4a8e6c2035ee5..145f4627dd73da 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -142,6 +142,19 @@ static bool retPrimValue(InterpState &S, CodePtr OpPC, APValue &Result,
 #undef RET_CASE
 }
 
+/// Get rounding mode to use in evaluation of the specified expression.
+///
+/// If rounding mode is unknown at compile time, still try to evaluate the
+/// expression. If the result is exact, it does not depend on rounding mode.
+/// So return "tonearest" mode instead of "dynamic".
+static llvm::RoundingMode getActiveRoundingMode(InterpState &S, const Expr *E) {
+  llvm::RoundingMode RM =
+      E->getFPFeaturesInEffect(S.getLangOpts()).getRoundingMode();
+  if (RM == llvm::RoundingMode::Dynamic)
+    RM = llvm::RoundingMode::NearestTiesToEven;
+  return RM;
+}
+
 static bool interp__builtin_is_constant_evaluated(InterpState &S, CodePtr OpPC,
                                                   const InterpFrame *Frame,
                                                   const CallExpr *Call) {
@@ -549,6 +562,22 @@ static bool interp__builtin_fpclassify(InterpState &S, CodePtr OpPC,
   return true;
 }
 
+static bool interp__builtin_fma(InterpState &S, CodePtr OpPC,
+                                const InterpFrame *Frame, const Function *Func,
+                                const CallExpr *Call) {
+  const Floating &X = getParam<Floating>(Frame, 0);
+  const Floating &Y = getParam<Floating>(Frame, 1);
+  const Floating &Z = getParam<Floating>(Frame, 2);
+  Floating Result;
+
+  llvm::RoundingMode RM = getActiveRoundingMode(S, Call);
+  Floating::mul(X, Y, RM, &Result);
+  Floating::add(Result, Z, RM, &Result);
+
+  S.Stk.push<Floating>(Result);
+  return true;
+}
+
 // The C standard says "fabs raises no floating-point exceptions,
 // even if x is a signaling NaN. The returned value is independent of
 // the current rounding direction mode."  Therefore constant folding can
@@ -1814,6 +1843,14 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
       return false;
     break;
 
+  case Builtin::BI__builtin_fma:
+  case Builtin::BI__builtin_fmaf:
+  case Builtin::BI__builtin_fmal:
+  case Builtin::BI__builtin_fmaf128:
+    if (!interp__builtin_fma(S, OpPC, Frame, F, Call))
+      return false;
+    break;
+
   case Builtin::BI__builtin_fabs:
   case Builtin::BI__builtin_fabsf:
   case Builtin::BI__builtin_fabsl:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 8e36cad2d2c6e7..685ce8a63f6c9e 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -15314,6 +15314,22 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) {
       Result.changeSign();
     return true;
 
+  case Builtin::BI__builtin_fma:
+  case Builtin::BI__builtin_fmaf:
+  case Builtin::BI__builtin_fmal:
+  case Builtin::BI__builtin_fmaf128: {
+    APFloat Y(0.), Z(0.);
+    if (!EvaluateFloat(E->getArg(0), Result, Info) ||
+        !EvaluateFloat(E->getArg(1), Y, Info) ||
+        !EvaluateFloat(E->getArg(2), Z, Info))
+      return false;
+
+    llvm::RoundingMode RM = getActiveRoundingMode(Info, E);
+    Result.multiply(Y, RM);
+    Result.add(Z, RM);
+    return true;
+  }
+
   case Builtin::BI__arithmetic_fence:
     return EvaluateFloat(E->getArg(0), Result, Info);
 
diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp
index b5d334178f8213..0dba62e252b3d7 100644
--- a/clang/test/AST/ByteCode/builtin-functions.cpp
+++ b/clang/test/AST/ByteCode/builtin-functions.cpp
@@ -265,6 +265,15 @@ namespace fpclassify {
   char classify_subnorm [__builtin_fpclassify(-1, -1, -1, +1, -1, 1.0e-38f)];
 }
 
+namespace fma {
+  static_assert(__builtin_fma(1.0, 1.0, 1.0) == 2.0);
+  static_assert(__builtin_fma(1.0, -1.0, 1.0) == 0.0);
+  static_assert(__builtin_fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
+  static_assert(__builtin_fmaf(1.0f, -1.0f, 1.0f) == 0.0f);
+  static_assert(__builtin_fmal(1.0L, 1.0L, 1.0L) == 2.0L);
+  static_assert(__builtin_fmal(1.0L, -1.0L, 1.0L) == 0.0L);
+} // namespace fma
+
 namespace abs {
   static_assert(__builtin_abs(14) == 14, "");
   static_assert(__builtin_labs(14L) == 14L, "");
diff --git a/clang/test/Sema/constant-builtins-2.c b/clang/test/Sema/constant-builtins-2.c
index e465a3c5f0ad86..2a2dbc2caee1d1 100644
--- a/clang/test/Sema/constant-builtins-2.c
+++ b/clang/test/Sema/constant-builtins-2.c
@@ -54,6 +54,13 @@ long double  g18 = __builtin_copysignl(1.0L, -1.0L);
 __float128   g18_2 = __builtin_copysignf128(1.0q, -1.0q);
 #endif
 
+double g19 = __builtin_fma(1.0, 1.0, 1.0);
+float g20 = __builtin_fmaf(1.0f, 1.0f, 1.0f);
+long double g21 = __builtin_fmal(1.0L, 1.0L, 1.0L);
+#if defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__)
+__float128 g21_2 = __builtin_fma(1.0q, 1.0q, 1.0q);
+#endif
+
 char classify_nan     [__builtin_fpclassify(+1, -1, -1, -1, -1, __builtin_nan(""))];
 char classify_snan    [__builtin_fpclassify(+1, -1, -1, -1, -1, __builtin_nans(""))];
 char classify_inf     [__builtin_fpclassify(-1, +1, -1, -1, -1, __builtin_inf())];

>From 89a87ca6bb11bf152259365731afabaeed5d0f24 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 19 Oct 2024 08:34:10 +0000
Subject: [PATCH 2/3] address review comments

---
 clang/include/clang/Basic/Builtins.td    | 2 +-
 clang/lib/AST/ByteCode/Floating.h        | 7 +++++++
 clang/lib/AST/ByteCode/InterpBuiltin.cpp | 4 ++--
 clang/lib/AST/ExprConstant.cpp           | 4 +---
 clang/test/Sema/constant-builtins-2.c    | 2 +-
 5 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 55f470a9f715b9..76d54c10f8edb9 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -199,7 +199,7 @@ def FloorF16F128 : Builtin, F16F128MathTemplate {
 
 def FmaF16F128 : Builtin, F16F128MathTemplate {
   let Spellings = ["__builtin_fma"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, ConstIgnoringErrnoAndExceptions, Constexpr];
   let Prototype = "T(T, T, T)";
 }
 
diff --git a/clang/lib/AST/ByteCode/Floating.h b/clang/lib/AST/ByteCode/Floating.h
index 114487821880fb..134490249c05b6 100644
--- a/clang/lib/AST/ByteCode/Floating.h
+++ b/clang/lib/AST/ByteCode/Floating.h
@@ -203,6 +203,13 @@ class Floating final {
     return R->F.divide(B.F, RM);
   }
 
+  static APFloat::opStatus fma(const Floating &A, const Floating &B,
+                               const Floating &C, llvm::RoundingMode RM,
+                               Floating *R) {
+    *R = Floating(A.F);
+    return R->F.fusedMultiplyAdd(B.F, C.F, RM);
+  }
+
   static bool neg(const Floating &A, Floating *R) {
     *R = -A;
     return false;
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 145f4627dd73da..d5fd67b3f0c8a8 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -571,8 +571,8 @@ static bool interp__builtin_fma(InterpState &S, CodePtr OpPC,
   Floating Result;
 
   llvm::RoundingMode RM = getActiveRoundingMode(S, Call);
-  Floating::mul(X, Y, RM, &Result);
-  Floating::add(Result, Z, RM, &Result);
+  if (!Floating::fma(X, Y, Z, RM, &Result))
+    return false;
 
   S.Stk.push<Floating>(Result);
   return true;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 685ce8a63f6c9e..01022f6ac40c63 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -15325,9 +15325,7 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) {
       return false;
 
     llvm::RoundingMode RM = getActiveRoundingMode(Info, E);
-    Result.multiply(Y, RM);
-    Result.add(Z, RM);
-    return true;
+    return Result.fusedMultiplyAdd(Y, Z, RM) == APFloat::opOK;
   }
 
   case Builtin::BI__arithmetic_fence:
diff --git a/clang/test/Sema/constant-builtins-2.c b/clang/test/Sema/constant-builtins-2.c
index 2a2dbc2caee1d1..2a47d189c23c7b 100644
--- a/clang/test/Sema/constant-builtins-2.c
+++ b/clang/test/Sema/constant-builtins-2.c
@@ -58,7 +58,7 @@ double g19 = __builtin_fma(1.0, 1.0, 1.0);
 float g20 = __builtin_fmaf(1.0f, 1.0f, 1.0f);
 long double g21 = __builtin_fmal(1.0L, 1.0L, 1.0L);
 #if defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__)
-__float128 g21_2 = __builtin_fma(1.0q, 1.0q, 1.0q);
+__float128 g21_2 = __builtin_fmaf128(1.0q, 1.0q, 1.0q);
 #endif
 
 char classify_nan     [__builtin_fpclassify(+1, -1, -1, -1, -1, __builtin_nan(""))];

>From 37093d7942ee1fa4594395cda3c4ab8618e84907 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 19 Oct 2024 08:49:49 +0000
Subject: [PATCH 3/3] fix

---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index d5fd67b3f0c8a8..f05c77932350c0 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -571,7 +571,7 @@ static bool interp__builtin_fma(InterpState &S, CodePtr OpPC,
   Floating Result;
 
   llvm::RoundingMode RM = getActiveRoundingMode(S, Call);
-  if (!Floating::fma(X, Y, Z, RM, &Result))
+  if (Floating::fma(X, Y, Z, RM, &Result) != APFloat::opOK)
     return false;
 
   S.Stk.push<Floating>(Result);



More information about the cfe-commits mailing list