New feature: return with SP depressed

Richard Kenner kenner@vlsi1.ultra.nyu.edu
Tue Aug 15 18:17:00 GMT 2000


This adds a feature that can be used by a front end to request that a
function return with the stack-pointer "depressed", meaning it does
not reclaim its frame and any alloca'd memory.  The calling function
treats it like a dynamic allocation of a variable within a scope and
restores the stack from the saved value at the beginning of that block.

The idea here is to allow a way for a function to return a variable amount
of data without the caller having to know how much data is being returned
and without the use of any "heap" allocation.  The function presumably is
going to return a pointer to the data and the caller will copy the data
within that block (normally the call and that copy will be the only thing
in that block).

Both the above and knowing which machines support this return mode is
currently the sole responsibility of the front end.

Currently, only Ada uses this.  Most "classic" Ada compilers have
always used this method for returning "unconstrained" data but it has
been hard to do it in GCC's mechanism until more ports started using
RTL for epilogues.

I should note this was originally developed under GCC 2.8.1 for the MIPS
but it worked the first time for Alpha in 2.96.

There are also some changes to print-tree.c and fold-const.c related to
tightening up the use of a flag in types.

Tue Aug 15 17:33:05 2000  Richard Kenner  <kenner@vlsi1.ultra.nyu.edu>

	* calls.c (ECF_SP_DEPRESSED): New macro.
	(calls_function_1): Treat calling sp-depressed function as alloca.
	(emit_call_1): Don't adjust SP if calling sp-depressed function.
	(expand_call): Set ECF_SP_DEPRESSED if TYPE_RETURNS_STACK_DEPRESSED.
	If sp-depressed, ensure block saves and restores SP.
	* fold-const.c (extract_muldiv): Only check TYPE_IS_SIZETYPE
	for INTEGER_TYPE.
	* function.c (keep_stack_depressed): New function.
	(thread_prologue_and_epilogue_insns): Call it.
	* print-tree.c (print_node): Use HOST_WIDE_INT_PRINT_UNSIGNED
	to print DECL_OFFSET_ALIGN.
	Print no-force-blk and transparent-union flags properly.
	* stmt.c (expand_goto_internal): Don't restore stack if last block
	and function returns with sp depressed.
	(fixup_gotos): Likewise.
	(save_stack_pointer): New function, from code in expand_decl.
	(expand_decl): Call new function.
	* tree.h (TYPE_IS_SIZETYPE): Call INTEGER_TYPE_CHECK.
	(TYPE_RETURNS_STACK_DEPRESSED): New macro.
	(save_stack_pointer): New declaration.

	* diagnostic.c (fatal_function): New variable.
	(set_fatal_function): New function.
	(fatal): Call it.
	* diagnostic.h (set_fatal_function): New declaration.

