Skip to content

Commit 00ef931

Browse files
RealKCawesomekling
authored andcommitted
LibC: Implement fenv.h
1 parent 53c4be9 commit 00ef931

File tree

3 files changed

+315
-0
lines changed

3 files changed

+315
-0
lines changed

Userland/Libraries/LibC/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ set(LIBC_SOURCES
66
dirent.cpp
77
dlfcn.cpp
88
fcntl.cpp
9+
fenv.cpp
910
getopt.cpp
1011
grp.cpp
1112
ioctl.cpp

Userland/Libraries/LibC/fenv.cpp

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/*
2+
* Copyright (c) 2021, Mițca Dumitru <dumitru0mitca@gmail.com>
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright notice, this
9+
* list of conditions and the following disclaimer.
10+
*
11+
* 2. Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following disclaimer in the documentation
13+
* and/or other materials provided with the distribution.
14+
*
15+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
*/
26+
27+
#include <AK/Types.h>
28+
#include <fenv.h>
29+
30+
// This is the size of the floating point envinronment image in protected mode
31+
static_assert(sizeof(__x87_floating_point_environment) == 28);
32+
33+
static u16 read_status_register()
34+
{
35+
u16 status_register;
36+
asm volatile("fstsw %0"
37+
: "=m"(status_register));
38+
return status_register;
39+
}
40+
41+
static u16 read_control_word()
42+
{
43+
u16 control_word;
44+
asm volatile("fstcw %0"
45+
: "=m"(control_word));
46+
return control_word;
47+
}
48+
49+
static void set_control_word(u16 new_control_word)
50+
{
51+
asm volatile("fldcw %0" ::"m"(new_control_word));
52+
}
53+
54+
static u32 read_mxcsr()
55+
{
56+
u32 mxcsr;
57+
asm volatile("stmxcsr %0"
58+
: "=m"(mxcsr));
59+
return mxcsr;
60+
}
61+
62+
static void set_mxcsr(u32 new_mxcsr)
63+
{
64+
asm volatile("ldmxcsr %0" ::"m"(new_mxcsr));
65+
}
66+
67+
static constexpr u32 default_mxcsr_value = 0x1f80;
68+
69+
extern "C" {
70+
71+
int fegetenv(fenv_t* env)
72+
{
73+
if (!env)
74+
return 1;
75+
76+
asm volatile("fstenv %0"
77+
: "=m"(env->__x87_fpu_env)::"memory");
78+
79+
env->__mxcsr = read_mxcsr();
80+
81+
return 0;
82+
}
83+
84+
int fesetenv(const fenv_t* env)
85+
{
86+
if (!env)
87+
return 1;
88+
89+
if (env == FE_DFL_ENV) {
90+
asm volatile("finit");
91+
set_mxcsr(default_mxcsr_value);
92+
return 0;
93+
}
94+
95+
asm volatile("fldenv %0" ::"m"(env)
96+
: "memory");
97+
98+
set_mxcsr(env->__mxcsr);
99+
100+
return 0;
101+
}
102+
103+
int feholdexcept(fenv_t* env)
104+
{
105+
fegetenv(env);
106+
107+
fenv_t current_env;
108+
fegetenv(&current_env);
109+
110+
current_env.__x87_fpu_env.__status_word &= ~FE_ALL_EXCEPT;
111+
current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit
112+
current_env.__x87_fpu_env.__control_word &= FE_ALL_EXCEPT; // Masking these bits stops the corresponding exceptions from being generated according to the Intel Programmer's Manual
113+
114+
fesetenv(&current_env);
115+
116+
return 0;
117+
}
118+
119+
int feupdateenv(const fenv_t* env)
120+
{
121+
auto currently_raised_exceptions = fetestexcept(FE_ALL_EXCEPT);
122+
123+
fesetenv(env);
124+
feraiseexcept(currently_raised_exceptions);
125+
126+
return 0;
127+
}
128+
129+
int fegetexceptflag(fexcept_t* except, int exceptions)
130+
{
131+
if (!except)
132+
return 1;
133+
*except = (uint16_t)fetestexcept(exceptions);
134+
return 0;
135+
}
136+
int fesetexceptflag(const fexcept_t* except, int exceptions)
137+
{
138+
if (!except)
139+
return 1;
140+
141+
fenv_t current_env;
142+
fegetenv(&current_env);
143+
144+
exceptions &= FE_ALL_EXCEPT;
145+
current_env.__x87_fpu_env.__status_word &= exceptions;
146+
current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Make sure exceptions don't get raised
147+
148+
fesetenv(&current_env);
149+
return 0;
150+
}
151+
152+
int fegetround()
153+
{
154+
// There's no way to signal whether the SSE rounding mode and x87 ones are different, so we assume they're the same
155+
return (read_status_register() >> 10) & 3;
156+
}
157+
158+
int fesetround(int rounding_mode)
159+
{
160+
if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOWARDSZERO)
161+
return 1;
162+
163+
auto control_word = read_control_word();
164+
165+
control_word &= ~(3 << 10);
166+
control_word |= rounding_mode << 10;
167+
168+
set_control_word(control_word);
169+
170+
auto mxcsr = read_mxcsr();
171+
172+
mxcsr &= ~(3 << 13);
173+
mxcsr |= rounding_mode << 13;
174+
175+
set_mxcsr(mxcsr);
176+
177+
return 0;
178+
}
179+
180+
int feclearexcepts(int exceptions)
181+
{
182+
exceptions &= FE_ALL_EXCEPT;
183+
184+
fenv_t current_env;
185+
fegetenv(&current_env);
186+
187+
current_env.__x87_fpu_env.__status_word &= ~exceptions;
188+
current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit
189+
190+
fesetenv(&current_env);
191+
return 0;
192+
}
193+
194+
int fetestexcept(int exceptions)
195+
{
196+
u16 status_register = read_status_register() & FE_ALL_EXCEPT;
197+
exceptions &= FE_ALL_EXCEPT;
198+
199+
return status_register & exceptions;
200+
}
201+
202+
int feraiseexcept(int exceptions)
203+
{
204+
fenv_t env;
205+
fegetenv(&env);
206+
207+
exceptions &= FE_ALL_EXCEPT;
208+
209+
// While the order in which the exceptions is raised is unspecified, FE_OVERFLOW and FE_UNDERFLOW must be raised before FE_INEXACT, so handle that case in this branch
210+
if (exceptions & FE_INEXACT) {
211+
env.__x87_fpu_env.__status_word &= ((u16)exceptions & ~FE_INEXACT);
212+
fesetenv(&env);
213+
asm volatile("fwait"); // "raise" the exception by performing a floating point operation
214+
215+
fegetenv(&env);
216+
env.__x87_fpu_env.__status_word &= FE_INEXACT;
217+
fesetenv(&env);
218+
asm volatile("fwait");
219+
220+
return 0;
221+
}
222+
223+
env.__x87_fpu_env.__status_word &= exceptions;
224+
fesetenv(&env);
225+
asm volatile("fwait");
226+
227+
return 0;
228+
}
229+
}

