Re: Printing decimal128 types out of registers

Luis Machado
2008-01-17T19:03:55Z

[-- Attachment #1: Type: text/plain, Size: 2455 bytes --]

Hi,

The patch to handle DFP expressions had some overlapping with this
patch, and since this was a bit old already, follows a refreshed version
for HEAD.

The patch remains essentially the same, with a few fixes. Running the
testsuite showed no regressions on 32-bit/64-bit ppc.

Is this OK to go in?

Regards,
Luis

On Wed, 2007-11-07 at 16:33 -0200, Luis Machado wrote:
> Hi folks,
>
> I'm continuing the work to enhance DFP type support on GDB. DFP types
> are nativelly supported on the Power6 processor, thus they are stored in
> registers as so. This patch handles the printing and setting of
> Decimal128 types directly out of/in the registers.
>
> A Decimal128 type is stored using pairs of Floating Point registers, and
> it always starts in an even-numbered register. So, we could, for
> example, have a Decimal128 var taking up FPR0-FPR1, FPR10-FPR11, but
> never FPR1-FPR2 or FPR23-24.
>
> Right now GDB isn't able to print those vars as it doesn't know they
> take up more than one register.
>
> The idea to overcome this was to create a new set of 16 Decimal128-typed
> pseudo-registers called DL (Standing for "D"ecimal "L"ong Double) that
> would represent all 16 pairs of Floating Point Registers.. By using a
> pseudo-register mechanism, we're able to force the DL/FPR registers to
> map directly into the FPR/DL registers.
>
> Benefits of this change include:
>
> * Better printing support for Decimal128 types. We just print the
> register as is. No need to include casting tricks and arrays to print
> that value.
>
> * Straightforward way of setting Decimal128 values directly in the
> registers. We just "set $dl0=1.23dl", for example, and it's
> automatically set.
>
> * Overview of the state of 16 Decimal128 registers using the "info reg
> all" command.
>
>
> Supporting the patch there is also a number of other modifications, as
> listed:
>
> * DFP types are now global since they were included as predefined types
> in the "target-descriptions.c" file.
>
> * A simple testcase making sure the registers are working as expected.
>
> * Piece of documentation for the GDB manual.
>
> * Fix to display a 128-bit hex literal representation of Decimal128
> types (using "info reg").
>
> Also note that this support will be available for both ppc64 and ppc32.
>
> Looking forward to have comments and suggestions for improvements.
>
> Regards,
> Luis
--
Luis Machado
Software Engineer
IBM Linux Technology Center

[-- Attachment #2: registers-dfp.diff --]
[-- Type: text/x-patch, Size: 15947 bytes --]

2008-01-17 Luis Machado <luisgpm@br.ibm.com>

* rs6000-tdep.c (rs6000_gdbarch_init): Add support for
Decimal128 pseudo-registers.
(rs6000_pseudo_register_type): Returns the correct type for
Decimal128 pseudo-registers.
(rs6000_pseudo_register_reggroup_p): Returns the correct register
group for Decimal128 pseudo-registers.
(ppc_pseudo_register_read): New function.
(ppc_pseudo_register_write): New function.
* ppc-tdep.h: Add new variables ppc_dl0_upper_regnum, ppc_dl0_regnum
and ppc_dl15_regnum.
* printcmd.c: Fix displaying of DFP types as hex.
* features/rs6000/powerpc-64.c (initialize_tdesc_powerpc_64): Create
a new feature for Decimal128 pseudo-registers.
* features/rs6000/powerpc-32.c (initialize_tdesc_powerpc_32): Create
a new feature for Decimal128 pseudo-registers.
* testsuite/gdb.arch/powerpc-d128-regs.exp: New testcase expect
file.
* testsuite/gdb.arch/powerpc-d128-regs.c: New testcase source
file.
* doc/gdb.textinfo: Add new powerpc dfp128 feature documentation.

Index: gdb/rs6000-tdep.c
===================================================================
--- gdb.orig/rs6000-tdep.c 2008-01-17 10:43:31.000000000 -0800
+++ gdb/rs6000-tdep.c 2008-01-17 10:44:39.000000000 -0800
@@ -2384,6 +2384,20 @@
return spe_regnames[regno - tdep->ppc_ev0_regnum];
}

+ /* Check if the decimal128 pseudo-registers are available. */
+ if (tdep->ppc_dl0_regnum >= 0
+ && tdep->ppc_dl0_regnum <= regno
+ && regno < tdep->ppc_dl0_regnum + 16)
+ {
+ static const char *const dfp128_regnames[] = {
+ "dl0", "dl1", "dl2", "dl3",
+ "dl4", "dl5", "dl6", "dl7",
+ "dl8", "dl9", "dl10", "dl11",
+ "dl12", "dl13", "dl14", "dl15"
+ };
+ return dfp128_regnames[regno - tdep->ppc_dl0_regnum];
+ }
+
return tdesc_register_name (gdbarch, regno);
}

