[rfc] with_size_expr for non-addressable variable sized objects

Richard Henderson rth@redhat.com
Thu Jul 8 19:57:00 GMT 2004


This patch hasn't been completely vetted, but since Kenner made some
noises about this problem this morning, I thought I'd let folks see
what I have so far.

We have three remaining contexts in which rtl tries to reference
decl or type sizes without letting the tree optimizers know about it.

  (1) Incoming parameters, including __builtin_va_start,
  (2) Outgoing parameters,
  (3) Function return values.

This patch does not attempt to address (1).  I was actually working
on (1) when I got sidetracked by (2) and (3).

Outgoing parameters are a special problem because they involve a
memcpy into the outgoing argument area.  We can't find the
destination address for that copy without duplicating all of the
ABI logic in calls.c.  Even then there would be the problem of
coordinating with calls.c.  Let's just call these non-addressable.

The proposed solution to (2) is to introduce WITH_SIZE_EXPR.  The
first operand holds the expression (any lvalue form), and the second
operand holds the size of the expression.  In this way the size of
the parameter are easily propagated down to where calls.c can find it.

Function return values are, in theory, a bit easier.  Under certain
circumstances we could use CALL_EXPR_HAS_RETURN_SLOT_ADDR, which
provides calls.c with the structure return address to fill in the
appropriate ABI slot.  The problem is that given "x = foo()", we
cannot always use "x" as the return-slot-addr.  The situations that
we cannot are those for which "&x" has escaped such that foo might
be able to access it via another path.

So we still need some way to handle (3).  One option is to gimplify
"foo()" immediately to

	__builtin_stack_alloc (&tmp, size);
	foo(&tmp) [CALL_EXPR_HAS_RETURN_SLOT_ADDR];
	*tmp;

but I think that might make it difficult to elide the temporary
later.  The option that I'm leaning toward is to wrap the call in
a WITH_SIZE_EXPR as well.  In the event that we do not optimize the
call statement at all, calls.c has enough information to create a
temporary on its own.  And it's a sufficiently transparent 
representation that an opimizer could later notice that, for a
given "x = foo()", that "x" does not in fact escape, and so can
then transform to the return-slot-addr form.

Thoughts?


r~



Index: gcc/tree-cfg.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-cfg.c,v
retrieving revision 2.26
diff -c -p -d -r2.26 tree-cfg.c
*** gcc/tree-cfg.c	7 Jul 2004 22:19:41 -0000	2.26
--- gcc/tree-cfg.c	8 Jul 2004 16:07:39 -0000
*************** make_ctrl_stmt_edges (basic_block bb)
*** 510,516 ****
  static void
  make_exit_edges (basic_block bb)
  {
!   tree last = last_stmt (bb);
  
    if (last == NULL_TREE)
      abort ();
--- 510,516 ----
  static void
  make_exit_edges (basic_block bb)
  {
!   tree last = last_stmt (bb), op;
  
    if (last == NULL_TREE)
      abort ();
*************** make_exit_edges (basic_block bb)
*** 550,557 ****
        /* A MODIFY_EXPR may have a CALL_EXPR on its RHS and the CALL_EXPR
  	 may have an abnormal edge.  Search the RHS for this case and
  	 create any required edges.  */
!       if (TREE_CODE (TREE_OPERAND (last, 1)) == CALL_EXPR
! 	  && TREE_SIDE_EFFECTS (TREE_OPERAND (last, 1))
  	  && current_function_has_nonlocal_label)
  	make_goto_expr_edges (bb);
  
--- 550,557 ----
        /* A MODIFY_EXPR may have a CALL_EXPR on its RHS and the CALL_EXPR
  	 may have an abnormal edge.  Search the RHS for this case and
  	 create any required edges.  */
!       op = get_call_expr_in (last);
!       if (op && TREE_SIDE_EFFECTS (op)
  	  && current_function_has_nonlocal_label)
  	make_goto_expr_edges (bb);
  
*************** clear_special_calls (void)
*** 1520,1526 ****
  static void
  remove_useless_stmts_1 (tree *tp, struct rus_data *data)
  {
!   tree t = *tp;
  
    switch (TREE_CODE (t))
      {
--- 1520,1526 ----
  static void
  remove_useless_stmts_1 (tree *tp, struct rus_data *data)
  {
!   tree t = *tp, op;
  
    switch (TREE_CODE (t))
      {
*************** remove_useless_stmts_1 (tree *tp, struct
*** 1566,1575 ****
      case MODIFY_EXPR:
        data->last_goto = NULL;
        fold_stmt (tp);
!       if (TREE_CODE (TREE_OPERAND (t, 1)) == CALL_EXPR)
  	{
! 	  update_call_expr_flags (TREE_OPERAND (t, 1));
! 	  notice_special_calls (TREE_OPERAND (t, 1));
  	}
        if (tree_could_throw_p (t))
  	data->may_throw = true;
--- 1566,1576 ----
      case MODIFY_EXPR:
        data->last_goto = NULL;
        fold_stmt (tp);
!       op = get_call_expr_in (t);
!       if (op)
  	{
! 	  update_call_expr_flags (op);
! 	  notice_special_calls (op);
  	}
        if (tree_could_throw_p (t))
  	data->may_throw = true;
*************** is_ctrl_stmt (tree t)
*** 2478,2514 ****
  bool
  is_ctrl_altering_stmt (tree t)
  {
!   tree call = t;
  
  #if defined ENABLE_CHECKING
    if (t == NULL)
      abort ();
  #endif
  
!   switch (TREE_CODE (t))
      {
-     case MODIFY_EXPR:
-       /* A MODIFY_EXPR with a rhs of a call has the characteristics
- 	 of the call.  */
-       call = TREE_OPERAND (t, 1);
-       if (TREE_CODE (call) != CALL_EXPR)
- 	break;
-       /* FALLTHRU */
- 
-     case CALL_EXPR:
        /* A non-pure/const CALL_EXPR alters flow control if the current
  	 function has nonlocal labels.  */
!       if (TREE_SIDE_EFFECTS (t)
! 	  && current_function_has_nonlocal_label)
  	return true;
  
        /* A CALL_EXPR also alters control flow if it does not return.  */
        if (call_expr_flags (call) & (ECF_NORETURN | ECF_LONGJMP))
  	return true;
-       break;
- 
-     default:
-       return false;
      }
  
    /* If a statement can throw, it alters control flow.  */
--- 2479,2502 ----
  bool
  is_ctrl_altering_stmt (tree t)
  {
!   tree call;
  
  #if defined ENABLE_CHECKING
    if (t == NULL)
      abort ();
  #endif
  
!   call = get_call_expr_in (t);
!   if (call)
      {
        /* A non-pure/const CALL_EXPR alters flow control if the current
  	 function has nonlocal labels.  */
!       if (TREE_SIDE_EFFECTS (call) && current_function_has_nonlocal_label)
  	return true;
  
        /* A CALL_EXPR also alters control flow if it does not return.  */
        if (call_expr_flags (call) & (ECF_NORETURN | ECF_LONGJMP))
  	return true;
      }
  
    /* If a statement can throw, it alters control flow.  */
*************** static bool
*** 4509,4523 ****
  tree_block_ends_with_call_p (basic_block bb)
  {
    block_stmt_iterator bsi = bsi_last (bb);
!   tree t = tsi_stmt (bsi.tsi);
! 
!   if (TREE_CODE (t) == RETURN_EXPR && TREE_OPERAND (t, 0))
!     t = TREE_OPERAND (t, 0);
! 
!   if (TREE_CODE (t) == MODIFY_EXPR)
!     t = TREE_OPERAND (t, 1);
! 
!   return TREE_CODE (t) == CALL_EXPR;
  }
  
  
--- 4497,4503 ----
  tree_block_ends_with_call_p (basic_block bb)
  {
    block_stmt_iterator bsi = bsi_last (bb);
!   return get_call_expr_in (bsi_stmt (bsi)) != NULL;
  }
  
  
*************** tree_block_ends_with_condjump_p (basic_b
*** 4538,4548 ****
  static bool
  need_fake_edge_p (tree t)
  {
!   if (TREE_CODE (t) == RETURN_EXPR && TREE_OPERAND (t, 0))
!     t = TREE_OPERAND (t, 0);
! 
!   if (TREE_CODE (t) == MODIFY_EXPR)
!     t = TREE_OPERAND (t, 1);
  
    /* NORETURN and LONGJMP calls already have an edge to exit.
       CONST, PURE and ALWAYS_RETURN calls do not need one.
--- 4518,4524 ----
  static bool
  need_fake_edge_p (tree t)
  {
!   tree call;
  
    /* NORETURN and LONGJMP calls already have an edge to exit.
       CONST, PURE and ALWAYS_RETURN calls do not need one.
*************** need_fake_edge_p (tree t)
*** 4551,4559 ****
       figured out from the RTL in mark_constant_function, and
       the counter incrementation code from -fprofile-arcs
       leads to different results from -fbranch-probabilities.  */
!   if (TREE_CODE (t) == CALL_EXPR
!       && !(call_expr_flags (t) & 
! 	    (ECF_NORETURN | ECF_LONGJMP | ECF_ALWAYS_RETURN)))
      return true;
  
    if (TREE_CODE (t) == ASM_EXPR
--- 4527,4536 ----
       figured out from the RTL in mark_constant_function, and
       the counter incrementation code from -fprofile-arcs
       leads to different results from -fbranch-probabilities.  */
!   call = get_call_expr_in (t);
!   if (call
!       && !(call_expr_flags (call) & 
! 	   (ECF_NORETURN | ECF_LONGJMP | ECF_ALWAYS_RETURN)))
      return true;
  
    if (TREE_CODE (t) == ASM_EXPR
Index: gcc/tree-eh.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-eh.c,v
retrieving revision 2.8
diff -c -p -d -r2.8 tree-eh.c
*** gcc/tree-eh.c	29 Jun 2004 06:59:34 -0000	2.8
--- gcc/tree-eh.c	8 Jul 2004 16:07:39 -0000
*************** lower_eh_constructs_1 (struct leh_state 
*** 1552,1565 ****
        /* Look for things that can throw exceptions, and record them.  */
        if (state->cur_region && tree_could_throw_p (t))
  	{
  	  record_stmt_eh_region (state->cur_region, t);
  	  note_eh_region_may_contain_throw (state->cur_region);
  
  	  /* ??? For the benefit of calls.c, converting all this to rtl, 
  	     we need to record the call expression, not just the outer
  	     modify statement.  */
! 	  if (TREE_CODE (TREE_OPERAND (t, 1)) == CALL_EXPR)
! 	    record_stmt_eh_region (state->cur_region, TREE_OPERAND (t, 1));
  	}
        break;
  
--- 1552,1568 ----
        /* Look for things that can throw exceptions, and record them.  */
        if (state->cur_region && tree_could_throw_p (t))
  	{
+ 	  tree op;
+ 
  	  record_stmt_eh_region (state->cur_region, t);
  	  note_eh_region_may_contain_throw (state->cur_region);
  
  	  /* ??? For the benefit of calls.c, converting all this to rtl, 
  	     we need to record the call expression, not just the outer
  	     modify statement.  */
! 	  op = get_call_expr_in (t);
! 	  if (op)
! 	    record_stmt_eh_region (state->cur_region, op);
  	}
        break;
  
Index: gcc/tree-sra.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-sra.c,v
retrieving revision 2.17
diff -c -p -d -r2.17 tree-sra.c
*** gcc/tree-sra.c	6 Jul 2004 23:02:22 -0000	2.17
--- gcc/tree-sra.c	8 Jul 2004 16:07:39 -0000
*************** sra_walk_modify_expr (tree expr, block_s
*** 811,820 ****
        else
  	fns->use (rhs_elt, &TREE_OPERAND (expr, 1), bsi, false);
      }
-   else if (TREE_CODE (rhs) == CALL_EXPR)
-     sra_walk_call_expr (rhs, bsi, fns);
    else
!     sra_walk_expr (&TREE_OPERAND (expr, 1), bsi, false, fns);
  }
  
  /* Entry point to the walk functions.  Search the entire function,
--- 811,824 ----
        else
  	fns->use (rhs_elt, &TREE_OPERAND (expr, 1), bsi, false);
      }
    else
!     {
!       tree call = get_call_expr_in (rhs);
!       if (call)
! 	sra_walk_call_expr (call, bsi, fns);
!       else
! 	sra_walk_expr (&TREE_OPERAND (expr, 1), bsi, false, fns);
!     }
  }
  
  /* Entry point to the walk functions.  Search the entire function,
Index: gcc/tree-ssa-ccp.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-ssa-ccp.c,v
retrieving revision 2.17
diff -c -p -d -r2.17 tree-ssa-ccp.c
*** gcc/tree-ssa-ccp.c	8 Jul 2004 08:50:00 -0000	2.17
--- gcc/tree-ssa-ccp.c	8 Jul 2004 16:07:39 -0000
*************** get_rhs (tree stmt)
*** 2106,2132 ****
  {
    enum tree_code code = TREE_CODE (stmt);
  
!   if (code == MODIFY_EXPR)
!     return TREE_OPERAND (stmt, 1);
!   if (code == COND_EXPR)
!     return COND_EXPR_COND (stmt);
!   else if (code == SWITCH_EXPR)
!     return SWITCH_COND (stmt);
!   else if (code == RETURN_EXPR)
      {
!       if (!TREE_OPERAND (stmt, 0))
! 	return NULL_TREE;
!       if (TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR)
! 	return TREE_OPERAND (TREE_OPERAND (stmt, 0), 1);
        else
! 	return TREE_OPERAND (stmt, 0);
      }
-   else if (code == GOTO_EXPR)
-     return GOTO_DESTINATION (stmt);
-   else if (code == LABEL_EXPR)
-     return LABEL_EXPR_LABEL (stmt);
-   else
-     return stmt;
  }
  
  
--- 2106,2135 ----
  {
    enum tree_code code = TREE_CODE (stmt);
  
!   switch (code)
      {
!     case RETURN_EXPR:
!       stmt = TREE_OPERAND (stmt, 0);
!       if (stmt)
! 	return get_rhs (stmt);
        else
! 	return NULL;
! 
!     case MODIFY_EXPR:
!       return TREE_OPERAND (stmt, 1);
! 
!     case COND_EXPR:
!       return COND_EXPR_COND (stmt);
!     case SWITCH_EXPR:
!       return SWITCH_COND (stmt);
!     case GOTO_EXPR:
!       return GOTO_DESTINATION (stmt);
!     case LABEL_EXPR:
!       return LABEL_EXPR_LABEL (stmt);
! 
!     default:
!       return stmt;
      }
  }
  
  
*************** get_rhs (tree stmt)
*** 2135,2142 ****
  static bool
  set_rhs (tree *stmt_p, tree expr)
  {
!   tree stmt = *stmt_p;
    enum tree_code code = TREE_CODE (expr);
  
    /* Verify the constant folded result is valid gimple.  */
    if (TREE_CODE_CLASS (code) == '2')
--- 2138,2146 ----
  static bool
  set_rhs (tree *stmt_p, tree expr)
  {
!   tree stmt = *stmt_p, op;
    enum tree_code code = TREE_CODE (expr);
+   stmt_ann_t ann;
  
    /* Verify the constant folded result is valid gimple.  */
    if (TREE_CODE_CLASS (code) == '2')
*************** set_rhs (tree *stmt_p, tree expr)
*** 2151,2180 ****
  	return false;
      }
  
!   code = TREE_CODE (stmt);
!   if (code == MODIFY_EXPR)
!     TREE_OPERAND (stmt, 1) = expr;
!   else if (code == COND_EXPR)
!     COND_EXPR_COND (stmt) = expr;
!   else if (code == SWITCH_EXPR)
!     SWITCH_COND (stmt) = expr;
!   else if (code == RETURN_EXPR)
!     {
!       if (TREE_OPERAND (stmt, 0)
! 	  && TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR)
! 	TREE_OPERAND (TREE_OPERAND (stmt, 0), 1) = expr;
!       else
! 	TREE_OPERAND (stmt, 0) = expr;
!     }
!   else if (code == GOTO_EXPR)
!     GOTO_DESTINATION (stmt) = expr;
!   else if (code == LABEL_EXPR)
!     LABEL_EXPR_LABEL (stmt) = expr;
!   else
      {
        /* Replace the whole statement with EXPR.  If EXPR has no side
  	 effects, then replace *STMT_P with an empty statement.  */
!       stmt_ann_t ann = stmt_ann (stmt);
        *stmt_p = TREE_SIDE_EFFECTS (expr) ? expr : build_empty_stmt ();
        (*stmt_p)->common.ann = (tree_ann_t) ann;
  
--- 2155,2193 ----
  	return false;
      }
  
!   switch (TREE_CODE (stmt))
      {
+     case RETURN_EXPR:
+       op = TREE_OPERAND (stmt, 0);
+       if (TREE_CODE (op) != MODIFY_EXPR)
+ 	{
+ 	  TREE_OPERAND (stmt, 0) = expr;
+ 	  break;
+ 	}
+       stmt = op;
+       /* FALLTHRU */
+ 
+     case MODIFY_EXPR:
+       TREE_OPERAND (stmt, 1) = expr;
+       break;
+ 
+     case COND_EXPR:
+       COND_EXPR_COND (stmt) = expr;
+       break;
+     case SWITCH_EXPR:
+       SWITCH_COND (stmt) = expr;
+       break;
+     case GOTO_EXPR:
+       GOTO_DESTINATION (stmt) = expr;
+       break;
+     case LABEL_EXPR:
+       LABEL_EXPR_LABEL (stmt) = expr;
+       break;
+ 
+     default:
        /* Replace the whole statement with EXPR.  If EXPR has no side
  	 effects, then replace *STMT_P with an empty statement.  */
!       ann = stmt_ann (stmt);
        *stmt_p = TREE_SIDE_EFFECTS (expr) ? expr : build_empty_stmt ();
        (*stmt_p)->common.ann = (tree_ann_t) ann;
  
*************** set_rhs (tree *stmt_p, tree expr)
*** 2211,2216 ****
--- 2224,2230 ----
  		SSA_NAME_DEF_STMT (var) = *stmt_p;
  	    }
  	}
+       break;
      }
  
    return true;
Index: gcc/tree-ssa-dce.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-ssa-dce.c,v
retrieving revision 2.8
diff -c -p -d -r2.8 tree-ssa-dce.c
*** gcc/tree-ssa-dce.c	29 Jun 2004 06:59:35 -0000	2.8
--- gcc/tree-ssa-dce.c	8 Jul 2004 16:07:39 -0000
*************** mark_stmt_if_obviously_necessary (tree s
*** 290,295 ****
--- 290,296 ----
    v_must_def_optype v_must_defs;
    stmt_ann_t ann;
    size_t i;
+   tree op;
  
    /* Statements that are implicitly live.  Most function calls, asm and return
       statements are required.  Labels and BIND_EXPR nodes are kept because
*************** mark_stmt_if_obviously_necessary (tree s
*** 319,326 ****
        return;
  
      case MODIFY_EXPR:
!       if (TREE_CODE (TREE_OPERAND (stmt, 1)) == CALL_EXPR
! 	  && TREE_SIDE_EFFECTS (TREE_OPERAND (stmt, 1)))
  	{
  	  mark_stmt_necessary (stmt, true);
  	  return;
--- 320,327 ----
        return;
  
      case MODIFY_EXPR:
!       op = get_call_expr_in (stmt);
!       if (op && TREE_SIDE_EFFECTS (op))
  	{
  	  mark_stmt_necessary (stmt, true);
  	  return;
*************** eliminate_unnecessary_stmts (void)
*** 638,648 ****
  	    remove_dead_stmt (&i, bb);
  	  else
  	    {
! 	      if (TREE_CODE (t) == CALL_EXPR)
! 		notice_special_calls (t);
! 	      else if (TREE_CODE (t) == MODIFY_EXPR
! 		       && TREE_CODE (TREE_OPERAND (t, 1)) == CALL_EXPR)
! 		notice_special_calls (TREE_OPERAND (t, 1));
  	      bsi_next (&i);
  	    }
  	}
--- 639,647 ----
  	    remove_dead_stmt (&i, bb);
  	  else
  	    {
! 	      tree call = get_call_expr_in (t);
! 	      if (call)
! 		notice_special_calls (call);
  	      bsi_next (&i);
  	    }
  	}
Index: gcc/tree-ssa-dse.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-ssa-dse.c,v
retrieving revision 2.5
diff -c -p -d -r2.5 tree-ssa-dse.c
*** gcc/tree-ssa-dse.c	26 Jun 2004 05:03:54 -0000	2.5
--- gcc/tree-ssa-dse.c	8 Jul 2004 16:07:39 -0000
*************** dse_optimize_stmt (struct dom_walk_data 
*** 244,253 ****
    if (NUM_V_MAY_DEFS (v_may_defs) == 0)
      return;
  
!   /* We know we have virtual definitions.  If this is a MODIFY_EXPR, then
!      record it into our table.  */
!   if (TREE_CODE (stmt) == MODIFY_EXPR
!       && TREE_CODE (TREE_OPERAND (stmt, 1)) != CALL_EXPR)
      {
        dataflow_t df = get_immediate_uses (stmt);
        unsigned int num_uses = num_immediate_uses (df);
--- 244,254 ----
    if (NUM_V_MAY_DEFS (v_may_defs) == 0)
      return;
  
!   /* We know we have virtual definitions.  If this is a MODIFY_EXPR that's
!      not also a function call, then record it into our table.  */
!   if (get_call_expr_in (stmt))
!     return;
!   if (TREE_CODE (stmt) == MODIFY_EXPR)
      {
        dataflow_t df = get_immediate_uses (stmt);
        unsigned int num_uses = num_immediate_uses (df);
Index: gcc/tree-tailcall.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-tailcall.c,v
retrieving revision 2.13
diff -c -p -d -r2.13 tree-tailcall.c
*** gcc/tree-tailcall.c	26 Jun 2004 05:03:55 -0000	2.13
--- gcc/tree-tailcall.c	8 Jul 2004 16:07:39 -0000
*************** optimize_tail_call (struct tailcall *t, 
*** 757,766 ****
      {
        tree stmt = bsi_stmt (t->call_bsi);
  
!       if (TREE_CODE (stmt) == MODIFY_EXPR)
! 	stmt = TREE_OPERAND (stmt, 1);
!       if (TREE_CODE (stmt) != CALL_EXPR)
! 	abort ();
        CALL_EXPR_TAILCALL (stmt) = 1;
        if (dump_file && (dump_flags & TDF_DETAILS))
          {
--- 757,763 ----
      {
        tree stmt = bsi_stmt (t->call_bsi);
  
!       stmt = get_call_expr_in (stmt);
        CALL_EXPR_TAILCALL (stmt) = 1;
        if (dump_file && (dump_flags & TDF_DETAILS))
          {



More information about the Gcc-patches mailing list