diff mbox

The nvptx port [7/11+] Inform the port about call arguments

Message ID 54451C57.5020705@codesourcery.com
State New
Headers show

Commit Message

Bernd Schmidt Oct. 20, 2014, 2:29 p.m. UTC
In ptx assembly we need to decorate call insns with the arguments that 
are being passed. We also need to know the exact function type. This is 
kind of hard to do with the existing infrastructure since things like 
function_arg are called at other times rather than just when emitting a 
call, so this patch adds two more hooks, one called just before argument 
registers are loaded (once for each arg), and the other just after the 
call is complete.


Bernd

Comments

Jeff Law Oct. 21, 2014, 9:11 p.m. UTC | #1
On 10/20/14 14:29, Bernd Schmidt wrote:
> In ptx assembly we need to decorate call insns with the arguments that
> are being passed. We also need to know the exact function type. This is
> kind of hard to do with the existing infrastructure since things like
> function_arg are called at other times rather than just when emitting a
> call, so this patch adds two more hooks, one called just before argument
> registers are loaded (once for each arg), and the other just after the
> call is complete.
>
>
> Bernd
>
>
> 007-callargs.diff
>
>
> 	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.
How exactly do you need to decorate?  Just mention the register, size 
information or do you need full type information?

We've had targets where we had to indicate register banks for each 
argument.  Those would walk CALL_INSN_FUNCTION_USAGE to find the 
argument registers, then from the register # we would know which 
register bank to use.   Would that work for you?

Jeff
Bernd Schmidt Oct. 21, 2014, 9:29 p.m. UTC | #2
On 10/21/2014 11:11 PM, Jeff Law wrote:
> On 10/20/14 14:29, Bernd Schmidt wrote:
>> In ptx assembly we need to decorate call insns with the arguments that
>> are being passed. We also need to know the exact function type. This is
>> kind of hard to do with the existing infrastructure since things like
>> function_arg are called at other times rather than just when emitting a
>> call, so this patch adds two more hooks, one called just before argument
>> registers are loaded (once for each arg), and the other just after the
>> call is complete.
>>
> How exactly do you need to decorate?  Just mention the register, size
> information or do you need full type information?

A normal call looks like

{
   .param.u32 %retval_in;
   .param.u64 %out_arg0;
   st.param.u64 [%out_arg0], %r1400;
   call (%retval_in), PopCnt, (%out_arg0);
   ld.param.u32    %r1403, [%retval_in];
}

which declares local variables for the args and return value, stores the 
pseudos in the outgoing args, calls the function with explicitly named 
args and return values, and loads the incoming return value. All this is 
produced by nvptx_output_call_insn for a single CALL rtx insn.

Indirect calls additionally need to produce a .callprototype pseudo-op 
which looks like a function declaration; for normal calls the called 
function must already be declared elsewhere. The machinery to produce 
such .callprototypes is also used to produce a ptx decl from the call 
insn for an external K&R declaration with no argument types.

> We've had targets where we had to indicate register banks for each
> argument.  Those would walk CALL_INSN_FUNCTION_USAGE to find the
> argument registers, then from the register # we would know which
> register bank to use.   Would that work for you?

Couple of problems with this - the fusage isn't available to gen_call, 
it gets added to the call insn after it is emitted, but the backend 
would like to have this information when emitting the insn. Also, I'd 
need the order to be reliable and I don't think CALL_INSN_FUNCTION_USAGE 
is really designed to guarantee that (I suspect the order of register 
args and things like the struct return reg is wrong). I also need the 
exact function type and the call_args hook seems like the easiest way to 
communicate it to the backend.


Bernd
Jeff Law Oct. 21, 2014, 9:53 p.m. UTC | #3
On 10/21/14 21:29, Bernd Schmidt wrote:
>
> A normal call looks like
>
> {
>    .param.u32 %retval_in;
>    .param.u64 %out_arg0;
>    st.param.u64 [%out_arg0], %r1400;
>    call (%retval_in), PopCnt, (%out_arg0);
>    ld.param.u32    %r1403, [%retval_in];
> }
>
> which declares local variables for the args and return value, stores the
> pseudos in the outgoing args, calls the function with explicitly named
> args and return values, and loads the incoming return value. All this is
> produced by nvptx_output_call_insn for a single CALL rtx insn.
So far, so good.

>
> Indirect calls additionally need to produce a .callprototype pseudo-op
> which looks like a function declaration; for normal calls the called
> function must already be declared elsewhere. The machinery to produce
> such .callprototypes is also used to produce a ptx decl from the call
> insn for an external K&R declaration with no argument types.
Yea, no surprise here.


> Couple of problems with this - the fusage isn't available to gen_call,
> it gets added to the call insn after it is emitted, but the backend
> would like to have this information when emitting the insn.
Right.  Targets which have needed this emit the decorations at 
insn-output time so the fusage has been attached.


