Fix the code reconstructing s87_tw (full tag word) from fx_sw (abridged
tag word) to correctly represent all register states.  The previous code
only distinguished between empty/non-empty registers, and assigned
'regular value' to all non-empty registers.  The new code explicitly
distinguishes the two other tag word values: empty and special.
---
 sys/arch/x86/x86/fpu.c                 | 28 ++++++++++++++++++++------
 tests/lib/libc/sys/t_ptrace_x86_wait.h |  2 --
 2 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/sys/arch/x86/x86/fpu.c b/sys/arch/x86/x86/fpu.c
index d89c78dcef47..c580f34b0615 100644
--- a/sys/arch/x86/x86/fpu.c
+++ b/sys/arch/x86/x86/fpu.c
@@ -668,7 +668,7 @@ fpu_sigreset(struct lwp *l)
 static void
 process_xmm_to_s87(const struct fxsave *sxmm, struct save87 *s87)
 {
-       unsigned int tag, ab_tag;
+       unsigned int tag, ab_tag, st;
        const struct fpaccfx *fx_reg;
        struct fpacc87 *s87_reg;
        int i;
@@ -723,12 +723,28 @@ process_xmm_to_s87(const struct fxsave *sxmm, struct 
save87 *s87)
                return;
        }
 
+       /* For ST(i), i = fpu_reg - top, we start with fpu_reg=7. */
+       st = 7 - ((sxmm->fx_sw >> 11) & 7);
        tag = 0;
-       /* Separate bits of abridged tag word with zeros */
-       for (i = 0x80; i != 0; tag <<= 1, i >>= 1)
-               tag |= ab_tag & i;
-       /* Replicate and invert so that 0 => 0b11 and 1 => 0b00 */
-       s87->s87_tw = (tag | tag >> 1) ^ 0xffff;
+       for (i = 0x80; i != 0; i >>= 1) {
+               tag <<= 2;
+               if (ab_tag & i) {
+                       unsigned int exp;
+                       /* Non-empty - we need to check ST(i) */
+                       fx_reg = &sxmm->fx_87_ac[st];
+                       exp = fx_reg->r.f87_exp_sign & 0x7fff;
+                       if (exp == 0) {
+                               if (fx_reg->r.f87_mantissa == 0)
+                                       tag |= 1; /* Zero */
+                               else
+                                       tag |= 2; /* Denormal */
+                       } else if (exp == 0x7fff)
+                               tag |= 2; /* Infinity or NaN */
+               } else
+                       tag |= 3; /* Empty */
+               st = (st - 1) & 7;
+       }
+       s87->s87_tw = tag;
 }
 
 static void
diff --git a/tests/lib/libc/sys/t_ptrace_x86_wait.h 
b/tests/lib/libc/sys/t_ptrace_x86_wait.h
index d7e93fde55a3..6067066a36c1 100644
--- a/tests/lib/libc/sys/t_ptrace_x86_wait.h
+++ b/tests/lib/libc/sys/t_ptrace_x86_wait.h
@@ -3367,10 +3367,8 @@ x86_register_test(enum x86_test_regset regset, enum 
x86_test_registers regs,
                                    expected_fpu.cw);
                                ATF_CHECK_EQ(fpr.fstate.s87_sw,
                                    expected_fpu.sw);
-#if 0 /* TODO: translation from FXSAVE is broken */
                                ATF_CHECK_EQ(fpr.fstate.s87_tw,
                                    expected_fpu.tw);
-#endif
                                ATF_CHECK_EQ(fpr.fstate.s87_opcode,
                                    expected_fpu.opcode);
                                ATF_CHECK_EQ(fpr.fstate.s87_ip.fa_32.fa_off,
-- 
2.28.0

Reply via email to