diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -17,9 +17,6 @@ libc.src.ctype.tolower libc.src.ctype.toupper - # errno.h entrypoints - libc.src.errno.__errno_location - # string.h entrypoints libc.src.string.bcmp libc.src.string.bzero diff --git a/libc/src/__support/FPUtil/x86_64/FEnvImpl.h b/libc/src/__support/FPUtil/x86_64/FEnvImpl.h --- a/libc/src/__support/FPUtil/x86_64/FEnvImpl.h +++ b/libc/src/__support/FPUtil/x86_64/FEnvImpl.h @@ -194,8 +194,8 @@ } static inline int get_except() { - uint16_t x87_cw = internal::get_x87_control_word(); - uint16_t enabled_excepts = ~x87_cw & 0x3F; + uint16_t mxcsr = internal::get_mxcsr(); + uint16_t enabled_excepts = ~(mxcsr >> 7) & 0x3F; return internal::exception_status_to_macro(enabled_excepts); } @@ -214,9 +214,8 @@ static inline int test_except(int excepts) { uint16_t status_value = internal::get_status_value_for_except(excepts); // Check both x87 status word and MXCSR. - return internal::exception_status_to_macro( - (status_value & internal::get_x87_status_word()) | - (status_value & internal::get_mxcsr())); + return internal::exception_status_to_macro(status_value & + internal::get_mxcsr()); } // Sets the exception flags but does not trigger the exception handler. @@ -252,9 +251,13 @@ auto raise_helper = [](uint16_t singleExceptFlag) { internal::X87StateDescriptor state; + uint32_t mxcsr = 0; internal::get_x87_state_descriptor(state); + mxcsr = internal::get_mxcsr(); state.status_word |= singleExceptFlag; + mxcsr |= singleExceptFlag; internal::write_x87_state_descriptor(state); + internal::write_mxcsr(mxcsr); internal::fwait(); }; @@ -345,8 +348,8 @@ // MSVC fenv.h defines a very simple representation of the floating point state // which just consists of control and status words of the x87 unit. struct FPState { - uint32_t ControlWord; - uint32_t StatusWord; + uint32_t control_word; + uint32_t status_word; }; #else struct FPState { @@ -364,20 +367,146 @@ #ifdef _WIN32 static inline int get_env(fenv_t *envp) { internal::FPState *state = reinterpret_cast(envp); - internal::X87StateDescriptor X87Status; - internal::getX87StateDescriptor(X87Status); - state->ControlWord = X87Status.ControlWord; - state->StatusWord = X87Status.StatusWord; + + uint32_t status_word = 0; + uint32_t control_word = 0; + + uint32_t mxcsr = internal::get_mxcsr(); + + // Set exception flags in the status word + status_word |= (mxcsr & 0x03) << 4; + status_word |= (mxcsr & 0x04) << 1; + status_word |= (mxcsr & 0x08) >> 1; + status_word |= (mxcsr & 0x10) >> 3; + status_word |= (mxcsr & 0x20) >> 5; + status_word |= status_word << 24; + + // Set exception masks in bits 0-5 and 24-29 + // The masks are also in almost reverse order. + control_word |= (mxcsr & 0x180) >> 3; + control_word |= (mxcsr & 0x200) >> 6; + control_word |= (mxcsr & 0x400) >> 8; + control_word |= (mxcsr & 0x800) >> 10; + control_word |= (mxcsr & 0x1000) >> 12; + control_word |= control_word << 24; + + // Set rounding in bits 6-7 and 30-31 + control_word |= (mxcsr & 0x6000) >> 5; + control_word |= (mxcsr & 0x6000) << 17; + + // Set flush-to-zero in bit 14 + control_word |= (mxcsr & 0x8000) >> 5; + + // Set denormals-are-zero xor flush-to-zero in bit 15 + control_word |= (((mxcsr & 0x8000) >> 9) ^ (mxcsr & 0x0040)) << 5; + + state->control_word = control_word; + state->status_word = status_word; return 0; } static inline int set_env(const fenv_t *envp) { const internal::FPState *state = reinterpret_cast(envp); - internal::X87StateDescriptor X87Status; - X87Status.ControlWord = state->ControlWord; - X87Status.StatusWord = state->StatusWord; - internal::writeX87StateDescriptor(X87Status); + /* + fenv_t control word format: + + Windows (at least for x64) uses a 4 byte control fenv control word stored in + a 32 bit integer. The first byte contains just the rounding mode and the + exception masks, while the last two bytes contain that same information as + well as the flush-to-zero and denormals-are-zero flags. The flags are + represented with a truth table: + + 00 - No flags set + 01 - Flush-to-zero and Denormals-are-zero set + 11 - Flush-to-zero set + 10 - Denormals-are-zero set + + U represents unused. + + +-----Rounding Mode-----+ + | | + ++ ++ + || || + RRMMMMMM UUUUUUUU UUUUFFRR UUMMMMMM + | | || | | + +----+ flags---++ +----+ + | | + +------Exception Masks-----+ + + + fenv_t status word format: + + The status word is a lot simpler for this conversion, since only the + exception flags are used in the MXCSR. + + +----+---Exception Flags---+----+ + | | | | + UUEEEEEE UUUUUUUU UUUUUUUU UUEEEEEE + + + + MXCSR Format: + + The MXCSR format is the same information, just organized differently. Since + the fenv_t struct for windows doesn't include the mxcsr bits, they must be + generated from the control word bits. + + Exception Masks---+ +---Exception Flags + | | + Flush-to-zero---+ +----+ +----+ + | | | | | + FRRMMMMMMDEEEEEE + || | + ++ +---Denormals-are-zero + | + +---Rounding Mode + */ + + uint32_t mxcsr = 0; + + // Set exception flags from the status word + /* + The mask and flag order is as follows: + + fenv_t mxcsr + + denormal precision + invalid underflow + div by 0 overflow + overflow div by 0 + underflow denormal + precision invalid + + This is almost reverse, except for denormal and invalid which are in the + same order in both. + */ + mxcsr |= static_cast((state->status_word & 0x30000000) >> 28); + mxcsr |= static_cast((state->status_word & 0x08000000) >> 25); + mxcsr |= static_cast((state->status_word & 0x04000000) >> 23); + mxcsr |= static_cast((state->status_word & 0x02000000) >> 21); + mxcsr |= static_cast((state->status_word & 0x01000000) >> 19); + + // Set denormals-are-zero from bit 14 xor bit 15 + mxcsr |= static_cast( + (((state->control_word & 0x800) >> 1) ^ (state->control_word & 0x400)) >> + 4); + + // Set exception masks from bits 24-29 + // The masks are also almost in reverse order. + mxcsr |= static_cast((state->control_word & 0x30000000) >> 21); + mxcsr |= static_cast((state->control_word & 0x08000000) >> 18); + mxcsr |= static_cast((state->control_word & 0x04000000) >> 16); + mxcsr |= static_cast((state->control_word & 0x02000000) >> 14); + mxcsr |= static_cast((state->control_word & 0x01000000) >> 12); + + // Set rounding from bits 30-31 + mxcsr |= static_cast((state->control_word & 0xc0000000) >> 17); + + // Set flush-to-zero from bit 14 + mxcsr |= static_cast((state->control_word & 0x400) << 5); + + internal::write_mxcsr(mxcsr); return 0; } #else