mgorny created this revision.
mgorny added reviewers: labath, krytarowski, emaste.
mgorny requested review of this revision.

Translate between abridged and full ftag values in order to expose
the latter in the gdb-remote protocol while the former are used by
FXSAVE/XSAVE...  This matches the gdb behavior.


https://reviews.llvm.org/D91504

Files:
  lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
  lldb/source/Plugins/Process/Utility/RegisterContext_x86.h
  lldb/test/Shell/Register/x86-64-fp-read.test
  lldb/test/Shell/Register/x86-64-fp-write.test
  lldb/test/Shell/Register/x86-fp-read.test
  lldb/test/Shell/Register/x86-fp-write.test
  lldb/unittests/Process/Utility/CMakeLists.txt
  lldb/unittests/Process/Utility/RegisterContextTest.cpp

Index: lldb/unittests/Process/Utility/RegisterContextTest.cpp
===================================================================
--- /dev/null
+++ lldb/unittests/Process/Utility/RegisterContextTest.cpp
@@ -0,0 +1,65 @@
+//===-- RegisterContextTest.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+
+#include "Plugins/Process/Utility/RegisterContext_x86.h"
+
+#include <array>
+
+using namespace lldb_private;
+
+struct TagWordTestVector {
+  uint16_t sw;
+  uint16_t tw;
+  uint8_t tw_abridged;
+  int st_reg_num;
+};
+
+constexpr MMSReg st_from_comp(uint64_t mantissa, uint16_t sign_exp) {
+  MMSReg ret = {};
+  ret.comp.mantissa = mantissa;
+  ret.comp.sign_exp = sign_exp;
+  return ret;
+}
+
+std::array<MMSReg, 8> st_regs = {
+	st_from_comp(0x8000000000000000, 0x4000), // +2.0
+	st_from_comp(0x3f00000000000000, 0x0000), // 1.654785e-4932
+	st_from_comp(0x0000000000000000, 0x0000), // +0
+	st_from_comp(0x0000000000000000, 0x8000), // -0
+	st_from_comp(0x8000000000000000, 0x7fff), // +inf
+	st_from_comp(0x8000000000000000, 0xffff), // -inf
+	st_from_comp(0xc000000000000000, 0xffff), // nan
+	st_from_comp(0x8000000000000000, 0xc000), // -2.0
+};
+
+std::array<TagWordTestVector, 8> tag_word_test_vectors{
+  TagWordTestVector{0x3800, 0x3fff, 0x80, 1},
+  TagWordTestVector{0x3000, 0x2fff, 0xc0, 2},
+  TagWordTestVector{0x2800, 0x27ff, 0xe0, 3},
+  TagWordTestVector{0x2000, 0x25ff, 0xf0, 4},
+  TagWordTestVector{0x1800, 0x25bf, 0xf8, 5},
+  TagWordTestVector{0x1000, 0x25af, 0xfc, 6},
+  TagWordTestVector{0x0800, 0x25ab, 0xfe, 7},
+  TagWordTestVector{0x0000, 0x25a8, 0xff, 8},
+};
+
+TEST(RegisterContext_x86Test, AbridgedToFullTagWord) {
+  for (TagWordTestVector &x : tag_word_test_vectors) {
+    std::array<MMSReg, 8> test_regs;
+    for (int i = 0; i < x.st_reg_num; ++i)
+      test_regs[i] = st_regs[x.st_reg_num - i - 1];
+    EXPECT_EQ(AbridgedToFullTagWord(x.tw_abridged, x.sw, test_regs.data()), x.tw);
+  }
+}
+
+TEST(RegisterContext_x86Test, FullToAbridgedTagWord) {
+  for (TagWordTestVector &x : tag_word_test_vectors)
+    EXPECT_EQ(FullToAbridgedTagWord(x.tw), x.tw_abridged);
+}
Index: lldb/unittests/Process/Utility/CMakeLists.txt
===================================================================
--- lldb/unittests/Process/Utility/CMakeLists.txt
+++ lldb/unittests/Process/Utility/CMakeLists.txt
@@ -1,4 +1,5 @@
 add_lldb_unittest(ProcessUtilityTests
+  RegisterContextTest.cpp
   RegisterContextFreeBSDTest.cpp
 
   LINK_LIBS
Index: lldb/test/Shell/Register/x86-fp-write.test
===================================================================
--- lldb/test/Shell/Register/x86-fp-write.test
+++ lldb/test/Shell/Register/x86-fp-write.test
@@ -7,8 +7,7 @@
 register write fctrl 0x037b
 register write fstat 0x8884
 # note: this needs to enable all registers for writes to be effective
-# TODO: fix it to use proper ftag values instead of 'abridged'
-register write ftag 0x00ff
+register write ftag 0x2a58
 register write fop 0x0033
 # the exact addresses do not matter, we want just to verify FXSAVE
 # note: segment registers are not supported on all CPUs
Index: lldb/test/Shell/Register/x86-fp-read.test
===================================================================
--- lldb/test/Shell/Register/x86-fp-read.test
+++ lldb/test/Shell/Register/x86-fp-read.test
@@ -8,9 +8,7 @@
 register read --all
 # CHECK-DAG: fctrl = 0x037b
 # CHECK-DAG: fstat = 0x8084
-# TODO: the following value is incorrect, it's a bug in the way
-# FXSAVE/XSAVE is interpreted
-# CHECK-DAG: ftag = 0x007f
+# CHECK-DAG: ftag = 0xea58
 # CHECK-DAG: fop = 0x0033
 
 # CHECK-DAG: st{{(mm)?}}0 = {0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0x00 0x40}
Index: lldb/test/Shell/Register/x86-64-fp-write.test
===================================================================
--- lldb/test/Shell/Register/x86-64-fp-write.test
+++ lldb/test/Shell/Register/x86-64-fp-write.test
@@ -8,8 +8,7 @@
 register write fctrl 0x037b
 register write fstat 0x8884
 # note: this needs to enable all registers for writes to be effective
-# TODO: fix it to use proper ftag values instead of 'abridged'
-register write ftag 0x00ff
+register write ftag 0x2a58
 register write fop 0x0033
 # the exact addresses do not matter, we want just to verify FXSAVE
 # note: fxrstor64 apparently truncates this to 48 bits, and sign extends
Index: lldb/test/Shell/Register/x86-64-fp-read.test
===================================================================
--- lldb/test/Shell/Register/x86-64-fp-read.test
+++ lldb/test/Shell/Register/x86-64-fp-read.test
@@ -18,9 +18,7 @@
 register read --all
 # CHECK-DAG: fctrl = 0x037b
 # CHECK-DAG: fstat = 0x8084
-# TODO: the following value is incorrect, it's a bug in the way
-# FXSAVE/XSAVE is interpreted
-# CHECK-DAG: ftag = 0x007f
+# CHECK-DAG: ftag = 0xea58
 # CHECK-DAG: fop = 0x0033
 # CHECK-DAG: fip = [[FDIV]]
 # CHECK-DAG: fdp = [[ZERO]]
Index: lldb/source/Plugins/Process/Utility/RegisterContext_x86.h
===================================================================
--- lldb/source/Plugins/Process/Utility/RegisterContext_x86.h
+++ lldb/source/Plugins/Process/Utility/RegisterContext_x86.h
@@ -239,10 +239,19 @@
 
 // Generic floating-point registers
 
+LLVM_PACKED_START
 struct MMSReg {
-  uint8_t bytes[10];
+  union {
+    uint8_t bytes[10];
+    struct {
+      uint64_t mantissa;
+      uint16_t sign_exp;
+    } comp;
+  };
   uint8_t pad[6];
 };
+LLVM_PACKED_END
+static_assert(sizeof(MMSReg) == 16, "MMSReg is not 16 bytes of size");
 
 struct XMMReg {
   uint8_t bytes[16]; // 128-bits for each XMM register
@@ -369,6 +378,46 @@
   ::memcpy(ymmh_bytes, input.bytes + sizeof(XMMReg), sizeof(YMMHReg));
 }
 
+inline uint16_t AbridgedToFullTagWord(uint8_t abridged_tw, uint16_t sw, MMSReg *st_regs) {
+  // Tag word is using internal FPU register numbering rather than ST(i).
+  // Mapping to ST(i): i = FPU regno - TOP (Status Word, bits 11:13).
+  // Here we start with FPU reg 7 and go down.
+  int st = 7 - ((sw >> 11) & 7);
+  uint16_t tw = 0;
+  for (uint8_t mask = 0x80; mask != 0; mask >>= 1) {
+    tw <<= 2;
+    if (abridged_tw & mask) {
+      // The register is non-empty, so we need to check the value of ST(i).
+      uint16_t exp = st_regs[st].comp.sign_exp & 0x7fff;  // Discard the sign bit.
+      if (exp == 0) {
+        if (st_regs[st].comp.mantissa == 0)
+          tw |= 1;  // Zero
+        else
+          tw |= 2;  // Denormal
+      } else if (exp == 0x7fff)
+        tw |= 2;  // Infinity or NaN
+      // 0 if normal number
+    } else
+      tw |= 3;  // Empty register
+
+    // Rotate ST down.
+    st = (st - 1) & 7;
+  }
+
+  return tw;
+}
+
+inline uint8_t FullToAbridgedTagWord(uint16_t tw) {
+  uint8_t abridged_tw = 0;
+  for (uint16_t mask = 0xc000; mask != 0; mask >>= 2) {
+    abridged_tw <<= 1;
+    // full TW uses 11 for empty registers, aTW uses 0
+    if ((tw & mask) != mask)
+      abridged_tw |= 1;
+  }
+  return abridged_tw;
+}
+
 } // namespace lldb_private
 
 #endif
Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
+++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
@@ -529,6 +529,16 @@
   assert((reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(FPR));
   uint8_t *src = (uint8_t *)m_xstate.get() + reg_info->byte_offset -
                  m_fctrl_offset_in_userarea;
+
+  // TODO
+  if (reg_info->kinds[lldb::eRegisterKindLLDB] == lldb_ftag_x86_64) {
+    uint8_t abridged_tw = *(uint8_t *)src;
+    uint16_t sw = m_xstate->fxsave.fstat;
+    MMSReg *st_regs = m_xstate->fxsave.stmm;
+    reg_value.SetUInt16(AbridgedToFullTagWord(abridged_tw, sw, st_regs));
+    return error;
+  }
+
   switch (reg_info->byte_size) {
   case 1:
     reg_value.SetUInt8(*(uint8_t *)src);
@@ -638,23 +648,29 @@
              sizeof(FPR));
       uint8_t *dst = (uint8_t *)m_xstate.get() + reg_info->byte_offset -
                      m_fctrl_offset_in_userarea;
-      switch (reg_info->byte_size) {
-      case 1:
-        *(uint8_t *)dst = reg_value.GetAsUInt8();
-        break;
-      case 2:
-        *(uint16_t *)dst = reg_value.GetAsUInt16();
-        break;
-      case 4:
-        *(uint32_t *)dst = reg_value.GetAsUInt32();
-        break;
-      case 8:
-        *(uint64_t *)dst = reg_value.GetAsUInt64();
-        break;
-      default:
-        assert(false && "Unhandled data size.");
-        return Status("unhandled register data size %" PRIu32,
-                      reg_info->byte_size);
+
+      // TODO
+      if (reg_info->kinds[lldb::eRegisterKindLLDB] == lldb_ftag_x86_64) {
+        *(uint16_t *)dst = FullToAbridgedTagWord(reg_value.GetAsUInt16());
+      } else {
+        switch (reg_info->byte_size) {
+        case 1:
+          *(uint8_t *)dst = reg_value.GetAsUInt8();
+          break;
+        case 2:
+          *(uint16_t *)dst = reg_value.GetAsUInt16();
+          break;
+        case 4:
+          *(uint32_t *)dst = reg_value.GetAsUInt32();
+          break;
+        case 8:
+          *(uint64_t *)dst = reg_value.GetAsUInt64();
+          break;
+        default:
+          assert(false && "Unhandled data size.");
+          return Status("unhandled register data size %" PRIu32,
+                        reg_info->byte_size);
+        }
       }
     }
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to