forked from OSchip/llvm-project
[lldb] Reland "Use translated full ftag values"
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. The Shell/Register tests now rely on the new behavior, and therefore are run on non-Darwin systems only. The Python (API) test relies on the legacy behavior, and is run on Darwin only. Differential Revision: https://reviews.llvm.org/D91504
This commit is contained in:
parent
9b7b8de6d1
commit
8aea95f3cb
|
@ -451,10 +451,16 @@ NativeRegisterContextFreeBSD_x86_64::ReadRegister(const RegisterInfo *reg_info,
|
|||
switch (set) {
|
||||
case GPRegSet:
|
||||
case FPRegSet:
|
||||
case DBRegSet:
|
||||
reg_value.SetBytes(GetOffsetRegSetData(set, reg_info->byte_offset),
|
||||
reg_info->byte_size, endian::InlHostByteOrder());
|
||||
case DBRegSet: {
|
||||
void *data = GetOffsetRegSetData(set, reg_info->byte_offset);
|
||||
FXSAVE *fpr = reinterpret_cast<FXSAVE *>(m_fpr.data());
|
||||
if (data == &fpr->ftag) // ftag
|
||||
reg_value.SetUInt16(
|
||||
AbridgedToFullTagWord(fpr->ftag, fpr->fstat, fpr->stmm));
|
||||
else
|
||||
reg_value.SetBytes(data, reg_info->byte_size, endian::InlHostByteOrder());
|
||||
break;
|
||||
}
|
||||
case YMMRegSet: {
|
||||
llvm::Optional<YMMSplitPtr> ymm_reg = GetYMMSplitReg(reg);
|
||||
if (!ymm_reg) {
|
||||
|
@ -511,10 +517,15 @@ Status NativeRegisterContextFreeBSD_x86_64::WriteRegister(
|
|||
switch (set) {
|
||||
case GPRegSet:
|
||||
case FPRegSet:
|
||||
case DBRegSet:
|
||||
::memcpy(GetOffsetRegSetData(set, reg_info->byte_offset),
|
||||
reg_value.GetBytes(), reg_value.GetByteSize());
|
||||
case DBRegSet: {
|
||||
void *data = GetOffsetRegSetData(set, reg_info->byte_offset);
|
||||
FXSAVE *fpr = reinterpret_cast<FXSAVE *>(m_fpr.data());
|
||||
if (data == &fpr->ftag) // ftag
|
||||
fpr->ftag = FullToAbridgedTagWord(reg_value.GetAsUInt16());
|
||||
else
|
||||
::memcpy(data, reg_value.GetBytes(), reg_value.GetByteSize());
|
||||
break;
|
||||
}
|
||||
case YMMRegSet: {
|
||||
llvm::Optional<YMMSplitPtr> ymm_reg = GetYMMSplitReg(reg);
|
||||
if (!ymm_reg) {
|
||||
|
|
|
@ -530,6 +530,13 @@ NativeRegisterContextLinux_x86_64::ReadRegister(const RegisterInfo *reg_info,
|
|||
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;
|
||||
|
||||
if (src == reinterpret_cast<uint8_t *>(&m_xstate->fxsave.ftag)) {
|
||||
reg_value.SetUInt16(AbridgedToFullTagWord(
|
||||
m_xstate->fxsave.ftag, m_xstate->fxsave.fstat, m_xstate->fxsave.stmm));
|
||||
return error;
|
||||
}
|
||||
|
||||
switch (reg_info->byte_size) {
|
||||
case 1:
|
||||
reg_value.SetUInt8(*(uint8_t *)src);
|
||||
|
@ -639,23 +646,28 @@ Status NativeRegisterContextLinux_x86_64::WriteRegister(
|
|||
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);
|
||||
|
||||
if (dst == reinterpret_cast<uint8_t *>(&m_xstate->fxsave.ftag))
|
||||
m_xstate->fxsave.ftag = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -607,9 +607,13 @@ NativeRegisterContextNetBSD_x86_64::ReadRegister(const RegisterInfo *reg_info,
|
|||
case lldb_fstat_x86_64:
|
||||
reg_value = (uint16_t)m_xstate.xs_fxsave.fx_sw;
|
||||
break;
|
||||
case lldb_ftag_x86_64:
|
||||
reg_value = (uint16_t)m_xstate.xs_fxsave.fx_tw;
|
||||
case lldb_ftag_x86_64: {
|
||||
llvm::ArrayRef<MMSReg> st_regs{
|
||||
reinterpret_cast<MMSReg *>(m_xstate.xs_fxsave.fx_87_ac), 8};
|
||||
reg_value = (uint16_t)AbridgedToFullTagWord(
|
||||
m_xstate.xs_fxsave.fx_tw, m_xstate.xs_fxsave.fx_sw, st_regs);
|
||||
break;
|
||||
}
|
||||
case lldb_fop_x86_64:
|
||||
reg_value = (uint64_t)m_xstate.xs_fxsave.fx_opcode;
|
||||
break;
|
||||
|
@ -905,7 +909,7 @@ Status NativeRegisterContextNetBSD_x86_64::WriteRegister(
|
|||
m_xstate.xs_fxsave.fx_sw = reg_value.GetAsUInt16();
|
||||
break;
|
||||
case lldb_ftag_x86_64:
|
||||
m_xstate.xs_fxsave.fx_tw = reg_value.GetAsUInt16();
|
||||
m_xstate.xs_fxsave.fx_tw = FullToAbridgedTagWord(reg_value.GetAsUInt16());
|
||||
break;
|
||||
case lldb_fop_x86_64:
|
||||
m_xstate.xs_fxsave.fx_opcode = reg_value.GetAsUInt16();
|
||||
|
|
|
@ -12,6 +12,7 @@ add_lldb_library(lldbPluginProcessUtility
|
|||
NativeRegisterContextRegisterInfo.cpp
|
||||
NativeRegisterContextWatchpoint_x86.cpp
|
||||
NetBSDSignals.cpp
|
||||
RegisterContext_x86.cpp
|
||||
RegisterContextDarwin_arm.cpp
|
||||
RegisterContextDarwin_arm64.cpp
|
||||
RegisterContextDarwin_i386.cpp
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
//===-- RegisterContext_x86.cpp ---------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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 "RegisterContext_x86.h"
|
||||
|
||||
using namespace lldb_private;
|
||||
|
||||
// Convert the 8-bit abridged FPU Tag Word (as found in FXSAVE) to the full
|
||||
// 16-bit FPU Tag Word (as found in FSAVE, and used by gdb protocol). This
|
||||
// requires knowing the values of the ST(i) registers and the FPU Status Word.
|
||||
uint16_t lldb_private::AbridgedToFullTagWord(uint8_t abridged_tw, uint16_t sw,
|
||||
llvm::ArrayRef<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;
|
||||
}
|
||||
|
||||
// Convert the 16-bit FPU Tag Word to the abridged 8-bit value, to be written
|
||||
// into FXSAVE.
|
||||
uint8_t lldb_private::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;
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/BitmaskEnum.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
|
@ -239,10 +240,23 @@ enum {
|
|||
|
||||
// Generic floating-point registers
|
||||
|
||||
LLVM_PACKED_START
|
||||
struct MMSRegComp {
|
||||
uint64_t mantissa;
|
||||
uint16_t sign_exp;
|
||||
};
|
||||
|
||||
struct MMSReg {
|
||||
uint8_t bytes[10];
|
||||
union {
|
||||
uint8_t bytes[10];
|
||||
MMSRegComp comp;
|
||||
};
|
||||
uint8_t pad[6];
|
||||
};
|
||||
LLVM_PACKED_END
|
||||
|
||||
static_assert(sizeof(MMSRegComp) == 10, "MMSRegComp is not 10 bytes of size");
|
||||
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 +383,10 @@ inline void YMMToXState(const YMMReg& input, void* xmm_bytes, void* ymmh_bytes)
|
|||
::memcpy(ymmh_bytes, input.bytes + sizeof(XMMReg), sizeof(YMMHReg));
|
||||
}
|
||||
|
||||
uint16_t AbridgedToFullTagWord(uint8_t abridged_tw, uint16_t sw,
|
||||
llvm::ArrayRef<MMSReg> st_regs);
|
||||
uint8_t FullToAbridgedTagWord(uint16_t tw);
|
||||
|
||||
} // namespace lldb_private
|
||||
|
||||
#endif
|
||||
|
|
|
@ -190,6 +190,11 @@ class RegisterCommandsTestCase(TestBase):
|
|||
' = ',
|
||||
new_value])
|
||||
|
||||
# This test relies on ftag containing the 'abridged' value. Linux
|
||||
# and *BSD targets have been ported to report the full value instead
|
||||
# consistently with GDB. They are covered by the new-style
|
||||
# lldb/test/Shell/Register/x86*-fp-read.test.
|
||||
@skipUnlessDarwin
|
||||
def fp_special_purpose_register_read(self):
|
||||
exe = self.getBuildArtifact("a.out")
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# XFAIL: system-windows
|
||||
# Darwin uses the legacy behavior of reporting abridged ftag value,
|
||||
# it is covered by TestRegisters.py::fp_special_purpose_register_read.
|
||||
# XFAIL: system-darwin
|
||||
# REQUIRES: native && target-x86_64
|
||||
# RUN: %clangxx_host -g %p/Inputs/x86-fp-read.cpp -o %t
|
||||
|
@ -15,9 +17,7 @@ print &zero
|
|||
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]]
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# XFAIL: system-windows
|
||||
# Darwin uses the legacy behavior of reporting abridged ftag value,
|
||||
# it is covered by TestRegisters.py::fp_special_purpose_register_read.
|
||||
# XFAIL: system-darwin
|
||||
# REQUIRES: native && target-x86_64
|
||||
# RUN: %clangxx_host %p/Inputs/x86-fp-write.cpp -o %t
|
||||
|
@ -8,8 +10,7 @@ process launch
|
|||
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
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# XFAIL: system-windows
|
||||
# Darwin uses the legacy behavior of reporting abridged ftag value,
|
||||
# it is covered by TestRegisters.py::fp_special_purpose_register_read.
|
||||
# XFAIL: system-darwin
|
||||
# REQUIRES: native && target-x86
|
||||
# RUN: %clangxx_host -g %p/Inputs/x86-fp-read.cpp -o %t
|
||||
# RUN: %lldb -b -s %s %t | FileCheck %s
|
||||
|
@ -14,9 +17,7 @@ print &zero
|
|||
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: fioff = [[FDIV]]
|
||||
# CHECK-DAG: fooff = [[ZERO]]
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# XFAIL: system-windows
|
||||
# Darwin uses the legacy behavior of reporting abridged ftag value,
|
||||
# it is covered by TestRegisters.py::fp_special_purpose_register_read.
|
||||
# XFAIL: system-darwin
|
||||
# REQUIRES: native && (target-x86 || target-x86_64)
|
||||
# RUN: %clangxx_host %p/Inputs/x86-fp-write.cpp -o %t
|
||||
|
@ -8,8 +10,7 @@ process launch
|
|||
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
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
add_lldb_unittest(ProcessUtilityTests
|
||||
RegisterContextTest.cpp
|
||||
RegisterContextFreeBSDTest.cpp
|
||||
LinuxProcMapsTest.cpp
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
//===-- 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 "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/FormatVariadic.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;
|
||||
}
|
||||
|
||||
const 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
|
||||
};
|
||||
|
||||
const 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 (const auto &x : llvm::enumerate(tag_word_test_vectors)) {
|
||||
SCOPED_TRACE(llvm::formatv("tag_word_test_vectors[{0}]", x.index()));
|
||||
std::array<MMSReg, 8> test_regs;
|
||||
for (int i = 0; i < x.value().st_reg_num; ++i)
|
||||
test_regs[i] = st_regs[x.value().st_reg_num - i - 1];
|
||||
EXPECT_EQ(
|
||||
AbridgedToFullTagWord(x.value().tw_abridged, x.value().sw, test_regs),
|
||||
x.value().tw);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RegisterContext_x86Test, FullToAbridgedTagWord) {
|
||||
for (const auto &x : llvm::enumerate(tag_word_test_vectors)) {
|
||||
SCOPED_TRACE(llvm::formatv("tag_word_test_vectors[{0}]", x.index()));
|
||||
EXPECT_EQ(FullToAbridgedTagWord(x.value().tw), x.value().tw_abridged);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue