[libc-commits] [libc] 7bd3702 - [libc] Extend the current fenv functions to aarch64.

Siva Chandra via libc-commits libc-commits at lists.llvm.org
Tue Jan 19 12:48:26 PST 2021


Author: Siva Chandra
Date: 2021-01-19T12:47:54-08:00
New Revision: 7bd3702b64043fb623bf82c1d1a8a2d168142219

URL: https://github.com/llvm/llvm-project/commit/7bd3702b64043fb623bf82c1d1a8a2d168142219
DIFF: https://github.com/llvm/llvm-project/commit/7bd3702b64043fb623bf82c1d1a8a2d168142219.diff

LOG: [libc] Extend the current fenv functions to aarch64.

This change does not try to move the common parts of x86 and aarch64 and
build few abstractions over them. While this is possible, x86 story
needs a bit of cleanup, especially around manipulation of the mxcsr
register. Moreover, on x86 one can raise exceptions without performing
exception raising operations. So, all of this can be done in follow up
patches.

Reviewed By: lntue

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

Added: 
    libc/utils/FPUtil/aarch64/FEnv.h

Modified: 
    libc/config/linux/aarch64/entrypoints.txt
    libc/utils/FPUtil/FEnv.h

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 4e20903ad260..ca7252b17e64 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -20,6 +20,13 @@ set(TARGET_LIBC_ENTRYPOINTS
     # errno.h entrypoints
     libc.src.errno.__errno_location
 
+    # fenv.h entrypoints
+    libc.src.fenv.feclearexcept
+    libc.src.fenv.fegetround
+    libc.src.fenv.fesetround
+    libc.src.fenv.feraiseexcept
+    libc.src.fenv.fetestexcept
+
     # stdlib.h entrypoints
     libc.src.stdlib.abs
     libc.src.stdlib.labs

diff  --git a/libc/utils/FPUtil/FEnv.h b/libc/utils/FPUtil/FEnv.h
index bf520672161e..1634f5f10125 100644
--- a/libc/utils/FPUtil/FEnv.h
+++ b/libc/utils/FPUtil/FEnv.h
@@ -11,6 +11,8 @@
 
 #ifdef __x86_64__
 #include "x86_64/FEnv.h"
+#elif defined(__aarch64__)
+#include "aarch64/FEnv.h"
 #else
 #include "DummyFEnv.h"
 #endif

diff  --git a/libc/utils/FPUtil/aarch64/FEnv.h b/libc/utils/FPUtil/aarch64/FEnv.h
new file mode 100644
index 000000000000..ac0ef70b522d
--- /dev/null
+++ b/libc/utils/FPUtil/aarch64/FEnv.h
@@ -0,0 +1,204 @@
+//===-- aarch64 floating point env manipulation functions -------*- 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_UTILS_FPUTIL_AARCH64_FENV_H
+#define LLVM_LIBC_UTILS_FPUTIL_AARCH64_FENV_H
+
+#include <arm_acle.h>
+#include <fenv.h>
+#include <stdint.h>
+
+#include "utils/FPUtil/FPBits.h"
+
+namespace __llvm_libc {
+namespace fputil {
+
+struct FEnv {
+  static constexpr uint32_t ToNearest = 0x0;
+  static constexpr uint32_t Upward = 0x1;
+  static constexpr uint32_t Downward = 0x2;
+  static constexpr uint32_t TowardZero = 0x3;
+
+  static constexpr uint32_t Invalid = 0x1;
+  static constexpr uint32_t DivByZero = 0x2;
+  static constexpr uint32_t Overflow = 0x4;
+  static constexpr uint32_t Underflow = 0x8;
+  static constexpr uint32_t Inexact = 0x10;
+
+  // Zero-th bit is the first bit.
+  static constexpr uint32_t RoundingControlBitPosition = 22;
+  static constexpr uint32_t ExceptionStatusFlagsBitPosition = 0;
+  static constexpr uint32_t ExceptionControlFlagsBitPosition = 8;
+
+  static inline uint32_t getStatusValueForExcept(int excepts) {
+    return (excepts & FE_INVALID ? Invalid : 0) |
+           (excepts & FE_DIVBYZERO ? DivByZero : 0) |
+           (excepts & FE_OVERFLOW ? Overflow : 0) |
+           (excepts & FE_UNDERFLOW ? Underflow : 0) |
+           (excepts & FE_INEXACT ? Inexact : 0);
+  }
+
+  static inline int exceptionStatusToMacro(uint32_t status) {
+    return (status & Invalid ? FE_INVALID : 0) |
+           (status & DivByZero ? FE_DIVBYZERO : 0) |
+           (status & Overflow ? FE_OVERFLOW : 0) |
+           (status & Underflow ? FE_UNDERFLOW : 0) |
+           (status & Inexact ? FE_INEXACT : 0);
+  }
+
+  static uint32_t getControlWord() { return __arm_rsr("fpcr"); }
+
+  static void writeControlWord(uint32_t fpcr) { __arm_wsr("fpcr", fpcr); }
+
+  static uint32_t getStatusWord() { return __arm_rsr("fpsr"); }
+
+  static void writeStatusWord(uint32_t fpsr) { __arm_wsr("fpsr", fpsr); }
+};
+
+static inline int enableExcept(int excepts) {
+  uint32_t newExcepts = FEnv::getStatusValueForExcept(excepts);
+  uint32_t controlWord = FEnv::getControlWord();
+  int oldExcepts =
+      (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F;
+  controlWord |= (newExcepts << FEnv::ExceptionControlFlagsBitPosition);
+  FEnv::writeControlWord(controlWord);
+  return FEnv::exceptionStatusToMacro(oldExcepts);
+}
+
+static inline int disableExcept(int excepts) {
+  uint32_t disabledExcepts = FEnv::getStatusValueForExcept(excepts);
+  uint32_t controlWord = FEnv::getControlWord();
+  int oldExcepts =
+      (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F;
+  controlWord &= ~(disabledExcepts << FEnv::ExceptionControlFlagsBitPosition);
+  FEnv::writeControlWord(controlWord);
+  return FEnv::exceptionStatusToMacro(oldExcepts);
+}
+
+static inline int clearExcept(int excepts) {
+  uint32_t controlWord = FEnv::getControlWord();
+  uint32_t toClear = FEnv::getStatusValueForExcept(excepts);
+  controlWord &= ~(toClear << FEnv::ExceptionStatusFlagsBitPosition);
+  FEnv::writeStatusWord(controlWord);
+  return 0;
+}
+
+static inline int testExcept(int excepts) {
+  uint32_t toTest = FEnv::getStatusValueForExcept(excepts);
+  uint32_t statusWord = FEnv::getStatusWord();
+  return FEnv::exceptionStatusToMacro(
+      (statusWord >> FEnv::ExceptionStatusFlagsBitPosition) & toTest);
+}
+
+static inline int raiseExcept(int excepts) {
+  float zero = 0.0f;
+  float one = 1.0f;
+  float largeValue = FPBits<float>(FPBits<float>::maxNormal);
+  float smallValue = FPBits<float>(FPBits<float>::minNormal);
+  auto divfunc = [](float a, float b) {
+    __asm__ __volatile__("ldr  s0, %0\n\t"
+                         "ldr  s1, %1\n\t"
+                         "fdiv s0, s0, s1\n\t"
+                         : // No outputs
+                         : "m"(a), "m"(b)
+                         : "s0", "s1" /* s0 and s1 are clobbered */);
+  };
+
+  uint32_t toRaise = FEnv::getStatusValueForExcept(excepts);
+
+  if (toRaise & FEnv::Invalid) {
+    divfunc(zero, zero);
+    uint32_t statusWord = FEnv::getStatusWord();
+    if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
+          FEnv::Invalid))
+      return -1;
+  }
+
+  if (toRaise & FEnv::DivByZero) {
+    divfunc(one, zero);
+    uint32_t statusWord = FEnv::getStatusWord();
+    if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
+          FEnv::DivByZero))
+      return -1;
+  }
+  if (toRaise & FEnv::Overflow) {
+    divfunc(largeValue, smallValue);
+    uint32_t statusWord = FEnv::getStatusWord();
+    if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
+          FEnv::Overflow))
+      return -1;
+  }
+  if (toRaise & FEnv::Underflow) {
+    divfunc(smallValue, largeValue);
+    uint32_t statusWord = FEnv::getStatusWord();
+    if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
+          FEnv::Underflow))
+      return -1;
+  }
+  if (toRaise & FEnv::Inexact) {
+    float two = 2.0f;
+    float three = 3.0f;
+    // 2.0 / 3.0 cannot be represented exactly in any radix 2 floating point
+    // format.
+    divfunc(two, three);
+    uint32_t statusWord = FEnv::getStatusWord();
+    if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
+          FEnv::Inexact))
+      return -1;
+  }
+  return 0;
+}
+
+static inline int getRound() {
+  uint32_t roundingMode =
+      (FEnv::getControlWord() >> FEnv::RoundingControlBitPosition) & 0x3;
+  switch (roundingMode) {
+  case FEnv::ToNearest:
+    return FE_TONEAREST;
+  case FEnv::Downward:
+    return FE_DOWNWARD;
+  case FEnv::Upward:
+    return FE_UPWARD;
+  case FEnv::TowardZero:
+    return FE_TOWARDZERO;
+  default:
+    return -1; // Error value.
+  }
+}
+
+static inline int setRound(int mode) {
+  uint16_t bitValue;
+  switch (mode) {
+  case FE_TONEAREST:
+    bitValue = FEnv::ToNearest;
+    break;
+  case FE_DOWNWARD:
+    bitValue = FEnv::Downward;
+    break;
+  case FE_UPWARD:
+    bitValue = FEnv::Upward;
+    break;
+  case FE_TOWARDZERO:
+    bitValue = FEnv::TowardZero;
+    break;
+  default:
+    return 1; // To indicate failure
+  }
+
+  uint32_t controlWord = FEnv::getControlWord();
+  controlWord &= ~(0x3 << FEnv::RoundingControlBitPosition);
+  controlWord |= (bitValue << FEnv::RoundingControlBitPosition);
+  FEnv::writeControlWord(controlWord);
+
+  return 0;
+}
+
+} // namespace fputil
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_UTILS_FPUTIL_AARCH64_FENV_H


        


More information about the libc-commits mailing list