aboutsummaryrefslogtreecommitdiff
path: root/libc/src/__support/FPUtil/arm/FEnvImpl.h
blob: e2669d13bc2a7c21e28226bb3ed0add16e36128d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//===-- arm 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_SRC___SUPPORT_FPUTIL_ARM_FENVIMPL_H
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_ARM_FENVIMPL_H

#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/macros/config.h" // For LIBC_INLINE

#include <fenv.h>
#include <stdint.h>

namespace LIBC_NAMESPACE {
namespace fputil {

struct FEnv {
  // Arm floating point state is all stored in a single 32-bit register named
  // fpscr.
  uint32_t fpscr;
  static constexpr uint32_t RoundingControlBitPosition = 22;
  static constexpr uint32_t ExceptionControlBitPosition = 8;

  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_ENABLE = 0x1;
  static constexpr uint32_t DIVBYZERO_ENABLE = 0x2;
  static constexpr uint32_t OVERFLOW_ENABLE = 0x4;
  static constexpr uint32_t UNDERFLOW_ENABLE = 0x8;
  static constexpr uint32_t INEXACT_ENABLE = 0x10;
  static constexpr uint32_t DENORMAL_ENABLE = 0x20;

  static constexpr uint32_t INVALID_STATUS = 0x1;
  static constexpr uint32_t DIVBYZERO_STATUS = 0x2;
  static constexpr uint32_t OVERFLOW_STATUS = 0x4;
  static constexpr uint32_t UNDERFLOW_STATUS = 0x8;
  static constexpr uint32_t INEXACT_STATUS = 0x10;
  static constexpr uint32_t DENORMAL_STATUS = 0x80;

  LIBC_INLINE static uint32_t get_fpscr() { return __builtin_arm_get_fpscr(); }
  LIBC_INLINE static void set_fpscr(uint32_t val) {
    __builtin_arm_set_fpscr(val);
  }

  LIBC_INLINE static int exception_enable_bits_to_macro(uint32_t status) {
    return (status & INVALID_ENABLE ? FE_INVALID : 0) |
           (status & DIVBYZERO_ENABLE ? FE_DIVBYZERO : 0) |
           (status & OVERFLOW_ENABLE ? FE_OVERFLOW : 0) |
           (status & UNDERFLOW_ENABLE ? FE_UNDERFLOW : 0) |
           (status & INEXACT_ENABLE ? FE_INEXACT : 0);
  }

  LIBC_INLINE static uint32_t exception_macro_to_enable_bits(int except) {
    return (except & FE_INVALID ? INVALID_ENABLE : 0) |
           (except & FE_DIVBYZERO ? DIVBYZERO_ENABLE : 0) |
           (except & FE_OVERFLOW ? OVERFLOW_ENABLE : 0) |
           (except & FE_UNDERFLOW ? UNDERFLOW_ENABLE : 0) |
           (except & FE_INEXACT ? INEXACT_ENABLE : 0);
  }

  LIBC_INLINE static uint32_t exception_macro_to_status_bits(int except) {
    return (except & FE_INVALID ? INVALID_STATUS : 0) |
           (except & FE_DIVBYZERO ? DIVBYZERO_STATUS : 0) |
           (except & FE_OVERFLOW ? OVERFLOW_STATUS : 0) |
           (except & FE_UNDERFLOW ? UNDERFLOW_STATUS : 0) |
           (except & FE_INEXACT ? INEXACT_STATUS : 0);
  }

