Skip to content

Commit

Permalink
LibC: Implement fenv.h
Browse files Browse the repository at this point in the history
  • Loading branch information
RealKC authored and awesomekling committed Mar 1, 2021
1 parent 53c4be9 commit 00ef931
Show file tree
Hide file tree
Showing 3 changed files with 315 additions and 0 deletions.
1 change: 1 addition & 0 deletions Userland/Libraries/LibC/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set(LIBC_SOURCES
dirent.cpp
dlfcn.cpp
fcntl.cpp
fenv.cpp
getopt.cpp
grp.cpp
ioctl.cpp
Expand Down
229 changes: 229 additions & 0 deletions Userland/Libraries/LibC/fenv.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
* Copyright (c) 2021, Mițca Dumitru <[email protected]>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <AK/Types.h>
#include <fenv.h>

// This is the size of the floating point envinronment image in protected mode
static_assert(sizeof(__x87_floating_point_environment) == 28);

static u16 read_status_register()
{
u16 status_register;
asm volatile("fstsw %0"
: "=m"(status_register));
return status_register;
}

static u16 read_control_word()
{
u16 control_word;
asm volatile("fstcw %0"
: "=m"(control_word));
return control_word;
}

static void set_control_word(u16 new_control_word)
{
asm volatile("fldcw %0" ::"m"(new_control_word));
}

static u32 read_mxcsr()
{
u32 mxcsr;
asm volatile("stmxcsr %0"
: "=m"(mxcsr));
return mxcsr;
}

static void set_mxcsr(u32 new_mxcsr)
{
asm volatile("ldmxcsr %0" ::"m"(new_mxcsr));
}

static constexpr u32 default_mxcsr_value = 0x1f80;

extern "C" {

int fegetenv(fenv_t* env)
{
if (!env)
return 1;

asm volatile("fstenv %0"
: "=m"(env->__x87_fpu_env)::"memory");

env->__mxcsr = read_mxcsr();

return 0;
}

int fesetenv(const fenv_t* env)
{
if (!env)
return 1;

if (env == FE_DFL_ENV) {
asm volatile("finit");
set_mxcsr(default_mxcsr_value);
return 0;
}

asm volatile("fldenv %0" ::"m"(env)
: "memory");

set_mxcsr(env->__mxcsr);

return 0;
}

int feholdexcept(fenv_t* env)
{
fegetenv(env);

fenv_t current_env;
fegetenv(&current_env);

current_env.__x87_fpu_env.__status_word &= ~FE_ALL_EXCEPT;
current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit
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

fesetenv(&current_env);

return 0;
}

int feupdateenv(const fenv_t* env)
{
auto currently_raised_exceptions = fetestexcept(FE_ALL_EXCEPT);

fesetenv(env);
feraiseexcept(currently_raised_exceptions);

return 0;
}

int fegetexceptflag(fexcept_t* except, int exceptions)
{
if (!except)
return 1;
*except = (uint16_t)fetestexcept(exceptions);
return 0;
}
int fesetexceptflag(const fexcept_t* except, int exceptions)
{
if (!except)
return 1;

fenv_t current_env;
fegetenv(&current_env);

exceptions &= FE_ALL_EXCEPT;
current_env.__x87_fpu_env.__status_word &= exceptions;
current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Make sure exceptions don't get raised

fesetenv(&current_env);
return 0;
}

int fegetround()
{
// There's no way to signal whether the SSE rounding mode and x87 ones are different, so we assume they're the same
return (read_status_register() >> 10) & 3;
}

int fesetround(int rounding_mode)
{
if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOWARDSZERO)
return 1;

auto control_word = read_control_word();

control_word &= ~(3 << 10);
control_word |= rounding_mode << 10;

set_control_word(control_word);

auto mxcsr = read_mxcsr();

mxcsr &= ~(3 << 13);
mxcsr |= rounding_mode << 13;

set_mxcsr(mxcsr);

return 0;
}

int feclearexcepts(int exceptions)
{
exceptions &= FE_ALL_EXCEPT;

fenv_t current_env;
fegetenv(&current_env);

current_env.__x87_fpu_env.__status_word &= ~exceptions;
current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit

fesetenv(&current_env);
return 0;
}

int fetestexcept(int exceptions)
{
u16 status_register = read_status_register() & FE_ALL_EXCEPT;
exceptions &= FE_ALL_EXCEPT;

return status_register & exceptions;
}

int feraiseexcept(int exceptions)
{
fenv_t env;
fegetenv(&env);

exceptions &= FE_ALL_EXCEPT;

// 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
if (exceptions & FE_INEXACT) {
env.__x87_fpu_env.__status_word &= ((u16)exceptions & ~FE_INEXACT);
fesetenv(&env);
asm volatile("fwait"); // "raise" the exception by performing a floating point operation

fegetenv(&env);
env.__x87_fpu_env.__status_word &= FE_INEXACT;
fesetenv(&env);
asm volatile("fwait");

return 0;
}

env.__x87_fpu_env.__status_word &= exceptions;
fesetenv(&env);
asm volatile("fwait");

return 0;
}
}
85 changes: 85 additions & 0 deletions Userland/Libraries/LibC/fenv.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2021, Mițca Dumitru <[email protected]>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#pragma once

#include <stdint.h>
#include <sys/cdefs.h>

__BEGIN_DECLS

struct __x87_floating_point_environment {
uint16_t __control_word;
uint16_t __reserved1;
uint16_t __status_word;
uint16_t __reserved2;
uint16_t __tag_word;
uint16_t __reserved3;
uint32_t __fpu_ip_offset;
uint16_t __fpu_ip_selector;
uint16_t __opcode : 11;
uint16_t __reserved4 : 5;
uint32_t __fpu_data_offset;
uint16_t __fpu_data_selector;
uint16_t __reserved5;
};

typedef struct fenv_t {
struct __x87_floating_point_environment __x87_fpu_env;
uint32_t __mxcsr;
} fenv_t;

#define FE_DFL_ENV ((const fenv_t*)-1)

int fegetenv(fenv_t*);
int fesetenv(const fenv_t*);
int feholdexcept(fenv_t*);
int feupdateenv(const fenv_t*);

#define FE_INVALID 1u << 0
#define FE_DIVBYZERO 1u << 2
#define FE_OVERFLOW 1u << 3
#define FE_UNDERFLOW 1u << 4
#define FE_INEXACT 1u << 5
#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW)

typedef uint16_t fexcept_t;
int fegetexceptflag(fexcept_t*, int exceptions);
int fesetexceptflag(const fexcept_t*, int exceptions);

int feclearexcepts(int exceptions);
int fetestexcept(int exceptions);
int feraiseexcept(int exceptions);

#define FE_TONEAREST 0
#define FE_DOWNWARD 1
#define FE_UPWARD 2
#define FE_TOWARDSZERO 3

int fesetround(int round);
int fegetround(void);

__END_DECLS

0 comments on commit 00ef931

Please sign in to comment.