Commit | Line | Data |
---|---|---|
49e4877c | 1 | /* Manipulating the FPU control word. -*- coding: utf-8 -*- |
5df4cba6 | 2 | Copyright (C) 2007-2020 Free Software Foundation, Inc. |
88b48903 WN |
3 | Written by Bruno Haible <bruno@clisp.org>, 2007. |
4 | ||
5 | This program is free software: you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 3 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
c0c3707f | 16 | along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
88b48903 WN |
17 | |
18 | #ifndef _FPUCW_H | |
19 | #define _FPUCW_H | |
20 | ||
21 | /* The i386 floating point hardware (the 387 compatible FPU, not the modern | |
22 | SSE/SSE2 hardware) has a controllable rounding precision. It is specified | |
23 | through the 'PC' bits in the FPU control word ('fctrl' register). (See | |
24 | the GNU libc i386 <fpu_control.h> header for details.) | |
25 | ||
26 | On some platforms, such as Linux or Solaris, the default precision setting | |
27 | is set to "extended precision". This means that 'long double' instructions | |
28 | operate correctly, but 'double' computations often produce slightly | |
29 | different results as on strictly IEEE 754 conforming systems. | |
30 | ||
31 | On some platforms, such as NetBSD, the default precision is set to | |
32 | "double precision". This means that 'long double' instructions will operate | |
33 | only as 'double', i.e. lead to wrong results. Similarly on FreeBSD 6.4, at | |
34 | least for the division of 'long double' numbers. | |
35 | ||
36 | The FPU control word is under control of the application, i.e. it is | |
37 | not required to be set either way by the ABI. (In fact, the i386 ABI | |
c0c3707f | 38 | https://www.linux-mips.org/pub/linux/mips/doc/ABI/abi386-4.pdf page 3-12 = page 38 |
88b48903 WN |
39 | is not clear about it. But in any case, gcc treats the control word |
40 | like a "preserved" register: it emits code that assumes that the control | |
41 | word is preserved across calls, and it restores the control word at the | |
42 | end of functions that modify it.) | |
43 | ||
c0c3707f | 44 | See Vincent Lefèvre's page https://www.vinc17.net/research/extended.en.html |
88b48903 | 45 | for a good explanation. |
c0c3707f | 46 | See https://web.archive.org/web/20060905133417/http://www.uwsg.iu.edu/hypermail/linux/kernel/0103.0/0453.html |
88b48903 WN |
47 | some argumentation which setting should be the default. */ |
48 | ||
49 | /* This header file provides the following facilities: | |
50 | fpucw_t integral type holding the value of 'fctrl' | |
51 | FPU_PC_MASK bit mask denoting the precision control | |
52 | FPU_PC_DOUBLE precision control for 53 bits mantissa | |
53 | FPU_PC_EXTENDED precision control for 64 bits mantissa | |
54 | GET_FPUCW () yields the current FPU control word | |
55 | SET_FPUCW (word) sets the FPU control word | |
56 | DECL_LONG_DOUBLE_ROUNDING variable declaration for | |
57 | BEGIN/END_LONG_DOUBLE_ROUNDING | |
58 | BEGIN_LONG_DOUBLE_ROUNDING () starts a sequence of instructions with | |
59 | 'long double' safe operation precision | |
60 | END_LONG_DOUBLE_ROUNDING () ends a sequence of instructions with | |
61 | 'long double' safe operation precision | |
62 | */ | |
63 | ||
64 | /* Inline assembler like this works only with GNU C. */ | |
65 | #if (defined __i386__ || defined __x86_64__) && defined __GNUC__ | |
66 | ||
67 | typedef unsigned short fpucw_t; /* glibc calls this fpu_control_t */ | |
68 | ||
69 | # define FPU_PC_MASK 0x0300 | |
70 | # define FPU_PC_DOUBLE 0x200 /* glibc calls this _FPU_DOUBLE */ | |
71 | # define FPU_PC_EXTENDED 0x300 /* glibc calls this _FPU_EXTENDED */ | |
72 | ||
c0c3707f | 73 | # define GET_FPUCW() __extension__ \ |
88b48903 WN |
74 | ({ fpucw_t _cw; \ |
75 | __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_cw)); \ | |
76 | _cw; \ | |
77 | }) | |
c0c3707f | 78 | # define SET_FPUCW(word) __extension__ \ |
88b48903 WN |
79 | (void)({ fpucw_t _ncw = (word); \ |
80 | __asm__ __volatile__ ("fldcw %0" : : "m" (*&_ncw)); \ | |
81 | }) | |
82 | ||
83 | # define DECL_LONG_DOUBLE_ROUNDING \ | |
84 | fpucw_t oldcw; | |
85 | # define BEGIN_LONG_DOUBLE_ROUNDING() \ | |
86 | (void)(oldcw = GET_FPUCW (), \ | |
87 | SET_FPUCW ((oldcw & ~FPU_PC_MASK) | FPU_PC_EXTENDED)) | |
88 | # define END_LONG_DOUBLE_ROUNDING() \ | |
89 | SET_FPUCW (oldcw) | |
90 | ||
91 | #else | |
92 | ||
93 | typedef unsigned int fpucw_t; | |
94 | ||
95 | # define FPU_PC_MASK 0 | |
96 | # define FPU_PC_DOUBLE 0 | |
97 | # define FPU_PC_EXTENDED 0 | |
98 | ||
99 | # define GET_FPUCW() 0 | |
100 | # define SET_FPUCW(word) (void)(word) | |
101 | ||
102 | # define DECL_LONG_DOUBLE_ROUNDING | |
103 | # define BEGIN_LONG_DOUBLE_ROUNDING() | |
104 | # define END_LONG_DOUBLE_ROUNDING() | |
105 | ||
106 | #endif | |
107 | ||
108 | #endif /* _FPUCW_H */ |