@@ -2395,12 +2409,19 @@
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- /* These are the only pseudo-registers we support. */
- gdb_assert (tdep->ppc_ev0_regnum >= 0
- && regnum >= tdep->ppc_ev0_regnum
- && regnum < tdep->ppc_ev0_regnum + 32);
+ /* These are the e500 pseudo-registers. */
+ if (tdep->ppc_ev0_regnum >= 0
+ && regnum >= tdep->ppc_ev0_regnum
+ && regnum < tdep->ppc_ev0_regnum + 32)
+ return rs6000_builtin_type_vec64 (gdbarch);
+
+ /* These are the ppc decimal128 pseudo-registers. */
+ if (tdep->ppc_dl0_regnum >= 0
+ && regnum >= tdep->ppc_dl0_regnum
+ && regnum < tdep->ppc_dl0_regnum + 16)
+ return builtin_type (current_gdbarch)->builtin_declong;;

- return rs6000_builtin_type_vec64 (gdbarch);
+ return NULL;
}

/* Is REGNUM a member of REGGROUP? */
@@ -2410,10 +2431,17 @@
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- /* These are the only pseudo-registers we support. */
- gdb_assert (tdep->ppc_ev0_regnum >= 0
- && regnum >= tdep->ppc_ev0_regnum
- && regnum < tdep->ppc_ev0_regnum + 32);
+ /* These are the e500 pseudo-registers. */
+ if (tdep->ppc_ev0_regnum < 0
+ && regnum < tdep->ppc_ev0_regnum
+ && regnum >= tdep->ppc_ev0_regnum + 32)
+ return -1;
+
+ /* These are the ppc decimal128 pseudo-registers. */
+ if (tdep->ppc_dl0_regnum < 0
+ && regnum < tdep->ppc_dl0_regnum
+ && regnum >= tdep->ppc_dl0_regnum + 16)
+ return -1;

if (group == all_reggroup || group == vector_reggroup)
return 1;
@@ -2556,6 +2584,89 @@
gdbarch_register_name (gdbarch, reg_nr), reg_nr);
}