*** calls.c	2000/07/31 14:04:41	1.148
--- calls.c	2000/08/15 20:34:23
*************** static int calls_function_1	PARAMS ((tre
*** 185,188 ****
--- 185,191 ----
     but may read memory.  */
  #define ECF_PURE		512
+ /* Nonzero if this is a call to a function that returns with the stack
+    pointer depressed.  */
+ #define ECF_SP_DEPRESSED	1024
  
  static void emit_call_1		PARAMS ((rtx, tree, tree, HOST_WIDE_INT,
*************** calls_function_1 (exp, which)
*** 279,282 ****
--- 282,288 ----
        if (which == 0)
  	return 1;
+       else if (TYPE_RETURNS_STACK_DEPRESSED
+ 	       (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
+ 	return 1;
        else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
  	       && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
*************** emit_call_1 (funexp, fndecl, funtype, st
*** 496,500 ****
  #if defined (HAVE_call) && defined (HAVE_call_value)
    if (HAVE_call && HAVE_call_value && HAVE_call_pop && HAVE_call_value_pop
!       && n_popped > 0)
  #else
    if (HAVE_call_pop && HAVE_call_value_pop)
--- 502,506 ----
  #if defined (HAVE_call) && defined (HAVE_call_value)
    if (HAVE_call && HAVE_call_value && HAVE_call_pop && HAVE_call_value_pop
!       && n_popped > 0 && ! (ecf_flags & ECF_SP_DEPRESSED))
  #else
    if (HAVE_call_pop && HAVE_call_value_pop)
*************** emit_call_1 (funexp, fndecl, funtype, st
*** 625,632 ****
  	 stack pointer will be changed.  */
  
!       if (rounded_stack_size != 0)
  	{
  	  if (flag_defer_pop && inhibit_defer_pop == 0
! 	      && !(ecf_flags & (ECF_CONST | ECF_PURE)))
  	    pending_stack_adjust += rounded_stack_size;
  	  else
--- 631,638 ----
  	 stack pointer will be changed.  */
  
!       if (rounded_stack_size != 0 && ! (ecf_flags & ECF_SP_DEPRESSED))
  	{
  	  if (flag_defer_pop && inhibit_defer_pop == 0
! 	      && ! (ecf_flags & (ECF_CONST | ECF_PURE)))
  	    pending_stack_adjust += rounded_stack_size;
  	  else
*************** special_function_p (fndecl, flags)
*** 757,760 ****
--- 763,767 ----
  
  /* Return nonzero when tree represent call to longjmp.  */
+ 
  int
  setjmp_call_p (fndecl)
*************** setjmp_call_p (fndecl)
*** 765,768 ****
--- 772,776 ----
  
  /* Detect flags (function attributes) from the function type node.  */
+ 
  static int
  flags_from_decl_or_type (exp)
*************** flags_from_decl_or_type (exp)
*** 770,773 ****
--- 778,782 ----
  {
    int flags = 0;
+ 
    /* ??? We can't set IS_MALLOC for function types?  */
    if (DECL_P (exp))
*************** flags_from_decl_or_type (exp)
*** 785,789 ****
      }
  
!   if (TREE_READONLY (exp) && !TREE_THIS_VOLATILE (exp))
      flags |= ECF_CONST;
  
--- 794,798 ----
      }
  
!   if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
      flags |= ECF_CONST;
  
*************** expand_call (exp, target, ignore)
*** 2132,2136 ****
    int old_stack_allocated;
    rtx call_fusage;
!   register tree p;
    register int i;
    /* The alignment of the stack, in bits.  */
--- 2141,2145 ----
    int old_stack_allocated;
    rtx call_fusage;
!   register tree p = TREE_OPERAND (exp, 0);
    register int i;
    /* The alignment of the stack, in bits.  */
*************** expand_call (exp, target, ignore)
*** 2184,2190 ****
       attributes set in the type.  */
    else
      {
!       p = TREE_OPERAND (exp, 0);
!       flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p)));
      }
  
--- 2193,2203 ----
       attributes set in the type.  */
    else
+     flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p)));
+ 
+   /* Mark if the function returns with the stack pointer depressed.  */
+   if (TYPE_RETURNS_STACK_DEPRESSED (TREE_TYPE (TREE_TYPE (p))))
      {
!       flags |= ECF_SP_DEPRESSED;
!       flags &= ~ (ECF_PURE | ECF_CONST);
      }
  
*************** expand_call (exp, target, ignore)
*** 3272,3276 ****
  	 argument, restore saved stack-pointer value.  */
  
!       if (old_stack_level)
  	{
  	  emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
--- 3285,3289 ----
  	 argument, restore saved stack-pointer value.  */
  
!       if (old_stack_level && ! (flags & ECF_SP_DEPRESSED))
  	{
  	  emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
*************** expand_call (exp, target, ignore)
*** 3412,3415 ****
--- 3425,3439 ----
  
    currently_expanding_call--;
+ 
+   /* If this function returns with the stack pointer depressed, ensure
+      this block saves and restores the stack pointer, show it was
+      changed, and adjust for any outgoing arg space.  */
+   if (flags & ECF_SP_DEPRESSED)
+     {
+       clear_pending_stack_adjust ();
+       emit_insn (gen_rtx (CLOBBER, VOIDmode, stack_pointer_rtx));
+       emit_move_insn (virtual_stack_dynamic_rtx, stack_pointer_rtx);
+       save_stack_pointer ();
+     }
  
    return target;

*** fold-const.c	2000/08/05 00:50:00	1.129
--- fold-const.c	2000/08/15 20:35:08
*************** extract_muldiv (t, c, code, wide_type)
*** 4403,4407 ****
  	   || TREE_CODE_CLASS (TREE_CODE (op0)) == 'e')
  	  && TREE_UNSIGNED (TREE_TYPE (op0))
! 	  && ! TYPE_IS_SIZETYPE (TREE_TYPE (op0))
  	  && (GET_MODE_SIZE (TYPE_MODE (ctype))
                > GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (op0)))))
--- 4403,4408 ----
  	   || TREE_CODE_CLASS (TREE_CODE (op0)) == 'e')
  	  && TREE_UNSIGNED (TREE_TYPE (op0))
