[libc-commits] [libc] 96d8572 - [libc] Move implementations of expf and exp2f from the AOR to src/math.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Fri May 15 12:52:19 PDT 2020


Author: Siva Chandra Reddy
Date: 2020-05-15T12:43:03-07:00
New Revision: 96d85726b0fc3c5154265cea156483a3f63ebe21

URL: https://github.com/llvm/llvm-project/commit/96d85726b0fc3c5154265cea156483a3f63ebe21
DIFF: https://github.com/llvm/llvm-project/commit/96d85726b0fc3c5154265cea156483a3f63ebe21.diff

LOG: [libc] Move implementations of expf and exp2f from the AOR to src/math.

Reviewers: phosek

Differential Revision: https://reviews.llvm.org/D79149

Added: 
    libc/src/math/exp2f.cpp
    libc/src/math/exp2f.h
    libc/src/math/exp_utils.cpp
    libc/src/math/exp_utils.h
    libc/src/math/expf.cpp
    libc/src/math/expf.h
    libc/src/math/math_utils.cpp
    libc/test/src/math/exp2f_test.cpp
    libc/test/src/math/expf_test.cpp

Modified: 
    libc/config/linux/api.td
    libc/lib/CMakeLists.txt
    libc/spec/stdc.td
    libc/src/math/CMakeLists.txt
    libc/src/math/cosf.cpp
    libc/src/math/math_utils.h
    libc/src/math/sincosf.cpp
    libc/src/math/sinf.cpp
    libc/test/src/math/CMakeLists.txt
    libc/utils/MPFRWrapper/MPFRUtils.cpp
    libc/utils/MPFRWrapper/MPFRUtils.h

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 3eb7ead48365..cb070e1466ad 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -152,6 +152,8 @@ def MathAPI : PublicAPI<"math.h"> {
    "cosf",
    "fabs",
    "fabsf",
+   "expf",
+   "exp2f",
    "round",
    "sincosf",
    "sinf",

diff  --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt
index 2e103ee3ecf4..55f213a449b5 100644
--- a/libc/lib/CMakeLists.txt
+++ b/libc/lib/CMakeLists.txt
@@ -50,6 +50,8 @@ add_entrypoint_library(
     libc.src.math.cosf
     libc.src.math.fabs
     libc.src.math.fabsf
+    libc.src.math.expf
+    libc.src.math.exp2f
     libc.src.math.round
     libc.src.math.sincosf
     libc.src.math.sinf

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 102f878d2fe0..139a1af84c58 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -188,6 +188,9 @@ def StdC : StandardSpec<"stdc"> {
           FunctionSpec<"cosf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
           FunctionSpec<"sinf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
 
+          FunctionSpec<"expf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
+          FunctionSpec<"exp2f", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
+
           FunctionSpec<"round", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
       ]
   >;

diff  --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index f74577ba85a1..41a20b31996f 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -1,5 +1,7 @@
-add_header_library(
+add_object_library(
   math_utils
+  SRCS
+    math_utils.cpp
   HDRS
     math_utils.h
   DEPENDS
@@ -88,3 +90,37 @@ add_entrypoint_object(
   DEPENDS
     libc.utils.FPUtil.fputil
 )
+
+add_object_library(
+  exp_utils
+  HDRS
+    exp_utils.h
+  SRCS
+    exp_utils.cpp
+  DEPENDS
+    .math_utils
+)
+
+add_entrypoint_object(
+  expf
+  SRCS
+    expf.cpp
+  HDRS
+    expf.h
+  DEPENDS
+    .exp_utils
+    .math_utils
+    libc.include.math
+)
+
+add_entrypoint_object(
+  exp2f
+  SRCS
+    exp2f.cpp
+  HDRS
+    exp2f.h
+  DEPENDS
+    .exp_utils
+    .math_utils
+    libc.include.math
+)

diff  --git a/libc/src/math/cosf.cpp b/libc/src/math/cosf.cpp
index db121b2cb396..f02e38bee133 100644
--- a/libc/src/math/cosf.cpp
+++ b/libc/src/math/cosf.cpp
@@ -58,7 +58,7 @@ float LLVM_LIBC_ENTRYPOINT(cosf)(float y) {
     return sinf_poly(x * s, x * x, p, n ^ 1);
   }
 
-  return invalidf(y);
+  return invalid(y);
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/math/exp2f.cpp b/libc/src/math/exp2f.cpp
new file mode 100644
index 000000000000..3d7423d0210b
--- /dev/null
+++ b/libc/src/math/exp2f.cpp
@@ -0,0 +1,63 @@
+//===-- Single-precision 2^x function -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "exp_utils.h"
+#include "math_utils.h"
+
+#include "include/math.h"
+#include "src/__support/common.h"
+
+#include <stdint.h>
+
+#define T exp2f_data.tab
+#define C exp2f_data.poly
+#define SHIFT exp2f_data.shift_scaled
+
+namespace __llvm_libc {
+
+float LLVM_LIBC_ENTRYPOINT(exp2f)(float x) {
+  uint32_t abstop;
+  uint64_t ki, t;
+  // double_t for better performance on targets with FLT_EVAL_METHOD==2.
+  double_t kd, xd, z, r, r2, y, s;
+
+  xd = static_cast<double_t>(x);
+  abstop = top12_bits(x) & 0x7ff;
+  if (unlikely(abstop >= top12_bits(128.0f))) {
+    // |x| >= 128 or x is nan.
+    if (as_uint32_bits(x) == as_uint32_bits(-INFINITY))
+      return 0.0f;
+    if (abstop >= top12_bits(INFINITY))
+      return x + x;
+    if (x > 0.0f)
+      return overflow<float>(0);
+    if (x <= -150.0f)
+      return underflow<float>(0);
+    if (x < -149.0f)
+      return may_underflow<float>(0);
+  }
+
+  // x = k/N + r with r in [-1/(2N), 1/(2N)] and int k.
+  kd = static_cast<double>(xd + SHIFT);
+  ki = as_uint64_bits(kd);
+  kd -= SHIFT; // k/N for int k.
+  r = xd - kd;
+
+  // exp2(x) = 2^(k/N) * 2^r ~= s * (C0*r^3 + C1*r^2 + C2*r + 1)
+  t = T[ki % N];
+  t += ki << (52 - EXP2F_TABLE_BITS);
+  s = as_double(t);
+  z = C[0] * r + C[1];
+  r2 = r * r;
+  y = C[2] * r + 1;
+  y = z * r2 + y;
+  y = y * s;
+  return static_cast<float>(y);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/math/exp2f.h b/libc/src/math/exp2f.h
new file mode 100644
index 000000000000..c1f72b1b80fa
--- /dev/null
+++ b/libc/src/math/exp2f.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for exp2f -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_EXP2F_H
+#define LLVM_LIBC_SRC_MATH_EXP2F_H
+
+namespace __llvm_libc {
+
+float exp2f(float x);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_MATH_EXP2F_H

diff  --git a/libc/src/math/exp_utils.cpp b/libc/src/math/exp_utils.cpp
new file mode 100644
index 000000000000..7635f44f1577
--- /dev/null
+++ b/libc/src/math/exp_utils.cpp
@@ -0,0 +1,129 @@
+//===-- Implemention of exp and friends' utils ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "exp_utils.h"
+
+#include "math_utils.h"
+
+namespace __llvm_libc {
+
+const Exp2fDataTable exp2f_data = {
+    // :tab[i] = uint(2^(i/N)) - (i << 52-BITS)
+    //    used for computing 2^(k/N) for an int |k| < 150 N as
+    //    double(tab[k%N] + (k << 52-BITS))
+    {
+// tab
+#if N == 8
+        0x3ff0000000000000,
+        0x3fef72b83c7d517b,
+        0x3fef06fe0a31b715,
+        0x3feebfdad5362a27,
+        0x3feea09e667f3bcd,
+        0x3feeace5422aa0db,
+        0x3feee89f995ad3ad,
+        0x3fef5818dcfba487,
+#elif N == 16
+        0x3ff0000000000000,
+        0x3fefb5586cf9890f,
+        0x3fef72b83c7d517b,
+        0x3fef387a6e756238,
+        0x3fef06fe0a31b715,
+        0x3feedea64c123422,
+        0x3feebfdad5362a27,
+        0x3feeab07dd485429,
+        0x3feea09e667f3bcd,
+        0x3feea11473eb0187,
+        0x3feeace5422aa0db,
+        0x3feec49182a3f090,
+        0x3feee89f995ad3ad,
+        0x3fef199bdd85529c,
+        0x3fef5818dcfba487,
+        0x3fefa4afa2a490da,
+#elif N == 32
+        0x3ff0000000000000, 0x3fefd9b0d3158574, 0x3fefb5586cf9890f,
+        0x3fef9301d0125b51, 0x3fef72b83c7d517b, 0x3fef54873168b9aa,
+        0x3fef387a6e756238, 0x3fef1e9df51fdee1, 0x3fef06fe0a31b715,
+        0x3feef1a7373aa9cb, 0x3feedea64c123422, 0x3feece086061892d,
+        0x3feebfdad5362a27, 0x3feeb42b569d4f82, 0x3feeab07dd485429,
+        0x3feea47eb03a5585, 0x3feea09e667f3bcd, 0x3fee9f75e8ec5f74,
+        0x3feea11473eb0187, 0x3feea589994cce13, 0x3feeace5422aa0db,
+        0x3feeb737b0cdc5e5, 0x3feec49182a3f090, 0x3feed503b23e255d,
+        0x3feee89f995ad3ad, 0x3feeff76f2fb5e47, 0x3fef199bdd85529c,
+        0x3fef3720dcef9069, 0x3fef5818dcfba487, 0x3fef7c97337b9b5f,
+        0x3fefa4afa2a490da, 0x3fefd0765b6e4540,
+#elif N == 64
+        0x3ff0000000000000, 0x3fefec9a3e778061, 0x3fefd9b0d3158574,
+        0x3fefc74518759bc8, 0x3fefb5586cf9890f, 0x3fefa3ec32d3d1a2,
+        0x3fef9301d0125b51, 0x3fef829aaea92de0, 0x3fef72b83c7d517b,
+        0x3fef635beb6fcb75, 0x3fef54873168b9aa, 0x3fef463b88628cd6,
+        0x3fef387a6e756238, 0x3fef2b4565e27cdd, 0x3fef1e9df51fdee1,
+        0x3fef1285a6e4030b, 0x3fef06fe0a31b715, 0x3feefc08b26416ff,
+        0x3feef1a7373aa9cb, 0x3feee7db34e59ff7, 0x3feedea64c123422,
+        0x3feed60a21f72e2a, 0x3feece086061892d, 0x3feec6a2b5c13cd0,
+        0x3feebfdad5362a27, 0x3feeb9b2769d2ca7, 0x3feeb42b569d4f82,
+        0x3feeaf4736b527da, 0x3feeab07dd485429, 0x3feea76f15ad2148,
+        0x3feea47eb03a5585, 0x3feea23882552225, 0x3feea09e667f3bcd,
+        0x3fee9fb23c651a2f, 0x3fee9f75e8ec5f74, 0x3fee9feb564267c9,
+        0x3feea11473eb0187, 0x3feea2f336cf4e62, 0x3feea589994cce13,
+        0x3feea8d99b4492ed, 0x3feeace5422aa0db, 0x3feeb1ae99157736,
+        0x3feeb737b0cdc5e5, 0x3feebd829fde4e50, 0x3feec49182a3f090,
+        0x3feecc667b5de565, 0x3feed503b23e255d, 0x3feede6b5579fdbf,
+        0x3feee89f995ad3ad, 0x3feef3a2b84f15fb, 0x3feeff76f2fb5e47,
+        0x3fef0c1e904bc1d2, 0x3fef199bdd85529c, 0x3fef27f12e57d14b,
+        0x3fef3720dcef9069, 0x3fef472d4a07897c, 0x3fef5818dcfba487,
+        0x3fef69e603db3285, 0x3fef7c97337b9b5f, 0x3fef902ee78b3ff6,
+        0x3fefa4afa2a490da, 0x3fefba1bee615a27, 0x3fefd0765b6e4540,
+        0x3fefe7c1819e90d8,
+#endif
+    },
+    as_double(0x4338000000000000) / N, // shift_scaled
+    {
+// poly
+#if N == 8
+        as_double(0x3fac6a00335106e2),
+        as_double(0x3fcec0c313449f55),
+        as_double(0x3fe62e431111f69f),
+#elif N == 16
+        as_double(0x3fac6ac6aa313963),
+        as_double(0x3fcebfff4532d9ba),
+        as_double(0x3fe62e43001bc49f),
+#elif N == 32
+        as_double(0x3fac6af84b912394),
+        as_double(0x3fcebfce50fac4f3),
+        as_double(0x3fe62e42ff0c52d6),
+#elif N == 64
+        as_double(0x3fac6b04b4221b2a),
+        as_double(0x3fcebfc213e184d7),
+        as_double(0x3fe62e42fefb5b7f),
+#endif
+    },
+    as_double(0x4338000000000000),     // shift
+    as_double(0x3ff71547652b82fe) * N, // invln2_scaled
+    {
+// poly_scaled
+#if N == 8
+        as_double(0x3fac6a00335106e2) / N / N / N,
+        as_double(0x3fcec0c313449f55) / N / N,
+        as_double(0x3fe62e431111f69f) / N,
+#elif N == 16
+        as_double(0x3fac6ac6aa313963) / N / N / N,
+        as_double(0x3fcebfff4532d9ba) / N / N,
+        as_double(0x3fe62e43001bc49f) / N,
+#elif N == 32
+        as_double(0x3fac6af84b912394) / N / N / N,
+        as_double(0x3fcebfce50fac4f3) / N / N,
+        as_double(0x3fe62e42ff0c52d6) / N,
+#elif N == 64
+        as_double(0x3fac6b04b4221b2a) / N / N / N,
+        as_double(0x3fcebfc213e184d7) / N / N,
+        as_double(0x3fe62e42fefb5b7f) / N,
+#endif
+    },
+};
+
+} // namespace __llvm_libc

diff  --git a/libc/src/math/exp_utils.h b/libc/src/math/exp_utils.h
new file mode 100644
index 000000000000..edbd60a2d568
--- /dev/null
+++ b/libc/src/math/exp_utils.h
@@ -0,0 +1,33 @@
+//===-- Collection of utils for exp and friends -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_EXP_UTILS_H
+#define LLVM_LIBC_SRC_MATH_EXP_UTILS_H
+
+#include <stdint.h>
+
+#define EXP2F_TABLE_BITS 5
+#define EXP2F_POLY_ORDER 3
+#define N (1 << EXP2F_TABLE_BITS)
+
+namespace __llvm_libc {
+
+struct Exp2fDataTable {
+  uint64_t tab[1 << EXP2F_TABLE_BITS];
+  double shift_scaled;
+  double poly[EXP2F_POLY_ORDER];
+  double shift;
+  double invln2_scaled;
+  double poly_scaled[EXP2F_POLY_ORDER];
+};
+
+extern const Exp2fDataTable exp2f_data;
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_MATH_EXP_UTILS_H

diff  --git a/libc/src/math/expf.cpp b/libc/src/math/expf.cpp
new file mode 100644
index 000000000000..beda61456b3a
--- /dev/null
+++ b/libc/src/math/expf.cpp
@@ -0,0 +1,69 @@
+//===-- Single-precision e^x function -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "exp_utils.h"
+#include "math_utils.h"
+
+#include "include/math.h"
+#include "src/__support/common.h"
+
+#include <stdint.h>
+
+#define InvLn2N exp2f_data.invln2_scaled
+#define T exp2f_data.tab
+#define C exp2f_data.poly_scaled
+#define SHIFT exp2f_data.shift
+
+namespace __llvm_libc {
+
+float LLVM_LIBC_ENTRYPOINT(expf)(float x) {
+  uint32_t abstop;
+  uint64_t ki, t;
+  // double_t for better performance on targets with FLT_EVAL_METHOD == 2.
+  double_t kd, xd, z, r, r2, y, s;
+
+  xd = static_cast<double_t>(x);
+  abstop = top12_bits(x) & 0x7ff;
+  if (unlikely(abstop >= top12_bits(88.0f))) {
+    // |x| >= 88 or x is nan.
+    if (as_uint32_bits(x) == as_uint32_bits(-INFINITY))
+      return 0.0f;
+    if (abstop >= top12_bits(INFINITY))
+      return x + x;
+    if (x > as_float(0x42b17217)) // x > log(0x1p128) ~= 88.72
+      return overflow<float>(0);
+    if (x < as_float(0xc2cff1b4)) // x < log(0x1p-150) ~= -103.97
+      return underflow<float>(0);
+    if (x < as_float(0xc2ce8ecf)) // x < log(0x1p-149) ~= -103.28
+      return may_underflow<float>(0);
+  }
+
+  // x*N/Ln2 = k + r with r in [-1/2, 1/2] and int k.
+  z = InvLn2N * xd;
+
+  // Round and convert z to int, the result is in [-150*N, 128*N] and
+  // ideally nearest int is used, otherwise the magnitude of r can be
+  // bigger which gives larger approximation error.
+  kd = static_cast<double>(z + SHIFT);
+  ki = as_uint64_bits(kd);
+  kd -= SHIFT;
+  r = z - kd;
+
+  // exp(x) = 2^(k/N) * 2^(r/N) ~= s *(C0*r^3 + C1*r^2 + C2*r + 1)
+  t = T[ki % N];
+  t += ki << (52 - EXP2F_TABLE_BITS);
+  s = as_double(t);
+  z = C[0] * r + C[1];
+  r2 = r * r;
+  y = C[2] * r + 1;
+  y = z * r2 + y;
+  y = y * s;
+  return static_cast<float>(y);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/math/expf.h b/libc/src/math/expf.h
new file mode 100644
index 000000000000..c61ce98e9ddc
--- /dev/null
+++ b/libc/src/math/expf.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for expf --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_EXPF_H
+#define LLVM_LIBC_SRC_MATH_EXPF_H
+
+namespace __llvm_libc {
+
+float expf(float x);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_MATH_EXPF_H

diff  --git a/libc/src/math/math_utils.cpp b/libc/src/math/math_utils.cpp
new file mode 100644
index 000000000000..42a912bac769
--- /dev/null
+++ b/libc/src/math/math_utils.cpp
@@ -0,0 +1,27 @@
+//===-- Implementation of math utils --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "math_utils.h"
+
+namespace __llvm_libc {
+
+const float XFlowValues<float>::overflow_value =
+    as_float(0x70000000); // 0x1p97f
+const float XFlowValues<float>::underflow_value =
+    as_float(0x10000000); // 0x1p97f
+const float XFlowValues<float>::may_underflow_value =
+    as_float(0x1a200000); // 0x1.4p-75f
+
+const double XFlowValues<double>::overflow_value =
+    as_double(0x7000000000000000); // 0x1p769
+const double XFlowValues<double>::underflow_value =
+    as_double(0x1000000000000000); // 0x1p-767
+const double XFlowValues<double>::may_underflow_value =
+    as_double(0x1e58000000000000); // 0x1.8p-538
+
+} // namespace __llvm_libc

diff  --git a/libc/src/math/math_utils.h b/libc/src/math/math_utils.h
index 3553673486f9..50851bd36f79 100644
--- a/libc/src/math/math_utils.h
+++ b/libc/src/math/math_utils.h
@@ -11,24 +11,22 @@
 
 #include "include/errno.h"
 #include "include/math.h"
-
 #include "src/__support/common.h"
 #include "src/errno/llvmlibc_errno.h"
+#include "utils/CPP/TypeTraits.h"
 
 #include <stdint.h>
 
 namespace __llvm_libc {
 
-static inline float with_errnof(float x, int err) {
-  if (math_errhandling & MATH_ERRNO)
-    llvmlibc_errno = err;
-  return x;
-}
-
 static inline uint32_t as_uint32_bits(float x) {
   return *reinterpret_cast<uint32_t *>(&x);
 }
 
+static inline uint64_t as_uint64_bits(double x) {
+  return *reinterpret_cast<uint64_t *>(&x);
+}
+
 static inline float as_float(uint32_t x) {
   return *reinterpret_cast<float *>(&x);
 }
@@ -37,12 +35,74 @@ static inline double as_double(uint64_t x) {
   return *reinterpret_cast<double *>(&x);
 }
 
-static inline constexpr float invalidf(float x) {
-  float y = (x - x) / (x - x);
-  return isnan(x) ? y : with_errnof(y, EDOM);
+static inline uint32_t top12_bits(float x) { return as_uint32_bits(x) >> 20; }
+
+static inline uint32_t top12_bits(double x) { return as_uint64_bits(x) >> 52; }
+
+// Values to trigger underflow and overflow.
+template <typename T> struct XFlowValues;
+
+template <> struct XFlowValues<float> {
+  static const float overflow_value;
+  static const float underflow_value;
+  static const float may_underflow_value;
+};
+
+template <> struct XFlowValues<double> {
+  static const double overflow_value;
+  static const double underflow_value;
+  static const double may_underflow_value;
+};
+
+template <typename T> static inline T with_errno(T x, int err) {
+  if (math_errhandling & MATH_ERRNO)
+    llvmlibc_errno = err;
+  return x;
 }
 
-static inline void force_eval_float(float x) { volatile float y UNUSED = x; }
+template <typename T> static inline void force_eval(T x) {
+  volatile T y UNUSED = x;
+}
+
+template <typename T> static inline T opt_barrier(T x) {
+  volatile T y = x;
+  return y;
+}
+
+template <typename T> struct IsFloatOrDouble {
+  static constexpr bool Value =
+      cpp::IsSame<T, float>::Value || cpp::IsSame<T, double>::Value;
+};
+
+template <typename T>
+using EnableIfFloatOrDouble = cpp::EnableIfType<IsFloatOrDouble<T>::Value, int>;
+
+template <typename T, EnableIfFloatOrDouble<T> = 0>
+T xflow(uint32_t sign, T y) {
+  // Underflow happens when two extremely small values are multiplied.
+  // Likewise, overflow happens when two large values are mulitplied.
+  y = opt_barrier(sign ? -y : y) * y;
+  return with_errno(y, ERANGE);
+}
+
+template <typename T, EnableIfFloatOrDouble<T> = 0> T overflow(uint32_t sign) {
+  return xflow(sign, XFlowValues<T>::overflow_value);
+}
+
+template <typename T, EnableIfFloatOrDouble<T> = 0> T underflow(uint32_t sign) {
+  return xflow(sign, XFlowValues<T>::underflow_value);
+}
+
+template <typename T, EnableIfFloatOrDouble<T> = 0>
+T may_underflow(uint32_t sign) {
+  return xflow(sign, XFlowValues<T>::may_underflow_value);
+}
+
+template <typename T, EnableIfFloatOrDouble<T> = 0>
+static inline constexpr float invalid(T x) {
+  T y = (x - x) / (x - x);
+  return isnan(x) ? y : with_errno(y, EDOM);
+}
 
 } // namespace __llvm_libc
 

diff  --git a/libc/src/math/sincosf.cpp b/libc/src/math/sincosf.cpp
index 717feaa470d2..710d7b4ffa06 100644
--- a/libc/src/math/sincosf.cpp
+++ b/libc/src/math/sincosf.cpp
@@ -32,7 +32,7 @@ void LLVM_LIBC_ENTRYPOINT(sincosf)(float y, float *sinp, float *cosp) {
     if (unlikely(abstop12(y) < abstop12(as_float(0x39800000)))) {
       if (unlikely(abstop12(y) < abstop12(as_float(0x800000))))
         // Force underflow for tiny y.
-        force_eval_float(x2);
+        force_eval<float>(x2);
       *sinp = y;
       *cosp = 1.0f;
       return;
@@ -69,7 +69,7 @@ void LLVM_LIBC_ENTRYPOINT(sincosf)(float y, float *sinp, float *cosp) {
     // Needed to set errno for +-Inf, the add is a hack to work
     // around a gcc register allocation issue: just passing y
     // affects code generation in the fast path.
-    invalidf(y + y);
+    invalid(y + y);
   }
 }
 

diff  --git a/libc/src/math/sinf.cpp b/libc/src/math/sinf.cpp
index 634e40c50f6b..ea15c474ecd5 100644
--- a/libc/src/math/sinf.cpp
+++ b/libc/src/math/sinf.cpp
@@ -32,7 +32,7 @@ float LLVM_LIBC_ENTRYPOINT(sinf)(float y) {
     if (unlikely(abstop12(y) < abstop12(as_float(0x39800000)))) {
       if (unlikely(abstop12(y) < abstop12(as_float(0x800000))))
         // Force underflow for tiny y.
-        force_eval_float(s);
+        force_eval<float>(s);
       return y;
     }
 
@@ -62,7 +62,7 @@ float LLVM_LIBC_ENTRYPOINT(sinf)(float y) {
     return sinf_poly(x * s, x * x, p, n);
   }
 
-  return invalidf(y);
+  return invalid(y);
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 3ea485c34301..ed36faaee4d8 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -96,3 +96,31 @@ add_math_unittest(
     libc.src.math.fabsf
     libc.utils.FPUtil.fputil
 )
+
+add_math_unittest(
+  expf_test
+  NEED_MPFR
+  SUITE
+    libc_math_unittests
+  SRCS
+    expf_test.cpp
+  DEPENDS
+    libc.include.errno
+    libc.include.math
+    libc.src.math.expf
+    libc.utils.FPUtil.fputil
+)
+
+add_math_unittest(
+  exp2f_test
+  NEED_MPFR
+  SUITE
+    libc_math_unittests
+  SRCS
+    exp2f_test.cpp
+  DEPENDS
+    libc.include.errno
+    libc.include.math
+    libc.src.math.exp2f
+    libc.utils.FPUtil.fputil
+)

diff  --git a/libc/test/src/math/exp2f_test.cpp b/libc/test/src/math/exp2f_test.cpp
new file mode 100644
index 000000000000..dbb7046e28bd
--- /dev/null
+++ b/libc/test/src/math/exp2f_test.cpp
@@ -0,0 +1,154 @@
+//===-- Unittests for exp2f -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "include/errno.h"
+#include "include/math.h"
+#include "src/errno/llvmlibc_errno.h"
+#include "src/math/exp2f.h"
+#include "utils/FPUtil/BitPatterns.h"
+#include "utils/FPUtil/FloatOperations.h"
+#include "utils/FPUtil/FloatProperties.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include "utils/UnitTest/Test.h"
+
+#include <stdint.h>
+
+using __llvm_libc::fputil::isNegativeQuietNaN;
+using __llvm_libc::fputil::isQuietNaN;
+using __llvm_libc::fputil::valueAsBits;
+using __llvm_libc::fputil::valueFromBits;
+
+using BitPatterns = __llvm_libc::fputil::BitPatterns<float>;
+
+namespace mpfr = __llvm_libc::testing::mpfr;
+
+// 12 additional bits of precision over the base precision of a |float|
+// value.
+static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12,
+                                           0xFFF};
+
+TEST(exp2fTest, SpecialNumbers) {
+  llvmlibc_errno = 0;
+
+  EXPECT_TRUE(
+      isQuietNaN(__llvm_libc::exp2f(valueFromBits(BitPatterns::aQuietNaN))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_TRUE(isNegativeQuietNaN(
+      __llvm_libc::exp2f(valueFromBits(BitPatterns::aNegativeQuietNaN))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_TRUE(isQuietNaN(
+      __llvm_libc::exp2f(valueFromBits(BitPatterns::aSignallingNaN))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_TRUE(isNegativeQuietNaN(
+      __llvm_libc::exp2f(valueFromBits(BitPatterns::aNegativeSignallingNaN))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_EQ(BitPatterns::inf,
+            valueAsBits(__llvm_libc::exp2f(valueFromBits(BitPatterns::inf))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_EQ(BitPatterns::zero, valueAsBits(__llvm_libc::exp2f(
+                                   valueFromBits(BitPatterns::negInf))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_EQ(BitPatterns::one,
+            valueAsBits(__llvm_libc::exp2f(valueFromBits(BitPatterns::zero))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_EQ(BitPatterns::one, valueAsBits(__llvm_libc::exp2f(
+                                  valueFromBits(BitPatterns::negZero))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+}
+
+TEST(ExpfTest, Overflow) {
+  llvmlibc_errno = 0;
+  EXPECT_EQ(BitPatterns::inf,
+            valueAsBits(__llvm_libc::exp2f(valueFromBits(0x7f7fffffU))));
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(BitPatterns::inf,
+            valueAsBits(__llvm_libc::exp2f(valueFromBits(0x43000000U))));
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(BitPatterns::inf,
+            valueAsBits(__llvm_libc::exp2f(valueFromBits(0x43000001U))));
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+}
+
+// Test with inputs which are the borders of underflow/overflow but still
+// produce valid results without setting errno.
+TEST(ExpfTest, Borderline) {
+  float x;
+
+  llvmlibc_errno = 0;
+  x = valueFromBits(0x42fa0001U);
+  EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  x = valueFromBits(0x42ffffffU);
+  EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  x = valueFromBits(0xc2fa0001U);
+  EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  x = valueFromBits(0xc2fc0000U);
+  EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  x = valueFromBits(0xc2fc0001U);
+  EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  x = valueFromBits(0xc3150000U);
+  EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, 0);
+}
+
+TEST(ExpfTest, Underflow) {
+  llvmlibc_errno = 0;
+  EXPECT_EQ(BitPatterns::zero,
+            valueAsBits(__llvm_libc::exp2f(valueFromBits(0xff7fffffU))));
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+
+  llvmlibc_errno = 0;
+  float x = valueFromBits(0xc3158000U);
+  EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+
+  llvmlibc_errno = 0;
+  x = valueFromBits(0xc3165432U);
+  EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+}
+
+TEST(exp2fTest, InFloatRange) {
+  constexpr uint32_t count = 1000000;
+  constexpr uint32_t step = UINT32_MAX / count;
+  for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) {
+    float x = valueFromBits(v);
+    if (isnan(x) || isinf(x))
+      continue;
+    llvmlibc_errno = 0;
+    float result = __llvm_libc::exp2f(x);
+
+    // If the computation resulted in an error or did not produce valid result
+    // in the single-precision floating point range, then ignore comparing with
+    // MPFR result as MPFR can still produce valid results because of its
+    // wider precision.
+    if (isnan(result) || isinf(result) || llvmlibc_errno != 0)
+      continue;
+    ASSERT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
+  }
+}

diff  --git a/libc/test/src/math/expf_test.cpp b/libc/test/src/math/expf_test.cpp
new file mode 100644
index 000000000000..aa50bd71974b
--- /dev/null
+++ b/libc/test/src/math/expf_test.cpp
@@ -0,0 +1,146 @@
+//===-- Unittests for expf ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "include/errno.h"
+#include "include/math.h"
+#include "src/errno/llvmlibc_errno.h"
+#include "src/math/expf.h"
+#include "utils/FPUtil/BitPatterns.h"
+#include "utils/FPUtil/FloatOperations.h"
+#include "utils/FPUtil/FloatProperties.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include "utils/UnitTest/Test.h"
+
+#include <stdint.h>
+
+using __llvm_libc::fputil::isNegativeQuietNaN;
+using __llvm_libc::fputil::isQuietNaN;
+using __llvm_libc::fputil::valueAsBits;
+using __llvm_libc::fputil::valueFromBits;
+
+using BitPatterns = __llvm_libc::fputil::BitPatterns<float>;
+
+namespace mpfr = __llvm_libc::testing::mpfr;
+
+// 12 additional bits of precision over the base precision of a |float|
+// value.
+static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12,
+                                           0xFFF};
+
+TEST(ExpfTest, SpecialNumbers) {
+  llvmlibc_errno = 0;
+
+  EXPECT_TRUE(
+      isQuietNaN(__llvm_libc::expf(valueFromBits(BitPatterns::aQuietNaN))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_TRUE(isNegativeQuietNaN(
+      __llvm_libc::expf(valueFromBits(BitPatterns::aNegativeQuietNaN))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_TRUE(isQuietNaN(
+      __llvm_libc::expf(valueFromBits(BitPatterns::aSignallingNaN))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_TRUE(isNegativeQuietNaN(
+      __llvm_libc::expf(valueFromBits(BitPatterns::aNegativeSignallingNaN))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_EQ(BitPatterns::inf,
+            valueAsBits(__llvm_libc::expf(valueFromBits(BitPatterns::inf))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_EQ(BitPatterns::zero,
+            valueAsBits(__llvm_libc::expf(valueFromBits(BitPatterns::negInf))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_EQ(BitPatterns::one,
+            valueAsBits(__llvm_libc::expf(valueFromBits(BitPatterns::zero))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  EXPECT_EQ(BitPatterns::one, valueAsBits(__llvm_libc::expf(
+                                  valueFromBits(BitPatterns::negZero))));
+  EXPECT_EQ(llvmlibc_errno, 0);
+}
+
+TEST(ExpfTest, Overflow) {
+  llvmlibc_errno = 0;
+  EXPECT_EQ(BitPatterns::inf,
+            valueAsBits(__llvm_libc::expf(valueFromBits(0x7f7fffffU))));
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(BitPatterns::inf,
+            valueAsBits(__llvm_libc::expf(valueFromBits(0x42cffff8U))));
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(BitPatterns::inf,
+            valueAsBits(__llvm_libc::expf(valueFromBits(0x42d00008U))));
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+}
+
+TEST(ExpfTest, Underflow) {
+  llvmlibc_errno = 0;
+  EXPECT_EQ(BitPatterns::zero,
+            valueAsBits(__llvm_libc::expf(valueFromBits(0xff7fffffU))));
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(BitPatterns::zero,
+            valueAsBits(__llvm_libc::expf(valueFromBits(0xc2cffff8U))));
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(BitPatterns::zero,
+            valueAsBits(__llvm_libc::expf(valueFromBits(0xc2d00008U))));
+  EXPECT_EQ(llvmlibc_errno, ERANGE);
+}
+
+// Test with inputs which are the borders of underflow/overflow but still
+// produce valid results without setting errno.
+TEST(ExpfTest, Borderline) {
+  float x;
+
+  llvmlibc_errno = 0;
+  x = valueFromBits(0x42affff8U);
+  ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  x = valueFromBits(0x42b00008U);
+  ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  x = valueFromBits(0xc2affff8U);
+  ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, 0);
+
+  x = valueFromBits(0xc2b00008U);
+  ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance);
+  EXPECT_EQ(llvmlibc_errno, 0);
+}
+
+TEST(ExpfTest, InFloatRange) {
+  constexpr uint32_t count = 1000000;
+  constexpr uint32_t step = UINT32_MAX / count;
+  for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) {
+    float x = valueFromBits(v);
+    if (isnan(x) || isinf(x))
+      continue;
+    llvmlibc_errno = 0;
+    float result = __llvm_libc::expf(x);
+
+    // If the computation resulted in an error or did not produce valid result
+    // in the single-precision floating point range, then ignore comparing with
+    // MPFR result as MPFR can still produce valid results because of its
+    // wider precision.
+    if (isnan(result) || isinf(result) || llvmlibc_errno != 0)
+      continue;
+    ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance);
+  }
+}

diff  --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index fe84c381114e..74c2f760f034 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -95,6 +95,12 @@ class MPFRNumber {
     case OP_Sin:
       mpfr_sin(value, mpfrInput.value, MPFR_RNDN);
       break;
+    case OP_Exp:
+      mpfr_exp(value, mpfrInput.value, MPFR_RNDN);
+      break;
+    case OP_Exp2:
+      mpfr_exp2(value, mpfrInput.value, MPFR_RNDN);
+      break;
     }
   }
 

diff  --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h
index f94a146a35f2..f6660f2fa78e 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.h
+++ b/libc/utils/MPFRWrapper/MPFRUtils.h
@@ -39,11 +39,7 @@ struct Tolerance {
   uint32_t bits;
 };
 
-enum Operation {
-  OP_Abs,
-  OP_Cos,
-  OP_Sin,
-};
+enum Operation { OP_Abs, OP_Cos, OP_Sin, OP_Exp, OP_Exp2 };
 
 namespace internal {
 


        


More information about the libc-commits mailing list