> Also, I'd
> need the order to be reliable and I don't think CALL_INSN_FUNCTION_USAGE
> is really designed to guarantee that (I suspect the order of register
> args and things like the struct return reg is wrong). I also need the
> exact function type and the call_args hook seems like the easiest way to
> communicate it to the backend.
We've depended on the ordering in the PA, well, forever.  However, I 
doubt ordering of regs in the fusage is documented at all!  We could 
change that.

So, in the end I'm torn.  I don't like adding new hooks when they're not 
needed, but I have some reservations about relying on the order of stuff 
in CALL_INSN_FUNCTION_USAGE and I worry a bit that you might end up with 
stuff other than arguments on that list -- the PA port could filter on 
the hard registers used for passing arguments, so other stuff appearing 
isn't a big deal.

Let me sleep on this one :-)
Jeff
Bernd Schmidt Oct. 21, 2014, 10:06 p.m. UTC | #4
On 10/21/2014 11:53 PM, Jeff Law wrote:

> So, in the end I'm torn.  I don't like adding new hooks when they're not
> needed, but I have some reservations about relying on the order of stuff
> in CALL_INSN_FUNCTION_USAGE and I worry a bit that you might end up with
> stuff other than arguments on that list -- the PA port could filter on
> the hard registers used for passing arguments, so other stuff appearing
> isn't a big deal.

This is another worry. Also, at the moment we don't actually add the 
pseudos to CALL_INSN_FUNCTION_USAGE (that's patch 6/11), we use the regs 
saved by the call_args hook to make proper USEs in a PARALLEL. I'm not 
convinced the rest of the compiler would be too happy to see pseudos there.

So, in all I'd say it's probably possible to do it that way, but it 
feels a lot iffier than I'd be happy with. I for one didn't know about 
the PA requirement, so I could easily have broken it unknowingly if I'd 
made some random change modifying call expansion.


Bernd
Jeff Law Oct. 22, 2014, 6:12 p.m. UTC | #5
On 10/21/14 16:06, Bernd Schmidt wrote:
> On 10/21/2014 11:53 PM, Jeff Law wrote:
>
>> So, in the end I'm torn.  I don't like adding new hooks when they're not
>> needed, but I have some reservations about relying on the order of stuff
>> in CALL_INSN_FUNCTION_USAGE and I worry a bit that you might end up with
>> stuff other than arguments on that list -- the PA port could filter on
>> the hard registers used for passing arguments, so other stuff appearing
>> isn't a big deal.
>
> This is another worry. Also, at the moment we don't actually add the
> pseudos to CALL_INSN_FUNCTION_USAGE (that's patch 6/11), we use the regs
> saved by the call_args hook to make proper USEs in a PARALLEL. I'm not
> convinced the rest of the compiler would be too happy to see pseudos there.
>
> So, in all I'd say it's probably possible to do it that way, but it
> feels a lot iffier than I'd be happy with. I for one didn't know about
> the PA requirement, so I could easily have broken it unknowingly if I'd
> made some random change modifying call expansion.
Yea, let's keep your approach.  Just wanted to explore a bit since the 
PA seems to have a variety of similar characteristics.

jeff
diff mbox

Patch

	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
@@ -5027,6 +5027,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
@@ -3929,6 +3929,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
@@ -70,6 +70,7 @@  extern void hook_void_constcharptr (cons
 extern void hook_void_rtx_int (rtx, 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
@@ -3825,6 +3825,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
@@ -3011,6 +3011,33 @@  expand_call (tree exp, rtx target, int i
 
       funexp = rtx_for_function_call (fndecl, addr);
 
+      /* 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, &reg_parm_seen);
+
+      if (CALL_EXPR_STATIC_CHAIN (exp))
+	static_chain_value = expand_normal (CALL_EXPR_STATIC_CHAIN (exp));
+      else
+	static_chain_value = 0;
+
+#ifdef REG_PARM_STACK_SPACE
+      /* Save the fixed argument area if it's part of the caller's frame and
+	 is clobbered by argument setup for this call.  */
+      if (ACCUMULATE_OUTGOING_ARGS && pass)
+	save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
+					      &low_to_save, &high_to_save);
+#endif
+
+      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
@@ -3037,23 +3064,6 @@  expand_call (tree exp, rtx target, int i
 	    }
 	}
 
-      /* 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, &reg_parm_seen);
-
-      if (CALL_EXPR_STATIC_CHAIN (exp))
-	static_chain_value = expand_normal (CALL_EXPR_STATIC_CHAIN (exp));
-      else
-	static_chain_value = 0;
-
-#ifdef REG_PARM_STACK_SPACE
-      /* Save the fixed argument area if it's part of the caller's frame and
-	 is clobbered by argument setup for this call.  */
-      if (ACCUMULATE_OUTGOING_ARGS && pass)
-	save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
-					      &low_to_save, &high_to_save);
-#endif
-
       /* Now store (and compute if necessary) all non-register parms.
 	 These come before register parms, since they can require block-moves,
 	 which could clobber the registers used for register parms.
@@ -3458,6 +3468,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 ();
 
@@ -3985,6 +3997,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
@@ -4224,6 +4248,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