commit 45c4637b3cf602268bb69b9e7fc045d5cbb70d71 Author: Vladimir Prus Date: Fri Jan 14 00:10:12 2011 +0300 Use the right structure and pointer return registers on m68k-elf. * m68k-tdep.h (struct gdbarch_tdep): Add ptr_value_regnum field. * m68k-tdep.c (m68k_gdbarch_init): Use A0 for structure returns on ELF targets, A1 otherwise. Use %d0 for ptr_value_regnum. (m68k_svr4_extract_return_value): Use tdep->ptr_value_regnum for pointer returns. (m68k_svr4_store_return_value): Likewise. (m68k_reg_struct_return_r): New, broken out of ... (m68k_reg_struct_return_p): ... here. Implement gcc's structure mode algorithm. (m68k_svr4_init_abi): No need to specify %a0 for structure returns here. Set ptr_value_regnum, though. * m68kbsd-tdep.c (m68kbsd_aout_init_abi): Set ptr_value_regnum to A0. (m68kbsd_elf_init_abi): Use A1 for struct return on OpenBSD. diff --git a/gdb/m68k-tdep.c b/gdb/m68k-tdep.c index 082cb84..194ecf3 100644 --- a/gdb/m68k-tdep.c +++ b/gdb/m68k-tdep.c @@ -320,7 +320,7 @@ m68k_svr4_extract_return_value (struct type *type, struct regcache *regcache, convert_typed_floating (buf, fpreg_type, valbuf, type); } else if (TYPE_CODE (type) == TYPE_CODE_PTR && len == 4) - regcache_raw_read (regcache, M68K_A0_REGNUM, valbuf); + regcache_raw_read (regcache, tdep->ptr_value_regnum, valbuf); else m68k_extract_return_value (type, regcache, valbuf); } @@ -362,16 +362,77 @@ m68k_svr4_store_return_value (struct type *type, struct regcache *regcache, regcache_raw_write (regcache, M68K_FP0_REGNUM, buf); } else if (TYPE_CODE (type) == TYPE_CODE_PTR && len == 4) - { - regcache_raw_write (regcache, M68K_A0_REGNUM, valbuf); - regcache_raw_write (regcache, M68K_D0_REGNUM, valbuf); - } + regcache_raw_write (regcache, tdep->ptr_value_regnum, valbuf); else m68k_store_return_value (type, regcache, valbuf); } /* Return non-zero if TYPE, which is assumed to be a structure or union type, should be returned in registers for architecture + GDBARCH. + + Unfortunately GCC incorrectly implements this optimization. Rather + than simply return all small structs, or even just those of size + 2^N, it uses the mode of the structure to determine this. BLKmode + structs will be returned by memory and QI,HI,SI,DI,SF&DF mode + structs will be returned by register. For m68k a struct is BLKmode + unless it's size is 2^N and the mode for that size does not need a + greater alignment than the structure itself. This is horrible. */ + + +static int +m68k_reg_struct_return_r (struct type *type, int *align_p) +{ + enum type_code code = TYPE_CODE (type); + int len = TYPE_LENGTH (type); + int field_align = 1; + int my_align = len > 2 ? 2 : len; + int ix; + + if (code != TYPE_CODE_STRUCT && code != TYPE_CODE_UNION) + { + if (align_p && my_align > *align_p) + *align_p = my_align; + return 1; + } + + if ((len & -len) != len) + /* Length is not 2^n. */ + return 0; + + for (ix = 0; ix != TYPE_NFIELDS (type); ix++) + { + struct type *field_type; + int field_len; + + if (field_is_static (&TYPE_FIELD (type, ix))) + /* Skip static fields. */ + continue; + + field_type = TYPE_FIELD_TYPE (type, ix); + field_type = check_typedef (field_type); + field_len = TYPE_LENGTH (field_type); + + /* Look through arrays. */ + while (TYPE_CODE (field_type) == TYPE_CODE_ARRAY) + { + field_type = TYPE_TARGET_TYPE (field_type); + field_type = check_typedef (field_type); + field_len = TYPE_LENGTH (field_type); + } + + if (!m68k_reg_struct_return_r (field_type, &field_align)) + return 0; + } + + if (align_p && field_align > *align_p) + *align_p = field_align; + + return align_p || my_align <= field_align; +} + +/* Return non-zero if TYPE, which is assumed to be a structure or + union type, should be returned in registers for architecture GDBARCH. */ static int @@ -386,7 +447,11 @@ m68k_reg_struct_return_p (struct gdbarch *gdbarch, struct type *type) if (tdep->struct_return == pcc_struct_return) return 0; - return (len == 1 || len == 2 || len == 4 || len == 8); + if (len > 8) + /* Length is too big. */ + return 0; + + return m68k_reg_struct_return_r (type, NULL); } /* Determine, for architecture GDBARCH, how a return value of TYPE @@ -440,25 +505,11 @@ m68k_svr4_return_value (struct gdbarch *gdbarch, struct type *func_type, if ((code == TYPE_CODE_STRUCT || code == TYPE_CODE_UNION) && !m68k_reg_struct_return_p (gdbarch, type)) { - /* The System V ABI says that: - - "A function returning a structure or union also sets %a0 to - the value it finds in %a0. Thus when the caller receives - control again, the address of the returned object resides in - register %a0." - - So the ABI guarantees that we can always find the return - value just after the function has returned. */ - - if (readbuf) - { - ULONGEST addr; - - regcache_raw_read_unsigned (regcache, M68K_A0_REGNUM, &addr); - read_memory (addr, readbuf, TYPE_LENGTH (type)); - } - - return RETURN_VALUE_ABI_RETURNS_ADDRESS; + /* Although they SYSV ABI specifies that a function returning a + structure this way should preserve %a0, GCC doesn't do that. + Furthermore there's no point changeing GCC to make it do it, + as that would just be bloat. */ + return RETURN_VALUE_STRUCT_CONVENTION; } /* This special case is for structures consisting of a single @@ -1053,8 +1104,7 @@ m68k_svr4_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* SVR4 uses a different calling convention. */ set_gdbarch_return_value (gdbarch, m68k_svr4_return_value); - /* SVR4 uses %a0 instead of %a1. */ - tdep->struct_value_regnum = M68K_A0_REGNUM; + tdep->ptr_value_regnum = M68K_A0_REGNUM; } @@ -1228,7 +1278,17 @@ m68k_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Function call & return. */ set_gdbarch_push_dummy_call (gdbarch, m68k_push_dummy_call); set_gdbarch_return_value (gdbarch, m68k_return_value); + tdep->struct_return = reg_struct_return; + /* These register numbers may be overridden by an OSABI + sniffer. */ + if (info.abfd == NULL) + tdep->struct_value_regnum = M68K_A0_REGNUM; + else if (bfd_get_flavour (info.abfd) == bfd_target_elf_flavour) + tdep->struct_value_regnum = M68K_A0_REGNUM; + else + tdep->struct_value_regnum = M68K_A1_REGNUM; + tdep->ptr_value_regnum = M68K_D0_REGNUM; /* Disassembler. */ set_gdbarch_print_insn (gdbarch, print_insn_m68k); @@ -1239,8 +1299,6 @@ m68k_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) #else tdep->jb_pc = -1; #endif - tdep->struct_value_regnum = M68K_A1_REGNUM; - tdep->struct_return = reg_struct_return; /* Frame unwinder. */ set_gdbarch_dummy_id (gdbarch, m68k_dummy_id); diff --git a/gdb/m68k-tdep.h b/gdb/m68k-tdep.h index 596a8cb..3ea7f24 100644 --- a/gdb/m68k-tdep.h +++ b/gdb/m68k-tdep.h @@ -83,6 +83,9 @@ struct gdbarch_tdep /* Convention for returning structures. */ enum struct_return struct_return; + /* Register in which pointers are returned. */ + int ptr_value_regnum; + /* Convention for returning floats. zero in int regs, non-zero in float. */ int float_return; diff --git a/gdb/m68kbsd-tdep.c b/gdb/m68kbsd-tdep.c index 81e34e9..f5bfa1a 100644 --- a/gdb/m68kbsd-tdep.c +++ b/gdb/m68kbsd-tdep.c @@ -213,11 +213,12 @@ m68kbsd_aout_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) m68kbsd_init_abi (info, gdbarch); tdep->struct_return = reg_struct_return; + tdep->ptr_value_regnum = M68K_A0_REGNUM; tramp_frame_prepend_unwinder (gdbarch, &m68kobsd_sigtramp); } -/* NetBSD ELF. */ +/* OpenBSD and NetBSD ELF. */ static void m68kbsd_elf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) @@ -233,6 +234,15 @@ m68kbsd_elf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* NetBSD ELF uses SVR4-style shared libraries. */ set_solib_svr4_fetch_link_map_offsets (gdbarch, svr4_ilp32_fetch_link_map_offsets); + + /* OpenBSD uses %a1to return structures. */ + if (info.abfd) + { + enum gdb_osabi osabi = gdbarch_lookup_osabi (info.abfd); + + if (osabi == GDB_OSABI_OPENBSD_ELF) + tdep->struct_value_regnum = M68K_A1_REGNUM; + } }