+/* Read method for PPC pseudo-registers. Currently this is handling the
+ 16 decimal128 registers that map into 16 pairs of FP registers. */
+static void
+ppc_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
+ int reg_nr, gdb_byte *buffer)
+{
+ struct gdbarch *regcache_arch = get_regcache_arch (regcache);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int reg_index;
+ gdb_byte *byte_buffer = buffer;
+
+ gdb_assert (regcache_arch == gdbarch);
+
+ if (tdep->ppc_dl0_regnum <= reg_nr
+ && reg_nr < tdep->ppc_dl0_regnum + 16)
+ {
+ reg_index = reg_nr - tdep->ppc_dl0_regnum;
+
+ if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
+ {
+ /* Read two FP registers to form a whole dl register. */
+ regcache_raw_read (regcache, tdep->ppc_dl0_upper_regnum +
+ 2 * reg_index, byte_buffer);
+ regcache_raw_read (regcache, tdep->ppc_dl0_upper_regnum +
+ 2 * reg_index + 1, byte_buffer + 8);
+ }
+ else
+ {
+ regcache_raw_read (regcache, tdep->ppc_dl0_upper_regnum +
+ 2 * reg_index + 1, byte_buffer + 8);
+ regcache_raw_read (regcache, tdep->ppc_dl0_upper_regnum +
+ 2 * reg_index, byte_buffer);
+ }
+ }
+ else
+ internal_error (__FILE__, __LINE__,
+ _("ppc_pseudo_register_read: "
+ "called on unexpected register '%s' (%d)"),
+ gdbarch_register_name (gdbarch, reg_nr), reg_nr);
+}
+
+/* Write method for PPC pseudo-registers. Currently this is handling the
+ 16 decimal128 registers that map into 16 pairs of FP registers. */
+static void
+ppc_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
+ int reg_nr, const gdb_byte *buffer)
+{
+ struct gdbarch *regcache_arch = get_regcache_arch (regcache);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int reg_index;
+ const gdb_byte *byte_buffer = buffer;
+
+ gdb_assert (regcache_arch == gdbarch);
+
+ if (tdep->ppc_dl0_regnum <= reg_nr
+ && reg_nr < tdep->ppc_dl0_regnum + 16)
+ {
+ reg_index = reg_nr - tdep->ppc_dl0_regnum;
+
+ if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
+ {
+ /* Write each half of the dl register into a separate
+ FP register. */
+ regcache_raw_write (regcache, tdep->ppc_dl0_upper_regnum +
+ 2 * reg_index, byte_buffer);
+ regcache_raw_write (regcache, tdep->ppc_dl0_upper_regnum +
+ 2 * reg_index + 1, byte_buffer + 8);
+ }
+ else
+ {
+ regcache_raw_write (regcache, tdep->ppc_dl0_upper_regnum +
+ 2 * reg_index + 1, byte_buffer + 8);
+ regcache_raw_write (regcache, tdep->ppc_dl0_upper_regnum +
+ 2 * reg_index, byte_buffer);
+ }
+ }
+ else
+ internal_error (__FILE__, __LINE__,
+ _("ppc_pseudo_register_write: "
+ "called on unexpected register '%s' (%d)"),
+ gdbarch_register_name (gdbarch, reg_nr), reg_nr);
+}
+
/* Convert a DBX STABS register number to a GDB register number. */
static int
rs6000_stab_reg_to_regnum (struct gdbarch *gdbarch, int num)
@@ -3174,7 +3285,8 @@
enum auto_boolean soft_float_flag = powerpc_soft_float_global;
int soft_float;
enum powerpc_vector_abi vector_abi = powerpc_vector_abi_global;
- int have_fpu = 1, have_spe = 0, have_mq = 0, have_altivec = 0;
+ int have_fpu = 1, have_spe = 0, have_mq = 0, have_altivec = 0,
+ have_dfp128 = 0;
int tdesc_wordsize = -1;
const struct target_desc *tdesc = info.target_desc;
struct tdesc_arch_data *tdesc_data = NULL;
@@ -3380,6 +3492,12 @@
else
have_altivec = 0;

