diff --git a/include/remill/Arch/AArch64/Runtime/State.h b/include/remill/Arch/AArch64/Runtime/State.h index b9ee995d..eab272e2 100644 --- a/include/remill/Arch/AArch64/Runtime/State.h +++ b/include/remill/Arch/AArch64/Runtime/State.h @@ -167,35 +167,38 @@ union NZCV { static_assert(8 == sizeof(NZCV), "Invalid packing of `union NZCV`."); -#if COMPILING_WITH_GCC -using FPURoundingMode = uint64_t; -using FPUFlushToZeroMode = uint64_t; -using FPUDefaultNaNMode = uint64_t; -using FPUHalfPrecisionMode = uint64_t; -#else - enum FPURoundingMode : uint64_t { - kFPURoundToNearestEven, // RN (round nearest). - kFPURoundUpInf, // RP (round toward plus infinity). - kFPURoundDownNegInf, // RM (round toward minus infinity). - kFPURoundToZero // RZ (round toward zero). + kFPURoundToNearestEven = 0, // RN (round nearest). + kFPURoundUpInf = 1, // RP (round toward plus infinity). + kFPURoundDownNegInf = 2, // RM (round toward minus infinity). + kFPURoundToZero = 3, // RZ (round toward zero). }; enum FPUFlushToZeroMode : uint64_t { - kFlushToZeroDisabled, - kFlushToZeroEnabled + kFlushToZeroDisabled = 0, + kFlushToZeroEnabled = 1, }; enum FPUDefaultNaNMode : uint64_t { - kPropagateOriginalNaN, - kPropagateDefaultNaN + kPropagateOriginalNaN = 0, + kPropagateDefaultNaN = 1, }; enum FPUHalfPrecisionMode : uint64_t { - kIEEEHalfPrecisionMode, - kAlternativeHalfPrecisionMode + kIEEEHalfPrecisionMode = 0, + kAlternativeHalfPrecisionMode = 1, +}; + +// AArch64 FPSR cumulative exception flags +enum FPUExceptionFlag : uint16_t { + kFPUExceptionInvalid = (1 << 0), // FPSR.ioc, bit 0 - Invalid Operation (FE_INVALID) + kFPUExceptionDivByZero = (1 << 1), // FPSR.dzc, bit 1 - Divide by Zero (FE_DIVBYZERO) + kFPUExceptionOverflow = (1 << 2), // FPSR.ofc, bit 2 - Overflow (FE_OVERFLOW) + kFPUExceptionUnderflow = (1 << 3), // FPSR.ufc, bit 3 - Underflow (FE_UNDERFLOW) + kFPUExceptionPrecision = (1 << 4), // FPSR.ixc, bit 4 - Inexact/Precision (FE_INEXACT) + kFPUExceptionDenormal = (1 << 7), // FPSR.idc, bit 7 - Input Denormal (no standard FE_ equivalent) + kFPUExceptionAll = 0x9F // All exception flags (bits 0-4, 7) }; -#endif // Floating point control register. Really, this is a 32-bit register, but // it is accessed 64-bit register instructions: `mrs , fpcr`. @@ -216,6 +219,8 @@ static_assert(sizeof(FPCR) == 8, "Invalid packing of `union FPCR`."); // Floating point status register. Really, this is a 32-bit register, but // it is accessed 64-bit register instructions: `mrs , fpsr`. +// NOTE: This register is not updated directly, the fields are mirrored in +// the SR register. union FPSR { uint64_t flat; struct { @@ -265,8 +270,10 @@ struct alignas(8) SR final { uint8_t idc; // Input denormal (cumulative). uint8_t _10; uint8_t ioc; // Invalid operation (cumulative). + uint8_t _11; + uint8_t dzc; // Divide by zero (cumulative). - uint8_t _padding[6]; + uint8_t _padding[4]; } __attribute__((packed)); static_assert(56 == sizeof(SR), "Invalid packing of `struct SR`."); diff --git a/include/remill/Arch/Runtime/Float.h b/include/remill/Arch/Runtime/Float.h index 15c4c256..b2c82e54 100644 --- a/include/remill/Arch/Runtime/Float.h +++ b/include/remill/Arch/Runtime/Float.h @@ -37,10 +37,6 @@ # define _RC_CHOP 0x00000300 // chop #endif -#if __has_include() -# include -#endif - #include "Math.h" // macOS does not have this flag diff --git a/include/remill/Arch/Runtime/Intrinsics.h b/include/remill/Arch/Runtime/Intrinsics.h index 2369c589..00c15168 100644 --- a/include/remill/Arch/Runtime/Intrinsics.h +++ b/include/remill/Arch/Runtime/Intrinsics.h @@ -257,17 +257,35 @@ __remill_compare_exchange_memory_128(Memory *, addr_t addr, uint128_t &expected, [[gnu::used]] extern Memory *__remill_fetch_and_nand_64(Memory *, addr_t addr, uint64_t &value); -// Read and modify the floating point exception state of the (virtual) machine -// that is executing the actual floating point operations. -// -// auto old = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT); -// auto y = ...; -// auto res = x op y; -// auto flags = __remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, 0); -// -// These flags are also subject to optimizations -[[gnu::used]] extern int __remill_fpu_exception_test_and_clear(int read_mask, - int clear_mask); +// Read current floating point exception flags. +// Uses architecture-specific FPUExceptionFlag values that are mapped to +// cfenv flags. Typically implemented via std::fetestexcept. +// NOTE: You need to use BarrierReorder around this to avoid reordering bugs. +[[gnu::used]] extern int32_t __remill_fpu_exception_test(int32_t read_mask); + +// Clear floating point exception flags. +// Uses architecture-specific FPUExceptionFlag values that are mapped to +// cfenv flags. Typically implemented via std::feclearexcept. +// NOTE: You need to use BarrierReorder around this to avoid reordering bugs. +[[gnu::used]] extern void __remill_fpu_exception_clear(int32_t clear_mask); + +// Raise floating point exception flags. +// Uses architecture-specific FPUExceptionFlag values that are mapped to +// cfenv flags. Typically implemented via std::feraiseexcept. +// NOTE: You need to use BarrierReorder around this to avoid reordering bugs. +[[gnu::used]] extern void __remill_fpu_exception_raise(int32_t except_mask); + +// Set the floating point rounding mode. +// Uses architecture-specific FPURoundingControl values that are mapped to +// cfenv rounding modes. Typically implemented via std::fesetround. +// NOTE: You need to use BarrierReorder around this to avoid reordering bugs. +[[gnu::used]] extern void __remill_fpu_set_rounding(int32_t round_mode); + +// Get the current floating point rounding mode. +// Returns architecture-specific FPURoundingControl values mapped from +// cfenv rounding modes. Typically implemented via std::fegetround. +// NOTE: You need to use BarrierReorder around this to avoid reordering bugs. +[[gnu::used]] extern int32_t __remill_fpu_get_rounding(); // Read/write to I/O ports. [[gnu::used]] extern uint8_t __remill_read_io_port_8(Memory *, addr_t); diff --git a/include/remill/Arch/Runtime/sysroot/cfenv b/include/remill/Arch/Runtime/sysroot/cfenv deleted file mode 100644 index 316378f3..00000000 --- a/include/remill/Arch/Runtime/sysroot/cfenv +++ /dev/null @@ -1,134 +0,0 @@ -#pragma once - -#ifndef __clang__ -# error Unsupported compiler! -#endif - -// NOTE: These values have to match the hardware for correct semantics - -#if defined(__arm__) - -# define FE_INVALID 1 -# define FE_DIVBYZERO 2 -# define FE_OVERFLOW 4 -# define FE_UNDERFLOW 8 -# define FE_INEXACT 16 -# define FE_ALL_EXCEPT 31 - -# define FE_TONEAREST 0 -# define FE_DOWNWARD 0x800000 -# define FE_UPWARD 0x400000 -# define FE_TOWARDZERO 0xc00000 - -#elif defined(__aarch64__) - -# define FE_INVALID 1 -# define FE_DIVBYZERO 2 -# define FE_OVERFLOW 4 -# define FE_UNDERFLOW 8 -# define FE_INEXACT 16 -# define FE_ALL_EXCEPT 31 - -# define FE_TONEAREST 0 -# define FE_DOWNWARD 0x800000 -# define FE_UPWARD 0x400000 -# define FE_TOWARDZERO 0xc00000 - -#elif defined(__powerpc__) - -# define FE_INEXACT 0x02000000 -# define FE_DIVBYZERO 0x04000000 -# define FE_UNDERFLOW 0x08000000 -# define FE_OVERFLOW 0x10000000 -# define FE_INVALID 0x20000000 -# define FE_ALL_EXCEPT 0x3e000000 - -# define FE_TONEAREST 0 -# define FE_TOWARDZERO 1 -# define FE_UPWARD 2 -# define FE_DOWNWARD 3 - -#elif defined(__sparc__) - -# define FE_INVALID (1 << 9) -# define FE_OVERFLOW (1 << 8) -# define FE_UNDERFLOW (1 << 7) -# define FE_DIVBYZERO (1 << 6) -# define FE_INEXACT (1 << 5) -# define FE_ALL_EXCEPT \ - (FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID) - -# define FE_TONEAREST (0 << 30) -# define FE_TOWARDZERO (1 << 30) -# define FE_UPWARD (-0x7fffffff - 1) /* (2 << 30) */ -# define FE_DOWNWARD (-0x40000000) /* (3 << 30) */ - -#elif defined(__sparc64__) - -# define FE_INVALID (1 << 9) -# define FE_OVERFLOW (1 << 8) -# define FE_UNDERFLOW (1 << 7) -# define FE_DIVBYZERO (1 << 6) -# define FE_INEXACT (1 << 5) -# define FE_ALL_EXCEPT \ - (FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID) - -# define FE_TONEAREST (0 << 30) -# define FE_TOWARDZERO (1 << 30) -# define FE_UPWARD (-0x7fffffff - 1) /* (2 << 30) */ -# define FE_DOWNWARD (-0x40000000) /* (3 << 30) */ - -#elif defined(__i386__) - -# define FE_INVALID 1 -# define __FE_DENORM 2 -# define FE_DIVBYZERO 4 -# define FE_OVERFLOW 8 -# define FE_UNDERFLOW 16 -# define FE_INEXACT 32 - -# define FE_ALL_EXCEPT 63 - -# define FE_TONEAREST 0 -# define FE_DOWNWARD 0x400 -# define FE_UPWARD 0x800 -# define FE_TOWARDZERO 0xc00 - -#elif defined(__x86_64__) - -# define FE_INVALID 1 -# define __FE_DENORM 2 -# define FE_DIVBYZERO 4 -# define FE_OVERFLOW 8 -# define FE_UNDERFLOW 16 -# define FE_INEXACT 32 - -# define FE_ALL_EXCEPT 63 - -# define FE_TONEAREST 0 -# define FE_DOWNWARD 0x400 -# define FE_UPWARD 0x800 -# define FE_TOWARDZERO 0xc00 - -#else -# error Unsupported architecture! -#endif - -// TODO: actually implement these functions? - -namespace std { - -// https://en.cppreference.com/w/cpp/numeric/fenv/feround -int fesetround(int round); -int fegetround(); - -// https://en.cppreference.com/w/cpp/numeric/fenv/fetestexcept -int fetestexcept(int excepts); - -// https://en.cppreference.com/w/c/numeric/fenv/feraiseexcept -int feraiseexcept(int excepts); - -// https://en.cppreference.com/w/c/numeric/fenv/feclearexcept.html -int feclearexcept(int excepts); - -} // namespace std \ No newline at end of file diff --git a/include/remill/Arch/X86/Runtime/State.h b/include/remill/Arch/X86/Runtime/State.h index 49dbe23a..4228258f 100644 --- a/include/remill/Arch/X86/Runtime/State.h +++ b/include/remill/Arch/X86/Runtime/State.h @@ -126,26 +126,34 @@ static_assert(2 == sizeof(FPUStatusWord), "Invalid structure packing of `FPUFlags`."); enum FPUPrecisionControl : uint16_t { - kPrecisionSingle, - kPrecisionReserved, - kPrecisionDouble, - kPrecisionExtended + kPrecisionSingle = 0, + kPrecisionReserved = 1, + kPrecisionDouble = 2, + kPrecisionExtended = 3, }; enum FPURoundingControl : uint16_t { - kFPURoundToNearestEven, - kFPURoundDownNegInf, - kFPURoundUpInf, - kFPURoundToZero + kFPURoundToNearestEven = 0, + kFPURoundDownNegInf = 1, + kFPURoundUpInf = 2, + kFPURoundToZero = 3, }; -enum FPUInfinityControl : uint16_t { kInfinityProjective, kInfinityAffine }; +enum FPUInfinityControl : uint16_t { + kInfinityProjective = 0, + kInfinityAffine = 1, +}; -#ifndef __clang__ -# define FPUPrecisionControl uint16_t -# define FPURoundingControl uint16_t -# define FPUInfinityControl uint16_t -#endif +enum FPUExceptionFlag : uint16_t { + kFPUExceptionInvalid = (1 << 0), // FSW.ie, bit 0 - Invalid Operation (FE_INVALID) + kFPUExceptionDenormal = (1 << 1), // FSW.de, bit 1 - Denormal Operand (FE_DENORMAL) + kFPUExceptionDivByZero = (1 << 2), // FSW.ze, bit 2 - Zero Divide (FE_DIVBYZERO) + kFPUExceptionOverflow = (1 << 3), // FSW.oe, bit 3 - Overflow (FE_OVERFLOW) + kFPUExceptionUnderflow = (1 << 4), // FSW.ue, bit 4 - Underflow (FE_UNDERFLOW) + kFPUExceptionPrecision = (1 << 5), // FSW.pe, bit 5 - Precision/Inexact (FE_INEXACT) + kFPUExceptionStackFault = (1 << 6), // FSW.sf, bit 6 - Stack Fault (no FE_ equivalent, x87-specific) + kFPUExceptionAll = 0x7F // All exception flags (bits 0-6) +}; union FPUControlWord final { uint16_t flat; @@ -369,7 +377,10 @@ struct FPUStatusFlags final { uint8_t _9; uint8_t ie; // Invalid operation. - uint8_t _padding[4]; + uint8_t _10; + uint8_t sf; // Stack overflow. + + uint8_t _padding[2]; } __attribute__((packed)); static_assert(24 == sizeof(FPUStatusFlags), diff --git a/lib/Arch/AArch64/Semantics/BINARY.cpp b/lib/Arch/AArch64/Semantics/BINARY.cpp index c5c3babc..90a15857 100644 --- a/lib/Arch/AArch64/Semantics/BINARY.cpp +++ b/lib/Arch/AArch64/Semantics/BINARY.cpp @@ -272,17 +272,14 @@ DEF_SEM(FMADD_S, V128W dst, V32 src1, V32 src2, V32 src3) { auto old_underflow = state.sr.ufc; - auto zero = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT); + __remill_fpu_exception_clear(kFPUExceptionAll); BarrierReorder(); auto prod = FMul32(factor1, factor2); - BarrierReorder(); - auto except_mul = __remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, zero); - BarrierReorder(); auto res = FAdd32(prod, add); BarrierReorder(); - auto except_add = - __remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, except_mul); - SetFPSRStatusFlags(state, except_add); + auto new_except = __remill_fpu_exception_test(kFPUExceptionAll); + BarrierReorder(); + SetFPSRStatusFlags(state, new_except); // Sets underflow for 0x3fffffff, 0x1 but native doesn't. if (state.sr.ufc && !old_underflow) { @@ -302,17 +299,13 @@ DEF_SEM(FMADD_D, V128W dst, V64 src1, V64 src2, V64 src3) { auto old_underflow = state.sr.ufc; - auto zero = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT); + __remill_fpu_exception_clear(kFPUExceptionAll); BarrierReorder(); auto prod = FMul64(factor1, factor2); - BarrierReorder(); - auto except_mul = __remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, zero); - BarrierReorder(); auto res = FAdd64(prod, add); BarrierReorder(); - auto except_add = - __remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, except_mul); - SetFPSRStatusFlags(state, except_add); + auto except_new = __remill_fpu_exception_test(kFPUExceptionAll); + SetFPSRStatusFlags(state, except_new); // Sets underflow for test case (0x3fffffffffffffff, 0x1) but native doesn't. if (state.sr.ufc && !old_underflow) { diff --git a/lib/Arch/AArch64/Semantics/FLAGS.cpp b/lib/Arch/AArch64/Semantics/FLAGS.cpp index 7676fe69..5fabc9f0 100644 --- a/lib/Arch/AArch64/Semantics/FLAGS.cpp +++ b/lib/Arch/AArch64/Semantics/FLAGS.cpp @@ -159,23 +159,25 @@ struct Carry { }; ALWAYS_INLINE static void SetFPSRStatusFlags(State &state, int mask) { - state.sr.ixc |= static_cast(0 != (mask & FE_INEXACT)); - state.sr.ofc |= static_cast(0 != (mask & FE_OVERFLOW)); - state.sr.ufc |= static_cast(0 != (mask & FE_UNDERFLOW)); - state.sr.ioc |= static_cast(0 != (mask & FE_INVALID)); + state.sr.ioc |= static_cast(0 != (mask & kFPUExceptionInvalid)); + state.sr.dzc |= static_cast(0 != (mask & kFPUExceptionDivByZero)); + state.sr.ofc |= static_cast(0 != (mask & kFPUExceptionOverflow)); + state.sr.ufc |= static_cast(0 != (mask & kFPUExceptionUnderflow)); + state.sr.ixc |= static_cast(0 != (mask & kFPUExceptionPrecision)); + state.sr.idc |= static_cast(0 != (mask & kFPUExceptionDenormal)); } template ALWAYS_INLINE static auto CheckedFloatUnaryOp(State &state, F func, T arg1) -> decltype(func(arg1)) { + // TODO: should this be uncommented? //state.sr.idc |= IsDenormal(arg1); - auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT); + __remill_fpu_exception_clear(kFPUExceptionAll); BarrierReorder(); auto res = func(arg1); BarrierReorder(); - auto new_except = __remill_fpu_exception_test_and_clear( - FE_ALL_EXCEPT, old_except /* zero */); + auto new_except = __remill_fpu_exception_test(kFPUExceptionAll); SetFPSRStatusFlags(state, new_except); return res; } @@ -185,13 +187,13 @@ ALWAYS_INLINE static auto CheckedFloatBinOp(State &state, F func, T arg1, T arg2) -> decltype(func(arg1, arg2)) { + // TODO: should this be uncommented? //state.sr.idc |= IsDenormal(arg1) | IsDenormal(arg2); - auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT); + __remill_fpu_exception_clear(kFPUExceptionAll); BarrierReorder(); auto res = func(arg1, arg2); BarrierReorder(); - auto new_except = __remill_fpu_exception_test_and_clear( - FE_ALL_EXCEPT, old_except /* zero */); + auto new_except = __remill_fpu_exception_test(kFPUExceptionAll); SetFPSRStatusFlags(state, new_except); return res; } diff --git a/lib/Arch/Runtime/Intrinsics.cpp b/lib/Arch/Runtime/Intrinsics.cpp index b83e973c..836aa843 100644 --- a/lib/Arch/Runtime/Intrinsics.cpp +++ b/lib/Arch/Runtime/Intrinsics.cpp @@ -107,7 +107,11 @@ extern "C" [[gnu::used]] void __remill_intrinsics(void) { USED(__remill_fetch_and_xor_32); USED(__remill_fetch_and_xor_64); - USED(__remill_fpu_exception_test_and_clear); + USED(__remill_fpu_exception_test); + USED(__remill_fpu_exception_clear); + USED(__remill_fpu_exception_raise); + USED(__remill_fpu_set_rounding); + USED(__remill_fpu_get_rounding); // USED(__remill_defer_inlining); diff --git a/lib/Arch/X86/Semantics/FLAGS.cpp b/lib/Arch/X86/Semantics/FLAGS.cpp index d107b5b0..79ed13e4 100644 --- a/lib/Arch/X86/Semantics/FLAGS.cpp +++ b/lib/Arch/X86/Semantics/FLAGS.cpp @@ -206,23 +206,24 @@ struct Carry { // X87 status flags are sticky, so we must not unset flags if set. ALWAYS_INLINE static void SetFPSRStatusFlags(State &state, int mask) { - state.sw.pe |= static_cast(0 != (mask & FE_INEXACT)); - state.sw.oe |= static_cast(0 != (mask & FE_OVERFLOW)); - state.sw.ue |= static_cast(0 != (mask & FE_UNDERFLOW)); - state.sw.ie |= static_cast(0 != (mask & FE_INVALID)); - state.sw.ze |= static_cast(0 != (mask & FE_DIVBYZERO)); + state.sw.ie |= static_cast(0 != (mask & kFPUExceptionInvalid)); + state.sw.de |= static_cast(0 != (mask & kFPUExceptionDenormal)); + state.sw.ze |= static_cast(0 != (mask & kFPUExceptionDivByZero)); + state.sw.oe |= static_cast(0 != (mask & kFPUExceptionOverflow)); + state.sw.ue |= static_cast(0 != (mask & kFPUExceptionUnderflow)); + state.sw.pe |= static_cast(0 != (mask & kFPUExceptionPrecision)); + state.sw.sf |= static_cast(0 != (mask & kFPUExceptionStackFault)); } template ALWAYS_INLINE static auto CheckedFloatUnaryOp(State &state, F func, T arg1) -> decltype(func(arg1)) { state.sw.de = IsDenormal(arg1); - auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT); + __remill_fpu_exception_clear(kFPUExceptionAll); BarrierReorder(); auto res = func(arg1); BarrierReorder(); - auto new_except = __remill_fpu_exception_test_and_clear( - FE_ALL_EXCEPT, old_except /* zero */); + auto new_except = __remill_fpu_exception_test(kFPUExceptionAll); SetFPSRStatusFlags(state, new_except); return res; } @@ -232,21 +233,15 @@ ALWAYS_INLINE static auto CheckedFloatUnaryOp2(State &state, F1 func1, F2 func2, T arg1) -> decltype(func2(func1(arg1))) { state.sw.de = IsDenormal(arg1); - auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT); + __remill_fpu_exception_clear(kFPUExceptionAll); BarrierReorder(); auto res1 = func1(arg1); - BarrierReorder(); - auto new_except1 = __remill_fpu_exception_test_and_clear( - FE_ALL_EXCEPT, old_except /* zero */); - - BarrierReorder(); auto res = func2(res1); BarrierReorder(); - auto new_except2 = - __remill_fpu_exception_test_and_clear(FE_ALL_EXCEPT, new_except1); + auto new_except = __remill_fpu_exception_test(kFPUExceptionAll); - SetFPSRStatusFlags(state, new_except1 | new_except2); + SetFPSRStatusFlags(state, new_except); return res; } @@ -255,12 +250,11 @@ ALWAYS_INLINE static auto CheckedFloatBinOp(State &state, F func, T arg1, T arg2) -> decltype(func(arg1, arg2)) { state.sw.de = IsDenormal(arg1) | IsDenormal(arg2); - auto old_except = __remill_fpu_exception_test_and_clear(0, FE_ALL_EXCEPT); + __remill_fpu_exception_clear(kFPUExceptionAll); BarrierReorder(); auto res = func(arg1, arg2); BarrierReorder(); - auto new_except = __remill_fpu_exception_test_and_clear( - FE_ALL_EXCEPT, old_except /* zero */); + auto new_except = __remill_fpu_exception_test(kFPUExceptionAll); SetFPSRStatusFlags(state, new_except); return res; } diff --git a/lib/Arch/X86/Semantics/SSE.cpp b/lib/Arch/X86/Semantics/SSE.cpp index fcc5595e..dfbaa657 100644 --- a/lib/Arch/X86/Semantics/SSE.cpp +++ b/lib/Arch/X86/Semantics/SSE.cpp @@ -2007,20 +2007,19 @@ DEF_SEM(LDMXCSR, M32 src) { auto &csr = state.x87.fxsave.mxcsr; csr.flat = Read(src); - int rounding_mode = FE_TONEAREST; - + FPURoundingControl rounding_mode; if (!csr.rp && !csr.rn) { - rounding_mode = FE_TONEAREST; + rounding_mode = kFPURoundToNearestEven; } else if (!csr.rp && csr.rn) { - rounding_mode = FE_DOWNWARD; + rounding_mode = kFPURoundDownNegInf; } else if (csr.rp && !csr.rn) { - rounding_mode = FE_UPWARD; + rounding_mode = kFPURoundUpInf; } else { - rounding_mode = FE_TOWARDZERO; + rounding_mode = kFPURoundToZero; } - std::fesetround(rounding_mode); + __remill_fpu_set_rounding(rounding_mode); - // TODO: set FPU precision based on MXCSR precision flag (csr.pe) + // TODO: MXCSR precision flag (csr.pe) controls exceptions and is not handled here return memory; } @@ -2028,25 +2027,22 @@ DEF_SEM(LDMXCSR, M32 src) { DEF_SEM(STMXCSR, M32W dst) { auto &csr = state.x87.fxsave.mxcsr; - // TODO: store the current FPU precision control: - csr.pe = 0; - // Store the current FPU rounding mode: - switch (std::fegetround()) { + switch (__remill_fpu_get_rounding()) { default: - case FE_TONEAREST: + case kFPURoundToNearestEven: csr.rp = 0; csr.rn = 0; break; - case FE_DOWNWARD: + case kFPURoundDownNegInf: csr.rp = 0; csr.rn = 1; break; - case FE_UPWARD: + case kFPURoundUpInf: csr.rp = 1; csr.rn = 0; break; - case FE_TOWARDZERO: + case kFPURoundToZero: csr.rp = 1; csr.rn = 1; break; diff --git a/lib/Arch/X86/Semantics/X87.cpp b/lib/Arch/X86/Semantics/X87.cpp index 41180d58..a0b15ee3 100644 --- a/lib/Arch/X86/Semantics/X87.cpp +++ b/lib/Arch/X86/Semantics/X87.cpp @@ -400,12 +400,12 @@ DEF_FPU_SEM(FPU_NOP) { } DEF_SEM(DoFWAIT) { - std::feraiseexcept(std::fetestexcept(FE_ALL_EXCEPT)); + __remill_fpu_exception_clear(__remill_fpu_exception_test(kFPUExceptionAll)); return memory; } DEF_SEM(DoFNCLEX) { - std::feclearexcept(FE_ALL_EXCEPT); + __remill_fpu_exception_clear(kFPUExceptionAll); state.sw.pe = 0; state.sw.ue = 0; state.sw.oe = 0; @@ -1311,13 +1311,7 @@ DEF_SEM(FNSTCW, M16W dst) { auto &cw = state.x87.fxsave.cwd; cw.pc = kPrecisionSingle; - switch (std::fegetround()) { - default: - case FE_TONEAREST: cw.rc = kFPURoundToNearestEven; break; - case FE_DOWNWARD: cw.rc = kFPURoundDownNegInf; break; - case FE_UPWARD: cw.rc = kFPURoundUpInf; break; - case FE_TOWARDZERO: cw.rc = kFPURoundToZero; break; - } + cw.rc = (FPURoundingControl) __remill_fpu_get_rounding(); Write(dst, cw.flat); return memory; } @@ -1326,17 +1320,7 @@ DEF_SEM(FLDCW, M16 cwd) { auto &cw = state.x87.fxsave.cwd; cw.flat = Read(cwd); cw.pc = kPrecisionSingle; - int rounding_mode = FE_TONEAREST; - switch (cw.rc) { - case kFPURoundToNearestEven: rounding_mode = FE_TONEAREST; break; - - case kFPURoundDownNegInf: rounding_mode = FE_DOWNWARD; break; - - case kFPURoundUpInf: rounding_mode = FE_UPWARD; break; - - case kFPURoundToZero: rounding_mode = FE_TOWARDZERO; break; - } - std::fesetround(rounding_mode); + __remill_fpu_set_rounding(cw.rc); return memory; } @@ -1502,10 +1486,10 @@ DEF_SEM(DoFNINIT) { state.x87.fsave.cs.flat = 0x0000; // FPU data operand segment selector // Mask all floating-point exceptions: - std::feclearexcept(FE_ALL_EXCEPT); + __remill_fpu_exception_clear(kFPUExceptionAll); // Set FPU rounding mode to nearest: - std::fesetround(FE_TONEAREST); + __remill_fpu_set_rounding(kFPURoundToNearestEven); // TODO: Set the FPU precision to 64 bits diff --git a/tests/AArch64/Run.cpp b/tests/AArch64/Run.cpp index aa148d79..73ed25ce 100644 --- a/tests/AArch64/Run.cpp +++ b/tests/AArch64/Run.cpp @@ -228,10 +228,99 @@ MAKE_ATOMIC_INTRINSIC(fetch_and_xor, uint, 16) MAKE_ATOMIC_INTRINSIC(fetch_and_xor, uint, 32) MAKE_ATOMIC_INTRINSIC(fetch_and_xor, uint, 64) -int __remill_fpu_exception_test_and_clear(int read_mask, int clear_mask) { - auto except = std::fetestexcept(read_mask); - std::feclearexcept(clear_mask); - return except; +static int MapFpuExceptToFe(int32_t guest_except) { + int host_except = 0; + if (guest_except & kFPUExceptionInvalid) + host_except |= FE_INVALID; + if (guest_except & kFPUExceptionDivByZero) + host_except |= FE_DIVBYZERO; + if (guest_except & kFPUExceptionOverflow) + host_except |= FE_OVERFLOW; + if (guest_except & kFPUExceptionUnderflow) + host_except |= FE_UNDERFLOW; + if (guest_except & kFPUExceptionPrecision) + host_except |= FE_INEXACT; + // NOTE: denormal exception is not available on all architectures +#ifdef FE_DENORMALOPERAND + if (guest_except & kFPUExceptionDenormal) + host_except |= FE_DENORMALOPERAND; +#endif // FE_DENORMALOPERAND +#ifdef FE_DENORMAL + if (guest_except & kFPUExceptionDenormal) + host_except |= FE_DENORMAL; +#endif + return host_except; +} + +static int MapFeToFpuExcept(int host_except) { + int guest_except = 0; + if (host_except & FE_INVALID) + guest_except |= kFPUExceptionInvalid; + if (host_except & FE_DIVBYZERO) + guest_except |= kFPUExceptionDivByZero; + if (host_except & FE_OVERFLOW) + guest_except |= kFPUExceptionOverflow; + if (host_except & FE_UNDERFLOW) + guest_except |= kFPUExceptionUnderflow; + if (host_except & FE_INEXACT) + guest_except |= kFPUExceptionPrecision; + // NOTE: denormal exception is not available on all architectures +#ifdef FE_DENORMALOPERAND + if (host_except & FE_DENORMALOPERAND) + guest_except |= kFPUExceptionDenormal; +#endif // FE_DENORMALOPERAND +#ifdef FE_DENORMAL + if (host_except & FE_DENORMAL) + guest_except |= kFPUExceptionDenormal; +#endif + return guest_except; +} + +static int MapFpuRoundToFe(int32_t guest_round) { + switch (guest_round) { + case kFPURoundToNearestEven: return FE_TONEAREST; + case kFPURoundUpInf: return FE_UPWARD; + case kFPURoundDownNegInf: return FE_DOWNWARD; + case kFPURoundToZero: return FE_TOWARDZERO; + default: return FE_TONEAREST; + } +} + +static int MapFeToFpuRound(int host_round) { + switch (host_round) { + case FE_TONEAREST: return kFPURoundToNearestEven; + case FE_UPWARD: return kFPURoundUpInf; + case FE_DOWNWARD: return kFPURoundDownNegInf; + case FE_TOWARDZERO: return kFPURoundToZero; + default: return kFPURoundToNearestEven; + } +} + +// New intrinsic implementations +int32_t __remill_fpu_exception_test(int32_t read_mask) { + int host_mask = MapFpuExceptToFe(read_mask); + int host_result = std::fetestexcept(host_mask); + return MapFeToFpuExcept(host_result); +} + +void __remill_fpu_exception_clear(int32_t clear_mask) { + int host_mask = MapFpuExceptToFe(clear_mask); + std::feclearexcept(host_mask); +} + +void __remill_fpu_exception_raise(int32_t except_mask) { + int host_mask = MapFpuExceptToFe(except_mask); + std::feraiseexcept(host_mask); +} + +void __remill_fpu_set_rounding(int32_t round_mode) { + int host_mode = MapFpuRoundToFe(round_mode); + std::fesetround(host_mode); +} + +int32_t __remill_fpu_get_rounding() { + int host_mode = std::fegetround(); + return MapFeToFpuRound(host_mode); } Memory *__remill_barrier_load_load(Memory *) { diff --git a/tests/X86/Run.cpp b/tests/X86/Run.cpp index 0b8bb753..12bd8f60 100644 --- a/tests/X86/Run.cpp +++ b/tests/X86/Run.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -286,10 +287,99 @@ MAKE_ATOMIC_INTRINSIC(fetch_and_xor, uint, 16) MAKE_ATOMIC_INTRINSIC(fetch_and_xor, uint, 32) MAKE_ATOMIC_INTRINSIC(fetch_and_xor, uint, 64) -int __remill_fpu_exception_test_and_clear(int read_mask, int clear_mask) { - auto except = std::fetestexcept(read_mask); - std::feclearexcept(clear_mask); - return except; +static int MapFpuExceptToFe(int32_t guest_except) { + int host_except = 0; + if (guest_except & kFPUExceptionInvalid) + host_except |= FE_INVALID; + if (guest_except & kFPUExceptionDivByZero) + host_except |= FE_DIVBYZERO; + if (guest_except & kFPUExceptionOverflow) + host_except |= FE_OVERFLOW; + if (guest_except & kFPUExceptionUnderflow) + host_except |= FE_UNDERFLOW; + if (guest_except & kFPUExceptionPrecision) + host_except |= FE_INEXACT; + // NOTE: denormal exception is not available on all architectures +#ifdef FE_DENORMALOPERAND + if (guest_except & kFPUExceptionDenormal) + host_except |= FE_DENORMALOPERAND; +#endif // FE_DENORMALOPERAND +#ifdef FE_DENORMAL + if (guest_except & kFPUExceptionDenormal) + host_except |= FE_DENORMAL; +#endif + return host_except; +} + +static int MapFeToFpuExcept(int host_except) { + int guest_except = 0; + if (host_except & FE_INVALID) + guest_except |= kFPUExceptionInvalid; + if (host_except & FE_DIVBYZERO) + guest_except |= kFPUExceptionDivByZero; + if (host_except & FE_OVERFLOW) + guest_except |= kFPUExceptionOverflow; + if (host_except & FE_UNDERFLOW) + guest_except |= kFPUExceptionUnderflow; + if (host_except & FE_INEXACT) + guest_except |= kFPUExceptionPrecision; + // NOTE: denormal exception is not available on all architectures +#ifdef FE_DENORMALOPERAND + if (host_except & FE_DENORMALOPERAND) + guest_except |= kFPUExceptionDenormal; +#endif // FE_DENORMALOPERAND +#ifdef FE_DENORMAL + if (host_except & FE_DENORMAL) + guest_except |= kFPUExceptionDenormal; +#endif + return guest_except; +} + +static int MapFpuRoundToFe(int32_t guest_round) { + switch (guest_round) { + case kFPURoundToNearestEven: return FE_TONEAREST; + case kFPURoundUpInf: return FE_UPWARD; + case kFPURoundDownNegInf: return FE_DOWNWARD; + case kFPURoundToZero: return FE_TOWARDZERO; + default: return FE_TONEAREST; + } +} + +static int MapFeToFpuRound(int host_round) { + switch (host_round) { + case FE_TONEAREST: return kFPURoundToNearestEven; + case FE_UPWARD: return kFPURoundUpInf; + case FE_DOWNWARD: return kFPURoundDownNegInf; + case FE_TOWARDZERO: return kFPURoundToZero; + default: return kFPURoundToNearestEven; + } +} + +// New intrinsic implementations +int32_t __remill_fpu_exception_test(int32_t read_mask) { + int host_mask = MapFpuExceptToFe(read_mask); + int host_result = std::fetestexcept(host_mask); + return MapFeToFpuExcept(host_result); +} + +void __remill_fpu_exception_clear(int32_t clear_mask) { + int host_mask = MapFpuExceptToFe(clear_mask); + std::feclearexcept(host_mask); +} + +void __remill_fpu_exception_raise(int32_t except_mask) { + int host_mask = MapFpuExceptToFe(except_mask); + std::feraiseexcept(host_mask); +} + +void __remill_fpu_set_rounding(int32_t round_mode) { + int host_mode = MapFpuRoundToFe(round_mode); + std::fesetround(host_mode); +} + +int32_t __remill_fpu_get_rounding() { + int host_mode = std::fegetround(); + return MapFeToFpuRound(host_mode); } Memory *__remill_barrier_load_load(Memory *) { @@ -898,6 +988,7 @@ static void RunWithFlags(const test::TestInfo *info, Flags flags, lifted_state->x87.fxsave.swd.oe = lifted_state->sw.oe; lifted_state->x87.fxsave.swd.ue = lifted_state->sw.ue; lifted_state->x87.fxsave.swd.pe = lifted_state->sw.pe; + lifted_state->x87.fxsave.swd.sf = lifted_state->sw.sf; lifted_state->x87.fxsave.swd.flat = 0; native_state->x87.fxsave.swd.flat = 0;