From 936db64b75379653b8a47b19fa2ca660af3bf81d Mon Sep 17 00:00:00 2001 From: Artem Dinaburg Date: Fri, 6 Aug 2021 14:17:06 -0700 Subject: [PATCH] Futher attempts at 80-bit floats --- include/remill/Arch/Runtime/Intrinsics.h | 2 ++ include/remill/Arch/Runtime/Operators.h | 33 +++++++++++++++++------ include/remill/Arch/Runtime/Types.h | 34 ++++++++++++++++++++++++ include/remill/Arch/X86/Runtime/Types.h | 4 +-- include/remill/BC/IntrinsicTable.h | 1 + lib/Arch/Runtime/Intrinsics.cpp | 1 + lib/Arch/X86/Semantics/MMX.cpp | 16 +++++------ lib/BC/IntrinsicTable.cpp | 3 ++- tests/AArch64/Run.cpp | 4 +++ tests/X86/Run.cpp | 4 +++ 10 files changed, 83 insertions(+), 19 deletions(-) diff --git a/include/remill/Arch/Runtime/Intrinsics.h b/include/remill/Arch/Runtime/Intrinsics.h index 3ae6ea590..7d2b9ca0f 100644 --- a/include/remill/Arch/Runtime/Intrinsics.h +++ b/include/remill/Arch/Runtime/Intrinsics.h @@ -85,6 +85,8 @@ __remill_write_memory_f64(Memory *, addr_t, float64_t); [[gnu::used, gnu::const]] extern float64_t __remill_undefined_f64(void); +[[gnu::used, gnu::const]] extern float80_t __remill_undefined_f80(void); + // Generic error. [[gnu::used]] extern Memory *__remill_error(State &, addr_t addr, Memory *); diff --git a/include/remill/Arch/Runtime/Operators.h b/include/remill/Arch/Runtime/Operators.h index ba399bd49..60ad2686f 100644 --- a/include/remill/Arch/Runtime/Operators.h +++ b/include/remill/Arch/Runtime/Operators.h @@ -106,6 +106,10 @@ ALWAYS_INLINE static float64_t _Read(Memory *, float64_t val) { return val; } +ALWAYS_INLINE static float80_t _Read(Memory *, float80_t val) { + return val; +} + ALWAYS_INLINE static float32_t _Read(Memory *, In imm) { return reinterpret_cast(imm.val); } @@ -114,6 +118,10 @@ ALWAYS_INLINE static float64_t _Read(Memory *, In imm) { return reinterpret_cast(imm.val); } +ALWAYS_INLINE static float80_t _Read(Memory *, In imm) { + return reinterpret_cast(imm.val); +} + template ALWAYS_INLINE static T _Read(Memory *, In imm) { return static_cast(imm.val); @@ -149,7 +157,7 @@ MAKE_MREAD(128, 128, uint, 128) MAKE_MREAD(32, 32, float, f32) MAKE_MREAD(64, 64, float, f64) -MAKE_MREAD(80, 64, float, f80) +MAKE_MREAD(80, 80, float, f80) #undef MAKE_MREAD @@ -174,6 +182,7 @@ MAKE_RWRITE(uint32_t) MAKE_RWRITE(uint64_t) MAKE_RWRITE(float32_t) MAKE_RWRITE(float64_t) +MAKE_RWRITE(float80_t) #undef MAKE_RWRITE @@ -193,7 +202,7 @@ MAKE_MWRITE(128, 128, uint, uint, 128) MAKE_MWRITE(32, 32, float, float, f32) MAKE_MWRITE(64, 64, float, float, f64) -MAKE_MWRITE(80, 64, float, float, f80) +MAKE_MWRITE(80, 80, float, float, f80) #undef MAKE_MWRITE @@ -222,6 +231,7 @@ MAKE_READRV(S, 64, sqwords, int64_t) MAKE_READRV(F, 32, floats, float32_t) MAKE_READRV(F, 64, doubles, float64_t) +MAKE_READRV(F, 80, tdoubles, float80_t) #undef MAKE_READRV @@ -252,6 +262,7 @@ MAKE_READV(S, 128, sdqwords) MAKE_READV(F, 32, floats) MAKE_READV(F, 64, doubles) +MAKE_READV(F, 80, tdouble) #undef MAKE_READV @@ -295,6 +306,7 @@ MAKE_MREADV(S, 128, sdqwords, s128) MAKE_MREADV(F, 32, floats, f32) MAKE_MREADV(F, 64, doubles, f64) +MAKE_MREADV(F, 80, tdoubles, f80) #undef MAKE_MREADV @@ -343,6 +355,7 @@ MAKE_WRITEV(S, 128, sdqwords, VnW, int128_t) MAKE_WRITEV(F, 32, floats, VnW, float32_t) MAKE_WRITEV(F, 64, doubles, VnW, float64_t) +MAKE_WRITEV(F, 80, tdoubles, VnW, float80_t) MAKE_WRITEV(U, 8, bytes, RVnW, uint8_t) MAKE_WRITEV(U, 16, words, RVnW, uint16_t) @@ -356,6 +369,7 @@ MAKE_WRITEV(S, 64, sqwords, RVnW, int64_t) MAKE_WRITEV(F, 32, floats, RVnW, float32_t) MAKE_WRITEV(F, 64, doubles, RVnW, float64_t) +MAKE_WRITEV(F, 80, tdoubles, RVnW, float80_t) #undef MAKE_WRITEV @@ -404,6 +418,7 @@ MAKE_MWRITEV(S, 128, sdqwords, s128, int128_t) MAKE_MWRITEV(F, 32, floats, f32, float32_t) MAKE_MWRITEV(F, 64, doubles, f64, float64_t) +MAKE_MWRITEV(F, 80, tdoubles, f80, float80_t) #undef MAKE_MWRITEV @@ -421,6 +436,7 @@ MAKE_WRITE_REF(uint64_t) MAKE_WRITE_REF(uint128_t) MAKE_WRITE_REF(float32_t) MAKE_WRITE_REF(float64_t) +MAKE_WRITE_REF(float80_t) #undef MAKE_WRITE_REF @@ -903,12 +919,10 @@ ALWAYS_INLINE static auto TruncTo(T val) -> typename IntegerType
::BT { op) make_int_op(S##name, int128_t, int128_t, op) \ make_int_op(S##name##128, int128_t, int128_t, op) \ make_float_op(F##name, float32_t, float32_t, op) \ - make_float_op( \ - F##name##32, float32_t, float32_t, \ - op) make_float_op(F##name, float64_t, \ - float64_t, op) \ - make_float_op(F##name##64, float64_t, \ - float64_t, op) + make_float_op(F##name##32, float32_t, float32_t, op) \ + make_float_op(F##name, float64_t, float64_t, op) \ + make_float_op(F##name##64, float64_t, float64_t, op) \ + make_float_op(F##name##80, float80_t, float80_t, op) MAKE_OPS(Add, +, MAKE_BINOP, MAKE_BINOP) MAKE_OPS(Sub, -, MAKE_BINOP, MAKE_BINOP) @@ -1086,6 +1100,7 @@ MAKE_EXTRACTV(64, int64_t, qwords, Signed, S) MAKE_EXTRACTV(128, int128_t, dqwords, Signed, S) MAKE_EXTRACTV(32, float32_t, floats, Identity, F) MAKE_EXTRACTV(64, float64_t, doubles, Identity, F) +MAKE_EXTRACTV(80, float80_t, tdoubles, Identity, F) #undef MAKE_EXTRACTV @@ -1158,6 +1173,7 @@ MAKE_INSERTV(S, 128, int128_t, sdqwords) MAKE_INSERTV(F, 32, float32_t, floats) MAKE_INSERTV(F, 64, float64_t, doubles) +MAKE_INSERTV(F, 80, float80_t, tdoubles) #undef MAKE_INSERTV @@ -1185,6 +1201,7 @@ MAKE_UPDATEV(S, 128, int128_t, sdqwords) MAKE_UPDATEV(F, 32, float32_t, floats) MAKE_UPDATEV(F, 64, float64_t, doubles) +MAKE_UPDATEV(F, 80, float80_t, tdoubles) #undef MAKE_UPDATEV diff --git a/include/remill/Arch/Runtime/Types.h b/include/remill/Arch/Runtime/Types.h index e5b5951a4..c1bd1a87e 100644 --- a/include/remill/Arch/Runtime/Types.h +++ b/include/remill/Arch/Runtime/Types.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include @@ -70,6 +71,39 @@ static_assert(8 == sizeof(float128_t), "Invalid `float128_t` size."); // TODO(pag): Assumes little endian. struct float80_t final { uint8_t data[10]; +#if defined(__x86_64__) || defined(__i386__) || defined(_M_X86) + // convert a long double into an f80 representation on x86/x86-64 + // this assumes long double uses the f80 format internally, but + // is simply aligned to an even boundary (hence size 12 or 16) + float80_t(long double ld) { + static_assert(12 == sizeof(long double) || 16 == sizeof(long double), "Invalid `long double` size."); + union ld_union { + long double ld_val; + struct { + char pad[sizeof(long double) - sizeof(float80_t)]; + float80_t f80; + } __attribute__((packed)) f80_data; + }; + + ld_union ldu {.ld_val = ld}; + std::memcpy(&data[0], &ldu.f80_data.f80.data[0], sizeof(data)); + } + + operator long double() const { + static_assert(12 == sizeof(long double) || 16 == sizeof(long double), "Invalid `long double` size."); + union ld_union { + long double ld_val; + struct { + char pad[sizeof(long double) - sizeof(float80_t)]; + float80_t f80; + } __attribute__((packed)) f80_data; + }; + + ld_union ldu {0}; + std::memcpy(&ldu.f80_data.f80.data[0], &data[0], sizeof(data)); + return ldu.ld_val; + } +#endif } __attribute__((packed)); static_assert(10 == sizeof(float80_t), "Invalid `float80_t` size."); diff --git a/include/remill/Arch/X86/Runtime/Types.h b/include/remill/Arch/X86/Runtime/Types.h index 545898e84..f19a0d125 100644 --- a/include/remill/Arch/X86/Runtime/Types.h +++ b/include/remill/Arch/X86/Runtime/Types.h @@ -131,5 +131,5 @@ typedef Rn RF64; typedef RnW RF64W; // Internally, we boil F80s down into F64s. -typedef Rn RF80; -typedef RnW RF80W; +typedef Rn RF80; +typedef RnW RF80W; diff --git a/include/remill/BC/IntrinsicTable.h b/include/remill/BC/IntrinsicTable.h index 6791b6d63..e1c6e0af8 100644 --- a/include/remill/BC/IntrinsicTable.h +++ b/include/remill/BC/IntrinsicTable.h @@ -85,6 +85,7 @@ class IntrinsicTable { llvm::Function *undefined_64; llvm::Function *undefined_f32; llvm::Function *undefined_f64; + llvm::Function *undefined_f80; private: IntrinsicTable(void) = delete; diff --git a/lib/Arch/Runtime/Intrinsics.cpp b/lib/Arch/Runtime/Intrinsics.cpp index 0a4245b98..e1a75b5bb 100644 --- a/lib/Arch/Runtime/Intrinsics.cpp +++ b/lib/Arch/Runtime/Intrinsics.cpp @@ -115,4 +115,5 @@ extern "C" void __remill_mark_as_used(const void *); USED(__remill_undefined_64); USED(__remill_undefined_f32); USED(__remill_undefined_f64); + USED(__remill_undefined_f80); } diff --git a/lib/Arch/X86/Semantics/MMX.cpp b/lib/Arch/X86/Semantics/MMX.cpp index 84dc31812..e9a99ae95 100644 --- a/lib/Arch/X86/Semantics/MMX.cpp +++ b/lib/Arch/X86/Semantics/MMX.cpp @@ -1900,14 +1900,14 @@ DEF_SEM(DoEMMS) { state.mmx.elems[6].val.qwords.elems[0] = __remill_undefined_64(); state.mmx.elems[7].val.qwords.elems[0] = __remill_undefined_64(); - state.st.elems[0].val = __remill_undefined_f64(); - state.st.elems[1].val = __remill_undefined_f64(); - state.st.elems[2].val = __remill_undefined_f64(); - state.st.elems[3].val = __remill_undefined_f64(); - state.st.elems[4].val = __remill_undefined_f64(); - state.st.elems[5].val = __remill_undefined_f64(); - state.st.elems[6].val = __remill_undefined_f64(); - state.st.elems[7].val = __remill_undefined_f64(); + state.st.elems[0].val = __remill_undefined_f80(); + state.st.elems[1].val = __remill_undefined_f80(); + state.st.elems[2].val = __remill_undefined_f80(); + state.st.elems[3].val = __remill_undefined_f80(); + state.st.elems[4].val = __remill_undefined_f80(); + state.st.elems[5].val = __remill_undefined_f80(); + state.st.elems[6].val = __remill_undefined_f80(); + state.st.elems[7].val = __remill_undefined_f80(); // TODO(pag): Add FPU tag word stuff to the `State` structure, and reset // it here. diff --git a/lib/BC/IntrinsicTable.cpp b/lib/BC/IntrinsicTable.cpp index 143d815e8..67a4277b4 100644 --- a/lib/BC/IntrinsicTable.cpp +++ b/lib/BC/IntrinsicTable.cpp @@ -123,7 +123,8 @@ IntrinsicTable::IntrinsicTable(llvm::Module *module) undefined_32(FindPureIntrinsic(module, "__remill_undefined_32")), undefined_64(FindPureIntrinsic(module, "__remill_undefined_64")), undefined_f32(FindPureIntrinsic(module, "__remill_undefined_f32")), - undefined_f64(FindPureIntrinsic(module, "__remill_undefined_f64")) { + undefined_f64(FindPureIntrinsic(module, "__remill_undefined_f64")), + undefined_f80(FindPureIntrinsic(module, "__remill_undefined_f80")) { // Make sure to set the correct attributes on this to make sure that // it's never optimized away. diff --git a/tests/AArch64/Run.cpp b/tests/AArch64/Run.cpp index df888be9d..bdd2c9ed6 100644 --- a/tests/AArch64/Run.cpp +++ b/tests/AArch64/Run.cpp @@ -334,6 +334,10 @@ float64_t __remill_undefined_f64(void) { return 0.0; } +float80_t __remill_undefined_f80(void) { + return {0}; +} + // Marks `mem` as being used. This is used for making sure certain symbols are // kept around through optimization, and makes sure that optimization doesn't // perform dead-argument elimination on any of the intrinsics. diff --git a/tests/X86/Run.cpp b/tests/X86/Run.cpp index a730c88e9..6ae40b1fb 100644 --- a/tests/X86/Run.cpp +++ b/tests/X86/Run.cpp @@ -441,6 +441,10 @@ float64_t __remill_undefined_f64(void) { return 0.0; } +float80_t __remill_undefined_f80(void) { + return {0}; +} + // Marks `mem` as being used. This is used for making sure certain symbols are // kept around through optimization, and makes sure that optimization doesn't // perform dead-argument elimination on any of the intrinsics.