! 	  && ! (TREE_CODE (TREE_TYPE (op0)) == INTEGER_TYPE
! 		&& TYPE_IS_SIZETYPE (TREE_TYPE (op0)))
  	  && (GET_MODE_SIZE (TYPE_MODE (ctype))
                > GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (op0)))))
*************** extract_muldiv (t, c, code, wide_type)
*** 4528,4532 ****
  	 computation overflowed.  */
        if (TREE_UNSIGNED (ctype)
! 	  && ! TYPE_IS_SIZETYPE (ctype)
  	  && ctype != type)
  	break;
--- 4529,4533 ----
  	 computation overflowed.  */
        if (TREE_UNSIGNED (ctype)
! 	  && ! (TREE_CODE (ctype) == INTEGER_TYPE && TYPE_IS_SIZETYPE (ctype))
  	  && ctype != type)
  	break;
*************** extract_muldiv (t, c, code, wide_type)
*** 4592,4596 ****
  	 overflowed.  */
        if ((! TREE_UNSIGNED (ctype)
! 	   || TYPE_IS_SIZETYPE (ctype))
  	  && ((code == MULT_EXPR && tcode == EXACT_DIV_EXPR)
  	      || (tcode == MULT_EXPR
--- 4593,4597 ----
  	 overflowed.  */
        if ((! TREE_UNSIGNED (ctype)
! 	   || (TREE_CODE (ctype) == INTEGER_TYPE && TYPE_IS_SIZETYPE (ctype)))
  	  && ((code == MULT_EXPR && tcode == EXACT_DIV_EXPR)
  	      || (tcode == MULT_EXPR
*** function.c	2000/08/14 14:34:52	1.212
--- function.c	2000/08/15 20:35:23
*************** static void put_addressof_into_stack PAR
*** 295,298 ****
--- 295,301 ----
  static boolean purge_addressof_1 PARAMS ((rtx *, rtx, int, int,
  					  struct hash_table *));
+ #ifdef HAVE_epilogue
+ static void keep_stack_depressed PARAMS ((rtx));
+ #endif
  static int is_addressof		PARAMS ((rtx *, void *));
  static struct hash_entry *insns_for_mem_newfunc PARAMS ((struct hash_entry *,
*************** emit_return_into_block (bb, line_note)
*** 6809,6812 ****
--- 6812,6877 ----
  #endif /* HAVE_return */
  
+ #ifdef HAVE_epilogue
+ 
+ /* Modify SEQ, a SEQUENCE that is part of the epilogue, to no modifications
+    to the stack pointer.  */
+ 
+ static void
+ keep_stack_depressed (seq)
+      rtx seq;
+ {
+   int i;
+   rtx sp_from_reg = 0;
+   int sp_modified_unknown = 0;
+ 
+   /* If the epilogue is just a single instruction, it's OK as is */
+ 
+   if (GET_CODE (seq) != SEQUENCE) return;
+ 
+   /* Scan all insns in SEQ looking for ones that modified the stack
+      pointer.  Record if it modified the stack pointer by copying it
+      from the frame pointer or if it modified it in some other way.
+      Then modify any subsequent stack pointer references to take that
+      into account.  We start by only allowing SP to be copied from a
+      register (presumably FP) and then be subsequently referenced.  */
+ 
+   for (i = 0; i < XVECLEN (seq, 0); i++)
+     {
+       rtx insn = XVECEXP (seq, 0, i);
+ 
+       if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ 	continue;
+ 
+       if (reg_set_p (stack_pointer_rtx, insn))
+ 	{
+ 	  rtx set = single_set (insn);
+ 
+ 	  /* If SP is set as a side-effect, we can't support this.  */
+ 	  if (set == 0)
+ 	    abort ();
+ 
+ 	  if (GET_CODE (SET_SRC (set)) == REG)
+ 	    sp_from_reg = SET_SRC (set);
+ 	  else
+ 	    sp_modified_unknown = 1;
+ 
+ 	  /* Don't allow the SP modification to happen.  */
+ 	  PUT_CODE (insn, NOTE);
+ 	  NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ 	  NOTE_SOURCE_FILE (insn) = 0;
+ 	}
+       else if (reg_referenced_p (stack_pointer_rtx, PATTERN (insn)))
+ 	{
+ 	  if (sp_modified_unknown)
+ 	    abort ();
+ 
+ 	  else if (sp_from_reg != 0)
+ 	    PATTERN (insn)
+ 	      = replace_rtx (PATTERN (insn), stack_pointer_rtx, sp_from_reg);
+ 	}
+     }
+ }
+ #endif
+ 
  /* Generate the prologue and epilogue RTL if the machine supports it.  Thread
     this into place with notes indicating where the prologue ends and where
*************** thread_prologue_and_epilogue_insns (f)
*** 6991,6994 ****
--- 7056,7065 ----
  
        seq = gen_epilogue ();
+ 
+       /* If this function returns with the stack depressed, massage
+ 	 the epilogue to actually do that.  */
+       if (TYPE_RETURNS_STACK_DEPRESSED (TREE_TYPE (current_function_decl)))
+ 	keep_stack_depressed (seq);
+ 
        emit_jump_insn (seq);
  
*** print-tree.c	2000/06/12 23:08:05	1.33
--- print-tree.c	2000/08/15 20:35:25
*************** print_node (file, prefix, node, indent)
*** 413,417 ****
  	  fprintf (file, " align %d", DECL_ALIGN (node));
  	  if (TREE_CODE (node) == FIELD_DECL)
! 	    fprintf (file, " offset_align %d", DECL_OFFSET_ALIGN (node));
  	}
        else if (DECL_INLINE (node))
--- 413,421 ----
  	  fprintf (file, " align %d", DECL_ALIGN (node));
  	  if (TREE_CODE (node) == FIELD_DECL)
! 	    {
! 	      fprintf (file, " offset_align ");
! 	      fprintf (file, HOST_WIDE_INT_PRINT_UNSIGNED,
! 		       DECL_OFFSET_ALIGN (node));
! 	    }
  	}
        else if (DECL_INLINE (node))
*************** print_node (file, prefix, node, indent)
*** 479,499 ****
  
      case 't':
!       if (TYPE_NO_FORCE_BLK (node))
  	fputs (" no-force-blk", file);
        if (TYPE_STRING_FLAG (node))
  	fputs (" string-flag", file);
        if (TYPE_NEEDS_CONSTRUCTING (node))
  	fputs (" needs-constructing", file);
        /* The transparent-union flag is used for different things in
  	 different nodes.  */
!       if (TYPE_CHECK (node)->type.transparent_union_flag)
! 	{
! 	  if (TREE_CODE (node) == UNION_TYPE)
! 	    fputs (" transparent-union", file);
! 	  else if (TREE_CODE (node) == ARRAY_TYPE)
! 	    fputs (" nonaliased-component", file);
! 	  else
! 	    fputs (" tu-flag", file);
! 	}
        if (TYPE_PACKED (node))
  	fputs (" packed", file);
--- 483,516 ----
  
      case 't':
!       /* The no-force-blk flag is used for different things in
! 	 different types.  */
!       if ((TREE_CODE (node) == RECORD_TYPE
! 	   || TREE_CODE (node) == UNION_TYPE
! 	   || TREE_CODE (node) == QUAL_UNION_TYPE)
! 	  && TYPE_NO_FORCE_BLK (node))
  	fputs (" no-force-blk", file);
+       else if (TREE_CODE (node) == INTEGER_TYPE
+ 	       && TYPE_IS_SIZETYPE (node))
+ 	fputs (" sizetype", file);
+       else if (TREE_CODE (node) == FUNCTION_TYPE
+ 	       && TYPE_RETURNS_STACK_DEPRESSED (node))
+ 	fputs (" returns-stack-depressed", file);
+ 
        if (TYPE_STRING_FLAG (node))
  	fputs (" string-flag", file);
        if (TYPE_NEEDS_CONSTRUCTING (node))
  	fputs (" needs-constructing", file);
+ 
        /* The transparent-union flag is used for different things in
  	 different nodes.  */
!       if (TREE_CODE (node) == UNION_TYPE && TYPE_TRANSPARENT_UNION (node))
! 	fputs (" transparent-union", file);
!       else if (TREE_CODE (node) == ARRAY_TYPE
! 	       && TYPE_NONALIASED_COMPONENT (node))
! 	fputs (" nonaliased-component", file);
!       else if (TREE_CODE (node) == FUNCTION_TYPE
! 	       && TYPE_AMBIENT_BOUNDEDNESS (node))
! 	fputs (" ambient-boundedness", file);
! 
        if (TYPE_PACKED (node))
  	fputs (" packed", file);
*************** print_node (file, prefix, node, indent)
*** 551,558 ****
  	       || TREE_CODE (node) == QUAL_UNION_TYPE)
  	print_node (file, "fields", TYPE_FIELDS (node), indent + 4);
!       else if (TREE_CODE (node) == FUNCTION_TYPE || TREE_CODE (node) == METHOD_TYPE)
  	{
  	  if (TYPE_METHOD_BASETYPE (node))
! 	    print_node_brief (file, "method basetype", TYPE_METHOD_BASETYPE (node), indent + 4);
  	  print_node (file, "arg-types", TYPE_ARG_TYPES (node), indent + 4);
  	}
--- 568,577 ----
  	       || TREE_CODE (node) == QUAL_UNION_TYPE)
  	print_node (file, "fields", TYPE_FIELDS (node), indent + 4);
!       else if (TREE_CODE (node) == FUNCTION_TYPE
! 	       || TREE_CODE (node) == METHOD_TYPE)
  	{
  	  if (TYPE_METHOD_BASETYPE (node))
! 	    print_node_brief (file, "method basetype",
! 			      TYPE_METHOD_BASETYPE (node), indent + 4);
  	  print_node (file, "arg-types", TYPE_ARG_TYPES (node), indent + 4);
  	}
*** stmt.c	2000/08/12 16:28:10	1.157
--- stmt.c	2000/08/15 20:35:36
*************** expand_goto_internal (body, label, last_
*** 904,908 ****
  	  clear_pending_stack_adjust ();
  	  do_pending_stack_adjust ();
! 	  emit_stack_restore (SAVE_BLOCK, stack_level, NULL_RTX);
  	}
  
--- 904,916 ----
  	  clear_pending_stack_adjust ();
  	  do_pending_stack_adjust ();
! 
! 	  /* Don't do this adjust if it's to the end label and this function
! 	     is to return with a depressed stack pointer.  */
! 	  if (label == return_label
! 	      && (TYPE_RETURNS_STACK_DEPRESSED
! 		  (TREE_TYPE (current_function_decl))))
! 	    ;
! 	  else
! 	    emit_stack_restore (SAVE_BLOCK, stack_level, NULL_RTX);
  	}
  
*************** fixup_gotos (thisblock, stack_level, cle
*** 1183,1187 ****
  	  /* Restore stack level for the biggest contour that this
  	     jump jumps out of.  */
! 	  if (f->stack_level)
  	    emit_stack_restore (SAVE_BLOCK, f->stack_level, f->before_jump);
  
--- 1191,1198 ----
  	  /* Restore stack level for the biggest contour that this
  	     jump jumps out of.  */
! 	  if (f->stack_level
! 	      && ! (f->target_rtl == return_label
! 		    && (TYPE_RETURNS_STACK_DEPRESSED 
! 			(TREE_TYPE (current_function_decl)))))
  	    emit_stack_restore (SAVE_BLOCK, f->stack_level, f->before_jump);
  
*************** expand_end_bindings (vars, mark_ends, do
*** 3668,3671 ****
--- 3679,3699 ----
  }
  
+ /* Generate code to save the stack pointer at the start of the current block
+    and set up to restore it on exit.  */
+ 
+ void
+ save_stack_pointer ()
+ {
+   struct nesting *thisblock = block_stack;
+ 
+   if (thisblock->data.block.stack_level == 0)
+     {
+       emit_stack_save (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
+ 		       &thisblock->data.block.stack_level,
+ 		       thisblock->data.block.first_insn);
+       stack_block_stack = thisblock;
+     }
+ }
+ 
  /* Generate RTL for the automatic variable declaration DECL.
     (Other kinds of declarations are simply ignored if seen here.)  */
*************** expand_decl (decl)
*** 3778,3789 ****
        /* Record the stack pointer on entry to block, if have
  	 not already done so.  */
!       if (thisblock->data.block.stack_level == 0)
! 	{
! 	  do_pending_stack_adjust ();
! 	  emit_stack_save (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
! 			   &thisblock->data.block.stack_level,
! 			   thisblock->data.block.first_insn);
! 	  stack_block_stack = thisblock;
! 	}
  
        /* In function-at-a-time mode, variable_size doesn't expand this,
--- 3806,3811 ----
        /* Record the stack pointer on entry to block, if have
  	 not already done so.  */
!       do_pending_stack_adjust ();
!       save_stack_pointer ();
  
        /* In function-at-a-time mode, variable_size doesn't expand this,
*** tree.h	2000/08/12 22:45:05	1.187
--- tree.h	2000/08/15 20:35:44
*************** struct tree_block
*** 955,960 ****
     both for validity checking and to permit optimziations that are unsafe
     for other types.  */
! #define TYPE_IS_SIZETYPE(NODE) (TYPE_CHECK (NODE)->type.no_force_blk_flag)
  
  /* Nonzero in a type considered volatile as a whole.  */
  #define TYPE_VOLATILE(NODE) ((NODE)->common.volatile_flag)
--- 955,966 ----
     both for validity checking and to permit optimziations that are unsafe
     for other types.  */
! #define TYPE_IS_SIZETYPE(NODE) \
!   (INTEGER_TYPE_CHECK (NODE)->type.no_force_blk_flag)
  
+ /* In a FUNCTION_TYPE, indicates that the function returns with the stack
+    pointer depressed.  */
+ #define TYPE_RETURNS_STACK_DEPRESSED(NODE) \
+   (FUNCTION_TYPE_CHECK(NODE)->type.no_force_blk_flag)
+ 
  /* Nonzero in a type considered volatile as a whole.  */
  #define TYPE_VOLATILE(NODE) ((NODE)->common.volatile_flag)
*************** extern void expand_start_target_temps	PA
*** 2812,2815 ****
--- 2818,2822 ----
  extern void expand_end_target_temps	PARAMS ((void));
  extern void expand_elseif		PARAMS ((tree));
+ extern void save_stack_pointer		PARAMS ((void));
  extern void expand_decl			PARAMS ((tree));
  extern int expand_decl_cleanup		PARAMS ((tree, tree));


More information about the Gcc-patches mailing list