From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 27041 invoked by alias); 17 Sep 2003 16:11:46 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 27026 invoked from network); 17 Sep 2003 16:11:44 -0000 Received: from unknown (HELO mx1.redhat.com) (66.187.233.31) by sources.redhat.com with SMTP; 17 Sep 2003 16:11:44 -0000 Received: from int-mx2.corp.redhat.com (nat-pool-rdu-dmz.redhat.com [172.16.52.200]) by mx1.redhat.com (8.11.6/8.11.6) with ESMTP id h8HGBe130524 for ; Wed, 17 Sep 2003 12:11:41 -0400 Received: from potter.sfbay.redhat.com (potter.sfbay.redhat.com [172.16.27.15]) by int-mx2.corp.redhat.com (8.11.6/8.11.6) with ESMTP id h8HGBaL14169 for ; Wed, 17 Sep 2003 12:11:36 -0400 Received: from cygbert.vinschen.de (vpn50-38.rdu.redhat.com [172.16.50.38]) by potter.sfbay.redhat.com (8.11.6/8.11.6) with ESMTP id h8HGBXw11602 for ; Wed, 17 Sep 2003 09:11:34 -0700 Received: by cygbert.vinschen.de (Postfix, from userid 500) id 11DEA5830F; Wed, 17 Sep 2003 18:11:27 +0200 (CEST) Date: Wed, 17 Sep 2003 16:11:00 -0000 From: Corinna Vinschen To: gdb-patches@sources.redhat.com Subject: [RFA] sh-tdep.c: Follow up patch to implement two different ABIs Message-ID: <20030917161127.GM9981@cygbert.vinschen.de> Reply-To: gdb-patches@sources.redhat.com Mail-Followup-To: gdb-patches@sources.redhat.com Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.4.1i X-SW-Source: 2003-09/txt/msg00372.txt.bz2 Hi, the below patch is a follow up patch which relies on the pending sh frame stuff patch. This patch now adds the ability to switch between two different ABIs on the gdb command line, the "gcc" and the "renesas" ABI. The patch also implements the differences between these ABIs which changes the way how arguments are passed and values are returned. Additionally it fixes the argument passing of doubles in the gcc ABI on sh variations with FPU. The changes in detail: - Add the commands `set calling_convention ' and `show calling_convention' with being one of "gcc" and "renesas". Default to "gcc". - Differ between a fpu and a nofpu version of sh_use_struct_convention. The nofpu version behaves like the fpu version, except that it uses struct convention also for all types >= 8 bytes when the Renesas ABI is active. This especially affects long longs and doubles which are then passed on the stack. - The Renesas ABI passes the address in which structs have to be returned not in r2 as the gcc ABI, but instead this return address is pushed onto the stack after all arguments have been pushed. This affects sh_extract_struct_value_address() as well as the sh_push_dummy_call*() functions. - To simplify the sh_push_dummy_call*() functions, I created two helper functions which took the part of right-justifying values < 4 byte, called sh_justify_value_in_reg(), and to count the number of bytes to be allocated on stack for all arguments, called sh_stack_allocsize(). - Two new functions are now used to evaluate the next floating point register to use for an argument. The tricky part with floats is that gcc and Renesas ABI are different in two situations: - gcc passes floats in little-endian mode using regsters criss-crossing, fr5, fr4, fr7, fr6, fr9, fr8 ... In big-endian mode or in Renesas ABI, the order is simple fr4, fr5, fr6, ... - In both ABIs doubles are passed in even register pairs, fr4/fr5, fr6/fr7, ... The difference is when a register is skipped to pass the next double. Example: void foo(float a, double b, float c); In gcc ABI, a is passed in fr4, b then skips the odd-numbered fr5 register, so it's passed in fr6/fr7 and c is then passed in the next free register, fr8. In Renesas ABI, a is passed in fr4, b is passed as in gcc ABI in fr6/fr7 but c is passed in the lowest unused register, in this example in fr5. - In the Renesas ABI for CPUs with FPU, long longs are not passed in registers but on stack. - The struct_return code in both sh_push_dummy_call*() functions is moved to the end of the function since it must be done after the argument passing for the Rensas ABI. - I renamed the flag `odd_sized_struct' to a more descriptive name, `pass_on_stack' since the old name does in no way reflect how the decision about passing on stack or in register is actually made. The actual decision is as follows: - On FPU CPUs, pass in registers unless the datatype is bigger than 16 bytes. Renesas ABI additionally passes long longs on the stack as well. - On non-FPU CPUs, doubles and long doubles are passed always on the stack. - On all CPUs, everything else is passed in registers until the argument registers are filled up, the remaining arguments are passed on stack. If an argument doesn't fit entirely in the remaining registers, it's split between regs and stack as see fit. - The code and comment that some data is sometimes passed in registers *and* stack simultaneously is dropped. That's not how it works. Seems to be something sh5 specific but that's now in sh64-tdep.c. - Also in sh_push_dummy_call*(), the code which adds 4 for every 4 bytes managed, is changed from e.g. len -= register_size (gdbarch, argreg); to len -= 4; The reason is that the original line is not exactly correct. It adds the register_size of the *next* register, not the register actually filled with data. Since all data and registers in question are 4 byte regs (and the target specific code should know that anyway), I've simplified the affected code. Corinna ChangeLog: ========== * sh-tdep.c (sh_cc_gcc): New static string. (sh_cc_renesas): Ditto. (sh_cc_enum): New array pointing to calling convention strings. (sh_active_calling_convention): New variable pointing to current active calling convention. (sh_use_struct_convention_fpu): New function. (sh_use_struct_convention_nofpu): New function. (sh_use_struct_convention): Remove. Superseeded by the previous two functions. (sh_extract_struct_value_address): Care for Renesas ABI. (sh_justify_value_in_reg): New function. (sh_stack_allocsize): Ditto. (flt_argreg_array): New array used for floating point argument passing. (sh_init_flt_argreg): New function. (sh_next_flt_argreg): Ditto. (sh_push_dummy_call_fpu): Simplify. Rename "odd_sized_struct" to "pass_on_stack". Use new helper functions. Accomodate Renesas ABI. Fix argument passing strategy. (sh_push_dummy_call_nofpu): Ditto. (sh_gdbarch_init): Accomodate new sh_use_struct_convention_fpu and sh_use_struct_convention_nofpu functions. (_initialize_sh_tdep): Initialize new "set calling_convention", "show calling_convention" commands. --- sh-tdep.c.AFTERPATCH2 2003-09-15 17:14:33.000000000 +0200 +++ sh-tdep.c 2003-09-16 18:17:39.000000000 +0200 @@ -55,6 +55,17 @@ /* registers numbers shared with the simulator */ #include "gdb/sim-sh.h" +static const char sh_cc_gcc[] = "gcc"; +static const char sh_cc_renesas[] = "renesas"; +static const char *sh_cc_enum[] = +{ + sh_cc_gcc, + sh_cc_renesas, + NULL +}; + +static const char *sh_active_calling_convention = sh_cc_gcc; + static void (*sh_show_regs) (void); #define SH_NUM_REGS 59 @@ -569,10 +580,25 @@ sh_skip_prologue (CORE_ADDR start_pc) /* Should call_function allocate stack space for a struct return? */ static int -sh_use_struct_convention (int gcc_p, struct type *type) +sh_use_struct_convention_fpu (int gcc_p, struct type *type) +{ + int len = TYPE_LENGTH (type); + int nelem = TYPE_NFIELDS (type); + + return ((len != 1 && len != 2 && len != 4 && len != 8) || nelem != 1) && + (len != 8 || TYPE_LENGTH (TYPE_FIELD_TYPE (type, 0)) != 4); +} + +static int +sh_use_struct_convention_nofpu (int gcc_p, struct type *type) { int len = TYPE_LENGTH (type); int nelem = TYPE_NFIELDS (type); + + /* The Renesas ABI returns long longs/doubles etc. always on stack. */ + if (sh_active_calling_convention == sh_cc_renesas && len >= 8) + return 1; + return ((len != 1 && len != 2 && len != 4 && len != 8) || nelem != 1) && (len != 8 || TYPE_LENGTH (TYPE_FIELD_TYPE (type, 0)) != 4); } @@ -584,8 +610,13 @@ static CORE_ADDR sh_extract_struct_value_address (struct regcache *regcache) { ULONGEST addr; - - regcache_cooked_read_unsigned (regcache, STRUCT_RETURN_REGNUM, &addr); + if (sh_active_calling_convention != sh_cc_renesas) + regcache_cooked_read_unsigned (regcache, STRUCT_RETURN_REGNUM, &addr); + else + { + regcache_cooked_read_unsigned (regcache, SP_REGNUM, &addr); + addr = read_memory_unsigned_integer (addr, 4); + } return addr; } @@ -647,6 +678,102 @@ sh_frame_align (struct gdbarch *ignore, not displace any of the other arguments passed in via registers R4 to R7. */ +/* Helper function to justify value in register according to endianess. */ +static char * +sh_justify_value_in_reg (struct value *val, int len) +{ + static char valbuf[4]; + + memset (valbuf, 0, sizeof (valbuf)); + if (len < 4) + { + /* value gets right-justified in the register or stack word */ + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) + memcpy (valbuf + (4 - len), (char *) VALUE_CONTENTS (val), len); + else + memcpy (valbuf, (char *) VALUE_CONTENTS (val), len); + return valbuf; + } + return (char *) VALUE_CONTENTS (val); +} + +/* Helper function to eval number of bytes to allocate on stack. */ +static CORE_ADDR +sh_stack_allocsize (int nargs, struct value **args) +{ + int stack_alloc = 0; + while (nargs-- > 0) + stack_alloc += ((TYPE_LENGTH (VALUE_TYPE (args[nargs])) + 3) & ~3); + return stack_alloc; +} + +/* Helper functions for getting the float arguments right. Registers usage + depends on the ABI and the endianess. The comments should enlighten how + it's intended to work. */ + +/* This array stores which of the float arg registers are already in use. */ +static int flt_argreg_array[FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM + 1]; + +/* This function just resets the above array to "no reg used so far". */ +static void +sh_init_flt_argreg (void) +{ + memset (flt_argreg_array, 0, sizeof flt_argreg_array); +} + +/* This function returns the next register to use for float arg passing. + It returns either a valid value between FLOAT_ARG0_REGNUM and + FLOAT_ARGLAST_REGNUM if a register is available, otherwise it returns + FLOAT_ARGLAST_REGNUM + 1 to indicate that no register is available. + + Note that register number 0 in flt_argreg_array corresponds with the + real float register fr4. In contrast to FLOAT_ARG0_REGNUM (value is + 29) the parity of the register number is preserved, which is important + for the double register passing test (see the "argreg & 1" test below). */ +static int +sh_next_flt_argreg (int len) +{ + int argreg; + + /* First search for the next free register. */ + for (argreg = 0; argreg <= FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM; ++argreg) + if (!flt_argreg_array[argreg]) + break; + + /* No register left? */ + if (argreg > FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM) + return FLOAT_ARGLAST_REGNUM + 1; + + if (len == 8) + { + /* Doubles are always starting in a even register number. */ + if (argreg & 1) + { + /* In gcc ABI, the skipped register is lost for further argument + passing now. Not so in Renesas ABI. */ + if (sh_active_calling_convention != sh_cc_renesas) + flt_argreg_array[argreg] = 1; + + ++argreg; + + /* No register left? */ + if (argreg > FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM) + return FLOAT_ARGLAST_REGNUM + 1; + } + /* Also mark the next register as used. */ + flt_argreg_array[argreg + 1] = 1; + } + else if (TARGET_BYTE_ORDER == BFD_ENDIAN_LITTLE + && sh_active_calling_convention != sh_cc_renesas) + { + /* In little endian, gcc passes floats like this: f5, f4, f7, f6, ... */ + if (!flt_argreg_array[argreg + 1]) + ++argreg; + } + flt_argreg_array[argreg] = 1; + return FLOAT_ARG0_REGNUM + argreg; +} + static CORE_ADDR sh_push_dummy_call_fpu (struct gdbarch *gdbarch, CORE_ADDR func_addr, @@ -656,77 +783,61 @@ sh_push_dummy_call_fpu (struct gdbarch * CORE_ADDR sp, int struct_return, CORE_ADDR struct_addr) { - int stack_offset, stack_alloc; - int argreg, flt_argreg; + int stack_offset = 0; + int argreg = ARG0_REGNUM; + int flt_argreg; int argnum; struct type *type; CORE_ADDR regval; char *val; - char valbuf[4]; int len; - int odd_sized_struct; + int pass_on_stack; /* first force sp to a 4-byte alignment */ sp = sh_frame_align (gdbarch, sp); - if (struct_return) - regcache_cooked_write_unsigned (regcache, - STRUCT_RETURN_REGNUM, - struct_addr); - - /* Now make sure there's space on the stack */ - for (argnum = 0, stack_alloc = 0; argnum < nargs; argnum++) - stack_alloc += ((TYPE_LENGTH (VALUE_TYPE (args[argnum])) + 3) & ~3); - sp -= stack_alloc; /* make room on stack for args */ + /* make room on stack for args */ + sp -= sh_stack_allocsize (nargs, args); + + /* Initialize float argument mechanism. */ + sh_init_flt_argreg (); /* 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. */ - - argreg = ARG0_REGNUM; - flt_argreg = FLOAT_ARG0_REGNUM; - for (argnum = 0, stack_offset = 0; argnum < nargs; argnum++) + for (argnum = 0; argnum < nargs; argnum++) { type = VALUE_TYPE (args[argnum]); len = TYPE_LENGTH (type); - memset (valbuf, 0, sizeof (valbuf)); - if (len < 4) - { - /* value gets right-justified in the register or stack word */ - if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) - memcpy (valbuf + (4 - len), - (char *) VALUE_CONTENTS (args[argnum]), len); - else - memcpy (valbuf, (char *) VALUE_CONTENTS (args[argnum]), len); - val = valbuf; - } - else - val = (char *) VALUE_CONTENTS (args[argnum]); + val = sh_justify_value_in_reg (args[argnum], len); + + /* 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. */ + else if (sh_active_calling_convention == sh_cc_renesas + && TYPE_CODE (type) == TYPE_CODE_INT && len == 8) + pass_on_stack = 1; /* So are long longs in renesas ABI */ + + /* Find out the next register to use for a floating point value. */ + if (TYPE_CODE (type) == TYPE_CODE_FLT) + flt_argreg = sh_next_flt_argreg (len); - if (len > 4 && (len & 3) != 0) - odd_sized_struct = 1; /* Such structs go entirely on stack. */ - else if (len > 16) - odd_sized_struct = 1; /* So do aggregates bigger than 4 words. */ - else - odd_sized_struct = 0; while (len > 0) { if ((TYPE_CODE (type) == TYPE_CODE_FLT && flt_argreg > FLOAT_ARGLAST_REGNUM) || argreg > ARGLAST_REGNUM - || odd_sized_struct) + || pass_on_stack) { - /* must go on the stack */ write_memory (sp + stack_offset, val, 4); stack_offset += 4; } - /* NOTE WELL!!!!! This is not an "else if" clause!!! - That's because some *&^%$ things get passed on the stack - AND in the registers! */ - if (TYPE_CODE (type) == TYPE_CODE_FLT && - flt_argreg > 0 && flt_argreg <= FLOAT_ARGLAST_REGNUM) + else if (TYPE_CODE (type) == TYPE_CODE_FLT + && flt_argreg <= FLOAT_ARGLAST_REGNUM) { - /* Argument goes in a single-precision fp reg. */ + /* Argument goes in a float argument register. */ regval = extract_unsigned_integer (val, register_size (gdbarch, argreg)); regcache_cooked_write_unsigned (regcache, flt_argreg++, regval); @@ -741,11 +852,25 @@ sh_push_dummy_call_fpu (struct gdbarch * /* Store the value 4 bytes at a time. This means that things larger than 4 bytes may go partly in registers and partly on the stack. */ - len -= register_size (gdbarch, argreg); - val += register_size (gdbarch, argreg); + len -= 4; + val += 4; } } + if (struct_return) + { + if (sh_active_calling_convention != sh_cc_renesas) + /* Using the gcc ABI, the "struct return pointer" pseudo-argument has + its own dedicated register */ + regcache_cooked_write_unsigned (regcache, + STRUCT_RETURN_REGNUM, + struct_addr); + else + /* If the function uses the renesas ABI, subtract another 4 bytes from + the stack and store the struct return address there. */ + write_memory_unsigned_integer (sp -= 4, 4, struct_addr); + } + /* Store return address. */ regcache_cooked_write_unsigned (regcache, PR_REGNUM, bp_addr); @@ -764,69 +889,45 @@ sh_push_dummy_call_nofpu (struct gdbarch CORE_ADDR sp, int struct_return, CORE_ADDR struct_addr) { - int stack_offset, stack_alloc; - int argreg; + int stack_offset = 0; + int argreg = ARG0_REGNUM; int argnum; struct type *type; CORE_ADDR regval; char *val; - char valbuf[4]; int len; - int odd_sized_struct; + int pass_on_stack; /* first force sp to a 4-byte alignment */ sp = sh_frame_align (gdbarch, sp); - if (struct_return) - regcache_cooked_write_unsigned (regcache, - STRUCT_RETURN_REGNUM, - struct_addr); - - /* Now make sure there's space on the stack */ - for (argnum = 0, stack_alloc = 0; argnum < nargs; argnum++) - stack_alloc += ((TYPE_LENGTH (VALUE_TYPE (args[argnum])) + 3) & ~3); - sp -= stack_alloc; /* make room on stack for args */ + /* make room on stack for args */ + sp -= sh_stack_allocsize (nargs, args); /* 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. */ - - argreg = ARG0_REGNUM; - for (argnum = 0, stack_offset = 0; argnum < nargs; argnum++) + for (argnum = 0; argnum < nargs; argnum++) { type = VALUE_TYPE (args[argnum]); len = TYPE_LENGTH (type); - memset (valbuf, 0, sizeof (valbuf)); - if (len < 4) - { - /* value gets right-justified in the register or stack word */ - if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) - memcpy (valbuf + (4 - len), - (char *) VALUE_CONTENTS (args[argnum]), len); - else - memcpy (valbuf, (char *) VALUE_CONTENTS (args[argnum]), len); - val = valbuf; - } - else - val = (char *) VALUE_CONTENTS (args[argnum]); + val = sh_justify_value_in_reg (args[argnum], len); + + /* Some decisions have to be made how various types are handled. + This also differs in different ABIs. */ + pass_on_stack = 0; + if (TYPE_CODE (type) == TYPE_CODE_FLT && len > 4 + && sh_active_calling_convention == sh_cc_renesas) + pass_on_stack = 1; /* Renesas ABI pushes doubles entirely on stack. */ - if (len > 4 && (len & 3) != 0) - odd_sized_struct = 1; /* such structs go entirely on stack */ - else - odd_sized_struct = 0; while (len > 0) { - if (argreg > ARGLAST_REGNUM - || odd_sized_struct) + if (argreg > ARGLAST_REGNUM || pass_on_stack) { - /* must go on the stack */ write_memory (sp + stack_offset, val, 4); stack_offset += 4; } - /* NOTE WELL!!!!! This is not an "else if" clause!!! - That's because some *&^%$ things get passed on the stack - AND in the registers! */ - if (argreg <= ARGLAST_REGNUM) + else if (argreg <= ARGLAST_REGNUM) { /* there's room in a register */ regval = extract_unsigned_integer (val, register_size (gdbarch, @@ -836,11 +937,25 @@ sh_push_dummy_call_nofpu (struct gdbarch /* Store the value 4 bytes at a time. This means that things larger than 4 bytes may go partly in registers and partly on the stack. */ - len -= register_size (gdbarch, argreg); - val += register_size (gdbarch, argreg); + len -= 4; + val += 4; } } + if (struct_return) + { + if (sh_active_calling_convention != sh_cc_renesas) + /* Using the gcc ABI, the "struct return pointer" pseudo-argument has + its own dedicated register */ + regcache_cooked_write_unsigned (regcache, + STRUCT_RETURN_REGNUM, + struct_addr); + else + /* If the function uses the renesas ABI, subtract another 4 bytes from + the stack and store the struct return address there. */ + write_memory_unsigned_integer (sp -= 4, 4, struct_addr); + } + /* Store return address. */ regcache_cooked_write_unsigned (regcache, PR_REGNUM, bp_addr); @@ -2060,7 +2175,6 @@ sh_gdbarch_init (struct gdbarch_info inf set_gdbarch_print_registers_info (gdbarch, sh_print_registers_info); set_gdbarch_breakpoint_from_pc (gdbarch, sh_breakpoint_from_pc); - set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention); set_gdbarch_print_insn (gdbarch, gdb_print_insn_sh); set_gdbarch_register_sim_regno (gdbarch, legacy_register_sim_regno); @@ -2079,6 +2193,7 @@ sh_gdbarch_init (struct gdbarch_info inf set_gdbarch_push_dummy_code (gdbarch, sh_push_dummy_code); set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_nofpu); + set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_nofpu); set_gdbarch_frame_args_skip (gdbarch, 0); set_gdbarch_frameless_function_invocation (gdbarch, @@ -2114,6 +2229,7 @@ sh_gdbarch_init (struct gdbarch_info inf set_gdbarch_store_return_value (gdbarch, sh3e_sh4_store_return_value); set_gdbarch_extract_return_value (gdbarch, sh3e_sh4_extract_return_value); set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu); + set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_fpu); break; case bfd_mach_sh_dsp: @@ -2135,6 +2251,7 @@ sh_gdbarch_init (struct gdbarch_info inf set_gdbarch_store_return_value (gdbarch, sh3e_sh4_store_return_value); set_gdbarch_extract_return_value (gdbarch, sh3e_sh4_extract_return_value); set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu); + set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_fpu); break; case bfd_mach_sh3_dsp: @@ -2152,6 +2269,7 @@ sh_gdbarch_init (struct gdbarch_info inf set_gdbarch_store_return_value (gdbarch, sh3e_sh4_store_return_value); set_gdbarch_extract_return_value (gdbarch, sh3e_sh4_extract_return_value); set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu); + set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_fpu); break; default: @@ -2178,4 +2296,11 @@ _initialize_sh_tdep (void) gdbarch_register (bfd_arch_sh, sh_gdbarch_init, NULL); add_com ("regs", class_vars, sh_show_regs_command, "Print all registers"); + + add_show_from_set ( + add_set_enum_cmd ("calling_convention", class_vars, sh_cc_enum, + &sh_active_calling_convention, + "Set calling convention used when calling target " + "functions from GDB.", + &setlist), &showlist); } -- Corinna Vinschen Cygwin Developer Red Hat, Inc.