From patchwork Mon Jun 27 14:49:38 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bhushan Attarde X-Patchwork-Id: 13392 Received: (qmail 11242 invoked by alias); 27 Jun 2016 14:50:45 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 11170 invoked by uid 89); 27 Jun 2016 14:50:44 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=3.7 required=5.0 tests=AWL, BAYES_00, GARBLED_SUBJECT, RCVD_IN_DNSWL_NONE, RP_MATCHES_RCVD, SPF_PASS autolearn=no version=3.3.2 spammy=chip, odd, transfered, 1387 X-HELO: mailapp01.imgtec.com Received: from mailapp01.imgtec.com (HELO mailapp01.imgtec.com) (195.59.15.196) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 27 Jun 2016 14:50:39 +0000 Received: from hhmail02.hh.imgtec.org (unknown [10.100.10.20]) by Forcepoint Email with ESMTPS id 90635C90A8474 for ; Mon, 27 Jun 2016 15:50:32 +0100 (IST) Received: from pudesk170.pu.imgtec.org (192.168.93.65) by hhmail02.hh.imgtec.org (10.100.10.20) with Microsoft SMTP Server (TLS) id 14.3.294.0; Mon, 27 Jun 2016 15:50:35 +0100 From: Bhushan Attarde To: CC: , , , , , Bhushan Attarde Subject: [PATCH 11/24] MIPS: Add support for hybrid fp32/fp64 mode Date: Mon, 27 Jun 2016 20:19:38 +0530 Message-ID: <1467038991-6600-11-git-send-email-bhushan.attarde@imgtec.com> In-Reply-To: <1467038991-6600-1-git-send-email-bhushan.attarde@imgtec.com> References: <1467038991-6600-1-git-send-email-bhushan.attarde@imgtec.com> MIME-Version: 1.0 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. */