  LIBC_INLINE static uint32_t exception_status_bits_to_macro(int status) {
    return (status & INVALID_STATUS ? FE_INVALID : 0) |
           (status & DIVBYZERO_STATUS ? FE_DIVBYZERO : 0) |
           (status & OVERFLOW_STATUS ? FE_OVERFLOW : 0) |
           (status & UNDERFLOW_STATUS ? FE_UNDERFLOW : 0) |
           (status & INEXACT_STATUS ? FE_INEXACT : 0);
  }
};

// Enables exceptions in |excepts| and returns the previously set exceptions.
LIBC_INLINE int enable_except(int excepts) {
  uint32_t new_excepts = FEnv::exception_macro_to_enable_bits(excepts);
  uint32_t fpscr = FEnv::get_fpscr();
  int old = (fpscr >> FEnv::ExceptionControlBitPosition) & 0x3F;
  fpscr |= (new_excepts << FEnv::ExceptionControlBitPosition);
  FEnv::set_fpscr(fpscr);
  return FEnv::exception_enable_bits_to_macro(old);
}

// Disables exceptions in |excepts| and returns the previously set exceptions.
LIBC_INLINE int disable_except(int excepts) {
  uint32_t disable_bits = FEnv::exception_macro_to_enable_bits(excepts);
  uint32_t fpscr = FEnv::get_fpscr();
  int old = (fpscr >> FEnv::ExceptionControlBitPosition) & 0x3F;
  fpscr &= ~(disable_bits << FEnv::ExceptionControlBitPosition);
  FEnv::set_fpscr(fpscr);
  return FEnv::exception_enable_bits_to_macro(old);
}

// Returns the currently enabled exceptions.
LIBC_INLINE int get_except() {
  uint32_t fpscr = FEnv::get_fpscr();
  int enabled_excepts = (fpscr >> FEnv::ExceptionControlBitPosition) & 0x3F;
  return FEnv::exception_enable_bits_to_macro(enabled_excepts);
}

// Clears the exceptions in |excepts|.
LIBC_INLINE int clear_except(int excepts) {
  uint32_t fpscr = FEnv::get_fpscr();
  uint32_t to_clear = FEnv::exception_macro_to_status_bits(excepts);
  fpscr &= ~to_clear;
  FEnv::set_fpscr(fpscr);
  return 0;
}

// Returns the set of exceptions which are from the input set |excepts|.
LIBC_INLINE int test_except(int excepts) {
  uint32_t to_test = FEnv::exception_macro_to_status_bits(excepts);
  uint32_t fpscr = FEnv::get_fpscr();
  return FEnv::exception_status_bits_to_macro(fpscr & 0x9F & to_test);
}

// Set the exceptions in |excepts|.
LIBC_INLINE int set_except(int excepts) {
  uint32_t fpscr = FEnv::get_fpscr();
  FEnv::set_fpscr(fpscr | FEnv::exception_macro_to_status_bits(excepts));
  return 0;
}

LIBC_INLINE int raise_except(int excepts) {
  float zero = 0.0f;
  float one = 1.0f;
  float large_value = float(FPBits<float>(FPBits<float>::MAX_NORMAL));
  float small_value = float(FPBits<float>(FPBits<float>::MIN_NORMAL));
  auto divfunc = [](float a, float b) {
    __asm__ __volatile__("flds  s0, %0\n\t"
                         "flds  s1, %1\n\t"
                         "fdivs s0, s0, s1\n\t"
                         : // No outputs
                         : "m"(a), "m"(b)
                         : "s0", "s1" /* s0 and s1 are clobbered */);
  };

  uint32_t to_raise = FEnv::exception_macro_to_status_bits(excepts);
  int result = 0;

  if (to_raise & FEnv::INVALID_STATUS) {
    divfunc(zero, zero);
    uint32_t fpscr = FEnv::get_fpscr();
    if (!(fpscr & FEnv::INVALID_STATUS))
      result = -1;
  }
  if (to_raise & FEnv::DIVBYZERO_STATUS) {
    divfunc(one, zero);
    uint32_t fpscr = FEnv::get_fpscr();
    if (!(fpscr & FEnv::DIVBYZERO_STATUS))
      result = -1;
  }
  if (to_raise & FEnv::OVERFLOW_STATUS) {
    divfunc(large_value, small_value);
    uint32_t fpscr = FEnv::get_fpscr();
    if (!(fpscr & FEnv::OVERFLOW_STATUS))
      result = -1;
  }
  if (to_raise & FEnv::UNDERFLOW_STATUS) {
    divfunc(small_value, large_value);
    uint32_t fpscr = FEnv::get_fpscr();
    if (!(fpscr & FEnv::UNDERFLOW_STATUS))
      result = -1;
  }
  if (to_raise & FEnv::INEXACT_STATUS) {
    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 fpscr = FEnv::get_fpscr();
    if (!(fpscr & FEnv::INEXACT_STATUS))
      result = -1;
  }
  return result;
}

LIBC_INLINE int get_round() {
  uint32_t mode = (FEnv::get_fpscr() >> FEnv::RoundingControlBitPosition) & 0x3;
  switch (mode) {
  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.
  }
  return 0;
}

LIBC_INLINE int set_round(int mode) {
  uint16_t bits;
  switch (mode) {
  case FE_TONEAREST:
    bits = FEnv::TONEAREST;
    break;
  case FE_DOWNWARD:
    bits = FEnv::DOWNWARD;
    break;
  case FE_UPWARD:
    bits = FEnv::UPWARD;
    break;
  case FE_TOWARDZERO:
    bits = FEnv::TOWARDZERO;
    break;
  default:
    return 1; // To indicate failure
  }

  uint32_t fpscr = FEnv::get_fpscr();
  fpscr &= ~(0x3 << FEnv::RoundingControlBitPosition);
  fpscr |= (bits << FEnv::RoundingControlBitPosition);
  FEnv::set_fpscr(fpscr);

  return 0;
}

LIBC_INLINE int get_env(fenv_t *envp) {
  FEnv *state = reinterpret_cast<FEnv *>(envp);
  state->fpscr = FEnv::get_fpscr();
  return 0;
}

LIBC_INLINE int set_env(const fenv_t *envp) {
  if (envp == FE_DFL_ENV) {
    uint32_t fpscr = FEnv::get_fpscr();
    // Default status implies:
    // 1. Round to nearest rounding mode.
    fpscr &= ~(0x3 << FEnv::RoundingControlBitPosition);
    fpscr |= (FEnv::TONEAREST << FEnv::RoundingControlBitPosition);
    // 2. All exceptions are disabled.
    fpscr &= ~(0x3F << FEnv::ExceptionControlBitPosition);
    // 3. All exceptions are cleared. There are two reserved bits
    // at bit 5 and 6 so we just write one full byte (6 bits for
    // the exceptions, and 2 reserved bits.)
    fpscr &= ~(static_cast<uint32_t>(0xFF));

    FEnv::set_fpscr(fpscr);
    return 0;
  }

  const FEnv *state = reinterpret_cast<const FEnv *>(envp);
  FEnv::set_fpscr(state->fpscr);
  return 0;
}

} // namespace fputil
} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_ARM_FENVIMPL_H