Userland/Libraries/LibC/fenv.h

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (c) 2021, Mițca Dumitru <dumitru0mitca@gmail.com>
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright notice, this
9+
* list of conditions and the following disclaimer.
10+
*
11+
* 2. Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following disclaimer in the documentation
13+
* and/or other materials provided with the distribution.
14+
*
15+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
*/
26+
27+
#pragma once
28+
29+
#include <stdint.h>
30+
#include <sys/cdefs.h>
31+
32+
__BEGIN_DECLS
33+
34+
struct __x87_floating_point_environment {
35+
uint16_t __control_word;
36+
uint16_t __reserved1;
37+
uint16_t __status_word;
38+
uint16_t __reserved2;
39+
uint16_t __tag_word;
40+
uint16_t __reserved3;
41+
uint32_t __fpu_ip_offset;
42+
uint16_t __fpu_ip_selector;
43+
uint16_t __opcode : 11;
44+
uint16_t __reserved4 : 5;
45+
uint32_t __fpu_data_offset;
46+
uint16_t __fpu_data_selector;
47+
uint16_t __reserved5;
48+
};
49+
50+
typedef struct fenv_t {
51+
struct __x87_floating_point_environment __x87_fpu_env;
52+
uint32_t __mxcsr;
53+
} fenv_t;
54+
55+
#define FE_DFL_ENV ((const fenv_t*)-1)
56+
57+
int fegetenv(fenv_t*);
58+
int fesetenv(const fenv_t*);
59+
int feholdexcept(fenv_t*);
60+
int feupdateenv(const fenv_t*);
61+
62+
#define FE_INVALID 1u << 0
63+
#define FE_DIVBYZERO 1u << 2
64+
#define FE_OVERFLOW 1u << 3
65+
#define FE_UNDERFLOW 1u << 4
66+
#define FE_INEXACT 1u << 5
67+
#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW)
68+
69+
typedef uint16_t fexcept_t;
70+
int fegetexceptflag(fexcept_t*, int exceptions);
71+
int fesetexceptflag(const fexcept_t*, int exceptions);
72+
73+
int feclearexcepts(int exceptions);
74+
int fetestexcept(int exceptions);
75+
int feraiseexcept(int exceptions);
76+
77+
#define FE_TONEAREST 0
78+
#define FE_DOWNWARD 1
79+
#define FE_UPWARD 2
80+
#define FE_TOWARDSZERO 3
81+
82+
int fesetround(int round);
83+
int fegetround(void);
84+
85+
__END_DECLS

0 commit comments

Comments
 (0)