1 /* $NetBSD: fenv.c,v 1.5 2024/05/17 02:11:07 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2014 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Thomas of 3am Software Foundry.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: fenv.c,v 1.5 2024/05/17 02:11:07 riastradh Exp $");
34
35 #include "namespace.h"
36
37 #include <sys/param.h>
38 #include <sys/sysctl.h>
39 #include <assert.h>
40 #include <fenv.h>
41 #include <stddef.h>
42 #include <string.h>
43
44 #include <riscv/sysreg.h>
45
46 #ifdef __weak_alias
47 __weak_alias(feclearexcept,_feclearexcept)
48 __weak_alias(fedisableexcept,_fedisableexcept)
49 __weak_alias(feenableexcept,_feenableexcept)
50 __weak_alias(fegetenv,_fegetenv)
51 __weak_alias(fegetexcept,_fegetexcept)
52 __weak_alias(fegetexceptflag,_fegetexceptflag)
53 __weak_alias(fegetround,_fegetround)
54 __weak_alias(feholdexcept,_feholdexcept)
55 __weak_alias(feraiseexcept,_feraiseexcept)
56 __weak_alias(fesetenv,_fesetenv)
57 __weak_alias(fesetexceptflag,_fesetexceptflag)
58 __weak_alias(fesetround,_fesetround)
59 __weak_alias(fetestexcept,_fetestexcept)
60 __weak_alias(feupdateenv,_feupdateenv)
61 #endif
62
63 /*
64 * The following constant represents the default floating-point environment
65 * (that is, the one installed at program startup) and has type pointer to
66 * const-qualified fenv_t.
67 *
68 * It can be used as an argument to the functions within the <fenv.h> header
69 * that manage the floating-point environment, namely fesetenv() and
70 * feupdateenv().
71 */
72 const fenv_t __fe_dfl_env = __SHIFTIN(FCSR_FRM_RNE, FCSR_FRM);
73
74 /*
75 * The feclearexcept() function clears the supported floating-point exceptions
76 * represented by `excepts'.
77 */
78 int
feclearexcept(int excepts)79 feclearexcept(int excepts)
80 {
81 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
82
83 int fflags = fcsr_fflags_read();
84
85 fflags &= ~(excepts & FE_ALL_EXCEPT);
86
87 fcsr_fflags_write(fflags);
88
89 /* Success */
90 return 0;
91 }
92
93 /*
94 * The fegetexceptflag() function stores an implementation-defined
95 * representation of the states of the floating-point status flags indicated by
96 * the argument excepts in the object pointed to by the argument flagp.
97 */
98 int
fegetexceptflag(fexcept_t * flagp,int excepts)99 fegetexceptflag(fexcept_t *flagp, int excepts)
100 {
101 _DIAGASSERT(flagp != NULL);
102 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
103
104 *flagp = fcsr_fflags_read() & excepts;
105
106 /* Success */
107 return 0;
108 }
109
110 /*
111 * The feraiseexcept() function raises the supported floating-point exceptions
112 * represented by the argument `excepts'.
113 *
114 * The standard explicitly allows us to execute an instruction that has the
115 * exception as a side effect, but we choose to manipulate the status register
116 * directly.
117 */
118 int
feraiseexcept(int excepts)119 feraiseexcept(int excepts)
120 {
121
122 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
123
124 excepts &= FE_ALL_EXCEPT;
125 fcsr_fflags_write(fcsr_fflags_read() | excepts);
126
127 /* Success */
128 return 0;
129 }
130
131 /*
132 * This function sets the floating-point status flags indicated by the argument
133 * `excepts' to the states stored in the object pointed to by `flagp'. It does
134 * NOT raise any floating-point exceptions, but only sets the state of the flags.
135 */
136 int
fesetexceptflag(const fexcept_t * flagp,int excepts)137 fesetexceptflag(const fexcept_t *flagp, int excepts)
138 {
139 _DIAGASSERT(flagp != NULL);
140 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
141
142 excepts &= FE_ALL_EXCEPT;
143
144 int fflags = fcsr_fflags_read();
145
146 fflags = (fflags & ~excepts) | (*flagp & excepts);
147
148 fcsr_fflags_write(fflags);
149
150 /* Success */
151 return 0;
152 }
153
154 /*
155 * The fetestexcept() function determines which of a specified subset of the
156 * floating-point exception flags are currently set. The `excepts' argument
157 * specifies the floating-point status flags to be queried.
158 */
159 int
fetestexcept(int excepts)160 fetestexcept(int excepts)
161 {
162 _DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
163
164 return fcsr_fflags_read() & excepts & FE_ALL_EXCEPT;
165 }
166
167 int
fegetround(void)168 fegetround(void)
169 {
170 return fcsr_frm_read();
171 }
172
173 /*
174 * The fesetround() function shall establish the rounding direction represented
175 * by its argument round. If the argument is not equal to the value of a
176 * rounding direction macro, the rounding direction is not changed.
177 */
178 int
fesetround(int round)179 fesetround(int round)
180 {
181 if ((unsigned int)round > FCSR_FRM_RMM) {
182 /* Failure */
183 return (-1);
184 }
185
186 fcsr_frm_write(round);
187
188 /* Success */
189 return 0;
190 }
191
192 /*
193 * The fegetenv() function attempts to store the current floating-point
194 * environment in the object pointed to by envp.
195 */
196 int
fegetenv(fenv_t * envp)197 fegetenv(fenv_t *envp)
198 {
199 _DIAGASSERT(envp != NULL);
200
201 *envp = fcsr_read();
202
203 /* Success */
204 return 0;
205 }
206
207 /*
208 * The feholdexcept() function saves the current floating-point environment in
209 * the object pointed to by envp, clears the floating-point status flags, and
210 * then installs a non-stop (continue on floating-point exceptions) mode, if
211 * available, for all floating-point exceptions.
212 */
213 int
feholdexcept(fenv_t * envp)214 feholdexcept(fenv_t *envp)
215 {
216 _DIAGASSERT(envp != NULL);
217
218 *envp = fcsr_read();
219
220 fcsr_fflags_write(0);
221
222 /* Success */
223 return 0;
224 }
225
226 /*
227 * The fesetenv() function attempts to establish the floating-point environment
228 * represented by the object pointed to by envp. The argument `envp' points
229 * to an object set by a call to fegetenv() or feholdexcept(), or equal a
230 * floating-point environment macro. The fesetenv() function does not raise
231 * floating-point exceptions, but only installs the state of the floating-point
232 * status flags represented through its argument.
233 */
234 int
fesetenv(const fenv_t * envp)235 fesetenv(const fenv_t *envp)
236 {
237
238 _DIAGASSERT(envp != NULL);
239
240 fenv_t env = *envp;
241
242 if ((env & ~(FCSR_FRM|FCSR_FFLAGS)
243 || __SHIFTOUT(env, FCSR_FRM) > FCSR_FRM_RMM)) {
244 return -1;
245 }
246
247 fcsr_write(env);
248
249 /* Success */
250 return 0;
251 }
252
253 /*
254 * The feupdateenv() function saves the currently raised floating-point
255 * exceptions in its automatic storage, installs the floating-point environment
256 * represented by the object pointed to by `envp', and then raises the saved
257 * floating-point exceptions. The argument `envp' shall point to an object set
258 * by a call to feholdexcept() or fegetenv(), or equal a floating-point
259 * environment macro.
260 */
261 int
feupdateenv(const fenv_t * envp)262 feupdateenv(const fenv_t *envp)
263 {
264 _DIAGASSERT(envp != NULL);
265
266 int fflags = fcsr_fflags_read();
267
268 fesetenv(envp);
269 feraiseexcept(fflags);
270
271 /* Success */
272 return 0;
273 }
274
275 /*
276 * The following functions are extensions to the standard
277 */
278 int
feenableexcept(int nmask)279 feenableexcept(int nmask)
280 {
281 return 0;
282 }
283
284 int
fedisableexcept(int nmask)285 fedisableexcept(int nmask)
286 {
287 return 0;
288 }
289
290 int
fegetexcept(void)291 fegetexcept(void)
292 {
293 return 0;
294 }
295