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); + } } } 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/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/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-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-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/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/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 + +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 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 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 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); +}