This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
m68k-elf return value registers
- From: Vladimir Prus <vladimir at codesourcery dot com>
- To: gdb-patches at sources dot redhat dot com
- Date: Fri, 14 Jan 2011 01:57:38 +0300
- Subject: m68k-elf return value registers
This patch, originally written by Nathan Sidwel, makes m68k-tdep.c use right
registrers for returning values from functions on bare metal. There are
actually 3 parts:
1. Right now, it's assumed that pointer values are returned in %a0. However,
for m68k-elf, they are returned in %d0. So, this patch makes %a0 the default,
and then makes OSABI sniffers set %a0 again.
2. Structures can be returned in either %a0 or %a1. This was discussed
previously at:
http://thread.gmane.org/gmane.comp.gdb.devel/20117
I have now put together a table listing what register gcc uses, and how
gdb does the same with this patch. See:
http://tinyurl.com/5r2j5x6
As you see, in the end we have only uclinuxoldeabi using wrong register
(just as now). I am not sure whether (i) this can be fixed, and (ii) anybody
care, given that this target was proposed for deprecation in gcc 4.6. Anyway,
if somebody cares, it can be done with a separate patch.
3. This patch also updates the logic that decides whether structure is
returned in memory or registers. This is a black magic written by Nathan
that I don't pretend to understand.
Is this OK? I have tests still running, but they will only cover elf and
linux, so it would be great to have somebody check behaviour for other
targets.
--
Vladimir Prus
Mentor Graphics
+7 (812) 677-68-40
commit 45c4637b3cf602268bb69b9e7fc045d5cbb70d71
Author: Vladimir Prus <vladimir@codesourcery.com>
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;
+ }
}