+ feature = tdesc_find_feature (tdesc,
+ "org.gnu.gdb.power.dfp128");
+
+ if (feature != NULL)
+ have_dfp128 = 1;
+
/* On machines supporting the SPE APU, the general-purpose registers
are 64 bits long. There are SIMD vector instructions to treat them
as pairs of floats, but the rest of the instruction set treats them
@@ -3568,6 +3686,7 @@
tdep->ppc_ev0_upper_regnum = have_spe ? PPC_SPE_UPPER_GP0_REGNUM : -1;
tdep->ppc_acc_regnum = have_spe ? PPC_SPE_ACC_REGNUM : -1;
tdep->ppc_spefscr_regnum = have_spe ? PPC_SPE_FSCR_REGNUM : -1;
+ tdep->ppc_dl0_upper_regnum = have_dfp128 ? PPC_F0_REGNUM : -1;

set_gdbarch_pc_regnum (gdbarch, PPC_PC_REGNUM);
set_gdbarch_sp_regnum (gdbarch, PPC_R0_REGNUM + 1);
@@ -3600,6 +3719,12 @@
set_gdbarch_pseudo_register_read (gdbarch, e500_pseudo_register_read);
set_gdbarch_pseudo_register_write (gdbarch, e500_pseudo_register_write);
}
+ else
+ if (have_dfp128)
+ {
+ set_gdbarch_pseudo_register_read (gdbarch, ppc_pseudo_register_read);
+ set_gdbarch_pseudo_register_write (gdbarch, ppc_pseudo_register_write);
+ }

set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1);

@@ -3610,7 +3735,12 @@
set_gdbarch_print_insn (gdbarch, gdb_print_insn_powerpc);

set_gdbarch_num_regs (gdbarch, PPC_NUM_REGS + num_sprs);
- set_gdbarch_num_pseudo_regs (gdbarch, have_spe ? 32 : 0);
+
+ if (have_spe)
+ set_gdbarch_num_pseudo_regs (gdbarch, 32);
+ else
+ if (have_dfp128)
+ set_gdbarch_num_pseudo_regs (gdbarch, 16);

set_gdbarch_ptr_bit (gdbarch, wordsize * TARGET_CHAR_BIT);
set_gdbarch_short_bit (gdbarch, 2 * TARGET_CHAR_BIT);
@@ -3734,6 +3864,10 @@
tdep->ppc_ev0_regnum = have_spe ? gdbarch_num_regs (gdbarch) : -1;
tdep->ppc_ev31_regnum = have_spe ? tdep->ppc_ev0_regnum + 31 : -1;

+ /* Set the register number for decimal128 pseudo-registers. */
+ tdep->ppc_dl0_regnum = have_dfp128 ? gdbarch_num_regs (gdbarch) : -1;
+ tdep->ppc_dl15_regnum = have_dfp128 ? tdep->ppc_dl0_regnum + 15 : -1;
+
return gdbarch;
}

Index: gdb/ppc-tdep.h
===================================================================
--- gdb.orig/ppc-tdep.h 2008-01-17 10:43:31.000000000 -0800
+++ gdb/ppc-tdep.h 2008-01-17 10:43:51.000000000 -0800
@@ -205,6 +205,11 @@
int ppc_acc_regnum; /* SPE 'acc' register */
int ppc_spefscr_regnum; /* SPE 'spefscr' register */

+ /* Decimal 128 registers. */
+ int ppc_dl0_regnum; /* First Decimal128 argument register pair. */
+ int ppc_dl15_regnum; /* Last Decimal128 argument register pair. */
+ int ppc_dl0_upper_regnum; /* First FPR upper half register for dl0. */
+
/* Offset to ABI specific location where link register is saved. */
int lr_frame_offset;

Index: gdb/features/rs6000/powerpc-64.c
===================================================================
--- gdb.orig/features/rs6000/powerpc-64.c 2008-01-17 10:43:31.000000000 -0800
+++ gdb/features/rs6000/powerpc-64.c 2008-01-17 10:43:51.000000000 -0800
@@ -160,5 +160,7 @@
tdesc_create_reg (feature, "vscr", 103, 1, "vector", 32, "int");
tdesc_create_reg (feature, "vrsave", 104, 1, "vector", 32, "int");

+ feature = tdesc_create_feature (result, "org.gnu.gdb.power.dfp128");
+
tdesc_powerpc_64 = result;
}
Index: gdb/features/rs6000/powerpc-32.c
===================================================================
--- gdb.orig/features/rs6000/powerpc-32.c 2008-01-17 10:43:31.000000000 -0800
+++ gdb/features/rs6000/powerpc-32.c 2008-01-17 10:43:51.000000000 -0800
@@ -160,5 +160,7 @@
tdesc_create_reg (feature, "vscr", 103, 1, "vector", 32, "int");
tdesc_create_reg (feature, "vrsave", 104, 1, "vector", 32, "int");

