On 10/22/2014 08:12 PM, Jeff Law wrote:
Yea, let's keep your approach. Just wanted to explore a bit since the PA seems to have a variety of similar characteristics.
Here's an updated version of the patch. I experimented a little with ptx calling conventions and ran into an arg that had to be moved with memcpy, which exposed an ordering problem - all call_args were added to the memcpy call. So the invocation of the hook had to be moved downwards a bit, and the calculation of the return value needs to happen after it (since nvptx_function_value needs to know whether we are actually trying to construct a call at the moment).
Bootstrapped and tested on x86_64-linux, ok? Bernd
gcc/ * target.def (call_args, end_call_args): New hooks. * hooks.c (hook_void_rtx_tree): New empty function. * hooks.h (hook_void_rtx_tree): Declare. * doc/tm.texi.in (TARGET_CALL_ARGS, TARGET_END_CALL_ARGS): Add. * doc/tm.texi: Regenerate. * calls.c (expand_call): Slightly rearrange the code. Use the two new hooks. (expand_library_call_value_1): Use the two new hooks. ------------------------------------------------------------------------ Index: gcc/doc/tm.texi =================================================================== --- gcc/doc/tm.texi.orig +++ gcc/doc/tm.texi @@ -4960,6 +4960,29 @@ except the last are treated as named. You need not define this hook if it always returns @code{false}. @end deftypefn +@deftypefn {Target Hook} void TARGET_CALL_ARGS (rtx, @var{tree}) +While generating RTL for a function call, this target hook is invoked once +for each argument passed to the function, either a register returned by +@code{TARGET_FUNCTION_ARG} or a memory location. It is called just +before the point where argument registers are stored. The type of the +function to be called is also passed as the second argument; it is +@code{NULL_TREE} for libcalls. The @code{TARGET_END_CALL_ARGS} hook is +invoked just after the code to copy the return reg has been emitted. +This functionality can be used to perform special setup of call argument +registers if a target needs it. +For functions without arguments, the hook is called once with @code{pc_rtx} +passed instead of an argument register. +Most ports do not need to implement anything for this hook. +@end deftypefn + +@deftypefn {Target Hook} void TARGET_END_CALL_ARGS (void) +This target hook is invoked while generating RTL for a function call, +just after the point where the return reg is copied into a pseudo. It +signals that all the call argument and return registers for the just +emitted call are now no longer in use. +Most ports do not need to implement anything for this hook. +@end deftypefn + @deftypefn {Target Hook} bool TARGET_PRETEND_OUTGOING_VARARGS_NAMED (cumulative_args_t @var{ca}) If you need to conditionally change ABIs so that one works with @code{TARGET_SETUP_INCOMING_VARARGS}, but the other works like neither Index: gcc/doc/tm.texi.in =================================================================== --- gcc/doc/tm.texi.in.orig +++ gcc/doc/tm.texi.in @@ -3856,6 +3856,10 @@ These machine description macros help im @hook TARGET_STRICT_ARGUMENT_NAMING +@hook TARGET_CALL_ARGS + +@hook TARGET_END_CALL_ARGS + @hook TARGET_PRETEND_OUTGOING_VARARGS_NAMED @node Trampolines Index: gcc/hooks.c =================================================================== --- gcc/hooks.c.orig +++ gcc/hooks.c @@ -245,6 +245,11 @@ hook_void_tree (tree a ATTRIBUTE_UNUSED) } void +hook_void_rtx_tree (rtx, tree) +{ +} + +void hook_void_constcharptr (const char *a ATTRIBUTE_UNUSED) { } Index: gcc/hooks.h =================================================================== --- gcc/hooks.h.orig +++ gcc/hooks.h @@ -71,6 +71,7 @@ extern void hook_void_constcharptr (cons extern void hook_void_rtx_insn_int (rtx_insn *, int); extern void hook_void_FILEptr_constcharptr (FILE *, const char *); extern bool hook_bool_FILEptr_rtx_false (FILE *, rtx); +extern void hook_void_rtx_tree (rtx, tree); extern void hook_void_tree (tree); extern void hook_void_tree_treeptr (tree, tree *); extern void hook_void_int_int (int, int); Index: gcc/target.def =================================================================== --- gcc/target.def.orig +++ gcc/target.def @@ -3816,6 +3816,33 @@ not generate any instructions in this ca default_setup_incoming_varargs) DEFHOOK +(call_args, + "While generating RTL for a function call, this target hook is invoked once\n\ +for each argument passed to the function, either a register returned by\n\ +@code{TARGET_FUNCTION_ARG} or a memory location. It is called just\n\ +before the point where argument registers are stored. The type of the\n\ +function to be called is also passed as the second argument; it is\n\ +@code{NULL_TREE} for libcalls. The @code{TARGET_END_CALL_ARGS} hook is\n\ +invoked just after the code to copy the return reg has been emitted.\n\ +This functionality can be used to perform special setup of call argument\n\ +registers if a target needs it.\n\ +For functions without arguments, the hook is called once with @code{pc_rtx}\n\ +passed instead of an argument register.\n\ +Most ports do not need to implement anything for this hook.", + void, (rtx, tree), + hook_void_rtx_tree) + +DEFHOOK +(end_call_args, + "This target hook is invoked while generating RTL for a function call,\n\ +just after the point where the return reg is copied into a pseudo. It\n\ +signals that all the call argument and return registers for the just\n\ +emitted call are now no longer in use.\n\ +Most ports do not need to implement anything for this hook.", + void, (void), + hook_void_void) + +DEFHOOK (strict_argument_naming, "Define this hook to return @code{true} if the location where a function\n\ argument is passed depends on whether or not it is a named argument.\n\ Index: gcc/calls.c =================================================================== --- gcc/calls.c.orig +++ gcc/calls.c @@ -2978,32 +2978,6 @@ expand_call (tree exp, rtx target, int i funexp = rtx_for_function_call (fndecl, addr); - /* Figure out the register where the value, if any, will come back. */ - valreg = 0; - if (TYPE_MODE (rettype) != VOIDmode - && ! structure_value_addr) - { - if (pcc_struct_value) - valreg = hard_function_value (build_pointer_type (rettype), - fndecl, NULL, (pass == 0)); - else - valreg = hard_function_value (rettype, fndecl, fntype, - (pass == 0)); - - /* If VALREG is a PARALLEL whose first member has a zero - offset, use that. This is for targets such as m68k that - return the same value in multiple places. */ - if (GET_CODE (valreg) == PARALLEL) - { - rtx elem = XVECEXP (valreg, 0, 0); - rtx where = XEXP (elem, 0); - rtx offset = XEXP (elem, 1); - if (offset == const0_rtx - && GET_MODE (where) == GET_MODE (valreg)) - valreg = where; - } - } - /* Precompute all register parameters. It isn't safe to compute anything once we have started filling any specific hard regs. */ precompute_register_parameters (num_actuals, args, ®_parm_seen); @@ -3082,6 +3056,42 @@ expand_call (tree exp, rtx target, int i sibcall_failure = 1; } + bool any_regs = false; + for (i = 0; i < num_actuals; i++) + if (args[i].reg != NULL_RTX) + { + any_regs = true; + targetm.calls.call_args (args[i].reg, funtype); + } + if (!any_regs) + targetm.calls.call_args (pc_rtx, funtype); + + /* Figure out the register where the value, if any, will come back. */ + valreg = 0; + if (TYPE_MODE (rettype) != VOIDmode + && ! structure_value_addr) + { + if (pcc_struct_value) + valreg = hard_function_value (build_pointer_type (rettype), + fndecl, NULL, (pass == 0)); + else + valreg = hard_function_value (rettype, fndecl, fntype, + (pass == 0)); + + /* If VALREG is a PARALLEL whose first member has a zero + offset, use that. This is for targets such as m68k that + return the same value in multiple places. */ + if (GET_CODE (valreg) == PARALLEL) + { + rtx elem = XVECEXP (valreg, 0, 0); + rtx where = XEXP (elem, 0); + rtx offset = XEXP (elem, 1); + if (offset == const0_rtx + && GET_MODE (where) == GET_MODE (valreg)) + valreg = where; + } + } + /* If register arguments require space on the stack and stack space was not preallocated, allocate stack space here for arguments passed in registers. */ @@ -3430,6 +3440,8 @@ expand_call (tree exp, rtx target, int i for (i = 0; i < num_actuals; ++i) free (args[i].aligned_regs); + targetm.calls.end_call_args (); + insns = get_insns (); end_sequence (); @@ -3956,6 +3968,18 @@ emit_library_call_value_1 (int retval, r } #endif + /* When expanding a normal call, args are stored in push order, + which is the reverse of what we have here. */ + bool any_regs = false; + for (int i = nargs; i-- > 0; ) + if (argvec[i].reg != NULL_RTX) + { + targetm.calls.call_args (argvec[i].reg, NULL_TREE); + any_regs = true; + } + if (!any_regs) + targetm.calls.call_args (pc_rtx, NULL_TREE); + /* Push the args that need to be pushed. */ /* ARGNUM indexes the ARGVEC array in the order in which the arguments @@ -4196,6 +4220,8 @@ emit_library_call_value_1 (int retval, r valreg = gen_rtx_REG (TYPE_MODE (tfom), REGNO (valreg)); } + targetm.calls.end_call_args (); + /* For calls to `setjmp', etc., inform function.c:setjmp_warnings that it should complain if nonvolatile values are live. For functions that cannot return, inform flow that control does not