xref: /netbsd-src/lib/libm/arch/riscv/fenv.c (revision afab4e300d3a9fb07dd8c80daf53d0feb3345706)
1 /* $NetBSD: fenv.c,v 1.4 2023/05/07 12:41:47 skrll 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.4 2023/05/07 12:41:47 skrll 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
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
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  * The validation of input is being deferred to fesetexceptflag().
119  */
120 int
121 feraiseexcept(int excepts)
122 {
123 	fexcept_t ex = 0;
124 
125 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
126 
127 	excepts &= FE_ALL_EXCEPT;
128 	fesetexceptflag(&ex, excepts);
129 	/* XXX exception magic XXX */
130 
131 	/* Success */
132 	return 0;
133 }
134 
135 /*
136  * This function sets the floating-point status flags indicated by the argument
137  * `excepts' to the states stored in the object pointed to by `flagp'. It does
138  * NOT raise any floating-point exceptions, but only sets the state of the flags.
139  */
140 int
141 fesetexceptflag(const fexcept_t *flagp, int excepts)
142 {
143 	_DIAGASSERT(flagp != NULL);
144 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
145 
146 	excepts &= FE_ALL_EXCEPT;
147 
148 	int fflags = fcsr_fflags_read();
149 
150 	fflags = (fflags & ~excepts) | (*flagp & excepts);
151 
152 	fcsr_fflags_write(fflags);
153 
154 	/* Success */
155 	return 0;
156 }
157 
158 /*
159  * The fetestexcept() function determines which of a specified subset of the
160  * floating-point exception flags are currently set. The `excepts' argument
161  * specifies the floating-point status flags to be queried.
162  */
163 int
164 fetestexcept(int excepts)
165 {
166 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
167 
168 	return fcsr_fflags_read() & excepts & FE_ALL_EXCEPT;
169 }
170 
171 int
172 fegetround(void)
173 {
174 	return fcsr_frm_read();
175 }
176 
177 /*
178  * The fesetround() function shall establish the rounding direction represented
179  * by its argument round. If the argument is not equal to the value of a
180  * rounding direction macro, the rounding direction is not changed.
181  */
182 int
183 fesetround(int round)
184 {
185 	if ((unsigned int)round > FCSR_FRM_RMM) {
186 		/* Failure */
187 		return (-1);
188 	}
189 
190 	fcsr_frm_write(round);
191 
192 	/* Success */
193 	return 0;
194 }
195 
196 /*
197  * The fegetenv() function attempts to store the current floating-point
198  * environment in the object pointed to by envp.
199  */
200 int
201 fegetenv(fenv_t *envp)
202 {
203 	_DIAGASSERT(envp != NULL);
204 
205 	*envp = fcsr_read();
206 
207 	/* Success */
208 	return 0;
209 }
210 
211 /*
212  * The feholdexcept() function saves the current floating-point environment in
213  * the object pointed to by envp, clears the floating-point status flags, and
214  * then installs a non-stop (continue on floating-point exceptions) mode, if
215  * available, for all floating-point exceptions.
216  */
217 int
218 feholdexcept(fenv_t *envp)
219 {
220 	_DIAGASSERT(envp != NULL);
221 
222 	*envp = fcsr_read();
223 
224 	fcsr_fflags_write(0);
225 
226 	/* Success */
227 	return 0;
228 }
229 
230 /*
231  * The fesetenv() function attempts to establish the floating-point environment
232  * represented by the object pointed to by envp. The argument `envp' points
233  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
234  * floating-point environment macro. The fesetenv() function does not raise
235  * floating-point exceptions, but only installs the state of the floating-point
236  * status flags represented through its argument.
237  */
238 int
239 fesetenv(const fenv_t *envp)
240 {
241 
242 	_DIAGASSERT(envp != NULL);
243 
244 	fenv_t env = *envp;
245 
246 	if ((env & ~(FCSR_FRM|FCSR_FFLAGS)
247 	    || __SHIFTOUT(env, FCSR_FRM) > FCSR_FRM_RMM)) {
248 		return -1;
249 	}
250 
251 	fcsr_write(env);
252 
253 	/* Success */
254 	return 0;
255 }
256 
257 /*
258  * The feupdateenv() function saves the currently raised floating-point
259  * exceptions in its automatic storage, installs the floating-point environment
260  * represented by the object pointed to by `envp', and then raises the saved
261  * floating-point exceptions. The argument `envp' shall point to an object set
262  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
263  * environment macro.
264  */
265 int
266 feupdateenv(const fenv_t *envp)
267 {
268 	_DIAGASSERT(envp != NULL);
269 
270 	int fflags = fcsr_fflags_read();
271 
272 	fesetenv(envp);
273 	feraiseexcept(fflags);
274 
275 	/* Success */
276 	return 0;
277 }
278 
279 /*
280  * The following functions are extensions to the standard
281  */
282 int
283 feenableexcept(int nmask)
284 {
285 	return 0;
286 }
287 
288 int
289 fedisableexcept(int nmask)
290 {
291 	return 0;
292 }
293 
294 int
295 fegetexcept(void)
296 {
297 	return 0;
298 }
299