* [RFA] Resurrect v850
@ 2005-05-13 12:28 Corinna Vinschen
2005-05-15 18:20 ` Daniel Jacobowitz
0 siblings, 1 reply; 12+ messages in thread
From: Corinna Vinschen @ 2005-05-13 12:28 UTC (permalink / raw)
To: gdb-patches
Hi,
the below patch resurrects v850. No deprecated mechanisms are used.
As promised, this target now uses trad_frames ;-)
Ok to apply?
Thanks,
Corinna
ChangeLog:
* MAINTAINERS: Undelete v850.
* Makefile.in: Re-add v850-tdep.o dependencies.
* configure.tgt: Re-add v850.
* v850-tdep.c: Reorder code slightly. Add register number enumerator.
(struct v850_frame_cache): New structure.
(v850_processor_type_table): Replace non-existant bfd_mach_v850ea
with bfd_mach_v850e1.
(v850_register_name): New function.
(struct prologue_info): Remove definition.
(v850_register_type): New function.
(v850_type_is_scalar): New function.
(v850_use_struct_convention): Align to gcc behaviour.
(handle_prepare): Change third parameter to struct v850_frame_cache *.
(handle_pushm): Ditto.
(v850_analyze_prologue): Replace v850_scan_prologue. Change to support
new frame code.
(v850_init_extra_frame_info): Remove.
(v850_frame_chain): Remove.
(v850_find_callers_reg): Remove.
(v850_skip_prologue): Make static.
(v850_pop_frame): Remove.
(v850_frame_align): New function.
(v850_push_dummy_call): Replace v850_push_arguments.
(v850_push_return_address): Remove.
(v850_extract_return_value): New function.
(v850_store_return_value): New function.
(v850_return_value): New function.
(v850_frame_saved_pc): Remove.
(v850_breakpoint_from_pc): New function.
(v850_alloc_frame_cache): New function.
(v850_fix_call_dummy): Remove.
(v850_frame_cache): New function.
(v850_target_architecture_hook): Remove.
(v850_frame_prev_register): New function.
(v850_frame_this_id): New function.
(v850_frame_unwind): New structure.
(v850_frame_sniffer): New function.
(v850_unwind_sp): New function.
(v850_unwind_pc): New function.
(v850_unwind_dummy_id): New function.
(v850_frame_base_address): New function.
(v850_frame_base): New structure.
(v850_gdbarch_init): New function.
* config/v850/v850.mt: Undelete. Drop TM_FILE.
Index: MAINTAINERS
===================================================================
RCS file: /cvs/src/src/gdb/MAINTAINERS,v
retrieving revision 1.315
diff -u -p -r1.315 MAINTAINERS
--- MAINTAINERS 2 May 2005 15:23:37 -0000 1.315
+++ MAINTAINERS 13 May 2005 11:35:08 -0000
@@ -116,7 +116,7 @@ the native maintainer when resolving ABI
sparc --target=sparc-elf broken
(bfd/elfxx-sparc:512 bfd_elf64_swap_reloca_out undef)
- v850 Deleted
+ v850 --target=v850-elf ,-Werror
vax --target=vax-netbsd broken
(opcodes/vax-dis.c:224 longjmp clober)
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.730
diff -u -p -r1.730 Makefile.in
--- Makefile.in 12 May 2005 21:36:08 -0000 1.730
+++ Makefile.in 13 May 2005 11:35:09 -0000
@@ -1448,6 +1448,7 @@ ALLDEPFILES = \
sparc64nbsd-nat.c sparc64nbsd-tdep.c sparc64obsd-tdep.c \
sparcnbsd-nat.c sparcnbsd-tdep.c sparcobsd-tdep.c \
symm-tdep.c symm-nat.c \
+ v850-tdep.c \
vax-nat.c vax-tdep.c vaxbsd-nat.c vaxnbsd-tdep.c \
win32-nat.c \
xcoffread.c xcoffsolib.c \
@@ -2697,6 +2698,10 @@ utils.o: utils.c $(defs_h) $(gdb_assert_
$(gdb_curses_h) $(readline_h)
uw-thread.o: uw-thread.c $(defs_h) $(gdbthread_h) $(target_h) $(inferior_h) \
$(regcache_h) $(gregset_h)
+v850-tdep.o: v850-tdep.c $(defs_h) $(frame_h) $(frame_base_h) $(trad_frame_h) \
+ $(frame_unwind_h) $(dwarf2_frame_h) $(gdbtypes_h) $(inferior_h) \
+ $(gdb_string_h) $(gdb_assert_h) $(gdbcore_h) $(arch_utils_h) \
+ $(regcache_h) $(dis_asm_h) $(osabi_h)
valarith.o: valarith.c $(defs_h) $(value_h) $(symtab_h) $(gdbtypes_h) \
$(expression_h) $(target_h) $(language_h) $(gdb_string_h) \
$(doublest_h) $(infcall_h)
Index: configure.tgt
===================================================================
RCS file: /cvs/src/src/gdb/configure.tgt,v
retrieving revision 1.163
diff -u -p -r1.163 configure.tgt
--- configure.tgt 26 Apr 2005 21:47:21 -0000 1.163
+++ configure.tgt 13 May 2005 11:35:09 -0000
@@ -193,6 +193,8 @@ sparc64-*-*) gdb_target=sparc64 ;;
xstormy16-*-*) gdb_target=xstormy16 ;;
+v850*-*-elf) gdb_target=v850 ;;
+
vax-*-netbsd* | vax-*-knetbsd*-gnu)
gdb_target=nbsd ;;
vax-*-openbsd*) gdb_target=nbsd ;;
Index: v850-tdep.c
===================================================================
RCS file: v850-tdep.c
diff -N v850-tdep.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ v850-tdep.c 13 May 2005 11:35:09 -0000
@@ -0,0 +1,1137 @@
+/* Target-dependent code for the NEC V850 for GDB, the GNU debugger.
+
+ Copyright 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free
+ Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include "defs.h"
+#include "frame.h"
+#include "frame-base.h"
+#include "trad-frame.h"
+#include "frame-unwind.h"
+#include "dwarf2-frame.h"
+#include "gdbtypes.h"
+#include "inferior.h"
+#include "gdb_string.h"
+#include "gdb_assert.h"
+#include "gdbcore.h"
+#include "arch-utils.h"
+#include "regcache.h"
+#include "dis-asm.h"
+#include "osabi.h"
+
+enum {
+ E_R0_REGNUM,
+ E_R1_REGNUM,
+ E_R2_REGNUM, E_SAVE1_START_REGNUM = E_R2_REGNUM, E_SAVE1_END_REGNUM = E_R2_REGNUM,
+ E_R3_REGNUM, E_SP_REGNUM = E_R3_REGNUM,
+ E_R4_REGNUM,
+ E_R5_REGNUM,
+ E_R6_REGNUM, E_ARG0_REGNUM = E_R6_REGNUM,
+ E_R7_REGNUM,
+ E_R8_REGNUM,
+ E_R9_REGNUM, E_ARGLAST_REGNUM = E_R9_REGNUM,
+ E_R10_REGNUM, E_V0_REGNUM = E_R10_REGNUM,
+ E_R11_REGNUM, E_V1_REGNUM = E_R11_REGNUM,
+ E_R12_REGNUM,
+ E_R13_REGNUM,
+ E_R14_REGNUM,
+ E_R15_REGNUM,
+ E_R16_REGNUM,
+ E_R17_REGNUM,
+ E_R18_REGNUM,
+ E_R19_REGNUM,
+ E_R20_REGNUM, E_SAVE2_START_REGNUM = E_R20_REGNUM,
+ E_R21_REGNUM,
+ E_R22_REGNUM,
+ E_R23_REGNUM,
+ E_R24_REGNUM,
+ E_R25_REGNUM,
+ E_R26_REGNUM,
+ E_R27_REGNUM,
+ E_R28_REGNUM,
+ E_R29_REGNUM, E_SAVE2_END_REGNUM = E_R29_REGNUM, E_FP_RAW_REGNUM = E_R29_REGNUM,
+ E_R30_REGNUM, E_EP_REGNUM = E_R30_REGNUM,
+ E_R31_REGNUM, E_SAVE3_START_REGNUM = E_R31_REGNUM, E_SAVE3_END_REGNUM = E_R31_REGNUM, E_LP_REGNUM = E_R31_REGNUM,
+ E_R32_REGNUM, E_SR0_REGNUM = E_R32_REGNUM,
+ E_R33_REGNUM,
+ E_R34_REGNUM,
+ E_R35_REGNUM,
+ E_R36_REGNUM,
+ E_R37_REGNUM, E_PS_REGNUM = E_R37_REGNUM,
+ E_R38_REGNUM,
+ E_R39_REGNUM,
+ E_R40_REGNUM,
+ E_R41_REGNUM,
+ E_R42_REGNUM,
+ E_R43_REGNUM,
+ E_R44_REGNUM,
+ E_R45_REGNUM,
+ E_R46_REGNUM,
+ E_R47_REGNUM,
+ E_R48_REGNUM,
+ E_R49_REGNUM,
+ E_R50_REGNUM,
+ E_R51_REGNUM,
+ E_R52_REGNUM, E_CTBP_REGNUM = E_R52_REGNUM,
+ E_R53_REGNUM,
+ E_R54_REGNUM,
+ E_R55_REGNUM,
+ E_R56_REGNUM,
+ E_R57_REGNUM,
+ E_R58_REGNUM,
+ E_R59_REGNUM,
+ E_R60_REGNUM,
+ E_R61_REGNUM,
+ E_R62_REGNUM,
+ E_R63_REGNUM,
+ E_R64_REGNUM, E_PC_REGNUM = E_R64_REGNUM,
+ E_R65_REGNUM, E_FP_REGNUM = E_R65_REGNUM,
+ E_NUM_REGS
+};
+
+enum
+{
+ v850_reg_size = 4
+};
+
+/* Size of return datatype which fits into all return registers. */
+enum
+{
+ E_MAX_RETTYPE_SIZE_IN_REGS = 2 * v850_reg_size
+};
+
+struct v850_frame_cache
+{
+ /* Base address. */
+ CORE_ADDR base;
+ LONGEST sp_offset;
+ CORE_ADDR pc;
+
+ /* Flag showing that a frame has been created in the prologue code. */
+ int uses_fp;
+
+ /* Saved registers. */
+ struct trad_frame_saved_reg *saved_regs;
+};
+
+/* Info gleaned from scanning a function's prologue. */
+struct pifsr /* Info about one saved reg */
+{
+ int offset; /* Offset from sp or fp */
+ int cur_frameoffset; /* Current frameoffset */
+ int reg; /* Saved register number */
+};
+
+static char *v850_generic_reg_names[] =
+{ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+ "eipc", "eipsw", "fepc", "fepsw", "ecr", "psw", "sr6", "sr7",
+ "sr8", "sr9", "sr10", "sr11", "sr12", "sr13", "sr14", "sr15",
+ "sr16", "sr17", "sr18", "sr19", "sr20", "sr21", "sr22", "sr23",
+ "sr24", "sr25", "sr26", "sr27", "sr28", "sr29", "sr30", "sr31",
+ "pc", "fp"
+};
+
+static char *v850e_reg_names[] =
+{
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+ "eipc", "eipsw", "fepc", "fepsw", "ecr", "psw", "sr6", "sr7",
+ "sr8", "sr9", "sr10", "sr11", "sr12", "sr13", "sr14", "sr15",
+ "ctpc", "ctpsw", "dbpc", "dbpsw", "ctbp", "sr21", "sr22", "sr23",
+ "sr24", "sr25", "sr26", "sr27", "sr28", "sr29", "sr30", "sr31",
+ "pc", "fp"
+};
+
+char **v850_register_names = v850_generic_reg_names;
+
+struct
+ {
+ char **regnames;
+ int mach;
+ }
+v850_processor_type_table[] =
+{
+ {
+ v850_generic_reg_names, bfd_mach_v850
+ }
+ ,
+ {
+ v850e_reg_names, bfd_mach_v850e
+ }
+ ,
+ {
+ v850e_reg_names, bfd_mach_v850e1
+ }
+ ,
+ {
+ NULL, 0
+ }
+};
+
+/* Function: v850_register_name
+ Returns the name of the v850/v850e register N. */
+
+static const char *
+v850_register_name (int regnum)
+{
+ if (regnum < 0 || regnum >= E_NUM_REGS)
+ internal_error (__FILE__, __LINE__,
+ "v850_register_name: illegal register number %d",
+ regnum);
+ else
+ return v850_register_names[regnum];
+
+}
+
+/* Function: v850_register_type
+ Returns the default type for register N. */
+
+static struct type *
+v850_register_type (struct gdbarch *gdbarch, int regnum)
+{
+ if (regnum < 0 || regnum >= E_NUM_REGS)
+ internal_error (__FILE__, __LINE__,
+ "v850_register_virtual_type: illegal register number %d",
+ regnum);
+ else if (regnum == E_PC_REGNUM)
+ return builtin_type_uint32;
+ else
+ return builtin_type_int32;
+}
+
+static int
+v850_type_is_scalar (struct type *t)
+{
+ return (TYPE_CODE (t) != TYPE_CODE_STRUCT
+ && TYPE_CODE (t) != TYPE_CODE_UNION
+ && TYPE_CODE (t) != TYPE_CODE_ARRAY);
+}
+
+/* Should call_function allocate stack space for a struct return? */
+static int
+v850_use_struct_convention (struct type *type)
+{
+ /* According to ABI:
+ * return TYPE_LENGTH (type) > 8);
+ */
+
+ /* Current implementation in gcc: */
+
+ int i;
+ struct type *fld_type, *tgt_type;
+
+ /* 1. The value is greater than 8 bytes -> returned by copying */
+ if (TYPE_LENGTH (type) > 8)
+ return 1;
+
+ /* 2. The value is a single basic type -> returned in register */
+ if (v850_type_is_scalar (type))
+ return 0;
+
+ /* The value is a structure or union with a single element
+ * and that element is either a single basic type or an array of
+ * a single basic type whoes size is greater than or equal to 4
+ * -> returned in register */
+ if ((TYPE_CODE (type) == TYPE_CODE_STRUCT
+ || TYPE_CODE (type) == TYPE_CODE_UNION)
+ && TYPE_NFIELDS (type) == 1)
+ {
+ fld_type = TYPE_FIELD_TYPE (type, 0);
+ if (v850_type_is_scalar (fld_type) && TYPE_LENGTH (fld_type) >= 4)
+ return 0;
+
+ if (TYPE_CODE (fld_type) == TYPE_CODE_ARRAY)
+ {
+ tgt_type = TYPE_TARGET_TYPE (fld_type);
+ if (v850_type_is_scalar (tgt_type) && TYPE_LENGTH (tgt_type) >= 4)
+ return 0;
+ }
+ }
+
+ /* The value is a structure whose first element is an integer or
+ * a float, and which contains no arrays of more than two elements
+ * -> returned in register */
+ if (TYPE_CODE (type) == TYPE_CODE_STRUCT
+ && v850_type_is_scalar (TYPE_FIELD_TYPE (type, 0))
+ && TYPE_LENGTH (TYPE_FIELD_TYPE (type, 0)) == 4)
+ {
+ for (i = 1; i < TYPE_NFIELDS (type); ++i)
+ {
+ fld_type = TYPE_FIELD_TYPE (type, 0);
+ if (TYPE_CODE (fld_type) == TYPE_CODE_ARRAY)
+ {
+ tgt_type = TYPE_TARGET_TYPE (fld_type);
+ if (TYPE_LENGTH (fld_type) >= 0 && TYPE_LENGTH (tgt_type) >= 0
+ && TYPE_LENGTH (fld_type) / TYPE_LENGTH (tgt_type) > 2)
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ /* The value is a union which contains at least one field which
+ * would be returned in registers according to these rules
+ * -> returned in register */
+ if (TYPE_CODE (type) == TYPE_CODE_UNION)
+ {
+ for (i = 0; i < TYPE_NFIELDS (type); ++i)
+ {
+ fld_type = TYPE_FIELD_TYPE (type, 0);
+ if (!v850_use_struct_convention (fld_type))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Structure for mapping bits in register lists to register numbers. */
+struct reg_list
+{
+ long mask;
+ int regno;
+};
+
+/* Helper function for v850_scan_prologue to handle prepare instruction. */
+
+static void
+handle_prepare (int insn, int insn2, CORE_ADDR * current_pc_ptr,
+ struct v850_frame_cache *pi, struct pifsr **pifsr_ptr)
+{
+ CORE_ADDR current_pc = *current_pc_ptr;
+ struct pifsr *pifsr = *pifsr_ptr;
+ long next = insn2 & 0xffff;
+ long list12 = ((insn & 1) << 16) + (next & 0xffe0);
+ long offset = (insn & 0x3e) << 1;
+ static struct reg_list reg_table[] =
+ {
+ {0x00800, 20}, /* r20 */
+ {0x00400, 21}, /* r21 */
+ {0x00200, 22}, /* r22 */
+ {0x00100, 23}, /* r23 */
+ {0x08000, 24}, /* r24 */
+ {0x04000, 25}, /* r25 */
+ {0x02000, 26}, /* r26 */
+ {0x01000, 27}, /* r27 */
+ {0x00080, 28}, /* r28 */
+ {0x00040, 29}, /* r29 */
+ {0x10000, 30}, /* ep */
+ {0x00020, 31}, /* lp */
+ {0, 0} /* end of table */
+ };
+ int i;
+
+ if ((next & 0x1f) == 0x0b) /* skip imm16 argument */
+ current_pc += 2;
+ else if ((next & 0x1f) == 0x13) /* skip imm16 argument */
+ current_pc += 2;
+ else if ((next & 0x1f) == 0x1b) /* skip imm32 argument */
+ current_pc += 4;
+
+ /* Calculate the total size of the saved registers, and add it
+ it to the immediate value used to adjust SP. */
+ for (i = 0; reg_table[i].mask != 0; i++)
+ if (list12 & reg_table[i].mask)
+ offset += v850_reg_size;
+ pi->sp_offset -= offset;
+
+ /* Calculate the offsets of the registers relative to the value
+ the SP will have after the registers have been pushed and the
+ imm5 value has been subtracted from it. */
+ if (pifsr)
+ {
+ for (i = 0; reg_table[i].mask != 0; i++)
+ {
+ if (list12 & reg_table[i].mask)
+ {
+ int reg = reg_table[i].regno;
+ offset -= v850_reg_size;
+ pifsr->reg = reg;
+ pifsr->offset = offset;
+ pifsr->cur_frameoffset = pi->sp_offset;
+#ifdef DEBUG
+ printf_filtered ("\tSaved register r%d, offset %d", reg, pifsr->offset);
+#endif
+ pifsr++;
+ }
+ }
+ }
+#ifdef DEBUG
+ printf_filtered ("\tfound ctret after regsave func");
+#endif
+
+ /* Set result parameters. */
+ *current_pc_ptr = current_pc;
+ *pifsr_ptr = pifsr;
+}
+
+
+/* Helper function for v850_scan_prologue to handle pushm/pushl instructions.
+ FIXME: the SR bit of the register list is not supported; must check
+ that the compiler does not ever generate this bit. */
+
+static void
+handle_pushm (int insn, int insn2, struct v850_frame_cache *pi,
+ struct pifsr **pifsr_ptr)
+{
+ struct pifsr *pifsr = *pifsr_ptr;
+ long list12 = ((insn & 0x0f) << 16) + (insn2 & 0xfff0);
+ long offset = 0;
+ static struct reg_list pushml_reg_table[] =
+ {
+ {0x80000, E_PS_REGNUM}, /* PSW */
+ {0x40000, 1}, /* r1 */
+ {0x20000, 2}, /* r2 */
+ {0x10000, 3}, /* r3 */
+ {0x00800, 4}, /* r4 */
+ {0x00400, 5}, /* r5 */
+ {0x00200, 6}, /* r6 */
+ {0x00100, 7}, /* r7 */
+ {0x08000, 8}, /* r8 */
+ {0x04000, 9}, /* r9 */
+ {0x02000, 10}, /* r10 */
+ {0x01000, 11}, /* r11 */
+ {0x00080, 12}, /* r12 */
+ {0x00040, 13}, /* r13 */
+ {0x00020, 14}, /* r14 */
+ {0x00010, 15}, /* r15 */
+ {0, 0} /* end of table */
+ };
+ static struct reg_list pushmh_reg_table[] =
+ {
+ {0x80000, 16}, /* r16 */
+ {0x40000, 17}, /* r17 */
+ {0x20000, 18}, /* r18 */
+ {0x10000, 19}, /* r19 */
+ {0x00800, 20}, /* r20 */
+ {0x00400, 21}, /* r21 */
+ {0x00200, 22}, /* r22 */
+ {0x00100, 23}, /* r23 */
+ {0x08000, 24}, /* r24 */
+ {0x04000, 25}, /* r25 */
+ {0x02000, 26}, /* r26 */
+ {0x01000, 27}, /* r27 */
+ {0x00080, 28}, /* r28 */
+ {0x00040, 29}, /* r29 */
+ {0x00010, 30}, /* r30 */
+ {0x00020, 31}, /* r31 */
+ {0, 0} /* end of table */
+ };
+ struct reg_list *reg_table;
+ int i;
+
+ /* Is this a pushml or a pushmh? */
+ if ((insn2 & 7) == 1)
+ reg_table = pushml_reg_table;
+ else
+ reg_table = pushmh_reg_table;
+
+ /* Calculate the total size of the saved registers, and add it
+ it to the immediate value used to adjust SP. */
+ for (i = 0; reg_table[i].mask != 0; i++)
+ if (list12 & reg_table[i].mask)
+ offset += v850_reg_size;
+ pi->sp_offset -= offset;
+
+ /* Calculate the offsets of the registers relative to the value
+ the SP will have after the registers have been pushed and the
+ imm5 value is subtracted from it. */
+ if (pifsr)
+ {
+ for (i = 0; reg_table[i].mask != 0; i++)
+ {
+ if (list12 & reg_table[i].mask)
+ {
+ int reg = reg_table[i].regno;
+ offset -= v850_reg_size;
+ pifsr->reg = reg;
+ pifsr->offset = offset;
+ pifsr->cur_frameoffset = pi->sp_offset;
+#ifdef DEBUG
+ printf_filtered ("\tSaved register r%d, offset %d", reg, pifsr->offset);
+#endif
+ pifsr++;
+ }
+ }
+ }
+#ifdef DEBUG
+ printf_filtered ("\tfound ctret after regsave func");
+#endif
+
+ /* Set result parameters. */
+ *pifsr_ptr = pifsr;
+}
+
+/* Function: scan_prologue
+ Scan the prologue of the function that contains PC, and record what
+ we find in PI. Returns the pc after the prologue. Note that the
+ addresses saved in frame->saved_regs are just frame relative (negative
+ offsets from the frame pointer). This is because we don't know the
+ actual value of the frame pointer yet. In some circumstances, the
+ frame pointer can't be determined till after we have scanned the
+ prologue. */
+
+static CORE_ADDR
+v850_analyze_prologue (CORE_ADDR func_addr, CORE_ADDR pc,
+ struct v850_frame_cache *pi)
+{
+ CORE_ADDR prologue_end, current_pc;
+ struct pifsr pifsrs[E_NUM_REGS + 1];
+ struct pifsr *pifsr, *pifsr_tmp;
+ int fp_used;
+ int ep_used;
+ int reg;
+ CORE_ADDR save_pc, save_end;
+ int regsave_func_p;
+ int r12_tmp;
+
+ memset (&pifsrs, 0, sizeof pifsrs);
+ pifsr = &pifsrs[0];
+
+ prologue_end = pc;
+
+ /* Now, search the prologue looking for instructions that setup fp, save
+ rp, adjust sp and such. We also record the frame offset of any saved
+ registers. */
+
+ pi->sp_offset = 0;
+ pi->uses_fp = 0;
+ ep_used = 0;
+ regsave_func_p = 0;
+ save_pc = 0;
+ save_end = 0;
+ r12_tmp = 0;
+
+#ifdef DEBUG
+ printf_filtered ("Current_pc = 0x%.8lx, prologue_end = 0x%.8lx\n",
+ (long) func_addr, (long) prologue_end);
+#endif
+
+ for (current_pc = func_addr; current_pc < prologue_end;)
+ {
+ int insn;
+ int insn2 = -1; /* dummy value */
+
+#ifdef DEBUG
+ fprintf_filtered (gdb_stdlog, "0x%.8lx ", (long) current_pc);
+ gdb_print_insn (current_pc, gdb_stdlog);
+#endif
+
+ insn = read_memory_integer (current_pc, 2);
+ current_pc += 2;
+ if ((insn & 0x0780) >= 0x0600) /* Four byte instruction? */
+ {
+ insn2 = read_memory_integer (current_pc, 2);
+ current_pc += 2;
+ }
+
+ if ((insn & 0xffc0) == ((10 << 11) | 0x0780) && !regsave_func_p)
+ { /* jarl <func>,10 */
+ long low_disp = insn2 & ~(long) 1;
+ long disp = (((((insn & 0x3f) << 16) + low_disp)
+ & ~(long) 1) ^ 0x00200000) - 0x00200000;
+
+ save_pc = current_pc;
+ save_end = prologue_end;
+ regsave_func_p = 1;
+ current_pc += disp - 4;
+ prologue_end = (current_pc
+ + (2 * 3) /* moves to/from ep */
+ + 4 /* addi <const>,sp,sp */
+ + 2 /* jmp [r10] */
+ + (2 * 12) /* sst.w to save r2, r20-r29, r31 */
+ + 20); /* slop area */
+
+#ifdef DEBUG
+ printf_filtered ("\tfound jarl <func>,r10, disp = %ld, low_disp = %ld, new pc = 0x%.8lx\n",
+ disp, low_disp, (long) current_pc + 2);
+#endif
+ }
+ else if ((insn & 0xffc0) == 0x0200 && !regsave_func_p)
+ { /* callt <imm6> */
+ long ctbp = read_register (E_CTBP_REGNUM);
+ long adr = ctbp + ((insn & 0x3f) << 1);
+
+ save_pc = current_pc;
+ save_end = prologue_end;
+ regsave_func_p = 1;
+ current_pc = ctbp + (read_memory_unsigned_integer (adr, 2) & 0xffff);
+ prologue_end = (current_pc
+ + (2 * 3) /* prepare list2,imm5,sp/imm */
+ + 4 /* ctret */
+ + 20); /* slop area */
+
+#ifdef DEBUG
+ printf_filtered ("\tfound callt, ctbp = 0x%.8lx, adr = %.8lx, new pc = 0x%.8lx\n",
+ ctbp, adr, (long) current_pc);
+#endif
+ continue;
+ }
+ else if ((insn & 0xffc0) == 0x0780) /* prepare list2,imm5 */
+ {
+ handle_prepare (insn, insn2, ¤t_pc, pi, &pifsr);
+ continue;
+ }
+ else if (insn == 0x07e0 && regsave_func_p && insn2 == 0x0144)
+ { /* ctret after processing register save function */
+ current_pc = save_pc;
+ prologue_end = save_end;
+ regsave_func_p = 0;
+#ifdef DEBUG
+ printf_filtered ("\tfound ctret after regsave func");
+#endif
+ continue;
+ }
+ else if ((insn & 0xfff0) == 0x07e0 && (insn2 & 5) == 1)
+ { /* pushml, pushmh */
+ handle_pushm (insn, insn2, pi, &pifsr);
+ continue;
+ }
+ else if ((insn & 0xffe0) == 0x0060 && regsave_func_p)
+ { /* jmp after processing register save function */
+ current_pc = save_pc;
+ prologue_end = save_end;
+ regsave_func_p = 0;
+#ifdef DEBUG
+ printf_filtered ("\tfound jmp after regsave func");
+#endif
+ continue;
+ }
+ else if ((insn & 0x07c0) == 0x0780 /* jarl or jr */
+ || (insn & 0xffe0) == 0x0060 /* jmp */
+ || (insn & 0x0780) == 0x0580) /* branch */
+ {
+#ifdef DEBUG
+ printf_filtered ("\n");
+#endif
+ break; /* Ran into end of prologue */
+ }
+
+ else if ((insn & 0xffe0) == ((E_SP_REGNUM << 11) | 0x0240)) /* add <imm>,sp */
+ pi->sp_offset += ((insn & 0x1f) ^ 0x10) - 0x10;
+ else if (insn == ((E_SP_REGNUM << 11) | 0x0600 | E_SP_REGNUM)) /* addi <imm>,sp,sp */
+ pi->sp_offset += insn2;
+ else if (insn == ((E_FP_RAW_REGNUM << 11) | 0x0000 | E_SP_REGNUM)) /* mov sp,fp */
+ pi->uses_fp = 1;
+
+ else if (insn == ((E_R12_REGNUM << 11) | 0x0640 | E_R0_REGNUM)) /* movhi hi(const),r0,r12 */
+ r12_tmp = insn2 << 16;
+ else if (insn == ((E_R12_REGNUM << 11) | 0x0620 | E_R12_REGNUM)) /* movea lo(const),r12,r12 */
+ r12_tmp += insn2;
+ else if (insn == ((E_SP_REGNUM << 11) | 0x01c0 | E_R12_REGNUM) && r12_tmp) /* add r12,sp */
+ pi->sp_offset += r12_tmp;
+ else if (insn == ((E_EP_REGNUM << 11) | 0x0000 | E_SP_REGNUM)) /* mov sp,ep */
+ ep_used = 1;
+ else if (insn == ((E_EP_REGNUM << 11) | 0x0000 | E_R1_REGNUM)) /* mov r1,ep */
+ ep_used = 0;
+ else if (((insn & 0x07ff) == (0x0760 | E_SP_REGNUM) /* st.w <reg>,<offset>[sp] */
+ || (pi->uses_fp
+ && (insn & 0x07ff) == (0x0760 | E_FP_RAW_REGNUM))) /* st.w <reg>,<offset>[fp] */
+ && pifsr
+ && (((reg = (insn >> 11) & 0x1f) >= E_SAVE1_START_REGNUM && reg <= E_SAVE1_END_REGNUM)
+ || (reg >= E_SAVE2_START_REGNUM && reg <= E_SAVE2_END_REGNUM)
+ || (reg >= E_SAVE3_START_REGNUM && reg <= E_SAVE3_END_REGNUM)))
+ {
+ pifsr->reg = reg;
+ pifsr->offset = insn2 & ~1;
+ pifsr->cur_frameoffset = pi->sp_offset;
+#ifdef DEBUG
+ printf_filtered ("\tSaved register r%d, offset %d", reg, pifsr->offset);
+#endif
+ pifsr++;
+ }
+
+ else if (ep_used /* sst.w <reg>,<offset>[ep] */
+ && ((insn & 0x0781) == 0x0501)
+ && pifsr
+ && (((reg = (insn >> 11) & 0x1f) >= E_SAVE1_START_REGNUM && reg <= E_SAVE1_END_REGNUM)
+ || (reg >= E_SAVE2_START_REGNUM && reg <= E_SAVE2_END_REGNUM)
+ || (reg >= E_SAVE3_START_REGNUM && reg <= E_SAVE3_END_REGNUM)))
+ {
+ pifsr->reg = reg;
+ pifsr->offset = (insn & 0x007e) << 1;
+ pifsr->cur_frameoffset = pi->sp_offset;
+#ifdef DEBUG
+ printf_filtered ("\tSaved register r%d, offset %d", reg, pifsr->offset);
+#endif
+ pifsr++;
+ }
+
+#ifdef DEBUG
+ printf_filtered ("\n");
+#endif
+ }
+
+ /* Fix up any offsets to the final offset. If a frame pointer was created, use it
+ instead of the stack pointer. */
+ for (pifsr_tmp = pifsrs; pifsr_tmp != pifsr; pifsr_tmp++)
+ {
+ pifsr_tmp->offset -= pi->sp_offset - pifsr_tmp->cur_frameoffset;
+ pi->saved_regs[pifsr_tmp->reg].addr = pifsr_tmp->offset;// + get_frame_base (fi);
+
+#ifdef DEBUG
+ printf_filtered ("Saved register r%d, offset = %d\n",
+ pifsr_tmp->reg, pifsr_tmp->offset);
+#endif
+ }
+
+#ifdef DEBUG
+ printf_filtered ("uses_fp = r%d, sp_offset = %d\n", pi->uses_fp, pi->sp_offset);
+#endif
+
+ return current_pc;
+}
+
+/* Function: skip_prologue
+ Return the address of the first code past the prologue of the function. */
+
+static CORE_ADDR
+v850_skip_prologue (CORE_ADDR pc)
+{
+ CORE_ADDR func_addr, func_end;
+
+ /* See what the symbol table says */
+
+ if (find_pc_partial_function (pc, NULL, &func_addr, &func_end))
+ {
+ struct symtab_and_line sal;
+
+ sal = find_pc_line (func_addr, 0);
+
+ if (sal.line != 0 && sal.end < func_end)
+ return sal.end;
+
+ /* Either there's no line info, or the line after the prologue is after
+ the end of the function. In this case, there probably isn't a
+ prologue. */
+ return pc;
+ }
+
+/* We can't find the start of this function, so there's nothing we can do. */
+ return pc;
+}
+
+static CORE_ADDR
+v850_frame_align (struct gdbarch *ignore, CORE_ADDR sp)
+{
+ return sp & ~3;
+}
+
+/* Function: push_dummy_call
+ Setup arguments and RP for a call to the target. First four args
+ go in R6->R9, subsequent args go into sp + 16 -> sp + ... Structs
+ are passed by reference. 64 bit quantities (doubles and long
+ longs) may be split between the regs and the stack. When calling a
+ function that returns a struct, a pointer to the struct is passed
+ in as a secret first argument (always in R6).
+
+ Stack space for the args has NOT been allocated: that job is up to us.
+ */
+
+static CORE_ADDR
+v850_push_dummy_call (struct gdbarch *gdbarch,
+ struct value *function,
+ struct regcache *regcache,
+ CORE_ADDR bp_addr,
+ int nargs,
+ struct value **args,
+ CORE_ADDR sp,
+ int struct_return,
+ CORE_ADDR struct_addr)
+{
+ int argreg;
+ int argnum;
+ int len = 0;
+ int stack_offset;
+
+ /* First, just for safety, make sure stack is aligned */
+ sp = v850_frame_align (gdbarch, sp);
+
+ /* The offset onto the stack at which we will start copying parameters
+ (after the registers are used up) begins at 16 rather than at zero.
+ I don't really know why, that's just the way it seems to work. */
+ stack_offset = 16;
+
+ /* Now make space on the stack for the args. */
+ for (argnum = 0; argnum < nargs; argnum++)
+ len += ((TYPE_LENGTH (value_type (args[argnum])) + 3) & ~3);
+ sp -= len + stack_offset; /* possibly over-allocating, but it works... */
+ /* (you might think we could allocate 16 bytes */
+ /* less, but the ABI seems to use it all! ) */
+
+ argreg = E_ARG0_REGNUM;
+ /* the struct_return pointer occupies the first parameter-passing reg */
+ if (struct_return)
+ regcache_cooked_write_unsigned (regcache, argreg++, struct_addr);
+
+ /* Now load as many as possible of the first arguments into
+ registers, and push the rest onto the stack. There are 16 bytes
+ in four registers available. Loop thru args from first to last. */
+ for (argnum = 0; argnum < nargs; argnum++)
+ {
+ int len;
+ char *val;
+ char valbuf[v850_reg_size];
+
+ if (!v850_type_is_scalar (value_type (*args))
+ && TYPE_LENGTH (value_type (*args)) > E_MAX_RETTYPE_SIZE_IN_REGS)
+ {
+ store_unsigned_integer (valbuf, 4, VALUE_ADDRESS (*args));
+ len = 4;
+ val = valbuf;
+ }
+ else
+ {
+ len = TYPE_LENGTH (value_type (*args));
+ val = (char *) value_contents (*args);
+ }
+
+ while (len > 0)
+ if (argreg <= E_ARGLAST_REGNUM)
+ {
+ CORE_ADDR regval;
+
+ regval = extract_unsigned_integer (val, v850_reg_size);
+ regcache_cooked_write_unsigned (regcache, argreg, regval);
+
+ len -= v850_reg_size;
+ val += v850_reg_size;
+ argreg++;
+ }
+ else
+ {
+ write_memory (sp + stack_offset, val, 4);
+
+ len -= 4;
+ val += 4;
+ stack_offset += 4;
+ }
+ args++;
+ }
+ /* Store return address. */
+ regcache_cooked_write_unsigned (regcache, E_LP_REGNUM, bp_addr);
+
+ /* Update stack pointer. */
+ regcache_cooked_write_unsigned (regcache, E_SP_REGNUM, sp);
+
+ return sp;
+}
+
+static void
+v850_extract_return_value (struct type *type, struct regcache *regcache,
+ void *valbuf)
+{
+ int len = TYPE_LENGTH (type);
+
+ if (len <= v850_reg_size)
+ {
+ ULONGEST val;
+
+ regcache_cooked_read_unsigned (regcache, E_V0_REGNUM, &val);
+ store_unsigned_integer (valbuf, len, val);
+ }
+ else if (len <= 2 * v850_reg_size)
+ {
+ int i, regnum = E_V0_REGNUM;
+ char buf[v850_reg_size];
+ for (i = 0; len > 0; i += 4, len -= 4)
+ {
+ regcache_raw_read (regcache, regnum++, buf);
+ memcpy ((char *) valbuf + i, buf, len > 4 ? 4 : len);
+ }
+ }
+}
+
+static void
+v850_store_return_value (struct type *type, struct regcache *regcache,
+ const char *valbuf)
+{
+ int len = TYPE_LENGTH (type);
+
+ if (len <= v850_reg_size)
+ regcache_cooked_write_unsigned (regcache, E_V0_REGNUM,
+ extract_unsigned_integer (valbuf, len));
+ else if (len <= 2 * v850_reg_size)
+ {
+ int i, regnum = E_V0_REGNUM;
+ for (i = 0; i < len; i += 4)
+ regcache_raw_write (regcache, regnum++, (char *) valbuf + i);
+ }
+}
+
+static enum return_value_convention
+v850_return_value (struct gdbarch *gdbarch, struct type *type,
+ struct regcache *regcache,
+ void *readbuf, const void *writebuf)
+{
+ if (v850_use_struct_convention (type))
+ return RETURN_VALUE_STRUCT_CONVENTION;
+ if (writebuf)
+ v850_store_return_value (type, regcache, writebuf);
+ else if (readbuf)
+ v850_extract_return_value (type, regcache, readbuf);
+ return RETURN_VALUE_REGISTER_CONVENTION;
+}
+
+const static unsigned char *
+v850_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
+{
+ static unsigned char breakpoint[] = { 0x85, 0x05 };
+ *lenptr = sizeof (breakpoint);
+ return breakpoint;
+}
+
+static struct v850_frame_cache *
+v850_alloc_frame_cache (struct frame_info *next_frame)
+{
+ struct v850_frame_cache *cache;
+ int i;
+
+ cache = FRAME_OBSTACK_ZALLOC (struct v850_frame_cache);
+ cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
+
+ /* Base address. */
+ cache->base = 0;
+ cache->sp_offset = 0;
+ cache->pc = 0;
+
+ /* Frameless until proven otherwise. */
+ cache->uses_fp = 0;
+
+ return cache;
+}
+
+static struct v850_frame_cache *
+v850_frame_cache (struct frame_info *next_frame, void **this_cache)
+{
+ struct v850_frame_cache *cache;
+ CORE_ADDR current_pc;
+ int i;
+
+ if (*this_cache)
+ return *this_cache;
+
+ cache = v850_alloc_frame_cache (next_frame);
+ *this_cache = cache;
+
+ /* In principle, for normal frames, fp holds the frame pointer,
+ which holds the base address for the current stack frame.
+ However, for functions that don't need it, the frame pointer is
+ optional. For these "frameless" functions the frame pointer is
+ actually the frame pointer of the calling frame. */
+ cache->base = frame_unwind_register_unsigned (next_frame, E_FP_RAW_REGNUM);
+ if (cache->base == 0)
+ return cache;
+
+ cache->pc = frame_func_unwind (next_frame);
+ current_pc = frame_pc_unwind (next_frame);
+ if (cache->pc != 0)
+ v850_analyze_prologue (cache->pc, current_pc, cache);
+
+ if (!cache->uses_fp)
+ {
+ /* We didn't find a valid frame, which means that CACHE->base
+ currently holds the frame pointer for our calling frame. If
+ we're at the start of a function, or somewhere half-way its
+ prologue, the function's frame probably hasn't been fully
+ setup yet. Try to reconstruct the base address for the stack
+ frame by looking at the stack pointer. For truly "frameless"
+ functions this might work too. */
+ cache->base = frame_unwind_register_unsigned (next_frame, E_SP_REGNUM);
+ }
+
+ /* Now that we have the base address for the stack frame we can
+ calculate the value of sp in the calling frame. */
+ trad_frame_set_value (cache->saved_regs, E_SP_REGNUM,
+ cache->base - cache->sp_offset);
+
+ /* Adjust all the saved registers such that they contain addresses
+ instead of offsets. */
+ for (i = 0; i < E_NUM_REGS; i++)
+ if (trad_frame_addr_p (cache->saved_regs, i))
+ cache->saved_regs[i].addr += cache->base;
+
+ return cache;
+}
+
+
+static void
+v850_frame_prev_register (struct frame_info *next_frame, void **this_cache,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, void *valuep)
+{
+ struct v850_frame_cache *cache = v850_frame_cache (next_frame, this_cache);
+
+ gdb_assert (regnum >= 0);
+
+ /* The PC of the previous frame is stored in the PR register of
+ the current frame. Frob regnum so that we pull the value from
+ the correct place. */
+ if (regnum == E_PC_REGNUM)
+ regnum = E_LP_REGNUM;
+
+ trad_frame_get_prev_register (next_frame, cache->saved_regs, regnum,
+ optimizedp, lvalp, addrp, realnump, valuep);
+}
+
+static void
+v850_frame_this_id (struct frame_info *next_frame, void **this_cache,
+ struct frame_id *this_id)
+{
+ struct v850_frame_cache *cache = v850_frame_cache (next_frame, this_cache);
+
+ /* This marks the outermost frame. */
+ if (cache->base == 0)
+ return;
+
+ *this_id = frame_id_build (cache->saved_regs[E_SP_REGNUM].addr, cache->pc);
+}
+
+static const struct frame_unwind v850_frame_unwind = {
+ NORMAL_FRAME,
+ v850_frame_this_id,
+ v850_frame_prev_register
+};
+
+static const struct frame_unwind *
+v850_frame_sniffer (struct frame_info *next_frame)
+{
+ return &v850_frame_unwind;
+}
+
+static CORE_ADDR
+v850_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ return frame_unwind_register_unsigned (next_frame, SP_REGNUM);
+}
+
+static CORE_ADDR
+v850_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ return frame_unwind_register_unsigned (next_frame, PC_REGNUM);
+}
+
+static struct frame_id
+v850_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ return frame_id_build (v850_unwind_sp (gdbarch, next_frame),
+ frame_pc_unwind (next_frame));
+}
+
+static CORE_ADDR
+v850_frame_base_address (struct frame_info *next_frame, void **this_cache)
+{
+ struct v850_frame_cache *cache = v850_frame_cache (next_frame, this_cache);
+
+ return cache->base;
+}
+
+static const struct frame_base v850_frame_base = {
+ &v850_frame_unwind,
+ v850_frame_base_address,
+ v850_frame_base_address,
+ v850_frame_base_address
+};
+
+static struct gdbarch *
+v850_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+ struct gdbarch *gdbarch;
+ int i;
+
+ /* find a candidate among the list of pre-declared architectures. */
+ arches = gdbarch_list_lookup_by_info (arches, &info);
+ if (arches != NULL)
+ return (arches->gdbarch);
+
+ /* Change the register names based on the current machine type. */
+ if (info.bfd_arch_info->arch != bfd_arch_v850)
+ return 0;
+
+ gdbarch = gdbarch_alloc (&info, 0);
+
+ for (i = 0; v850_processor_type_table[i].regnames != NULL; i++)
+ {
+ if (v850_processor_type_table[i].mach == info.bfd_arch_info->mach)
+ {
+ v850_register_names = v850_processor_type_table[i].regnames;
+ break;
+ }
+ }
+
+ set_gdbarch_num_regs (gdbarch, E_NUM_REGS);
+ set_gdbarch_num_pseudo_regs (gdbarch, 0);
+ set_gdbarch_sp_regnum (gdbarch, E_SP_REGNUM);
+ set_gdbarch_pc_regnum (gdbarch, E_PC_REGNUM);
+ set_gdbarch_fp0_regnum (gdbarch, -1);
+
+ set_gdbarch_register_name (gdbarch, v850_register_name);
+ set_gdbarch_register_type (gdbarch, v850_register_type);
+
+ set_gdbarch_char_signed (gdbarch, 0);
+ set_gdbarch_short_bit (gdbarch, 2 * TARGET_CHAR_BIT);
+ set_gdbarch_int_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_long_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_long_long_bit (gdbarch, 8 * TARGET_CHAR_BIT);
+
+ set_gdbarch_float_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_double_bit (gdbarch, 8 * TARGET_CHAR_BIT);
+ set_gdbarch_long_double_bit (gdbarch, 8 * TARGET_CHAR_BIT);
+
+ set_gdbarch_ptr_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_addr_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+
+ set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+ set_gdbarch_breakpoint_from_pc (gdbarch, v850_breakpoint_from_pc);
+
+ set_gdbarch_return_value (gdbarch, v850_return_value);
+ set_gdbarch_push_dummy_call (gdbarch, v850_push_dummy_call);
+ set_gdbarch_skip_prologue (gdbarch, v850_skip_prologue);
+
+ set_gdbarch_print_insn (gdbarch, print_insn_v850);
+
+ set_gdbarch_frame_align (gdbarch, v850_frame_align);
+ set_gdbarch_unwind_sp (gdbarch, v850_unwind_sp);
+ set_gdbarch_unwind_pc (gdbarch, v850_unwind_pc);
+ set_gdbarch_unwind_dummy_id (gdbarch, v850_unwind_dummy_id);
+ frame_base_set_default (gdbarch, &v850_frame_base);
+
+ /* Hook in ABI-specific overrides, if they have been registered. */
+ gdbarch_init_osabi (info, gdbarch);
+
+ frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
+ frame_unwind_append_sniffer (gdbarch, v850_frame_sniffer);
+
+ return gdbarch;
+}
+
+extern initialize_file_ftype _initialize_v850_tdep; /* -Wmissing-prototypes */
+
+void
+_initialize_v850_tdep (void)
+{
+ register_gdbarch_init (bfd_arch_v850, v850_gdbarch_init);
+}
Index: config/v850/v850.mt
===================================================================
RCS file: config/v850/v850.mt
diff -N config/v850/v850.mt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ config/v850/v850.mt 13 May 2005 11:35:09 -0000
@@ -0,0 +1,4 @@
+# Target: NEC V850 processor
+TDEPFILES= v850-tdep.o
+SIM_OBS = remote-sim.o
+SIM = ../sim/v850/libsim.a
--
Corinna Vinschen
Cygwin Project Co-Leader
Red Hat, Inc.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFA] Resurrect v850
2005-05-13 12:28 [RFA] Resurrect v850 Corinna Vinschen
@ 2005-05-15 18:20 ` Daniel Jacobowitz
2005-05-17 13:39 ` Corinna Vinschen
0 siblings, 1 reply; 12+ messages in thread
From: Daniel Jacobowitz @ 2005-05-15 18:20 UTC (permalink / raw)
To: gdb-patches
On Fri, May 13, 2005 at 01:40:16PM +0200, Corinna Vinschen wrote:
> Hi,
>
> the below patch resurrects v850. No deprecated mechanisms are used.
> As promised, this target now uses trad_frames ;-)
>
> Ok to apply?
Some comments...
> +enum {
> + E_R0_REGNUM,
> + E_R1_REGNUM,
> + E_R2_REGNUM, E_SAVE1_START_REGNUM = E_R2_REGNUM, E_SAVE1_END_REGNUM = E_R2_REGNUM,
> + E_R3_REGNUM, E_SP_REGNUM = E_R3_REGNUM,
Several of the lines in this list are too long. I do see how you were
trying to organize it, but if you put one to a line the lines with
equality operators will still stand out.
> +static char *v850_generic_reg_names[] =
This and the two below should be const.
> +struct
> + {
> + char **regnames;
> + int mach;
> + }
> +v850_processor_type_table[] =
> +{
> + {
> + v850_generic_reg_names, bfd_mach_v850
> + }
> + ,
Please compress these; all you need is:
{ v850_generic_reg_names, bfd_mach_v850 },
> +/* Function: v850_register_name
> + Returns the name of the v850/v850e register N. */
Two spaces after all the periods in comments (throughout the file).
You don't really need the first line either; it's right above the
function already.
> +static const char *
> +v850_register_name (int regnum)
> +{
> + if (regnum < 0 || regnum >= E_NUM_REGS)
> + internal_error (__FILE__, __LINE__,
> + "v850_register_name: illegal register number %d",
> + regnum);
Please don't add new untranslated messages.
Of course, the i18n changes could have been structured so that the
arguments to these functions were translated by default, without all
the _() markers. Another thing that got lost in the rush to add the
markup.
> + /* According to ABI:
> + * return TYPE_LENGTH (type) > 8);
> + */
Please use full sentences in comments when possible (all through this
file). Also please don't use the leading * on every line; that's
not how GDB does it.
> +#ifdef DEBUG
> + printf_filtered ("\tSaved register r%d, offset %d", reg, pifsr->offset);
> +#endif
Please either delete the #ifdef DEBUG bits, or make them a runtime
option under "set debug", depending on how useful they are.
> +/* Helper function for v850_scan_prologue to handle pushm/pushl instructions.
> + FIXME: the SR bit of the register list is not supported; must check
> + that the compiler does not ever generate this bit. */
Did you check that? :-)
> + else if ((insn & 0xffe0) == ((E_SP_REGNUM << 11) | 0x0240)) /* add <imm>,sp */
Long lines again.
> + /* First, just for safety, make sure stack is aligned */
> + sp = v850_frame_align (gdbarch, sp);
The common code already does this.
> + /* The offset onto the stack at which we will start copying parameters
> + (after the registers are used up) begins at 16 rather than at zero.
> + I don't really know why, that's just the way it seems to work. */
> + stack_offset = 16;
> +
> + /* Now make space on the stack for the args. */
> + for (argnum = 0; argnum < nargs; argnum++)
> + len += ((TYPE_LENGTH (value_type (args[argnum])) + 3) & ~3);
> + sp -= len + stack_offset; /* possibly over-allocating, but it works... */
> + /* (you might think we could allocate 16 bytes */
> + /* less, but the ABI seems to use it all! ) */
The comments in this function are seriously un-encouraging. Is this
excess space necessary or not? If it is, why? At a guess, the extra
space is used by the called function to save the incoming argument
registers if necessary; MIPS o32 does something similar.
> + /* Now load as many as possible of the first arguments into
> + registers, and push the rest onto the stack. There are 16 bytes
> + in four registers available. Loop thru args from first to last. */
> + for (argnum = 0; argnum < nargs; argnum++)
> + {
> + int len;
> + char *val;
> + char valbuf[v850_reg_size];
We just spent weeks discussing the proper type for data buffers, and
char isn't it. Please use gdb_byte.
> +static void
> +v850_frame_prev_register (struct frame_info *next_frame, void **this_cache,
> + int regnum, int *optimizedp,
> + enum lval_type *lvalp, CORE_ADDR *addrp,
> + int *realnump, void *valuep)
> +{
> + struct v850_frame_cache *cache = v850_frame_cache (next_frame, this_cache);
> +
> + gdb_assert (regnum >= 0);
> +
> + /* The PC of the previous frame is stored in the PR register of
> + the current frame. Frob regnum so that we pull the value from
> + the correct place. */
> + if (regnum == E_PC_REGNUM)
> + regnum = E_LP_REGNUM;
> +
> + trad_frame_get_prev_register (next_frame, cache->saved_regs, regnum,
> + optimizedp, lvalp, addrp, realnump, valuep);
> +}
You can do the frobbing when you record the saved registers - at the
end, copy the saved location.
> +static struct gdbarch *
> +v850_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
> +{
> + struct gdbarch *gdbarch;
> + int i;
> +
> + /* find a candidate among the list of pre-declared architectures. */
> + arches = gdbarch_list_lookup_by_info (arches, &info);
> + if (arches != NULL)
> + return (arches->gdbarch);
This isn't right, see below...
> +
> + /* Change the register names based on the current machine type. */
> + if (info.bfd_arch_info->arch != bfd_arch_v850)
> + return 0;
> +
> + gdbarch = gdbarch_alloc (&info, 0);
> +
> + for (i = 0; v850_processor_type_table[i].regnames != NULL; i++)
> + {
> + if (v850_processor_type_table[i].mach == info.bfd_arch_info->mach)
> + {
> + v850_register_names = v850_processor_type_table[i].regnames;
> + break;
> + }
> + }
... here. You're setting a global variable in the process of choosing
a gdbarch. That global variable should be in gdbarch_tdep, along with
the criteria used to set it, and you should refuse to reuse an
architecture from the arches list that doesn't have the right
architecture. There are plenty of examples of this.
--
Daniel Jacobowitz
CodeSourcery, LLC
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFA] Resurrect v850
2005-05-15 18:20 ` Daniel Jacobowitz
@ 2005-05-17 13:39 ` Corinna Vinschen
2005-05-17 13:52 ` Daniel Jacobowitz
2005-05-17 14:01 ` Corinna Vinschen
0 siblings, 2 replies; 12+ messages in thread
From: Corinna Vinschen @ 2005-05-17 13:39 UTC (permalink / raw)
To: gdb-patches
Hi Daniel,
On May 15 13:44, Daniel Jacobowitz wrote:
> On Fri, May 13, 2005 at 01:40:16PM +0200, Corinna Vinschen wrote:
> > Hi,
> >
> > the below patch resurrects v850. No deprecated mechanisms are used.
> > As promised, this target now uses trad_frames ;-)
> >
> > Ok to apply?
>
> Some comments...
While reworking the code according to your comments, I came across three
problems:
> > +enum {
> > + E_R0_REGNUM,
> > + E_R1_REGNUM,
> > + E_R2_REGNUM, E_SAVE1_START_REGNUM = E_R2_REGNUM, E_SAVE1_END_REGNUM = E_R2_REGNUM,
> > + E_R3_REGNUM, E_SP_REGNUM = E_R3_REGNUM,
>
> Several of the lines in this list are too long. I do see how you were
> trying to organize it, but if you put one to a line the lines with
> equality operators will still stand out.
Is a layout like this:
E_R1_REGNUM,
E_R2_REGNUM, E_SAVE1_START_REGNUM = E_R2_REGNUM,
E_SAVE1_END_REGNUM = E_R2_REGNUM,
E_R3_REGNUM, E_SP_REGNUM = E_R3_REGNUM,
[...]
ok?
> The comments in this function are seriously un-encouraging. Is this
> excess space necessary or not? If it is, why?
I don't know. The ABI is defined this way and inspecting a lot of code
I didn't see any reason for this behaviour. Nevertheless, gcc emits
code which allocates these 16 byte for no apparent reason. I removed
useless comments and the remaining comment now read like this:
/* The offset onto the stack at which we will start copying parameters
(after the registers are used up) begins at 16 rather than at zero.
That's how the ABI is defined, though there's no indication that these
16 bytes are used for anything, not even for saving incoming
argument registers. */
Is that ok?
> > +static void
> > +v850_frame_prev_register (struct frame_info *next_frame, void **this_cache,
> > + int regnum, int *optimizedp,
> > + enum lval_type *lvalp, CORE_ADDR *addrp,
> > + int *realnump, void *valuep)
> > +{
> > + struct v850_frame_cache *cache = v850_frame_cache (next_frame, this_cache);
> > +
> > + gdb_assert (regnum >= 0);
> > +
> > + /* The PC of the previous frame is stored in the PR register of
> > + the current frame. Frob regnum so that we pull the value from
> > + the correct place. */
> > + if (regnum == E_PC_REGNUM)
> > + regnum = E_LP_REGNUM;
> > +
> > + trad_frame_get_prev_register (next_frame, cache->saved_regs, regnum,
> > + optimizedp, lvalp, addrp, realnump, valuep);
> > +}
>
> You can do the frobbing when you record the saved registers - at the
> end, copy the saved location.
Sorry, I don't understand this. Can you show me some (pseudo) code what
you mean?
Thanks,
Corinna
--
Corinna Vinschen
Cygwin Project Co-Leader
Red Hat, Inc.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFA] Resurrect v850
2005-05-17 13:39 ` Corinna Vinschen
@ 2005-05-17 13:52 ` Daniel Jacobowitz
2005-05-17 14:39 ` Corinna Vinschen
2005-05-17 14:01 ` Corinna Vinschen
1 sibling, 1 reply; 12+ messages in thread
From: Daniel Jacobowitz @ 2005-05-17 13:52 UTC (permalink / raw)
To: gdb-patches
On Tue, May 17, 2005 at 03:23:48PM +0200, Corinna Vinschen wrote:
> Hi Daniel,
>
> On May 15 13:44, Daniel Jacobowitz wrote:
> > On Fri, May 13, 2005 at 01:40:16PM +0200, Corinna Vinschen wrote:
> > > Hi,
> > >
> > > the below patch resurrects v850. No deprecated mechanisms are used.
> > > As promised, this target now uses trad_frames ;-)
> > >
> > > Ok to apply?
> >
> > Some comments...
>
> While reworking the code according to your comments, I came across three
> problems:
>
> > > +enum {
> > > + E_R0_REGNUM,
> > > + E_R1_REGNUM,
> > > + E_R2_REGNUM, E_SAVE1_START_REGNUM = E_R2_REGNUM, E_SAVE1_END_REGNUM = E_R2_REGNUM,
> > > + E_R3_REGNUM, E_SP_REGNUM = E_R3_REGNUM,
> >
> > Several of the lines in this list are too long. I do see how you were
> > trying to organize it, but if you put one to a line the lines with
> > equality operators will still stand out.
>
> Is a layout like this:
>
> E_R1_REGNUM,
> E_R2_REGNUM, E_SAVE1_START_REGNUM = E_R2_REGNUM,
> E_SAVE1_END_REGNUM = E_R2_REGNUM,
> E_R3_REGNUM, E_SP_REGNUM = E_R3_REGNUM,
> [...]
>
> ok?
I was originally going to suggest that - but no, it isn't.
gdb_indent.sh will promptly undo this the next time someone needs to
correct indentation in the file. That's the problem with
machine-enforced standards...
>
> > The comments in this function are seriously un-encouraging. Is this
> > excess space necessary or not? If it is, why?
>
> I don't know. The ABI is defined this way and inspecting a lot of code
> I didn't see any reason for this behaviour. Nevertheless, gcc emits
> code which allocates these 16 byte for no apparent reason. I removed
> useless comments and the remaining comment now read like this:
>
> /* The offset onto the stack at which we will start copying parameters
> (after the registers are used up) begins at 16 rather than at zero.
> That's how the ABI is defined, though there's no indication that these
> 16 bytes are used for anything, not even for saving incoming
> argument registers. */
>
> Is that ok?
Yes, definitely.
>
> > > +static void
> > > +v850_frame_prev_register (struct frame_info *next_frame, void **this_cache,
> > > + int regnum, int *optimizedp,
> > > + enum lval_type *lvalp, CORE_ADDR *addrp,
> > > + int *realnump, void *valuep)
> > > +{
> > > + struct v850_frame_cache *cache = v850_frame_cache (next_frame, this_cache);
> > > +
> > > + gdb_assert (regnum >= 0);
> > > +
> > > + /* The PC of the previous frame is stored in the PR register of
> > > + the current frame. Frob regnum so that we pull the value from
> > > + the correct place. */
> > > + if (regnum == E_PC_REGNUM)
> > > + regnum = E_LP_REGNUM;
> > > +
> > > + trad_frame_get_prev_register (next_frame, cache->saved_regs, regnum,
> > > + optimizedp, lvalp, addrp, realnump, valuep);
> > > +}
> >
> > You can do the frobbing when you record the saved registers - at the
> > end, copy the saved location.
>
> Sorry, I don't understand this. Can you show me some (pseudo) code what
> you mean?
I believe that you can do "cache->saved_regs[E_PC_REGNUM] =
cache->saved_regs[E_LP_REGNUM]", right by the call to
trad_frame_addr_p. Does that work?
--
Daniel Jacobowitz
CodeSourcery, LLC
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFA] Resurrect v850
2005-05-17 13:39 ` Corinna Vinschen
2005-05-17 13:52 ` Daniel Jacobowitz
@ 2005-05-17 14:01 ` Corinna Vinschen
2005-05-17 14:10 ` Daniel Jacobowitz
1 sibling, 1 reply; 12+ messages in thread
From: Corinna Vinschen @ 2005-05-17 14:01 UTC (permalink / raw)
To: gdb-patches
Hi Daniel,
On May 17 15:23, Corinna Vinschen wrote:
> [...]
> > You can do the frobbing when you record the saved registers - at the
> > end, copy the saved location.
>
> Sorry, I don't understand this. Can you show me some (pseudo) code what
> you mean?
Do you mean like this, as in m32r-tdep.c, function m32r_frame_unwind_cache:
/* The call instruction moves the caller's PC in the callee's LR.
Since this is an unwind, do the reverse. Copy the location of LR
into PC (the address / regnum) so that a request for PC will be
converted into a request for the LR. */
info->saved_regs[M32R_PC_REGNUM] = info->saved_regs[LR_REGNUM];
?
Corinna
--
Corinna Vinschen
Cygwin Project Co-Leader
Red Hat, Inc.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFA] Resurrect v850
2005-05-17 14:01 ` Corinna Vinschen
@ 2005-05-17 14:10 ` Daniel Jacobowitz
0 siblings, 0 replies; 12+ messages in thread
From: Daniel Jacobowitz @ 2005-05-17 14:10 UTC (permalink / raw)
To: gdb-patches
On Tue, May 17, 2005 at 03:38:27PM +0200, Corinna Vinschen wrote:
> Hi Daniel,
>
> On May 17 15:23, Corinna Vinschen wrote:
> > [...]
> > > You can do the frobbing when you record the saved registers - at the
> > > end, copy the saved location.
> >
> > Sorry, I don't understand this. Can you show me some (pseudo) code what
> > you mean?
>
> Do you mean like this, as in m32r-tdep.c, function m32r_frame_unwind_cache:
>
> /* The call instruction moves the caller's PC in the callee's LR.
> Since this is an unwind, do the reverse. Copy the location of LR
> into PC (the address / regnum) so that a request for PC will be
> converted into a request for the LR. */
> info->saved_regs[M32R_PC_REGNUM] = info->saved_regs[LR_REGNUM];
>
> ?
Yes, precisely.
--
Daniel Jacobowitz
CodeSourcery, LLC
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFA] Resurrect v850
2005-05-17 13:52 ` Daniel Jacobowitz
@ 2005-05-17 14:39 ` Corinna Vinschen
2005-05-17 20:40 ` Mark Kettenis
2005-05-18 1:40 ` Daniel Jacobowitz
0 siblings, 2 replies; 12+ messages in thread
From: Corinna Vinschen @ 2005-05-17 14:39 UTC (permalink / raw)
To: gdb-patches
Hi Daniel,
On May 17 09:31, Daniel Jacobowitz wrote:
> On Tue, May 17, 2005 at 03:23:48PM +0200, Corinna Vinschen wrote:
> > Is a layout like this:
> >
> > E_R1_REGNUM,
> > E_R2_REGNUM, E_SAVE1_START_REGNUM = E_R2_REGNUM,
> > E_SAVE1_END_REGNUM = E_R2_REGNUM,
> > E_R3_REGNUM, E_SP_REGNUM = E_R3_REGNUM,
> > [...]
> >
> > ok?
>
> I was originally going to suggest that - but no, it isn't.
> gdb_indent.sh will promptly undo this the next time someone needs to
> correct indentation in the file. That's the problem with
> machine-enforced standards...
Too bad. Flat indenting these special names seems unintuitive.
However, I changed this entirely by moving the decision, whether a
register is a "save" register or not into one single function. This
allows to remove the "E_SAVExxx" defines since the function alone is
intuitive enough, IMHO.
> > Sorry, I don't understand this. Can you show me some (pseudo) code what
> > you mean?
>
> I believe that you can do "cache->saved_regs[E_PC_REGNUM] =
> cache->saved_regs[E_LP_REGNUM]", right by the call to
> trad_frame_addr_p. Does that work?
Yes, that works. I just tried it before I got your reply :-)
Ok, I *think* I have followed all your suggestions and hints throughout
the file. The result is attached below.
Thanks for your review,
Corinna
* MAINTAINERS: Undelete v850.
* Makefile.in: Re-add v850-tdep.o dependencies.
* configure.tgt: Re-add v850.
* v850-tdep.c: Reorder code slightly. Add register number enumerator.
(struct v850_frame_cache): New structure.
(v850_processor_type_table): Remove. Move functionality into
v850_register_name, v850e_register_name and v850_gdbarch_init.
(v850_register_name): New function.
(v850e_register_name): New function.
(struct prologue_info): Remove definition.
(v850_register_type): New function.
(v850_type_is_scalar): New function.
(v850_use_struct_convention): Align to gcc behaviour.
(v850_handle_prepare): Renamed from handle_prepare. Change third
parameter to struct v850_frame_cache *.
(v850_handle_pushm): Renamed from handle_pushm. Change third parameter
to struct v850_frame_cache *.
(v850_is_save_register): New function to evaluate if a register is
caller-save.
(v850_analyze_prologue): Replace v850_scan_prologue. Change to support
new frame code.
(v850_init_extra_frame_info): Remove.
(v850_frame_chain): Remove.
(v850_find_callers_reg): Remove.
(v850_skip_prologue): Make static.
(v850_pop_frame): Remove.
(v850_frame_align): New function.
(v850_push_dummy_call): Replace v850_push_arguments.
(v850_push_return_address): Remove.
(v850_extract_return_value): New function.
(v850_store_return_value): New function.
(v850_return_value): New function.
(v850_frame_saved_pc): Remove.
(v850_breakpoint_from_pc): New function.
(v850_alloc_frame_cache): New function.
(v850_fix_call_dummy): Remove.
(v850_frame_cache): New function.
(v850_target_architecture_hook): Remove.
(v850_frame_prev_register): New function.
(v850_frame_this_id): New function.
(v850_frame_unwind): New structure.
(v850_frame_sniffer): New function.
(v850_unwind_sp): New function.
(v850_unwind_pc): New function.
(v850_unwind_dummy_id): New function.
(v850_frame_base_address): New function.
(v850_frame_base): New structure.
(v850_gdbarch_init): New function.
* config/v850/v850.mt: Undelete. Drop TM_FILE.
Index: MAINTAINERS
===================================================================
RCS file: /cvs/src/src/gdb/MAINTAINERS,v
retrieving revision 1.315
diff -u -p -r1.315 MAINTAINERS
--- MAINTAINERS 2 May 2005 15:23:37 -0000 1.315
+++ MAINTAINERS 17 May 2005 14:07:10 -0000
@@ -116,7 +116,7 @@ the native maintainer when resolving ABI
sparc --target=sparc-elf broken
(bfd/elfxx-sparc:512 bfd_elf64_swap_reloca_out undef)
- v850 Deleted
+ v850 --target=v850-elf ,-Werror
vax --target=vax-netbsd broken
(opcodes/vax-dis.c:224 longjmp clober)
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.731
diff -u -p -r1.731 Makefile.in
--- Makefile.in 15 May 2005 18:19:43 -0000 1.731
+++ Makefile.in 17 May 2005 14:07:11 -0000
@@ -1448,6 +1448,7 @@ ALLDEPFILES = \
sparc64nbsd-nat.c sparc64nbsd-tdep.c sparc64obsd-tdep.c \
sparcnbsd-nat.c sparcnbsd-tdep.c sparcobsd-tdep.c \
symm-tdep.c symm-nat.c \
+ v850-tdep.c \
vax-nat.c vax-tdep.c vaxbsd-nat.c vaxnbsd-tdep.c \
win32-nat.c \
xcoffread.c xcoffsolib.c \
@@ -2697,6 +2698,10 @@ utils.o: utils.c $(defs_h) $(gdb_assert_
$(gdb_curses_h) $(readline_h)
uw-thread.o: uw-thread.c $(defs_h) $(gdbthread_h) $(target_h) $(inferior_h) \
$(regcache_h) $(gregset_h)
+v850-tdep.o: v850-tdep.c $(defs_h) $(frame_h) $(frame_base_h) $(trad_frame_h) \
+ $(frame_unwind_h) $(dwarf2_frame_h) $(gdbtypes_h) $(inferior_h) \
+ $(gdb_string_h) $(gdb_assert_h) $(gdbcore_h) $(arch_utils_h) \
+ $(regcache_h) $(dis_asm_h) $(osabi_h)
valarith.o: valarith.c $(defs_h) $(value_h) $(symtab_h) $(gdbtypes_h) \
$(expression_h) $(target_h) $(language_h) $(gdb_string_h) \
$(doublest_h) $(infcall_h)
Index: configure.tgt
===================================================================
RCS file: /cvs/src/src/gdb/configure.tgt,v
retrieving revision 1.164
diff -u -p -r1.164 configure.tgt
--- configure.tgt 14 May 2005 20:21:03 -0000 1.164
+++ configure.tgt 17 May 2005 14:07:11 -0000
@@ -193,6 +193,8 @@ sparc64-*-*) gdb_target=sparc64 ;;
xstormy16-*-*) gdb_target=xstormy16 ;;
+v850*-*-elf) gdb_target=v850 ;;
+
vax-*-netbsd* | vax-*-knetbsd*-gnu)
gdb_target=nbsd ;;
vax-*-openbsd*) gdb_target=obsd ;;
Index: v850-tdep.c
===================================================================
RCS file: v850-tdep.c
diff -N v850-tdep.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ v850-tdep.c 17 May 2005 14:07:11 -0000
@@ -0,0 +1,1045 @@
+/* Target-dependent code for the NEC V850 for GDB, the GNU debugger.
+
+ Copyright 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free
+ Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include "defs.h"
+#include "frame.h"
+#include "frame-base.h"
+#include "trad-frame.h"
+#include "frame-unwind.h"
+#include "dwarf2-frame.h"
+#include "gdbtypes.h"
+#include "inferior.h"
+#include "gdb_string.h"
+#include "gdb_assert.h"
+#include "gdbcore.h"
+#include "arch-utils.h"
+#include "regcache.h"
+#include "dis-asm.h"
+#include "osabi.h"
+
+enum
+ {
+ E_R0_REGNUM,
+ E_R1_REGNUM,
+ E_R2_REGNUM,
+ E_R3_REGNUM, E_SP_REGNUM = E_R3_REGNUM,
+ E_R4_REGNUM,
+ E_R5_REGNUM,
+ E_R6_REGNUM, E_ARG0_REGNUM = E_R6_REGNUM,
+ E_R7_REGNUM,
+ E_R8_REGNUM,
+ E_R9_REGNUM, E_ARGLAST_REGNUM = E_R9_REGNUM,
+ E_R10_REGNUM, E_V0_REGNUM = E_R10_REGNUM,
+ E_R11_REGNUM, E_V1_REGNUM = E_R11_REGNUM,
+ E_R12_REGNUM,
+ E_R13_REGNUM,
+ E_R14_REGNUM,
+ E_R15_REGNUM,
+ E_R16_REGNUM,
+ E_R17_REGNUM,
+ E_R18_REGNUM,
+ E_R19_REGNUM,
+ E_R20_REGNUM,
+ E_R21_REGNUM,
+ E_R22_REGNUM,
+ E_R23_REGNUM,
+ E_R24_REGNUM,
+ E_R25_REGNUM,
+ E_R26_REGNUM,
+ E_R27_REGNUM,
+ E_R28_REGNUM,
+ E_R29_REGNUM, E_FP_REGNUM = E_R29_REGNUM,
+ E_R30_REGNUM, E_EP_REGNUM = E_R30_REGNUM,
+ E_R31_REGNUM, E_LP_REGNUM = E_R31_REGNUM,
+ E_R32_REGNUM, E_SR0_REGNUM = E_R32_REGNUM,
+ E_R33_REGNUM,
+ E_R34_REGNUM,
+ E_R35_REGNUM,
+ E_R36_REGNUM,
+ E_R37_REGNUM, E_PS_REGNUM = E_R37_REGNUM,
+ E_R38_REGNUM,
+ E_R39_REGNUM,
+ E_R40_REGNUM,
+ E_R41_REGNUM,
+ E_R42_REGNUM,
+ E_R43_REGNUM,
+ E_R44_REGNUM,
+ E_R45_REGNUM,
+ E_R46_REGNUM,
+ E_R47_REGNUM,
+ E_R48_REGNUM,
+ E_R49_REGNUM,
+ E_R50_REGNUM,
+ E_R51_REGNUM,
+ E_R52_REGNUM, E_CTBP_REGNUM = E_R52_REGNUM,
+ E_R53_REGNUM,
+ E_R54_REGNUM,
+ E_R55_REGNUM,
+ E_R56_REGNUM,
+ E_R57_REGNUM,
+ E_R58_REGNUM,
+ E_R59_REGNUM,
+ E_R60_REGNUM,
+ E_R61_REGNUM,
+ E_R62_REGNUM,
+ E_R63_REGNUM,
+ E_R64_REGNUM, E_PC_REGNUM = E_R64_REGNUM,
+ E_R65_REGNUM,
+ E_NUM_REGS
+ };
+
+enum
+{
+ v850_reg_size = 4
+};
+
+/* Size of return datatype which fits into all return registers. */
+enum
+{
+ E_MAX_RETTYPE_SIZE_IN_REGS = 2 * v850_reg_size
+};
+
+struct v850_frame_cache
+{
+ /* Base address. */
+ CORE_ADDR base;
+ LONGEST sp_offset;
+ CORE_ADDR pc;
+
+ /* Flag showing that a frame has been created in the prologue code. */
+ int uses_fp;
+
+ /* Saved registers. */
+ struct trad_frame_saved_reg *saved_regs;
+};
+
+/* Info gleaned from scanning a function's prologue. */
+struct pifsr /* Info about one saved reg */
+{
+ int offset; /* Offset from sp or fp */
+ int cur_frameoffset; /* Current frameoffset */
+ int reg; /* Saved register number */
+};
+
+static const char *
+v850_register_name (int regnum)
+{
+ static const char *v850_reg_names[] =
+ { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+ "eipc", "eipsw", "fepc", "fepsw", "ecr", "psw", "sr6", "sr7",
+ "sr8", "sr9", "sr10", "sr11", "sr12", "sr13", "sr14", "sr15",
+ "sr16", "sr17", "sr18", "sr19", "sr20", "sr21", "sr22", "sr23",
+ "sr24", "sr25", "sr26", "sr27", "sr28", "sr29", "sr30", "sr31",
+ "pc", "fp"
+ };
+ if (regnum < 0 || regnum >= E_NUM_REGS)
+ return NULL;
+ return v850_reg_names[regnum];
+}
+
+static const char *
+v850e_register_name (int regnum)
+{
+ static const char *v850e_reg_names[] =
+ {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+ "eipc", "eipsw", "fepc", "fepsw", "ecr", "psw", "sr6", "sr7",
+ "sr8", "sr9", "sr10", "sr11", "sr12", "sr13", "sr14", "sr15",
+ "ctpc", "ctpsw", "dbpc", "dbpsw", "ctbp", "sr21", "sr22", "sr23",
+ "sr24", "sr25", "sr26", "sr27", "sr28", "sr29", "sr30", "sr31",
+ "pc", "fp"
+ };
+ if (regnum < 0 || regnum >= E_NUM_REGS)
+ return NULL;
+ return v850e_reg_names[regnum];
+}
+
+/* Returns the default type for register N. */
+
+static struct type *
+v850_register_type (struct gdbarch *gdbarch, int regnum)
+{
+ if (regnum == E_PC_REGNUM)
+ return builtin_type_uint32;
+ return builtin_type_int32;
+}
+
+static int
+v850_type_is_scalar (struct type *t)
+{
+ return (TYPE_CODE (t) != TYPE_CODE_STRUCT
+ && TYPE_CODE (t) != TYPE_CODE_UNION
+ && TYPE_CODE (t) != TYPE_CODE_ARRAY);
+}
+
+/* Should call_function allocate stack space for a struct return? */
+static int
+v850_use_struct_convention (struct type *type)
+{
+ int i;
+ struct type *fld_type, *tgt_type;
+
+ /* 1. The value is greater than 8 bytes -> returned by copying. */
+ if (TYPE_LENGTH (type) > 8)
+ return 1;
+
+ /* 2. The value is a single basic type -> returned in register. */
+ if (v850_type_is_scalar (type))
+ return 0;
+
+ /* The value is a structure or union with a single element and that
+ element is either a single basic type or an array of a single basic
+ type whoes size is greater than or equal to 4 -> returned in register. */
+ if ((TYPE_CODE (type) == TYPE_CODE_STRUCT
+ || TYPE_CODE (type) == TYPE_CODE_UNION)
+ && TYPE_NFIELDS (type) == 1)
+ {
+ fld_type = TYPE_FIELD_TYPE (type, 0);
+ if (v850_type_is_scalar (fld_type) && TYPE_LENGTH (fld_type) >= 4)
+ return 0;
+
+ if (TYPE_CODE (fld_type) == TYPE_CODE_ARRAY)
+ {
+ tgt_type = TYPE_TARGET_TYPE (fld_type);
+ if (v850_type_is_scalar (tgt_type) && TYPE_LENGTH (tgt_type) >= 4)
+ return 0;
+ }
+ }
+
+ /* The value is a structure whose first element is an integer or a float,
+ and which contains no arrays of more than two elements -> returned in
+ register. */
+ if (TYPE_CODE (type) == TYPE_CODE_STRUCT
+ && v850_type_is_scalar (TYPE_FIELD_TYPE (type, 0))
+ && TYPE_LENGTH (TYPE_FIELD_TYPE (type, 0)) == 4)
+ {
+ for (i = 1; i < TYPE_NFIELDS (type); ++i)
+ {
+ fld_type = TYPE_FIELD_TYPE (type, 0);
+ if (TYPE_CODE (fld_type) == TYPE_CODE_ARRAY)
+ {
+ tgt_type = TYPE_TARGET_TYPE (fld_type);
+ if (TYPE_LENGTH (fld_type) >= 0 && TYPE_LENGTH (tgt_type) >= 0
+ && TYPE_LENGTH (fld_type) / TYPE_LENGTH (tgt_type) > 2)
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ /* The value is a union which contains at least one field which would be
+ returned in registers according to these rules -> returned in register. */
+ if (TYPE_CODE (type) == TYPE_CODE_UNION)
+ {
+ for (i = 0; i < TYPE_NFIELDS (type); ++i)
+ {
+ fld_type = TYPE_FIELD_TYPE (type, 0);
+ if (!v850_use_struct_convention (fld_type))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Structure for mapping bits in register lists to register numbers. */
+struct reg_list
+{
+ long mask;
+ int regno;
+};
+
+/* Helper function for v850_scan_prologue to handle prepare instruction. */
+
+static void
+v850_handle_prepare (int insn, int insn2, CORE_ADDR * current_pc_ptr,
+ struct v850_frame_cache *pi, struct pifsr **pifsr_ptr)
+{
+ CORE_ADDR current_pc = *current_pc_ptr;
+ struct pifsr *pifsr = *pifsr_ptr;
+ long next = insn2 & 0xffff;
+ long list12 = ((insn & 1) << 16) + (next & 0xffe0);
+ long offset = (insn & 0x3e) << 1;
+ static struct reg_list reg_table[] =
+ {
+ {0x00800, 20}, /* r20 */
+ {0x00400, 21}, /* r21 */
+ {0x00200, 22}, /* r22 */
+ {0x00100, 23}, /* r23 */
+ {0x08000, 24}, /* r24 */
+ {0x04000, 25}, /* r25 */
+ {0x02000, 26}, /* r26 */
+ {0x01000, 27}, /* r27 */
+ {0x00080, 28}, /* r28 */
+ {0x00040, 29}, /* r29 */
+ {0x10000, 30}, /* ep */
+ {0x00020, 31}, /* lp */
+ {0, 0} /* end of table */
+ };
+ int i;
+
+ if ((next & 0x1f) == 0x0b) /* skip imm16 argument */
+ current_pc += 2;
+ else if ((next & 0x1f) == 0x13) /* skip imm16 argument */
+ current_pc += 2;
+ else if ((next & 0x1f) == 0x1b) /* skip imm32 argument */
+ current_pc += 4;
+
+ /* Calculate the total size of the saved registers, and add it to the
+ immediate value used to adjust SP. */
+ for (i = 0; reg_table[i].mask != 0; i++)
+ if (list12 & reg_table[i].mask)
+ offset += v850_reg_size;
+ pi->sp_offset -= offset;
+
+ /* Calculate the offsets of the registers relative to the value the SP
+ will have after the registers have been pushed and the imm5 value has
+ been subtracted from it. */
+ if (pifsr)
+ {
+ for (i = 0; reg_table[i].mask != 0; i++)
+ {
+ if (list12 & reg_table[i].mask)
+ {
+ int reg = reg_table[i].regno;
+ offset -= v850_reg_size;
+ pifsr->reg = reg;
+ pifsr->offset = offset;
+ pifsr->cur_frameoffset = pi->sp_offset;
+ pifsr++;
+ }
+ }
+ }
+
+ /* Set result parameters. */
+ *current_pc_ptr = current_pc;
+ *pifsr_ptr = pifsr;
+}
+
+
+/* Helper function for v850_scan_prologue to handle pushm/pushl instructions.
+ The SR bit of the register list is not supported. gcc does not generate
+ this bit. */
+
+static void
+v850_handle_pushm (int insn, int insn2, struct v850_frame_cache *pi,
+ struct pifsr **pifsr_ptr)
+{
+ struct pifsr *pifsr = *pifsr_ptr;
+ long list12 = ((insn & 0x0f) << 16) + (insn2 & 0xfff0);
+ long offset = 0;
+ static struct reg_list pushml_reg_table[] =
+ {
+ {0x80000, E_PS_REGNUM}, /* PSW */
+ {0x40000, 1}, /* r1 */
+ {0x20000, 2}, /* r2 */
+ {0x10000, 3}, /* r3 */
+ {0x00800, 4}, /* r4 */
+ {0x00400, 5}, /* r5 */
+ {0x00200, 6}, /* r6 */
+ {0x00100, 7}, /* r7 */
+ {0x08000, 8}, /* r8 */
+ {0x04000, 9}, /* r9 */
+ {0x02000, 10}, /* r10 */
+ {0x01000, 11}, /* r11 */
+ {0x00080, 12}, /* r12 */
+ {0x00040, 13}, /* r13 */
+ {0x00020, 14}, /* r14 */
+ {0x00010, 15}, /* r15 */
+ {0, 0} /* end of table */
+ };
+ static struct reg_list pushmh_reg_table[] =
+ {
+ {0x80000, 16}, /* r16 */
+ {0x40000, 17}, /* r17 */
+ {0x20000, 18}, /* r18 */
+ {0x10000, 19}, /* r19 */
+ {0x00800, 20}, /* r20 */
+ {0x00400, 21}, /* r21 */
+ {0x00200, 22}, /* r22 */
+ {0x00100, 23}, /* r23 */
+ {0x08000, 24}, /* r24 */
+ {0x04000, 25}, /* r25 */
+ {0x02000, 26}, /* r26 */
+ {0x01000, 27}, /* r27 */
+ {0x00080, 28}, /* r28 */
+ {0x00040, 29}, /* r29 */
+ {0x00010, 30}, /* r30 */
+ {0x00020, 31}, /* r31 */
+ {0, 0} /* end of table */
+ };
+ struct reg_list *reg_table;
+ int i;
+
+ /* Is this a pushml or a pushmh? */
+ if ((insn2 & 7) == 1)
+ reg_table = pushml_reg_table;
+ else
+ reg_table = pushmh_reg_table;
+
+ /* Calculate the total size of the saved registers, and add it it to the
+ immediate value used to adjust SP. */
+ for (i = 0; reg_table[i].mask != 0; i++)
+ if (list12 & reg_table[i].mask)
+ offset += v850_reg_size;
+ pi->sp_offset -= offset;
+
+ /* Calculate the offsets of the registers relative to the value the SP
+ will have after the registers have been pushed and the imm5 value is
+ subtracted from it. */
+ if (pifsr)
+ {
+ for (i = 0; reg_table[i].mask != 0; i++)
+ {
+ if (list12 & reg_table[i].mask)
+ {
+ int reg = reg_table[i].regno;
+ offset -= v850_reg_size;
+ pifsr->reg = reg;
+ pifsr->offset = offset;
+ pifsr->cur_frameoffset = pi->sp_offset;
+ pifsr++;
+ }
+ }
+ }
+
+ /* Set result parameters. */
+ *pifsr_ptr = pifsr;
+}
+
+/* Helper function to evaluate if register is one of the "save" registers.
+ This allows to simplify conditionals in v850_analyze_prologue a lot. */
+
+static int
+v850_is_save_register (int reg)
+{
+ /* The caller-save registers are R2, R20 - R29 and R31. All other
+ registers are either special purpose (PC, SP), argument registers,
+ or just considered free for use in the caller. */
+ return reg == E_R2_REGNUM
+ || (reg >= E_R20_REGNUM && reg <= E_R29_REGNUM)
+ || reg == E_R31_REGNUM;
+}
+
+/* Scan the prologue of the function that contains PC, and record what
+ we find in PI. Returns the pc after the prologue. Note that the
+ addresses saved in frame->saved_regs are just frame relative (negative
+ offsets from the frame pointer). This is because we don't know the
+ actual value of the frame pointer yet. In some circumstances, the
+ frame pointer can't be determined till after we have scanned the
+ prologue. */
+
+static CORE_ADDR
+v850_analyze_prologue (CORE_ADDR func_addr, CORE_ADDR pc,
+ struct v850_frame_cache *pi)
+{
+ CORE_ADDR prologue_end, current_pc;
+ struct pifsr pifsrs[E_NUM_REGS + 1];
+ struct pifsr *pifsr, *pifsr_tmp;
+ int fp_used;
+ int ep_used;
+ int reg;
+ CORE_ADDR save_pc, save_end;
+ int regsave_func_p;
+ int r12_tmp;
+
+ memset (&pifsrs, 0, sizeof pifsrs);
+ pifsr = &pifsrs[0];
+
+ prologue_end = pc;
+
+ /* Now, search the prologue looking for instructions that setup fp, save
+ rp, adjust sp and such. We also record the frame offset of any saved
+ registers. */
+
+ pi->sp_offset = 0;
+ pi->uses_fp = 0;
+ ep_used = 0;
+ regsave_func_p = 0;
+ save_pc = 0;
+ save_end = 0;
+ r12_tmp = 0;
+
+ for (current_pc = func_addr; current_pc < prologue_end;)
+ {
+ int insn;
+ int insn2 = -1; /* dummy value */
+
+ insn = read_memory_integer (current_pc, 2);
+ current_pc += 2;
+ if ((insn & 0x0780) >= 0x0600) /* Four byte instruction? */
+ {
+ insn2 = read_memory_integer (current_pc, 2);
+ current_pc += 2;
+ }
+
+ if ((insn & 0xffc0) == ((10 << 11) | 0x0780) && !regsave_func_p)
+ { /* jarl <func>,10 */
+ long low_disp = insn2 & ~(long) 1;
+ long disp = (((((insn & 0x3f) << 16) + low_disp)
+ & ~(long) 1) ^ 0x00200000) - 0x00200000;
+
+ save_pc = current_pc;
+ save_end = prologue_end;
+ regsave_func_p = 1;
+ current_pc += disp - 4;
+ prologue_end = (current_pc
+ + (2 * 3) /* moves to/from ep */
+ + 4 /* addi <const>,sp,sp */
+ + 2 /* jmp [r10] */
+ + (2 * 12) /* sst.w to save r2, r20-r29, r31 */
+ + 20); /* slop area */
+ }
+ else if ((insn & 0xffc0) == 0x0200 && !regsave_func_p)
+ { /* callt <imm6> */
+ long ctbp = read_register (E_CTBP_REGNUM);
+ long adr = ctbp + ((insn & 0x3f) << 1);
+
+ save_pc = current_pc;
+ save_end = prologue_end;
+ regsave_func_p = 1;
+ current_pc = ctbp + (read_memory_unsigned_integer (adr, 2) & 0xffff);
+ prologue_end = (current_pc
+ + (2 * 3) /* prepare list2,imm5,sp/imm */
+ + 4 /* ctret */
+ + 20); /* slop area */
+ continue;
+ }
+ else if ((insn & 0xffc0) == 0x0780) /* prepare list2,imm5 */
+ {
+ v850_handle_prepare (insn, insn2, ¤t_pc, pi, &pifsr);
+ continue;
+ }
+ else if (insn == 0x07e0 && regsave_func_p && insn2 == 0x0144)
+ { /* ctret after processing register save. */
+ current_pc = save_pc;
+ prologue_end = save_end;
+ regsave_func_p = 0;
+ continue;
+ }
+ else if ((insn & 0xfff0) == 0x07e0 && (insn2 & 5) == 1)
+ { /* pushml, pushmh */
+ v850_handle_pushm (insn, insn2, pi, &pifsr);
+ continue;
+ }
+ else if ((insn & 0xffe0) == 0x0060 && regsave_func_p)
+ { /* jmp after processing register save. */
+ current_pc = save_pc;
+ prologue_end = save_end;
+ regsave_func_p = 0;
+ continue;
+ }
+ else if ((insn & 0x07c0) == 0x0780 /* jarl or jr */
+ || (insn & 0xffe0) == 0x0060 /* jmp */
+ || (insn & 0x0780) == 0x0580) /* branch */
+ {
+ break; /* Ran into end of prologue */
+ }
+
+ else if ((insn & 0xffe0) == ((E_SP_REGNUM << 11) | 0x0240))
+ /* add <imm>,sp */
+ pi->sp_offset += ((insn & 0x1f) ^ 0x10) - 0x10;
+ else if (insn == ((E_SP_REGNUM << 11) | 0x0600 | E_SP_REGNUM))
+ /* addi <imm>,sp,sp */
+ pi->sp_offset += insn2;
+ else if (insn == ((E_FP_REGNUM << 11) | 0x0000 | E_SP_REGNUM))
+ /* mov sp,fp */
+ pi->uses_fp = 1;
+ else if (insn == ((E_R12_REGNUM << 11) | 0x0640 | E_R0_REGNUM))
+ /* movhi hi(const),r0,r12 */
+ r12_tmp = insn2 << 16;
+ else if (insn == ((E_R12_REGNUM << 11) | 0x0620 | E_R12_REGNUM))
+ /* movea lo(const),r12,r12 */
+ r12_tmp += insn2;
+ else if (insn == ((E_SP_REGNUM << 11) | 0x01c0 | E_R12_REGNUM) && r12_tmp)
+ /* add r12,sp */
+ pi->sp_offset += r12_tmp;
+ else if (insn == ((E_EP_REGNUM << 11) | 0x0000 | E_SP_REGNUM))
+ /* mov sp,ep */
+ ep_used = 1;
+ else if (insn == ((E_EP_REGNUM << 11) | 0x0000 | E_R1_REGNUM))
+ /* mov r1,ep */
+ ep_used = 0;
+ else if (((insn & 0x07ff) == (0x0760 | E_SP_REGNUM)
+ || (pi->uses_fp
+ && (insn & 0x07ff) == (0x0760 | E_FP_REGNUM)))
+ && pifsr
+ && v850_is_save_register (reg = (insn >> 11) & 0x1f))
+ {
+ /* st.w <reg>,<offset>[sp] or st.w <reg>,<offset>[fp] */
+ pifsr->reg = reg;
+ pifsr->offset = insn2 & ~1;
+ pifsr->cur_frameoffset = pi->sp_offset;
+ pifsr++;
+ }
+ else if (ep_used
+ && ((insn & 0x0781) == 0x0501)
+ && pifsr
+ && v850_is_save_register (reg = (insn >> 11) & 0x1f))
+ {
+ /* sst.w <reg>,<offset>[ep] */
+ pifsr->reg = reg;
+ pifsr->offset = (insn & 0x007e) << 1;
+ pifsr->cur_frameoffset = pi->sp_offset;
+ pifsr++;
+ }
+ }
+
+ /* Fix up any offsets to the final offset. If a frame pointer was created,
+ use it instead of the stack pointer. */
+ for (pifsr_tmp = pifsrs; pifsr_tmp != pifsr; pifsr_tmp++)
+ {
+ pifsr_tmp->offset -= pi->sp_offset - pifsr_tmp->cur_frameoffset;
+ pi->saved_regs[pifsr_tmp->reg].addr = pifsr_tmp->offset;
+ }
+
+ return current_pc;
+}
+
+/* Return the address of the first code past the prologue of the function. */
+
+static CORE_ADDR
+v850_skip_prologue (CORE_ADDR pc)
+{
+ CORE_ADDR func_addr, func_end;
+
+ /* See what the symbol table says */
+
+ if (find_pc_partial_function (pc, NULL, &func_addr, &func_end))
+ {
+ struct symtab_and_line sal;
+
+ sal = find_pc_line (func_addr, 0);
+
+ if (sal.line != 0 && sal.end < func_end)
+ return sal.end;
+
+ /* Either there's no line info, or the line after the prologue is after
+ the end of the function. In this case, there probably isn't a
+ prologue. */
+ return pc;
+ }
+
+/* We can't find the start of this function, so there's nothing we can do. */
+ return pc;
+}
+
+static CORE_ADDR
+v850_frame_align (struct gdbarch *ignore, CORE_ADDR sp)
+{
+ return sp & ~3;
+}
+
+/* Setup arguments and RP for a call to the target. First four args
+ go in R6->R9, subsequent args go into sp + 16 -> sp + ... Structs
+ are passed by reference. 64 bit quantities (doubles and long longs)
+ may be split between the regs and the stack. When calling a function
+ that returns a struct, a pointer to the struct is passed in as a secret
+ first argument (always in R6).
+
+ Stack space for the args has NOT been allocated: that job is up to us. */
+
+static CORE_ADDR
+v850_push_dummy_call (struct gdbarch *gdbarch,
+ struct value *function,
+ struct regcache *regcache,
+ CORE_ADDR bp_addr,
+ int nargs,
+ struct value **args,
+ CORE_ADDR sp,
+ int struct_return,
+ CORE_ADDR struct_addr)
+{
+ int argreg;
+ int argnum;
+ int len = 0;
+ int stack_offset;
+
+ /* The offset onto the stack at which we will start copying parameters
+ (after the registers are used up) begins at 16 rather than at zero.
+ That's how the ABI is defined, though there's no indication that these
+ 16 bytes are used for anything, not even for saving incoming
+ argument registers. */
+ stack_offset = 16;
+
+ /* Now make space on the stack for the args. */
+ for (argnum = 0; argnum < nargs; argnum++)
+ len += ((TYPE_LENGTH (value_type (args[argnum])) + 3) & ~3);
+ sp -= len + stack_offset;
+
+ argreg = E_ARG0_REGNUM;
+ /* The struct_return pointer occupies the first parameter register. */
+ if (struct_return)
+ regcache_cooked_write_unsigned (regcache, argreg++, struct_addr);
+
+ /* Now load as many as possible of the first arguments into
+ registers, and push the rest onto the stack. There are 16 bytes
+ in four registers available. Loop thru args from first to last. */
+ for (argnum = 0; argnum < nargs; argnum++)
+ {
+ int len;
+ gdb_byte *val;
+ gdb_byte valbuf[v850_reg_size];
+
+ if (!v850_type_is_scalar (value_type (*args))
+ && TYPE_LENGTH (value_type (*args)) > E_MAX_RETTYPE_SIZE_IN_REGS)
+ {
+ store_unsigned_integer (valbuf, 4, VALUE_ADDRESS (*args));
+ len = 4;
+ val = valbuf;
+ }
+ else
+ {
+ len = TYPE_LENGTH (value_type (*args));
+ val = (gdb_byte *) value_contents (*args);
+ }
+
+ while (len > 0)
+ if (argreg <= E_ARGLAST_REGNUM)
+ {
+ CORE_ADDR regval;
+
+ regval = extract_unsigned_integer (val, v850_reg_size);
+ regcache_cooked_write_unsigned (regcache, argreg, regval);
+
+ len -= v850_reg_size;
+ val += v850_reg_size;
+ argreg++;
+ }
+ else
+ {
+ write_memory (sp + stack_offset, val, 4);
+
+ len -= 4;
+ val += 4;
+ stack_offset += 4;
+ }
+ args++;
+ }
+
+ /* Store return address. */
+ regcache_cooked_write_unsigned (regcache, E_LP_REGNUM, bp_addr);
+
+ /* Update stack pointer. */
+ regcache_cooked_write_unsigned (regcache, E_SP_REGNUM, sp);
+
+ return sp;
+}
+
+static void
+v850_extract_return_value (struct type *type, struct regcache *regcache,
+ gdb_byte *valbuf)
+{
+ int len = TYPE_LENGTH (type);
+
+ if (len <= v850_reg_size)
+ {
+ ULONGEST val;
+
+ regcache_cooked_read_unsigned (regcache, E_V0_REGNUM, &val);
+ store_unsigned_integer (valbuf, len, val);
+ }
+ else if (len <= 2 * v850_reg_size)
+ {
+ int i, regnum = E_V0_REGNUM;
+ gdb_byte buf[v850_reg_size];
+ for (i = 0; len > 0; i += 4, len -= 4)
+ {
+ regcache_raw_read (regcache, regnum++, buf);
+ memcpy (valbuf + i, buf, len > 4 ? 4 : len);
+ }
+ }
+}
+
+static void
+v850_store_return_value (struct type *type, struct regcache *regcache,
+ const gdb_byte *valbuf)
+{
+ int len = TYPE_LENGTH (type);
+
+ if (len <= v850_reg_size)
+ regcache_cooked_write_unsigned (regcache, E_V0_REGNUM,
+ extract_unsigned_integer (valbuf, len));
+ else if (len <= 2 * v850_reg_size)
+ {
+ int i, regnum = E_V0_REGNUM;
+ for (i = 0; i < len; i += 4)
+ regcache_raw_write (regcache, regnum++, valbuf + i);
+ }
+}
+
+static enum return_value_convention
+v850_return_value (struct gdbarch *gdbarch, struct type *type,
+ struct regcache *regcache,
+ gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+ if (v850_use_struct_convention (type))
+ return RETURN_VALUE_STRUCT_CONVENTION;
+ if (writebuf)
+ v850_store_return_value (type, regcache, writebuf);
+ else if (readbuf)
+ v850_extract_return_value (type, regcache, readbuf);
+ return RETURN_VALUE_REGISTER_CONVENTION;
+}
+
+const static unsigned char *
+v850_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
+{
+ static unsigned char breakpoint[] = { 0x85, 0x05 };
+ *lenptr = sizeof (breakpoint);
+ return breakpoint;
+}
+
+static struct v850_frame_cache *
+v850_alloc_frame_cache (struct frame_info *next_frame)
+{
+ struct v850_frame_cache *cache;
+ int i;
+
+ cache = FRAME_OBSTACK_ZALLOC (struct v850_frame_cache);
+ cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
+
+ /* Base address. */
+ cache->base = 0;
+ cache->sp_offset = 0;
+ cache->pc = 0;
+
+ /* Frameless until proven otherwise. */
+ cache->uses_fp = 0;
+
+ return cache;
+}
+
+static struct v850_frame_cache *
+v850_frame_cache (struct frame_info *next_frame, void **this_cache)
+{
+ struct v850_frame_cache *cache;
+ CORE_ADDR current_pc;
+ int i;
+
+ if (*this_cache)
+ return *this_cache;
+
+ cache = v850_alloc_frame_cache (next_frame);
+ *this_cache = cache;
+
+ /* In principle, for normal frames, fp holds the frame pointer,
+ which holds the base address for the current stack frame.
+ However, for functions that don't need it, the frame pointer is
+ optional. For these "frameless" functions the frame pointer is
+ actually the frame pointer of the calling frame. */
+ cache->base = frame_unwind_register_unsigned (next_frame, E_FP_REGNUM);
+ if (cache->base == 0)
+ return cache;
+
+ cache->pc = frame_func_unwind (next_frame);
+ current_pc = frame_pc_unwind (next_frame);
+ if (cache->pc != 0)
+ v850_analyze_prologue (cache->pc, current_pc, cache);
+
+ if (!cache->uses_fp)
+ {
+ /* We didn't find a valid frame, which means that CACHE->base
+ currently holds the frame pointer for our calling frame. If
+ we're at the start of a function, or somewhere half-way its
+ prologue, the function's frame probably hasn't been fully
+ setup yet. Try to reconstruct the base address for the stack
+ frame by looking at the stack pointer. For truly "frameless"
+ functions this might work too. */
+ cache->base = frame_unwind_register_unsigned (next_frame, E_SP_REGNUM);
+ }
+
+ /* Now that we have the base address for the stack frame we can
+ calculate the value of sp in the calling frame. */
+ trad_frame_set_value (cache->saved_regs, E_SP_REGNUM,
+ cache->base - cache->sp_offset);
+
+ /* Adjust all the saved registers such that they contain addresses
+ instead of offsets. */
+ for (i = 0; i < E_NUM_REGS; i++)
+ if (trad_frame_addr_p (cache->saved_regs, i))
+ cache->saved_regs[i].addr += cache->base;
+
+ /* The call instruction moves the caller's PC in the callee's LP.
+ Since this is an unwind, do the reverse. Copy the location of LP
+ into PC (the address / regnum) so that a request for PC will be
+ converted into a request for the LP. */
+
+ cache->saved_regs[E_PC_REGNUM] = cache->saved_regs[E_LP_REGNUM];
+
+ return cache;
+}
+
+
+static void
+v850_frame_prev_register (struct frame_info *next_frame, void **this_cache,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, void *valuep)
+{
+ struct v850_frame_cache *cache = v850_frame_cache (next_frame, this_cache);
+
+ gdb_assert (regnum >= 0);
+
+ trad_frame_get_prev_register (next_frame, cache->saved_regs, regnum,
+ optimizedp, lvalp, addrp, realnump, valuep);
+}
+
+static void
+v850_frame_this_id (struct frame_info *next_frame, void **this_cache,
+ struct frame_id *this_id)
+{
+ struct v850_frame_cache *cache = v850_frame_cache (next_frame, this_cache);
+
+ /* This marks the outermost frame. */
+ if (cache->base == 0)
+ return;
+
+ *this_id = frame_id_build (cache->saved_regs[E_SP_REGNUM].addr, cache->pc);
+}
+
+static const struct frame_unwind v850_frame_unwind = {
+ NORMAL_FRAME,
+ v850_frame_this_id,
+ v850_frame_prev_register
+};
+
+static const struct frame_unwind *
+v850_frame_sniffer (struct frame_info *next_frame)
+{
+ return &v850_frame_unwind;
+}
+
+static CORE_ADDR
+v850_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ return frame_unwind_register_unsigned (next_frame, SP_REGNUM);
+}
+
+static CORE_ADDR
+v850_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ return frame_unwind_register_unsigned (next_frame, PC_REGNUM);
+}
+
+static struct frame_id
+v850_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ return frame_id_build (v850_unwind_sp (gdbarch, next_frame),
+ frame_pc_unwind (next_frame));
+}
+
+static CORE_ADDR
+v850_frame_base_address (struct frame_info *next_frame, void **this_cache)
+{
+ struct v850_frame_cache *cache = v850_frame_cache (next_frame, this_cache);
+
+ return cache->base;
+}
+
+static const struct frame_base v850_frame_base = {
+ &v850_frame_unwind,
+ v850_frame_base_address,
+ v850_frame_base_address,
+ v850_frame_base_address
+};
+
+static struct gdbarch *
+v850_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+ struct gdbarch *gdbarch;
+
+ /* Change the register names based on the current machine type. */
+ if (info.bfd_arch_info->arch != bfd_arch_v850)
+ return NULL;
+
+ gdbarch = gdbarch_alloc (&info, NULL);
+
+ switch (info.bfd_arch_info->mach)
+ {
+ case bfd_mach_v850:
+ set_gdbarch_register_name (gdbarch, v850_register_name);
+ break;
+ case bfd_mach_v850e:
+ case bfd_mach_v850e1:
+ set_gdbarch_register_name (gdbarch, v850e_register_name);
+ break;
+ }
+
+ set_gdbarch_num_regs (gdbarch, E_NUM_REGS);
+ set_gdbarch_num_pseudo_regs (gdbarch, 0);
+ set_gdbarch_sp_regnum (gdbarch, E_SP_REGNUM);
+ set_gdbarch_pc_regnum (gdbarch, E_PC_REGNUM);
+ set_gdbarch_fp0_regnum (gdbarch, -1);
+
+ set_gdbarch_register_type (gdbarch, v850_register_type);
+
+ set_gdbarch_char_signed (gdbarch, 0);
+ set_gdbarch_short_bit (gdbarch, 2 * TARGET_CHAR_BIT);
+ set_gdbarch_int_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_long_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_long_long_bit (gdbarch, 8 * TARGET_CHAR_BIT);
+
+ set_gdbarch_float_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_double_bit (gdbarch, 8 * TARGET_CHAR_BIT);
+ set_gdbarch_long_double_bit (gdbarch, 8 * TARGET_CHAR_BIT);
+
+ set_gdbarch_ptr_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_addr_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+
+ set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+ set_gdbarch_breakpoint_from_pc (gdbarch, v850_breakpoint_from_pc);
+
+ set_gdbarch_return_value (gdbarch, v850_return_value);
+ set_gdbarch_push_dummy_call (gdbarch, v850_push_dummy_call);
+ set_gdbarch_skip_prologue (gdbarch, v850_skip_prologue);
+
+ set_gdbarch_print_insn (gdbarch, print_insn_v850);
+
+ set_gdbarch_frame_align (gdbarch, v850_frame_align);
+ set_gdbarch_unwind_sp (gdbarch, v850_unwind_sp);
+ set_gdbarch_unwind_pc (gdbarch, v850_unwind_pc);
+ set_gdbarch_unwind_dummy_id (gdbarch, v850_unwind_dummy_id);
+ frame_base_set_default (gdbarch, &v850_frame_base);
+
+ /* Hook in ABI-specific overrides, if they have been registered. */
+ gdbarch_init_osabi (info, gdbarch);
+
+ frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
+ frame_unwind_append_sniffer (gdbarch, v850_frame_sniffer);
+
+ return gdbarch;
+}
+
+extern initialize_file_ftype _initialize_v850_tdep; /* -Wmissing-prototypes */
+
+void
+_initialize_v850_tdep (void)
+{
+ register_gdbarch_init (bfd_arch_v850, v850_gdbarch_init);
+}
Index: config/v850/v850.mt
===================================================================
RCS file: config/v850/v850.mt
diff -N config/v850/v850.mt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ config/v850/v850.mt 17 May 2005 14:07:11 -0000
@@ -0,0 +1,4 @@
+# Target: NEC V850 processor
+TDEPFILES= v850-tdep.o
+SIM_OBS = remote-sim.o
+SIM = ../sim/v850/libsim.a
--
Corinna Vinschen
Cygwin Project Co-Leader
Red Hat, Inc.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFA] Resurrect v850
2005-05-17 14:39 ` Corinna Vinschen
@ 2005-05-17 20:40 ` Mark Kettenis
2005-05-18 1:11 ` Daniel Jacobowitz
2005-05-18 1:40 ` Daniel Jacobowitz
1 sibling, 1 reply; 12+ messages in thread
From: Mark Kettenis @ 2005-05-17 20:40 UTC (permalink / raw)
To: gdb-patches; +Cc: gdb-patches
Date: Tue, 17 May 2005 16:09:35 +0200
From: Corinna Vinschen <vinschen@redhat.com>
Hi Daniel,
Ok, I *think* I have followed all your suggestions and hints throughout
the file. The result is attached below.
This looks pretty good. I noticed that v850_register_type returns
builtin_type_uint32 for your E_PC_REGNUM. Did you consider returning
builtin_type_void_func_ptr for that register? It has the nice
side-effect that "info registers" displays the function name the pc is
currently in.
Don't let this withhold you from checking this in though if Daniel has
no objections.
Mark
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFA] Resurrect v850
2005-05-17 20:40 ` Mark Kettenis
@ 2005-05-18 1:11 ` Daniel Jacobowitz
2005-05-18 1:25 ` Mark Kettenis
0 siblings, 1 reply; 12+ messages in thread
From: Daniel Jacobowitz @ 2005-05-18 1:11 UTC (permalink / raw)
To: gdb-patches
On Tue, May 17, 2005 at 10:29:53PM +0200, Mark Kettenis wrote:
> Date: Tue, 17 May 2005 16:09:35 +0200
> From: Corinna Vinschen <vinschen@redhat.com>
>
> Hi Daniel,
>
> Ok, I *think* I have followed all your suggestions and hints throughout
> the file. The result is attached below.
>
> This looks pretty good. I noticed that v850_register_type returns
> builtin_type_uint32 for your E_PC_REGNUM. Did you consider returning
> builtin_type_void_func_ptr for that register? It has the nice
> side-effect that "info registers" displays the function name the pc is
> currently in.
Would "set $pc = $pc + 2" still work if $pc is a function pointer?
--
Daniel Jacobowitz
CodeSourcery, LLC
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFA] Resurrect v850
2005-05-18 1:11 ` Daniel Jacobowitz
@ 2005-05-18 1:25 ` Mark Kettenis
0 siblings, 0 replies; 12+ messages in thread
From: Mark Kettenis @ 2005-05-18 1:25 UTC (permalink / raw)
To: drow; +Cc: gdb-patches
Date: Tue, 17 May 2005 16:32:55 -0400
From: Daniel Jacobowitz <drow@false.org>
On Tue, May 17, 2005 at 10:29:53PM +0200, Mark Kettenis wrote:
> Date: Tue, 17 May 2005 16:09:35 +0200
> From: Corinna Vinschen <vinschen@redhat.com>
>
> Hi Daniel,
>
> Ok, I *think* I have followed all your suggestions and hints throughout
> the file. The result is attached below.
>
> This looks pretty good. I noticed that v850_register_type returns
> builtin_type_uint32 for your E_PC_REGNUM. Did you consider returning
> builtin_type_void_func_ptr for that register? It has the nice
> side-effect that "info registers" displays the function name the pc is
> currently in.
Would "set $pc = $pc + 2" still work if $pc is a function pointer?
Seems to work for me on the i386 target that I just modified to do
this ;-). And amd64 and sparc/sparc64 already do this for quite some
time. Actually I was surprised to find out that i386 didn't. Expect
that to be fixed soon ;-).
Mark
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFA] Resurrect v850
2005-05-17 14:39 ` Corinna Vinschen
2005-05-17 20:40 ` Mark Kettenis
@ 2005-05-18 1:40 ` Daniel Jacobowitz
2005-05-18 10:18 ` Corinna Vinschen
1 sibling, 1 reply; 12+ messages in thread
From: Daniel Jacobowitz @ 2005-05-18 1:40 UTC (permalink / raw)
To: gdb-patches
On Tue, May 17, 2005 at 04:09:35PM +0200, Corinna Vinschen wrote:
> Ok, I *think* I have followed all your suggestions and hints throughout
> the file. The result is attached below.
This is OK with trivial changes, with or without Mark's suggested
change for the type of $pc. Thanks.
> +/* Info gleaned from scanning a function's prologue. */
> +struct pifsr /* Info about one saved reg */
> +{
> + int offset; /* Offset from sp or fp */
> + int cur_frameoffset; /* Current frameoffset */
> + int reg; /* Saved register number */
> +};
Missed a couple of trailing stops in the comments.
> + /* The value is a structure or union with a single element and that
> + element is either a single basic type or an array of a single basic
> + type whoes size is greater than or equal to 4 -> returned in register. */
"whose".
--
Daniel Jacobowitz
CodeSourcery, LLC
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFA] Resurrect v850
2005-05-18 1:40 ` Daniel Jacobowitz
@ 2005-05-18 10:18 ` Corinna Vinschen
0 siblings, 0 replies; 12+ messages in thread
From: Corinna Vinschen @ 2005-05-18 10:18 UTC (permalink / raw)
To: gdb-patches
On May 17 21:10, Daniel Jacobowitz wrote:
> On Tue, May 17, 2005 at 04:09:35PM +0200, Corinna Vinschen wrote:
> > Ok, I *think* I have followed all your suggestions and hints throughout
> > the file. The result is attached below.
>
> This is OK with trivial changes, with or without Mark's suggested
> change for the type of $pc. Thanks.
>
> > +/* Info gleaned from scanning a function's prologue. */
> > +struct pifsr /* Info about one saved reg */
> > +{
> > + int offset; /* Offset from sp or fp */
> > + int cur_frameoffset; /* Current frameoffset */
> > + int reg; /* Saved register number */
> > +};
>
> Missed a couple of trailing stops in the comments.
>
> > + /* The value is a structure or union with a single element and that
> > + element is either a single basic type or an array of a single basic
> > + type whoes size is greater than or equal to 4 -> returned in register. */
>
> "whose".
I re-inspected the comments and fixed a few more. I've also changed
v850_register_type according to Mark's suggestion.
Applied.
Thanks to both of you,
Corinna
--
Corinna Vinschen
Cygwin Project Co-Leader
Red Hat, Inc.
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2005-05-18 8:53 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-05-13 12:28 [RFA] Resurrect v850 Corinna Vinschen
2005-05-15 18:20 ` Daniel Jacobowitz
2005-05-17 13:39 ` Corinna Vinschen
2005-05-17 13:52 ` Daniel Jacobowitz
2005-05-17 14:39 ` Corinna Vinschen
2005-05-17 20:40 ` Mark Kettenis
2005-05-18 1:11 ` Daniel Jacobowitz
2005-05-18 1:25 ` Mark Kettenis
2005-05-18 1:40 ` Daniel Jacobowitz
2005-05-18 10:18 ` Corinna Vinschen
2005-05-17 14:01 ` Corinna Vinschen
2005-05-17 14:10 ` Daniel Jacobowitz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox