Re: [PATCH] Make sibcall argument overlap check less pessimistic (PR middle-end/50074, take 2)

2011-12-04 Thread Eric Botcazou
> I think it is.  Those called during internal_arg_pointer_based_exp_scan
> will see scan_start equal to pc_rtx and won't scan, and for the calls after
> it, while scan_start won't be pc_rtx, as it is after scan, it is either
> NULL_RTX with no insns in the sequence, or some insn whose NEXT_INSN is
> NULL, therefore it will attempt to scan, but won't scan a single insn.

Right, internal_arg_pointer_based_exp_scan will be invoked for nothing.

> But surely, if you prefer the explicit argument, I can test that version
> too.

Yes, I think it is better in the end. :-)

-- 
Eric Botcazou


Re: [PATCH] Make sibcall argument overlap check less pessimistic (PR middle-end/50074, take 2)

2011-12-04 Thread Jakub Jelinek
On Sun, Dec 04, 2011 at 09:53:42PM +0100, Eric Botcazou wrote:
> > What about this way?  I've groupped the two variables into a structure
> > to make it clear it is internal internal_arg_pointer_based_exp* state,
> > scanning is done in a separate function and the SCAN argument is gone,
> > instead the internal_arg_pointer_based_exp_scan function disables scanning
> > during recursion by tweaking the internal state.
> 
> Thanks.  I think this isn't exactly equivalent to the previous version 
> though, 
> as the recursive call made through internal_arg_pointer_based_exp_1 will now 
> scan as well, won't it?  OK, my fault, the argument was probably better then.

I think it is.  Those called during internal_arg_pointer_based_exp_scan
will see scan_start equal to pc_rtx and won't scan, and for the calls after
it, while scan_start won't be pc_rtx, as it is after scan, it is either
NULL_RTX with no insns in the sequence, or some insn whose NEXT_INSN is
NULL, therefore it will attempt to scan, but won't scan a single insn.
But surely, if you prefer the explicit argument, I can test that version
too.

Jakub


Re: [PATCH] Make sibcall argument overlap check less pessimistic (PR middle-end/50074, take 2)

2011-12-04 Thread Eric Botcazou
> What about this way?  I've groupped the two variables into a structure
> to make it clear it is internal internal_arg_pointer_based_exp* state,
> scanning is done in a separate function and the SCAN argument is gone,
> instead the internal_arg_pointer_based_exp_scan function disables scanning
> during recursion by tweaking the internal state.

Thanks.  I think this isn't exactly equivalent to the previous version though, 
as the recursive call made through internal_arg_pointer_based_exp_1 will now 
scan as well, won't it?  OK, my fault, the argument was probably better then.
But I think that we can keep the structure and the 2 functions for clarity.

Adjusted patch attached, same ChangeLog.  Please double-check, make the changes 
you deem necessary and install.

-- 
Eric Botcazou
Index: calls.c
===
--- calls.c	(revision 181902)
+++ calls.c	(working copy)
@@ -1658,6 +1658,129 @@ rtx_for_function_call (tree fndecl, tree
   return funexp;
 }
 
