From afce3d2aa20daedddd9855d0a0fa10f899c837f8 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Fri, 10 Oct 2003 20:30:45 +0000 Subject: [PATCH] * sh-tdep.c (sh_treat_as_flt_p): New function to recognize float types correctly. (sh_push_dummy_call_fpu): Fix argument passing rules. (sh3e_sh4_extract_return_value): Call sh_treat_as_flt_p to recognize float types. (sh3e_sh4_store_return_value): Ditto. --- gdb/ChangeLog | 9 ++++++ gdb/sh-tdep.c | 84 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 5bc5854abbd..852d8dae64b 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,12 @@ +2003-10-10 Corinna Vinschen + + * sh-tdep.c (sh_treat_as_flt_p): New function to recognize float + types correctly. + (sh_push_dummy_call_fpu): Fix argument passing rules. + (sh3e_sh4_extract_return_value): Call sh_treat_as_flt_p to recognize + float types. + (sh3e_sh4_store_return_value): Ditto. + 2003-10-10 Elena Zannoni * sh-tdep.c (sh_use_struct_convention): Clarify one case in diff --git a/gdb/sh-tdep.c b/gdb/sh-tdep.c index 3d8c62943fe..c99b8acde86 100644 --- a/gdb/sh-tdep.c +++ b/gdb/sh-tdep.c @@ -788,6 +788,55 @@ sh_next_flt_argreg (int len) return FLOAT_ARG0_REGNUM + argreg; } +/* Helper function which figures out, if a type is treated like a float type. + + Second, the FPU ABIs have a special way how to treat types as float types. + Structures with exactly one member, which is of type float or double, are + treated exactly as the base types float or double: + + struct sf { + float f; + }; + + struct sd { + double d; + }; + + are handled the same way as just + + float f; + + double d; + + As a result, arguments of these struct types are pushed into floating point + registers exactly as floats or doubles, using the same decision algorithm. + + The same is valid if these types are used as function return types. The + above structs are returned in fr0 resp. fr0,fr1 instead of in r0, r0,r1 + or even using struct convention as it is for other structs. */ + +static int +sh_treat_as_flt_p (struct type *type) +{ + int len = TYPE_LENGTH (type); + + /* Ordinary float types are obviously treated as float. */ + if (TYPE_CODE (type) == TYPE_CODE_FLT) + return 1; + /* Otherwise non-struct types are not treated as float. */ + if (TYPE_CODE (type) != TYPE_CODE_STRUCT) + return 0; + /* Otherwise structs with more than one memeber are not treated as float. */ + if (TYPE_NFIELDS (type) != 1) + return 0; + /* Otherwise if the type of that member is float, the whole type is + treated as float. */ + if (TYPE_CODE (TYPE_FIELD_TYPE (type, 0)) == TYPE_CODE_FLT) + return 1; + /* Otherwise it's not treated as float. */ + return 0; +} + static CORE_ADDR sh_push_dummy_call_fpu (struct gdbarch *gdbarch, CORE_ADDR func_addr, @@ -805,7 +854,8 @@ sh_push_dummy_call_fpu (struct gdbarch *gdbarch, CORE_ADDR regval; char *val; int len, reg_size = 0; - int pass_on_stack; + int pass_on_stack = 0; + int treat_as_flt; /* first force sp to a 4-byte alignment */ sp = sh_frame_align (gdbarch, sp); @@ -832,43 +882,43 @@ sh_push_dummy_call_fpu (struct gdbarch *gdbarch, /* Some decisions have to be made how various types are handled. This also differs in different ABIs. */ pass_on_stack = 0; - if (len > 16) - pass_on_stack = 1; /* Types bigger than 16 bytes are passed on stack. */ /* Find out the next register to use for a floating point value. */ - if (TYPE_CODE (type) == TYPE_CODE_FLT) + treat_as_flt = sh_treat_as_flt_p (type); + if (treat_as_flt) flt_argreg = sh_next_flt_argreg (len); + /* In contrast to non-FPU CPUs, arguments are never split between + registers and stack. If an argument doesn't fit in the remaining + registers it's always pushed entirely on the stack. */ + else if (len > ((ARGLAST_REGNUM - argreg + 1) * 4)) + pass_on_stack = 1; while (len > 0) { - if ((TYPE_CODE (type) == TYPE_CODE_FLT - && flt_argreg > FLOAT_ARGLAST_REGNUM) - || argreg > ARGLAST_REGNUM || pass_on_stack) + if ((treat_as_flt && flt_argreg > FLOAT_ARGLAST_REGNUM) + || (!treat_as_flt && (argreg > ARGLAST_REGNUM + || pass_on_stack))) { - /* The remainder of the data goes entirely on the stack, - 4-byte aligned. */ + /* The data goes entirely on the stack, 4-byte aligned. */ reg_size = (len + 3) & ~3; write_memory (sp + stack_offset, val, reg_size); stack_offset += reg_size; } - else if (TYPE_CODE (type) == TYPE_CODE_FLT - && flt_argreg <= FLOAT_ARGLAST_REGNUM) + else if (treat_as_flt && flt_argreg <= FLOAT_ARGLAST_REGNUM) { /* Argument goes in a float argument register. */ reg_size = register_size (gdbarch, flt_argreg); regval = extract_unsigned_integer (val, reg_size); regcache_cooked_write_unsigned (regcache, flt_argreg++, regval); } - else if (argreg <= ARGLAST_REGNUM) + else if (!treat_as_flt && argreg <= ARGLAST_REGNUM) { /* there's room in a register */ reg_size = register_size (gdbarch, argreg); regval = extract_unsigned_integer (val, reg_size); regcache_cooked_write_unsigned (regcache, argreg++, regval); } - /* Store the value reg_size bytes at a time. This means that things - larger than reg_size bytes may go partly in registers and partly - on the stack. */ + /* Store the value one register at a time or in one step on stack. */ len -= reg_size; val += reg_size; } @@ -986,7 +1036,7 @@ static void sh3e_sh4_extract_return_value (struct type *type, struct regcache *regcache, void *valbuf) { - if (TYPE_CODE (type) == TYPE_CODE_FLT) + if (sh_treat_as_flt_p (type)) { int len = TYPE_LENGTH (type); int i, regnum = FP0_REGNUM; @@ -1027,7 +1077,7 @@ static void sh3e_sh4_store_return_value (struct type *type, struct regcache *regcache, const void *valbuf) { - if (TYPE_CODE (type) == TYPE_CODE_FLT) + if (sh_treat_as_flt_p (type)) { int len = TYPE_LENGTH (type); int i, regnum = FP0_REGNUM; -- 2.30.2