This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 11/24] MIPS: Add support for hybrid fp32/fp64 mode
- From: Bhushan Attarde <bhushan dot attarde at imgtec dot com>
- To: <gdb-patches at sourceware dot org>
- Cc: <Maciej dot Rozycki at imgtec dot com>, <Matthew dot Fortune at imgtec dot com>, <James dot Hogan at imgtec dot com>, <Andrew dot Bennett at imgtec dot com>, <Jaydeep dot Patil at imgtec dot com>, Bhushan Attarde <bhushan dot attarde at imgtec dot com>
- Date: Mon, 27 Jun 2016 20:19:38 +0530
- Subject: [PATCH 11/24] MIPS: Add support for hybrid fp32/fp64 mode
- Authentication-results: sourceware.org; auth=none
- References: <1467038991-6600-1-git-send-email-bhushan dot attarde at imgtec dot com>
Add support for the new hybrid fp32/fp64 mode, which is intended to allow
fp32 (which requires FR=0) and MSA code (which requires FR=1) to coexist
in the same process. It is determined by the FRE bit in the Config5
register, which causes single-precision instructions to trap so that the
kernel can emulate them using the FR=0 aliasing of singles in even
doubles.
When FR=1 and FRE=1:
- Even FP registers can be interpreted as a double, or a single (aliasing
the least significant half of the double).
- Odd FP registers can be interpreted as a double, or a single (aliasing
the most significant half of the even double).
The even registers can use the existing fp64 type, however a new type is
required for the odd FP registers when FRE=1, since the single and double
interpretations don't alias. It is equivalent to this C definition:
struct fp96 {
union {
double f64;
int64 i64;
};
union {
float f32;
int32 i32;
};
};
For example:
(gdb) info float
...
f30: 0x41f8000041f00000 flt: 30 dbl: 6442451998.9999998
f31: 0x403f000000000000 flt: 31 dbl: 31
(gdb) p $f30
$1 = {f32 = 30, f64 = 6442451998.9999998,
i32 = 1106247680, i64 = 4753549407795806208}
(gdb) p $f31
$2 = {f32 = 31, f64 = 31,
i32 = 1106771968, i64 = 4629418941960159232}
gdb/ChangeLog:
* mips-linux-tdep.c (mips_linux_init_abi): Drop fixed register
mode.
* mips-tdep.c (CONF5_FRE): New define.
(mips_set_float_regsize): Detect MIPS_FPU_HYBRID from Config5.FRE.
(mips_float_regsize, mips_get_fp_single_location): Handle
MIPS_FPU_HYBRID.
(mips_get_fp_multi_location, mips_pseudo_register_read,
mips_pseudo_register_write, mips_convert_register_p,
mips_register_to_value, mips_value_to_register, mips_fp_type,
mips_read_fp_register_single): Handle fp96.
(mips_float_hybrid, mips_convert_register_float_fre_case_p,
mips_fp96_type): New function.
(mips_gdbarch_init): Initialise tdep->fp96_type.
(mips_register_type, mips_print_fp_register): Use
mips_float_regsize to decide size of fp registers.
* mips-tdep.h (enum mips_fpu_mode): Add MIPS_FPU_HYBRID.
(struct gdbarch_tdep): Update fp_mode comment, add fp96_type.
---
gdb/mips-linux-tdep.c | 1 -
gdb/mips-tdep.c | 182 +++++++++++++++++++++++++++++++++++++++++---------
gdb/mips-tdep.h | 7 +-
3 files changed, 157 insertions(+), 33 deletions(-)
diff --git a/gdb/mips-linux-tdep.c b/gdb/mips-linux-tdep.c
index da5ccc0..d337a5b 100644
--- a/gdb/mips-linux-tdep.c
+++ b/gdb/mips-linux-tdep.c
@@ -1725,7 +1725,6 @@ mips_linux_init_abi (struct gdbarch_info info,
mips_gdb_signal_to_target);
tdep->syscall_next_pc = mips_linux_syscall_next_pc;
- tdep->fp_register_mode_fixed_p = 1;
if (((struct gdbarch_tdep_info*)(info.tdep_info))->tdesc_data)
{
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index 2cc49aa..13c1532 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -78,6 +78,10 @@ static void mips_print_float_info (struct gdbarch *, struct ui_file *,
/* This bit is set if we are emulating 32-bit FPRs on a 64-bit chip. */
#define ST0_FR (1 << 26)
+/* A useful bit in the CP0 Config5 register.
+ This bit is set in hybrid 64-bit FPR mode. */
+#define CONF5_FRE (1 << 8)
+
/* The sizes of floating point registers. */
enum
@@ -307,29 +311,36 @@ mips_set_float_regsize (struct gdbarch *gdbarch, struct regcache *regcache)
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
struct gdbarch_tdep_info tdep_info = { NULL };
struct gdbarch_info info;
- int fpsize;
enum mips_fpu_mode fp_mode;
+ enum register_status status;
+ ULONGEST sr, config5;
if (tdep->fp_register_mode_fixed_p)
return 0;
- fpsize = mips_isa_regsize (gdbarch);
- fp_mode = fpsize == 8 ? MIPS_FPU_64 : MIPS_FPU_32;
- if (fpsize == 8)
- {
- enum register_status status;
- ULONGEST sr;
+ status = regcache_raw_read_unsigned (regcache, MIPS_PS_REGNUM, &sr);
+ if (status == REG_VALID)
+ fp_mode = (sr & ST0_FR) ? MIPS_FPU_64 : MIPS_FPU_32;
+ else
+ fp_mode = mips_isa_regsize (gdbarch) == 8 ? MIPS_FPU_64 : MIPS_FPU_32;
- status = regcache_raw_read_unsigned (regcache, MIPS_PS_REGNUM, &sr);
- if (status == REG_VALID)
- fp_mode = (sr & ST0_FR) ? MIPS_FPU_64 : MIPS_FPU_32;
+ if (fp_mode == MIPS_FPU_64 && mips_regnum (gdbarch)->config5 >= 0)
+ {
+ /* Find out if FRE is set */
+ status = regcache_raw_read_unsigned (regcache,
+ mips_regnum (gdbarch)->config5,
+ &config5);
+ if (status == REG_VALID && config5 & CONF5_FRE)
+ fp_mode = MIPS_FPU_HYBRID;
}
if (fp_mode == tdep->fp_mode)
return 0;
- /* Need a new gdbarch, go get one. */
+ /* Need a new gdbarch, go get one.
+ Be careful to preserve target description. */
gdbarch_info_init (&info);
+ info.target_desc = target_current_description ();
info.tdep_info = &tdep_info;
((struct gdbarch_tdep_info*)(info.tdep_info))->fp_mode = fp_mode;
gdbarch_update_p (info);
@@ -347,12 +358,22 @@ mips_float_regsize (struct gdbarch *gdbarch)
case MIPS_FPU_32:
return 4;
case MIPS_FPU_64:
+ case MIPS_FPU_HYBRID:
return 8;
default:
return 0;
}
}
+/* Return whether the FPU is currently in hybrid 64-bit mode (where odd singles
+ are found in the top half of the 64-bit even FP registers. */
+
+static int
+mips_float_hybrid (struct gdbarch *gdbarch)
+{
+ return gdbarch_tdep (gdbarch)->fp_mode == MIPS_FPU_HYBRID;
+}
+
/* MIPS16/microMIPS function addresses are odd (bit 0 is set). Here
are some functions to handle addresses associated with compressed
code including but not limited to testing, setting, or clearing
@@ -835,6 +856,7 @@ mips_get_fp_single_location (struct gdbarch *gdbarch,
switch (fp_mode)
{
case MIPS_FPU_32:
+ case MIPS_FPU_HYBRID:
loc->regnum = raw_num + (idx & ~1);
loc->offset = 4 * (big_endian ^ (idx & 1));
return 1;
@@ -892,7 +914,7 @@ mips_get_fp_double_location (struct gdbarch *gdbarch,
}
/* Get the raw register part(s) composing a cooked float register.
- Returns the number of parts (maximum 2) written through loc. */
+ Returns the number of parts (maximum 3) written through loc. */
static unsigned int
mips_get_fp_multi_location (struct gdbarch *gdbarch,
@@ -904,8 +926,9 @@ mips_get_fp_multi_location (struct gdbarch *gdbarch,
/* The cooked formats supported are:
fp32 (len=4): just a single.
- fp64 (len=8): double (with aliased single). */
- if (cooked_len > 8 || cooked_len < 4 || cooked_len & 0x3)
+ fp64 (len=8): double (with aliased single).
+ fp96 (len=12): consecutive double and single. */
+ if (cooked_len > 12 || cooked_len < 4 || cooked_len & 0x3)
internal_error (__FILE__, __LINE__, _("bad cooked register size"));
/* Formats containing a distinct double. */
@@ -988,7 +1011,7 @@ mips_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
if (mips_float_register_p (gdbarch, rawnum))
{
- struct mips_reg_part loc[2];
+ struct mips_reg_part loc[3];
unsigned int parts;
fpnum = rawnum - mips_regnum (gdbarch)->fp0;
@@ -1034,7 +1057,7 @@ mips_pseudo_register_write (struct gdbarch *gdbarch,
if (mips_float_register_p (gdbarch, rawnum))
{
- struct mips_reg_part loc[2];
+ struct mips_reg_part loc[3];
unsigned int parts;
fpnum = rawnum - mips_regnum (gdbarch)->fp0;
@@ -1149,6 +1172,20 @@ mips_convert_register_float_case_p (struct gdbarch *gdbarch, int regnum,
&& TYPE_CODE (type) == TYPE_CODE_FLT && TYPE_LENGTH (type) == 4);
}
+/* This predicate tests for the case of a 4 or 8 byte floating point
+ value that is being transferred to or from a floating point
+ register which is 12 bytes wide (containing both single and double). */
+
+static int
+mips_convert_register_float_fre_case_p (struct gdbarch *gdbarch,
+ int regnum, struct type *type)
+{
+ return (register_size (gdbarch, regnum) == 12
+ && mips_float_register_p (gdbarch, regnum)
+ && TYPE_CODE (type) == TYPE_CODE_FLT
+ && (TYPE_LENGTH (type) == 4 || TYPE_LENGTH (type) == 8));
+}
+
/* This predicate tests for the case of a value of less than 8
bytes in width that is being transfered to or from an 8 byte
general purpose register. */
@@ -1168,6 +1205,7 @@ mips_convert_register_p (struct gdbarch *gdbarch,
int regnum, struct type *type)
{
return (mips_convert_register_float_case_p (gdbarch, regnum, type)
+ || mips_convert_register_float_fre_case_p (gdbarch, regnum, type)
|| mips_convert_register_gpreg_case_p (gdbarch, regnum, type));
}
@@ -1196,6 +1234,26 @@ mips_register_to_value (struct frame_info *frame, int regnum,
*optimizedp = *unavailablep = 0;
return 1;
}
+ else if (mips_convert_register_float_fre_case_p (gdbarch, regnum, type))
+ {
+ int len = TYPE_LENGTH (type);
+ if (len == 8)
+ {
+ /* double comes first */
+ if (!get_frame_register_bytes (frame, regnum, 0, 8, to,
+ optimizedp, unavailablep))
+ return 0;
+ }
+ else
+ {
+ /* followed by single */
+ if (!get_frame_register_bytes (frame, regnum, 8, 4, to,
+ optimizedp, unavailablep))
+ return 0;
+ }
+ *optimizedp = *unavailablep = 0;
+ return 1;
+ }
else if (mips_convert_register_gpreg_case_p (gdbarch, regnum, type))
{
int len = TYPE_LENGTH (type);
@@ -1314,6 +1372,56 @@ mips_fp64_type (struct gdbarch *gdbarch)
return tdep->fp64_type;
}
+/* Get FRE odd floating point type, which can contains separate single and
+ double precision floats, or a separate 32-bit or 64-bit signed integer.
+ This is used for odd fp registers when FR=1 and FRE=1 (the odd single comes
+ from the upper half of the even double, so odd singles and odd doubles do not
+ overlap). */
+
+static struct type *
+mips_fp96_type (struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (tdep->fp96_type == NULL)
+ {
+ const struct builtin_type *bt = builtin_type (gdbarch);
+ struct type *t;
+ struct field *f;
+
+ /* The type we're building is roughly this:
+
+ struct __gdb_builtin_mips_fp96 {
+ union {
+ double f64;
+ int64 i64;
+ };
+ union {
+ float f32;
+ int32 i32;
+ };
+ }; */
+
+
+ t = arch_composite_type (gdbarch, "__gdb_builtin_type_mips_fp96",
+ TYPE_CODE_STRUCT);
+ f = append_composite_type_field_raw (t, "f32", bt->builtin_float);
+ SET_FIELD_BITPOS (*f, 64);
+ f = append_composite_type_field_raw (t, "f64", bt->builtin_double);
+ SET_FIELD_BITPOS (*f, 0);
+ f = append_composite_type_field_raw (t, "i32", bt->builtin_int32);
+ SET_FIELD_BITPOS (*f, 64);
+ f = append_composite_type_field_raw (t, "i64", bt->builtin_int64);
+ SET_FIELD_BITPOS (*f, 0);
+
+ TYPE_LENGTH (t) = 12;
+ TYPE_NAME (t) = "fp96";
+ tdep->fp96_type = t;
+ }
+
+ return tdep->fp96_type;
+}
+
/* Get the floating point type for an arbitrary FP register. This returns the
appropriate type depending on the possible types and overlaps of the
register. */
@@ -1321,9 +1429,12 @@ mips_fp64_type (struct gdbarch *gdbarch)
static struct type *
mips_fp_type (struct gdbarch *gdbarch, int fpnum)
{
- if ((fpnum & 1) == 0 || mips_float_regsize (gdbarch) == 8)
+ if ((fpnum & 1) == 1 && mips_float_hybrid (gdbarch) )
+ /* 64-bit hybrid registers: odd singles and doubles don't overlap. */
+ return mips_fp96_type (gdbarch);
+ else if ((fpnum & 1) == 0 || mips_float_regsize (gdbarch) == 8)
/* Even singles and doubles always overlap, as do odd singles and
- doubles when FR=1. */
+ doubles when FR=1 (and FRE=0). */
return mips_fp64_type (gdbarch);
else
/* 32-bit odd singles (there are no odd doubles). */
@@ -1517,6 +1628,16 @@ mips_value_to_register (struct frame_info *frame, int regnum,
else
put_frame_register_bytes (frame, regnum, 0, 4, from);
}
+ else if (mips_convert_register_float_fre_case_p (gdbarch, regnum, type))
+ {
+ int len = TYPE_LENGTH (type);
+ if (len == 8)
+ /* double goes first */
+ put_frame_register_bytes (frame, regnum, 0, 8, from);
+ else
+ /* followed by single */
+ put_frame_register_bytes (frame, regnum, 8, 4, from);
+ }
else if (mips_convert_register_gpreg_case_p (gdbarch, regnum, type))
{
gdb_byte fill[8];
@@ -1564,11 +1685,12 @@ mips_register_type (struct gdbarch *gdbarch, int regnum)
if (regnum < gdbarch_num_regs (gdbarch))
{
/* The raw or ISA registers. These are all sized according to
- the ISA regsize. */
+ the ISA regsize, except FP registers which may be double
+ even on MIPS32 since rev 2 of the architecture. */
int regsize = mips_isa_regsize (gdbarch);
if (mips_float_register_p (gdbarch, regnum))
- return (regsize == 4
+ return (mips_float_regsize (gdbarch) == 4
? builtin_type (gdbarch)->builtin_float
: builtin_type (gdbarch)->builtin_double);
else
@@ -6776,8 +6898,12 @@ mips_read_fp_register_single (struct frame_info *frame, int regno,
if (!deprecated_frame_register_read (frame, regno, raw_buffer))
return 0;
- if (cooked_size == 8)
- /* FR=1
+ if (cooked_size == 12)
+ /* FR=1, FRE=1
+ Single is after double. */
+ memcpy(rare_buffer, raw_buffer+8, 8);
+ else if (cooked_size == 8)
+ /* FR=1, FRE=0
Single is overlapping double. */
memcpy(rare_buffer, raw_buffer + 4*big_endian, 4);
else
@@ -6814,7 +6940,7 @@ mips_print_fp_register (struct ui_file *file, struct frame_info *frame,
int regnum)
{ /* Do values for FP (float) regs. */
struct gdbarch *gdbarch = get_frame_arch (frame);
- int fpsize = register_size (gdbarch, regnum);
+ int fpsize = mips_float_regsize (gdbarch);
gdb_byte *raw_buffer;
double doub, flt1; /* Doubles extracted from raw hex data. */
int res1, res2, inv1, inv2;
@@ -8910,7 +9036,6 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
const struct tdesc_feature *feature;
int valid_p;
int fpsize;
- enum mips_fpu_mode fpmode;
feature = tdesc_find_feature (info.target_desc,
"org.gnu.gdb.mips.cpu");
@@ -8983,12 +9108,11 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
supplied the description got the current setting right wrt
CP0 Status register's bit FR if applicable. */
fpsize = tdesc_register_size (feature, mips_fprs[0]) / 8;
- fpmode = (fpsize == 8) ? MIPS_FPU_64 : MIPS_FPU_32;
/* Only accept a description whose floating-point register size
matches the requested size or if none was specified. */
- valid_p = (info.tdep_info->fp_mode == MIPS_FPU_UNKNOWN
- || info.tdep_info->fp_mode == fpmode);
+ valid_p = 1;
+
for (i = 0; i < 32; i++)
valid_p &= tdesc_numbered_register (feature, tdesc_data,
i + mips_regnum.fp0, mips_fprs[i]);
@@ -9043,9 +9167,6 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
}
}
- /* Fix the floating-point register mode found. */
- info.tdep_info->fp_mode = fpmode;
-
/* It would be nice to detect an attempt to use a 64-bit ABI
when only 32-bit registers are provided. */
reg_names = NULL;
@@ -9287,6 +9408,7 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->fp_ir_type = NULL;
tdep->fp32_type = NULL;
tdep->fp64_type = NULL;
+ tdep->fp96_type = NULL;
if (info.target_desc)
{
diff --git a/gdb/mips-tdep.h b/gdb/mips-tdep.h
index ad5cba3..110eb6c 100644
--- a/gdb/mips-tdep.h
+++ b/gdb/mips-tdep.h
@@ -89,6 +89,8 @@ enum mips_fpu_mode
MIPS_FPU_UNKNOWN = 0,
MIPS_FPU_32, /* FR=0, 32bit FP regs, doubles in pairs. */
MIPS_FPU_64, /* FR=1, 64bit FP regs. */
+ MIPS_FPU_HYBRID, /* FR=1, FRE=1, 64bit FP regs, odd singles in upper half
+ of even doubles. */
};
/* MIPS specific per-architecture information. */
@@ -123,8 +125,8 @@ struct gdbarch_tdep
int register_size;
/* The floating-point register mode determined at run time.
- This corresponds to CP0 Status register's FR bit unless fixed_p is
- set. */
+ This corresponds to CP0 Status register's FR bit and Config5's FRE bit
+ unless fixed_p is set. */
int fp_register_mode_fixed_p;
enum mips_fpu_mode fp_mode;
@@ -136,6 +138,7 @@ struct gdbarch_tdep
struct type *fp_ir_type;
struct type *fp32_type;
struct type *fp64_type;
+ struct type *fp96_type;
/* Return the expected next PC if FRAME is stopped at a syscall
instruction. */
--
1.9-rc2