+/* Internal state for internal_arg_pointer_based_exp and its helpers.  */
+static struct
+{
+  /* Last insn that has been scanned by internal_arg_pointer_based_exp_scan,
+ or NULL_RTX if none has been scanned yet.  */
+  rtx scan_start;
+  /* Vector indexed by REGNO - FIRST_PSEUDO_REGISTER, recording if a pseudo is
+ based on crtl->args.internal_arg_pointer.  The element is NULL_RTX if the
+ pseudo isn't based on it, a CONST_INT offset if the pseudo is based on it
+ with fixed offset, or PC if this is with variable or unknown offset.  */
+  VEC(rtx, heap) *cache;
+} internal_arg_pointer_exp_state;
+
+static rtx internal_arg_pointer_based_exp (rtx, bool);
+
+/* Helper function for internal_arg_pointer_based_exp.  Scan insns in
+   the tail call sequence, starting with first insn that hasn't been
+   scanned yet, and note for each pseudo on the LHS whether it is based
+   on crtl->args.internal_arg_pointer or not, and what offset from that
+   that pointer it has.  */
+
+static void
+internal_arg_pointer_based_exp_scan (void)
+{
+  rtx insn, scan_start = internal_arg_pointer_exp_state.scan_start;
+
+  if (scan_start == NULL_RTX)
+insn = get_insns ();
+  else
+insn = NEXT_INSN (scan_start);
+
+  while (insn)
+{
+  rtx set = single_set (insn);
+  if (set && REG_P (SET_DEST (set)) && !HARD_REGISTER_P (SET_DEST (set)))
+	{
+	  rtx val = NULL_RTX;
+	  unsigned int idx = REGNO (SET_DEST (set)) - FIRST_PSEUDO_REGISTER;
+	  /* Punt on pseudos set multiple times.  */
+	  if (idx < VEC_length (rtx, internal_arg_pointer_exp_state.cache)
+	  && (VEC_index (rtx, internal_arg_pointer_exp_state.cache, idx)
+		  != NULL_RTX))
+	val = pc_rtx;
+	  else
+	val = internal_arg_pointer_based_exp (SET_SRC (set), false);
+	  if (val != NULL_RTX)
+	{
+	  VEC_safe_grow_cleared (rtx, heap,
+ internal_arg_pointer_exp_state.cache,
+ idx + 1);
+	  VEC_replace (rtx, internal_arg_pointer_exp_state.cache,
+			   idx, val);
+	}
+	}
+  if (NEXT_INSN (insn) == NULL_RTX)
+	scan_start = insn;
+  insn = NEXT_INSN (insn);
+}
+
+  internal_arg_pointer_exp_state.scan_start = scan_start;
+}
+
+/* Helper function for internal_arg_pointer_based_exp, called through
+   for_each_rtx.  Return 1 if *LOC is a register based on
+   crtl->args.internal_arg_pointer.  Return -1 if *LOC is not based on it
+   and the subexpressions need not be examined.  Otherwise return 0.  */
+
+static int
+internal_arg_pointer_based_exp_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+{
+  if (REG_P (*loc) && internal_arg_pointer_based_exp (*loc, false) != NULL_RTX)
+return 1;
+  if (MEM_P (*loc))
+return -1;
+  return 0;
+}
+
+/* Compute whether RTL is based on crtl->args.internal_arg_pointer.  Return
+   NULL_RTX if RTL isn't based on it, a CONST_INT offset if RTL is based on
+   it with fixed offset, or PC if this is with variable or unknown offset.
+   TOPLEVEL is true if the function is invoked at the topmost level.  */
+
+static rtx
+internal_arg_pointer_based_exp (rtx rtl, bool toplevel)
+{
+  if (CONSTANT_P (rtl))
+return NULL_RTX;
+
+  if (rtl == crtl->args.internal_arg_pointer)
+return const0_rtx;
+
+  if (REG_P (rtl) && HARD_REGISTER_P (rtl))
+return NULL_RTX;
+
+  if (GET_CODE (rtl) == PLUS && CONST_INT_P (XEXP (rtl, 1)))
+{
+  rtx val = internal_arg_pointer_based_exp (XEXP (rtl, 0), toplevel);
+  if (val == NULL_RTX || val == pc_rtx)
+	return val;
+  return plus_constant (val, INTVAL (XEXP (rtl, 1)));
+}
+
+  /* When called at the topmost level, scan pseudo assignments in between the
+ last scanned instruction in the tail call sequence and the latest insn
+ in that sequence.  */
+  if (toplevel)
+internal_arg_pointer_based_exp_scan ();
+
+  if (REG_P (rtl))
+{
+  unsigned int idx = REGNO (rtl) - FIRST_PSEUDO_REGISTER;
+  if (idx < VEC_length (rtx, internal_arg_pointer_exp_state.cache))
+	return VEC_index (rtx, inte

[PATCH] Make sibcall argument overlap check less pessimistic (PR middle-end/50074, take 2)

2011-11-29 Thread Jakub Jelinek
On Mon, Nov 28, 2011 at 11:10:56PM +0100, Eric Botcazou wrote:
> > Here is an attempt to make the check more complete (e.g.
> > the change wouldn't see overlap if addr was PLUS of two REGs,
> > where one of the REGs was based on internal_arg_pointer, etc.)
> > and less pessimistic.  As tree-tailcall.c doesn't allow tail calls
> > from functions that have address of any of the caller's parameters
> > taken, IMHO it is enough to look for internal_arg_pointer based
> > pseudos initialized in the tail call sequence.
> > This patch scans the tail call sequence and notes which pseudos
> > are based on internal_arg_pointer (and what offset from
> > that pointer they have) and uses that in
> > mem_overlaps_already_clobbered_arg_p.
> 
> This looks reasonable, but the logic is a bit hard to follow, especially the 
> double usage of internal_arg_pointer_based_reg depending on SCAN's value.
> Would it be possible to split it into 2 functions that recursively call each 
> other?

What about this way?  I've groupped the two variables into a structure
to make it clear it is internal internal_arg_pointer_based_exp* state,
scanning is done in a separate function and the SCAN argument is gone,
instead the internal_arg_pointer_based_exp_scan function disables scanning
during recursion by tweaking the internal state.

2011-11-29  Jakub Jelinek  

PR middle-end/51323
PR middle-end/50074
* calls.c (internal_arg_pointer_exp_state): New variable.
(internal_arg_pointer_based_exp_1,
internal_arg_pointer_exp_scan): New functions.
(internal_arg_pointer_based_exp): New function.
(mem_overlaps_already_clobbered_arg_p): Use it.
(expand_call): Free internal_arg_pointer_exp_state.cache vector
and clear internal_arg_pointer_exp_state.scan_start.

* gcc.c-torture/execute/pr51323.c: New test.

--- gcc/calls.c.jj  2011-11-29 08:58:50.164030662 +0100
+++ gcc/calls.c 2011-11-29 09:29:21.355613795 +0100
@@ -1658,6 +1658,139 @@ rtx_for_function_call (tree fndecl, tree
   return funexp;
 }
 
+/* Internal state for internal_arg_pointer_based_exp function and its
+   helpers.  */
+static struct
+{
+  /* Last insn that has been already scanned by
+ internal_arg_pointer_based_exp_scan, or NULL_RTX if none has been
+ scanned yet and scan should start at get_insns (), or pc_rtx if
+ internal_arg_pointer_based_exp is called from within
+ internal_arg_pointer_based_exp_scan and scanning shouldn't
+ be performed.  */
+  rtx scan_start;
+  /* Vector indexed by REGNO - FIRST_PSEUDO_REGISTER, recording if a pseudo is
+ based on crtl->args.internal_arg_pointer.  The element is NULL_RTX if the
+ pseudo isn't based on it, a CONST_INT offset if the pseudo is based on it
+ with fixed offset, or PC if this is with variable or unknown offset.  */
+  VEC(rtx, heap) *cache;
+} internal_arg_pointer_exp_state;
+
+static rtx internal_arg_pointer_based_exp (rtx);
+
+/* Helper function for internal_arg_pointer_based_exp, called through
+   for_each_rtx.  Return 1 if a crtl->args.internal_arg_pointer based
+   register is seen anywhere.  Return -1 if it is not based on it and
+   subexpressions of *LOC should not be examined.  */
+
+static int
+internal_arg_pointer_based_exp_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+{
+  if (REG_P (*loc) && internal_arg_pointer_based_exp (*loc) != NULL_RTX)
+return 1;
+  if (MEM_P (*loc))
+return -1;
+  return 0;
+}
+
+/* Helper function for internal_arg_pointer_based_exp.  Scan insns
+   in the tail call sequence, starting with first insn that hasn't been
+   scanned yet, and note for each LHS pseudo whether it is based on
+   crtl->args.internal_arg_pointer or not and what offset from
+   that pointer it has.  */
+
+static void
+internal_arg_pointer_based_exp_scan (void)
+{
+  rtx insn, scan_start = internal_arg_pointer_exp_state.scan_start;
+
+  if (scan_start == NULL_RTX)
+insn = get_insns ();
+  else
+insn = NEXT_INSN (scan_start);
+
+  /* Disable scanning in the recursive internal_arg_pointer_based_exp
+ calls.  */
+  internal_arg_pointer_exp_state.scan_start = pc_rtx;
+
+  while (insn)
+{
+  rtx set = single_set (insn);
+  if (set && REG_P (SET_DEST (set)) && !HARD_REGISTER_P (SET_DEST (set)))
+   {
+ rtx val = NULL_RTX;
+ unsigned int idx = REGNO (SET_DEST (set)) - FIRST_PSEUDO_REGISTER;
+ /* Punt on pseudos set multiple times.  */
+ if (idx < VEC_length (rtx, internal_arg_pointer_exp_state.cache)
+ && (VEC_index (rtx, internal_arg_pointer_exp_state.cache, idx)
+ != NULL_RTX))
+   val = pc_rtx;
+ else
+   val = internal_arg_pointer_based_exp (SET_SRC (set));
+ if (val != NULL_RTX)
+   {
+ VEC_safe_grow_cleared (rtx, heap,
+internal_arg_pointer_exp_state.cache,
+idx + 1);
+ VEC_replace (rtx,