PATCH: RTL_EXPR vs. inlining-on-trees

Mark Mitchell mark@codesourcery.com
Sun Mar 5 12:37:00 GMT 2000


      Yes, but consider this:

	S f() { return S(); }
	S g() { return f(); }
	void h () { S s = g (); }

      Since we actually return structures by passing a pointer to a spot
      created in the caller, `g' creates a temporary.  In C++, we cleverly
      use TARGET_EXPRs to avoid extra copies, so given this situation we
      create only one temoprary; there is no additional temporary created in
      `h'.  So, if the `g' temporary is destroyed by the time we get back to
      `h', we're in trouble.

  Right, but that's what the preserve calls are all about!

That only works if you actually "return" the expression from the block
mode object.  It's possible to still need it in the enclosing scope,
without returning it.  In fact, you might return something else.  The
preserve scheme is not general -- it allows but one thing to be
preserved.

The full detail here is gory.  A TARGET_EXPR is passed in to the
function in question.  The inliner assigns the address of the
TARGET_EXPR to a parameter in the callee.  That causes the TARGET_EXPR
to be expanded midway through the initialization code for the
function, creating a new temporary.  The function returns that
pointer, not the structure value itself.  Hence, the temporary is not
preserved.  It is not in general possible to determine whether the
pointer returned by the function corresponds to the TARGET_EXPR or
not.  Hence, the only safe thing is to preserve all the temporaries
until the next proper scope.

Again, this points out what I consider to be a missing feature in GCC:
a stack-slot allocator that could operate much later in the game.
It's silly to tie down the stack-slot shape as we expand statements;
if those statements are, for example, dead, we're committed to
stack-slots we don't want.

  No, but I'd argue that it's implicit in that definition: if you create any
  temporaries within the expressions being expanded, they are "part" of
  the RTL_EXPR.

The question is this: can you imagine legitimate uses of an RTL_EXPR,
where the temporaries have a longer lifetime?  And the answer, of
course, is yes.  You might want to generate a little bit of RTL to
create a few variables, initialize them, and then use the whole thing
somewhere else.

I've adopted the solution I rejected in my previous posting: to add a
flag to RTL_EXPRs.

--
Mark Mitchell                   mark@codesourcery.com
CodeSourcery, LLC               http://www.codesourcery.com

2000-03-05  Mark Mitchell  <mark@codesourcery.com>

	* tree.def (RTL_EXPR): Update documentation.
	* tree.h (RTL_EXPR_HAS_NO_SCOPE): New macro.
	* expr.c (expand_expr): Handle RTL_EXPR_HAS_NO_SCOPE.
	* function.c (preserve_rtl_expr_temp): New function.
	(preserve_rtl_expr_temps): Likewise.
	(preserve_rtl_expr_result): Use it.

	Revert this patch:
	2000-03-04  Mark Mitchell  <mark@codesourcery.com>

Index: tree.def
===================================================================
RCS file: /cvs/gcc/egcs/gcc/tree.def,v
retrieving revision 1.28
diff -c -p -r1.28 tree.def
*** tree.def	2000/02/28 09:51:41	1.28
--- tree.def	2000/03/05 20:34:40
*************** DEFTREECODE (SAVE_EXPR, "save_expr", 'e'
*** 686,695 ****
     but where we must re-expand.  */
  DEFTREECODE (UNSAVE_EXPR, "unsave_expr", 'e', 1)
  
! /* Represents something whose RTL has already been expanded
!    as a sequence which should be emitted when this expression is expanded.
!    The first operand is the RTL to emit.  It is the first of a chain of insns.
!    The second is the RTL expression for the result.  */
  DEFTREECODE (RTL_EXPR, "rtl_expr", 'e', 2)
  
  /* & in C.  Value is the address at which the operand's value resides.
--- 686,699 ----
     but where we must re-expand.  */
  DEFTREECODE (UNSAVE_EXPR, "unsave_expr", 'e', 1)
  
! /* Represents something whose RTL has already been expanded as a
!    sequence which should be emitted when this expression is expanded.
!    The first operand is the RTL to emit.  It is the first of a chain
!    of insns.  The second is the RTL expression for the result.  If
!    RTL_EXPR_HAS_NO_SCOPE does not hold for this expression, then all
!    temporaries created within this RTL_EXPR (except for the
!    RTL_EXPR_RTL) are out-of-scope after the RTL_EXPR is expanded.  (In
!    other words, their stack slots may be reused.)  */
  DEFTREECODE (RTL_EXPR, "rtl_expr", 'e', 2)
  
  /* & in C.  Value is the address at which the operand's value resides.
Index: tree.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/tree.h,v
retrieving revision 1.134
diff -c -p -r1.134 tree.h
*** tree.h	2000/03/05 19:34:29	1.134
--- tree.h	2000/03/05 20:34:42
*************** struct tree_vec
*** 749,754 ****
--- 749,758 ----
  /* In a RTL_EXPR node.  */
  #define RTL_EXPR_SEQUENCE(NODE) (*(struct rtx_def **) &EXPR_CHECK (NODE)->exp.operands[0])
  #define RTL_EXPR_RTL(NODE) (*(struct rtx_def **) &EXPR_CHECK (NODE)->exp.operands[1])
+ /* Nonzero if the RTL_EXPR does not define a scope, i.e., if
+    temporaries defined during its scope should persist even after the
+    RTL_EXPR has been expanded.  */
+ #define RTL_EXPR_HAS_NO_SCOPE(NODE) TREE_ASM_WRITTEN (NODE)
  
  /* In a CALL_EXPR node.  */
  #define CALL_EXPR_RTL(NODE) (*(struct rtx_def **) &EXPR_CHECK (NODE)->exp.operands[2])
Index: expr.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/expr.c,v
retrieving revision 1.207
diff -c -p -r1.207 expr.c
*** expr.c	2000/03/05 19:34:29	1.207
--- expr.c	2000/03/05 20:34:48
*************** expand_expr (exp, target, tmode, modifie
*** 6306,6313 ****
  	  emit_insns (RTL_EXPR_SEQUENCE (exp));
  	  RTL_EXPR_SEQUENCE (exp) = const0_rtx;
  	}
!       preserve_rtl_expr_result (RTL_EXPR_RTL (exp));
!       free_temps_for_rtl_expr (exp);
        return RTL_EXPR_RTL (exp);
  
      case CONSTRUCTOR:
--- 6306,6318 ----
  	  emit_insns (RTL_EXPR_SEQUENCE (exp));
  	  RTL_EXPR_SEQUENCE (exp) = const0_rtx;
  	}
!       if (RTL_EXPR_HAS_NO_SCOPE (exp))
! 	preserve_rtl_expr_temps (exp);
!       else
! 	{
! 	  preserve_rtl_expr_result (RTL_EXPR_RTL (exp));
! 	  free_temps_for_rtl_expr (exp);
! 	}
        return RTL_EXPR_RTL (exp);
  
      case CONSTRUCTOR:
Index: function.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/function.c,v
retrieving revision 1.170
diff -c -p -r1.170 function.c
*** function.c	2000/03/05 19:34:29	1.170
--- function.c	2000/03/05 20:34:52
*************** static void mark_function_chain PARAMS (
*** 293,298 ****
--- 293,299 ----
  static void prepare_function_start PARAMS ((void));
  static void do_clobber_return_reg PARAMS ((rtx, void *));
  static void do_use_return_reg PARAMS ((rtx, void *));
+ static void preserve_rtl_expr_temp PARAMS ((struct temp_slot *));
  
  /* Pointer to chain of `struct function' for containing functions.  */
  struct function *outer_function_chain;
