[PATCH] PPC - Printing Decimal 128 types out of registers

Luis Machado
2007-11-07T19:33:53Z

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

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

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

2007-11-07 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.
* target-descriptions.c: Add new predefined DFP types.
* c-exp.y: Fix DFP type names.
* gdbtypes.c (_initialize_gdbtypes): Initialize new global
DFP types.
* gdbtypes.h: Declare new global DFP types and remove the old ones.
* 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: git/gdb/rs6000-tdep.c
===================================================================
--- git.orig/gdb/rs6000-tdep.c 2007-11-05 11:37:24.000000000 -0800
+++ git/gdb/rs6000-tdep.c 2007-11-07 07:43:50.000000000 -0800
@@ -2381,6 +2381,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);
}

@@ -2392,12 +2406,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_declong;

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

/* Is REGNUM a member of REGGROUP? */
@@ -2407,10 +2428,16 @@
{
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;
+
+ 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;
@@ -2552,6 +2579,89 @@
gdbarch_register_name (gdbarch, reg_nr), reg_nr);
}

+/* Read method for PPC pseudo-registers. Currently this is handling the
+ 16 Decimal 128 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 Decimal 128 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_read: "
+ "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 (int num)
@@ -3170,7 +3280,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;
@@ -3376,6 +3487,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
@@ -3564,6 +3681,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);
@@ -3596,6 +3714,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);

@@ -3606,7 +3730,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);
@@ -3730,6 +3859,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: git/gdb/ppc-tdep.h
===================================================================
--- git.orig/gdb/ppc-tdep.h 2007-11-05 11:37:24.000000000 -0800
+++ git/gdb/ppc-tdep.h 2007-11-06 04:20:22.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 register pair. */
+ int ppc_dl15_regnum; /* Last Decimal128 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: git/gdb/printcmd.c
===================================================================
--- git.orig/gdb/printcmd.c 2007-11-06 04:20:21.000000000 -0800
+++ git/gdb/printcmd.c 2007-11-06 04:20:22.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)
{
Index: git/gdb/target-descriptions.c
===================================================================
--- git.orig/gdb/target-descriptions.c 2007-11-05 11:37:24.000000000 -0800
+++ git/gdb/target-descriptions.c 2007-11-06 04:20:22.000000000 -0800
@@ -360,7 +360,10 @@
{ "uint128", &builtin_type_uint128 },
{ "ieee_single", &builtin_type_ieee_single },
{ "ieee_double", &builtin_type_ieee_double },
- { "arm_fpa_ext", &builtin_type_arm_ext }
+ { "arm_fpa_ext", &builtin_type_arm_ext },
+ { "decfloat", &builtin_type_decfloat },
+ { "decdouble", &builtin_type_decdouble },
+ { "declong", &builtin_type_declong }
};

/* Return the type associated with ID in the context of FEATURE, or
Index: git/gdb/c-exp.y
===================================================================
--- git.orig/gdb/c-exp.y 2007-11-05 11:37:24.000000000 -0800
+++ git/gdb/c-exp.y 2007-11-06 04:20:22.000000000 -0800
@@ -1098,7 +1098,7 @@
{
p[len - 2] = '\0';
putithere->typed_val_decfloat.type
- = builtin_type (current_gdbarch)->builtin_decfloat;
+ = builtin_type_decfloat;
decimal_from_string (putithere->typed_val_decfloat.val, 4, p);
p[len] = saved_char;
return (DECFLOAT);
@@ -1108,7 +1108,7 @@
{
p[len - 2] = '\0';
putithere->typed_val_decfloat.type
- = builtin_type (current_gdbarch)->builtin_decdouble;
+ = builtin_type_decdouble;
decimal_from_string (putithere->typed_val_decfloat.val, 8, p);
p[len] = saved_char;
return (DECFLOAT);
@@ -1118,7 +1118,7 @@
{
p[len - 2] = '\0';
putithere->typed_val_decfloat.type
- = builtin_type (current_gdbarch)->builtin_declong;
+ = builtin_type_declong;
decimal_from_string (putithere->typed_val_decfloat.val, 16, p);
p[len] = saved_char;
return (DECFLOAT);
Index: git/gdb/gdbtypes.c
===================================================================
--- git.orig/gdb/gdbtypes.c 2007-11-05 11:37:24.000000000 -0800
+++ git/gdb/gdbtypes.c 2007-11-07 06:40:37.000000000 -0800
@@ -103,7 +103,9 @@
struct type *builtin_type_arm_ext;
struct type *builtin_type_ia64_spill;
struct type *builtin_type_ia64_quad;
-
+struct type *builtin_type_decfloat;
+struct type *builtin_type_decdouble;
+struct type *builtin_type_declong;

int opaque_type_resolution = 1;
static void
@@ -3172,21 +3174,6 @@
0,
"bool", (struct objfile *) NULL);

- /* The following three are about decimal floating point types, which
- are 32-bits, 64-bits and 128-bits respectively. */
- builtin_type->builtin_decfloat
- = init_type (TYPE_CODE_DECFLOAT, 32 / 8,
- 0,
- "decimal float", (struct objfile *) NULL);
- builtin_type->builtin_decdouble
- = init_type (TYPE_CODE_DECFLOAT, 64 / 8,
- 0,
- "decimal double", (struct objfile *) NULL);
- builtin_type->builtin_declong
- = init_type (TYPE_CODE_DECFLOAT, 128 / 8,
- 0,
- "decimal long double", (struct objfile *) NULL);
-
/* Pointer/Address types. */