+ feature = tdesc_create_feature (result, "org.gnu.gdb.power.dfp128");
+
tdesc_powerpc_32 = result;
}
Index: gdb/doc/gdb.texinfo
===================================================================
--- gdb.orig/doc/gdb.texinfo 2008-01-17 10:43:31.000000000 -0800
+++ gdb/doc/gdb.texinfo 2008-01-17 10:43:51.000000000 -0800
@@ -26654,6 +26654,13 @@
these to present registers @samp{ev0} through @samp{ev31} to the
user.

+The @samp{org.gnu.gdb.power.dfp128} feature is optional and is aimed at targets
+that support DFP types. It should contain registers @samp{dl0} through
+@samp{dl15}. Each @samp{dl} register is actually a pair of 64-bit floating
+point registers, so, for example, @samp{dl0} is composed by joining @samp{f0}
+and @samp{f1}. @value{GDBN} will combine the pair of floating
+point registers to present a single @samp{dl} register.
+
@include gpl.texi

@raisesections
Index: gdb/testsuite/gdb.arch/powerpc-d128-regs.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb/testsuite/gdb.arch/powerpc-d128-regs.c 2008-01-17 10:43:51.000000000 -0800
@@ -0,0 +1,25 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2008 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Tests ppc decimal128 pseudo-registers. */
+
+int main(void)
+{
+ _Decimal128 d128 = 1.2345678910dl;
+
+ return 0;
+}
Index: gdb/testsuite/gdb.arch/powerpc-d128-regs.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb/testsuite/gdb.arch/powerpc-d128-regs.exp 2008-01-17 10:43:51.000000000 -0800
@@ -0,0 +1,77 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2008
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Please email any bugs, comments, and/or additions to this file to:
+# bug-gdb@gnu.org
+
+# Testcase for ppc decimal128 pseudo-registers.
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+if ![istarget "powerpc64-*"] then {
+ verbose "Skipping powerpc Decimal128 pseudo-registers testcase."
+ return
+}
+
+set testfile "powerpc-d128-regs"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {quiet debug}] != "" } {
+ untested powerpc-d128-regs.exp
+ return -1
+}
+
+# Start with a fresh gdb.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if { ![runto main] } then {
+ fail "run to main"
+ return
+}
+
+if [gdb_test "show arch" ".*currently powerpc:common.*" "Checking for PPC arch"] {
+ return -1;
+}
+
+gdb_test "next" ""
+
+for {set i 0} {$i < 16} {incr i 1} {
+gdb_test "set \$dl$i=d128" "" "Set dl$i register"
+
+gdb_test "print \$dl$i" "\\\$$decimal = 1\.2345678910" "Print dl$i register as DFP"
+
+gdb_test "info reg dl$i" \
+ "dl$i\[ \]*0x2205800000000000000000049c5de09c\[\t\]*1\.2345678910" \
+ "Print dl$i register with the info reg command"
+
+gdb_test "info reg f[expr 2*$i]" \
+ "f[expr 2*$i]\[ \]*8\.608957309287334e\-145\[\t\]*\\(raw 0x2205800000000000\\)" \
+ "Testing lower half of dl$i register"
+
+gdb_test "info reg f[expr 2*$i+1]" \
+ "f[expr 2*$i+1]\[ \]*9\.7841140127686122e\-314\[\t\]*\\(raw 0x000000049c5de09c\\)" \
+ "Testing upper half of dl$i register"
+
+}
Index: gdb/printcmd.c
===================================================================
--- gdb.orig/printcmd.c 2008-01-17 10:48:18.000000000 -0800
+++ gdb/printcmd.c 2008-01-17 10:48:29.000000000 -0800
@@ -326,7 +326,8 @@

if (len > sizeof(LONGEST) &&
(TYPE_CODE (type) == TYPE_CODE_INT
- || TYPE_CODE (type) == TYPE_CODE_ENUM))
+ || TYPE_CODE (type) == TYPE_CODE_ENUM
+ || TYPE_CODE (type) == TYPE_CODE_DECFLOAT))
{
switch (format)
{