*************** preserve_temp_slots (x)
*** 1129,1134 ****
--- 1130,1165 ----
        p->level--;
  }
  
+ /* Preserve the temporary slot given by P (originally created during
+    the building of an RTL_EXPR) at least as long as things in our
+    current scope.  */
+ 
+ static void
+ preserve_rtl_expr_temp (p)
+      struct temp_slot *p;
+ {
+   /* Set the slot level to that of the currently prevailing scope.  */
+   p->level = MIN (p->level, temp_slot_level);
+   /* This slot is no longer associated with the RTL_EXPR from which it
+      originated.  */
+   p->rtl_expr = NULL_TREE;
+ }
+ 
+ /* Preserve the temporary slots created during the building of the
+    RTL_EXPR given by T at least as long as things in our current
+    scope.  */
+ 
+ void
+ preserve_rtl_expr_temps (t)
+      tree t;
+ {
+   struct temp_slot *p;
+ 
+   for (p = temp_slots; p; p = p->next)
+     if (p->in_use && p->rtl_expr == t)
+       preserve_rtl_expr_temp (p);
+ }
+ 
  /* X is the result of an RTL_EXPR.  If it is a temporary slot associated
     with that RTL_EXPR, promote it into a temporary slot at the present
     level so it will not be freed when we free slots made in the
*************** preserve_rtl_expr_result (x)
*** 1148,1158 ****
    /* If we can find a match, move it to our level unless it is already at
       an upper level.  */
    p = find_temp_slot_from_address (XEXP (x, 0));
!   if (p != 0)
!     {
!       p->level = MIN (p->level, temp_slot_level);
!       p->rtl_expr = 0;
!     }
  
    return;
  }
--- 1179,1186 ----
    /* If we can find a match, move it to our level unless it is already at
       an upper level.  */
    p = find_temp_slot_from_address (XEXP (x, 0));
!   if (p)
!     preserve_rtl_expr_temp (p);
  
    return;
  }


More information about the Gcc-patches mailing list