Replace cfenv dependency with new remill FPU intrinsics

This commit is contained in:
Duncan Ogilvie
2025-10-28 16:36:24 +01:00
committed by Kyle Elliott
parent ac78aa9dca
commit eba0652f86
13 changed files with 325 additions and 274 deletions
+26 -19
View File
@@ -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 <Xt>, 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 <Xt>, 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`.");
-4
View File
@@ -37,10 +37,6 @@
# define _RC_CHOP 0x00000300 // chop
#endif
#if __has_include(<cfenv>)
# include <cfenv>
#endif
#include "Math.h"
// macOS does not have this flag
+29 -11
View File
@@ -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);
-134
View File
@@ -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
+26 -15
View File
@@ -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),
+7 -14
View File
@@ -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) {
+12 -10
View File
@@ -159,23 +159,25 @@ struct Carry<tag_sub> {
};
ALWAYS_INLINE static void SetFPSRStatusFlags(State &state, int mask) {
state.sr.ixc |= static_cast<uint8_t>(0 != (mask & FE_INEXACT));
state.sr.ofc |= static_cast<uint8_t>(0 != (mask & FE_OVERFLOW));
state.sr.ufc |= static_cast<uint8_t>(0 != (mask & FE_UNDERFLOW));
state.sr.ioc |= static_cast<uint64_t>(0 != (mask & FE_INVALID));
state.sr.ioc |= static_cast<uint8_t>(0 != (mask & kFPUExceptionInvalid));
state.sr.dzc |= static_cast<uint8_t>(0 != (mask & kFPUExceptionDivByZero));
state.sr.ofc |= static_cast<uint8_t>(0 != (mask & kFPUExceptionOverflow));
state.sr.ufc |= static_cast<uint8_t>(0 != (mask & kFPUExceptionUnderflow));
state.sr.ixc |= static_cast<uint8_t>(0 != (mask & kFPUExceptionPrecision));
state.sr.idc |= static_cast<uint8_t>(0 != (mask & kFPUExceptionDenormal));
}
template <typename F, typename T>
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;
}
+5 -1
View File
@@ -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);
+14 -20
View File
@@ -206,23 +206,24 @@ struct Carry<tag_sub> {
// 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<uint8_t>(0 != (mask & FE_INEXACT));
state.sw.oe |= static_cast<uint8_t>(0 != (mask & FE_OVERFLOW));
state.sw.ue |= static_cast<uint8_t>(0 != (mask & FE_UNDERFLOW));
state.sw.ie |= static_cast<uint8_t>(0 != (mask & FE_INVALID));
state.sw.ze |= static_cast<uint8_t>(0 != (mask & FE_DIVBYZERO));
state.sw.ie |= static_cast<uint8_t>(0 != (mask & kFPUExceptionInvalid));
state.sw.de |= static_cast<uint8_t>(0 != (mask & kFPUExceptionDenormal));
state.sw.ze |= static_cast<uint8_t>(0 != (mask & kFPUExceptionDivByZero));
state.sw.oe |= static_cast<uint8_t>(0 != (mask & kFPUExceptionOverflow));
state.sw.ue |= static_cast<uint8_t>(0 != (mask & kFPUExceptionUnderflow));
state.sw.pe |= static_cast<uint8_t>(0 != (mask & kFPUExceptionPrecision));
state.sw.sf |= static_cast<uint8_t>(0 != (mask & kFPUExceptionStackFault));
}
template <typename F, typename T>
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;
}
+12 -16
View File
@@ -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;
+6 -22
View File
@@ -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
+93 -4
View File
@@ -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 *) {
+95 -4
View File
@@ -24,6 +24,7 @@
#include <signal.h>
#include <ucontext.h>
#include <cfenv>
#include <cmath>
#include <cstdint>
#include <cstdlib>
@@ -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;