mirror of
https://github.com/lifting-bits/remill
synced 2026-06-21 13:56:07 +00:00
Replace cfenv dependency with new remill FPU intrinsics
This commit is contained in:
committed by
Kyle Elliott
parent
ac78aa9dca
commit
eba0652f86
@@ -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`.");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user