forked from OSchip/llvm-project
Bug 24457 - X87 FPU Special Purpose Registers
Summary: - For 'register read --all' command on x86_64-Linux Platform: -- Provide correct values of X87 FPU Special Purpose Registers -- Both 32-bit & 64-bit inferiors give correct values on this Platform - Added a Test Vector: -- To verify the expected behaviour of the command Signed-off-by: Abhishek Aggarwal <abhishek.a.aggarwal@intel.com> Reviewers: ashok.thirumurthi, granata.enrico, tfiala, clayborg Differential Revision: http://reviews.llvm.org/D12592 llvm-svn: 246955
This commit is contained in:
parent
907abf799d
commit
7f658edd61
28
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
Normal file → Executable file
28
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp
Normal file → Executable file
|
@ -422,6 +422,10 @@ NativeRegisterContextLinux_x86_64::NativeRegisterContextLinux_x86_64 (const Arch
|
||||||
|
|
||||||
// Clear out the FPR state.
|
// Clear out the FPR state.
|
||||||
::memset(&m_fpr, 0, sizeof(FPR));
|
::memset(&m_fpr, 0, sizeof(FPR));
|
||||||
|
|
||||||
|
// Store byte offset of fctrl (i.e. first register of FPR)
|
||||||
|
const RegisterInfo *reg_info_fctrl = GetRegisterInfoByName("fctrl");
|
||||||
|
m_fctrl_offset_in_userarea = reg_info_fctrl->byte_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CONSIDER after local and llgs debugging are merged, register set support can
|
// CONSIDER after local and llgs debugging are merged, register set support can
|
||||||
|
@ -559,8 +563,16 @@ NativeRegisterContextLinux_x86_64::ReadRegister (const RegisterInfo *reg_info, R
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get pointer to m_fpr.xstate.fxsave variable and set the data from it.
|
// Get pointer to m_fpr.xstate.fxsave variable and set the data from it.
|
||||||
assert (reg_info->byte_offset < sizeof(m_fpr));
|
|
||||||
uint8_t *src = (uint8_t *)&m_fpr + reg_info->byte_offset;
|
// Byte offsets of all registers are calculated wrt 'UserArea' structure.
|
||||||
|
// However, ReadFPR() reads fpu registers {using ptrace(PTRACE_GETFPREGS,..)}
|
||||||
|
// and stores them in 'm_fpr' (of type FPR structure). To extract values of fpu
|
||||||
|
// registers, m_fpr should be read at byte offsets calculated wrt to FPR structure.
|
||||||
|
|
||||||
|
// Since, FPR structure is also one of the member of UserArea structure.
|
||||||
|
// byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) - byte_offset(fctrl wrt UserArea)
|
||||||
|
assert ( (reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(m_fpr));
|
||||||
|
uint8_t *src = (uint8_t *)&m_fpr + reg_info->byte_offset - m_fctrl_offset_in_userarea;
|
||||||
switch (reg_info->byte_size)
|
switch (reg_info->byte_size)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
|
@ -620,8 +632,16 @@ NativeRegisterContextLinux_x86_64::WriteRegister (const RegisterInfo *reg_info,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Get pointer to m_fpr.xstate.fxsave variable and set the data to it.
|
// Get pointer to m_fpr.xstate.fxsave variable and set the data to it.
|
||||||
assert (reg_info->byte_offset < sizeof(m_fpr));
|
|
||||||
uint8_t *dst = (uint8_t *)&m_fpr + reg_info->byte_offset;
|
// Byte offsets of all registers are calculated wrt 'UserArea' structure.
|
||||||
|
// However, WriteFPR() takes m_fpr (of type FPR structure) and writes only fpu
|
||||||
|
// registers using ptrace(PTRACE_SETFPREGS,..) API. Hence fpu registers should
|
||||||
|
// be written in m_fpr at byte offsets calculated wrt FPR structure.
|
||||||
|
|
||||||
|
// Since, FPR structure is also one of the member of UserArea structure.
|
||||||
|
// byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) - byte_offset(fctrl wrt UserArea)
|
||||||
|
assert ( (reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(m_fpr));
|
||||||
|
uint8_t *dst = (uint8_t *)&m_fpr + reg_info->byte_offset - m_fctrl_offset_in_userarea;
|
||||||
switch (reg_info->byte_size)
|
switch (reg_info->byte_size)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
|
|
|
@ -136,6 +136,7 @@ namespace process_linux {
|
||||||
YMM m_ymm_set;
|
YMM m_ymm_set;
|
||||||
RegInfo m_reg_info;
|
RegInfo m_reg_info;
|
||||||
uint64_t m_gpr_x86_64[k_num_gpr_registers_x86_64];
|
uint64_t m_gpr_x86_64[k_num_gpr_registers_x86_64];
|
||||||
|
uint32_t m_fctrl_offset_in_userarea;
|
||||||
|
|
||||||
// Private member methods.
|
// Private member methods.
|
||||||
bool IsRegisterSetAvailable (uint32_t set_index) const;
|
bool IsRegisterSetAvailable (uint32_t set_index) const;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
LEVEL = ../../make
|
LEVEL = ../../make
|
||||||
|
|
||||||
CXX_SOURCES := main.cpp
|
CXX_SOURCES := main.cpp a.cpp
|
||||||
|
|
||||||
include $(LEVEL)/Makefile.rules
|
include $(LEVEL)/Makefile.rules
|
||||||
|
|
|
@ -35,6 +35,13 @@ class RegisterCommandsTestCase(TestBase):
|
||||||
self.buildDefault()
|
self.buildDefault()
|
||||||
self.fp_register_write()
|
self.fp_register_write()
|
||||||
|
|
||||||
|
def test_fp_special_purpose_register_read(self):
|
||||||
|
"""Test commands that read fpu special purpose registers."""
|
||||||
|
if not self.getArchitecture() in ['amd64', 'i386', 'x86_64']:
|
||||||
|
self.skipTest("This test requires x86 or x86_64 as the architecture for the inferior")
|
||||||
|
self.buildDefault()
|
||||||
|
self.fp_special_purpose_register_read()
|
||||||
|
|
||||||
def test_register_expressions(self):
|
def test_register_expressions(self):
|
||||||
"""Test expression evaluation with commands related to registers."""
|
"""Test expression evaluation with commands related to registers."""
|
||||||
if not self.getArchitecture() in ['amd64', 'i386', 'x86_64']:
|
if not self.getArchitecture() in ['amd64', 'i386', 'x86_64']:
|
||||||
|
@ -152,6 +159,69 @@ class RegisterCommandsTestCase(TestBase):
|
||||||
self.expect("register read " + register,
|
self.expect("register read " + register,
|
||||||
substrs = [register + ' = ', new_value])
|
substrs = [register + ' = ', new_value])
|
||||||
|
|
||||||
|
def fp_special_purpose_register_read(self):
|
||||||
|
exe = os.path.join(os.getcwd(), "a.out")
|
||||||
|
|
||||||
|
# Create a target by the debugger.
|
||||||
|
target = self.dbg.CreateTarget(exe)
|
||||||
|
self.assertTrue(target, VALID_TARGET)
|
||||||
|
|
||||||
|
# Find the line number to break inside a.cpp.
|
||||||
|
self.line = line_number('a.cpp', '// Set break point at this line.')
|
||||||
|
|
||||||
|
# Set breakpoint
|
||||||
|
lldbutil.run_break_set_by_file_and_line (self, "a.cpp", self.line, num_expected_locations=1, loc_exact=True)
|
||||||
|
|
||||||
|
# Launch the process, and do not stop at the entry point.
|
||||||
|
self.runCmd ("run", RUN_SUCCEEDED)
|
||||||
|
|
||||||
|
process = target.GetProcess()
|
||||||
|
self.assertTrue(process.GetState() == lldb.eStateStopped,
|
||||||
|
PROCESS_STOPPED)
|
||||||
|
|
||||||
|
thread = process.GetThreadAtIndex(0)
|
||||||
|
self.assertTrue(thread.IsValid(), "current thread is valid")
|
||||||
|
|
||||||
|
currentFrame = thread.GetFrameAtIndex(0)
|
||||||
|
self.assertTrue(currentFrame.IsValid(), "current frame is valid")
|
||||||
|
|
||||||
|
# Extract the value of fstat and ftag flag at the point just before
|
||||||
|
# we start pushing floating point values on st% register stack
|
||||||
|
value = currentFrame.FindValue("fstat", lldb.eValueTypeRegister)
|
||||||
|
error = lldb.SBError()
|
||||||
|
reg_value_fstat_initial = value.GetValueAsUnsigned(error, 0)
|
||||||
|
|
||||||
|
self.assertTrue(error.Success(), "reading a value for fstat")
|
||||||
|
value = currentFrame.FindValue("ftag", lldb.eValueTypeRegister)
|
||||||
|
error = lldb.SBError()
|
||||||
|
reg_value_ftag_initial = value.GetValueAsUnsigned(error, 0)
|
||||||
|
|
||||||
|
self.assertTrue(error.Success(), "reading a value for ftag")
|
||||||
|
fstat_top_pointer_initial = (reg_value_fstat_initial & 0x3800)>>11
|
||||||
|
|
||||||
|
# Execute 'si' aka 'thread step-inst' instruction 5 times and with
|
||||||
|
# every execution verify the value of fstat and ftag registers
|
||||||
|
for x in range(0,5):
|
||||||
|
# step into the next instruction to push a value on 'st' register stack
|
||||||
|
self.runCmd ("si", RUN_SUCCEEDED)
|
||||||
|
|
||||||
|
# Verify fstat and save it to be used for verification in next execution of 'si' command
|
||||||
|
if not (reg_value_fstat_initial & 0x3800):
|
||||||
|
self.expect("register read fstat",
|
||||||
|
substrs = ['fstat' + ' = ', str("0x%0.4x" %((reg_value_fstat_initial & ~(0x3800))| 0x3800))])
|
||||||
|
reg_value_fstat_initial = ((reg_value_fstat_initial & ~(0x3800))| 0x3800)
|
||||||
|
fstat_top_pointer_initial = 7
|
||||||
|
else :
|
||||||
|
self.expect("register read fstat",
|
||||||
|
substrs = ['fstat' + ' = ', str("0x%0.4x" % (reg_value_fstat_initial - 0x0800))])
|
||||||
|
reg_value_fstat_initial = (reg_value_fstat_initial - 0x0800)
|
||||||
|
fstat_top_pointer_initial -= 1
|
||||||
|
|
||||||
|
# Verify ftag and save it to be used for verification in next execution of 'si' command
|
||||||
|
self.expect("register read ftag",
|
||||||
|
substrs = ['ftag' + ' = ', str("0x%0.4x" % (reg_value_ftag_initial | (1<< fstat_top_pointer_initial)))])
|
||||||
|
reg_value_ftag_initial = reg_value_ftag_initial | (1<< fstat_top_pointer_initial)
|
||||||
|
|
||||||
def fp_register_write(self):
|
def fp_register_write(self):
|
||||||
exe = os.path.join(os.getcwd(), "a.out")
|
exe = os.path.join(os.getcwd(), "a.out")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
//===-- a.cpp ------------------------------------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
long double
|
||||||
|
return_long_double (long double value)
|
||||||
|
{
|
||||||
|
float a=2, b=4,c=8, d=16, e=32, f=64, k=128, l=256, add=0;
|
||||||
|
__asm__ ( "fld %1 ;"
|
||||||
|
"fld %2 ;"
|
||||||
|
"fld %3 ;"
|
||||||
|
"fld %4 ;"
|
||||||
|
"fld %5 ;"
|
||||||
|
"fld %6 ;"
|
||||||
|
"fld %7 ;"
|
||||||
|
"fadd ;" : "=g" (add) : "g" (a), "g" (b), "g" (c), "g" (d), "g" (e), "g" (f), "g" (k), "g" (l) ); // Set break point at this line.
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
long double
|
||||||
|
outer_return_long_double (long double value)
|
||||||
|
{
|
||||||
|
long double val = return_long_double(value);
|
||||||
|
val *= 2 ;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
long double
|
||||||
|
outermost_return_long_double (long double value)
|
||||||
|
{
|
||||||
|
long double val = outer_return_long_double(value);
|
||||||
|
val *= 2 ;
|
||||||
|
return val;
|
||||||
|
}
|
|
@ -15,6 +15,8 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
long double outermost_return_long_double (long double my_long_double);
|
||||||
|
|
||||||
int main (int argc, char const *argv[])
|
int main (int argc, char const *argv[])
|
||||||
{
|
{
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
|
@ -32,6 +34,7 @@ int main (int argc, char const *argv[])
|
||||||
|
|
||||||
char my_string[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 0};
|
char my_string[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 0};
|
||||||
double my_double = 1234.5678;
|
double my_double = 1234.5678;
|
||||||
|
long double my_long_double = 1234.5678;
|
||||||
|
|
||||||
// For simplicity assume that any cmdline argument means wait for attach.
|
// For simplicity assume that any cmdline argument means wait for attach.
|
||||||
if (argc > 1)
|
if (argc > 1)
|
||||||
|
@ -43,5 +46,6 @@ int main (int argc, char const *argv[])
|
||||||
|
|
||||||
printf("my_string=%s\n", my_string);
|
printf("my_string=%s\n", my_string);
|
||||||
printf("my_double=%g\n", my_double);
|
printf("my_double=%g\n", my_double);
|
||||||
|
outermost_return_long_double (my_long_double);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue