diff options
author | Daniel Jacobowitz <drow@false.org> | 2009-07-28 18:26:51 +0000 |
---|---|---|
committer | Daniel Jacobowitz <drow@false.org> | 2009-07-28 18:26:51 +0000 |
commit | 58d6951de562a3ef2214927006541fc16f0e5bd0 (patch) | |
tree | 53dbf386a054ee94e4559b475732f0ef2c821f95 /gdb/arm-tdep.c | |
parent | 12b42a12507a884c4e78b2a8c31ecab4d4534ae6 (diff) | |
download | gdb-58d6951de562a3ef2214927006541fc16f0e5bd0.zip gdb-58d6951de562a3ef2214927006541fc16f0e5bd0.tar.gz gdb-58d6951de562a3ef2214927006541fc16f0e5bd0.tar.bz2 |
* NEWS: Mention ARM VFP support.
* target-descriptions.c (tdesc_register_type): Make public.
(tdesc_unnumbered_register): New function.
(tdesc_register_reggroup_p): Allow missing
pseudo_register_reggroup_p.
* target-descriptions.h (tdesc_register_type): Declare.
(tdesc_unnumbered_register): Declare.
* arm-tdep.c (arm_neon_quad_read, arm_neon_quad_write): New functions.
(arm_push_dummy_call): Use arm_neon_quad_write.
(arm_neon_double_type, arm_neon_quad_type): New functions.
(arm_register_type): Handle VFP and NEON registers. Override the
types of double-precision registers for NEON. Disable FPA registers
if they are not present.
(arm_dwarf_reg_to_regnum): Add current VFP and NEON register numbers.
(arm_return_value): Use arm_neon_quad_write and arm_neon_quad_read.
(arm_register_name): Handle VFP single and NEON quad registers.
(arm_pseudo_read, arm_pseudo_write): New functions.
(arm_gdbarch_init): Check for VFP and NEON in the target description.
Assign numbers to double-precision registers. Register VFP and NEON
pseudo registers. Remove a shadowed "i" variable.
* arm-tdep.h (enum gdb_regnum): Add ARM_D0_REGNUM and
ARM_D31_REGNUM.
(struct gdbarch_tdep): Add have_neon_pseudos, have_neon,
have_vfp_registers, have_vfp_pseudos, neon_double_type,
and neon_quad_type.
* features/Makefile: Make expedite settings only architecture
specific.
(WHICH): Add new ARM descriptions.
* features/arm-with-neon.xml, features/arm-with-vfpv2.c,
features/arm-with-vfpv3.c, features/arm-vfpv2.xml,
features/arm-vfpv3.xml, features/arm-with-vfpv2.xml,
features/arm-with-vfpv3.xml, features/arm-with-neon.c: New files.
* regformats/arm-with-neon.dat, regformats/arm-with-vfpv2.dat,
regformats/arm-with-vfpv3.dat: Generate.
doc/
* gdb.texinfo (ARM Features): Document org.gnu.gdb.arm.vfp and
org.gnu.gdb.arm.neon.
gdbserver/
* linux-low.c (linux_write_memory): Update debugging output.
* Makefile.in (clean): Add new descriptions.
(arm-with-vfpv2.o, arm-with-vfpv2.c, arm-with-vfpv3.o)
(arm-with-vfpv3.c, arm-with-neon.o, arm-with-neon.c): New rules.
* configure.srv: Add new files for arm*-*-linux*.
* linux-arm-low.c: Add new declarations.
(PTRACE_GETVFPREGS, PTRACE_SETVFPREGS): Define if undefined.
(arm_hwcap, HWCAP_VFP, HWCAP_IWMMXT, HWCAP_NEON, HWCAP_VFPv3)
(HWCAP_VFPv3D16): New.
(arm_fill_wmmxregset, arm_store_wmmxregset): Check HWCAP_IWMMXT
instead of __IWMMXT__.
(arm_fill_vfpregset, arm_store_vfpregset, arm_get_hwcap)
(arm_arch_setup): New.
(target_regsets): Remove #ifdef. Add VFP regset.
(the_low_target): Use arm_arch_setup.
testsuite/
* gdb.base/float.exp: Handle VFP registers.
Diffstat (limited to 'gdb/arm-tdep.c')
-rw-r--r-- | gdb/arm-tdep.c | 446 |
1 files changed, 428 insertions, 18 deletions
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index 733318d..3cc8d01 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -208,6 +208,13 @@ static void convert_from_extended (const struct floatformat *, const void *, static void convert_to_extended (const struct floatformat *, void *, const void *, int); +static void arm_neon_quad_read (struct gdbarch *gdbarch, + struct regcache *regcache, + int regnum, gdb_byte *buf); +static void arm_neon_quad_write (struct gdbarch *gdbarch, + struct regcache *regcache, + int regnum, const gdb_byte *buf); + struct arm_prologue_cache { /* The stack pointer at the time this frame was created; i.e. the @@ -1709,11 +1716,17 @@ arm_push_dummy_call (struct gdbarch *gdbarch, struct value *function, { char name_buf[4]; int regnum; - sprintf (name_buf, "%c%d", reg_char, reg_scaled + i); - regnum = user_reg_map_name_to_regnum (gdbarch, name_buf, - strlen (name_buf)); - regcache_cooked_write (regcache, regnum, + if (reg_char == 'q') + arm_neon_quad_write (gdbarch, regcache, reg_scaled + i, val + i * unit_length); + else + { + sprintf (name_buf, "%c%d", reg_char, reg_scaled + i); + regnum = user_reg_map_name_to_regnum (gdbarch, name_buf, + strlen (name_buf)); + regcache_cooked_write (regcache, regnum, + val + i * unit_length); + } } continue; } @@ -1874,14 +1887,115 @@ arm_ext_type (struct gdbarch *gdbarch) return tdep->arm_ext_type; } +static struct type * +arm_neon_double_type (struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + if (tdep->neon_double_type == NULL) + { + struct type *t, *elem; + + t = arch_composite_type (gdbarch, "__gdb_builtin_type_neon_d", + TYPE_CODE_UNION); + elem = builtin_type (gdbarch)->builtin_uint8; + append_composite_type_field (t, "u8", init_vector_type (elem, 8)); + elem = builtin_type (gdbarch)->builtin_uint16; + append_composite_type_field (t, "u16", init_vector_type (elem, 4)); + elem = builtin_type (gdbarch)->builtin_uint32; + append_composite_type_field (t, "u32", init_vector_type (elem, 2)); + elem = builtin_type (gdbarch)->builtin_uint64; + append_composite_type_field (t, "u64", elem); + elem = builtin_type (gdbarch)->builtin_float; + append_composite_type_field (t, "f32", init_vector_type (elem, 2)); + elem = builtin_type (gdbarch)->builtin_double; + append_composite_type_field (t, "f64", elem); + + TYPE_VECTOR (t) = 1; + TYPE_NAME (t) = "neon_d"; + tdep->neon_double_type = t; + } + + return tdep->neon_double_type; +} + +/* FIXME: The vector types are not correctly ordered on big-endian + targets. Just as s0 is the low bits of d0, d0[0] is also the low + bits of d0 - regardless of what unit size is being held in d0. So + the offset of the first uint8 in d0 is 7, but the offset of the + first float is 4. This code works as-is for little-endian + targets. */ + +static struct type * +arm_neon_quad_type (struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + if (tdep->neon_quad_type == NULL) + { + struct type *t, *elem; + + t = arch_composite_type (gdbarch, "__gdb_builtin_type_neon_q", + TYPE_CODE_UNION); + elem = builtin_type (gdbarch)->builtin_uint8; + append_composite_type_field (t, "u8", init_vector_type (elem, 16)); + elem = builtin_type (gdbarch)->builtin_uint16; + append_composite_type_field (t, "u16", init_vector_type (elem, 8)); + elem = builtin_type (gdbarch)->builtin_uint32; + append_composite_type_field (t, "u32", init_vector_type (elem, 4)); + elem = builtin_type (gdbarch)->builtin_uint64; + append_composite_type_field (t, "u64", init_vector_type (elem, 2)); + elem = builtin_type (gdbarch)->builtin_float; + append_composite_type_field (t, "f32", init_vector_type (elem, 4)); + elem = builtin_type (gdbarch)->builtin_double; + append_composite_type_field (t, "f64", init_vector_type (elem, 2)); + + TYPE_VECTOR (t) = 1; + TYPE_NAME (t) = "neon_q"; + tdep->neon_quad_type = t; + } + + return tdep->neon_quad_type; +} + /* Return the GDB type object for the "standard" data type of data in register N. */ static struct type * arm_register_type (struct gdbarch *gdbarch, int regnum) { + int num_regs = gdbarch_num_regs (gdbarch); + + if (gdbarch_tdep (gdbarch)->have_vfp_pseudos + && regnum >= num_regs && regnum < num_regs + 32) + return builtin_type (gdbarch)->builtin_float; + + if (gdbarch_tdep (gdbarch)->have_neon_pseudos + && regnum >= num_regs + 32 && regnum < num_regs + 32 + 16) + return arm_neon_quad_type (gdbarch); + + /* If the target description has register information, we are only + in this function so that we can override the types of + double-precision registers for NEON. */ + if (tdesc_has_registers (gdbarch_target_desc (gdbarch))) + { + struct type *t = tdesc_register_type (gdbarch, regnum); + + if (regnum >= ARM_D0_REGNUM && regnum < ARM_D0_REGNUM + 32 + && TYPE_CODE (t) == TYPE_CODE_FLT + && gdbarch_tdep (gdbarch)->have_neon) + return arm_neon_double_type (gdbarch); + else + return t; + } + if (regnum >= ARM_F0_REGNUM && regnum < ARM_F0_REGNUM + NUM_FREGS) - return arm_ext_type (gdbarch); + { + if (!gdbarch_tdep (gdbarch)->have_fpa_registers) + return builtin_type (gdbarch)->builtin_void; + + return arm_ext_type (gdbarch); + } else if (regnum == ARM_SP_REGNUM) return builtin_type (gdbarch)->builtin_data_ptr; else if (regnum == ARM_PC_REGNUM) @@ -1925,6 +2039,34 @@ arm_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg) if (reg >= 192 && reg <= 199) return ARM_WC0_REGNUM + reg - 192; + /* VFP v2 registers. A double precision value is actually + in d1 rather than s2, but the ABI only defines numbering + for the single precision registers. This will "just work" + in GDB for little endian targets (we'll read eight bytes, + starting in s0 and then progressing to s1), but will be + reversed on big endian targets with VFP. This won't + be a problem for the new Neon quad registers; you're supposed + to use DW_OP_piece for those. */ + if (reg >= 64 && reg <= 95) + { + char name_buf[4]; + + sprintf (name_buf, "s%d", reg - 64); + return user_reg_map_name_to_regnum (gdbarch, name_buf, + strlen (name_buf)); + } + + /* VFP v3 / Neon registers. This range is also used for VFP v2 + registers, except that it now describes d0 instead of s0. */ + if (reg >= 256 && reg <= 287) + { + char name_buf[4]; + + sprintf (name_buf, "d%d", reg - 256); + return user_reg_map_name_to_regnum (gdbarch, name_buf, + strlen (name_buf)); + } + return -1; } @@ -2874,17 +3016,31 @@ arm_return_value (struct gdbarch *gdbarch, struct type *func_type, int i; for (i = 0; i < vfp_base_count; i++) { - char name_buf[4]; - int regnum; - sprintf (name_buf, "%c%d", reg_char, i); - regnum = user_reg_map_name_to_regnum (gdbarch, name_buf, - strlen (name_buf)); - if (writebuf) - regcache_cooked_write (regcache, regnum, - writebuf + i * unit_length); - if (readbuf) - regcache_cooked_read (regcache, regnum, - readbuf + i * unit_length); + if (reg_char == 'q') + { + if (writebuf) + arm_neon_quad_write (gdbarch, regcache, i, + writebuf + i * unit_length); + + if (readbuf) + arm_neon_quad_read (gdbarch, regcache, i, + readbuf + i * unit_length); + } + else + { + char name_buf[4]; + int regnum; + + sprintf (name_buf, "%c%d", reg_char, i); + regnum = user_reg_map_name_to_regnum (gdbarch, name_buf, + strlen (name_buf)); + if (writebuf) + regcache_cooked_write (regcache, regnum, + writebuf + i * unit_length); + if (readbuf) + regcache_cooked_read (regcache, regnum, + readbuf + i * unit_length); + } } return RETURN_VALUE_REGISTER_CONVENTION; } @@ -3141,6 +3297,32 @@ set_disassembly_style_sfunc (char *args, int from_tty, static const char * arm_register_name (struct gdbarch *gdbarch, int i) { + const int num_regs = gdbarch_num_regs (gdbarch); + + if (gdbarch_tdep (gdbarch)->have_vfp_pseudos + && i >= num_regs && i < num_regs + 32) + { + static const char *const vfp_pseudo_names[] = { + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", + "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", + "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31", + }; + + return vfp_pseudo_names[i - num_regs]; + } + + if (gdbarch_tdep (gdbarch)->have_neon_pseudos + && i >= num_regs + 32 && i < num_regs + 32 + 16) + { + static const char *const neon_pseudo_names[] = { + "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", + "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", + }; + + return neon_pseudo_names[i - num_regs - 32]; + } + if (i >= ARRAY_SIZE (arm_register_names)) /* These registers are only supported on targets which supply an XML description. */ @@ -3278,6 +3460,140 @@ arm_write_pc (struct regcache *regcache, CORE_ADDR pc) } } +/* Read the contents of a NEON quad register, by reading from two + double registers. This is used to implement the quad pseudo + registers, and for argument passing in case the quad registers are + missing; vectors are passed in quad registers when using the VFP + ABI, even if a NEON unit is not present. REGNUM is the index of + the quad register, in [0, 15]. */ + +static void +arm_neon_quad_read (struct gdbarch *gdbarch, struct regcache *regcache, + int regnum, gdb_byte *buf) +{ + char name_buf[4]; + gdb_byte reg_buf[8]; + int offset, double_regnum; + + sprintf (name_buf, "d%d", regnum << 1); + double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf, + strlen (name_buf)); + + /* d0 is always the least significant half of q0. */ + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + offset = 8; + else + offset = 0; + + regcache_raw_read (regcache, double_regnum, reg_buf); + memcpy (buf + offset, reg_buf, 8); + + offset = 8 - offset; + regcache_raw_read (regcache, double_regnum + 1, reg_buf); + memcpy (buf + offset, reg_buf, 8); +} + +static void +arm_pseudo_read (struct gdbarch *gdbarch, struct regcache *regcache, + int regnum, gdb_byte *buf) +{ + const int num_regs = gdbarch_num_regs (gdbarch); + char name_buf[4]; + gdb_byte reg_buf[8]; + int offset, double_regnum; + + gdb_assert (regnum >= num_regs); + regnum -= num_regs; + + if (gdbarch_tdep (gdbarch)->have_neon_pseudos && regnum >= 32 && regnum < 48) + /* Quad-precision register. */ + arm_neon_quad_read (gdbarch, regcache, regnum - 32, buf); + else + { + /* Single-precision register. */ + gdb_assert (regnum < 32); + + /* s0 is always the least significant half of d0. */ + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + offset = (regnum & 1) ? 0 : 4; + else + offset = (regnum & 1) ? 4 : 0; + + sprintf (name_buf, "d%d", regnum >> 1); + double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf, + strlen (name_buf)); + + regcache_raw_read (regcache, double_regnum, reg_buf); + memcpy (buf, reg_buf + offset, 4); + } +} + +/* Store the contents of BUF to a NEON quad register, by writing to + two double registers. This is used to implement the quad pseudo + registers, and for argument passing in case the quad registers are + missing; vectors are passed in quad registers when using the VFP + ABI, even if a NEON unit is not present. REGNUM is the index + of the quad register, in [0, 15]. */ + +static void +arm_neon_quad_write (struct gdbarch *gdbarch, struct regcache *regcache, + int regnum, const gdb_byte *buf) +{ + char name_buf[4]; + gdb_byte reg_buf[8]; + int offset, double_regnum; + + sprintf (name_buf, "d%d", regnum << 1); + double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf, + strlen (name_buf)); + + /* d0 is always the least significant half of q0. */ + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + offset = 8; + else + offset = 0; + + regcache_raw_write (regcache, double_regnum, buf + offset); + offset = 8 - offset; + regcache_raw_write (regcache, double_regnum + 1, buf + offset); +} + +static void +arm_pseudo_write (struct gdbarch *gdbarch, struct regcache *regcache, + int regnum, const gdb_byte *buf) +{ + const int num_regs = gdbarch_num_regs (gdbarch); + char name_buf[4]; + gdb_byte reg_buf[8]; + int offset, double_regnum; + + gdb_assert (regnum >= num_regs); + regnum -= num_regs; + + if (gdbarch_tdep (gdbarch)->have_neon_pseudos && regnum >= 32 && regnum < 48) + /* Quad-precision register. */ + arm_neon_quad_write (gdbarch, regcache, regnum - 32, buf); + else + { + /* Single-precision register. */ + gdb_assert (regnum < 32); + + /* s0 is always the least significant half of d0. */ + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + offset = (regnum & 1) ? 0 : 4; + else + offset = (regnum & 1) ? 4 : 0; + + sprintf (name_buf, "d%d", regnum >> 1); + double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf, + strlen (name_buf)); + + regcache_raw_read (regcache, double_regnum, reg_buf); + memcpy (reg_buf + offset, buf, 4); + regcache_raw_write (regcache, double_regnum, reg_buf); + } +} + static struct value * value_of_arm_user_reg (struct frame_info *frame, const void *baton) { @@ -3322,6 +3638,8 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) enum arm_float_model fp_model = arm_fp_model; struct tdesc_arch_data *tdesc_data = NULL; int i; + int have_vfp_registers = 0, have_vfp_pseudos = 0, have_neon_pseudos = 0; + int have_neon = 0; int have_fpa_registers = 1; /* Check any target description for validity. */ @@ -3334,7 +3652,7 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) static const char *const arm_pc_names[] = { "r15", "pc", NULL }; const struct tdesc_feature *feature; - int i, valid_p; + int valid_p; feature = tdesc_find_feature (info.target_desc, "org.gnu.gdb.arm.core"); @@ -3416,6 +3734,67 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return NULL; } } + + /* If we have a VFP unit, check whether the single precision registers + are present. If not, then we will synthesize them as pseudo + registers. */ + feature = tdesc_find_feature (info.target_desc, + "org.gnu.gdb.arm.vfp"); + if (feature != NULL) + { + static const char *const vfp_double_names[] = { + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", + "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", + "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", + "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", + }; + + /* Require the double precision registers. There must be either + 16 or 32. */ + valid_p = 1; + for (i = 0; i < 32; i++) + { + valid_p &= tdesc_numbered_register (feature, tdesc_data, + ARM_D0_REGNUM + i, + vfp_double_names[i]); + if (!valid_p) + break; + } + + if (!valid_p && i != 16) + { + tdesc_data_cleanup (tdesc_data); + return NULL; + } + + if (tdesc_unnumbered_register (feature, "s0") == 0) + have_vfp_pseudos = 1; + + have_vfp_registers = 1; + + /* If we have VFP, also check for NEON. The architecture allows + NEON without VFP (integer vector operations only), but GDB + does not support that. */ + feature = tdesc_find_feature (info.target_desc, + "org.gnu.gdb.arm.neon"); + if (feature != NULL) + { + /* NEON requires 32 double-precision registers. */ + if (i != 32) + { + tdesc_data_cleanup (tdesc_data); + return NULL; + } + + /* If there are quad registers defined by the stub, use + their type; otherwise (normally) provide them with + the default type. */ + if (tdesc_unnumbered_register (feature, "q0") == 0) + have_neon_pseudos = 1; + + have_neon = 1; + } + } } /* If we have an object to base this architecture on, try to determine @@ -3559,6 +3938,11 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) && fp_model != gdbarch_tdep (best_arch->gdbarch)->fp_model) continue; + /* There are various other properties in tdep that we do not + need to check here: those derived from a target description, + since gdbarches with a different target description are + automatically disqualified. */ + /* Found a match. */ break; } @@ -3578,6 +3962,10 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) tdep->arm_abi = arm_abi; tdep->fp_model = fp_model; tdep->have_fpa_registers = have_fpa_registers; + tdep->have_vfp_registers = have_vfp_registers; + tdep->have_vfp_pseudos = have_vfp_pseudos; + tdep->have_neon_pseudos = have_neon_pseudos; + tdep->have_neon = have_neon; /* Breakpoints. */ switch (info.byte_order_for_code) @@ -3717,8 +4105,30 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_long_double_format (gdbarch, floatformats_ieee_double); } + if (have_vfp_pseudos) + { + /* NOTE: These are the only pseudo registers used by + the ARM target at the moment. If more are added, a + little more care in numbering will be needed. */ + + int num_pseudos = 32; + if (have_neon_pseudos) + num_pseudos += 16; + set_gdbarch_num_pseudo_regs (gdbarch, num_pseudos); + set_gdbarch_pseudo_register_read (gdbarch, arm_pseudo_read); + set_gdbarch_pseudo_register_write (gdbarch, arm_pseudo_write); + } + if (tdesc_data) - tdesc_use_registers (gdbarch, info.target_desc, tdesc_data); + { + set_tdesc_pseudo_register_name (gdbarch, arm_register_name); + + tdesc_use_registers (gdbarch, info.target_desc, tdesc_data); + + /* Override tdesc_register_type to adjust the types of VFP + registers for NEON. */ + set_gdbarch_register_type (gdbarch, arm_register_type); + } /* Add standard register aliases. We add aliases even for those nanes which are used by the current architecture - it's simpler, |