fenv.c revision 1.2
1/* $NetBSD: fenv.c,v 1.2 2012/08/04 03:53:56 riastradh Exp $ */
2
3/*-
4 * Copyright (c) 2004-2005 David Schultz <das (at) FreeBSD.ORG>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__RCSID("$NetBSD: fenv.c,v 1.2 2012/08/04 03:53:56 riastradh Exp $");
31
32#include <assert.h>
33#include <fenv.h>
34#include <stddef.h>
35#include <string.h>
36
37/* Load x87 Control Word */
38#define	__fldcw(__cw)		__asm__ __volatile__ \
39	("fldcw %0" : : "m" (__cw))
40
41/* No-Wait Store Control Word */
42#define	__fnstcw(__cw)		__asm__ __volatile__ \
43	("fnstcw %0" : "=m" (*(__cw)))
44
45/* No-Wait Store Status Word */
46#define	__fnstsw(__sw)		__asm__ __volatile__ \
47	("fnstsw %0" : "=am" (*(__sw)))
48
49/* No-Wait Clear Exception Flags */
50#define	__fnclex()		__asm__ __volatile__ \
51	("fnclex")
52
53/* Load x87 Environment */
54#define	__fldenv(__env)		__asm__ __volatile__ \
55	("fldenv %0" : : "m" (__env))
56
57/* No-Wait Store x87 environment */
58#define	__fnstenv(__env)	__asm__ __volatile__ \
59	("fnstenv %0" : "=m" (*(__env)))
60
61/* Load the MXCSR register */
62#define	__ldmxcsr(__mxcsr)	__asm__ __volatile__ \
63	("ldmxcsr %0" : : "m" (__mxcsr))
64
65/* Store the MXCSR register state */
66#define	__stmxcsr(__mxcsr)	__asm__ __volatile__ \
67	("stmxcsr %0" : "=m" (*(__mxcsr)))
68
69/*
70 * The following constant represents the default floating-point environment
71 * (that is, the one installed at program startup) and has type pointer to
72 * const-qualified fenv_t.
73 *
74 * It can be used as an argument to the functions within the <fenv.h> header
75 * that manage the floating-point environment, namely fesetenv() and
76 * feupdateenv().
77 *
78 * x87 fpu registers are 16bit wide. The upper bits, 31-16, are marked as
79 * RESERVED. We provide a partial floating-point environment, where we
80 * define only the lower bits. The reserved bits are extracted and set by
81 * the consumers of FE_DFL_ENV, during runtime.
82 */
83fenv_t __fe_dfl_env = {
84	{
85		__NetBSD_NPXCW__,	/* Control word register */
86		0x00000000,		/* Status word register */
87		0x0000ffff,		/* Tag word register */
88		{
89			0x00000000,
90			0x00000000,
91			0x00000000,
92			0x00000000,
93		},
94	},
95	__INITIAL_MXCSR__       /* MXCSR register */
96};
97#define FE_DFL_ENV      ((const fenv_t *) &__fe_dfl_env)
98
99
100/*
101 * The feclearexcept() function clears the supported floating-point exceptions
102 * represented by `excepts'.
103 */
104int
105feclearexcept(int excepts)
106{
107	fenv_t fenv;
108	int ex;
109
110	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
111
112	ex = excepts & FE_ALL_EXCEPT;
113
114	/* Store the current x87 floating-point environment */
115	__fnstenv(&fenv);
116
117	/* Clear the requested floating-point exceptions */
118	fenv.x87.status &= ~ex;
119
120	/* Load the x87 floating-point environent */
121	__fldenv(fenv);
122
123	/* Same for SSE environment */
124	__stmxcsr(&fenv.mxcsr);
125	fenv.mxcsr &= ~ex;
126	__ldmxcsr(fenv.mxcsr);
127
128	/* Success */
129	return (0);
130}
131
132/*
133 * The fegetexceptflag() function stores an implementation-defined
134 * representation of the states of the floating-point status flags indicated by
135 * the argument excepts in the object pointed to by the argument flagp.
136 */
137int
138fegetexceptflag(fexcept_t *flagp, int excepts)
139{
140	uint32_t mxcsr;
141	uint16_t x87_status;
142	int ex;
143
144	_DIAGASSERT(flagp != NULL);
145	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
146
147	ex = excepts & FE_ALL_EXCEPT;
148
149	/* Store the current x87 status register */
150	__fnstsw(&x87_status);
151
152	/* Store the MXCSR register */
153	__stmxcsr(&mxcsr);
154
155	/* Store the results in flagp */
156	*flagp = (x87_status | mxcsr) & ex;
157
158	/* Success */
159	return (0);
160}
161
162/*
163 * The feraiseexcept() function raises the supported floating-point exceptions
164 * represented by the argument `excepts'.
165 *
166 * The standard explicitly allows us to execute an instruction that has the
167 * exception as a side effect, but we choose to manipulate the status register
168 * directly.
169 *
170 * The validation of input is being deferred to fesetexceptflag().
171 */
172int
173feraiseexcept(int excepts)
174{
175	int ex;
176
177	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
178
179	ex = excepts & FE_ALL_EXCEPT;
180	fesetexceptflag((unsigned int *)&excepts, excepts);
181
182	/* Success */
183	return (0);
184}
185
186/*
187 * This function sets the floating-point status flags indicated by the argument
188 * `excepts' to the states stored in the object pointed to by `flagp'. It does
189 * NOT raise any floating-point exceptions, but only sets the state of the flags.
190 */
191int
192fesetexceptflag(const fexcept_t *flagp, int excepts)
193{
194	fenv_t fenv;
195	int ex;
196
197	_DIAGASSERT(flagp != NULL);
198	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
199
200	ex = excepts & FE_ALL_EXCEPT;
201
202	/* Store the current x87 floating-point environment */
203	__fnstenv(&fenv);
204
205	/* Set the requested status flags */
206	fenv.x87.status |= *flagp & ex;
207
208	/* Load the x87 floating-point environent */
209	__fldenv(fenv);
210
211	/* Same for SSE environment */
212	__stmxcsr(&fenv.mxcsr);
213	fenv.mxcsr |= *flagp & ex;
214	__ldmxcsr(fenv.mxcsr);
215
216	/* Success */
217	return (0);
218}
219
220/*
221 * The fetestexcept() function determines which of a specified subset of the
222 * floating-point exception flags are currently set. The `excepts' argument
223 * specifies the floating-point status flags to be queried.
224 */
225int
226fetestexcept(int excepts)
227{
228	fenv_t fenv;
229	uint32_t mxcsr;
230	uint16_t status;
231	int ex;
232
233	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
234
235	ex = excepts & FE_ALL_EXCEPT;
236
237	/* Store the current x87 floating-point environment */
238	memset(&fenv, 0, sizeof(fenv));
239
240	__fnstenv(&fenv);
241	__fnstsw(&status);
242
243	/* Store the MXCSR register state */
244	__stmxcsr(&fenv.mxcsr);
245	__stmxcsr(&mxcsr);
246
247	return ((fenv.x87.status | fenv.mxcsr) & ex);
248}
249
250/*
251 * The fegetround() function gets the current rounding direction.
252 */
253int
254fegetround(void)
255{
256	uint32_t mxcsr;
257	uint16_t control;
258
259	/*
260	 * We check both the x87 floating-point unit _and_ the SSE unit.
261	 * Normally, those two must agree with respect to each other. If they
262	 * don't, it's not our fault and the result is non-determinable, in
263	 * which case POSIX says that a negative value should be returned.
264	 */
265	__fnstcw(&control);
266	__stmxcsr(&mxcsr);
267
268	if ((control & _X87_ROUNDING_MASK)
269	    != ((mxcsr & _SSE_ROUNDING_MASK) >> 3)) {
270		return (-1);
271	}
272
273	return (control & _X87_ROUNDING_MASK);
274}
275
276/*
277 * The fesetround() function establishes the rounding direction represented by
278 * its argument `round'. If the argument is not equal to the value of a rounding
279 * direction macro, the rounding direction is not changed.
280 */
281int
282fesetround(int round)
283{
284	uint32_t  mxcsr;
285	uint16_t control;
286
287	/* Check whether requested rounding direction is supported */
288	if (round & (~_X87_ROUNDING_MASK))
289		return (-1);
290
291	/* Store the current x87 control word register  */
292	__fnstcw(&control);
293
294	/*
295	 * Set the rounding direction
296	 * Rounding Control is bits 10-11, so shift appropriately
297	 */
298	control &= ~_X87_ROUNDING_MASK;
299	control |= round;
300
301	/* Load the x87 control word register */
302	__fldcw(control);
303
304	/*
305	 * Same for the SSE environment
306	 * Rounding Control is bits 13-14, so shift appropriately
307	 */
308	__stmxcsr(&mxcsr);
309	mxcsr &= ~_SSE_ROUNDING_MASK;
310	mxcsr |= (round << _SSE_ROUND_SHIFT);
311	__ldmxcsr(mxcsr);
312
313	/* Success */
314	return (0);
315}
316
317/*
318 * The fegetenv() function attempts to store the current floating-point
319 * environment in the object pointed to by envp.
320 */
321int
322fegetenv(fenv_t *envp)
323{
324	_DIAGASSERT(envp != NULL);
325
326	/* Store the current x87 floating-point environment */
327	__fnstenv(envp);
328
329	/* Store the MXCSR register state */
330	__stmxcsr(&envp->mxcsr);
331
332     /*
333      * When an FNSTENV instruction is executed, all pending exceptions are
334      * essentially lost (either the x87 FPU status register is cleared or all
335      * exceptions are masked).
336      *
337      * 8.6 X87 FPU EXCEPTION SYNCHRONIZATION -
338      * Intel(R) 64 and IA-32 Architectures Softare Developer's Manual - Vol 1
339      *
340      */
341	__fldcw(envp->x87.control);
342
343	/* Success */
344	return (0);
345}
346
347/*
348 * The feholdexcept() function saves the current floating-point environment
349 * in the object pointed to by envp, clears the floating-point status flags, and
350 * then installs a non-stop (continue on floating-point exceptions) mode, if
351 * available, for all floating-point exceptions.
352 */
353int
354feholdexcept(fenv_t *envp)
355{
356	uint32_t mxcsr;
357
358	_DIAGASSERT(envp != NULL);
359
360	/* Store the current x87 floating-point environment */
361	__fnstenv(envp);
362
363	/* Clear all exception flags in FPU */
364	__fnclex();
365
366	/* Store the MXCSR register state */
367	__stmxcsr(&envp->mxcsr);
368
369	/* Clear exception flags in MXCSR XXX */
370	mxcsr = envp->mxcsr;
371	mxcsr &= ~FE_ALL_EXCEPT;
372
373	/* Mask all exceptions */
374	mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
375
376	__ldmxcsr(mxcsr);
377
378	/* Success */
379	return (0);
380}
381
382/*
383 * The fesetenv() function attempts to establish the floating-point environment
384 * represented by the object pointed to by envp. The argument `envp' points
385 * to an object set by a call to fegetenv() or feholdexcept(), or equal a
386 * floating-point environment macro. The fesetenv() function does not raise
387 * floating-point exceptions, but only installs the state of the floating-point
388 * status flags represented through its argument.
389 */
390int
391fesetenv(const fenv_t *envp)
392{
393	fenv_t fenv;
394
395	_DIAGASSERT(envp != NULL);
396
397	/* Store the x87 floating-point environment */
398	memset(&fenv, 0, sizeof fenv);
399	__fnstenv(&fenv);
400
401	__fe_dfl_env.x87.control = (fenv.x87.control & 0xffff0000)
402	    | (__fe_dfl_env.x87.control & 0x0000ffff);
403	__fe_dfl_env.x87.status = (fenv.x87.status & 0xffff0000)
404	    | (__fe_dfl_env.x87.status & 0x0000ffff);
405	__fe_dfl_env.x87.tag = (fenv.x87.tag & 0xffff0000)
406	    | (__fe_dfl_env.x87.tag & 0x0000ffff);
407	__fe_dfl_env.x87.others[3] = (fenv.x87.others[3] & 0xffff0000)
408	    | (__fe_dfl_env.x87.others[3] & 0x0000ffff);
409	__fldenv(*envp);
410
411	/* Store the MXCSR register */
412	__ldmxcsr(envp->mxcsr);
413
414	/* Success */
415	return (0);
416}
417
418/*
419 * The feupdateenv() function saves the currently raised floating-point
420 * exceptions in its automatic storage, installs the floating-point environment
421 * represented by the object pointed to by `envp', and then raises the saved
422 * floating-point exceptions. The argument `envp' shall point to an object set
423 * by a call to feholdexcept() or fegetenv(), or equal a floating-point
424 * environment macro.
425 */
426int
427feupdateenv(const fenv_t *envp)
428{
429	fenv_t fenv;
430	uint32_t mxcsr;
431	uint16_t sw;
432
433	_DIAGASSERT(envp != NULL);
434
435	/* Store the x87 floating-point environment */
436	memset(&fenv, 0, sizeof(fenv));
437	__fnstenv(&fenv);
438
439	__fe_dfl_env.x87.control = (fenv.x87.control & 0xffff0000)
440	    | (__fe_dfl_env.x87.control & 0x0000ffff);
441	__fe_dfl_env.x87.status = (fenv.x87.status & 0xffff0000)
442	    | (__fe_dfl_env.x87.status & 0x0000ffff);
443	__fe_dfl_env.x87.tag = (fenv.x87.tag & 0xffff0000)
444	    | (__fe_dfl_env.x87.tag & 0x0000ffff);
445	__fe_dfl_env.x87.others[3] = (fenv.x87.others[3] & 0xffff0000)
446	    | (__fe_dfl_env.x87.others[3] & 0x0000ffff);
447
448	/* Store the x87 status register */
449	__fnstsw(&sw);
450
451	/* Store the MXCSR register */
452	__stmxcsr(&mxcsr);
453
454	/* Install new floating-point environment */
455	fesetenv(envp);
456
457	/* Raise any previously accumulated exceptions */
458	feraiseexcept((sw | mxcsr) & FE_ALL_EXCEPT);
459
460	/* Success */
461	return (0);
462}
463
464/*
465 * The following functions are extentions to the standard
466 */
467int
468feenableexcept(int mask)
469{
470	uint32_t mxcsr, omask;
471	uint16_t control;
472
473	_DIAGASSERT((mask & ~FE_ALL_EXCEPT) == 0);
474	mask &= FE_ALL_EXCEPT;
475
476	__fnstcw(&control);
477	__stmxcsr(&mxcsr);
478
479	omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
480	control &= ~mask;
481	__fldcw(control);
482
483	mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
484	__ldmxcsr(mxcsr);
485
486	return (FE_ALL_EXCEPT & ~omask);
487
488}
489
490int
491fedisableexcept(int mask)
492{
493	uint32_t mxcsr, omask;
494	uint16_t control;
495
496	_DIAGASSERT((mask & ~FE_ALL_EXCEPT) == 0);
497
498	__fnstcw(&control);
499	__stmxcsr(&mxcsr);
500
501	omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
502	control |= mask;
503	__fldcw(control);
504
505	mxcsr |= mask << _SSE_EMASK_SHIFT;
506	__ldmxcsr(mxcsr);
507
508	return (FE_ALL_EXCEPT & ~omask);
509}
510
511int
512fegetexcept(void)
513{
514	uint16_t control;
515
516	/*
517	 * We assume that the masks for the x87 and the SSE unit are
518	 * the same.
519	 */
520	__fnstcw(&control);
521
522	return (control & FE_ALL_EXCEPT);
523}
524
525