Index: lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h =================================================================== --- lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h +++ lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h @@ -136,6 +136,7 @@ YMM m_ymm_set; RegInfo m_reg_info; uint64_t m_gpr_x86_64[k_num_gpr_registers_x86_64]; + uint32_t m_fctrl_offset_in_userarea; // Private member methods. bool IsRegisterSetAvailable (uint32_t set_index) const; Index: lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp +++ lldb/trunk/source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp @@ -422,6 +422,10 @@ // Clear out the FPR state. ::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 @@ -559,8 +563,16 @@ } // 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) { case 2: @@ -620,8 +632,16 @@ else { // 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) { case 2: Index: lldb/trunk/test/functionalities/register/Makefile =================================================================== --- lldb/trunk/test/functionalities/register/Makefile +++ lldb/trunk/test/functionalities/register/Makefile @@ -1,5 +1,5 @@ LEVEL = ../../make -CXX_SOURCES := main.cpp +CXX_SOURCES := main.cpp a.cpp include $(LEVEL)/Makefile.rules Index: lldb/trunk/test/functionalities/register/TestRegisters.py =================================================================== --- lldb/trunk/test/functionalities/register/TestRegisters.py +++ lldb/trunk/test/functionalities/register/TestRegisters.py @@ -35,6 +35,13 @@ self.buildDefault() 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): """Test expression evaluation with commands related to registers.""" if not self.getArchitecture() in ['amd64', 'i386', 'x86_64']: @@ -152,6 +159,69 @@ self.expect("register read " + register, 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): exe = os.path.join(os.getcwd(), "a.out") Index: lldb/trunk/test/functionalities/register/a.cpp =================================================================== --- lldb/trunk/test/functionalities/register/a.cpp +++ lldb/trunk/test/functionalities/register/a.cpp @@ -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 + +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; +} Index: lldb/trunk/test/functionalities/register/main.cpp =================================================================== --- lldb/trunk/test/functionalities/register/main.cpp +++ lldb/trunk/test/functionalities/register/main.cpp @@ -15,6 +15,8 @@ #include #include +long double outermost_return_long_double (long double my_long_double); + int main (int argc, char const *argv[]) { #if defined(__linux__) @@ -32,6 +34,7 @@ char my_string[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 0}; double my_double = 1234.5678; + long double my_long_double = 1234.5678; // For simplicity assume that any cmdline argument means wait for attach. if (argc > 1) @@ -43,5 +46,6 @@ printf("my_string=%s\n", my_string); printf("my_double=%g\n", my_double); + outermost_return_long_double (my_long_double); return 0; }