/* NOTE: on some targets, addresses and pointers are not necessarily
@@ -3305,6 +3292,21 @@
TYPE_FLAG_UNSIGNED,
"uint128_t", (struct objfile *) NULL);

+ builtin_type_decfloat
+ = init_type (TYPE_CODE_DECFLOAT, 32 / 8,
+ 0,
+ "decimal float", (struct objfile *) NULL);
+
+ builtin_type_decdouble
+ = init_type (TYPE_CODE_DECFLOAT, 64 / 8,
+ 0,
+ "decimal double", (struct objfile *) NULL);
+
+ builtin_type_declong
+ = init_type (TYPE_CODE_DECFLOAT, 128 / 8,
+ 0,
+ "decimal long double", (struct objfile *) NULL);
+
builtin_type_ieee_single =
build_flt (-1, "builtin_type_ieee_single", floatformats_ieee_single);
builtin_type_ieee_double =
Index: git/gdb/gdbtypes.h
===================================================================
--- git.orig/gdb/gdbtypes.h 2007-11-05 11:37:24.000000000 -0800
+++ git/gdb/gdbtypes.h 2007-11-07 06:41:15.000000000 -0800
@@ -1016,9 +1016,6 @@
struct type *builtin_bool;
struct type *builtin_long_long;
struct type *builtin_unsigned_long_long;
- struct type *builtin_decfloat;
- struct type *builtin_decdouble;
- struct type *builtin_declong;
};

/* Return the type table for the specified architecture. */
@@ -1107,6 +1104,9 @@
extern struct type *builtin_type_arm_ext;
extern struct type *builtin_type_ia64_spill;
extern struct type *builtin_type_ia64_quad;
+extern struct type *builtin_type_decfloat;
+extern struct type *builtin_type_decdouble;
+extern struct type *builtin_type_declong;

/* This type represents a type that was unrecognized in symbol
read-in. */
Index: git/gdb/features/rs6000/powerpc-64.c
===================================================================
--- git.orig/gdb/features/rs6000/powerpc-64.c 2007-11-05 11:37:24.000000000 -0800
+++ git/gdb/features/rs6000/powerpc-64.c 2007-11-06 04:20:22.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: git/gdb/features/rs6000/powerpc-32.c
===================================================================
--- git.orig/gdb/features/rs6000/powerpc-32.c 2007-10-21 12:33:37.000000000 -0700
+++ git/gdb/features/rs6000/powerpc-32.c 2007-11-07 06:52:18.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: git/gdb/doc/gdb.texinfo
===================================================================
--- git.orig/gdb/doc/gdb.texinfo 2007-11-06 04:20:21.000000000 -0800
+++ git/gdb/doc/gdb.texinfo 2007-11-07 07:12:34.000000000 -0800
@@ -26322,6 +26322,13 @@
these to present registers @samp{ev0} through @samp{ev31} to the
user.

+The @samp{org.gnu.gdb.power.dfp128} feature is optional and i 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 combined the pair of Floating
+Point registers to present a single @samp{dl} register.
+
@include gpl.texi

@raisesections
Index: git/gdb/testsuite/gdb.arch/powerpc-d128-regs.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ git/gdb/testsuite/gdb.arch/powerpc-d128-regs.c 2007-11-06 09:41:24.000000000 -0800
@@ -0,0 +1,25 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2007 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 the PPC64 Decimal128 pseudo-registers. */
+
+int main(void)
+{
+ _Decimal128 d128 = 1.2345678910dl;
+
+ return 0;
+}
Index: git/gdb/testsuite/gdb.arch/powerpc-d128-regs.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ git/gdb/testsuite/gdb.arch/powerpc-d128-regs.exp 2007-11-06 10:04:57.000000000 -0800
@@ -0,0 +1,77 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2007
+# 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 the PPC64 Decimal128 pseudo-registers.
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+if ![istarget "powerpc64-*"] then {
+ verbose "Skipping powerpc64 Decimal128 pseudo-registers testcase."
+ return
+}
+
+set testfile "powerpc64-d128-regs"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {quiet debug}] != "" } {
+ untested printcmds.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:common64.*" "Checking for PPC64 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"
+
+}